You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现AES加密、C语言通过OpenSSL解密出现问题求助

Java (BouncyCastle) Encryption → C++ Decryption Guide

Alright, let's tackle this cross-language encryption/decryption problem. The golden rule here is every encryption parameter must match exactly between Java and C++—algorithm, key length, mode, padding, IV (if required), etc. Even a tiny mismatch will lead to failed decryption or garbage output. I’ll use AES-256-CBC (a common, secure choice) to walk you through a complete, working implementation.

Step 1: Complete the Java Encryption Code

Your provided code snippet is incomplete, so I’ll fill in the gaps with a standard BouncyCastle AES setup. This ensures we have clear, reproducible encryption logic:

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Base64;

public class AesCryptoUtil {
    static {
        // Register BouncyCastle provider
        Security.addProvider(new BouncyCastleProvider());
    }

    // AES-256 requires a 32-byte key; adjust to 16 bytes for AES-128
    private static final int AES_KEY_SIZE = 256;
    // CBC mode needs a 16-byte IV (matches AES block size)
    // ⚠️ In production, generate a random IV per encryption and store/transmit it with the ciphertext!
    private static final String FIXED_IV = "1234567890abcdef";

    public byte[] encryptJson(String jsonData) throws Exception {
        SecretKey secretKey = getSecretEncryptionKey();
        return encryptText(jsonData, secretKey);
    }

    public static SecretKey getSecretEncryptionKey() throws Exception {
        // Option 1: Generate a new key (for initial setup)
        // KeyGenerator keyGen = KeyGenerator.getInstance("AES", "BC");
        // keyGen.init(AES_KEY_SIZE);
        // return keyGen.generateKey();

        // Option 2: Restore key from encoded bytes (what you'll use for C++ decryption)
        // Replace with your actual key's Base64 string from getEncoded()
        byte[] keyBytes = Base64.getDecoder().decode("YourBase64EncodedKeyFromJava");
        return new SecretKeySpec(keyBytes, "AES");
    }

    private byte[] encryptText(String plainText, SecretKey secretKey) throws Exception {
        // Use AES/CBC/PKCS5Padding (PKCS5 = PKCS7 for 16-byte blocks like AES)
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
        IvParameterSpec ivSpec = new IvParameterSpec(FIXED_IV.getBytes("UTF-8"));
        
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
        return cipher.doFinal(plainText.getBytes("UTF-8"));
    }

    // Optional: Java-side decrypt to verify encryption works
    private String decryptText(byte[] cipherText, SecretKey secretKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
        IvParameterSpec ivSpec = new IvParameterSpec(FIXED_IV.getBytes("UTF-8"));
        
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
        byte[] decryptedBytes = cipher.doFinal(cipherText);
        return new String(decryptedBytes, "UTF-8");
    }
}

Key Java Notes:

  • Parameter Consistency: AES/CBC/PKCS5Padding is critical—C++ will need to mirror this exactly.
  • Key Handling: getSecretEncryptionKey().getEncoded() gives you the raw key bytes. Convert this to Base64 for safe transmission/storage (binary bytes can get corrupted in text channels).
  • IV Best Practice: Never hardcode an IV in production! Generate a random 16-byte IV each time you encrypt, then prepend it to the ciphertext before sending to C++. Decrypt by first extracting the IV from the start of the ciphertext.

Step 2: C++ Decryption Implementation (Using OpenSSL)

OpenSSL is the go-to library for C++ crypto, and it’s fully compatible with BouncyCastle when parameters match. Here’s the corresponding decrypt code:

#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <openssl/aes.h>
#include <openssl/evp.h>

// AES-256-CBC decryption with PKCS7 padding (matches Java's PKCS5Padding)
std::string aes_256_cbc_decrypt(const std::vector<unsigned char>& ciphertext,
                                const std::vector<unsigned char>& key,
                                const std::vector<unsigned char>& iv) {
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) {
        throw std::runtime_error("Failed to create encryption context");
    }

    // Initialize decrypt context with AES-256-CBC
    if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), iv.data()) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("Failed to initialize decryption");
    }

    // Allocate buffer for plaintext (add extra block size to handle padding)
    std::vector<unsigned char> plaintext(ciphertext.size() + AES_BLOCK_SIZE);
    int bytes_processed;
    int total_plaintext_len = 0;

    // Process main ciphertext chunk
    if (EVP_DecryptUpdate(ctx, plaintext.data(), &bytes_processed, ciphertext.data(), ciphertext.size()) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("Decryption update failed");
    }
    total_plaintext_len += bytes_processed;

    // Finalize decryption (handles padding removal)
    if (EVP_DecryptFinal_ex(ctx, plaintext.data() + total_plaintext_len, &bytes_processed) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("Decryption finalization failed (check key, IV, or ciphertext)");
    }
    total_plaintext_len += bytes_processed;

    EVP_CIPHER_CTX_free(ctx);

    // Convert to UTF-8 string (matches Java's encoding)
    return std::string(plaintext.begin(), plaintext.begin() + total_plaintext_len);
}

int main() {
    try {
        // 1. Raw key bytes from Java's getSecretEncryptionKey().getEncoded()
        // If you transmitted it as Base64, decode it first to get these bytes
        std::vector<unsigned char> key = { /* Insert your Java key bytes here */ };
        
        // 2. IV must EXACTLY match the one used in Java (16 bytes)
        std::vector<unsigned char> iv = {'1','2','3','4','5','6','7','8','9','0','a','b','c','d','e','f'};
        
        // 3. Ciphertext bytes received from Java
        std::vector<unsigned char> ciphertext = { /* Insert your Java ciphertext bytes here */ };

        std::string decrypted_json = aes_256_cbc_decrypt(ciphertext, key, iv);
        std::cout << "Decrypted JSON:\n" << decrypted_json << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error during decryption: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}

Compilation & Usage:

Compile with OpenSSL linked (you’ll need OpenSSL installed on your system):

g++ -o aes_decrypt aes_decrypt.cpp -lcrypto

Key C++ Notes:

  • Padding Match: Java’s PKCS5Padding is identical to OpenSSL’s PKCS7Padding for AES (since AES uses 16-byte blocks), so no extra work needed here.
  • Encoding: We use UTF-8 for string conversion to match Java’s getBytes("UTF-8").
  • Error Handling: The code includes basic error checking—if decryption fails, the exception will hint at possible issues (bad key, wrong IV, corrupted ciphertext).

Step 3: Troubleshooting Common Issues

  • Decryption Fails with Padding Error: 9 times out of 10, this means your parameters don’t match. Double-check key length, mode, IV, and padding between Java and C++.
  • Garbage Output: Likely a byte encoding issue (e.g., Java used UTF-16 but C++ uses UTF-8) or incorrect IV.
  • Key Mismatch: Ensure you’re using the exact raw bytes from getEncoded()—don’t accidentally truncate or encode/decode the key incorrectly.

内容的提问来源于stack exchange,提问作者Harshil Makwana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:42:46