2011-10-21 27 views
9

So che la risposta principale che probabilmente otterrò è perché diavolo vorresti farlo ?!Utilizzo di una chiave pubblica RSA per decrittografare una stringa crittografata utilizzando la chiave privata RSA

Purtroppo nonostante le mie proteste devo farlo, anche se so che non ha molto senso.

Ho funzioni scritte in .Net per decodificare utilizzando una chiave privata, crittografare utilizzando una chiave pubblica. Ho anche firmato RSA e verificare e avere una ragionevole comprensione di come tutto questo funzioni, penso.

Attualmente mi viene inviato un valore RSA crittografato utilizzando una chiave privata che dovrei ricavare un valore utilizzabile decifrando utilizzando la chiave pubblica.

Non riesco a capire come farlo. Sono un idiota? È normale?

Mi viene detto dalla persona che mi invia il valore che questo non è un problema in PHP. Non lo so e non ho ancora usato PHP. Non riesco a trovare una libreria per farlo in una delle lingue principali che conosco, C++, Java, C#. Il server su cui sto lavorando utilizza .Net.

Spero che qualcuno possa aiutarmi.

Sarebbe bello se esistesse una soluzione ragionevole oltre a implorare loro di cambiare ciò che stavano facendo.

Questo è il mio metodo (aggiornato dal mio precedente cattivo come sottolineato da Iridium), ma quando cerco di decifrare il valore ottengo un'eccezione

"Errore durante la decodifica imbottitura OAEP."

Se uso rsa.Decrypt (byte, falso) ottengo un'eccezione chiave errata.

public static string DecryptUsingPublic(string dataEncrypted, string publicKey) 
    { 
     if (dataEncrypted == null) throw new ArgumentNullException("dataEncrypted"); 
     if (publicKey == null) throw new ArgumentNullException("publicKey"); 
     try 
     { 
      RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false); 
      RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey); 

      byte[] bytes = Convert.FromBase64String(dataEncrypted); 
      byte[] decryptedBytes = rsa.Decrypt(bytes, true); 

      ArrayList arrayList = new ArrayList(); 
      arrayList.AddRange(decryptedBytes); 

      return Encoding.UTF8.GetString(decryptedBytes); 
     } 
     catch 
     { 
      return null; 
     } 
    } 

    private static RSAParameters LoadRsaPublicKey(String publicKeyFilePath, Boolean isFile) 
    { 
     RSAParameters RSAKeyInfo = new RSAParameters(); 
     byte[] pubkey = ReadFileKey(publicKeyFilePath, "PUBLIC KEY", isFile); 
     byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; 
     byte[] seq = new byte[15]; 
     // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ 
     MemoryStream mem = new MemoryStream(pubkey); 
     BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading 
     byte bt = 0; 
     ushort twobytes = 0; 

     try 
     { 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8230) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      seq = binr.ReadBytes(15);  //read the Sequence OID 
      if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8203) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      bt = binr.ReadByte(); 
      if (bt != 0x00)  //expect null byte next 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8230) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      byte lowbyte = 0x00; 
      byte highbyte = 0x00; 

      if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) 
       lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus 
      else if (twobytes == 0x8202) 
      { 
       highbyte = binr.ReadByte(); //advance 2 bytes 
       lowbyte = binr.ReadByte(); 
      } 
      else 
       return RSAKeyInfo; 
      byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order 
      int modsize = BitConverter.ToInt32(modint, 0); 

      byte firstbyte = binr.ReadByte(); 
      binr.BaseStream.Seek(-1, SeekOrigin.Current); 

      if (firstbyte == 0x00) 
      { //if first byte (highest order) of modulus is zero, don't include it 
       binr.ReadByte(); //skip this null byte 
       modsize -= 1; //reduce modulus buffer size by 1 
      } 

      byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes 

      if (binr.ReadByte() != 0x02)   //expect an Integer for the exponent data 
       return RSAKeyInfo; 
      int expbytes = (int)binr.ReadByte();  // should only need one byte for actual exponent data (for all useful values) 
      byte[] exponent = binr.ReadBytes(expbytes); 


      RSAKeyInfo.Modulus = modulus; 
      RSAKeyInfo.Exponent = exponent; 

      return RSAKeyInfo; 
     } 
     catch (Exception) 
     { 
      return RSAKeyInfo; 
     } 

     finally { binr.Close(); } 
     //return RSAparams; 

    } 

private static RSACryptoServiceProvider InitRSAProvider(RSAParameters rsaParam) 
    { 
     // 
     // Initailize the CSP 
     // Supresses creation of a new key 
     // 
     CspParameters csp = new CspParameters(); 
     //csp.KeyContainerName = "RSA Test (OK to Delete)"; 

     const int PROV_RSA_FULL = 1; 
     csp.ProviderType = PROV_RSA_FULL; 

     const int AT_KEYEXCHANGE = 1; 
     // const int AT_SIGNATURE = 2; 
     csp.KeyNumber = AT_KEYEXCHANGE; 
     // 
     // Initialize the Provider 
     // 
     RSACryptoServiceProvider rsa = 
      new RSACryptoServiceProvider(csp); 
     rsa.PersistKeyInCsp = false; 

     // 
     // The moment of truth... 
     // 
     rsa.ImportParameters(rsaParam); 
     return rsa; 
    } 

    private static int GetIntegerSize(BinaryReader binr) 
    { 
     byte bt = 0; 
     byte lowbyte = 0x00; 
     byte highbyte = 0x00; 
     int count = 0; 
     bt = binr.ReadByte(); 
     if (bt != 0x02)  //expect integer 
      return 0; 
     bt = binr.ReadByte(); 

     if (bt == 0x81) 
      count = binr.ReadByte(); // data size in next byte 
     else 
      if (bt == 0x82) 
      { 
       highbyte = binr.ReadByte(); // data size in next 2 bytes 
       lowbyte = binr.ReadByte(); 
       byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; 
       count = BitConverter.ToInt32(modint, 0); 
      } 
      else 
      { 
       count = bt;  // we already have the data size 
      } 

     while (binr.ReadByte() == 0x00) 
     { //remove high order zeros in data 
      count -= 1; 
     } 
     binr.BaseStream.Seek(-1, SeekOrigin.Current);  //last ReadByte wasn't a removed zero, so back up a byte 
     return count; 
    } 

    private static bool CompareBytearrays(byte[] a, byte[] b) 
    { 
     if (a.Length != b.Length) 
      return false; 
     int i = 0; 
     foreach (byte c in a) 
     { 
      if (c != b[i]) 
       return false; 
      i++; 
     } 
     return true; 
    } 

I due metodi precedenti InitRSAProvider e LoadRsaPublicKey stati ottenuti su tutorial per consentire tasti PEM come stringhe da utilizzare con Net.

+0

Say what? Questo è esattamente ciò che l'algoritmo dovrebbe fare. Decifrare usando una chiave privata ciò che è stato crittografato usando la chiave pubblica corrispondente. O sei confuso alla grande o sono io! C# supporta perfettamente RSA :) –

+0

Siamo spiacenti. Modificato la mia domanda. L'ho scritto completamente sbagliato. Il cervello inizia a arrampicarsi un po 'il venerdì. –

+0

Cosa non riesci a capire? Dici di avere già funzioni per decodificare usando una chiave pubblica. – James

risposta

12

Dopo aver esaminato alcune delle informazioni sulle modalità di crittografia RSA, sembrerebbe che PKCS # 1 v1.5 (che si sta utilizzando, perché si sta chiamando Decrypt(..., false))

" ... può operare sui messaggi della lunghezza fino a k - 11 ottetti (k è la lunghezza ottetto del RSA modulo) "

(RFC 3447, enfasi mia).

Sulla base del messaggio di errore, che indica che la chiave è di 128 byte, il che significa che non è possibile eseguire RSA (en | de) crittati utilizzando PKCS # 1 v1.5 su un messaggio con più di 128-11 = 117 byte.

Invece di crittografare il messaggio direttamente utilizzando RSA, è necessario utilizzare un algoritmo simmetrico per crittografare il corpo del messaggio e crittografare solo la chiave di crittografia simmetrica tramite RSA. Solo se il tuo messaggio è ragionevolmente breve (vale a dire inferiore a 117 byte per la dimensione della chiave), dovresti prendere in considerazione la possibilità di crittografare il messaggio direttamente con RSA.

ho aggiunto quanto segue, partendo dal presupposto che l'input è codificato Base64 come si indica nel vostro commento qui sotto:

public string DecryptUsingPublic(string dataEncryptedBase64, string publicKey) 
    { 
     if (dataEncryptedBase64 == null) throw new ArgumentNullException("dataEncryptedBase64"); 
     if (publicKey == null) throw new ArgumentNullException("publicKey"); 
     try 
     { 
      RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false); 
      RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey); 

      byte[] bytes = Convert.FromBase64String(dataEncryptedBase64); 
      byte[] decryptedBytes = rsa.Decrypt(bytes, false); 

      // I assume here that the decrypted data is intended to be a 
      // human-readable string, and that it was UTF8 encoded. 
      return Encoding.UTF8.GetString(decryptedBytes); 
     } 
     catch 
     { 
      return null; 
     } 
    } 
+0

Grazie per la risposta. Ho letto RFC 3447 tutto il giorno cercando di trovare una soluzione. Il problema principale è che non ho alcun controllo su come il valore è crittografato. Fondamentalmente mi viene dato un valore i.e. value = BASE64 [RSA_PRIVATE_ENCRYPT (some_text)] Mi è stato lasciato capire come decifrare e usare questo valore. Ho provato molte cose ma nessuna gioia. Ho chiesto un esempio di lavoro per la decrittazione da parte della persona che mi ha inviato il valore. Se lo avrò, lo posterò qui. –

+0

Questo non è ciò che suggerisce lo snippet di codice, in quanto non hai decodifica Base64. (Non dovresti usare Encoding.ASCII per questo scopo, dato che è solo a 7 bit). Se il tuo input è Base64, dovresti utilizzare Convert.FromBase64String (dataEncrypted) per ottenere un array di byte da utilizzare con le funzioni RSA. – Iridium

+0

Anche se la mia risposta originale è ancora valida, ho aggiunto del codice alla mia risposta che potrebbe correggere il problema, partendo dal presupposto che l'input sia in realtà codificato Base64 come menzionato nel commento sopra. – Iridium

13

RSA è integrato in .NET: System.Security.Cryptography.RSA.

La crittografia utilizzando la chiave pubblica e la decrittografia con la chiave privata è una delle cose più comuni che gli utenti fanno con algoritmi asimmetrici, consentendo a chiunque di inviarti qualcosa in modo sicuro.

Se lo si fa in un altro modo: crittografare utilizzando la chiave privata e decifrare con la chiave pubblica, il messaggio viene inviato dal titolare della chiave privata. Ma poiché presumibilmente chiunque può ottenere la chiave pubblica, la gente non tende a crittografare l'intero messaggio, ma firma semplicemente un hash dei dati usando la chiave privata. Quindi, lo RSACryptoServiceProvider ha i metodi Sign__ e Verify__ per fare proprio questo.

Ancora, ci sono i metodi Encrypt/Decrypt se il vostro partner insiste.

Detto questo, ho trovato le classi di crittografia Microsoft un po 'complicate da gestire e prive di alcune aree e preferisco di gran lunga lo Bouncy Castle libraries.

+0

Scusa se sono un idiota. Ho ottenuto la mia domanda completamente all'indietro. L'ho modificato per mostrare cosa intendevo davvero. –

+1

Pensato così! Aggiornata la risposta. –

+0

Grazie mille per la tua risposta. In questo caso non riesco a far funzionare il metodo di decrittografia. Ho aggiornato la mia domanda per mostrare dove sta andando male. Probabilmente sto facendo qualcosa di stupido e segnerò questa come risposta non appena avrò capito di cosa si tratta. –

6

RSA è non scopo di crittografare i dati arbitrari, anche meno arbitrario lunghezza dei dati (come @Iridium te l'ha già detto). Il limite dipende dal padding utilizzato e l'utilizzo del padding è molto importante (abbastanza che MS non ti consente di chiamare EncryptValue e DecryptValue direttamente).

il modo corretto per eseguire questa operazione è crittografare la stringa utilizzando un codice simmetrico (come AES), quindi crittografare la chiave segreta utilizzando la chiave pubblica RSA.

L'altra parte sarà in grado di decodificare la chiave segreta (AES) utilizzando la chiave privata RSA. Quindi utilizzando la chiave decrittografare la stringa.

Ho un vecchio (ma ancora attuale) blog entry sull'argomento che include codice sorgente (C#).

+0

Grazie per la risposta. Sono completamente d'accordo con te. Il problema è che non sto crittografando il valore. Ho una chiave privata e una chiave pubblica. Mi viene inviato un valore che è stato crittografato utilizzando la chiave privata e ora devo capire come decodificarlo. Potrebbe non avere senso e potrebbe non esserci una soluzione semplice, ma non ne ho molta esperienza quindi apprezzo tutti i commenti e i suggerimenti. –

+1

Come il tuo blog, a proposito. –

+0

Se si è bloccati sulla fine ** decrypting ** allora non si ha scelta (sicura o meno) che eseguire le operazioni inverse per recuperare i dati. Speriamo che non richieda l'utilizzo di 'DecryptValue' (non dovrebbe esserlo se l'altro, criptando, parte usa .NET) ma anche allora ci sono soluzioni per questo :) Facci sapere! – poupou

Problemi correlati