@@ -56,6 +56,16 @@ internal static IReadOnlyList<SignatureLog> X509Certificate2ToLogMessages(X509Ce
5656 issues . Add ( SignatureLog . InformationLog ( $ "{ indentation } { string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateIssuer , cert . IssuerName . Name ) } ") ) ;
5757 issues . Add ( SignatureLog . MinimalLog ( $ "{ indentation } { string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateValidity , cert . NotBefore , cert . NotAfter ) } ") ) ;
5858
59+ foreach ( string url in GetCrlDistributionPointUrls ( cert ) )
60+ {
61+ issues . Add ( SignatureLog . InformationLog ( $ "{ indentation } { string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateCrlUrl , url ) } ") ) ;
62+ }
63+
64+ foreach ( string url in GetOcspUrls ( cert ) )
65+ {
66+ issues . Add ( SignatureLog . InformationLog ( $ "{ indentation } { string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateOcspUrl , url ) } ") ) ;
67+ }
68+
5969 return issues ;
6070 }
6171
@@ -68,6 +78,16 @@ private static void X509Certificate2ToString(X509Certificate2 cert, StringBuilde
6878 certStringBuilder . AppendLine ( indentation + string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateHash , fingerprintAlgorithm . ToString ( ) , certificateFingerprint ) ) ;
6979 certStringBuilder . AppendLine ( indentation + string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateIssuer , cert . IssuerName . Name ) ) ;
7080 certStringBuilder . AppendLine ( indentation + string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateValidity , cert . NotBefore , cert . NotAfter ) ) ;
81+
82+ foreach ( string url in GetCrlDistributionPointUrls ( cert ) )
83+ {
84+ certStringBuilder . AppendLine ( indentation + string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateCrlUrl , url ) ) ;
85+ }
86+
87+ foreach ( string url in GetOcspUrls ( cert ) )
88+ {
89+ certStringBuilder . AppendLine ( indentation + string . Format ( CultureInfo . CurrentCulture , Strings . CertUtilityCertificateOcspUrl , url ) ) ;
90+ }
7191 }
7292
7393 /// <summary>
@@ -447,5 +467,117 @@ private static bool IsHex(string certificateFingerprint)
447467
448468 return true ;
449469 }
470+
471+ /// <summary>
472+ /// Extracts CRL Distribution Point URLs from the certificate's CRL Distribution Points extension (OID 2.5.29.31).
473+ /// </summary>
474+ internal static IReadOnlyList < string > GetCrlDistributionPointUrls ( X509Certificate2 cert )
475+ {
476+ const string CrlDistributionPointsOid = "2.5.29.31" ;
477+ // context-specific primitive tag [6] for uniformResourceIdentifier in GeneralName
478+ const byte GeneralNameUriTag = 0x86 ;
479+
480+ var urls = new List < string > ( ) ;
481+ var extension = cert . Extensions [ CrlDistributionPointsOid ] ;
482+
483+ if ( extension == null )
484+ {
485+ return urls ;
486+ }
487+
488+ try
489+ {
490+ // CRLDistributionPoints ::= SEQUENCE OF DistributionPoint
491+ var reader = new DerEncoding . DerSequenceReader ( extension . RawData ) ;
492+
493+ while ( reader . HasData )
494+ {
495+ // DistributionPoint ::= SEQUENCE { distributionPoint [0] ... }
496+ var dpReader = reader . ReadSequence ( ) ;
497+
498+ if ( dpReader . HasData && dpReader . HasTag ( DerEncoding . DerSequenceReader . ContextSpecificConstructedTag0 ) )
499+ {
500+ // distributionPoint [0] CONSTRUCTED
501+ byte [ ] dpNameData = dpReader . ReadValue ( ( DerEncoding . DerSequenceReader . DerTag ) DerEncoding . DerSequenceReader . ContextSpecificConstructedTag0 ) ;
502+ var dpNameReader = DerEncoding . DerSequenceReader . CreateForPayload ( dpNameData ) ;
503+
504+ if ( dpNameReader . HasData && dpNameReader . HasTag ( DerEncoding . DerSequenceReader . ContextSpecificConstructedTag0 ) )
505+ {
506+ // fullName [0] CONSTRUCTED = GeneralNames
507+ byte [ ] fullNameData = dpNameReader . ReadValue ( ( DerEncoding . DerSequenceReader . DerTag ) DerEncoding . DerSequenceReader . ContextSpecificConstructedTag0 ) ;
508+ var gnReader = DerEncoding . DerSequenceReader . CreateForPayload ( fullNameData ) ;
509+
510+ while ( gnReader . HasData )
511+ {
512+ byte tag = gnReader . PeekTag ( ) ;
513+
514+ if ( tag == GeneralNameUriTag )
515+ {
516+ byte [ ] uriBytes = gnReader . ReadValue ( ( DerEncoding . DerSequenceReader . DerTag ) GeneralNameUriTag ) ;
517+ urls . Add ( Encoding . ASCII . GetString ( uriBytes ) ) ;
518+ }
519+ else
520+ {
521+ gnReader . SkipValue ( ) ;
522+ }
523+ }
524+ }
525+ }
526+ }
527+ }
528+ catch ( System . Security . Cryptography . CryptographicException )
529+ {
530+ }
531+
532+ return urls ;
533+ }
534+
535+ /// <summary>
536+ /// Extracts OCSP responder URLs from the certificate's Authority Information Access extension (OID 1.3.6.1.5.5.7.1.1).
537+ /// </summary>
538+ internal static IReadOnlyList < string > GetOcspUrls ( X509Certificate2 cert )
539+ {
540+ const string AuthorityInfoAccessOid = "1.3.6.1.5.5.7.1.1" ;
541+ const string OcspAccessMethodOid = "1.3.6.1.5.5.7.48.1" ;
542+ // context-specific primitive tag [6] for uniformResourceIdentifier in GeneralName
543+ const byte GeneralNameUriTag = 0x86 ;
544+
545+ var urls = new List < string > ( ) ;
546+ var extension = cert . Extensions [ AuthorityInfoAccessOid ] ;
547+
548+ if ( extension == null )
549+ {
550+ return urls ;
551+ }
552+
553+ try
554+ {
555+ // AuthorityInfoAccessSyntax ::= SEQUENCE OF AccessDescription
556+ var reader = new DerEncoding . DerSequenceReader ( extension . RawData ) ;
557+
558+ while ( reader . HasData )
559+ {
560+ // AccessDescription ::= SEQUENCE { accessMethod OID, accessLocation GeneralName }
561+ var adReader = reader . ReadSequence ( ) ;
562+ string oid = adReader . ReadOidAsString ( ) ;
563+
564+ if ( string . Equals ( oid , OcspAccessMethodOid , StringComparison . Ordinal ) && adReader . HasData )
565+ {
566+ byte tag = adReader . PeekTag ( ) ;
567+
568+ if ( tag == GeneralNameUriTag )
569+ {
570+ byte [ ] uriBytes = adReader . ReadValue ( ( DerEncoding . DerSequenceReader . DerTag ) GeneralNameUriTag ) ;
571+ urls . Add ( Encoding . ASCII . GetString ( uriBytes ) ) ;
572+ }
573+ }
574+ }
575+ }
576+ catch ( System . Security . Cryptography . CryptographicException )
577+ {
578+ }
579+
580+ return urls ;
581+ }
450582 }
451583}
0 commit comments