2011-12-07 20 views
6

To jest mój pierwszy post, więc mam nadzieję, że nie przegapiłem niczego ważnego. Robię projekt w C#, gdzie muszę użyć szyfrowania klucza publicznego/prywatnego, aby zaszyfrować wiadomość, a następnie wysłać go za pośrednictwem połączenia SSL.RSA Szyfrowanie dużych danych w C#

Wybrałem użycie RSACryptoService, ponieważ zgodnie z dokumentacją był to jedyny asymetryczny schemat szyfrowania używany do szyfrowania danych. Problem polega na tym, że mam z tym wiele problemów. (Chciałem zrobić szyfrowanie symetryczne, ale to nie jest to, co mój nauczyciel chce, abym zrobił, i według niego powinno być łatwo określić rozmiar bloku, a następnie powinien wykonać całą pracę dla ciebie.) Cóż, do tej pory nie ma szczęścia i próbowałem kilka różnych podejść, ale teraz jestem z powrotem do podstaw i próbuje jeszcze raz, to jest mój bieżący kod:

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

Jak widać, wygenerować klucze i mimmick infrastruktury PKI przez wysłanie klucza publicznego do prostej klasy, która go przechowuje, a następnie klucz prywatny jest zapisywany do pliku (Zauważ, że mam też inną metodę, która robi to samo, ale przechowuje ją zamiast tablicy, tylko dlatego, że chciałem przetestować i upraszczaj rzeczy, gdy dostaję No such key exceptions, a czasem wyjątki kryptograficzne, gdy robię to w sposób pokazany w obfite, więc chciałem uprościć go po prostu przechowywania ciąg rsa.ToXmlString jako ciąg w tablicy, ale bez powodzenia)

Teraz mam szyfrowania i deszyfrowania metody w następujący sposób:.

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

deszyfrowania :

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

wiem, że jest to ściana tekstu, ale mam nadzieję, że może mi pomóc, bo już walić głową o ścianę tak długo, teraz to nie jest zabawne :)

Problem polega na tym, w jaki sposób przejść o wiadomościach szyfrowanie z RSA (lub jakakolwiek inna/szyfrowanie prywatny klucz publiczny)

Oto klient testowy:

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

    } 

Jak już wspomniano, układy łańcuchowe są tam ponieważ chciałem wyeliminować błędne parsowanie z dokumentów XML ...

Po uruchomieniu klienta testowego, jeśli użyję klucza prywatnego do zaszyfrowania i klucza publicznego do odszyfrowania, otrzymam komunikat "Klucz nie istnieje wyjątek" i jeśli robię to na odwrót, otrzymuję wyjątek zły.

Pomóżcie mi, chłopaki, jeśli znacie jakiś dobry przewodnik, lub możecie mi powiedzieć, jak w prosty sposób wdrożyć szyfrowanie klucza publicznego/prywatnego w wiadomościach tekstowych, proszę, pomóżcie mi.

Doceniam każdą pomoc.

Odpowiedz

5

To nie jest sposób szyfrowania RSA.

RSA to wszystko o matematyce. To, co zaszyfrujesz, jest liczbą, więc musi być skończonej długości i pasującej długości klucza RSA, której używasz. Dalsze ograniczenia długości są nakładane przez zastosowane wypełnienie (PKCS # 1 lub OAEP).

Jeśli chcesz zaszyfrować duże dane za pomocą RSA, musisz zrobić to pośrednio - tj. Użyć klucza symetrycznego do zaszyfrowania dużych danych i zaszyfrowania tego klucza przy użyciu klucza publicznego RSA.

Możesz przeczytać o implementacji tego na moim blog.

+0

Rozmawiałem z moim profesorem i wtedy mój punkt widzenia był taki, że lepiej byłoby zaszyfrować klucz, wymienić klucz i następnie użyj go, jako podstawy symetrycznego algorytmu, takiego jak rijndael do szyfrowania/odszyfrowania wiadomości, jednak nie chciał, abyśmy używali szyfrowania symetrycznego, więc teraz jestem w sytuacji, w której na razie jest to rodzaj off-limit. Wydajność, ile czasu zajęłoby szyfrowanie wiadomości, 501 bajtów (przy użyciu 4096bitowych kluczy RSA), gdy rozmawialiśmy o wiadomościach HTTP zawierających nazwę użytkownika i hasło? ciągle blokuję szyfrowanie RSA :( –

+0

Można zaszyfrować szyfrowanie/odszyfrowanie RSA, aby pozostać poniżej limitu rozmiaru (dopełnienie) i konkatenować/podzielić wynik. Ile czasu? Twój procesor, czy to stary smartfon i nowy 8-rdzeniowy serwer? ;-), ale o wiele dłuższy niż odpowiednia alternatywa. – poupou

+0

Chciałbym móc to zrobić, ale teraz nie mogę nawet uruchomić tego dla jakichkolwiek danych. Zrobiłem wersję uproszczoną i wykorzystałem to, jednak nadal otrzymuję błąd "Key does not exist" i wszystko, co naprawdę zrobiłem, to było refaktoryzowanie projektu, od jednej głównej metody do oddzielnych metod. Może używam konstruktora, prawda? Część, w której importuję kluczowe ustawienia? Wiem, że szyfr blokowy jest 1000 razy wolniejszy od algorytmu symetrycznego, ale w tej chwili chcę go uruchomić, niezależnie od tego, czy muszę go zakodować na twardym dysku tak, aby posiekać każdy blok danych na kawałki (keyize/8 - 11 (dla dopełnienia)) bajtów. –

0

Może brakuje mi czegoś, ale wygląda na to, że twoja funkcja Encrypt() nie używa ani parametru keyString ani zawartości encryptKey.

+0

Tak, strasznie mi przykro z tego powodu, ale musiałem o tym zapomnieć, ale zapewniam cię, że upewniłem się załadować klucz. Zaktualizowałem teraz PO. Właśnie próbowałem ładować dane tyle razy, że musiałem zmiksować niektóre wersje i nie widziałem ich :) Mój problem wciąż trwa. Problem, który mam teraz, polega na tym, że kiedy używam klucza prywatnego do szyfrowania, a publiczny do odszyfrowania, otrzymuję wyjątek keyDoesn'tExist. Jeśli jednak zrobię to w drugą stronę, dostaję wyjątek "złe dane". –

1

OK, w końcu udało mi się znaleźć rozwiązanie problemu, który napisałem w moim oryginalnym wpisie. To jest coś, czego nie przetestowałem dokładnie ani nic, ale coś, czego się dowiedziałem z małego procesu prób i błędów.

Oto aktualny kod mam:

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

    } 

Jak już mówiłem, nie testowałem go dużo, inne niż rozmiarach poniżej, przy i nad blokiem wielkości, ale wydaje się, że robi to, co powinno. Nadal jestem nowicjuszem, więc bardzo chciałbym, abyś przeanalizował mój kod :)