簡體   English   中英

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

[英]AES Encryption in Java and Decryption in C#

您好,我已經加密了使用標准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;
    } 

但是輸出不符合預期。 請指出我要去哪里了?

您的代碼有一個大問題:它正在混合字符編碼!

在Java中,您正在調用key.getBytes() ,而沒有參數。 此方法將返回UTF-8或CP1252 / ISO 8859-1編碼的數據,具體取決於您的操作系統和Java中的默認字符集。

在C#端,您使用的是Encoding.Unicode.GetBytes(key) - .Net中的“ Unicode”是雙字節字符別名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,則差異很大。

我不僅需要C#的東西,還需要Silverlight和Windows Phone 7兼容的東西。 而且我肯定對缺少Java和C#(以及基於Base64)都可以接受的完整示例感到厭煩。

代碼沒什么花哨的,但可以。 正如我將其標記為社區Wiki一樣,請隨時進行改進,但是請確保先進行測試,然后再提交任何更改。

這是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的出色實現。

我希望這可以節省人們的時間。

嘗試以下組合:

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

我懷疑錯誤是您沒有在此等式的Java端指定填充或操作模式。 我不確定Java的AES實現默認為什么,但是從獲取密碼時同時指定兩者開始。 例如:

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

您需要查找Java中AES支持的填充方案和操作模式,然后確保將C#代碼配置為使用完全相同的配置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM