2011-12-07 69 views
6

Esta es mi primera publicación, así que espero no haberme perdido algo importante. Estoy haciendo un proyecto en C# donde necesito usar un cifrado de clave pública/privada para encriptar un mensaje y luego enviarlo a través de una conexión SSL.Cifrado RSA de datos grandes en C#

Opté por usar el RSACryptoService, ya que de acuerdo con la documentación, ese era el único esquema de cifrado asimétrico utilizado para encriptar datos. El problema es que estoy teniendo muchos problemas con esto. (Quería hacer un cifrado simétrico, pero eso no es lo que mi maestro quiere que haga, y según él, debería ser fácil determinar el tamaño de un bloque y luego debería hacer todo el trabajo por usted). Bueno, hasta ahora sin suerte y he probado algunos enfoques diferentes, pero ahora estoy de vuelta a lo básico y volver a intentarlo, esta es mi código actual:

public string[] GenerateKeysToStrings(string uniqueIdentifier) 
    { 
     string[] keys; 
     using (var rsa = new RSACryptoServiceProvider(4096)) 
     { 
      try 
      { 
       string privateKey = rsa.ToXmlString(true); 
       string publicKey = rsa.ToXmlString(false); 

       this.pki.StoreKey(publicKey, uniqueIdentifier); 

       keys = new string[2]; 
       keys[0] = privateKey; 
       keys[1] = publicKey; 
      } 
      finally 
      { 
       //// Clear the RSA key container, deleting generated keys. 
       rsa.PersistKeyInCsp = false; 
      } 
     } 
     return keys; 
    } 

Como se puede ver, genero las llaves y yo mimmick una PKI enviando la clave pública a una clase simple que la almacena, y luego la clave privada se escribe en un archivo (Observe que también tengo otro método que hace lo mismo pero lo almacena en una matriz, simplemente porque quería probar y simplificar las cosas a medida que obtengo No such key exceptions y, a veces, excepciones criptográficas cuando lo hago de la manera que se muestra en el ex amplia, así que quería simplificarlo simplemente almacenar la cadena rsa.ToXmlString, como una cadena en una matriz, pero no hubo suerte)

Ahora tengo una cifrar y descifrar el método de la siguiente manera:.

public string Encrypt(string keyString, string message) 
    { 
     string encryptedMessage; 
     using (var rsa = new RSACryptoServiceProvider()) 
     { 
      try 
      { 
       //// Load the key from the specified path 
       var encryptKey = new XmlDocument(); 
       encryptKey.Load(@"C:\Test\PrivateKeyInfo.xml"); 
       rsa.FromXmlString(encryptKey.OuterXml); 


       //// Conver the string message to a byte array for encryption 
       //// var encoder = new UTF8Encoding(); 
       ASCIIEncoding byteConverter = new ASCIIEncoding(); 
       byte[] dataToEncrypt = byteConverter.GetBytes(message); 

       byte[] encryptedData = rsa.Encrypt(dataToEncrypt, false); 

       //// Convert the byte array back to a string message 
       encryptedMessage = byteConverter.GetString(encryptedData); 
      } 
      finally 
      { 
       //// Clear the RSA key container, deleting generated keys. 
       rsa.PersistKeyInCsp = false; 
      } 
     } 
     return encryptedMessage; 
    } 

descifrado :

public string Decrypt(string keyString, string message) 
    { 
     string decryptedText; 
     using (var rsa = new RSACryptoServiceProvider()) 
     { 
      try 
      { 
       //// Loads the keyinfo into the rsa parameters from the keyfile 
       /* 
       var privateKey = new XmlDocument(); 
       privateKey.Load(keyString); 
       */ 
       rsa.FromXmlString(keyString); 

       //// Convert the text from string to byte array for decryption 
       ASCIIEncoding byteConverter = new ASCIIEncoding(); 
       var encryptedBytes = byteConverter.GetBytes(message); 

       //// Create an aux array to store all the encrypted bytes 
       byte[] decryptedBytes = rsa.Decrypt(encryptedBytes, false); 

       decryptedText = byteConverter.GetString(decryptedBytes); 
      } 
      finally 
      { 
       //// Clear the RSA key container, deleting generated keys. 
       rsa.PersistKeyInCsp = false; 
      } 
     } 
     return decryptedText; 
    } 

sé que este es un muro de texto, pero espero que usted me puede ayudar a cabo porque he estado golpeando mi cabeza contra la pared durante tanto tiempo que no es gracioso :)

El problema es, ¿cómo hago para el cifrado de mensajes con RSA (o cualquier otro cifrado de clave pública/privada)

Aquí es el cliente de prueba:

public static void Main(string[] args) 
    { 
     PublicKeyInfrastructure pki = new PublicKeyInfrastructure(); 
     Cryptograph crypto = new Cryptograph(); 
     string[] keys = crypto.GenerateKeysToStrings("[email protected]"); 


     string plainText = "Hello play with me, please"; 
     string publicKey = crypto.GetPublicKey("[email protected]"); 

     string encryptedText = crypto.Encrypt(keys[0], plainText); 


     string decryptedText = crypto.Decrypt(keys[1], encryptedText); 

    } 

Como ya he mencionado, las matrices de cadenas están ahí porque quería eliminar el error de análisis incorrecto de documentos XML ...

Cuando ejecuto el cliente de prueba, si uso la clave privada para encriptar y la clave pública para descifrar, obtengo una "Clave no existe excepción" y si lo hago al revés, recibo una excepción de datos erróneos.

Por favor, ayúdenme chicos, si conocen alguna buena guía, o pueden decirme cómo implementar algo de encriptación de clave pública/privada en mensajes de texto, por favor ayúdenme.

Agradezco cualquier ayuda.

Respuesta

5

Así no se debe hacer el cifrado RSA.

RSA tiene que ver con las matemáticas. Lo que encriptas es un número, por lo que tiene que tener una longitud finita y coincidir con la longitud del par de llaves RSA que estás usando. Se imponen limitaciones de longitud adicionales mediante el relleno utilizado (PKCS n. ° 1 u OAEP).

Si desea encriptar datos grandes con RSA, debe hacerlo indirectamente, es decir, use una clave simétrica para encriptar los datos grandes y cifrar esta clave mediante la clave pública RSA.

Puede leer sobre la implementación de esto en mi blog.

+0

Hablé con mi profesor y en ese momento mi punto de vista era que sería mejor cifrar una clave, cambiar la clave y luego utilícelo, como base para un algoritmo simétrico, como rijndael para cifrar/descifrar el mensaje; sin embargo, no quería que usáramos cifrado simétrico, así que ahora estoy en una posición en la que por ahora está fuera de límite. En lo que respecta al rendimiento, ¿cuánto más tiempo lleva cifrar mensajes, 501 bytes por vez (utilizando claves RSA de 4096 bits) cuando hablamos de mensajes HTTP que contienen nombre de usuario y contraseña? codificación bloque por bloque o no, todavía tengo problemas usando RSA :( –

+0

Puede enuclear el cifrado/descifrado RSA para mantenerse por debajo del límite de tamaño (relleno incluido) y concatenar/dividir el resultado. ¿Cuánto tiempo? eso dependerá de su CPU, ¿es un teléfono inteligente antiguo y un nuevo servidor de 8 núcleos? ;-) pero mucho más que una alternativa correcta. – poupou

+0

Ojalá pudiera cronometrarlo, pero ahora mismo no puedo hacer que esto funcione para ningún dato. Tomé una versión simplificada y usé eso, sin embargo, sigo teniendo el error "La clave no existe" y todo lo que realmente hice fue refactorizar el diseño, desde un solo método principal hasta métodos separados. Tal vez estoy usando el constructor ¿verdad? ¿La parte donde importo las configuraciones clave? Sé que un cifrado en bloque es 1000 veces más lento que un algoritmo simétrico, pero en este momento solo quiero que funcione, no importa si tengo que codificarlo para que corte cada bloque de datos en partes de (tamaño de clave/8 - 11 (para relleno)) bytes. –

0

Tal vez me falta algo, pero parece que la función Encrypt() no utiliza ni el parámetro keyString ni el contenido de encryptKey.

+0

Sí, lo siento mucho por eso, pero debo haber olvidado ponerlo allí, pero le aseguro que me he asegurado de cargar la llave. He actualizado el OP ahora. Acabo de intentar cargar los datos tantas veces, que debo haber mezclado algunas de las versiones y no haberlo visto :) Mi problema aún persiste. El problema que estoy teniendo ahora es que cuando uso mi clave privada para encriptar y el público la descifra, obtengo una excepción keyDoes'tExist. Sin embargo, si lo hago al revés, obtengo una excepción de "datos incorrectos". –

1

De acuerdo, finalmente he encontrado una solución al problema que expuse en mi publicación original. Esto es algo que no he probado a fondo ni nada, pero descubrí algo de un pequeño proceso de prueba y error.

Aquí está el código actual que tengo:

public static string Encrypt(string dataToEncrypt, RSAParameters publicKeyInfo) 
    { 
     //// Our bytearray to hold all of our data after the encryption 
     byte[] encryptedBytes = new byte[0]; 
     using (var RSA = new RSACryptoServiceProvider()) 
     { 
      try 
      { 
       //Create a new instance of RSACryptoServiceProvider. 
       UTF8Encoding encoder = new UTF8Encoding(); 

       byte[] encryptThis = encoder.GetBytes(dataToEncrypt); 

       //// Importing the public key 
       RSA.ImportParameters(publicKeyInfo); 

       int blockSize = (RSA.KeySize/8) - 32; 

       //// buffer to write byte sequence of the given block_size 
       byte[] buffer = new byte[blockSize]; 

       byte[] encryptedBuffer = new byte[blockSize]; 

       //// Initializing our encryptedBytes array to a suitable size, depending on the size of data to be encrypted 
       encryptedBytes = new byte[encryptThis.Length + blockSize - (encryptThis.Length % blockSize) + 32]; 

       for (int i = 0; i < encryptThis.Length; i += blockSize) 
       { 
        //// If there is extra info to be parsed, but not enough to fill out a complete bytearray, fit array for last bit of data 
        if (2 * i > encryptThis.Length && ((encryptThis.Length - i) % blockSize != 0)) 
        { 
         buffer = new byte[encryptThis.Length - i]; 
         blockSize = encryptThis.Length - i; 
        } 

        //// If the amount of bytes we need to decrypt isn't enough to fill out a block, only decrypt part of it 
        if (encryptThis.Length < blockSize) 
        { 
         buffer = new byte[encryptThis.Length]; 
         blockSize = encryptThis.Length; 
        } 

        //// encrypt the specified size of data, then add to final array. 
        Buffer.BlockCopy(encryptThis, i, buffer, 0, blockSize); 
        encryptedBuffer = RSA.Encrypt(buffer, false); 
        encryptedBuffer.CopyTo(encryptedBytes, i); 
       } 
      } 
      catch (CryptographicException e) 
      { 
       Console.Write(e); 
      } 
      finally 
      { 
       //// Clear the RSA key container, deleting generated keys. 
       RSA.PersistKeyInCsp = false; 
      } 
     } 
     //// Convert the byteArray using Base64 and returns as an encrypted string 
     return Convert.ToBase64String(encryptedBytes); 
    } 

    /// <summary> 
    /// Decrypt this message using this key 
    /// </summary> 
    /// <param name="dataToDecrypt"> 
    /// The data To decrypt. 
    /// </param> 
    /// <param name="privateKeyInfo"> 
    /// The private Key Info. 
    /// </param> 
    /// <returns> 
    /// The decrypted data. 
    /// </returns> 
    public static string Decrypt(string dataToDecrypt, RSAParameters privateKeyInfo) 
    { 
     //// The bytearray to hold all of our data after decryption 
     byte[] decryptedBytes; 

     //Create a new instance of RSACryptoServiceProvider. 
     using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider()) 
     { 
      try 
      { 
       byte[] bytesToDecrypt = Convert.FromBase64String(dataToDecrypt); 

       //// Import the private key info 
       RSA.ImportParameters(privateKeyInfo); 

       //// No need to subtract padding size when decrypting (OR do I?) 
       int blockSize = RSA.KeySize/8; 

       //// buffer to write byte sequence of the given block_size 
       byte[] buffer = new byte[blockSize]; 

       //// buffer containing decrypted information 
       byte[] decryptedBuffer = new byte[blockSize]; 

       //// Initializes our array to make sure it can hold at least the amount needed to decrypt. 
       decryptedBytes = new byte[dataToDecrypt.Length]; 

       for (int i = 0; i < bytesToDecrypt.Length; i += blockSize) 
       { 
        if (2 * i > bytesToDecrypt.Length && ((bytesToDecrypt.Length - i) % blockSize != 0)) 
        { 
         buffer = new byte[bytesToDecrypt.Length - i]; 
         blockSize = bytesToDecrypt.Length - i; 
        } 

        //// If the amount of bytes we need to decrypt isn't enough to fill out a block, only decrypt part of it 
        if (bytesToDecrypt.Length < blockSize) 
        { 
         buffer = new byte[bytesToDecrypt.Length]; 
         blockSize = bytesToDecrypt.Length; 
        } 

        Buffer.BlockCopy(bytesToDecrypt, i, buffer, 0, blockSize); 
        decryptedBuffer = RSA.Decrypt(buffer, false); 
        decryptedBuffer.CopyTo(decryptedBytes, i); 
       } 
      } 
      finally 
      { 
       //// Clear the RSA key container, deleting generated keys. 
       RSA.PersistKeyInCsp = false; 
      } 
     } 

     //// We encode each byte with UTF8 and then write to a string while trimming off the extra empty data created by the overhead. 
     var encoder = new UTF8Encoding(); 
     return encoder.GetString(decryptedBytes).TrimEnd(new[] { '\0' }); 

    } 

Como ya he dicho, yo no lo he probado mucho, aparte de tamaños abajo, en y por encima del bloque de tamaño, pero parece estar haciendo lo debería. Todavía soy un novato, así que realmente me gustaría que escudriñes mi código :)