Pregunta

Tengo una función que genera un par de claves BouncyCastle RSA. Necesito encriptar la clave privada y luego almacenar las claves privadas y públicas encriptadas en campos separados de la base de datos SQL2008.

Estoy usando lo siguiente para obtener el par de claves:

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

Esto está devolviendo bien las claves, pero no estoy seguro de cómo puedo cifrar la clave privada y luego almacenarla en la base de datos.

Esto es lo que estoy usando actualmente para cifrar los datos (¿incorrectamente?):

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, la configuración keyBytes donde estoy convirtiendo keyParam.ToString () no es correcta, ya que solo convierte el nombre KeyParameter, no el valor real. Estoy enviando a esta función el par de claves anterior devolución de claves. Privado.

La otra pregunta es, ya que no estoy encriptando la clave pública, ¿qué formato debo almacenar en la base de datos SQL2008, nvarchar (256) u otra?

Cualquier ayuda sería muy apreciada.

¿Fue útil?

Solución

Por razones que deberían estar claras, la serialización predeterminada (y quizás involuntaria) no funciona bien con claves privadas que solo deberían escribirse en situaciones muy limitadas.

BouncyCastle tiene soporte para PKCS # 8, que es el estándar relevante para " serialización " llaves privadas Hay estructuras ASN.1 llamadas PrivateKeyInfo y EncryptedPrivateKeyInfo. Como están en ASN.1, hay formas estándar de serializarlas / deserializarlas. Como su nombre indica, uno almacena la clave en texto sin formato, el otro cifra la clave en función de una contraseña.

Para las claves públicas, normalmente no se cifrarían. BC admite el formato estándar X.509 de SubjectPublicKeyInfo para serializarlos.

En la compilación de C #, las clases de alto nivel a considerar serían:

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

Otros consejos

Mientras el objeto esté marcado como serializable, una forma de convertir un objeto en una matriz de bytes es utilizar la clase BinaryFormatter en .Net.

Deberá agregar esto usando la declaración a su archivo de código:

using System.Runtime.Serialization.Formatters.Binary;

Un formateador binario puede enviar su clase a una secuencia. Cuando intente convertir su objeto en una matriz de bytes, puede usar un System.IO.MemoryStream como almacenamiento temporal.

MemoryStream memStream = new MemoryStream();

Luego puede crear un nuevo formateador binario.

BinaryFormatter formatter = new BinarryFomatter();

y use esto para serializar su objeto.

formatter.Serialize(memStream, someObject);

Para obtener los bytes que puede usar:

return memStream.ToArray();

Para deserializar la matriz de bytes, debe escribir los bytes en una secuencia de memoria.

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

Volver al comienzo de la transmisión.

memStream.Seek(0, SeekOrigin.Begin);

Luego usa el formateador para recrear el objeto.

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

Si ya está utilizando funciones de cifrado, debería poder cifrar la matriz de bytes creada con bastante facilidad antes de almacenarla en la base de datos.

Espero que eso te ayude en la dirección correcta. Si tiene suerte, los objetos BouncyCastle se marcarán como serializables, de lo contrario, necesitará un código adicional. Más tarde, tendré la oportunidad de mirar las bibliotecas BouncyCastle para poder probar esto y publicaré más código si es necesario.


... Nunca he usado BouncyCastle antes. Después de algunas pruebas, parece que los objetos de clave pública y privada no son serializables, por lo que deberá convertir estos objetos en algo que sí lo sea.

Parece que las claves públicas y privadas exponen propiedades como varios valores BouncyCastle.Math.BigInteger. (Las claves también se pueden construir a partir de estos BigIntegers). Además, BigIntegers tiene una función ToByteArray () y también se puede construir a partir de una matriz de bytes. Muy útil ...

Sabiendo que puede dividir cada clave en BigIntegers y estas a su vez en una matriz de bytes y que también es posible lo inverso, puede almacenar todo esto en un objeto serializable. Una estructura o clase simple haría, por ejemplo,

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

Esto nos da un par de objetos serializables fáciles de usar.

AsymmetricCipherKeyPair expone las claves públicas y privadas como objetos AsymmetricKeyParameter. Para obtener las propiedades más detalladas, deberá convertirlas en lo siguiente:

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

Las siguientes funciones los convertirán a las estructuras declaradas 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 el formateador binario mencionado anteriormente, podemos convertir los objetos serializables que acabamos de crear en una matriz 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();

La desierialización es simplemente la inversa como se describió anteriormente. Una vez que haya deserializado las estructuras públicas o privadas, puede usar los contructores BouncyCastle para recrear las claves. Estas funciones demuestran esto.

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

Si necesita volver a crear el par de claves original por cualquier motivo:

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

¡Ojalá todo tenga sentido! Los ejemplos de código deberían ayudarlo en su camino.

El enfoque correcto es utilizar la sugerencia de Peters.

He incluido un pequeño ejemplo de código C # a continuación:

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

La muestra usa la API de Bouncy Castle. Tenga en cuenta que la muestra NO cifra la clave. El método CreatePrivateKeyInfo está sobrecargado para permitir el uso de una contraseña como protección de la clave.

Con respecto a la segunda parte de su pregunta, el tipo de datos que debe usarse para almacenar la clave sería VARBINARIO (256).

Volviendo a la primera parte de su pregunta, en realidad tiene la opción de que SQL Server maneje el cifrado por usted. De acuerdo, si desea o no hacer esto sería una cuestión de cuáles son los requisitos de su aplicación, pero lo revisaré en caso de que sea una opción.

Seremos bastante básicos aquí y solo usaremos teclas simétricas y Triple-DES.

Primero, la base de datos tiene una clave maestra que se utiliza para proteger los certificados y las claves asimétricas. La clave maestra está encriptada con Triple-DES.

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'supersecretpassword'  

SQL Server 2005/2008 puede generar sus propios certificados X.509 utilizados para proteger las claves utilizadas para cifrar los datos reales.

CREATE CERTIFICATE ExampleCertificate 
     WITH SUBJECT = 'thisisjustsomemetadata'

Hay muchas opciones para encriptar claves simétricas (certificados, contraseñas, otras claves), así como muchos algoritmos compatibles. Pero para este ejemplo, usaremos nuestro certificado.

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

La clave debe descifrarse utilizando el mismo método con el que se cifró. En nuestro caso, este sería el certificado que creamos.

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

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

El descifrado es igual de sencillo.

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

Con suerte, esto resolvió su problema, o al menos lo abrió a soluciones alternativas (aunque alguien que haya tenido experiencia en cifrado en aplicaciones C # probablemente podría encontrar la falla en su código anterior). Si tiene requisitos que requieren que los datos ni siquiera puedan pasar a través del cable al Servidor SQL en texto plano, obviamente esto es una prohibición (bueno, puede crear conexiones SSL a SQL Server ...).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top