请求协助:将VB.NET的RijndaelManaged加密代码转Java
I've worked through converting VB.NET's RijndaelManaged encryption/decryption functions to Java while maintaining full compatibility. The key to getting this right is matching every single parameter and behavior from the VB code—small mismatches (like encoding or key derivation order) will break compatibility.
Core Compatibility Notes First
Before diving into code, let's align on the critical details your VB.NET code is likely using (adjust if your actual VB settings differ):
- Key Derivation: Uses
Rfc2898DeriveBytes(PBKDF2 with HMAC-SHA1) with a fixed salt and iteration count - Cipher Settings:
BlockSize=256,KeySize=256,Mode=CBC,Padding=PKCS7 - Encoding: VB's
Encoding.UTF8(orEncoding.Unicode—we'll note how to adjust this) - IV Generation: Derived from the same
Rfc2898DeriveBytesinstance as the key (not a separate one)
Java Implementation (With BouncyCastle for 256-bit Block Size)
Java's standard library doesn't support Rijndael with 256-bit block sizes (only AES 128-bit blocks), so we use BouncyCastle for full compatibility.
Step 1: Add BouncyCastle Dependency
For Maven, add this to your pom.xml:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency>
Step 2: Complete Java Class
import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.Security; import java.util.Base64; public class ClRijndaelManaged { static { // Register BouncyCastle as a security provider Security.addProvider(new BouncyCastleProvider()); } // Replace these values with EXACT matches from your VB.NET code private static final byte[] SALT = Base64.getDecoder().decode("VGVzdFNhbHQ="); // Match VB's Convert.FromBase64String("your-salt") private static final int ITERATION_COUNT = 1000; // Match VB's Rfc2898DeriveBytes iteration count private static final int KEY_SIZE = 256; // Match VB's KeySize private static final int BLOCK_SIZE = 256; // Match VB's BlockSize /** * Equivalent to VB.NET's fncVerschluesseln */ public static String encrypt(String plainText, String password) throws Exception { // Derive combined key + IV bytes (matches VB's sequential GetBytes calls) SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATION_COUNT, KEY_SIZE + BLOCK_SIZE); byte[] keyIvBytes = factory.generateSecret(spec).getEncoded(); // Split into key and IV (VB gets key first, then IV) byte[] keyBytes = new byte[KEY_SIZE / 8]; byte[] ivBytes = new byte[BLOCK_SIZE / 8]; System.arraycopy(keyIvBytes, 0, keyBytes, 0, keyBytes.length); System.arraycopy(keyIvBytes, keyBytes.length, ivBytes, 0, ivBytes.length); // Initialize Rijndael cipher (CBC mode + PKCS7 padding) Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7Padding", "BC"); SecretKey secretKey = new SecretKeySpec(keyBytes, "Rijndael"); IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); // Encrypt and encode to Base64 (matches VB's Convert.ToBase64String) byte[] plainBytes = plainText.getBytes(StandardCharsets.UTF_8); // IF your VB code uses Encoding.Unicode, replace with StandardCharsets.UTF_16LE byte[] encryptedBytes = cipher.doFinal(plainBytes); return Base64.getEncoder().encodeToString(encryptedBytes); } /** * Equivalent to VB.NET's fncEntschluesseln */ public static String decrypt(String encryptedText, String password) throws Exception { // Derive key + IV exactly like encryption (critical for compatibility) SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATION_COUNT, KEY_SIZE + BLOCK_SIZE); byte[] keyIvBytes = factory.generateSecret(spec).getEncoded(); byte[] keyBytes = new byte[KEY_SIZE / 8]; byte[] ivBytes = new byte[BLOCK_SIZE / 8]; System.arraycopy(keyIvBytes, 0, keyBytes, 0, keyBytes.length); System.arraycopy(keyIvBytes, keyBytes.length, ivBytes, 0, ivBytes.length); // Initialize decryptor Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7Padding", "BC"); SecretKey secretKey = new SecretKeySpec(keyBytes, "Rijndael"); IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec); // Decode Base64 and decrypt byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); // Again, use UTF_16LE if VB used Encoding.Unicode } // Test compatibility with your VB.NET output public static void main(String[] args) { try { String testPassword = "your-test-password"; String testPlainText = "Hello from VB.NET!"; String encrypted = encrypt(testPlainText, testPassword); System.out.println("Encrypted (Java): " + encrypted); String decrypted = decrypt(encrypted, testPassword); System.out.println("Decrypted (Java): " + decrypted); // Compare 'encrypted' to the output of your VB fncVerschluesseln—they should match exactly } catch (Exception e) { e.printStackTrace(); } } }
If Your VB Code Uses 128-bit Block Size (AES Compatible)
If your VB.NET RijndaelManaged.BlockSize = 128 (the default), you can skip BouncyCastle entirely. Modify these parts:
- Remove the BouncyCastle dependency and
Security.addProvidercall - Change
BLOCK_SIZE = 128 - Update cipher initialization to use standard AES:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
PKCS5 padding works here because Java's PKCS5Padding implementation correctly handles PKCS7 padding for AES 128-bit blocks.
Common Mistakes That Break Compatibility
Your original Java code likely failed due to one or more of these:
- Wrong Key/IV Derivation: Generating key and IV from separate
PBKDF2instances (VB uses the same instance sequentially) - Mismatched Block Size: Using Java's default AES 128-bit blocks while VB uses 256-bit
- Encoding Mismatch: Using UTF-8 in Java but
Encoding.Unicode(UTF-16LE) in VB - Incorrect Padding: Using
NoPaddingorISO10126Paddinginstead of PKCS7/PKCS5 - Wrong Hash Algorithm: Using PBKDF2 with HMAC-SHA256 instead of SHA1 (VB's
Rfc2898DeriveBytesdefaults to SHA1)
Test with a known plaintext/password pair—if the encrypted string matches between VB and Java, decryption will work perfectly.
内容的提问来源于stack exchange,提问作者C.B.

