posts - 644, comments - 2003, trackbacks - 137

My Links

News

Raffaele Rialdi website

Su questo sito si trovano i miei articoli, esempi, snippet, tools, etc.

Archives

Post Categories

Image Galleries

Blogs

Links

Ricavare da codice l'identity di un certificato per WCF

SvcUtil permette di creare automaticamente la configurazione client di un servizio WCF.

Tra le cose che vengono create c'è l'encoding in Base64 della chiave pubblica del certificato usato dal server

   1: <identity>
   2:      <certificate encodedValue="AwAAAA ...." />
   3: </identity>

Naturalmente SvcUtil ricava questa informazione dai metadati.

Sfortunatamente ci sono casi in cui la configurazione del servizio è complessa e non si riesce in modo semplice ad abilitare l'endpoint dei metadati. Guardacaso mi è capitato e il certificato delle due macchine di sviluppo erano diverse.

Apparentemente la soluzione è semplice:

   1: private static string GetEncoded(X509Certificate2 cert)
   2: {
   3:     byte[] export = cert.Export(X509ContentType.SerializedCert);
   4:     string encoded = Convert.ToBase64String(export);
   5:     return encoded;
   6: }

Questo valore è corretto per molti certificati ma ha un side-effect pericolosissimo. Il metodo Export esporta il certificato nella sua interezza, cioè compresa la chiave privata, se presente. Quando viene fatto il deploy di un certificato nello store si può scegliere se installarlo con o senza chiave privata.

Questo significa che la funzione GetEncoded ricava la stessa stringa di SvcUtil per i certificati con sola chiave pubblica, ma dal momento in cui ci imbattiamo in un certificato con chiave privata esportiamo anche quella. Dare una chiave privata in giro equivale a dare una copia delle chiavi di casa a tutti quelli che incontriamo. Non è proprio quello che si dice una cosa saggia.

Così ho cercato di eliminare la chiave privata dal certificato prima di esportarla. Dopo un po' di tentativi andati a vuoto mi sono rivolto al buon amico Mario Fontana (anche se lo tiene nascosto bene, un po' di codice delle CAPICOM viene dalla sua tastiera) che mi ha fatto notare che il formato DER dei certificati si può ottenere semplicemente esportando con l'opzione "X509ContentType.Cert".

   1: byte[] der = certRaf.Export(X509ContentType.Cert); // solo public key

A noi però interessa la SerializedCert e quindi non ci resta che re-importare il certificato appena esportato con "Cert"

   1: private static X509Certificate2 ImportFromBlob(byte[] certBlob)
   2: {
   3:     X509Certificate2Collection certs = new X509Certificate2Collection();
   4:     certs.Import(certBlob);
   5:     X509Certificate2 imported = certs[0];
   6:     return imported;
   7: }
Ed infine richiamare la GetEncoded per ottenere la magica stringa che viene usata nella configurazione client di WCF.

Print | posted on martedì 12 maggio 2009 12:19 |

Feedback

Gravatar

# re: Ricavare da codice l'identity di un certificato per WCF

Grazie, utilissimo!! adesso lo provo subito... e dire che avevo adottato la "soluzione semplice"...
12/05/2009 13:04 | Babba
Gravatar

# re: Ricavare da codice l'identity di un certificato per WCF

Ciao!
Mi sono imbattuto nel medesimo problema, solo che me ne intendo un po' meno di te di WCF e certificati, quindi la soluzione che hai scritto non mi è chiarissima. Non capisco ad esempio cosa sia la variabile 'certRaf' sulla quale esegui Export. Inoltre non ho idea di dove piazzare ed usare le funzioni 'GetEncoded' e 'ImportFromBlob'. Spero tu abbia il tempo di darmi una risposta... Grazie in anticipo.
10/03/2010 11:43 | Emanuele Lotti
Gravatar

# re: Ricavare da codice l'identity di un certificato per WCF

Ciao Emanuele, la variabile certRaf è un certificato e quindi di tipo X509Certificate2.
In pratica in seguenza devi:
1. Creare un'istanza di X509Certificate2 che rappresenti il tuo certificato
2. Esportare il certificato con Export specificando il formato Cert
3. Re-importarlo con la ImportFromBlob. A questo punto ottieni un certificato dove la chiave privata è stata strippata via.
4. Chiamare la GetEncoded per ottenere la stringa da specificare nella configurazione WCF

Per controprova, confronta il risultato della GetEncoded con i due certificati:
A. usando la procedura che ho scritto qui sopra (così contiene solo la chiave pubblica)
B. passando direttamente il certificato del punto 1 alla GetEncoded (contiene chiave pubblica e privata ==> pericoloso)

La stringa B sarà più lunga della A. Entrambe funzionano in WCF, ma mettendo la B stai rivelando la tua chiave privata il che è estremamente pericoloso.

Spero di aver fatto chiarezza. Ciao
10/03/2010 17:40 | Raffaele Rialdi
Gravatar

# re: Ricavare da codice l'identity di un certificato per WCF

Prego:) Ci sono tanti modi per ottenere un X509Certificate2 a partire dai certificati installati.
Questo è un metodo che mi sono scritto per recuperare il certificato:
- StoreName è una enumerazione che indica da dove prendere il certificato. Esempio "My" sono il certificati personali di utente o macchina
- StoreLocation serve a specificare se vuoi un certificato utente o di macchina
- X509FindType serve a dire cos'è specificato in value
- value è un'attributo del certificato (sceglilo univoco) che ti permette di identificarlo con certezza.
Di solito si sceglie il certificato per SubjectName o meglio per Thumbprint
public static X509Certificate2 GetCertificate(StoreName name, StoreLocation location, X509FindType findType, object value)
{
X509Store store = new X509Store(name, location);
X509Certificate2Collection allCerts = null;
store.Open(OpenFlags.ReadOnly);

try
{
allCerts = store.Certificates;
X509Certificate2Collection foundCerts = allCerts.Find(findType, value, false);
if(foundCerts.Count > 1)
throw new Exception("There is more than one certificate with this search");
if(foundCerts.Count == 0)
return null;

return foundCerts[0];
}
finally
{
if(allCerts != null)
{
foreach(X509Certificate2 cert in allCerts)
cert.Reset();
}
store.Close();
}
}

Mentre lavori da codice tieniti aperto la management console sui certificati:
- fai partire mmc.exe
- File - add/remove snapin
- scegli "Certificates" e poi Add
- alla dialog che appare scegli My User oppure Computer (corrisponde allo StoreLocation) e poi Finish
- Sotto il ramo Personal-Certificates hai l'equivalente di dello Store "My"

buon lavoro ...
11/03/2010 10:48 | Raffaele Rialdi
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET