首页 文章

Java中的AES加密和C#中的解密

提问于
浏览
5

您好我已使用标准AES算法加密的加密十六进制字符串和密钥 . 码:

final String key = "=abcd!#Axd*G!pxP";
        final javax.crypto.spec.SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
        final javax.crypto.Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte [] encryptedValue = cipher.doFinal(input.getBytes());
        return new String(org.apache.commons.codec.binary.Hex.encodeHex(encryptedValue));

现在我尝试使用C#代码解密它:

RijndaelManaged rijndaelCipher = new RijndaelManaged();
            // Assumed Mode and padding values.
            rijndaelCipher.Mode = CipherMode.ECB;
            rijndaelCipher.Padding = PaddingMode.None;

            // AssumedKeySize and BlockSize values.
            rijndaelCipher.KeySize = 0x80;
            rijndaelCipher.BlockSize = 0x80;
            // Convert Hex keys to byte Array.
            byte[] encryptedData = hexStringToByteArray(textToDecrypt);

            byte[] pwdBytes = Encoding.Unicode.GetBytes(key);
            byte[] keyBytes = new byte[0x10];
            int len = pwdBytes.Length;
            if (len > keyBytes.Length)
            {
                len = keyBytes.Length;
            }
            Array.Copy(pwdBytes, keyBytes, len);
            rijndaelCipher.Key = keyBytes;
            rijndaelCipher.IV = keyBytes;
            // Decrypt data
            byte[] plainText = rijndaelCipher.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);
            str = Encoding.UTF8.GetString(plainText);

static private byte[] HexToBytes(string str)
    {
        if (str.Length == 0 || str.Length % 2 != 0)
            return new byte[0];
        byte[] buffer = new byte[str.Length / 2];
        char c;
        for (int bx = 0, sx = 0; bx < buffer.Length; ++bx, ++sx)
        {
            // Convert first half of byte   
            c = str[sx];
            buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')) << 4);
            // Convert second half of byte    
            c = str[++sx];
            buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0'));
        }
        return buffer;
    }

但输出并不像预期的那样 . 请指出我哪里出错了?

4 回答

  • 8

    我怀疑错误是你没有在这个等式的Java方面指定填充或操作模式 . 我不确定Java的AES实现默认是什么,但我会从获取密码时开始指定两者 . 例如:

    Cipher.getInstance("<algorithm>/<mode of operation>/<padding>");
    

    您需要在Java中查找AES支持的填充方案和操作模式,然后确保将C#代码配置为使用完全相同的配置 .

  • 4

    你的代码有一个大问题:它混合了字符编码!

    在Java中,您调用 key.getBytes() ,没有参数 . 此方法返回UTF-8或CP1252 / ISO 8859-1编码数据,具体取决于您的操作系统和Java中的默认字符集 .

    在C#端,您正在使用 Encoding.Unicode.GetBytes(key) - "Unicode" in .Net is a synonym for double byte characters alias UTF-16 (Little-Endian) . 因此,您在C#中使用不同的密钥 .

    您应该能够通过比较Java和C#中的字节数来查看差异:

    Java: "=abcd!#Axd*G!pxP".getBytes().length = 16

    C#: Encoding.Unicode.GetBytes("=abcd!#Axd*G!pxP").Length = 32

    我强烈建议您使用字节数组而不是字符串来定义加密密钥 .

    更新:另一个区别是您在C#中设置初始化向量(IV),而在Java中则没有这样做 . 当您使用ECB时,不应使用IV,但如果您更改为CBC,例如这会产生很大的不同 .

  • 2

    我不仅需要C#,还需要兼容Silverlight和Windows Phone 7 . 而且我完全厌倦了缺乏Java和C#(以及基于Base64)可接受的完整示例 .

    代码不是什么花哨的东西,但有效 . 请随意改进,因为我将其标记为社区维基,但请确保在提交任何更改之前对其进行测试 .

    这是C#代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Security.Cryptography;
    using System.IO;
    //Author: Doguhan Uluca
    //Website: www.deceivingarts.com
    
    namespace DeceivingArts.Common
    {
        public class Encryptor
        {
            private string _seed = "";
    
            public Encryptor(string seed)
            {
            _seed = seed;
            }
    
            public string Encrypt<TSymmetricAlgorithm>(string input) where TSymmetricAlgorithm : SymmetricAlgorithm, new()
            {
                var pwdBytes = Encoding.UTF8.GetBytes(_seed);
                using(TSymmetricAlgorithm sa = new TSymmetricAlgorithm())
                {
                    ICryptoTransform saEnc = sa.CreateEncryptor(pwdBytes, pwdBytes);
    
                    var encBytes = Encoding.UTF8.GetBytes(input);
    
                    var resultBytes = saEnc.TransformFinalBlock(encBytes, 0, encBytes.Length);
    
                    return Convert.ToBase64String(resultBytes);
                }
            }
    
            public string Decrypt<TSymmetricAlgorithm>(string input) where TSymmetricAlgorithm : SymmetricAlgorithm, new()
            {
                var pwdBytes = Encoding.UTF8.GetBytes(_seed);
                using(TSymmetricAlgorithm sa = new TSymmetricAlgorithm())
                {
                    ICryptoTransform saDec = sa.CreateDecryptor(pwdBytes, pwdBytes);
    
                    var encBytes = Convert.FromBase64String(input);
    
                    var resultBytes = saDec.TransformFinalBlock(encBytes, 0, encBytes.Length);
                    return Encoding.UTF8.GetString(resultBytes);
                }
            }
        }
    }
    

    这是Android兼容的Java代码:

    import javax.crypto.Cipher;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    /**
     * Usage:
     * <pre>
     * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext)
     * ...
     * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto)
     * </pre>
     * @author ferenc.hechler
     * @author Doguhan Uluca
     */
    public class Encryptor {
    
        public static String encrypt(String seed, String cleartext) throws Exception {
                byte[] rawKey = getRawKey(seed.getBytes());
                byte[] result = encrypt(rawKey, cleartext.getBytes());
                return toBase64(result);
        }
    
        public static String decrypt(String seed, String encrypted) throws Exception {
                byte[] rawKey = getRawKey(seed.getBytes());
                byte[] enc = fromBase64(encrypted);
                byte[] result = decrypt(rawKey, enc);
                return new String(result);
        }
    
        private static byte[] getRawKey(byte[] seed) throws Exception {
            SecretKey skey = new SecretKeySpec(seed, "AES");
    
            byte[] raw = skey.getEncoded();
    
            return raw;
        }
    
        private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
    
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(raw);
    
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
            byte[] encrypted = cipher.doFinal(clear);
            return encrypted;
        }
    
        private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(raw);
    
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
            byte[] decrypted = cipher.doFinal(encrypted);
            return decrypted;
        }
    
        public static String toBase64(byte[] buf)
        {
            return Base64.encodeBytes(buf);
        }
    
        public static byte[] fromBase64(String str) throws Exception
        {
            return Base64.decode(str);
        }
    }
    

    对于Base64转换,请参阅http://iharder.net/base64的优秀实施 .

    我希望这可以节省人们的时间 .

  • 0

    试试这个组合:

    aesAlg.Mode = CipherMode.ECB;   
    aesAlg.Padding = PaddingMode.PKCS7;   
    //aesAlg.IV; - use default (not assign)
    

相关问题