@@ -34,7 +34,6 @@ class TSA {
3434
3535 public function __construct () {
3636 $ this ->ensureOidsAreLoaded ();
37- $ this ->initializeTimestampStructure ();
3837 }
3938
4039 private function processContentCandidate ($ content , ?string &$ cmsDer ): array {
@@ -72,12 +71,6 @@ private function ensureOidsAreLoaded(): void {
7271 self ::$ areOidsInitialized = true ;
7372 }
7473
75- private function initializeTimestampStructure (): void {
76- if (self ::$ timestampInfoStructure === null ) {
77- self ::$ timestampInfoStructure = $ this ->buildTimestampInfoStructure ();
78- }
79- }
80-
8174 private function convertDerToPkcs7Pem (string $ derData ): string {
8275 return "-----BEGIN PKCS7----- \n" . chunk_split (base64_encode ($ derData ), 64 , "\n" ) . "-----END PKCS7----- \n" ;
8376 }
@@ -140,7 +133,6 @@ public function extract(array $root): array {
140133 $ tstInfoOctets = null ;
141134 $ cnHints = [];
142135
143- // Optimized extraction with early termination
144136 $ values = $ this ->getAttributeValuesSetAfterOID ($ root , self ::TIMESTAMP_OIDS ['TIME_STAMP_TOKEN ' ]);
145137 if ($ values ) {
146138 foreach ($ values as $ candidate ) {
@@ -174,7 +166,7 @@ public function extract(array $root): array {
174166 $ tst = null ;
175167 if ($ tstNode && ($ tstNode ['type ' ] ?? null ) === ASN1 ::TYPE_SEQUENCE ) {
176168 ASN1 ::setTimeFormat ('Y-m-d\TH:i:s\Z ' );
177- $ tst = ASN1 ::asn1map ($ tstNode , self ::$ timestampInfoStructure );
169+ $ tst = ASN1 ::asn1map ($ tstNode , self ::$ timestampInfoStructure ??= $ this -> buildTimestampInfoStructure () );
178170
179171 if (!is_array ($ tst )) {
180172 $ tst = $ this ->parseTstInfoFallback ($ tstInfoOctets );
@@ -193,8 +185,13 @@ public function extract(array $root): array {
193185
194186 if (!empty ($ tst ['messageImprint ' ])) {
195187 $ algOid = $ tst ['messageImprint ' ]['hashAlgorithm ' ]['algorithm ' ] ?? null ;
196- $ tsa ['hashAlgorithmOID ' ] = $ algOid ;
197- $ tsa ['hashAlgorithm ' ] = $ this ->resolveHashAlgorithm ($ algOid );
188+
189+ $ friendlyName = $ this ->resolveHashAlgorithm ($ algOid );
190+
191+ $ numericOid = $ this ->getNumericOid ($ algOid );
192+
193+ $ tsa ['hashAlgorithm ' ] = $ friendlyName ;
194+ $ tsa ['hashAlgorithmOID ' ] = $ numericOid ;
198195
199196 $ hashed = $ tst ['messageImprint ' ]['hashedMessage ' ] ?? null ;
200197 if (is_string ($ hashed )) {
@@ -385,10 +382,12 @@ private function extractCertificateHints(array $asn1Tree): array {
385382
386383 private function isStringValidUtf8 (string $ text ): bool {
387384 return mb_check_encoding ($ text , 'UTF-8 ' )
388- && preg_match ('/[\P{C}]/u ' , $ text )
389- && !preg_match ('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/ ' , $ text );
390- } private function generateDistinguishedNames (array $ hints ): string {
391- $ map = [
385+ && preg_match ('/[\P{C}]/u ' , $ text )
386+ && !preg_match ('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/ ' , $ text );
387+ }
388+
389+ private function generateDistinguishedNames (array $ hints ): string {
390+ $ mapping = [
392391 'countryName ' => 'C ' ,
393392 'stateOrProvinceName ' => 'ST ' ,
394393 'localityName ' => 'L ' ,
@@ -399,25 +398,14 @@ private function isStringValidUtf8(string $text): bool {
399398 'emailAddress ' => 'emailAddress ' ,
400399 ];
401400
402- $ order = [
403- 'countryName ' ,
404- 'stateOrProvinceName ' ,
405- 'localityName ' ,
406- 'organizationName ' ,
407- 'organizationalUnitName ' ,
408- 'commonName ' ,
409- 'description ' ,
410- 'emailAddress ' ,
411- ];
412-
413- $ parts = [];
414- foreach ($ order as $ field ) {
415- if (!empty ($ hints [$ field ])) {
416- $ abbr = $ map [$ field ];
417- $ val = addcslashes ($ hints [$ field ], '/+<>"#; ' );
418- $ parts [] = "$ abbr= $ val " ;
419- }
420- }
401+ $ parts = array_filter (
402+ array_map (
403+ fn ($ field ) => empty ($ hints [$ field ])
404+ ? null
405+ : $ mapping [$ field ] . '= ' . addcslashes ($ hints [$ field ], '/+<>"#; ' ),
406+ array_keys ($ mapping )
407+ )
408+ );
421409
422410 return '/ ' . implode ('/ ' , $ parts );
423411 }
@@ -434,33 +422,40 @@ private function bigToString($v): ?string {
434422 }
435423
436424 private function resolveHashAlgorithm (?string $ oid ): ?string {
437- if (!$ oid ) {
438- return null ;
439- }
440-
441- $ resolved = ASN1 ::getOID ($ oid );
442- if ($ resolved && $ resolved !== $ oid ) {
443- return match (strtolower ($ resolved )) {
444- 'sha1withrsaencryption ' , 'ecdsa-with-sha1 ' , 'id-dsa-with-sha1 ' => 'SHA-1 ' ,
445- 'sha224withrsaencryption ' , 'ecdsa-with-sha224 ' , 'id-dsa-with-sha224 ' => 'SHA-224 ' ,
446- 'sha256withrsaencryption ' , 'ecdsa-with-sha256 ' , 'id-dsa-with-sha256 ' => 'SHA-256 ' ,
447- 'sha384withrsaencryption ' , 'ecdsa-with-sha384 ' => 'SHA-384 ' ,
448- 'sha512withrsaencryption ' , 'ecdsa-with-sha512 ' => 'SHA-512 ' ,
449- 'md2withrsaencryption ' => 'MD2 ' ,
450- 'md5withrsaencryption ' => 'MD5 ' ,
451- default => strtoupper ($ resolved ),
452- };
453- }
454-
455425 return match ($ oid ) {
426+ null => null ,
456427 '1.3.14.3.2.26 ' => 'SHA-1 ' ,
457428 '2.16.840.1.101.3.4.2.4 ' => 'SHA-224 ' ,
458429 '2.16.840.1.101.3.4.2.1 ' => 'SHA-256 ' ,
459430 '2.16.840.1.101.3.4.2.2 ' => 'SHA-384 ' ,
460431 '2.16.840.1.101.3.4.2.3 ' => 'SHA-512 ' ,
461432 '1.2.840.113549.2.5 ' => 'MD5 ' ,
462433 '1.2.840.113549.2.2 ' => 'MD2 ' ,
463- default => $ oid ,
434+ 'id-sha1 ' , 'sha1withrsaencryption ' , 'ecdsa-with-sha1 ' , 'id-dsa-with-sha1 ' => 'SHA-1 ' ,
435+ 'id-sha224 ' , 'sha224withrsaencryption ' , 'ecdsa-with-sha224 ' , 'id-dsa-with-sha224 ' => 'SHA-224 ' ,
436+ 'id-sha256 ' , 'sha256withrsaencryption ' , 'ecdsa-with-sha256 ' , 'id-dsa-with-sha256 ' => 'SHA-256 ' ,
437+ 'id-sha384 ' , 'sha384withrsaencryption ' , 'ecdsa-with-sha384 ' => 'SHA-384 ' ,
438+ 'id-sha512 ' , 'sha512withrsaencryption ' , 'ecdsa-with-sha512 ' => 'SHA-512 ' ,
439+ 'md2 ' , 'md2withrsaencryption ' => 'MD2 ' ,
440+ 'md5 ' , 'md5withrsaencryption ' => 'MD5 ' ,
441+ default => $ oid , // Return original if not mapped
442+ };
443+ }
444+
445+ private function getNumericOid (?string $ oid ): ?string {
446+ if (!$ oid || preg_match ('/^\d+(\.\d+)*$/ ' , $ oid )) {
447+ return $ oid ;
448+ }
449+
450+ return match ($ oid ) {
451+ 'id-sha1 ' , 'sha1withrsaencryption ' , 'ecdsa-with-sha1 ' , 'id-dsa-with-sha1 ' => '1.3.14.3.2.26 ' ,
452+ 'id-sha224 ' , 'sha224withrsaencryption ' , 'ecdsa-with-sha224 ' , 'id-dsa-with-sha224 ' => '2.16.840.1.101.3.4.2.4 ' ,
453+ 'id-sha256 ' , 'sha256withrsaencryption ' , 'ecdsa-with-sha256 ' , 'id-dsa-with-sha256 ' => '2.16.840.1.101.3.4.2.1 ' ,
454+ 'id-sha384 ' , 'sha384withrsaencryption ' , 'ecdsa-with-sha384 ' => '2.16.840.1.101.3.4.2.2 ' ,
455+ 'id-sha512 ' , 'sha512withrsaencryption ' , 'ecdsa-with-sha512 ' => '2.16.840.1.101.3.4.2.3 ' ,
456+ 'md2 ' , 'md2withrsaencryption ' => '1.2.840.113549.2.2 ' ,
457+ 'md5 ' , 'md5withrsaencryption ' => '1.2.840.113549.2.5 ' ,
458+ default => $ oid , // Return original if not mapped
464459 };
465460 }
466461
0 commit comments