有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

AES256 OpenSSL C++和java加密:解密时的BADPADION异常

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

服务器端:

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:Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

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

服务器端:

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密钥。当使用一个库生成密钥时,它将通过与另一方的安全连接共享。很明显,对于特定的运行时,同一个键在两侧使用。我使用以下代码生成密钥:

爪哇:

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();
}

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


共 (1) 个答案

  1. # 1 楼答案

    我看到的最重要的问题是,您错误地使用了iv。您需要向EVP_EncryptInit_ex提供iv,但不需要。您需要在那里提供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];
    }
    

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