2016-02-24 32 views
16

Nel mio codice C# ho un oggetto X509Certificate2 che rappresenta un certificato SSL (da un archivio locale o da una richiesta HTTP riuscita su SSL). Il certificato è firmato con un certificato intermedio che forse è presente nel negozio locale, forse no, quindi usare X509Chain.Build() probabilmente non funzionerà.Come faccio a trovare a livello di programmazione quale certificato è stato utilizzato per firmare un determinato certificato?

Una foto di visualizzatore di certificato di Firefox (perché non ho ancora nessun codice utilizzabile):

enter image description here

In Dettagli, nella "Gerarchia Certificate", vedo questo:

  • DigiCert High Assurance EV Root CA
    • DigiCert SHA2 Extended Validation Server CA
      • github.com

mio oggetto rappresenta "github.com", la linea più basso della catena. Devo identificare a livello di codice la linea di mezzo ("DigiCert SHA2 Extended Validation Server CA").

Come faccio a sapere un'identificazione personale o qualcosa di equivalente che mi consenta di identificare il certificato utilizzato per firmare il mio certificato?

+4

Come hai [chiesto una meta domanda sulla chiusura] (http://meta.stackoverflow.com/q/317570/1364007), ti risponderò in dettaglio qui.Per favore concedimi qualche minuto per rispondere. –

+0

Ho [risposto] (http://meta.stackoverflow.com/a/317581/1364007). Scuse per il ritardo. –

+0

Cosa c'è di sbagliato nella proprietà 'IssuerName'? – erickson

risposta

10

In questo caso specifico (github.com), X509Chain.Build funzionerà, poiché il certificato di fine contiene informazioni sull'ubicazione del certificato di emittente (nell'estensione di accesso alle informazioni di autorità).

Ma a volte questo potrebbe non funzionare (ad esempio, con certificati Thawte, perché Thawte non fornisce informazioni esplicite sull'ubicazione del certificato dell'emittente). E se il certificato è installato nell'archivio certificati locale, non è possibile localizzare automaticamente l'emittente.

Opzione 1 - connessione SSL

Tuttavia, se si lavora con un certificato SSL e si può stabilire una sessione SSL, è possibile ottenere il certificato con l'aggiunta di un listener per la proprietà ServicePointManager.ServerCertificateValidationCallback: https://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.servercertificatevalidationcallback.aspx

RemoteCertificateValidationCallback delegato contiene diversi parametri, uno di questi è chain, che contiene una catena di certificati SSL restituita dal server. E se il server remoto contiene un certificato di emittente, verrà presentato nella collezione ChainElements. Questo oggetto di solito contiene diversi elementi:

-Leaf Certificate 
    -Issuer Certificate 
     -(Optional Issuer certs when available) 

Quindi, è necessario verificare due cose:

  1. Se ChainElements contiene almeno due elementi (ad esempio, certificato di foglie e proposto dell'emittente).
  2. Se il primo elemento della raccolta ChainElements non ha lo stato NotSignatureValid nella raccolta ChainelementStatus.

si potrebbe aggiungere il seguente pezzo di codice nel RemoteCertificateValidationCallback delegato di eseguire questi controlli:

X509Certificate2 issuer = null; 
if (
    chain.ChainElements.Count > 1 && 
    !chain.ChainElements[0].ChainElementStatus.Any(x => x.Status == X509ChainStatusFlags.NotSignatureValid)) { 
    issuer = chain.ChainElements[1].Certificate; 
} 

Se dopo l'esecuzione di questo pezzo di codice variabile issuer è null, allora non è possibile determinare automaticamente chi è l'emittente del certificato. Questo processo richiederà alcune ricerche aggiuntive. E non è null, quindi la variabile issuer manterrà il certificato di emissione effettivo.

Opzione 2 - ricerca certificati locale

Ok, secondo i vostri commenti, si vuole determinare se il certificato emittente è installato nell'archivio certificati locale o no. Leggendo la tua domanda non l'ho capito. Perché dovremmo indovinare cosa stai guardando? Alla fine, sono ancora insicuro se sai/capisci cosa vuoi ottenere.

Se si vuole trovare se l'emittente è installato nel negozio locale, è possibile utilizzare il seguente algoritmo:

1) utilizzare X509Certificate2Collection.Find metodo e trovare i certificati candidati con il loro nome soggetto

2) trovare nell'elenco dei candidati (recuperato nel passaggio 1) quelli con valore Identificatore chiave soggetto identico al valore Identificatore chiave autorizzazione del certificato nell'oggetto.

X509Certificate2Collection certs = new X509Certificate2Collection(); 
// grab candidates from CA and Root stores 
foreach (var storeName in new[] { StoreName.CertificateAuthority, StoreName.Root }) { 
    X509Store store = new X509Store(storeName, StoreLocation.CurrentUser); 
    store.Open(OpenFlags.ReadOnly); 
    certs.AddRange(store.Certificates); 
    store.Close(); 
} 
certs = certs.Find(X509FindType.FindBySubjectDistinguishedName, cert.Issuer, false); 
if (certs.Count == 0) { 
    Console.WriteLine("Issuer is not installed in the local certificate store."); 
    return; 
} 
var aki = cert.Extensions["2.5.29.35"]; 
if (aki == null) { 
    Console.WriteLine("Issuer candidates: "); 
    foreach (var candidate in certs) { 
     Console.WriteLine(candidate.Thumbprint); 
    } 
    return; 
} 
var match = Regex.Match(aki.Format(false), "KeyID=(.+)", RegexOptions.IgnoreCase); 
if (match.Success) { 
    var keyid = match.Groups[1].Value.Replace(" ", null).ToUpper(); 
    Console.WriteLine("Issuer candidates: "); 
    foreach (var candidate in certs.Find(X509FindType.FindBySubjectKeyIdentifier, keyid, false)) { 
     Console.WriteLine(candidate.Thumbprint); 
    } 
} else { 
    // if KeyID is not presented in the AKI extension, attempt to get serial number from AKI: 
    match = Regex.Match(aki.Format(false), "Certificate SerialNumber=(.+)", RegexOptions.IgnoreCase); 
    var serial = match.Groups[1].Value.Replace(" ", null); 
    Console.WriteLine("Issuer candidates: "); 
    foreach (var candidate in certs.Find(X509FindType.FindBySerialNumber, serial, false)) { 
     Console.WriteLine(candidate.Thumbprint); 
    } 
} 

supponendo che cert variabile memorizza certificato nel soggetto (per cui l'emittente viene ricercato). Questo approccio presenta problemi poiché non convalida la firma e può restituire falsi positivi.

2

Ho chiesto "Cosa c'è che non va nella proprietà IssuerName?"

La risposta è stata: "Niente tranne che non deve corrispondere al soggetto del certificato firmatario e anche se corrisponde non c'è modo di sapere se è esattamente il certificato giusto o solo qualche certificato con lo stesso soggetto."

Questo non è corretto.

PKIX § 6.1 dice "per tutti x in {1, ..., n-1}, l'oggetto del certificato x è l'emittente del certificato x + 1."

Le sottosezioni chiariscono questo suggerendo, "Assegna il nome del soggetto del certificato a working_issuer_name," e quindi, per il prossimo certificato nella catena, verificando che il "nome emittente del certificato di & hellip; è il working_issuer_name."

Potresti essere confuso perché potresti avere molti certificati di emittente con lo stesso nome, ma chiavi diverse. In tal caso, la chiave di firma corretta può essere identificata facendo corrispondere l'emittente subject key identifier all'identificatore di chiave dell'autorità del soggetto. Gli identificatori di chiavi corrispondenti non sono tuttavia sufficienti: i nomi devono corrispondere per primi.

Non è chiaro il motivo per cui si sta tentando di farlo manualmente. Dovresti essere in grado di provide all available intermediates per il tuo path-building library, e lasciare trovare una catena valida se esiste.

+0

La proprietà 'ExtraStore' è utile se si dispone del certificato giusto, che non è sempre il caso. Come accennato con Thawte, i client non hanno bisogno del certificato intermedio Thawte CA per funzionare correttamente nel browser Web, poiché questo certificato viene consegnato dal server Web e viene eliminato dopo la convalida del certificato. – Crypt32

+0

@CryptoGuy Non vedo alcuna rilevanza nel tuo commento. Se il server ti invia l'intermedio, lo hai e puoi metterlo in 'ExtraStore'. Che sia il certificato "giusto" è qualcosa che testate per convalida. Puoi chiarire il tuo punto in modo più chiaro? – erickson

+0

Ho modificato la mia risposta aggiungendo del codice e chiarito il mio punto. Spero che questo sia chiaro. – Crypt32