From 2c09913054a7d5a8c292392f003149a144198498 Mon Sep 17 00:00:00 2001 From: Mihkel Kivisild Date: Tue, 5 Nov 2024 08:57:48 +0200 Subject: [PATCH] Add customizable hash algorithm for generating certificate ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WE2-1022 Co-authored-by: Kai Hölscher 51371415+hoels@users.noreply.github.com Signed-off-by: Mihkel Kivisild --- src/ocsp/Ocsp.php | 14 +++++++------- src/util/AsnUtil.php | 19 +++++++++++++++---- src/util/HashAlgorithm.php | 15 +++++++++++++++ tests/ocsp/OcspTest.php | 14 ++++++++++++++ 4 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 src/util/HashAlgorithm.php diff --git a/src/ocsp/Ocsp.php b/src/ocsp/Ocsp.php index 2792dbd..82e4287 100644 --- a/src/ocsp/Ocsp.php +++ b/src/ocsp/Ocsp.php @@ -31,6 +31,7 @@ use phpseclib3\File\X509; use web_eid\web_eid_authtoken_validation_php\ocsp\exceptions\OcspCertificateException; use web_eid\web_eid_authtoken_validation_php\util\AsnUtil; +use web_eid\web_eid_authtoken_validation_php\util\HashAlgorithm; class Ocsp { @@ -65,7 +66,8 @@ class Ocsp */ public function generateCertificateId( X509 $certificate, - X509 $issuerCertificate + X509 $issuerCertificate, + HashAlgorithm $hashAlgorithm = HashAlgorithm::SHA1 ): array { AsnUtil::loadOIDs(); @@ -87,9 +89,7 @@ public function generateCertificateId( ); } - $certificateId["serialNumber"] = clone $certificate->getCurrentCert()[ - "tbsCertificate" - ]["serialNumber"]; + $certificateId["serialNumber"] = clone $certificate->getCurrentCert()["tbsCertificate"]["serialNumber"]; // issuer name if ( @@ -109,7 +109,7 @@ public function generateCertificateId( "subject" ]; $issuerEncoded = ASN1::encodeDER($issuer, Name::MAP); - $certificateId["issuerNameHash"] = hash("sha1", $issuerEncoded, true); + $certificateId["issuerNameHash"] = hash($hashAlgorithm->value, $issuerEncoded, true); // issuer public key if ( @@ -129,12 +129,12 @@ public function generateCertificateId( "subjectPublicKeyInfo" ]["subjectPublicKey"]; $certificateId["issuerKeyHash"] = hash( - "sha1", + $hashAlgorithm->value, AsnUtil::extractKeyData($publicKey), true ); - $certificateId["hashAlgorithm"]["algorithm"] = Asn1::getOID("id-sha1"); + $certificateId["hashAlgorithm"]["algorithm"] = Asn1::getOID("id-" . $hashAlgorithm->value); return $certificateId; } diff --git a/src/util/AsnUtil.php b/src/util/AsnUtil.php index c7b197a..d15f0f8 100644 --- a/src/util/AsnUtil.php +++ b/src/util/AsnUtil.php @@ -43,16 +43,20 @@ public static function isSignatureInAsn1Format(string $signature): bool // ASN.1 format: 0x30 b1 0x02 b2 r 0x02 b3 s. // Note: unpack() returns an array indexed from 1, not 0. - if(!isset($sigByteArray[1]) || + if ( + !isset($sigByteArray[1]) || !isset($sigByteArray[2]) || !isset($sigByteArray[3]) || - !isset($sigByteArray[4])) { + !isset($sigByteArray[4]) + ) { return false; } $b1 = $sigByteArray[2]; $b2 = $sigByteArray[4]; - if(!isset($sigByteArray[5 + $b2]) || - !isset($sigByteArray[6 + $b2])) { + if ( + !isset($sigByteArray[5 + $b2]) || + !isset($sigByteArray[6 + $b2]) + ) { return false; } $b3 = $sigByteArray[6 + $b2]; @@ -108,6 +112,13 @@ public static function loadOIDs(): void ASN1::loadOIDs([ "id-pkix-ocsp-nonce" => self::ID_PKIX_OCSP_NONCE, "id-sha1" => "1.3.14.3.2.26", + 'id-sha256' => '2.16.840.1.101.3.4.2.1', + 'id-sha384' => '2.16.840.1.101.3.4.2.2', + 'id-sha512' => '2.16.840.1.101.3.4.2.3', + 'id-sha224' => '2.16.840.1.101.3.4.2.4', + 'id-sha512/224' => '2.16.840.1.101.3.4.2.5', + 'id-sha512/256' => '2.16.840.1.101.3.4.2.6', + 'id-mgf1' => '1.2.840.113549.1.1.8', "sha256WithRSAEncryption" => "1.2.840.113549.1.1.11", "qcStatements(3)" => "1.3.6.1.5.5.7.1.3", "street" => "2.5.4.9", diff --git a/src/util/HashAlgorithm.php b/src/util/HashAlgorithm.php new file mode 100644 index 0000000..9a5105a --- /dev/null +++ b/src/util/HashAlgorithm.php @@ -0,0 +1,15 @@ +assertEquals([168, 74, 106, 99, 4, 125, 221, 186, 230, 209, 57, 183, 166, 69, 101, 239, 243, 168, 236, 161], array_values(unpack('C*', $result['issuerKeyHash']))); } + public function testWhenGenerateCertificateIdWithSha256IsSuccess(): void + { + $result = (new Ocsp)->generateCertificateId( + (new CertificateLoader)->fromFile(__DIR__ . '/../_resources/revoked.crt')->getCert(), + (new CertificateLoader)->fromFile(__DIR__ . '/../_resources/revoked.issuer.crt')->getCert(), + HashAlgorithm::SHA256 + ); + + $this->assertEquals("2.16.840.1.101.3.4.2.1", $result['hashAlgorithm']['algorithm']); + $this->assertEquals([95, 66, 108, 14, 230, 221, 220, 19, 204, 150, 46, 50, 249, 230, 243, 173, 85, 145, 220, 162, 11, 98, 80, 34, 131, 168, 252, 178, 130, 128, 58, 168], array_values(unpack('C*', $result['issuerNameHash']))); + $this->assertEquals([171, 181, 182, 119, 64, 116, 118, 100, 255, 11, 197, 252, 216, 32, 39, 48, 158, 67, 174, 62, 32, 137, 104, 62, 240, 236, 220, 228, 44, 99, 167, 49], array_values(unpack('C*', $result['issuerKeyHash']))); + } + public function testWhenMissingSerialNumberInSubjectCertificateThrow(): void {