Domanda

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 di database SQL2008 separati.

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 restituendo le chiavi bene, ma non sono sicuro di come posso quindi crittografare la chiave privata e successivamente memorizzarla nel database.

Questo è ciò che sto attualmente utilizzando per crittografare i dati (in modo errato?):

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 l'impostazione keyBytes in cui sto convertendo keyParam.ToString () non è corretta in quanto converte solo il nome KeyParameter, non il valore effettivo. Sto sottoponendo a questa funzione la precedente coppia di chiavi return of keys.Private.

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

Qualsiasi aiuto sarebbe molto apprezzato.

È stato utile?

Soluzione

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 " serializzazione " chiavi private. Esistono strutture ASN.1 chiamate PrivateKeyInfo e EncryptedPrivateKeyInfo. Dal momento che sono in ASN.1 ci sono modi standard per serializzare / deserializzarli. Come suggerisce il nome, uno memorizza la chiave in testo normale, l'altro crittografa la chiave in base a una password.

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

Nella build C #, le classi di alto livello da guardare sarebbero:

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

Altri suggerimenti

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

Dovrai aggiungere questa istruzione usando il tuo file di codice:

using System.Runtime.Serialization.Formatters.Binary;

Un formattatore binario può generare la tua classe su un flusso. Quando 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 usalo per serializzare il tuo oggetto.

formatter.Serialize(memStream, someObject);

Per ottenere i byte puoi usare:

return memStream.ToArray();

Per deserializzare l'array di byte è necessario scrivere i byte in un flusso di memoria.

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

Torna all'inizio dello stream.

memStream.Seek(0, SeekOrigin.Begin);

Quindi usa il formattatore per ricreare l'oggetto.

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

Se stai già utilizzando le funzioni di crittografia, dovresti riuscire a crittografare l'array di byte creato abbastanza facilmente prima di memorizzarlo nel database.

Speriamo che ti possa aiutare nella giusta direzione. Se sei fortunato, gli oggetti BouncyCastle saranno contrassegnati come serializzabili, altrimenti avrai bisogno di qualche codice aggiuntivo. Più tardi, avrò la possibilità di esaminare le librerie BouncyCastle per poterlo testare e pubblicherò altro codice se necessario.


... Non ho mai usato BouncyCastle prima. Dopo alcuni test, sembra che gli oggetti chiave pubblica e privata non siano serializzabili, quindi dovrai convertirli 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 BigInteger). Inoltre, BigIntegers ha una funzione ToByteArray () e può anche essere costruita da una matrice di byte. Molto utile ..

Sapendo che è possibile suddividere ciascuna chiave in BigIntegers e queste a loro volta in un array di byte e che è anche possibile il contrario, è un modo per archiviarle tutte in un oggetto serializzabile. Una semplice struttura o classe farebbe ad esempio

[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, devi lanciarle come segue:

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

Le seguenti funzioni le convertiranno in strutture dichiarate 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;
}

Usando il formattatore binario menzionato in precedenza, possiamo convertire gli oggetti serializzabili che abbiamo appena creato in 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 deserializzazione è quindi solo l'inverso come descritto in precedenza. Una volta deserializzate le strutture pubbliche o private, è possibile utilizzare i contruttori 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 abbia un senso! Gli esempi di codice dovrebbero aiutarti nella tua strada.

L'approccio corretto è usare il suggerimento di Peters.

Di seguito ho incluso un piccolo esempio di codice C #:

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 Bouncy Castle. Nota che l'esempio NON crittografa la chiave. Il metodo CreatePrivateKeyInfo è sovraccarico per consentire l'uso di una password come protezione della chiave.

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

Tornando alla prima parte della tua domanda, hai effettivamente la possibilità che SQL Server gestisca la crittografia per te. Concesso, che tu voglia o meno fare questo sarebbe una questione di quali siano i requisiti della tua domanda, ma lo esaminerò nel caso in cui sia un'opzione.

Saremo piuttosto semplici qui e useremo semplicemente i tasti simmetrici 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 può generare i propri certificati X.509 utilizzati per proteggere le chiavi utilizzate per crittografare i dati effettivi.

CREATE CERTIFICATE ExampleCertificate 
     WITH SUBJECT = 'thisisjustsomemetadata'

Esistono molte opzioni per crittografare chiavi simmetriche (certificati, password, altre chiavi), oltre a 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 decodificata 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 decrittazione è altrettanto semplice.

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

Speriamo che questo abbia risolto il tuo problema, o almeno ti abbia aperto a soluzioni alternative (anche se qualcuno che ha avuto esperienza nel fare la crittografia nelle app C # potrebbe probabilmente trovare l'errore nel tuo codice sopra). Se hai requisiti che richiedono che i dati non possano nemmeno passare attraverso il cavo a SQL Server in testo normale, ovviamente questo è un no-go (beh, puoi puoi effettivamente creare connessioni SSL a SQL Server ...).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top