2011-10-21 44 views
9

Sé que la respuesta principal que probablemente obtendré es ¿por qué demonios querría hacer eso?Uso de una clave pública de RSA para descifrar una cadena cifrada con la clave privada de RSA

Desafortunadamente, a pesar de mis protestas tengo que hacerlo, aunque sé que tiene poco sentido.

Tengo funciones escritas en .Net para descifrar usando una clave privada, encriptar usando una clave pública. También firmo y verifico RSA y tengo una comprensión razonable de cómo funciona todo esto, creo.

Me están enviando un valor cifrado con RSA utilizando una clave privada que se supone que debo obtener un valor utilizable mediante el descifrado con la clave pública.

Parece que no puedo encontrar la manera de hacerlo. ¿Estoy siendo un idiota? ¿Es esto algo normal?

La persona que me envía me dice que esto no es un problema en PHP. No sé y aún no he usado PHP. No puedo encontrar una biblioteca para hacerlo en ninguno de los idiomas principales que conozco, es decir, C++, Java, C#. El servidor en el que estoy trabajando usa .Net.

Espero que alguien pueda ayudarme.

Sería genial si hay algún tipo de solución razonable además de rogarles que cambien lo que están haciendo.

Este es mi método (actualizado desde mi mala anterior como se ha señalado por Iridium), pero cuando trato de descifrar el valor consigo una excepción

"Se produjo un error al decodificar el relleno OAEP."

Si uso rsa.Decrypt (bytes, falso) obtengo una excepción de clave incorrecta.

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

Los dos métodos anteriores InitRSAProvider y LoadRsaPublicKey se incorporaron de tutoriales para permitir que las teclas de PEM como cadenas para ser utilizado con .NET.

+0

Di que? Eso es exactamente lo que se supone que debe hacer el algoritmo. Descifre usando una clave privada lo que fue encriptado usando la clave pública correspondiente. ¡O estás confundido a lo grande o lo estoy! C# perfectamente compatible con RSA :) –

+0

Lo siento. Editado mi pregunta. Lo escribí completamente mal. Cerebro comienza a revolver un poco los viernes. –

+0

¿Qué no puedes entender? Usted dice que ya tiene funciones para descifrar usando una clave pública. – James

Respuesta

12

Después de haber observado algo de la información en los modos de encriptación RSA, parecería que PKCS # 1 v1.5 (que está utilizando, ya que está llamando Decrypt(..., false))

" ... puede operar en los mensajes de longitud hasta K - 11 octetos (k es la longitud octeto de la RSA módulo) "

(RFC 3447, énfasis mío).

Según el mensaje de error, que indica que su clave es de 128 bytes, eso significa que no puede realizar la criptación RSA (en | de) usando PKCS # 1 v1.5 en un mensaje con más de 128 - 11 = 117 bytes.

En lugar de cifrar su mensaje directamente utilizando RSA, debe utilizar un algoritmo simétrico para cifrar el cuerpo del mensaje y cifrar solo la clave de cifrado simétrica mediante RSA. Solo si su mensaje es razonablemente corto (es decir, menos de 117 bytes para su tamaño de clave), debería considerar encriptar el mensaje directamente con RSA.

he añadido lo siguiente, suponiendo que su entrada está codificado base 64 como se indica en su comentario a continuación:

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

Gracias por su respuesta. He estado leyendo RFC 3447 todo el día tratando de encontrar una solución. El principal problema es que no tengo control sobre cómo se encripta el valor. Básicamente, se me da un valor, es decir, value = BASE64 [RSA_PRIVATE_ENCRYPT (some_text)] Me ha quedado la idea de descifrar y usar este valor. He intentado muchas cosas pero no alegría. He pedido un ejemplo de trabajo para descifrado de la persona que me envía el valor. Si lo consigo, lo publicaré aquí. –

+0

Eso no es lo que su fragmento de código sugiere, ya que no tiene decodificación Base64. (Tampoco debería utilizar Encoding.ASCII para este fin, ya que solo es de 7 bits). Si su entrada es Base64, debe usar Convert.FromBase64String (dataEncrypted) para obtener una matriz de bytes para usar con las funciones RSA. – Iridium

+0

Si bien aún se aplica mi respuesta original, he agregado un código a mi respuesta que puede corregir su problema, basado en la suposición de que la entrada está codificada en Base64 como mencionó en su comentario anterior. – Iridium

13

RSA está incorporado en .NET: System.Security.Cryptography.RSA.

Encriptar usando la clave pública y descifrar con la clave privada es una de las cosas más comunes que las personas hacen con algoritmos asimétricos, permite que cualquiera le envíe algo de forma segura.

Si lo haces de la otra manera: encriptar usando la clave privada, y descifrar con la clave pública, entonces prueba que el mensaje fue enviado por el titular de la clave privada. Pero debido a que presumiblemente alguien puede obtener la clave pública, las personas no tienden a encriptar todo el mensaje, sino que simplemente firman un hash de los datos usando la clave privada. Por lo tanto, RSACryptoServiceProvider tiene Sign__ y Verify__ métodos para hacer precisamente eso.

Aún así, hay Encrypt/Decrypt métodos si su pareja insiste.

Diciendo eso, he encontrado que las clases de cifrado de Microsoft son un poco difíciles de tratar y carecen en ciertas áreas y prefieren mucho el Bouncy Castle libraries.

+0

Lo siento, soy un idiota. Tengo mi pregunta completamente al revés. Lo he editado para mostrar lo que realmente quise decir. –

+1

¡Pensé que sí! Actualizado la respuesta. –

+0

Muchas gracias por su respuesta. Parece que no puedo hacer que el método de descifrado funcione en este caso. He actualizado mi pregunta para mostrar dónde va mal. Probablemente estoy haciendo algo tonto y marcaré esto como la respuesta tan pronto como descubra qué es. –

6

RSA es no significaba para cifrar datos arbitrarios, incluso menos arbitraria longitud de datos (como @Iridium ya te dijo). El límite depende del relleno utilizado y el uso del relleno es muy muy importante (suficiente para que MS no le permita llamar al EncryptValue y DecryptValue directamente).

right forma de hacer esto es cifrar su cadena utilizando un cifrado simétrico (como AES) y luego cifrar la clave secreta con la clave pública RSA.

La otra parte podrá descifrar la clave secreta (AES) con la clave privada RSA. Luego, usando la clave, descifra tu cadena.

Tengo un viejo (pero todavía actualizado) blog entry sobre el tema que incluye código fuente (C#).

+0

Gracias por su respuesta. Estoy completamente de acuerdo contigo. El problema es que no estoy encriptando el valor. Tengo una clave privada y una clave pública. Me están enviando un valor cifrado con la clave privada y ahora tengo que descubrir cómo descifrarlo. Puede que no tenga sentido y puede que no haya una solución simple, pero no tengo mucha experiencia con esto, así que aprecio todos los comentarios y sugerencias. –

+1

Como su blog por cierto. –

+0

Si se atascó en el extremo de ** descifrado **, entonces no tiene otra opción (segura o no) que hacer las operaciones inversas para recuperar los datos. Esperemos que no requiera que uses 'DecryptValue' (no debería ser si el otro, encriptando, party usa .NET) pero aun así hay soluciones para eso :) ¡Haznos saber! – poupou

Cuestiones relacionadas