2012-02-14 20 views
16

In poche parole, questo è il mio problema:Uso Base64 codificati chiave pubblica per verificare RSA firma

private string publicKeyString = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVGUzbydMZS+fnkGTsUkDKEyFOGwghR234d5GjPnMIC0RFtXtw2tdcNM8I9Qk+h6fnPHiA7r27iHBfdxTP3oegQJWpbY2RMwSmOs02eQqpKx4QtIjWqkKk2Gmck5cll9GCoI8AUAA5e0D02T0ZgINDmo5yGPhGAAmqYrm8YiupwQIDAQAB"; 

/* Some transformation required, using publicKeyString to initiate a new RSACryptoServiceProvider object 
*/ 

//for now: 
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 

byte[] selfComputedHash = new byte[]; //left out of the example 
byte[] signature = new byte[]; 

bool result = rsa.VerifyHash(selfComputedHash, CryptoConfig.MapNameToOID("SHA1"), signature); 

Come si può vedere, il problema sta avviando una nuova RSACryptoServiceProvider con la stringa chiave pubblica dato codifica Base64. Sono stato in grado di eseguire l'istanziazione utilizzando un oggetto RSAParameters, caricato con il byte [] per Modulus ed Exponent derivato da questa stringa di chiave pubblica utilizzando un comando di shell OpenSSL. Ma dal momento che questa chiave pubblica potrebbe cambiare in futuro, voglio essere in grado di memorizzarla nella sua forma originale in un database. Ci deve essere un modo più diretto per affrontare questo.

Molti degli esempi che ho letto finora evitano questo problema esportando e importando le chiavi private e pubbliche generate da e verso un oggetto portachiavi e utilizzandolo nello stesso pezzo di codice e quindi non "trasferendo" 'la chiave in qualche forma di stringa dalla memoria. Alcune persone hanno espresso lo stesso problema, sia qui su StackOverflow che su altri siti, ma non sono ancora riuscito a trovare una risposta soddisfacente.

Qualsiasi idea è più che benvenuta.

Informazioni di base: Il mio partner di comunicazione calcola un SHA1-hash da 20 byte da una stringa di input di lunghezza variabile, composta dalle informazioni contenute in diversi campi di un messaggio codificato ASCII. Questo hash viene quindi firmato con RSA con la chiave privata del mio partner e inviato insieme al messaggio ASCII. All'arrivo, computo da solo l'hash SHA1, utilizzando gli stessi campi del messaggio ASCII, quindi provo a verificare se questi campi non sono stati modificati chiamando VerifyHash.

La chiave è fornita in 2 forme: normale e 'noNL'. La versione noNL è incluso nel codice sopra, la versione normale è questo:

-----BEGIN PUBLIC KEY----- 
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVGUzbydMZS+fnkGTsUkDKEyFO 
GwghR234d5GjPnMIC0RFtXtw2tdcNM8I9Qk+h6fnPHiA7r27iHBfdxTP3oegQJWp 
bY2RMwSmOs02eQqpKx4QtIjWqkKk2Gmck5cll9GCoI8AUAA5e0D02T0ZgINDmo5y 
GPhGAAmqYrm8YiupwQIDAQAB 
-----END PUBLIC KEY----- 

risposta

13

La stringa è la codifica base64 di un SubjectPublicKeyInfo. È possibile utilizzare Bouncycastle.net per decodificare in questo modo:

byte[] publicKeyBytes = Convert.FromBase64String(publicKeyString); 
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKeyBytes); 
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters) asymmetricKeyParameter; 
RSAParameters rsaParameters = new RSAParameters(); 
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(); 
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned(); 
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
rsa.ImportParameters(rsaParameters); 
+0

Grazie, è abbastanza facile. Analizzerò la libreria BouncyCastle, ma sei sicuro che non ci sia modo di farlo utilizzando solo la libreria di crittografia in .NET stessa? – jscheppers

+0

@jscheppers: puoi farlo manualmente come suggerito da poupou, ma non esiste un supporto diretto per il formato in .NET. Una rapida ricerca rivela questo esempio per la decodifica manuale: http://www.jensign.com/JavaScience/dotnet/pempublic/pempublic.cs, ma sembra un po 'disordinato. –

+0

Penso che preferisco la tua soluzione BouncyCastle quindi;) L'ho già provato nella mia attuale implementazione e funziona. Segnerò il tuo post come risposta! – jscheppers

4

Prima base64 è solo una codifica di alcuni dati binari. C'è più di un modo per codificare, in binario, una chiave pubblica RSA. Tuttavia, se si ottiene questo da OpenSSL è probabile una struttura codificata DER RSAPublicKey.

RSAPublicKey ::= SEQUENCE { 
    modulus   INTEGER, -- n 
    publicExponent  INTEGER } -- e 

In generale si avrebbe bisogno di un decoder ASN.1, Mono.Security.dll fornisce one, ma per una struttura così semplice si potrebbe desiderare di farlo a mano dal ASN.1 è fondamentalmente un Tag, Lunghezza e a Valore.

+0

L'organizzazione che fornisce le chiavi pubbliche non menziona il formato della chiave, ma visto che sono in grado di 'leggere' loro con OpenSSL e poiché la chiave contiene entrambi i ----- BEGIN PUBLIC KEY ----- e ----- END PUBLIC KEY ----- tag (aggiungerò questo al mio post originale) Sono abbastanza certo che si tratta di una codifica DER chiave. Come procederesti a convertire questa stringa in una struttura ASN.1 a mano? Quando i byte entrano in gioco, non sono così sicuro di me :) – jscheppers

+1

Si noti che in realtà è un oggetto "SubjectPublicKeyInfo' * contenente * un 'RSAPublicKey'. –

0

Se il tuo partner utilizza anche .NET, può derivare da Mono's makecert https://github.com/mono/mono/blob/bf55818da11240bd108dc51b374fae3d3b5482ce/mcs/tools/security/makecert.cs per generare file di certificato e inviarti direttamente.

In tal caso, si può facilmente caricare certificati anziché di byte grezzi,

http://www.lextm.com/2012/02/simple-publicprivate-key-signing-sample-code/

+0

Questa sarebbe una buona possibilità, ma non ho alcun controllo sul modo in cui i tasti sono generati. Le chiavi sono fornite così come sono, nessun certificato è disponibile dove questa chiave è presente. – jscheppers

Problemi correlati