2015-08-23 69 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 –