AES256 OpenSSL C++和Java加密:解密时出现BadPaddingException



我正在编写一个程序,其中C++/Qt客户端与Java服务器进行通信。客户端使用OpenSSL库,服务器端使用Crypto库。以下是两者中使用的功能:

服务器端:

public static String encryptWithKey(String plaintext, String key) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom randomSecureRandom = new SecureRandom();
byte[] iv = new byte[IVSIZE];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(key.getBytes("UTF-8")),"AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);

byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));

ByteArrayOutputStream b = new ByteArrayOutputStream();
b.write(iv);
b.write(ciphertext);
return Base64.getEncoder().encodeToString(b.toByteArray());

} catch (Exception e) {
System.err.println("Error while encrypting: " + e.toString());
}

return null;
}

public static String decryptWithKey(String ciphertext, String key) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
byte[] ciph = Base64.getDecoder().decode(ciphertext.getBytes());
byte[] iv = Arrays.copyOfRange(ciph , 0, IVSIZE);
IvParameterSpec ivspec = new IvParameterSpec(ciph,0,IVSIZE);
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(key.getBytes("UTF-8")),"AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
byte[] plaintext = cipher.doFinal(ciph);
return new String(Arrays.copyOfRange(plaintext, IVSIZE, plaintext.length));
} catch (Exception e) {
System.err.println("Error while decrypting: " + e.toString());
}
return null;
}

客户端:

int IVLENGTH = 16;
int KEYSIZE = 32;
QByteArray encryptAESWithExistingKey(QByteArray Base64AESKey, QByteArray &data){
const char *temp_key=QByteArray::fromBase64(Base64AESKey).toStdString().c_str();
//const char *temp_iv="Y)S$adw`=Tz2AFk";
const char *temp_iv=randomBytes(IVLENGTH).data();
unsigned char key[KEYSIZE];
unsigned char iv[IVLENGTH];
for(uint i=0;i<strlen(temp_key);i++){
key[i]=temp_key[i];
}
EVP_CIPHER_CTX *en = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(en);
if(!EVP_EncryptInit_ex(en, EVP_aes_256_cbc(),NULL,key, iv))
{
qCritical() << "EVP_EncryptInit_ex() failed " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
for(uint i=0;i<strlen(temp_iv);i++){
iv[i]=temp_iv[i];
}
QByteArray str;
for(int i=0;i<IVLENGTH;i++) {
str.append(iv[i]);
}
char *input = data.data();
int len = data.size();
int c_len = len + AES_BLOCK_SIZE, f_len = 0;
unsigned char *ciphertext = (unsigned char*)malloc(c_len);
if(!EVP_EncryptInit_ex(en, NULL, NULL, NULL, NULL))
{
qCritical() << "EVP_EncryptInit_ex() failed " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}

if(!EVP_EncryptUpdate(en, ciphertext, &c_len,(unsigned char *)input, len))
{
qCritical() << "EVP_EncryptUpdate() failed " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
if(!EVP_EncryptFinal(en, ciphertext+c_len, &f_len))
{
qCritical() << "EVP_EncryptFinal_ex() failed "  << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
len = c_len + f_len;
EVP_CIPHER_CTX_cipher(en);
QByteArray encrypted = QByteArray(reinterpret_cast<char*>(ciphertext), len);
QByteArray finished;
finished.append(str);
finished.append(encrypted);
free(ciphertext);
return finished.toBase64();
}
QByteArray decryptAESWithExistingKey(QByteArray Base64AESKey, QByteArray &data){
unsigned char key[KEYSIZE];
unsigned char iv[IVLENGTH];

data=QByteArray::fromBase64(data);
QByteArray temp_iv=data.mid(0,IVLENGTH);
data=data.mid(IVLENGTH);
const char *temp_key=QByteArray::fromBase64(Base64AESKey).toStdString().c_str();
for(uint i=0;i<strlen(temp_key);i++){
key[i]=temp_key[i];
}
EVP_CIPHER_CTX *de = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(de);
if(!EVP_DecryptInit_ex(de,EVP_aes_256_cbc(), NULL, key, iv))
{
qCritical() << "EVP_DecryptInit_ex() failed" << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
for(int i=0;i<temp_iv.length();i++){
iv[i]=temp_iv.at(i);
}
QByteArray str;
for(int i=0;i<IVLENGTH;i++) {
str.append(iv[i]);
}
char *input = data.data();
int len = data.size();
int p_len = len, f_len = 0;
unsigned char *plaintext = (unsigned char *)malloc(p_len + AES_BLOCK_SIZE);
if(!EVP_DecryptUpdate(de, plaintext, &p_len, (unsigned char *)input, len))
{
qCritical() << "EVP_DecryptUpdate() failed " <<  ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
if(!EVP_DecryptFinal_ex(de,plaintext+p_len,&f_len))
{
qCritical() << "EVP_DecryptFinal_ex() failed " <<  ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
len = p_len + f_len;
EVP_CIPHER_CTX_cleanup(de);
QByteArray decrypted = QByteArray(reinterpret_cast<char*>(plaintext), len);
free(plaintext);
return decrypted;
}

我对加密编码还很陌生,如果上面的代码看起来很糟糕,那只是我缺乏经验。

问题是,当我在客户端加密字符串并将其发送到服务器进行解密时,我得到:

BadPaddingException:给定的最后一个块没有正确填充。如果在解密过程中使用了坏密钥,则可能会出现这样的问题。

我做错了什么?我在服务器和客户端的主要功能中进行以下功能调用:

服务器端:

String key="YY3jic+tKY4O5+jLiJ/l2RHYORyIX8TeJ7TUy7Flbhg=";
String str="Hello server!"; //String as received from client
str=decryptWithKey(str,key);
if(str.equals("Hello server!")){
String sendback=encryptWithKey("Hello client!",key);
//Send encrypted string back to client
}

客户端:

QByteArray key="YY3jic+tKY4O5+jLiJ/l2RHYORyIX8TeJ7TUy7Flbhg=";
QByteArray message="Hello server!";
QByteArray enc=encryptAESWithExistingKey(key,message);
//Send to server and receive response
QByteArray response;
//Receive ciphertext from server and store it into "response"
qDebug()<<decryptAESWithExistingKey(key,response);
//Expected ouput: "Hello client"

以上代码在服务器中解密失败。由于BadPaddingException是在服务器上抛出的,所以它会加密或不返回任何内容。因此,我的客户端在超时后断开连接。

我提供Base64格式的预生成AES256密钥作为函数参数。我尝试使用OpenSSL和Java的加密库生成的不同AES256密钥。当使用一个库生成密钥时,它将通过与另一端的安全连接共享。显然,对于特定的运行时,双方都使用了相同的密钥。我使用以下代码生成密钥:

JAVA:

public byte[] generateNewAESKey(String password){
byte[] key = "".getBytes();     
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), RandomStringUtils.randomAlphanumeric(password.length()).getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
key = secretKey.getEncoded();
} catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
Logger.getLogger(LoginServiceThread.class.getName()).log(Level.SEVERE, null, ex);
}
return key;
}

注意:RandomStringUtils来自Apache Commons lang3包。

OPENSSL C++:

int SALTSIZE = 8;
int KEYSIZE = 32;
QByteArray generateRandomAES(){
QByteArray msalt = randomBytes(SALTSIZE);
QByteArray passphrase = randomBytes(KEYSIZE);
QByteArray returnkey;
int rounds = 65536;
unsigned char key[KEYSIZE];
unsigned char iv[IVLENGTH] ;
const unsigned char* salt = (const unsigned char*) msalt.constData();
const unsigned char* password = (const unsigned char*) passphrase.constData();
int i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), salt, password, passphrase.length(),rounds,key,iv);

for(int i=0;i<KEYSIZE;i++) {
returnkey.append(key[i]);
}
return returnkey.toBase64();
}

我做错了什么?为什么加密和解密在两者之间不能顺利进行?请帮我拿这个。

我看到的最重要的问题是您使用iv不正确。您需要提供ivEVP_EncryptInit_ex,但您没有。您需要在那里提供iv,然后将其发送到另一侧。

问题是这部分:

for(uint i=0;i<strlen(temp_key);i++){
key[i]=temp_key[i];
}
EVP_CIPHER_CTX *en = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(en);
if(!EVP_EncryptInit_ex(en, EVP_aes_256_cbc(),NULL,key, iv))
{
qCritical() << "EVP_EncryptInit_ex() failed " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
for(uint i=0;i<strlen(temp_iv);i++){ // <<-- here is incorrect. need to be before EVP_EncryptInit_ex
iv[i]=temp_iv[i];
}

评论中提到了其他问题,但这是主要问题。解密函数中也存在同样的问题。

最新更新