C#与Java同加密算法输出不一致,如何实现输出统一?
问题:C#与Java AES加密结果不一致
将C#的密码加密代码转为Java后,两者生成的加密字符串无法匹配。输入密码为Hello World2时:
- C#输出:
orc_Aup5rLQZ_vB5atFuqw2 - Java输出:
orc_Aup5rLQZ_vB5atFuqw==
需要调整Java代码,使其输出与C#一致。
C# 原加密代码
using System; using System.IO; using System.Security.Cryptography; using System.Text; using System.Web; namespace Crypto.Client { /// <summary> /// Helper functions to encrypt and decrypt stuff /// </summary> public static class CryptoHelper { private static readonly byte[] Key = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; private static readonly byte[] InitializationVector = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; //Encrypts the input string using a .net function. public static string Encrypt(string plainText) { var rmCrypto = new RijndaelManaged(); using (var memoryStream = new MemoryStream()) { using ( var cryptoStream = new CryptoStream( memoryStream, rmCrypto.CreateEncryptor(Key, InitializationVector), CryptoStreamMode.Write)) { using (var streamWiter = new StreamWriter(cryptoStream)) { streamWiter.Write(plainText); } } return HttpServerUtility.UrlTokenEncode(memoryStream.ToArray()); } } } }
Java 原加密代码
public class CryptoHelper { private static final byte[] Key = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; private static final byte[] InitializationVector = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; public static String Encrypt(String plainText) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec = new SecretKeySpec(Key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(InitializationVector); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); CipherOutputStream cryptoStream = new CipherOutputStream(outputStream, cipher); cryptoStream.write(plainText.getBytes("UTF-8")); cryptoStream.close(); byte[] encryptedBytes = outputStream.toByteArray(); return Base64.getUrlEncoder().encodeToString(encryptedBytes); } }
解决方案
差异根源在于Base64编码的处理规则不同:
- C#的
HttpServerUtility.UrlTokenEncode是微软自定义的URL安全Base64:- 先做标准Base64编码
- 把
+替换为-,/替换为_ - 去掉末尾的
=,并在最后添加一个数字表示去掉的=数量(比如2代表去掉了2个=)
- Java的
Base64.getUrlEncoder()是标准URL安全Base64:仅替换+为-、/为_,保留末尾的=
要让Java输出与C#一致,需实现相同的URL Token编码逻辑,修改后的Java代码如下:
import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayOutputStream; import java.util.Base64; public class CryptoHelper { private static final byte[] Key = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; private static final byte[] InitializationVector = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; public static String Encrypt(String plainText) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec = new SecretKeySpec(Key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(InitializationVector); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); CipherOutputStream cryptoStream = new CipherOutputStream(outputStream, cipher); cryptoStream.write(plainText.getBytes("UTF-8")); cryptoStream.close(); byte[] encryptedBytes = outputStream.toByteArray(); // 实现与C# HttpServerUtility.UrlTokenEncode一致的逻辑 String base64Url = Base64.getUrlEncoder().withoutPadding().encodeToString(encryptedBytes); int paddingCount = (4 - (encryptedBytes.length % 4)) % 4; return base64Url + paddingCount; } }
代码说明:
- 使用
Base64.getUrlEncoder().withoutPadding()生成不带末尾=的URL安全Base64字符串 - 计算原本需要的
=数量(paddingCount),并将这个数字追加到字符串末尾 - 处理后Java输出将与C#的
UrlTokenEncode结果完全一致
内容的提问来源于stack exchange,提问作者Rajesh Kumar
相关产品推荐
相关产品推荐

