2010-05-12 15 views
7

Tengo un cifrado AES están realizando en dos columnas: una de estas columnas se almacena en una base de datos SQL Server 2000 ; la otra se almacena en una base de datos SQL Server 2008 .Cómo conseguir la compatibilidad entre C# y cifrado AES SQL2k8?

Como la base de datos de la primera columna (2000) no tiene una funcionalidad nativa para el cifrado/descifrado, hemos decidido hacer la lógica de criptografía a nivel de aplicación, con clases .NET, para ambos. Pero como la base de datos de la segunda columna (2008) permite este tipo de funcionalidad, nos gustaría que la migración de datos usando las funciones de la base de datos sea más rápida, ya que la migración de datos en SQL durará más de 50 horas debido a que se realizó a nivel de aplicación.

Mi problema comenzó en este punto: usando la misma clave, no logré el mismo resultado al encriptar un valor, ni el mismo tamaño de resultado.

A continuación tenemos la lógica completa en ambos lados .. Por supuesto no estoy mostrando la clave, pero todo lo demás es el mismo:

private byte[] RijndaelEncrypt(byte[] clearData, byte[] Key) 
{ 
    var memoryStream = new MemoryStream(); 

    Rijndael algorithm = Rijndael.Create(); 

    algorithm.Key = Key; 
    algorithm.IV = InitializationVector; 

    var criptoStream = new CryptoStream(memoryStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write); 
    criptoStream.Write(clearData, 0, clearData.Length); 
    criptoStream.Close(); 

    byte[] encryptedData = memoryStream.ToArray(); 
    return encryptedData; 
} 

private byte[] RijndaelDecrypt(byte[] cipherData, byte[] Key) 
{ 
    var memoryStream = new MemoryStream(); 

    Rijndael algorithm = Rijndael.Create(); 

    algorithm.Key = Key; 
    algorithm.IV = InitializationVector; 

    var criptoStream = new CryptoStream(memoryStream, algorithm.CreateDecryptor(), CryptoStreamMode.Write); 

    criptoStream.Write(cipherData, 0, cipherData.Length); 

    criptoStream.Close(); 

    byte[] decryptedData = memoryStream.ToArray(); 

    return decryptedData; 
} 

Este es el ejemplo de código SQL:

open symmetric key columnKey decryption by password = N'{pwd!!i_ll_not_show_it_here}' 

declare @enc varchar(max) 

set @enc = dbo.VarBinarytoBase64(EncryptByKey(Key_GUID('columnKey'), 'blablabla')) 

select LEN(@enc), @enc 

Este varbinaryToBase64 es una función SQL probado que utilizamos para convertir varbinary en el mismo formato que utilizamos para cadenas de tiendas en la aplicación .NET.

El resultado en C# es: eg0wgTeR3noWYgvdmpzTKijkdtTsdvnvKzh + uhyN3Lo =

el mismo resultado en SQL2k8 es: AI0zI7D77EmqgTQrdgMBHAEAAACyACXb + P3HvctA0yBduAuwPS4Ah3AB4Dbdj2KBGC1Dk4b8GEbtXs5fINzvusp8FRBknF15Br2xI1CqP0Qb/M4W

sólo que no entiendo todavía lo que estoy haciendo incorrecto.

¿Tiene alguna idea?

EDIT: Un punto creo que es crucial: Tengo un vector de inicialización en mi código C#, 16 bytes. Este IV no está configurado con la clave simétrica de SQL, ¿podría hacer esto?

Pero aún no llenar el IV en C#, consigo resultados muy diferentes, tanto en su contenido y duración.

+0

¿Hay algo en la documentación que indique que los dos algoritmos de cifrado son de hecho el mismo? – Joe

+0

dicen que ambos son AES, ¿verdad? Pensé que esto sería suficiente para garantizar cierta interoperabilidad. –

Respuesta

5

Hay un par de cosas que me miraría:

  1. estar absolutamente seguro de que el texto plano es idéntico en contenido y la codificación. IIRC, los flujos predeterminados a UTF-8 mientras que si su función VarBinaryToBase64 toma un parámetro nvarchar, será Unicode.

  2. Asegúrese de que ambos algoritmos de cifrado usen el mismo tamaño de bloque. En SQL, usted determina el algoritmo cuando llama al CREATE SYMMETRIC KEY. Si no especifica un algoritmo, usa AES256. En .NET usando RijndaelManaged, creo que el tamaño de bloque predeterminado es 128 pero puede establecerlo en 256 (no puede si usa la clase Aes).

  3. Lo último que buscaría es cómo trata SQL Server con los vectores de inicialización como mencionó en su publicación modificada. Quiero decir que usa el parámetro authenticator para esto, pero eso es una suposición descabellada.

EDITAR

que era muy lejos. Dado lo que he descubierto, no puede usar una clase .NET para descifrar el texto cifrado mediante el cifrado integrado de SQL Server porque SQL Server agrega un montón de goo a lo que se cifra, incluido un vector de inicialización aleatorio. Del libro de Michael Cole "Pro T-SQL 2005 Guía del programador" (aunque 2008 hace esto de la misma manera):

Cuando SQL Server cifra de clave simétrica , se añade metadatos a la resultado cifrado, así como relleno, haciendo que el resultado cifrado sea más grande (a veces significativamente más grande) que el texto sin cifrar descifrado. El formato para el resultado cifrado con metadatos sigue este formato:

  • Los primeros 16 bytes de la resultado cifrado representar el GUID de la clave simétrica utilizada para cifrar los datos
  • Los próximos 4 bytes representan la número de versión, actualmente codificado como "01000000".
  • Los siguientes 8 bytes para el cifrado DES (16 bytes para el cifrado AES) representan el vector de inicialización generado aleatoriamente.
  • Los siguientes 8 bytes son información del encabezado que representa las opciones utilizadas para encriptar los datos. Si se utiliza la opción autenticador, esta información de cabecera incluye un de 20 bytes hash SHA1 del autenticador, haciendo la información del encabezado de 28 bytes de longitud .
  • La última parte de los datos cifrados son los datos reales y el relleno en sí. Para los algoritmos DES, la longitud de este datos cifrados será un múltiplo de 8 bytes. Para los algoritmos AES, la longitud será un múltiplo de 16 bytes.
+0

A juzgar por la longitud de salida (84 bytes antes de la codificación base64), no creo que SQL Server esté usando AES256. Sin embargo, no es un múltiplo de 16. Bastante raro. – Thorarin

+0

@Thorarin - Believe Coles solo dice que los datos + relleno serán un múltiplo de 16, no el cifrado completo. Por lo tanto, 84 menos un encabezado de 36 bytes es 48 bytes, que es un múltiplo de 16. – Thomas

+0

Ah, tu edición aclara mucho las cosas, al menos explica por qué los primeros 18 caracteres base64 fueron los mismos y el resto siguió cambiando .. Sin embargo , el descifrado con .NET debería ser posible ahora que conoce el diseño del encabezado. No creo estar cifrando, depende del hash SHA1, supongo. – Thorarin

1

No es realmente una respuesta, pero demasiado grande para un comentario. Tal vez ayudará a alguien más a resolverlo :)
SQL Server parece hacer una gran cantidad de relleno. Cuando probé tu ejemplo, seguí obteniendo resultados variables, aunque los primeros 18 caracteres parecen ser los mismos siempre. Sin embargo, todavía no tiene sentido para mí ...

No está mostrando cómo se llama RijndaelEncrypt, pero con las diferentes longitudes de datos estaba pensando en las diferencias de unicode vs ANSI codepage. Sin embargo, parece que no está utilizando Unicode en SQL Server, por lo que la diferencia de longitud sería al revés, en todo caso.

Cuestiones relacionadas