2015-08-23 72 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++代碼中,它使用salt生成密鑰和iv,隨後用於初始化EVP密碼上下文,如EVP_DecryptInit_ex(de, EVP_aes_128_cbc(), NULL, key, iv)中所述。我不知道在java中的等效操作。其次,在C++代碼中沒有直接提到是否使用了填充。我嘗試了NoPadding和PKCS5Padding,但不確定哪一個是正確的。

那麼,我該如何重現java中的C++邏輯呢?那裏有沒有例子?

更新
我也試過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++代碼中的鹽中派生的。另一個缺點是salt也會在C++示例中產生一個所謂的關鍵參數,這在你的java示例中沒有地位。 – JBT

+0

有一個關於stackoverflow在使用java的AES中談論鹽的問題我想你應該看看它可能會有所幫助http://stackoverflow.com/questions/7303103/java-aes-encryption-with-salt –