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