Tamaños de clave RSA v / s tamaños de clave pública

1

Soy nuevo en criptografía. Tengo un api proporcionado por un banco. Los bancos dicen que están utilizando el algoritmo RSA con 2048 bits. Usamos esta API primero para obtener la clave pública. Aquí está la clave pública de muestra (algunos caracteres actualizados para seguridad),

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgqvmScnvYGpQ+exGe9OM2tYM+JGjEZbQ
VxQsnBQ7tfSTc6gSsUSFZdASD7Jrr2Mo1EvcBW+wyuENvS5G+d65EGv2lKlFb7iCgVF2RGw5dDdS
kD7hF1lCnXA6TNi7hMwQWeCInEMTeD1ZR52KvRK4jmLr3EWyujTP/h0oyOpBRc5dvoCOgyi7eSXG
q7uUWUOOZNnTtW/fIixdNQJ28Kz5Mf4HykHZxIPTtjg6I1jZgBdVL6pYgwA2oyHGEOe2ObB6ZeTB
9+meHuTxyIRUtTPSNSK0bfHYT56TwruwgJwnrHjvvM07Lzah69wLvhWAiR2mPQb6juG2zCaU5Mad
sesEuQIDAQAB

Este se parece a la cadena Base 64. La longitud es 397. Supongo que esto es Módulo y Exponente es AQAB. Cuando convierto esta Base64 en bytes, se convierten en 294 y (294 * 8) 2352 bits. ¿Por qué 2352 y no 2048 bits?

En realidad, estoy cifrando una cadena y enviándola a la API del Banco tanto en Java como en C #. En Java lo estoy haciendo,

public class RSAUtility {
public static String encrypt(String plainText, String key){
    try{
        PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.decode(key, Base64.DEFAULT)));
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return Base64.encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")),Base64.DEFAULT);
    }catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

Funciona muy bien en java. Pero en C # estoy haciendo esto y no funciona. Cualquier experto puede ayudarme, por favor.

var keyXml = "<RSAKeyValue><Modulus>" + key + "</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

EncrptedValue = CryptUtils.Encrypt(keyXml, "MyString", RsaKeyLengths.Bit2048);

    public static string Encrypt(string publicKey, string data, RsaKeyLengths length = RsaKeyLengths.Bit2048)
    {
        // full array of bytes to encrypt
        byte[] bytesToEncrypt;

        // worker byte array
        byte[] block;

        // encrypted bytes
        byte[] encryptedBytes;

        // length of bytesToEncrypt
        var dataLength = 0;

        // number of bytes in key                
        var keySize = 0;

        // maximum block length to encrypt          
        var maxLength = 0;

        // how many blocks must we encrypt to encrypt entire message?
        var iterations = 0;

        // the encrypted data
        var encryptedData = new StringBuilder();

        // instantiate the crypto provider with the correct key length
        var rsaCryptoServiceProvider = new RSACryptoServiceProvider((int)length);

        // initialize the RSA object from the given public key
        rsaCryptoServiceProvider.FromXmlString(publicKey);

        // convert data to byte array
        bytesToEncrypt = Encoding.Unicode.GetBytes(data);

        // get length of byte array
        dataLength = bytesToEncrypt.Length;

        // convert length of key from bits to bytes
        keySize = (int)length / 8;

        // .NET RSACryptoServiceProvider uses SHA1 Hash function
        // use this to work out the maximum length to encrypt per block
        maxLength = ((keySize - 2) - (2 * SHA1.Create().ComputeHash(bytesToEncrypt).Length));

        // how many blocks do we need to encrypt?
        iterations = dataLength / maxLength;

        // encrypt block by block
        for (int index = 0; index <= iterations; index++)
        {
            // is there more than one full block of data left to encrypt?
            if ((dataLength - maxLength * index) > maxLength)
            {
                block = new byte[maxLength];
            }
            else
            {
                block = new byte[dataLength - maxLength * index];
            }

            // copy the required number of bytes from the array of bytes to encrypt to our worker array
            Buffer.BlockCopy(bytesToEncrypt, maxLength * index, block, 0, block.Length);

            // encrypt the current worker array block of bytes
            encryptedBytes = rsaCryptoServiceProvider.Encrypt(block, true);

            // RSACryptoServiceProvider reverses the order of encrypted bytesToEncrypt after encryption and before decryption.
            // Undo this reversal for compatibility with other implementations
            Array.Reverse(encryptedBytes);

            // convert to base 64 string
            encryptedData.Append(Convert.ToBase64String(encryptedBytes));
        }

        return encryptedData.ToString();
    }
    
pregunta user960567 05.04.2015 - 14:30
fuente

1 respuesta

4

Posiblemente respuesta parcial.

Primero y aparte: no es necesario "asegurar" una clave pública cambiando los bits; todo el propósito y el punto de la criptografía de clave pública es que es segura, aunque la clave pública sea pública, que incluye a los adversarios.

La forma de publicación que tienes NO es solo "módulo y exponente". Es cierto que los últimos 3 bytes en este caso son el exponente, y por suerte, la codificación de la clave total es un múltiplo de 3, por lo que esos 3 bytes son los últimos 4 caracteres de base64. Si está tratando los otros 294-3 bytes como el módulo que está muy mal.

Lo que tienes tienes, como lo dijiste correctamente en las llamadas JCA, es lo que Java llama X509EncodedKeySpec y en términos estándar es la estructura SubjectPublicKeyInfo definida en el estándar X.509 (muy utilizado). Esa estructura es una SECUENCIA ASN.1 que contiene un AlgorithmIdentifier que identifica el algoritmo utilizando otra SECUENCIA en este momento de un IDENTIFICADOR DE OBJETO conocido como OID u OBJETO que en este caso es un OID para RSA y (generalmente) un campo de parámetros de tipo variable que en este caso el caso es NULL, todo eso seguido de una BIT STRING que contiene la codificación específica del algoritmo del valor de publicación, que para RSA se define por PKCS # 1 como se usa en rfc3279 o su actualización rfc4055 , y es una secuencia (nother) de dos INTEGER que son el módulo y el exponente (público) respectivamente. Cada pieza de una estructura ASN.1 tiene su propio encabezado de "tipo y longitud" (pequeño).

Para ser concreto, el valor de su publicación "segura" (y presumiblemente incorrecta) se puede ver fácilmente con la utilidad de línea de comando openssl asn1parse en sus datos. La secuencia SPKI (externa) de AlgId más cadena de bits:

c:\work>openssl asn1parse <temp.b64
    0:d=0  hl=4 l= 290 cons: SEQUENCE
    4:d=1  hl=2 l=  13 cons: SEQUENCE
    6:d=2  hl=2 l=   9 prim: OBJECT            :rsaEncryption
   17:d=2  hl=2 l=   0 prim: NULL
   19:d=1  hl=4 l= 271 prim: BIT STRING

La RSAPublicKey (interna) dentro de la cadena de bits (con los valores numéricos en hexadecimal):

c:\work>openssl asn1parse <temp.b64 -strparse 19
    0:d=0  hl=4 l= 266 cons: SEQUENCE
    4:d=1  hl=4 l= 257 prim: INTEGER           :82ABE649C9EF606A50F9EC467BD38CDA
D60CF891A31196D057142C9C143BB5F49373A812B1448565D0120FB26BAF6328D44BDC056FB0CAE1
0DBD2E46F9DEB9106BF694A9456FB882815176446C39743752903EE11759429D703A4CD8BB84CC10
59E0889C4313783D59479D8ABD12B88E62EBDC45B2BA34CFFE1D28C8EA4145CE5DBE808E8328BB79
25C6ABBB9459438E64D9D3B56FDF222C5D350276F0ACF931FE07CA41D9C483D3B6383A2358D98017
552FAA58830036A321C610E7B639B07A65E4C1F7E99E1EE4F1C88454B533D23522B46DF1D84F9E93
C2BBB0809C27AC78EFBCCD3B2F36A1EBDC0BBE1580891DA63D06FA8EE1B6CC2694E4C69DB1EB04B9
  265:d=1  hl=2 l=   3 prim: INTEGER           :010001

Supongo que si tomas la parte de tu publicación real que es el módulo y la representas en base64 en el elemento <Modulus> que funcionará, pero yo no hago dot-NET.

    
respondido por el dave_thompson_085 05.04.2015 - 23:23
fuente

Lea otras preguntas en las etiquetas