繁体   English   中英

前 8 个字节在 java 中的 AES 解密中被截断,其中加密在 c# 中完成

[英]First 8 bytes is truncating in AES decryption in java where encryption is done in c#

我正在尝试解密在 c# 中使用相同密钥长度和向量加密的字符串。

爪哇代码

public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        String cum006333 = decrypt("h+VJ5or2yBFQYawNWXET11nekUzWYYVWk0/O2fHxoLNm60l3d3qQo2NJjGr3+zZP", "MYPRIVATE@KEY");
        System.out.println(cum006333);

    }

    public static String decrypt(String cipherText, String passphrase) {
        String s = null;
        try {

            Cipher   cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeyFactory  factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            
            byte[] bytes = Base64.getDecoder().decode(cipherText);
            int keySize = 256;
            int iterCount = 1000;

            int ivLength = 16;
            byte[] saltByte = new byte[]{0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};
            byte[] ivB = Arrays.copyOf(bytes, ivLength);
            byte[] data = Arrays.copyOfRange(bytes,ivLength, bytes.length);

            KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), saltByte, iterCount, keySize);
            SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
            IvParameterSpec iv = new IvParameterSpec(ivB);
            cipher.init(Cipher.DECRYPT_MODE, key, iv);
            byte[] original = cipher.doFinal(data);
            s = new String(original);
            System.out.println(s);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return s;
    }

    
}

expexted 输出是 - 重复的请求 ID

输出是 - e 请求 ID

我需要在java中加密请求并发送到api,他们将在那里解密并使用它。 所以我想要 java 相当于下面的 C# 代码..

请在下面找到用于加密和解密的 C# 代码

public static string EncryptResponse(string clearText)
    {
        string EncryptionKey = System.Configuration.ConfigurationManager.AppSettings["SECRETMSG"].ToString();
        byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes rfcdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = rfcdb.GetBytes(32);
            encryptor.IV = rfcdb.GetBytes(16);
            using (MemoryStream mem = new MemoryStream())
            {
                using (CryptoStream cryp = new CryptoStream(mem, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cryp.Write(clearBytes, 0, clearBytes.Length);
                    cryp.Close();
                }
                clearText = Convert.ToBase64String(mem.ToArray());
            }
        }
        return clearText;
    }


    public static string DecryptResponse(string cipherText)
    {
        string EncryptionKey = System.Configuration.ConfigurationManager.AppSettings["SECRETMSG"].ToString();
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes rfcdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = rfcdb.GetBytes(32);
            encryptor.IV = rfcdb.GetBytes(16);

            using (MemoryStream mem = new MemoryStream())
            {
                using (CryptoStream cryp = new CryptoStream(mem, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cryp.Write(cipherBytes, 0, cipherBytes.Length);
                    cryp.Close();
                }
                cipherText = Encoding.Unicode.GetString(mem.ToArray());
            }
        }
        return cipherText;
    }

您没有向我们展示 C# 中的加密代码真是太遗憾了,因为它会直接将我们指向解决方案,而不是争论和搜索。

您声称“截断”的原因不在 Java 端(解密),而是在您的 c# 加密功能上。 很可能您对纯文本使用 UTF-16 编码,但在 Java 端使用标准编码 (UTF-8)。

您可以在下面找到一个稍微添加的代码版本,其中包含用于数据的专用输出,以确保所有数据的长度都正确。

看看“.doFinal”输出,我们看到一个值(十六进制)“ 650020005200650071007500650073007400200049004400 ”——每个字符之间有很多“x00”值,这绝对是一个UTF-16编码,所以不是e Request Id ” 但“ e R equest ID ”。

将“新字符串”编码更改为“ new String(original, StandardCharsets.UTF_16LE) ”获得“e请求ID”的“正确”输出 - 但是开头缺少的“Duplicat”在哪里? 我确定您也通过“数组复制”获得了(base64 编码的)密文字符串,并且您只是复制了密文的最后 12 个(或 UTF-16 中的 24 个)字节,所以 - 我相信 -您正在截断 C# 端的密文。

输出

First 8 bytes is truncating in AES decryption in java where encryption is done in c#
cipherByte length: 48 data: 87e549e68af6c8115061ac0d597113d759de914cd6618556934fced9f1f1a0b366eb4977777a90a363498c6af7fb364f
ivB        length: 16 data: 87e549e68af6c8115061ac0d597113d7
data       length: 32 data:                                 59de914cd6618556934fced9f1f1a0b366eb4977777a90a363498c6af7fb364f
original   length: 24 data: 650020005200650071007500650073007400200049004400
e   R e q u e s t   I D
e   R e q u e s t   I D

更改代码行

s = new String(original);
to
s = new String(original, StandardCharsets.UTF_16LE);

给出:

e Request ID

代码:

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.security.spec.KeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;

public class mainSo {

    public static void main(String[] args) {
        System.out.println("First 8 bytes is truncating in AES decryption in java where encryption is done in c#");
        String cipherstring = "h+VJ5or2yBFQYawNWXET11nekUzWYYVWk0/O2fHxoLNm60l3d3qQo2NJjGr3+zZP";
        byte[] cipherByte = Base64.getDecoder().decode(cipherstring);
        System.out.println("cipherByte length: " + cipherByte.length + " data: " + bytesToHex(cipherByte));
        String cum006333 = decrypt(cipherstring, "KEY@CRISIL123");
        System.out.println(cum006333);
    }

    public static String decrypt(String cipherText, String passphrase) {
        String s = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            byte[] bytes = Base64.getDecoder().decode(cipherText);
            int keySize = 256;
            int iterCount = 1000;
            int ivLength = 16;
            byte[] saltByte = new byte[]{0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};
            byte[] ivB = Arrays.copyOf(bytes, ivLength);
            byte[] data = Arrays.copyOfRange(bytes,ivLength, bytes.length);
            System.out.println("ivB        length: " + ivB.length + " data: " + bytesToHex(ivB));
            System.out.println("data       length: " + data.length + " data:                                 " + bytesToHex(data));
            KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), saltByte, iterCount, keySize);
            SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
            IvParameterSpec iv = new IvParameterSpec(ivB);
            cipher.init(Cipher.DECRYPT_MODE, key, iv);
            byte[] original = cipher.doFinal(data);
            System.out.println("original   length: " + original.length + " data: " + bytesToHex(original));
            s = new String(original);
            //s = new String(original, StandardCharsets.UTF_16LE);
            System.out.println(s);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return s;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuffer result = new StringBuffer();
        for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }
}

由于我不喜欢混合答案,因此我正在为您的问题编写第二个答案和最终解决方案。

感谢您提供 C# 代码,因为它回答了 2 个问题 - 明文的编码是什么以及密文中包含哪些数据。

在 C# 上使用这一行向我们展示了编码是 UNICODE (=UTF 16),正如我在我的第一个答案中所说的:

byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);

要在 Java 中获得相同的结果,您需要使用以下命令对任何解密结果进行解码

s = new String(original, StandardCharsets.UTF_16LE);

第二个问题 - 密文中包含的内容非常简单 - 只是密文本身(没有 IV )。 必要的 IV 由

Rfc2898DeriveBytes...
encryptor.Key = rfcdb.GetBytes(32);
encryptor.IV = rfcdb.GetBytes(16);

将这两个信息放在一起,我可以使用给定的密钥解密密文,如下所示:

First 8 bytes is truncating in AES decryption in java where encryption is done in c#
cipherByte length: 48 data: 87e549e68af6c8115061ac0d597113d759de914cd6618556934fced9f1f1a0b366eb4977777a90a363498c6af7fb364f
keyDerived length: 32 data: e35b88684a86fe06ae80eab429b2c9c242ab64f225649aa484ced8f53e5de146
ivB        length: 16 data: 19b9e11cb1e6c6aa69060fbdfe10484c
original   length: 40 data: 4400750070006c006900630061007400650020005200650071007500650073007400200049004400
Duplicate Request ID
Duplicate Request ID

这是完整的代码:

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.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;

public class mainSo2 {

    public static void main(String[] args) {
        System.out.println("First 8 bytes is truncating in AES decryption in java where encryption is done in c#");
        String cipherstring = "h+VJ5or2yBFQYawNWXET11nekUzWYYVWk0/O2fHxoLNm60l3d3qQo2NJjGr3+zZP";
        byte[] cipherByte = Base64.getDecoder().decode(cipherstring);
        System.out.println("cipherByte length: " + cipherByte.length + " data: " + bytesToHex(cipherByte));
        String cum006333 = decrypt(cipherstring, "KEY@CRISIL123");
        System.out.println(cum006333);
    }

    public static String decrypt(String cipherText, String passphrase) {
        String s = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            byte[] bytes = Base64.getDecoder().decode(cipherText);
            int keySize = 256;
            int iterCount = 1000;
            int ivLength = 16;
            byte[] saltByte = new byte[]{0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};
            KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), saltByte, iterCount, (keySize + (8 * ivLength))); // 32 + 16
            byte[] rawKey = factory.generateSecret(spec).getEncoded(); // 48 bytes
            byte[] keyDerived = Arrays.copyOf(rawKey, (keySize / 8)); // first 32 bytes
            byte[] ivB = Arrays.copyOfRange(rawKey, (keySize / 8), rawKey.length); // last 16 bytes
            System.out.println("keyDerived length: " + keyDerived.length + " data: " + bytesToHex(keyDerived));
            System.out.println("ivB        length: " + ivB.length + " data: " + bytesToHex(ivB));
            SecretKey key = new SecretKeySpec(keyDerived, "AES");
            IvParameterSpec iv = new IvParameterSpec(ivB);
            cipher.init(Cipher.DECRYPT_MODE, key, iv);
            //byte[] original = cipher.doFinal(data);
            byte[] original = cipher.doFinal(bytes);
            System.out.println("original   length: " + original.length + " data: " + bytesToHex(original));
            s = new String(original, StandardCharsets.UTF_16LE);
            System.out.println(s);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return s;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuffer result = new StringBuffer();
        for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM