Packages · supply-chain
k2gl/sshsig
Sign and verify with the SSH signature format (SSHSIG) in PHP.
Use the same SSHSIG format as `ssh-keygen -Y` to sign and verify blobs from PHP.
Install
composer require k2gl/sshsig Reach for it when
- You sign or verify with SSH keys in the SSHSIG format (ssh-keygen -Y compatible).
- You want signing that interoperates with existing SSH key infrastructure.
Look elsewhere when
- You want Sigstore/transparency-log-backed signing — use sigstore-sign / sigstore-verify.
k2gl/sshsig
Sign and verify files with SSH keys in PHP, in the OpenSSH SSHSIG format that ssh-keygen -Y sign/verify produces (the signatures Git uses for commit and tag signing). Verifying checks
the signer against an allowed_signers file; signing emits bytes ssh-keygen -Y verify
accepts. No dependencies.
Fail-closed by design: anything malformed, unsupported, cryptographically invalid, or unauthorized throws — nothing is ever silently treated as verified.
Install
composer require k2gl/sshsig
Requires PHP 8.1+ with ext-sodium (Ed25519) and ext-openssl (RSA and ECDSA) — both are
bundled with most PHP builds.
Usage
Verify a signature against an allowed_signers file
This is the equivalent of ssh-keygen -Y verify -f allowed_signers -I <identity> -n <namespace>.
use K2gl\Sshsig\AllowedSigners;
use K2gl\Sshsig\Exception\SshsigException;
use K2gl\Sshsig\SshsigVerifier;
$verifier = new SshsigVerifier;
try {
$result = $verifier->verify(
message: file_get_contents('release.tar.gz'),
armoredSignature: file_get_contents('release.tar.gz.sig'),
allowedSigners: AllowedSigners::fromFile('allowed_signers'),
identity: 'alice@example.com',
namespace: 'file',
);
echo "Verified: {$result->publicKey->fingerprint()}\n";
} catch (SshsigException $e) {
// not verified — malformed, unsupported, bad signature, or unauthorized signer
echo "Rejected: {$e->getMessage()}\n";
}
Check a signature without authorizing the signer
The equivalent of ssh-keygen -Y check-novalidate -n <namespace>: confirm the signature is
structurally sound and cryptographically valid under its own embedded key, then inspect it.
$signature = $verifier->checkNoValidate($message, $armoredSignature, namespace: 'git');
echo $signature->signatureAlgorithm; // e.g. "ssh-ed25519"
echo $signature->publicKey->fingerprint(); // "SHA256:…"
Sign a message
Produce an SSHSIG signature (the ssh-keygen -Y sign operation). Provide the signing key —
Ed25519 (a 64-byte libsodium secret key) or an OpenSSL RSA/ECDSA private key:
use K2gl\Sshsig\Ed25519SigningKey;
use K2gl\Sshsig\OpensshPrivateKey;
use K2gl\Sshsig\OpensslSigningKey;
use K2gl\Sshsig\SshsigSigner;
// Ed25519
$keypair = sodium_crypto_sign_keypair();
$signer = new SshsigSigner(new Ed25519SigningKey(sodium_crypto_sign_secretkey($keypair)));
// …or RSA / ECDSA from an OpenSSL private key (PEM)
$signer = new SshsigSigner(OpensslSigningKey::fromPem(file_get_contents('id_rsa')));
// …or an unencrypted ssh-ed25519 key straight off disk, in the format ssh-keygen writes
$signer = new SshsigSigner(OpensshPrivateKey::fromFile('/home/you/.ssh/id_ed25519'));
$armored = $signer->sign($message, namespace: 'file'); // sha512 by default
file_put_contents('message.sig', $armored);
// the resulting signature verifies with `ssh-keygen -Y verify` and with this library
$signer->publicKey() returns the matching SshPublicKey (e.g. to build an allowed_signers
line). The default hash is sha512; pass hashAlgorithm: 'sha256' to switch.
Inspect or build allowed_signers in memory
$allowed = AllowedSigners::fromString(<<<TXT
# comments and blank lines are ignored
alice@example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA…
*@example.com namespaces="git,file" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA…
TXT);
Design
- Fail-closed. Every failure path throws a
K2gl\Sshsig\Exception\SshsigException(InvalidSignatureException,UnsupportedAlgorithmException,SignatureVerificationFailed,SignerNotAllowedException). A returned value always means verified. - Spec-accurate. Implements OpenSSH
PROTOCOL.sshsig: theSSHSIGmagic preamble, version 1, theto-be-signedblob (namespace + reserved + hash algorithm + message hash), and the armor. - Algorithms.
ssh-ed25519,rsa-sha2-256,rsa-sha2-512,ecdsa-sha2-nistp256/384/521;sha256andsha512message hashing. The legacy SHA-1ssh-rsasignature algorithm is refused. allowed_signers. Principals pattern-lists,namespaces=,valid-after/valid-beforevalidity windows, andcert-authorityentries (parsed and recorded; certificate-chain verification is not yet performed).- Signing.
SshsigSignerover a pluggableSigningKey(Ed25519 via ext-sodium; RSA and ECDSA via ext-openssl), producing armor byte-compatible withssh-keygen -Y verify.OpensshPrivateKey::fromFile()/::fromString()load an unencryptedssh-ed25519key straight from theopenssh-key-v1containerssh-keygenwrites by default; encrypted containers and RSA/ECDSA containers are refused (fail-closed) rather than partially read. - Zero dependencies. Pure PHP over
ext-sodiumandext-openssl; nophpseclib. - Verified against OpenSSH. The test suite verifies real
ssh-keygen -Y signoutput for every supported algorithm, plus tampered, wrong-namespace, unauthorized, and malformed cases.
License
MIT. See LICENSE.
API
Public classes and methods, generated from the source.
K2gl\Sshsig\AllowedSigner class
__construct( public readonly string $principals, public readonly SshPublicKey $key, public readonly bool $certAuthority = false, public readonly ?string $namespaces = null, public readonly ?DateTimeImmutable $validAfter = null, public readonly ?DateTimeImmutable $validBefore = null, )
K2gl\Sshsig\AllowedSigners class
__construct(private readonly array $signers)fromString(string $contents): selffromFile(string $path): selfall(): arrayfindMatch(string $identity, string $namespace, SshPublicKey $key, DateTimeImmutable $time): ?AllowedSigner
K2gl\Sshsig\Ed25519SigningKey class
__construct(private readonly string $secretKey)publicKey(): SshPublicKeysignTosign(string $tosign, string $hashAlgorithm): array
K2gl\Sshsig\Exception\InvalidSignatureException class
K2gl\Sshsig\Exception\SignatureVerificationFailed class
K2gl\Sshsig\Exception\SignerNotAllowedException class
K2gl\Sshsig\Exception\SigningException class
K2gl\Sshsig\Exception\SshsigException interface
K2gl\Sshsig\Exception\UnsupportedAlgorithmException class
K2gl\Sshsig\OpensshPrivateKey class
fromFile(string $path): SigningKeyfromString(string $armored): SigningKey
K2gl\Sshsig\OpensslSigningKey class
fromKey(OpenSSLAsymmetricKey $key): selffromPem(string $pem, ?string $passphrase = null): selfpublicKey(): SshPublicKeysignTosign(string $tosign, string $hashAlgorithm): array
K2gl\Sshsig\SigningKey interface
publicKey(): SshPublicKeysignTosign(string $tosign, string $hashAlgorithm): array
K2gl\Sshsig\SshPublicKey class
fromBlob(string $blob): selffromOpensshLine(string $line): selffingerprint(): stringequals(self $other): bool
K2gl\Sshsig\SshSignature class
fromArmored(string $armored): selfsignedData(string $message): string
K2gl\Sshsig\SshsigSigner class
__construct(private readonly SigningKey $key)publicKey(): SshPublicKeysign(string $message, string $namespace, string $hashAlgorithm = 'sha512'): string
K2gl\Sshsig\SshsigVerifier class
verify( string $message, string $armoredSignature, AllowedSigners $allowedSigners, string $identity, string $namespace, ?DateTimeImmutable $verifyTime = null, ): VerifiedSignaturecheckNoValidate(string $message, string $armoredSignature, string $namespace): SshSignature
K2gl\Sshsig\VerifiedSignature class
__construct( public readonly string $identity, public readonly string $namespace, public readonly SshPublicKey $publicKey, public readonly string $principals, )