PHP cryptography integration

Use the native M7\Crypto\Key class for local key, signature and RSA-OAEP operations. This guide describes the 0.3.0 source candidate. Complete installation first and check release status. Local cryptography requires no network account or provider registration.

For a complete server setup, follow the AlmaLinux walkthrough: development packages, C/PHP builds, correctness and memory tests, installation, service activation and rollback. It also explains the optional SDK path and possible PHP 8.5 port.

Public API

Key is final and has a private constructor. Its supported creation paths are:

Factory Input and result
Key::fromPrivatePem($pem) Import unencrypted PKCS#8, RSA PKCS#1 or EC SEC1 PEM
Key::fromPublicPem($pem) Import SPKI BEGIN PUBLIC KEY PEM
Key::fromSecret($bytes) Copy a raw HMAC secret into a native handle
Key::generate($algorithm, $rsaBits = 0) Generate an asymmetric key for an exact profile

Each returns a Key. Public PEM import does not parse a certificate or JWK. The instance operations are:

Method Return value
sign($data, $algorithm) Binary signature or MAC string
verify($data, $signature, $algorithm) true on valid signature; false for native invalid-signature status
encrypt($plaintext, $algorithm) Binary RSA-OAEP ciphertext
decrypt($ciphertext, $algorithm) Binary RSA-OAEP plaintext
toPrivatePem() Unencrypted PKCS#8 PEM string
toPublicPem() SPKI PEM string
close() Release the native handle early; safe to repeat

Algorithms and key policy

Family Exact signing/MAC identifiers
RSA PKCS#1 v1.5 RS256, RS384, RS512
RSA-PSS PS256, PS384, PS512
ECDSA ES256, ES384, ES512, ES256K
Edwards Ed25519, Ed448
HMAC HS256, HS384, HS512
ML-DSA ML-DSA-44, ML-DSA-65, ML-DSA-87

These 18 names are recognized profiles, not a guarantee that the active OpenSSL provider supports every operation. Names are case-sensitive and have no aliases, embedded-NUL truncation or fallback. Select an algorithm from trusted application policy, not an untrusted token header alone. The static library list is not identity-provider discovery or a client's permitted algorithm list.

Pass raw messages, not precomputed digests. Messages, signatures and secrets are binary-safe PHP strings. ECDSA signatures use fixed-width R || S, not DER. Edwards and ML-DSA operations use pure mode with an empty context.

Generation supports the 15 asymmetric signing profiles and RSA-OAEP-256. RSA accepts 2048, 3072, 4096 or 8192 bits; zero selects 4096. Other profiles require zero and choose their fixed curve/key type. PS profiles generate ordinary RSA keys. HMAC generation and the ambiguous EdDSA alias are rejected. For HMAC, import securely generated random bytes: at least 32 for HS256, 48 for HS384, or 64 for HS512. Secret and asymmetric handles are not interchangeable.

Public export accepts a private or public-only asymmetric handle. Public-only keys cannot export private PEM, and secret handles cannot export PEM. Export is capped at 65536 bytes. Private PEM is unencrypted; protect it before storage. Returned strings own their bytes and survive closing the key.

Minimal sign and verify example

Save as example.php and run using the isolated module from installation. The example generates a disposable key and prints no key or message material.

<?php
declare(strict_types=1);

use M7\Crypto\Key;

$key = Key::generate('Ed25519');
try {
    $message = 'example payload';
    $signature = $key->sign($message, 'Ed25519');
    if ($key->verify($message, $signature, 'Ed25519') !== true) {
        throw new RuntimeException('Signature rejected');
    }
} finally {
    $key->close();
}

For incoming messages, import the expected public key separately. A verifier must reject on either false or an exception, including provider errors. A valid signature alone does not establish issuer, audience, expiration, sender binding or permission to act. JWT assembly, base64url encoding, JWK conversion and token policy belong to the application or its Identity SDK.

Encrypt a small content key

RSA-OAEP-256 fixes OAEP SHA-256, MGF1 SHA-256 and an empty label. Ordinary RSA keys must be 2048–8192 bits. Encryption uses the public part of a private or public key; decryption needs the private key. RSA-PSS-restricted, other asymmetric and secret keys are rejected.

The plaintext limit is ceil(key_bits / 8) - 66 bytes (190 at 2048 bits), and ciphertext is ceil(key_bits / 8) bytes. Use it for a small secret, such as a 32-byte AES content-encryption key:

<?php
declare(strict_types=1);

use M7\Crypto\Key;

$recipient = Key::generate('RSA-OAEP-256', 2048);
try {
    $contentKey = random_bytes(32);
    $encryptedKey = $recipient->encrypt($contentKey, 'RSA-OAEP-256');
    $recoveredKey = $recipient->decrypt($encryptedKey, 'RSA-OAEP-256');
    if (!hash_equals($contentKey, $recoveredKey)) {
        throw new RuntimeException('Content key mismatch');
    }
} finally {
    $recipient->close();
}

This example tests only key encryption. AES-GCM, JWE serialization and application integration are separate. A failed decryption throws with a fixed failure message and never returns partial plaintext. Do not convert this into acceptance of the message or try another encryption algorithm automatically.

Lifetime, errors and security

Each object owns one native key. Destruction and request shutdown release it; close() is an optional earlier release. Later operations on a closed key raise ValueError. Assigning the object to another variable shares the same object, so closing either reference closes it. Cloning, serialization and dynamic properties are disabled. Keep objects within their request/worker; cross-thread key sharing is outside the supported contract.

The wrapper copies native output into a PHP string, then cleanses and frees the native buffer. Closing the key does not erase caller variables or copies of PEM, secrets, messages or returned plaintext. Sensitive parameters redact some exception traces; they do not replace secure storage or safe logging.

Failure PHP behavior
Native invalid-signature status verify() returns false
Unknown algorithm, invalid arguments or closed key ValueError
Wrong PHP types Normal PHP argument parsing, including TypeError and scalar coercion rules
Invalid PEM, wrong key type, bad secret length or disallowed export M7\Crypto\CryptoException
Provider or native allocation failure M7\Crypto\CryptoException
Wrong decryption key or invalid ciphertext M7\Crypto\CryptoException, code 7, fixed failure message

CryptoException extends RuntimeException and carries the native status code. Missing provider support during generation uses code 3. Some malformed signatures produce an OpenSSL error rather than false; all exceptions mean the operation did not succeed. PHP engine allocation failures retain their fatal-error behavior. Keep diagnostics free of keys, tokens and message bytes.

Return to M7 PHP Crypto.