Pergunta

Eu tenho uma função que gera um par de chaves RSA BouncyCastle. Eu preciso para criptografar a chave privada e, em seguida, armazenar as chaves privadas e públicas criptografados em campos de banco de dados SQL2008 separadas.

Eu estou usando o seguinte para obter o par de chaves:

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

Esta é retornar as chaves bem, mas eu não sei como eu posso então criptografar a chave privada e, posteriormente, armazená-lo no banco de dados.

Este é o que eu estou usando atualmente a criptografar os dados (de forma incorrecta?):

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;
}

Obviamente, os keyBytes cenário onde eu estou convertendo keyParam.ToString () não está correto, pois só converte o nome KeyParameter, e não o valor real. Eu estou submetendo a esta função o retorno par de chaves anterior de keys.Private.

A outra questão é que eu não estou criptografando a chave pública que formato devo estar armazenando esta no banco de dados SQL2008, nvarchar (256) ou outro?

Qualquer ajuda seria muito apreciada.

Foi útil?

Solução

Por razões que devem ser claras, padrão (e talvez involuntária) de serialização não joga bem com chaves privadas que só deve ser escrita em situações muito limitadas.

BouncyCastle tem suporte para PKCS # 8, que é o padrão relevante para "serialização" chaves privadas. Existem estruturas ASN.1 chamado PrivateKeyInfo e EncryptedPrivateKeyInfo. Uma vez que eles estão em ASN.1 existem formas padronizadas para serializar / desserializar-los. Como o nome sugere, um armazena a chave em texto simples, os outros criptografa a chave com base em uma senha.

Para as chaves públicas - estes seriam normalmente não ser criptografados. BC suporta o formato padrão de SubjectPublicKeyInfo X.509 para a serialização-los.

No C # construção, as classes de alto nível para olhar seria:

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

Outras dicas

Enquanto o objecto é marcado como seriável, uma forma de converter um objecto a uma matriz de bytes é usar a classe BinaryFormatter em Net.

Você vai precisar adicionar esta instrução usando para seu arquivo de código:

using System.Runtime.Serialization.Formatters.Binary;

Um binário formatador lata saída de sua classe para um riacho. Como você pretende converter seu objeto para um array de bytes, você pode usar um System.IO.MemoryStream como armazenamento temporário.

MemoryStream memStream = new MemoryStream();

Você pode criar um novo formatador binário.

BinaryFormatter formatter = new BinarryFomatter();

e usar isso para serializar seu objeto.

formatter.Serialize(memStream, someObject);

Para obter o bytes que você pode usar:

return memStream.ToArray();

Para anular a serialização a matriz de bytes que você precisa escrever os bytes para um fluxo de memória.

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

Voltar ao início do fluxo.

memStream.Seek(0, SeekOrigin.Begin);

Em seguida, use o formatador para recriar o objeto.

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

Se você já estiver usando funções de criptografia que você deve ser capaz de criptografar a matriz de bytes criado muito facilmente antes de armazená-lo no banco de dados.

Esperemos que irá ajudá-lo na direção certa. Se você tiver sorte, os objetos BouncyCastle será marcado como serializável, se não você vai precisar de algum código extra. Mais tarde, vou ter a chance de olhar para as librarys BouncyCastle para ser capaz de testar isso e publicará mais código, se necessário.


... Eu nunca usei BouncyCastle antes. Depois de alguns testes, parece que os objetos de chaves públicas e privadas não são serializáveis, então você vai precisar para converter esses objetos em algo que é!

Parece que as chaves públicas e privadas expor propriedades como vários valores BouncyCastle.Math.BigInteger. (As chaves podem também ser construídos a partir destes BigIntegers). Além disso, têm uma função BigIntegers ToByteArray () e também pode ser construído a partir de uma matriz de bytes. Muito útil ..

sabendo que você pode quebrar cada chave na BigIntegers e estes por sua vez a uma matriz de bytes e que o inverso também é possível, você uma maneira de armazenar tudo isso em um objeto serializado. A estrutura simples ou classe faria por exemplo.

[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;
}

Isto dá-nos um par de fácil de usar objetos serializados.

O AsymmetricCipherKeyPair expõe as chaves privadas Públicas e como objetos AsymmetricKeyParameter. Para obter as propriedades mais detalhados que você precisa para lançar estes para o seguinte:

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

As seguintes funções irá convertê-los para as estruturas para declarado anteriormente:

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 o formatador binário mencionado anteriormente, podemos converter os objetos serializados que acabamos de criar para um array de bytes.

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();

Desierializing é, então, apenas o inverso, como descrito anteriormente. Depois de ter tanto o público ou estruturas privadas desserializado você pode usar as contructors BouncyCastle para recriar as chaves. Estas funções demonstrar isso.

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 você precisar recriar o par de chaves original para qualquer razão:

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

Esperamos que tudo faz sentido! Os exemplos de código deve ajudá-lo em seu caminho.

A abordagem correta é usar sugestão Peters'.

Eu incluí um C amostra # código pequena abaixo:

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);

O exemplo utiliza a API Bouncy Castle. Nota que a amostra não criptografa a chave. O método CreatePrivateKeyInfo está sobrecarregado para permitir o uso de uma senha como a proteção da chave.

Em relação à segunda parte da sua pergunta, o tipo de dados que deve ser usado para armazenar a chave seria VARBINARY (256).

Voltar à primeira parte da sua pergunta, você realmente tem a opção de ter SQL Server lidar com a criptografia para você. Concedido, se ou não você iria querer fazer isso seria uma questão do que seus requisitos de aplicação são, mas eu vou passar por isso no caso, é uma opção.

Vamos ser bastante básico aqui e é só usar chaves simétricas e Triple-DES.

Primeiro, o banco de dados tem uma chave mestra que é usada para proteger certificados e chaves assimétricas. A chave mestre é criptografada com Triple-DES.

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'supersecretpassword'  

SQL Server 2005/2008 pode gerar seus próprios certificados X.509 usados ??para proteger as chaves usadas para criptografar os dados reais.

CREATE CERTIFICATE ExampleCertificate 
     WITH SUBJECT = 'thisisjustsomemetadata'

Há uma série de opções para criptografar chaves simétricas (certificados, senhas, outras chaves), bem como muitos algoritmos suportados. Mas para este exemplo, vamos usar o nosso certificado.

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

A chave precisa ser decifrada usando o mesmo método com que foi criptografado. No nosso caso, este seria o certificado que criamos.

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

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

A descriptografia é tão simples.

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

Esperamos que isso solucionou o problema, ou pelo menos abriu-lhe até soluções alternativas (embora alguém que teve experiência fazendo de criptografia em C # aplicativos provavelmente poderia encontrar a falha em seu código acima). Se você tiver requisitos que exigem que os dados não podem mesmo ir sobre o fio para o SQL Server em texto simples, obviamente, este é um não-go (bem, você pode realmente criar conexões SSL para SQL Server ...).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top