2011-12-07 22 views
6

Questo è il mio primo post, quindi spero di non aver perso nulla di importante. Sto facendo un progetto in C# in cui ho bisogno di utilizzare la crittografia a chiave pubblica/privata per crittografare un messaggio e quindi inviarlo tramite una connessione SSL.RSA Crittografia dei grandi dati in C#

Ho scelto di utilizzare RSACryptoService, in base alla documentazione, che era l'unico schema di crittografia asimmetrico utilizzato per la crittografia dei dati. Il problema è che sto avendo molti problemi con questo. (Volevo fare la crittografia simmetrica, ma non è quello che il mio insegnante vuole che faccia, e secondo lui dovrebbe essere facile determinare solo una dimensione del blocco e poi dovrebbe fare tutto il lavoro per te.) Bene, finora senza fortuna e ho provato alcuni approcci diversi, ma ora sono tornato alle origini e provare di nuovo, questo è il mio codice corrente:

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

Come potete vedere, ho generare le chiavi e ho mimmick una PKI inviando la chiave pubblica ad una semplice classe che la memorizza, e quindi la chiave privata viene scritta su un file (Si noti che ho anche un altro metodo che fa lo stesso ma lo memorizza invece su un array, solo perché volevo testare e semplificare le cose man mano che ottengo le eccezioni No such key exceptions ea volte crittografiche quando eseguo il modo mostrato nell'ex ampia, così ho voluto semplificare semplicemente memorizzare la stringa rsa.ToXmlString, come una stringa in un array, ma senza fortuna)

Ora ho un cifrare e metodo decifro come segue:.

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

decrittografia :

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

so che questo è un muro di testo, ma spero che mi può dare una mano perché ho sbattere la testa contro il muro per così tanto tempo ormai non è divertente :)

Il problema è, come posso fare per la crittografia dei messaggi con RSA (o qualsiasi altro/crittografia a chiave privata pubblica)

Ecco il client di prova:

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

    } 

Come ho già detto, gli array di stringhe sono lì perché volevo eliminare l'errore di analisi errato dai documenti XML ...

Quando eseguo il client di test, se uso la chiave privata per crittografare e la chiave pubblica per decodificare, ottengo una "chiave non esiste eccezione" e se lo faccio al contrario, ottengo un'eccezione di dati non validi.

Per favore aiutatemi ragazzi, se siete a conoscenza di una buona guida, o potete dirmi come implementare in qualche modo una cifratura della chiave pubblica/privata sui messaggi di stringa, per favore aiutatemi.

Apprezzo qualsiasi aiuto.

risposta

5

Non è così che deve essere eseguita la crittografia RSA.

RSA significa matematica. Ciò che crittografate è un numero quindi deve essere di lunghezza finita e corrispondente alla lunghezza della coppia di chiavi RSA che state utilizzando. Ulteriori limiti di lunghezza sono imposti dal padding utilizzato (PKCS # 1 o OAEP).

Se si desidera crittografare i dati di grandi dimensioni con RSA, è necessario farlo indirettamente, ovvero utilizzare una chiave simmetrica per crittografare i dati di grandi dimensioni e crittografare questa chiave utilizzando la chiave pubblica RSA.

Si può leggere sull'implementazione di questo sul mio blog.

+0

Ho parlato con il mio professore e in quel momento il mio punto di vista era che sarebbe stato meglio cifrare una chiave, scambiare la chiave e quindi usarlo come base per un algoritmo simmetrico, come rijndael per crittografare/decifrare il messaggio, tuttavia, non voleva che usassimo la crittografia simmetrica, quindi ora sono in una posizione in cui per ora è un po 'fuori limite. Per quanto riguarda le prestazioni, quanto tempo ci vorrebbe per crittografare i messaggi, 501 byte alla volta (usando le chiavi RSA 4096 bit) quando parlavamo di messaggi HTTP contenenti nome utente e password? codifica blocco per blocco o meno, ho ancora problemi con RSA :( –

+0

È possibile eseguire il loop della crittografia/decodifica RSA per rimanere al di sotto del limite di dimensione (padding incluso) e concatenare/dividere il risultato. la tua CPU, è un vecchio smartphone e un nuovo server 8 core? ;-) ma molto più a lungo di un'alternativa corretta. – poupou

+0

Mi piacerebbe avere tempo, ma al momento non riesco nemmeno a far funzionare tutto questo per nessun dato. Ho preso una versione semplificata e l'ho usata, tuttavia, ho ancora l'errore "La chiave non esiste" e tutto ciò che ho fatto è stato il refactoring del design, da un singolo metodo principale per separare i metodi. Forse sto usando il costruttore giusto? La parte in cui importare le impostazioni chiave? So che un cifrario a blocchi è 1000 volte più lento di un algoritmo simmetrico, ma in questo momento voglio solo farlo funzionare, non importa se devo hardcode in modo da tagliare ogni blocco di dati in pezzi di (keysize/8 - 11 (per riempimento)) byte. –

0

Forse mi manca qualcosa, ma sembra che la funzione Encrypt() non utilizzi né il parametro keyString né il contenuto di encryptKey.

+0

Sì, sono terribilmente dispiaciuto per quello, ma devo aver dimenticato di inserirlo, ma ti assicuro che mi sono assicurato di caricare la chiave. Ho aggiornato l'OP ora. Ho appena provato a caricare i dati tante volte, che devo avere alcune delle versioni mescolate e non vederle :) Il mio problema persiste comunque. Il problema che sto avendo ora, è che quando uso la mia chiave privata per cifrare, e il pubblico per decifrare, ottengo un'eccezione Do not not Exist. Se lo faccio al contrario, tuttavia ottengo un'eccezione "dati errati". –

1

OK, ho finalmente trovato una soluzione al problema che ho dichiarato nel mio post originale. Questo è qualcosa che non ho provato a fondo o nulla, ma qualcosa che ho capito da un piccolo processo di prova ed errore.

Ecco il codice corrente che ho:

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

    } 

Come ho detto, non ho ancora testato molto, diverso da dimensioni inferiori, e al di sopra del blocco-size, ma sembra stia facendo quello che dovrebbe. Sono ancora un novizio, quindi mi piacerebbe molto che tu scrutasse il mio codice :)

Problemi correlati