2009-05-10 7 views
5

Ho una funzione che genera una coppia di chiavi RSA BouncyCastle. Devo crittografare la chiave privata e quindi archiviare le chiavi private e pubbliche crittografate in campi separati del database SQL2008.Crittografia di una chiave RSA BouncyCastle Accoppiamento e memorizzazione in un database SQL2008

Sto usando il seguente per ottenere la coppia di chiavi:

private static AsymmetricCipherKeyPair createASymRandomCipher() 
{ 
    RsaKeyPairGenerator r = new RsaKeyPairGenerator(); 
    r.Init(new KeyGenerationParameters(new SecureRandom(), 1024)); 
    AsymmetricCipherKeyPair keys = r.GenerateKeyPair(); 
    return keys; 
} 

Questo sta tornando le chiavi bene, ma io non sono sicuro di come posso quindi crittografare la chiave privata e, successivamente, memorizzare nel database.

Questo è ciò Attualmente sto usando il crittografare i dati in modo non corretto (?):

public static byte[] encBytes2(AsymmetricKeyParameter keyParam, byte[] Key, byte[] IV) 
{ 
    MemoryStream ms = new MemoryStream(); 
    Rijndael rjdAlg = Rijndael.Create(); 
    rjdAlg.Key = Key; 
    rjdAlg.IV = IV; 
    CryptoStream cs = new CryptoStream(ms, rjdAlg.CreateEncryptor(), CryptoStreamMode.Write); 
    byte[] keyBytes = System.Text.Encoding.Unicode.GetBytes(keyParam.ToString()); 
    cs.Write(keyBytes, 0, keyBytes.Length); 
    cs.Close(); 
    byte[] encryptedData = ms.ToArray(); 
    return encryptedData; 
} 

Ovviamente le keyBytes ambiente dove sto convertendo keyParam.ToString() non è corretto in quanto converte solo il nome KeyParameter , non il valore reale. Sto inoltrando a questa funzione la precedente coppia di chiavi di restituzione delle chiavi. Privato.

L'altra domanda è che non sto crittografando la chiave pubblica quale formato dovrei memorizzare nel database SQL2008, nvarchar (256) o altro?

Qualsiasi aiuto sarebbe molto apprezzato.

+0

Qualcuno ha qualche idea su questo? – TravisPUK

risposta

16

Per motivi che dovrebbero essere chiari, la serializzazione predefinita (e forse involontaria) non funziona bene con le chiavi private che dovrebbero essere scritte solo in situazioni molto limitate.

BouncyCastle ha il supporto per PKCS # 8, che è lo standard pertinente per la "serializzazione" di chiavi private. Esistono strutture ASN.1 denominate PrivateKeyInfo e EncryptedPrivateKeyInfo. Poiché sono in ASN.1, esistono metodi standard per serializzarli/deserializzarli. Come suggerisce il nome, uno memorizza la chiave in chiaro, l'altra cifra la chiave in base a una password.

Per le chiavi pubbliche: queste non sarebbero ordinariamente crittografate. BC supporta il formato standard X.509 di SubjectPublicKeyInfo per serializzarli.

In C# compilazione, le classi di alto livello a guardare sarebbe:

  • Org.BouncyCastle.Security.PrivateKeyFactory
  • Org.BouncyCastle.Security.PublicKeyFactory
  • Org.BouncyCastle. Pkcs.EncryptedPrivateKeyInfoFactory
  • Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory
  • Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory
+0

Peter, grazie per il tuo commento. Lo esaminerò. – TravisPUK

+1

Penso che questo commento sia in realtà la risposta corretta a questa domanda. C'è un motivo per cui la classe AsymmetricKeyParameter non è serializzabile. La soluzione delineata nella risposta selezionata è un problema di sicurezza in attesa di accadere. – Henrik

+0

Ricevo un errore: tentare di utilizzare l'algoritmo non PBE con la generazione PBE EncryptedPrivateKeyInfo - Qualcuno sa quale algoritmo dovrei usare? Attualmente sto usando PBEWithSHAAnd3KeyTripleDES – daveBM

1

Per quanto riguarda la seconda parte della domanda, il tipo di dati da utilizzare per la memorizzazione della chiave è VARBINARY (256).

Torna alla prima parte della domanda, in realtà hai la possibilità di avere SQL Server per gestire la crittografia per te. Certo, se vorreste o meno fare questo sarebbe una questione di quali siano i vostri requisiti applicativi, ma ci andrò sopra nel caso in cui sia un'opzione.

Saremo piuttosto semplici qui e usiamo solo le chiavi simmetriche e Triple-DES.

Innanzitutto, il database ha una chiave master che viene utilizzata per proteggere i certificati e le chiavi asimmetriche. La chiave master è crittografata con Triple-DES.

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'supersecretpassword' 

SQL Server 2005/2008 in grado di generare i propri certificati X.509 utilizzati per proteggere le chiavi utilizzate per crittografare i dati effettivi.

CREATE CERTIFICATE ExampleCertificate 
    WITH SUBJECT = 'thisisjustsomemetadata' 

Ci sono un sacco di opzioni per la crittografia chiavi simmetriche (certificati, password, altre chiavi), così come molti algoritmi supportati. Ma per questo esempio, useremo il nostro certificato.

CREATE SYMMETRIC KEY ExampleKey 
    WITH ALGORITHM = TRIPLE_DES 
    ENCRYPTION BY CERTIFICATE EncryptTestCert 

La chiave deve essere decrittografata utilizzando lo stesso metodo con cui è stata crittografata. Nel nostro caso, questo sarebbe il certificato che abbiamo creato.

DECLARE @Value VARCHAR(50) 
SET @Value = 'supersecretdata!' 

OPEN SYMMETRIC KEY ExampleKey DECRYPTION BY CERTIFICATE ExampleCertificate 
    UPDATE SomeTable 
    SET SomeColumn = ENCRYPTBYKEY(KEY_GUID('ExampleKey'), @Value) 

La decrittografia è altrettanto semplice.

OPEN SYMMETRIC KEY ExampleKey DECRYPTION BY CERTIFICATE ExampleCertificate 
    SELECT CONVERT(VARCHAR(50),DECRYPTBYKEY(SomeColumn)) AS DecryptedData 
    FROM SomeTable 

Speriamo che questo risolto il problema, o almeno si apre fino a soluzioni alternative (anche se qualcuno che ha avuto esperienza fare crittografia in C# app potrebbe probabilmente trovare il guasto nel vostro codice di cui sopra).Se si dispone di requisiti che richiedono che i dati non possano nemmeno andare oltre il collegamento a SQL Server in testo semplice, ovviamente questo è un no-go (beh, si può effettivamente creare connessioni SSL a SQL Server ...).

+0

Pigro: Grazie per la risposta. Ho ricordato che SQL2008 poteva fare la propria crittografia, ma non ne sapeva nulla, grazie per quell'informazione. La funzione che sto costruendo deve fare la crittografia e la decrittografia sul client in base alle chiavi dell'operatore, quindi non posso inviare il testo chiaro attraverso il filo. Quindi, questo non ha risolto il mio problema, ma certamente ha aperto gli occhi su alcune alternative. ;) – TravisPUK

+0

Oh, grazie anche per il suggerimento varbinary (256). – TravisPUK

11

Finché l'oggetto è contrassegnato come serializzabile, un modo per convertire un oggetto in un array di byte consiste nell'utilizzare la classe BinaryFormatter in .Net.

Sarà necessario aggiungere questo using al file di codice:

using System.Runtime.Serialization.Formatters.Binary; 

Un formattatore binario può produrre la classe in un flusso. Poiché si intende convertire l'oggetto in un array di byte, è possibile utilizzare System.IO.MemoryStream come memoria temporanea.

MemoryStream memStream = new MemoryStream(); 

È quindi possibile creare un nuovo formattatore binario.

BinaryFormatter formatter = new BinarryFomatter(); 

e utilizzare questo per serializzare l'oggetto.

formatter.Serialize(memStream, someObject); 

Per ottenere i byte che è possibile utilizzare:

return memStream.ToArray(); 

per deserializzare l'array di byte è necessario scrivere i byte a un flusso di memoria.

memStream.Write(arrBytes, 0, arrBytes.Length); 

Ritorno all'inizio del flusso.

memStream.Seek(0, SeekOrigin.Begin); 

Quindi utilizzare il formattatore per ricreare l'oggetto.

Object obj = (Object)formatter.Deserialize(memStream); 

Se si sta già utilizzando le funzioni di crittografia si dovrebbe essere in grado di crittografare l'array di byte creato abbastanza facilmente prima di riporla nel database.

Speriamo che questo ti aiuti nella giusta direzione. Se sei fortunato, gli oggetti BouncyCastle saranno contrassegnati come serializzabili, in caso contrario avrai bisogno di un po 'di codice aggiuntivo. Più tardi, avrò la possibilità di guardare i librerie di BouncyCastle per poterlo testare e pubblicherò altro codice se necessario.


... Non ho mai usato BouncyCastle prima. Dopo alcuni test, sembra che gli oggetti chiave pubblici e privati ​​non siano serializzabili, quindi sarà necessario convertire questi oggetti in qualcosa che sia!

Sembra che le chiavi pubbliche e private espongano le proprietà come vari valori BouncyCastle.Math.BigInteger. (Le chiavi possono anche essere costruite da questi BigIntegers). Inoltre, BigIntegers ha una funzione ToByteArray() e può anche essere costruita da una matrice di byte. Molto utile ..

Sapendo che è possibile suddividere ogni chiave in BigIntegers e questi a sua volta in un array di byte e che è possibile anche il contrario, è un modo per archiviarli tutti in un oggetto serializzabile. Ad esempio, una struttura o una classe semplice farebbe

[Serializable] 
private struct CipherPrivateKey 
{ 
    public byte[] modulus; 
    public byte[] publicExponent; 
    public byte[] privateExponent; 
    public byte[] p; 
    public byte[] q; 
    public byte[] dP; 
    public byte[] dQ; 
    public byte[] qInv; 
} 

[Serializable] 
private struct CipherPublicKey 
{ 
    public bool isPrivate; 
    public byte[] modulus; 
    public byte[] exponent; 
} 

Questo ci dà un paio di oggetti serializzabili facili da usare.

AsymmetricCipherKeyPair espone le chiavi pubbliche e private come oggetti AsymmetricKeyParameter. Per ottenere le proprietà più dettagliate è necessario lanciare questi per il seguente:

keyPair.Public a BouncyCastle.Crypto.Parameters.RsaKeyParameters keyPair.Private a BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters

Il seguente funzioni convertirà questi ai struct a dichiarato in precedenza:

private static CipherPublicKey getCipherPublicKey(Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters cPublic) 
{ 
    CipherPublicKey cpub = new CipherPublicKey(); 
    cpub.modulus = cPublic.Modulus.ToByteArray(); 
    cpub.exponent = cPublic.Exponent.ToByteArray(); 
    return cpub; 
} 
private static CipherPrivateKey getCipherPrivateKey(Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters cPrivate) 
{ 
    CipherPrivateKey cpri = new CipherPrivateKey(); 
    cpri.dP = cPrivate.DP.ToByteArray(); 
    cpri.dQ = cPrivate.DQ.ToByteArray(); 
    cpri.modulus = cPrivate.Modulus.ToByteArray(); 
    cpri.p = cPrivate.P.ToByteArray(); 
    cpri.privateExponent = cPrivate.Exponent.ToByteArray(); 
    cpri.publicExponent = cPrivate.PublicExponent.ToByteArray(); 
    cpri.q = cPrivate.Q.ToByteArray(); 
    cpri.qInv = cPrivate.QInv.ToByteArray(); 
    return cpri; 
} 

Utilizzando il formattatore binario accennato in precedenza, siamo in grado di convertire gli oggetti serializzabili abbiamo appena creato a un array di byte.

CipherPublicKey cpub = getCipherPublicKey((Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)keypair.Public); 
MemoryStream memStream = new MemoryStream(); 
BinaryFormatter formatter = new BinarryFomatter(); 
formatter.Serialize(memStream, cpub); 
return memStream.ToArray(); 

La desesiizzazione è quindi solo l'inversa come descritto in precedenza. Una volta che le strutture pubbliche o private sono state deserializzate, puoi utilizzare i contrasti di BouncyCastle per ricreare le chiavi. Queste funzioni lo dimostrano.

private static Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters recreateASymCipherPublicKey(CipherPublicKey cPublicKey) 
{ 
    Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters key; 
    key = new Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters(
      cPublicKey.isPrivate, 
      createBigInteger(cPublicKey.modulus), 
      createBigInteger(cPublicKey.exponent)); 
    return key; 
} 

private static Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters recreateASymCipherPrivateKey(CipherPrivateKey cPrivateKey) 
{ 
    Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters key; 
    key = new Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters(
      createBigInteger(cPrivateKey.modulus), 
      createBigInteger(cPrivateKey.publicExponent), 
      createBigInteger(cPrivateKey.privateExponent), 
      createBigInteger(cPrivateKey.p), 
      createBigInteger(cPrivateKey.q), 
      createBigInteger(cPrivateKey.dP), 
      createBigInteger(cPrivateKey.dQ), 
      createBigInteger(cPrivateKey.qInv)); 
    return key; 
} 

Se è necessario ricreare la coppia di chiavi originale per qualsiasi motivo:

AsymmetricKeyParameter publ = (AsymmetricKeyParameter)recreateASymCipherPublicKey(cKeyPair.publicKey); 
AsymmetricKeyParameter priv = (AsymmetricKeyParameter)recreateASymCipherPrivateKey(cKeyPair.privateKey); 
AsymmetricCipherKeyPair keyPair = new AsymmetricCipherKeyPair(publ, priv); 

Speriamo che tutto ha un senso! I campioni di codice dovrebbero aiutarti a raggiungere la tua strada.

+0

@James, grazie per questo. Certamente mi metterà nella giusta direzione. Per quanto riguarda gli oggetti BouncyCastle essendo serializzabili, non ne sono sicuro. Investirò anche su questo. Sto già utilizzando le funzioni di crittografia, quindi proverò ad applicarlo. – TravisPUK

+0

Hey cool, sembra che faccia bene il trucco. Grazie James, apprezzo molto l'aiuto! – TravisPUK

+0

Mentre la mia risposta mostra che può essere fatto non significa che dovrebbe essere fatto, il suggerimento di Peter è molto migliore e, come suggerisce Henrik, la serializzazione in questo modo potrebbe causare problemi di sicurezza. – JamPickle

6

L'approccio corretto è utilizzare il suggerimento di Peters.

ho incluso un piccolo codice C# di esempio:

var keyPair = GetKeypair(); 

PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);       
byte[] serializedKey = privateKeyInfo.ToAsn1Object().GetDerEncoded(); 

AsymmetricKeyParameter deserializedKey1 = PrivateKeyFactory.CreateKey(serializedKey); 
Assert.AreEqual(keyPair.Private, deserializedKey1); 

AsymmetricKeyParameter deserializedKey2 = PrivateKeyFactory.CreateKey(privateKeyInfo);    
Assert.AreEqual(keyPair.Private, deserializedKey2); 

L'esempio utilizza l'API Castello gonfiabile. Nota che l'esempio NON cripta la chiave. Il metodo CreatePrivateKeyInfo è sovraccarico per consentire l'uso di una password come protezione della chiave.

+0

Grazie Henrik, lo esaminerò. – TravisPUK

+0

Grazie per l'esempio esplicito - stavo cercando 'PrivateKeyFactory.CreateKey (byte [])' per sempre! –