Java AES/ECB 加密 与 PHP 互通

本文介绍如何确保Java与PHP之间的AES-ECB加密算法能够正确互通。重点在于理解秘钥长度与算法名称的关系,即16位秘钥对应AES-128-ECB,32位秘钥对应AES-256-ECB,并提供Java与PHP的实现代码。

Java AES/ECB 加密 与 PHP 互通

关键点

  • 秘钥长度如果是 16位, 对应 PHP 的 算法名为 AES-128-ECB, 128 = 16 * 8
  • 秘钥长度如果是 32位, 则对应 PHP 的 算法名应为 AES-256-ECB, 256 = 32 * 8
  • AES-128-ECB 算法下, 使用长度超过16的秘钥,只会使用前16位

Java 代码

public static String encrypt(String str) {
  try {
    String key = "1245678124567812456781245678"; // 密钥
    System.out.println("key => " + key);
    byte[] kb = key.getBytes("utf-8");
    SecretKeySpec sks = new SecretKeySpec(kb, "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");// 算法/模式/补码方式
    cipher.init(Cipher.ENCRYPT_MODE, sks);
    byte[] eb = cipher.doFinal(str.getBytes("utf-8"));
    return new Base64().encodeToString(eb);
  } catch (Exception e) {
    e.printStackTrace();
    return null;
  }
}

PHP代码

public function encrypt($content)
{
  $key = $this->getSecretKey();
  $data = openssl_encrypt($content, 'AES-256-ECB', $key);
  return $data;
}
<think>我们正在处理UniApp前端Java/PHP后端之间的AES-256 ECB加解密互通问题。由于ECB模式存在安全隐患,但在某些特定场景下仍被使用,我们需要确保前后端使用相同的加密参数。 关键点: 1. 密钥:必须是32字节(256位) 2. 模式:ECB(无IV) 3. 填充:PKCS#7(在Java中通常称为PKCS5Padding,因为PKCS#7是PKCS#5的扩展,且两者在块大小为8字节时相同,但AES块大小为16字节,所以Java中实际使用PKCS5Padding等同于PKCS#7填充) 4. 编码:通常明文和密文使用Base64编码进行传输 以下分别给出JavaPHP后端的实现方法,并UniApp前端(使用CryptoJS)进行互通。 第一部分:UniApp前端(使用CryptoJS)的代码(已经在上一个回答中提供,这里再强调一下关键点) 第二部分:Java后端实现 第部分:PHP后端实现 ## 第一部分:UniApp前端(CryptoJS)代码 加密: ```javascript import CryptoJS from 'crypto-js' function encryptAes256Ecb(plainText, key) { // 密钥必须是32字节 const parsedKey = CryptoJS.enc.Utf8.parse(key); const encrypted = CryptoJS.AES.encrypt( CryptoJS.enc.Utf8.parse(plainText), parsedKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } ); return encrypted.toString(); // 返回Base64字符串 } ``` 解密: ```javascript function decryptAes256Ecb(cipherBase64, key) { const parsedKey = CryptoJS.enc.Utf8.parse(key); const decrypted = CryptoJS.AES.decrypt( cipherBase64, parsedKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } ); return decrypted.toString(CryptoJS.enc.Utf8); } ``` ## 第二部分:Java后端实现(Spring Boot示例) Java中使用`javax.crypto`包实现AES-256 ECB加解密。 注意:默认情况下,Java的JRE限制了密钥长度(128位以上需要无限制策略文件),但现代Java版本(Java 9以后)默认支持256位AES。如果遇到问题,请检查Java版本。 添加Maven依赖(仅用于Base64编码): ```xml <!-- 如果使用Java 8及以下,需要添加Base64依赖 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> ``` 但是,Java 8及以上已经内置了`java.util.Base64`。 加密解密工具类: ```java import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; public class Aes256EcbUtils { public static String encrypt(String plainText, String secretKey) throws Exception { // 检查密钥长度 if (secretKey.length() != 32) { throw new IllegalArgumentException("密钥长度必须为32字节"); } // 创建密钥 SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES"); // ECB模式,使用PKCS5Padding(实际等同于PKCS7Padding) Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(encryptedBytes); } public static String decrypt(String cipherText, String secretKey) throws Exception { if (secretKey.length() != 32) { throw new IllegalArgumentException("密钥长度必须为32字节"); } SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec); byte[] cipherBytes = Base64.getDecoder().decode(cipherText); byte[] decryptedBytes = cipher.doFinal(cipherBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); } } ``` ## 第部分:PHP后端实现 PHP使用`openssl_encrypt`和`openssl_decrypt`函数实现。 注意:确保PHP已启用OpenSSL扩展。 ```php function encryptAes256Ecb($plainText, $key) { // 检查密钥长度 if (strlen($key) !== 32) { throw new Exception('密钥长度必须为32字节'); } // 加密 $cipherText = openssl_encrypt($plainText, 'AES-256-ECB', $key, OPENSSL_RAW_DATA); // 返回Base64编码 return base64_encode($cipherText); } function decryptAes256Ecb($cipherTextBase64, $key) { if (strlen($key) !== 32) { throw new Exception('密钥长度必须为32字节'); } $cipherText = base64_decode($cipherTextBase64); return openssl_decrypt($cipherText, 'AES-256-ECB', $key, OPENSSL_RAW_DATA); } ``` ## 前后端互通要点 1. **密钥一致**:前后端使用完全相同的32字节密钥(UTF-8编码)。 2. **加密模式**:ECB模式(无IV)。 3. **填充方式**:PKCS#7(在Java中叫PKCS5Padding,在PHP和CryptoJS中明确指定)。 4. **数据编码**:明文和密文都使用UTF-8编码,密文传输使用Base64编码。 ### 测试用例 使用相同的密钥和明文,分别在前端(UniApp)和后端(Java/PHP)进行加密,然后交换密文进行解密,验证是否能正确还原。 示例密钥:`"01234567890123456789012345678901"` (32字节) 示例明文:`"Hello, AES-256 ECB互通测试!"` 在UniApp中加密,到Java/PHP后端解密,反之亦然。 ## 注意事项 1. **ECB模式的安全性**:由于ECB模式不安全,除非必要,否则不推荐用于生产环境。考虑使用CBC或GCM模式,并引入随机IV。 2. **Java密钥长度限制**:旧版Java需要安装无限制策略文件(Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files),但Java 9及以上默认已支持256位AES。 3. **PHP的OpenSSL扩展**:确保PHP已安装并启用OpenSSL扩展。 4. **跨平台编码**:确保所有平台使用相同的字符串编码(UTF-8)。 通过以上实现,可以实现UniApp前端Java/PHP后端的AES-256 ECB加解密互通。 §§相关问题§§ 1. 如何在Java后端生成安全的AES-256密钥? 2. 如何解决AES-256 ECB模式在加密重复数据时的安全问题? 3. PHP后端如何安全地存储和使用AES密钥? 4. UniApp前端Java后端使用AES-256 CBC模式加解密互通的实现方法? </think># UniApp前端Java/PHP后端AES-256 ECB加解密互通指南 实现UniApp前端Java/PHP后端的AES-256 ECB加解密互通,核心是确保**加密参数完全一致**。以下是详细实现方案: ## 关键互通参数 | 参数 | 值 | 说明 | |------|----|------| | 加密算法 | AES-256 | 密钥长度256位(32字节) | | 加密模式 | ECB | 无初始向量(IV) | | 填充方式 | PKCS#7 | 也称为PKCS5Padding | | 字符编码 | UTF-8 | 明文编码 | | 数据格式 | Base64 | 密文传输格式 | ## 一、UniApp前端实现(使用CryptoJS) ```javascript import CryptoJS from 'crypto-js' // AES-256 ECB加密 export function encryptAes256Ecb(plainText, secretKey) { const key = CryptoJS.enc.Utf8.parse(secretKey); const encrypted = CryptoJS.AES.encrypt( CryptoJS.enc.Utf8.parse(plainText), key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } ); return encrypted.toString(); } // AES-256 ECB解密 export function decryptAes256Ecb(cipherText, secretKey) { const key = CryptoJS.enc.Utf8.parse(secretKey); const decrypted = CryptoJS.AES.decrypt( cipherText, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } ); return decrypted.toString(CryptoJS.enc.Utf8); } ``` ## 二、Java后端实现(Spring Boot示例) ```java import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class AesUtils { public static String encrypt(String plainText, String secretKey) throws Exception { // 验证密钥长度 if (secretKey.length() != 32) { throw new IllegalArgumentException("密钥长度必须为32字节"); } SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8")); return Base64.getEncoder().encodeToString(encryptedBytes); } public static String decrypt(String cipherText, String secretKey) throws Exception { if (secretKey.length() != 32) { throw new IllegalArgumentException("密钥长度必须为32字节"); } SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec); byte[] cipherBytes = Base64.getDecoder().decode(cipherText); byte[] decryptedBytes = cipher.doFinal(cipherBytes); return new String(decryptedBytes, "UTF-8"); } } ``` ## PHP后端实现 ```php function encryptAes256Ecb(string $plainText, string $secretKey): string { if (strlen($secretKey) !== 32) { throw new InvalidArgumentException('密钥长度必须为32字节'); } // 使用OPENSSL_RAW_DATA获取原始加密数据 $cipherText = openssl_encrypt( $plainText, 'AES-256-ECB', $secretKey, OPENSSL_RAW_DATA ); return base64_encode($cipherText); } function decryptAes256Ecb(string $cipherText, string $secretKey): string { if (strlen($secretKey) !== 32) { throw new InvalidArgumentException('密钥长度必须为32字节'); } $cipherText = base64_decode($cipherText); return openssl_decrypt( $cipherText, 'AES-256-ECB', $secretKey, OPENSSL_RAW_DATA ); } ``` ## 四、加解密一致性测试 ```javascript // UniApp前端测试代码 const testInteroperability = () => { const secretKey = "this_is_a_32_byte_key_for_aes_256!!"; // 32字节密钥 const plainText = "跨平台AES-256 ECB互通测试"; // 前端加密 const encrypted = encryptAes256Ecb(plainText, secretKey); // 发送到后端解密 uni.request({ url: 'https://your-api.com/decrypt', method: 'POST', data: { cipherText: encrypted, key: secretKey }, success: (res) => { console.log("后端解密结果:", res.data.decryptedText); console.log("原始文本:", plainText); } }); } ``` ## 五、互通过程中的常见问题及解决方案 ### 1. 密钥长度不一致 - **问题**:Java抛出`InvalidKeyException` - **解决**:确保所有平台密钥都是32字节 - **处理方案**: ```java // Java密钥填充 if (key.length() < 32) { key = String.format("%-32s", key).replace(' ', '0'); } else if (key.length() > 32) { key = key.substring(0, 32); } ``` ### 2. 加密模式不匹配 - **症状**:解密结果乱码 - **解决**:确认方都使用ECB模式且不使用IV - **PHP注意点**:确保使用`AES-256-ECB`而非`AES-256` ### 3. 填充方式差异 - **问题**:解密时出现`BadPaddingException` - **解决**:统一使用PKCS#7填充(Java中称为PKCS5Padding) ## 六、安全增强建议 1. **避免硬编码密钥**:使用密钥管理系统(KMS)或环境变量存储密钥 2. **增加随机盐值**:对明文添加随机前缀防止重复加密 3. **启用HTTPS**:防止传输过程中的中间人攻击[^1] 4. **定期轮换密钥**:建议每3-6个月更换一次加密密钥 ## 七、跨平台加解密流程 ```mermaid sequenceDiagram UniApp前端->>+Java/PHP后端: 发送加密请求(明文) Java/PHP后端-->>-UniApp前端: 返回Base64格式密文 UniApp前端->>UniApp前端: 本地存储/展示密文 UniApp前端->>+Java/PHP后端: 发送解密请求(密文) Java/PHP后端-->>-UniApp前端: 返回解密后的明文 ``` ## 八、性能优化建议 1. **Web Worker处理加密**:防止主线程阻塞 ```javascript // UniApp中使用Worker const worker = uni.createWorker('workers/crypto.js') worker.postMessage({ action: 'encrypt', data: plainText }) ``` 2. **批量数据处理**:后端使用流式加密大文件 3. **启用硬件加速**:后端使用AES-NI指令集 > **数学原理**:AES-256 ECB加密过程可表示为: > $$C_i = E_K(P_i) \quad \text{for } i=1,2,...,n$$ > 其中$E_K$表示AES加密函数,$K$为256位密钥,$P_i$为明文块,$C_i$为密文块。 通过上述方案,您可以在UniApp前端Java/PHP后端之间实现无缝的AES-256 ECB加解密互通
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值