Demasiados datos para el bloque RSA fallan. ¿Qué es PKCS # 7?

Hablando de javax.crypto.Cipher

Intentaba cifrar datos usando Cipher.getInstance("RSA/None/NoPadding", "BC") pero obtuve la excepción:

  ArrayIndexOutOfBoundsException: demasiados datos para el bloque RSA 

Parece que hay algo relacionado con el “NoPadding”, por lo tanto, al leer sobre el relleno, parece que CBC es el mejor enfoque para usar aquí.

Encontré en google algo sobre “RSA / CBC / PKCS # 7”, ¿qué es este “PKCS # 7”? ¿Y por qué no aparece en los nombres de algoritmo estándar de sun ?

Actualizar:

Me pregunto, si es un problema de relleno, ¿por qué este ejemplo funciona bien?

 import java.math.BigInteger; import java.security.KeyFactory; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import javax.crypto.Cipher; /** * Basic RSA example. */ public class BaseRSAExample { public static void main( String[] args) throws Exception { byte[] input = new byte[] { (byte)0xbe, (byte)0xef }; Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC"); KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC"); // create the keys RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec( new BigInteger("d46f473a2d746537de2056ae3092c451", 16), new BigInteger("11", 16)); RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec( new BigInteger("d46f473a2d746537de2056ae3092c451", 16), new BigInteger("57791d5430d593164082036ad8b29fb1", 16)); RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(pubKeySpec); RSAPrivateKey privKey = (RSAPrivateKey)keyFactory.generatePrivate(privKeySpec); // encryption step cipher.init(Cipher.ENCRYPT_MODE, pubKey); byte[] cipherText = cipher.doFinal(input); // decryption step cipher.init(Cipher.DECRYPT_MODE, privKey); byte[] plainText = cipher.doFinal(cipherText); } } 

Actualización 2:

Me di cuenta de que incluso si uso solo Cipher.getInstance("RSA", "BC") arroja la misma excepción.

Si usa un cifrado en bloque, la entrada debe ser un múltiplo exacto de la longitud del bit del bloque.

Para cifrar datos de longitud arbitrarios, primero debe rellenar sus datos con un múltiplo de la longitud del bloque. Esto se puede hacer con cualquier método, pero hay una serie de estándares. PKCS7 es uno que es bastante común, puede ver una descripción general en el artículo de wikipedia sobre relleno .

Como los bloqueadores de bloques operan en bloques, también necesita encontrar una forma de concatenar los bloques encriptados. Esto es muy importante, ya que las técnicas ingenuas reducen en gran medida la fuerza del cifrado. También hay un artículo de wikipedia sobre esto .

Lo que hizo fue intentar encriptar (o descifrar) los datos de una longitud que no coincidía con la longitud del bloque del cifrado, y también pidió explícitamente que no haya relleno y tampoco el modo de operación de encadenamiento.

En consecuencia, el cifrado de bloque no se pudo aplicar a sus datos y obtuvo la excepción informada.

ACTUALIZAR:

Como respuesta a su actualización y al comentario de GregS, me gustaría reconocer que GregS tenía razón (no sabía esto sobre RSA), y elaborar un poco:

RSA no opera en bits, opera en números enteros. Para usar RSA, por lo tanto, necesita convertir su mensaje de cadena en un entero m: 0 < m < n , donde n es el módulo de los dos primos distintos elegidos en el proceso de generación. El tamaño de una clave en el algoritmo RSA normalmente se refiere a n . Se pueden encontrar más detalles al respecto en el artículo de Wikipedia sobre RSA .

El proceso de convertir un mensaje de cadena en un entero, sin pérdida (por ejemplo, truncar ceros iniciales), generalmente se sigue el estándar PKCS # 1 . Este proceso también agrega otra información para la integridad del mensaje (un resumen de hash), una seguridad semántica (un IV) ed cétera. Con estos datos adicionales, la cantidad máxima de bytes que se pueden suministrar a la RSA / None / PKCS1Padding es (keylength - 11). No sé cómo PKCS # 1 asigna los datos de entrada al rango de enteros de salida, pero mi impresión es que puede tomar cualquier longitud de entrada menor o igual que keylegth - 11 y producir un entero válido para el cifrado RSA.

Si no usa relleno, su entrada simplemente se interpretará como un número. Su entrada de ejemplo, {0xbe, 0xef} probablemente se interpretará como {10111110 + o 11101111} = 1011111011101111_2 = 48879_10 = beef_16 (sic!). Desde 0

Esto se menciona en las preguntas frecuentes de bouncycastle . También declaran lo siguiente:

La implementación de RSA que se envía con Bouncy Castle solo permite el cifrado de un solo bloque de datos. El algoritmo RSA no es adecuado para la transmisión de datos y no debe usarse de esa manera. En una situación como esta, debe encriptar los datos usando una clave generada aleatoriamente y un cifrado simétrico, luego debe encriptar la clave generada aleatoriamente usando RSA, y luego enviar los datos encriptados y la clave aleatoria encriptada al otro extremo donde puedan invierta el proceso (es decir, descifre la clave aleatoria usando su clave privada RSA y luego descifre los datos).

RSA es una encriptación asimétrica de un solo uso con restricciones. Encripta un solo “mensaje” de una vez, pero el mensaje debe encajar dentro de límites bastante estrictos en función del tamaño de la clave pública. Para una clave RSA típica de 1024 bits, la longitud máxima del mensaje de entrada (con RSA como se describe en el estándar PKCS # 1 ) es de 117 bytes, no más. Además, con dicha clave, el mensaje cifrado tiene una longitud de 128 bytes, independientemente de la longitud del mensaje de entrada. Como mecanismo de cifrado genérico, RSA es muy ineficiente y desperdicia ancho de banda de red.

Los sistemas de encriptación simétrica (por ejemplo, AES o 3DES) son mucho más eficientes y vienen con “modos de encadenamiento” que les permiten procesar mensajes de entrada de longitud arbitraria. Pero no tienen la propiedad “asimétrica” ​​de RSA: con RSA, puede hacer que la clave de cifrado sea pública sin revelar la clave de descifrado. Ese es el objective de RSA. Con el cifrado simétrico, quien tiene la capacidad de cifrar un mensaje también tiene toda la información necesaria para descifrar los mensajes, por lo tanto, no puede hacer pública la clave de cifrado porque también haría pública la clave de descifrado.

Por lo tanto, es habitual utilizar un sistema híbrido, en el que un mensaje (grande) está cifrado simétricamente (con, por ejemplo, AES), utilizando una clave simétrica (que es una secuencia corta arbitraria de bytes aleatorios), y tiene esa clave cifrada con RSA. La parte receptora utiliza el descifrado de RSA para recuperar esa clave simétrica, y luego la usa para descifrar el mensaje en sí.

Más allá de la descripción más bien simplista anterior, los sistemas criptográficos, en particular los sistemas híbridos, están repletos de pequeños detalles que, si no se tienen en cuenta, pueden hacer que su aplicación sea extremadamente débil contra los atacantes. Entonces, es mejor usar un protocolo con una implementación que ya maneje todo ese trabajo duro. PKCS # 7 es un protocolo de este tipo. Hoy en día, está estandarizado bajo el nombre de CMS . Se usa en varios lugares, por ejemplo, en el corazón de S / MIME (un estándar para encriptar y firmar correos electrónicos). Otro protocolo conocido, destinado a encriptar el tráfico de red, es SSL (ahora estandarizado bajo el nombre de TLS , y se usa a menudo en combinación con HTTP como el famoso protocolo “HTTPS”).

Java contiene una implementación de SSL (ver javax.net.ssl ). Java no contiene una implementación de CMS (al menos no en su API), pero Bouncy Castle tiene algún código para CMS.

Este error indica que el tamaño de los datos de entrada es mayor que el tamaño del módulo de clave. Necesitará un tamaño de clave mayor para encriptar los datos. Si cambiar la longitud de la clave no es una opción, como alternativa, puede que necesite investigar si realmente está esperando esa gran información de entrada.

RSA solo se puede usar para encriptar, cuando la cantidad de bits que se utilizan para encriptar es mayor que el tamaño de lo que está atando para encriptar + 11 bytes

Estándares de criptografía de clave pública – PKCS

Desplácese un poco hacia abajo y lo verá. No es un algortihm de cifrado (como RSA) o un modo de cifrado como CBC, sino una descripción de la forma en que el certificado se codifica como bytes (es decir, una syntax de estructura de datos). Puede encontrar las especificaciones aquí .

PKCS # 7 está en la lista (refiriéndose a su enlace). Su encoding es PKCS7

Descripción

Un objeto SignedData PKCS # 7, con el único campo significativo son los certificados.

Use java.security.cert.CertificateFactory o CertPath cuando use PKCS7 .


RSA es un cifrado de bloque. Encripta el bloque del mismo tamaño de clave. Por lo tanto, BouncyCastle RSA ofrece una excepción si intenta encriptar un bloque que es más largo que el tamaño de la clave.

Eso es todo lo que puedo decirte hasta ahora.

No debe encriptar sus datos usando RSA directamente. Encripte sus datos con una clave simétrica aleatoria (es decir, AES / CBC / PKCS5Padding) y cifre la clave simétrica utilizando RSA / None / PKCS1Padding.