Algorithm identifiers¶
An AlgorithmIdentifier is an OID and, sometimes, parameters. It appears everywhere: beside a key to say
what kind of key it is, beside a signature to say how it was made, inside an extension to name a digest.
Every one is its own class, so a signature algorithm is a type rather than a string you have to recognise.
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\SHA256WithRSAEncryptionAlgorithmIdentifier;
$algorithm = SHA256WithRSAEncryptionAlgorithmIdentifier::create();
$algorithm->oid(); // "1.2.840.113549.1.1.11"
$algorithm->name(); // a human-readable name, for an error message
The families¶
The interfaces under AlgorithmIdentifier\Feature are what a signature takes when any member of a
family will do.
| Interface | Means |
|---|---|
SignatureAlgorithmIdentifier |
may sign a certificate, a request or an attribute certificate |
HashAlgorithmIdentifier |
a digest |
CipherAlgorithmIdentifier |
a symmetric cipher |
BlockCipherAlgorithmIdentifier |
a block cipher, with a block size and an IV |
AsymmetricCryptoAlgorithmIdentifier |
names a key type |
PRFAlgorithmIdentifier |
a pseudo-random function |
EncryptionAlgorithmIdentifier |
encrypts |
TBSCertificate::sign() takes a SignatureAlgorithmIdentifier, so passing a hash is a type error rather
than a runtime surprise.
Signature algorithms¶
Class, under AlgorithmIdentifier\Signature |
Accepted by default in path validation |
|---|---|
SHA1WithRSAEncryptionAlgorithmIdentifier |
yes |
SHA224WithRSAEncryptionAlgorithmIdentifier |
yes |
SHA256WithRSAEncryptionAlgorithmIdentifier |
yes |
SHA384WithRSAEncryptionAlgorithmIdentifier |
yes |
SHA512WithRSAEncryptionAlgorithmIdentifier |
yes |
ECDSAWithSHA1AlgorithmIdentifier |
yes |
ECDSAWithSHA224AlgorithmIdentifier |
yes |
ECDSAWithSHA256AlgorithmIdentifier |
yes |
ECDSAWithSHA384AlgorithmIdentifier |
yes |
ECDSAWithSHA512AlgorithmIdentifier |
yes |
MD2WithRSAEncryptionAlgorithmIdentifier |
no |
MD4WithRSAEncryptionAlgorithmIdentifier |
no |
MD5WithRSAEncryptionAlgorithmIdentifier |
no |
Ed25519 and Ed448 sign too, through Asymmetric\Ed25519AlgorithmIdentifier and
Asymmetric\Ed448AlgorithmIdentifier: in RFC 8410 the key type and the signature algorithm are the same
OID. Both are accepted by default, subject to what the runtime can do.
The MD2, MD4 and MD5 classes exist so that a certificate signed with one can be parsed and reported. They are left out of the default accepted set: chosen-prefix collisions against MD5 have been practical since 2009, which is enough to forge a certificate that verifies, and no authority in service still signs with them.
RSASSA-PSS is not implemented. Asymmetric\RSAPSSSSAEncryptionAlgorithmIdentifier models the
identifier, and a PSS-signed certificate is refused when it is parsed. The OID is deliberately absent
from the default accepted set: allowing it would advertise a capability that does not exist. Implementing
it will mean binding the parameters as well as the OID, since Certificate::fromASN1() compares only the
OIDs of the outer and signed algorithm identifiers, which would otherwise leave a hash, MGF and salt
length downgrade open.
Key algorithms¶
Class, under AlgorithmIdentifier\Asymmetric |
Key type |
|---|---|
RSAEncryptionAlgorithmIdentifier |
RSA |
RSAPSSSSAEncryptionAlgorithmIdentifier |
RSASSA-PSS |
ECPublicKeyAlgorithmIdentifier |
elliptic curve; carries the named curve |
Ed25519AlgorithmIdentifier, Ed448AlgorithmIdentifier |
EdDSA signing keys |
X25519AlgorithmIdentifier, X448AlgorithmIdentifier |
key agreement keys |
ECPublicKeyAlgorithmIdentifier is the one that carries a parameter — the curve — as an OID:
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric\ECPublicKeyAlgorithmIdentifier;
$algorithm = ECPublicKeyAlgorithmIdentifier::create(ECPublicKeyAlgorithmIdentifier::CURVE_PRIME256V1);
$algorithm->namedCurve();
The CURVE_* constants cover the prime curves of X9.62 and the SEC 2 curves, CURVE_PRIME256V1
(P-256), CURVE_SECP384R1 (P-384) and CURVE_SECP521R1 (P-521) among them.
Hashes and ciphers¶
Under AlgorithmIdentifier\Hash: MD5, SHA1, SHA224, SHA256, SHA384, SHA512, and the
RFC 4231 HMAC identifiers HMACWithSHA1 through HMACWithSHA512.
Under AlgorithmIdentifier\Cipher: DESCBC, DESEDE3CBC, RC2CBC, AES128CBC, AES192CBC,
AES256CBC. A block cipher identifier carries its initialisation vector, which is why
Crypto::encrypt() takes no separate IV.
Reading one you were handed¶
AlgorithmIdentifier::fromASN1() dispatches on the OID and returns the right class:
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
$algorithm = AlgorithmIdentifier::fromASN1($sequence);
An OID the library does not know comes back as a GenericAlgorithmIdentifier, which keeps the OID and
the parameters as they were. Nothing is lost and nothing throws: you can inspect and re-encode an
identifier the library has no class for.
Adding your own¶
Implement AlgorithmIdentifierProvider — supportsOID(string $oid): bool and
getClassByOID(string $oid): string — and hand it to the factory:
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifierFactory;
$factory = AlgorithmIdentifierFactory::create($myProvider);
$factory->getClass('1.3.6.1.4.1.99999.1'); // your class name, or null
$algorithm = $factory->parse($sequence); // your class, or one of the library's
Your class extends SpecificAlgorithmIdentifier and implements the Feature interface of its family —
SignatureAlgorithmIdentifier for a signature algorithm, which is what makes it acceptable to sign().
A provider adds, it does not override
The built-in map is consulted first, so a provider cannot replace an identifier the library already
ships. AlgorithmIdentifier::fromASN1() uses a factory with no providers at all, so an OID only your
provider knows comes back from it as a GenericAlgorithmIdentifier. Call your own factory's
parse() where you want your classes.
Signing with it is a separate matter: the crypto engine has to know how, and the one shipped speaks the algorithms OpenSSL does.