Шифрование ключа AES с открытым ключом RSA - PullRequest
6 голосов
/ 12 марта 2012

Я пишу небольшое приложение для передачи файлов, более или менее как способ узнать больше о программных основах шифрования.Идея состоит в том, чтобы сгенерировать пару ключей RSA, обменяться открытыми ключами и отправить AES iv и ключ для дальнейшей расшифровки.Я хочу зашифровать ключ AES открытым ключом RSA получателя, например, так:

// encode the SecretKeySpec
private byte[] EncryptSecretKey ()
{
    Cipher cipher = null;
    byte[] key = null;

    try
    {
        cipher = Cipher.getInstance("RSA/ECB/NOPADDING");
        // contact.getPublicKey returns a public key of type Key
        cipher.init(Cipher.ENCRYPT_MODE, contact.getPublicKey() );
        // skey is the SecretKey used to encrypt the AES data
        key = cipher.doFinal(skey.getEncoded());
    }
    catch(Exception e )
    {
        System.out.println ( "exception encoding key: " + e.getMessage() );
        e.printStackTrace();
    }
    return key;
}

Затем я записываю значение ключа получателю и расшифровываю его следующим образом:

private SecretKey decryptAESKey(byte[] data )
{
    SecretKey key = null;
    PrivateKey privKey = null;
    Cipher cipher = null;

    System.out.println ( "Data as hex: " + utility.asHex(data) );
    System.out.println ( "data length: " + data.length );
    try
    {
        // assume this loads our private key
        privKey = (PrivateKey)utility.loadLocalKey("private.key", false);

        cipher = Cipher.getInstance("RSA/ECB/NOPADDING");
        cipher.init(Cipher.DECRYPT_MODE, privKey );
        key = new SecretKeySpec(cipher.doFinal(data), "AES");

        System.out.println ( "Key decrypted, length is " + key.getEncoded().length );
        System.out.println ( "data: " + utility.asHex(key.getEncoded()));
    }
    catch(Exception e)
    {
        System.out.println ( "exception decrypting the aes key: " + e.getMessage() );
        e.printStackTrace();
        return null;
    }

    return key;
}

В консоли, с другой стороны, я получаю это как вывод:

read_bytes for key: 16
data length: 16
Data as hex: <hex string>
Key decrypted, length is 256
java.security.InvalidKeyException: Invalid AES key length: 256 bytes

Кроме того, если я создаю байтовый массив размером 16 и помещаю в него вывод cipher.doFinal (data),размер массива, по-видимому, уменьшен до 256 байт (по крайней мере, .length так говорит).С чем это связано, и, кроме того, что я делаю неправильно?

edit
Я решил это и подумал, что опубликую проблему на случай, если кто-то столкнется с этим.Проблема, оказывается, была в RSA / ECB / NOPADDING.По какой-то странной причине это испортило мое создание SecretKey, когда я передал его клиенту.Это может иметь какое-то отношение к тому, как я генерирую пары ключей (я использую для этого getInstance ("RSA")), но я не совсем уверен.

Ответы [ 3 ]

5 голосов
/ 19 марта 2012

Как упомянул owlstead, вы не можете просто использовать «сырой» RSA без заполнения для шифрования / дешифрования.Для одного это очень небезопасно, а для другого библиотеки Java даже не поддерживают его.Ниже приведен рабочий код для шифрования / дешифрования ключа AES с использованием пар ключей RSA.

private byte[] EncryptSecretKey ()
{
    Cipher cipher = null;
    byte[] key = null;

    try
    {
        // initialize the cipher with the user's public key
        cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, contact.getPublicKey() );
        key = cipher.doFinal(skey.getEncoded());
    }
    catch(Exception e )
    {
        System.out.println ( "exception encoding key: " + e.getMessage() );
        e.printStackTrace();
    }
    return key;
}

Расшифровка ключа AES выглядит следующим образом:

private SecretKey decryptAESKey(byte[] data )
{
    SecretKey key = null;
    PrivateKey privKey = null;
    Cipher cipher = null;

    try
    {
        // this is OUR private key
        privKey = (PrivateKey)utility.loadLocalKey(
                                ConfigFrame.privateKeyLocation, false);

        // initialize the cipher...
        cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privKey );

        // generate the aes key!
        key = new SecretKeySpec ( cipher.doFinal(data), "AES" );
    }
    catch(Exception e)
    {
        System.out.println ( "exception decrypting the aes key: " 
                                               + e.getMessage() );
        return null;
    }

    return key;
}
4 голосов
/ 14 марта 2012

Вы не можете просто использовать «сырой» RSA для шифрования данных без заполнения.Вам нужна какая-то схема заполнения, хотя бы по соображениям безопасности.Обычно используется «RSA / ECB / PKCS1Padding».Это может зашифровать данные до 11 байтов меньше, чем размер ключа.Он гарантирует, что данные вписываются в модуль, и добавляет как минимум 8 байтов случайных данных, чтобы убедиться, что шифрование, например, слово «Да» дважды, не приводит к двум одинаковым зашифрованным текстам.Наконец, он гарантирует, что вы сможете узнать размер зашифрованных данных в октетах, поэтому вы можете просто зашифровать 16, 24 или 32 байта, составляющих ключ AES.

0 голосов
/ 12 марта 2012

Стоит помнить, что с RSA и многими другими Алгоритмы, включая AES, обычно «полезные» данные, которые вы предоставляете не буквально данные, которые зашифрованы. Обычно, некоторые дополнительные данные должен быть включен, например, с указанием фактической длины данные в некотором роде, данные для любой проверки целостности ... Для пользователя, количество входных байтов не обязательно равно количеству байтов следующее шифрование.

Источник комментариев

Чтобы получить правильный размер ключа, вы можете использовать алгоритм HashAlgorithm для ключа (SHA), который даст вам фиксированный размер вывода. В противном случае вы можете просто использовать первые 16 байтов в качестве ключа и игнорировать остальные? Удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...