2012-03-11 3 views
5

Я пишу небольшое приложение для передачи файлов более или менее, чтобы узнать больше о программных основах шифрования. Идея состоит в создании пары ключей RSA, обмене открытыми ключами и отправке AES iv и ключа для дальнейшего расшифровки. Я хочу, чтобы зашифровать ключ AES с открытым ключом приемники RSA, например, так:Шифрование ключа 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 (данные) в него, массив казалось бы уменьшен до 256 байт (.leng th говорит так, по крайней мере). Почему это было бы и дальше, что я делаю неправильно?

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

+1

Привет шлет, пожалуйста, напишите ваши ** ** полный ответ в ответ вместо редактирования в вашем вопросе (вы сказать нам, что проблема была, а не ответ). Затем вы можете принять свой собственный ответ через некоторое время. Или примите мое, что объясняет, почему вы должны использовать '' RSA/ECB/PKCS1Padding'' вместо '' RSA/ECB/NoPadding'' ... –

+0

Обратите внимание, что вы должны использовать защиту целостности (например, подпись) при создании онлайн-протокол, который выполняет шифрование. Даже шифрование RSA может быть приемлемо, например, атаки оракула. Отсутствие защиты целостности является распространенной ошибкой, хотя эта проблема ограничена, если вы используете PKCS # 1 v1.5 (подразумевается «RSA/ECB/PKCS1Padding»). –

+0

В качестве побочного элемента ECB редко является хорошим режимом для блочного шифрования. – CodesInChaos

ответ

4

Как упоминалось выше, вы не можете просто использовать «необработанный» 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; 
} 
+1

Привет, созданный небольшой редактор, чтобы явно полагаться на «RSA/ECB/PKCS1Padding» вместо использования поставщика по умолчанию (который возвращает тот же результат в текущем поставщике Oracle JCE, но вы гораздо более безопасно указываете его напрямую). –

+0

спасибо за помощь/разъяснение, owlstead! – hat

0

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

Comment Source

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

+0

Вы правы, похоже, что он заполняет длину ключа (2048 бит ключей, в моем случае). Я также не вижу поддержки NOPADDING с объектом Cipher Java 7, но это не исключение для меня. Полагаю, я буду исследовать разбор реальных данных, которые мне нужны. Благодаря! – hat

4

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

+1

Конечно, вы также можете использовать преемника OAEP, но это действительно не требуется и может быть менее совместимым, чем стандартная по умолчанию схема PKCS # 1 v1.5. –

Смежные вопросы