Шифрование AES / CBC на Java, дешифрование на Ruby - PullRequest
3 голосов
/ 25 июня 2011

Я пытаюсь перевести следующий (рабочий) код Java на Ruby.

   public static final String PROVIDER = "BC";
   public static final int IV_LENGTH = 16;
   private static final String HASH_ALGORITHM = "SHA-512";
   private static final String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
   private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
   private static final String SECRET_KEY_ALGORITHM = "AES";
   public String decrypt(SecretKey secret, String encrypted) {

         Cipher decryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
         String ivHex = encrypted.substring(0, IV_LENGTH * 2);
         String encryptedHex = encrypted.substring(IV_LENGTH * 2);
         IvParameterSpec ivspec = new IvParameterSpec(HexEncoder.toByte(ivHex));
         decryptionCipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
         byte[] decryptedText = decryptionCipher.doFinal(HexEncoder.toByte(encryptedHex));
         String decrypted = new String(decryptedText, "UTF-8");
         return decrypted;
        } 

Мой (не работающий) код Ruby такой:

require 'openssl'
require 'digest/sha2'

SECRET = "MY PASSWORD AS RAW TEXT"
IV_LENGHT = 16
encoded = "45D0EC4D910C0A6FF67325FF7362DCBC4613B6F3BFDFE35930D764FB1FE62251"

iv = encoded.slice(0, IV_LENGHT * 2)
e = encoded.slice(IV_LENGHT*2..-1)

binary_iv = iv.unpack('a2'*IV_LENGHT).map{|x| x.hex}.pack('c'*IV_LENGHT)
binary_e = e.unpack('a2'*IV_LENGHT).map{|x| x.hex}.pack('c'*IV_LENGHT)


c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
c.decrypt
c.key = Digest::SHA256.digest(SECRET).slice(0, IV_LENGHT* 2 )
c.iv = binary_iv
d = c.update(binary_e)
d << c.final
puts "decrypted: #{d}\n"

Я пробовалбинарные и недвоичные версии, без удачи.Кто-то может указать на проблему?

Ответы [ 2 ]

3 голосов
/ 20 декабря 2011

Исходя из названия здесь, я собираюсь предположить, что вы хотите иметь возможность шифровать сообщение в Java, а затем расшифровать это сообщение в Ruby, используя пароль- на основе шифрования AES-CBC.

Теперь стандартная библиотека openssl в Ruby легко поддерживает * 2 8 * основанную на пароле функцию извлечения ключей 2 на основе PKCS5 . Вы можете значительно упростить код для расшифровки Ruby , если используете его в своем шифровании Java .

Вот как вы должны шифровать, используя PBKDF2 в PKCS5 в Java:

// in Java-land
import java.security.AlgorithmParameters;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

...

static String printHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        sb.append(String.format("%02x", (b & 0xFF)));
    }
    return sb.toString();
}

public static Map<String,String> encrypt(String msg, String pwd, byte[] salt)
        throws Exception {
    Map<String,String> retval = new HashMap<String,String>();

    // prepare to use PBKDF2/HMAC+SHA1, since ruby supports this readily
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    // our key is 256 bits, and can be generated knowing the password and salt
    KeySpec spec = new PBEKeySpec(pwd.toCharArray(), salt, 1024, 256);
    SecretKey tmp = factory.generateSecret(spec);
    SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

    // given key above, our cippher will be aes-256-cbc in ruby/openssl
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();

    // generate the intialization vector
    byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
    retval.put("iv", printHex(iv));

    byte[] ciphertext = cipher.doFinal(msg.getBytes("UTF-8"));
    retval.put("encrypted", printHex(ciphertext));

    return retval;
}

public static void main(String[] args) throws Exception {
    String msg  = "To Ruby, from Java, with love...";
    String pwd  = "password";
    String salt = "8 bytes!"; // in reality, you would use SecureRandom!

    System.out.println("password (plaintext): " + pwd);
    System.out.println("salt: " + salt);

    Map<String,String> m = encrypt(msg, pwd, salt.getBytes());
    System.out.println("encrypted: " + m.get("encrypted"));
    System.out.println("iv: " + m.get("iv"));
}

Выполнение вышеуказанного приведет к чему-то вроде следующего:

password (plaintext): password
salt: 8 bytes!
encrypted: 4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d
iv: ecbc985b3550edc977a17acc066f2192

Шестнадцатеричные строки используются для зашифрованного сообщения и вектора инициализации, поскольку вы можете использовать OpenSSL для проверки процесса шифрования / дешифрования (настоятельно рекомендуется).

Теперь из Ruby-программы вы должны использовать шифр AES-256-CBC и получать секретный ключ из строк password и salt (не byte[] в соответствии с Java). Используя вывод вышеупомянутой Java-программы, мы имеем:

# from Ruby-land
require 'openssl'

d = OpenSSL::Cipher.new("AES-256-CBC")
d.decrypt
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1("password", "8 bytes!", 1024, d.key_len)
d.key = key
d.iv = "ecbc985b3550edc977a17acc066f2192".scan(/../).map{|b|b.hex}.pack('c*')
data = "4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d".scan(/../).map{|b|b.hex}.pack('c*')
d.update(data) << d.final
=> "To Ruby, from Java, with love..."

ПРИМЕЧАНИЕ. Часть этого кода, написанная на Ruby, во многом дословно взята из документации на японском языке стандартной библиотеки openssl .

1 голос
/ 27 августа 2011

Однажды у меня была похожая проблема с CIPHER_ALGORITHM = "AES / CBC / PKCS5Padding"; и расшифровкой через openSSL-библиотеку в C, которую я не мог решить. Я избежал этой проблемы, используя "AES / CBC / NoPadding" и добавляя определенный отступ к открытому тексту вручную.

...