2015-08-23 2 views
1

Мне нужно перенести crypto-пакет из C++/openssl в чистую реализацию Java. Однако у меня возникают некоторые проблемы, которые я не знаю, как их решить.Миграция openssl в java

Ниже приведен список C++, в котором описывается схема дешифрования, которую я в настоящее время пытаюсь выполнить.

#include <openssl/evp.h> 
#include <openssl/aes.h> 
#include <openssl/rand.h> 
#include <openssl/bio.h> 
#include <openssl/buffer.h> 

// set master key 
AES_KEY master_key; 
const int AES128_KEY_SIZE = 16; 
unsigned char* master_secret = "averysecretmastersecret"; 
AES_set_encrypt_key(master_secret, AES128_KEY_SIZE * 8 , &master_key); 

// Base64 decode; encryptedInput is the original input text 
// b64_output consists of two parts: a leading salt (16 bytes) and the following actual data 
char* b64_output = base64Decode(encryptedInput); // base64Decode(const char* encodedText) -> char* decodedText 

// prepare salt 
const char SALT_LEN = 16; // first byte is reserved. Actually only use 15 bytes = 120 bit 
unsigned char salt[SALT_LEN]; 
memcpy(salt, b64_output, SALT_LEN); // read salt 

// generate key 
const int AES128_KEY_SIZE = 16; 
unsigned char key[AES128_KEY_SIZE]; 
salt[0] = 1; // 
AES_ecb_encrypt(salt, key, &master_key, AES_ENCRYPT); 

// generate iv 
const int AES128_IV_SIZE = 16; 
unsigned char iv[AES128_IV_SIZE]; 
salt[0] = 2; // ensure that key and iv are different 
AES_ecb_encrypt(salt, iv, &master_key, AES_ENCRYPT); 

// initialize cipher context 
EVP_CIPHER_CTX *de; 
de = EVP_CIPHER_CTX_new(); 
EVP_CIPHER_CTX_init(de); 
EVP_DecryptInit_ex(de, EVP_aes_128_cbc(), NULL, key, iv) 

aes_decrypt(b64_output + SALT_LEN, length - SALT_LEN); 

// plaintext is a buffer to contain the output 
int plaintext_size = DEFAULT_BUFFER_SIZE; 
char *plaintext = (char*)malloc(plaintext_size); 
int aes_decrypt(const char *ciphertext, int len) 
{ 
    int p_len = len, f_len = 0; 
    // allocate an extra cipher block size of memory because padding is ON 
    // #define AES_BLOCK_SIZE 16 
    if(p_len + AES_BLOCK_SIZE > plaintext_size) { 
     ASSERT_CALL(enlarge_buffer(plaintext, plaintext_size, p_len + AES_BLOCK_SIZE), "enlarge plaintext buffer failed"); 
    } 

    ASSERT_OPENSSL(EVP_DecryptInit_ex(de, NULL, NULL, NULL, NULL), "sets up decode context failed"); 
    ASSERT_OPENSSL(EVP_DecryptUpdate(de, (unsigned char*)plaintext, &p_len, (unsigned char*)ciphertext, len), "decrypt failed"); 
    EVP_DecryptFinal_ex(de, (unsigned char*)plaintext+p_len, &f_len); 
    return EY_SUCCESS; 
} 

EVP_CIPHER_CTX_free(de); 
dec_result = std::string(plaintext); 

Ниже приведен список Java-код, который я в настоящее время (не работает, конечно), чтобы воспроизвести выше C++ логика:

String encrypted = "AtUKTnCF18kFTJIycg/RXKJ82IVCtaa+eKNVl8FhT0k+wvpc+cBIs5jb/QlLRMf4"; 

String secret = "averysecretmastersecret"; 

int SALT_LEN = 16; 
String keyAlgorithm = "AES"; 
String ECB_TRANSFORM = "AES/ECB/NoPadding"; 
String CBC_TRANSFORM = "AES/CBC/NoPadding"; 

byte[] bytesOfSecret = Arrays.copyOf(secret.getBytes(), 16); 
Key key =new SecretKeySpec(bytesOfSecret, keyAlgorithm); 
Cipher ecbCipher = Cipher.getInstance(ECB_TRANSFORM); 
Cipher cbcCipher = Cipher.getInstance(CBC_TRANSFORM); 

// decode 
byte[] decoded = Base64.getDecoder().decode(encrypted); 

byte[] salt = Arrays.copyOf(decoded, SALT_LEN); 
byte[] data = Arrays.copyOfRange(decoded, SALT_LEN, decoded.length); 

// get iv 
salt[0] = 2; 
ecbCipher.init(Cipher.ENCRYPT_MODE, key); 
byte[] iv = ecbCipher.doFinal(salt); 
iv = Arrays.copyOf(iv, 16); 

AlgorithmParameterSpec parameterSpec = new IvParameterSpec(iv); 

cbcCipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); 
byte[] bytes = cbcCipher.doFinal(data); 
String decrypted = new String(bytes); 

System.out.println(decrypted); 

Есть несколько мест, которые я не знаю, как Теперь от C++ до Java. Во-первых, в коде C++ он использует соль для генерации ключа и iv, которые впоследствии используются для инициализации контекста шифрования EVP, как в EVP_DecryptInit_ex(de, EVP_aes_128_cbc(), NULL, key, iv). Я не знаю эквивалентную операцию в java.

Во-вторых, в коде C++ нет прямого упоминания о том, используется ли дополнение. Я пробовал как NoPadding, так и PKCS5Padding, но не уверен, какой из них правильный.

Итак, как я могу воспроизвести логику C++ в java? Есть ли какой-нибудь пример?

обновление
Я также пробовал BouncyCastle. Он все еще не работает. Ниже мой код:

int SALT_LEN = 16; 

String encrypted = "AtUKTnCF18kFTJIycg/RXKJ82IVCtaa+eKNVl8FhT0k+wvpc+cBIs5jb/QlLRMf4"; 
String password = "averysecretmastersecret"; 

// decode 
byte[] decoded = Base64.getDecoder().decode(encrypted); 
byte[] salt = Arrays.copyOf(decoded, SALT_LEN); 
byte[] data = Arrays.copyOfRange(decoded, SALT_LEN, decoded.length); 

BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine())); 

PBEParametersGenerator generator = new OpenSSLPBEParametersGenerator(); 
byte[] bytesOfSecret = PBEParametersGenerator.PKCS5PasswordToBytes(password.toCharArray()); 
generator.init(bytesOfSecret, salt, 1); 

ParametersWithIV parametersWithIV = (ParametersWithIV) generator.generateDerivedParameters(128, 128); 

// for decryption 
cipher.init(false, parametersWithIV); 

byte[] decrypted = new byte[cipher.getOutputSize(data.length)]; 
System.out.println("expected decrypted size = " + decrypted.length); // prints ... size = 32 

int processedBytes = cipher.processBytes(data, 0, data.length, decrypted, 0); 
System.out.println("processed bytes = " + processedBytes); // prints ... bytes = 16 

cipher.doFinal(decrypted, processedBytes); // Line 59, run into exception 

String output = new String(decrypted); 

System.out.println(output); 

линии 59, как отмечено выше, дает это исключение:

org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted 
    at org.bouncycastle.crypto.paddings.PKCS7Padding.padCount(Unknown Source) 
    at org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher.doFinal(Unknown Source) 
... 
+0

Я подозреваю, 'EVP_DecryptInit_ex' - еще один метод, который вам нужно перенести. –

+0

Вы действительно ничего не мигрируете. Вы пишете эквивалентный код в совершенно другом API. – EJP

ответ

1

Это является примером Java AES шифрования я надеюсь, что это помогает

 String key = "HkJHBKJBvffdbv"; 
     String IV= "qjfghftrsbdghzir"; 
     String theMessageToCifer ="your message"; 

     SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES"); 

     IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes()); 
     try{ 

     //specify your mode 
     Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); 
     cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,ivSpec); 

     encrypted = cipher.doFinal(theMessageToCifer.getBytes()); 

     bytesEncoded = Base64.encode(encrypted); 
     System.out.println(" base64 code " +bytesEncoded); 
     System.out.println("encrypted string: " +encrypted); 
     // decryption 
     cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,ivSpec); 
     byte[] original = cipher.doFinal(encrypted); 
     String originalString = new String(original); 
     System.out.println("Original string: " + originalString); 
     }catch (Exception e){ 
     e.printStackTrace(); 
     } 
+0

Спасибо за ответ. Однако я боюсь, что это мало помогает. Один пробел между вашим примером и примером C++ в моем сообщении заключается в том, что iv предопределен в вашем примере, тогда как iv получен из соли в коде C++. Другой недостаток заключается в том, что соль также создает так называемый ключевой параметр в примере на C++, который не имеет места в вашем примере Java. – JBT

+0

Существует вопрос здесь о stackoverflow говорить о соли в AES с помощью java, я думаю, вы должны взглянуть, он может помочь http://stackoverflow.com/questions/7303103/java-aes-encryption-with-salt –

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