使用 CBC 和 PKCS7Padding 的 Java AES 加密

标签 java encryption aes pkcs#7

我已经为此苦苦挣扎了几天。我需要使用一个带有加密参数的 API。该 API 是用 C# 编写的。请求的加密如下:


算法:AES
加密方式:CBC
填充模式:PKCS7
区 block 大小:128
key 大小:256

Key: String --> 通过将提供的字符串转换为大小为 32 的字节数组来生成 key :Encoding.ASCII.GetBytes(…)。 API 声明该字符串是由他们使用字符串的 MD5 哈希函数生成的。

IV:IV 数组是通过将提供的字符串转换为大小为 16 的字节数组而生成的:Encoding.ASCII.GetBytes(…)。

加密字符串的表示形式:Base64


在搜索并尝试了网上建议的很多方法之后,我仍然无法生成相同的加密值(特别是默认情况下不支持 PKCS7,而 PKCS5 应该可以正常工作,但事实并非如此)。以下是我尝试过的一些事情:
1)使用充气城堡 jar 来使用PKCS7
2) 添加 JCE 合规性,以便能够消除对 key 和 block 大小的限制。

与他们联系后,他们向我发送了一个正在运行的 android 代码片段(如果我在纯 java 8 中运行,则会提示提供程序(NoSuchAlgorithmException:找不到任何支持 AES/CBC/PKCS7Padding 的提供程序)):

public static String encrypt(String value) {
        String plainText = value;
        String escapedString;
        try {
            byte[] key = ENCRYPT_KEY.getBytes("UTF-8");
            byte[] ivs = ENCRYPT_IV.getBytes("UTF-8");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivs);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
            escapedString = Base64.encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")), Base64.DEFAULT).trim();

            return escapedString;
        } catch (Exception e) {
            e.printStackTrace();
            return value;
        }
    }  

请提供任何帮助,我们将不胜感激。

这是我尝试过的代码片段:

package com.melhem.TestJava;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class StringFunc {


    final static String key = "API_KEY_32_CHARs";
    final static String iv = "API_IV_16_CHARs";
    final static String algorithm = "AES/CBC/PKCS7Padding";
    private static Cipher cipher = null;
    private static SecretKeySpec skeySpec = null;
    private static IvParameterSpec  ivSpec = null;

    public static void main(String[] args) {
        System.out.println(encrypt("STRING_TO_ENCODE"));
    }

    private static void setUp(){
        try{
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
            skeySpec = new SecretKeySpec(key.getBytes("ASCII"), "AES");
            ivSpec = new IvParameterSpec(iv.getBytes("ASCII"));
            cipher = Cipher.getInstance(algorithm);
        }catch(NoSuchAlgorithmException | NoSuchPaddingException ex){

            ex.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public static String encrypt(String str){
        try{
//            Integer strL = (int) Math.ceil(str.length() / 8.0);
//            Integer strB = strL*8;
//            str = padRight(str, ' ', strB);
            setUp();
            try {
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
                System.out.println("Block size: " + cipher.getBlockSize() * 8);
                System.out.println("Algorithm name: " + cipher.getAlgorithm());
                System.out.println("Key size: " + skeySpec.getEncoded().length * 8);
            } catch (InvalidAlgorithmParameterException ex) {
                ex.printStackTrace();
                return "";
            }
            byte[] enc = cipher.doFinal(str.getBytes("ASCII"));
            String s = new String(Base64.getEncoder().encode(enc));
            s = s.replace("+", "__plus__");
            s = s.replace("/", "__slash__");
            return s;
        }catch(InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex){
            ex.printStackTrace();
            return "";            
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return "";
        }
    }

    public static String padRight(String msg, char x, int l) {
        String result = "";
        if (!msg.isEmpty()) {
            for (int i=0; i<(l-msg.length()); i++) {
                result = result + x;
            }
            result = msg + result;
        }
        return result;
    }
}

最佳答案

Java Cipher package仅支持使用 AES/CBC/PKCS5Padding 进行 PKCS#7 填充。这不是一个好的命名,因为 PKCS#5 填充支持 8 字节 block 大小,而 DES 和 PKCS#7 支持最多 255 字节。对于 Java 使用此;

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

对于最现代的分组密码来说,#5 和 #7 不可互换,因为 AES 是 128 位分组密码。请参阅 Crypto.StackExchange 上的问题。

并且,用于使用 256 位 key 大小的 AES;

Java 标准密码库限制为 128 位 key 大小。你必须去下载Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6

关于使用 CBC 和 PKCS7Padding 的 Java AES 加密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53139243/

相关文章:

python - 尝试用RSA加密视频帧;解密后得到垃圾而不是原始数据

java - AEADBadTagException 标签与套接字数据不匹配

encryption - AES-128-ECB 和 OpenSSL 的测试向量

javascript - Android 上基于密码的 AES 加密和使用 CryptoJS 解密

java - 使用 Spring MVC 处理多个 URL 参数

java - 带问号的通用列表类型

java - 有关如何从头开始设置非常简单的 J2EE Web 应用程序的教程?

java - 您知道的所有 Maven Archetype 目录的 URL 是什么?

java - 使用 AES 在 Java 和 Python 中加密/解密

Node.js:尝试解密加密的对称 key 时出现无效的 RSAES-OAEP 填充