java - Java 中的 AES 加密与 C# 输出相匹配

标签 java c# encryption aes tripledes

我正在尝试使用 JAVA 进行 AES 加密,我进行了多次尝试,尝试了很多代码并做了很多更改,最终达到了我的加密文本与使用 C# 代码生成的加密文本部分匹配的位置。最后一个 32 位 block 不同。我无权访问 C# 代码,因为它是第三方服务。谁能指导我缺少什么?

提到的条件是使用:

在 CBC 模式下使用 256 位 AES 加密并使用 PKCS5 填充,使用主 key 和初始化 vector 来加密整个查询字符串。 (查询字符串中不要包含消息摘要。)主键是 64 位十六进制字符串,初始化 vector 是 32 位十六进制字符串。

我使用的示例值是:

Aes_IV = 50B666AADBAEDC14C3401E82CD6696D4

Aes_Key = D4612601EDAF9B0852FC0641DC2F273E0F2B9D6E85EBF3833764BF80E09DD89F(我的 key Material )

Plain_Text = ss=brock&pw=123456&ts=20190304234431(输入)

Encrypted_Text = 7643C7B400B9A6A2AD0FCFC40AC1B11E51A038A32C84E5560D92C0C49B3B7E0 A072AF44AADB62FA66F047EACA5C6A018(输出)

我的输出 = 7643C7B400B9A6A2AD0FCFC40AC1B11E51A038A32C84E5560D92C0C49B3B7E0 A38E71E5C846BAA6C31F996AB05AFD089

public static String encrypt( String keyMaterial, String unencryptedString, String ivString ) {
    String encryptedString = "";
    Cipher cipher;
    try {
        byte[] secretKey = hexStrToByteArray( keyMaterial );
        SecretKey key = new SecretKeySpec( secretKey, "AES" );
        cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
        IvParameterSpec iv;
        iv = new IvParameterSpec( hexStrToByteArray( ivString ) );
        cipher.init( Cipher.ENCRYPT_MODE, key, iv );
        byte[] plainText = unencryptedString.getBytes( "UTF-8") ;
        byte[] encryptedText = cipher.doFinal( plainText );
        encryptedString = URLEncoder.encode(byteArrayToHexString( encryptedText ),"UTF-8");
    }
    catch( InvalidKeyException | InvalidAlgorithmParameterException | UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e ) {
        System.out.println( "Exception=" +e.toString() );
    }
    return encryptedString;
}

我用它来进行转换。

public static byte[] hexStrToByteArray ( String input) {
    if (input == null) return null;
    if (input.length() == 0) return new byte[0];

    if ((input.length() % 2) != 0)
        input = input + "0";

    byte[] result = new byte[input.length() / 2];
    for (int i = 0; i < result.length; i++) {
        String byteStr = input.substring(2*i, 2*i+2);
        result[i] = (byte) Integer.parseInt("0" + byteStr, 16);
    }
    return result;
}
public static String byteArrayToHexString(byte[] ba) {
    String build = "";
    for (int i = 0; i < ba.length; i++) {
        build += bytesToHexString(ba[i]);
    }
    return build;
}
    public static String bytesToHexString ( byte bt) {
    String hexStr ="0123456789ABCDEF";
    char ch[] = new char[2];
    int value = (int) bt;

    ch[0] = hexStr.charAt((value >> 4) & 0x000F);
    ch[1] = hexStr.charAt(value & 0x000F);

    String str = new String(ch);

    return str;
}

有什么建议,我应该怎么做才能匹配输出?

最佳答案

如果只有 ECB/CBC 填充的最后一个 block 不同,那么您可以非常确定使用了不同的 block 密码填充。要验证使用哪种填充,您可以尝试(正如 Topaco 在问题下面的评论中所做的那样),或者您可以在不填充的情况下解密密文。对于 Java,这将是“AES/CBC/NoPadding”

因此,如果您在给定 key (和 IV)的情况下执行此操作,那么您将获得以下十六进制输出:

73733D62726F636B2670773D3132333435362674733D3230313930333034323334343331000000000000000000000000

显然这是零填充。

零填充有一个很大的缺点:如果您的密文以零值字节结尾,那么该字节可能会被视为填充并从结果中删除。一般来说,这对于由 ASCII 或 UTF-8 字符串组成的明文来说不是问题,但对于二进制输出来说可能会比较棘手。当然,我们在这里假设该字符串不使用预计出现在加密明文中的空终止符。

还有另一个较小的缺点:如果您的明文恰好是 block 大小,那么零填充就不够标准,有两种情况:

  1. 填充始终会应用并需要删除,这意味着如果明文大小恰好是 block 大小的几倍,则仍会添加完整的填充 block (因此对于 AES,您将有 1..16 个零值字节作为填充);

  2. 只有在严格要求的情况下才会应用填充,这意味着如果明文大小恰好是 block 大小的几倍,则不会应用填充(因此对于 AES,您将有 0..15 个零值字节作为填充)。

因此,目前,对于加密,您可能必须测试哪个是预期/接受的。例如。 Bouncy CaSTLe - 可用于 C# 和 Java - 总是(不)填充,而可怕的 PHP/mcrypt 库仅在需要时填充。

当然,您始终可以执行自己的填充,然后对 Java 使用 "NoPadding"。但请记住,您绝不能取消填充超过 16 个字节。

一般警告:未经身份验证的加密不适合传输模式安全。

关于java - Java 中的 AES 加密与 C# 输出相匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55029481/

相关文章:

java - 使用ResourceDatabasePopulator时如何正确转义Oracle列名?

java - 打印类到文件 java/HTA

java - java中文件名和类名不同

c# - 缺少 jQuery 自动完成功能?

java - AES GCM解密绕过JAVA中的身份验证

Java错误包不存在

c# - Android Xamarin NavigationView 处理事件错误

c# - 如何验证 CodeDOM 中的命名空间名称?

java - 错误填充异常 : Data must start with zero

android - 在 Cordova/Phonegap 应用程序中保护 Assets