C# Encrypt/Decrypt 来自 Java AES/CBC/PKCS5Padding
C# Encrypt/Decrypt from Java AES/CBC/PKCS5Padding
我在尝试解密已在 Java 中使用以下属性(Java 代码)
加密的字符串时遇到问题
public static Builder getDefaultBuilder(String key, String salt, byte[] iv) {
return new Builder()
.setIv(iv)
.setKey(key)
.setSalt(salt)
.setKeyLength(128)
.setKeyAlgorithm("AES")
.setCharsetName("UTF8")
.setIterationCount(1)
.setDigestAlgorithm("SHA1")
.setBase64Mode(Base64.DEFAULT)
.setAlgorithm("AES/CBC/PKCS5Padding")
.setSecureRandomAlgorithm("SHA1PRNG")
.setSecretKeyType("PBKDF2WithHmacSHA1");
}
到目前为止,这是我的代码 (C#)
public string DecryptText(string encryptedString)
{
using (myRijndael = new RijndaelManaged())
{
myRijndael.Key = Convert.FromBase64String(encryptionKey);
myRijndael.IV = new byte[16];
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
Byte[] ourEnc = Convert.FromBase64String(encryptedString);
string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);
return ourDec;
}
}
protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
但是当我尝试解密时出现以下异常“System.Security.Cryptography.CryptographicException: 'Specified key is not a valid size for this algorithm.'
".
Java 代码的来源在这里
https://github.com/simbiose/Encryption/blob/master/Encryption/main/se/simbio/encryption/Encryption.java
这是加密时的Java代码
public String encrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException {
if (data == null) return null;
SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
byte[] dataBytes = data.getBytes(mBuilder.getCharsetName());
Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
return Base64.encodeToString(cipher.doFinal(dataBytes), mBuilder.getBase64Mode());
}
private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance(mBuilder.getSecretKeyType());
KeySpec spec = new PBEKeySpec(key, mBuilder.getSalt().getBytes(mBuilder.getCharsetName()), mBuilder.getIterationCount(), mBuilder.getKeyLength());
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), mBuilder.getKeyAlgorithm());
}
private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance(mBuilder.getDigestAlgorithm());
messageDigest.update(key.getBytes(mBuilder.getCharsetName()));
return Base64.encodeToString(messageDigest.digest(), Base64.NO_PADDING).toCharArray();
}
我已经为这个问题苦苦挣扎了两天,因为我并没有真正使用加密,所以任何帮助将不胜感激。
谢谢!
更新:
整个class
public sealed class MyCryptoClass
{
protected RijndaelManaged myRijndael;
private static string encryptionKey = "random";
// Singleton pattern used here with ensured thread safety
protected static readonly MyCryptoClass _instance = new MyCryptoClass();
public static MyCryptoClass Instance
{
get { return _instance; }
}
public MyCryptoClass()
{
}
public string DecryptText(string encryptedString)
{
using (myRijndael = new RijndaelManaged())
{
myRijndael.Key = Convert.FromBase64String(encryptionKey);
myRijndael.IV = new byte[16];
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
Byte[] ourEnc = Convert.FromBase64String(encryptedString);
string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);
return ourDec;
}
}
public string EncryptText(string plainText)
{
using (myRijndael = new RijndaelManaged())
{
myRijndael.Key = HexStringToByte(encryptionKey);
myRijndael.IV = HexStringToByte(initialisationVector);
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
byte[] encrypted = EncryptStringToBytes(plainText, myRijndael.Key, myRijndael.IV);
string encString = Convert.ToBase64String(encrypted);
return encString;
}
}
protected byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
public static void GenerateKeyAndIV()
{
// This code is only here for an example
RijndaelManaged myRijndaelManaged = new RijndaelManaged();
myRijndaelManaged.Mode = CipherMode.CBC;
myRijndaelManaged.Padding = PaddingMode.PKCS7;
myRijndaelManaged.GenerateIV();
myRijndaelManaged.GenerateKey();
string newKey = ByteArrayToHexString(myRijndaelManaged.Key);
string newinitVector = ByteArrayToHexString(myRijndaelManaged.IV);
}
protected static byte[] HexStringToByte(string hexString)
{
try
{
int bytesCount = (hexString.Length) / 2;
byte[] bytes = new byte[bytesCount];
for (int x = 0; x < bytesCount; ++x)
{
bytes[x] = Convert.ToByte(hexString.Substring(x * 2, 2), 16);
}
return bytes;
}
catch
{
throw;
}
}
public static string ByteArrayToHexString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
}
因为你的 MyCryptoClass.encryptionKey
对应于你的 Encryption.Builder.mKey
你必须在 C# 端生成密钥,即你必须在 C# 端为每个Java-这个过程涉及的方法。这些 Java 方法是 getSecretKey(char[] key)
、hashTheKey(String key)
以及 third.part.android.util.Base64.encodeToString(byte[] input, int flags)
.
Java-方法 getSecretKey(char[] key)
的可能 C# 对应物:
private static byte[] GetSecretKey()
{
string hashedKey = GetHashedKey();
byte[] saltBytes = Encoding.UTF8.GetBytes(salt); // builder.mCharsetName = "UTF8";
int iterations = 1; // builder.mIterationCount = 1
byte[] secretKey = null;
using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(hashedKey, saltBytes, iterations)) // builder.mSecretKeyType = "PBKDF2WithHmacSHA1";
{
secretKey = rfc2898.GetBytes(16); // builder.mKeyLength = 128;
//Console.WriteLine("Key: " + ByteArrayToHexString(secretKey));
}
return secretKey;
}
此方法使用 PBKDF2WithHmacSHA1
以密钥、salt、迭代计数和密钥长度作为输入来导出密钥。此处使用的密钥(更准确地说是密码)是由 GetHashedKey()
(见下文)提供的 MyCryptoClass.encryptionKey
的 base64 编码的 SHA1 哈希。
可能的 C#-Java-方法的对应部分 hashTheKey(String key)
:
private static string GetHashedKey()
{
string hashBase64 = String.Empty;
using (SHA1Managed sha1 = new SHA1Managed()) // builder.mDigestAlgorithm = "SHA1";
{
byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey)); // builder.mCharsetName = "UTF8";
hashBase64 = Base64ThirdPartAndroid(hash, true);
//Console.WriteLine("Hash (base64): " + hashBase64);
}
return hashBase64;
}
此方法从 MyCryptoClass.encryptionKey
派生出 SHA1
散列,并对该散列进行 base64 编码。对于 base64 编码,使用方法 Base64ThirdPartAndroid(byte[] arr, bool withoutPadding)
(见下文)。
Java-方法third.part.android.util.Base64.encodeToString(byte[] input, int flags)
(https://github.com/simbiose/Encryption/blob/master/Encryption/main/third/part/android/util/Base64.java)可能的 C# 对应物:
private static string Base64ThirdPartAndroid(byte[] arr, bool withoutPadding)
{
string base64String = System.Convert.ToBase64String(arr);
if (withoutPadding) base64String = base64String.TrimEnd('='); // Remove trailing "="-characters
base64String += "\n"; // Append LF (10)
//Console.WriteLine("Array as base64 encoded string: " + base64String);
return base64String;
}
在 Java 代码中,third.part.android.util.Base64.encodeToString(byte[] input, int flags)
与 flags = Base64.NO_PADDING
一起使用,删除了 base64 编码字符串末尾的“=”字符。此外,还会附加一个换行符(LF,\n
,ASCII 值:10)。如果使用不删除“=”字符或没有终止换行符的 Base64 编码,解密将失败,因为哈希是后来生成的密钥的基础,必须在加密和解密方。据我所知,C# 端没有具有必要特征的 Base64 编码。但是,如果有这样的编码,您当然可以使用它。
将所有三个 C# 对应项添加到您的 MyCryptoClass class
。
此外(对静态字段 encryptionKey
)将静态字段 initialisationVector
、salt
和 secretKey
添加到您的 MyCryptoClass
-class 并分配以下值用于测试目的:
private static string encryptionKey = "A7zb534OPq59gU7q";
private static string salt = "JV5k9GoH";
private static byte[] initialisationVector = Encoding.UTF8.GetBytes("l4iG63jN9Dcg6537");
private static byte[] secretKey = GetSecretKey();
参数的类型对应Java代码中的类型(encryptionKey
和salt
是字符串,initialisationVector
是字节数组)。 GetSecretKey()
生成的密钥存储在字节数组 secretKey
.
中
在您的 C# DecryptText
- 和 EncryptText
方法中将 myRijndael.Key
和 myRijndael.IV
设置为
myRijndael.Key = secretKey;
myRijndael.IV = initialisationVector;
测试修改如下:
用你的Java encrypt
-方法加密以下纯文本:
Test: The quick brown fox jumps over the lazy dog...
将上面的 key/salt/iv 与
结合使用
mBuilder = Builder.getDefaultBuilder("A7zb534OPq59gU7q","JV5k9GoH","l4iG63jN9Dcg6537".getBytes("UTF-8"));
密文为:
mL4ajZtdRgD8CtGSfJGkT24Ebw4SrGUGKQI6bvBw1ziCO/J7SeLiyIw41zumTHMMD9GOYK+kR79CVcpoaHT9TQ==
使用 C# DecryptText
方法对其进行解密再次给出纯文本。下面是两个测试用例:
static void Main(string[] args)
{
// Test 1: Encrypted text from C#
MyCryptoClass mcc = MyCryptoClass.Instance;
string encryptedText = mcc.EncryptText("This is a plain text which needs to be encrypted...");
Console.WriteLine("Encrypted text (base64): " + encryptedText);
string decryptedText = mcc.DecryptText(encryptedText);
Console.WriteLine("Decrypted text: " + decryptedText);
// Test 2: Encrypted text from Java
string javaEncryptedText = "mL4ajZtdRgD8CtGSfJGkT24Ebw4SrGUGKQI6bvBw1ziCO/J7SeLiyIw41zumTHMMD9GOYK+kR79CVcpoaHT9TQ==";
Console.WriteLine("Encrypted text from Java (base64): " + javaEncryptedText);
string javaDecryptedText = mcc.DecryptText(javaEncryptedText);
Console.WriteLine("Decrypted text from Java: " + javaDecryptedText);
}
以下是完整的 C# class,我可以像这样工作 java class
目前我只检查了加密部分
public sealed class MyCryptoClass
{
protected AesManaged myRijndael;
private static string encryptionKey = "MyKey";
private static string salt = "Mysalt";
private static byte[] initialisationVector = new byte[16];
//private static byte[] initialisationVector = Encoding.UTF8.GetBytes("l4iG63jN9Dcg6537");
private static byte[] secretKey = GetSecretKey();
// Singleton pattern used here with ensured thread safety
protected static readonly MyCryptoClass _instance = new MyCryptoClass();
public static MyCryptoClass Instance
{
get { return _instance; }
}
public MyCryptoClass()
{
}
public string DecryptText(string encryptedString)
{
using (myRijndael = new AesManaged())
{
myRijndael.Key = Convert.FromBase64String(encryptionKey);
myRijndael.IV = new byte[16];
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
Byte[] ourEnc = Convert.FromBase64String(encryptedString);
string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);
return ourDec;
}
}
public string EncryptText(string plainText)
{
using (myRijndael = new AesManaged())
{
myRijndael.Key = secretKey;
myRijndael.IV = initialisationVector;
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
byte[] encrypted = EncryptStringToBytes(plainText, myRijndael.Key, myRijndael.IV);
string encString = Convert.ToBase64String(encrypted);
return encString;
}
}
protected byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
public static void GenerateKeyAndIV()
{
// This code is only here for an example
AesManaged myRijndaelManaged = new AesManaged();
myRijndaelManaged.Mode = CipherMode.CBC;
myRijndaelManaged.Padding = PaddingMode.PKCS7;
myRijndaelManaged.GenerateIV();
myRijndaelManaged.GenerateKey();
string newKey = ByteArrayToHexString(myRijndaelManaged.Key);
string newinitVector = ByteArrayToHexString(myRijndaelManaged.IV);
}
protected static byte[] HexStringToByte(string hexString)
{
try
{
int bytesCount = (hexString.Length) / 2;
byte[] bytes = new byte[bytesCount];
for (int x = 0; x < bytesCount; ++x)
{
bytes[x] = Convert.ToByte(hexString.Substring(x * 2, 2), 16);
}
return bytes;
}
catch
{
throw;
}
}
public static string ByteArrayToHexString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
private static byte[] GetSecretKey()
{
string hashedKey = GetHashedKey();
byte[] saltBytes = Encoding.UTF8.GetBytes(salt); // builder.mCharsetName = "UTF8";
int iterations = 1; // builder.mIterationCount = 1
byte[] secretKey = null;
using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(hashedKey, saltBytes, iterations)) // builder.mSecretKeyType = "PBKDF2WithHmacSHA1";
{
secretKey = rfc2898.GetBytes(16); // builder.mKeyLength = 128;
//Console.WriteLine("Key: " + ByteArrayToHexString(secretKey));
}
return secretKey;
}
private static string GetHashedKey()
{
string hashBase64 = String.Empty;
using (SHA1Managed sha1 = new SHA1Managed()) // builder.mDigestAlgorithm = "SHA1";
{
byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey)); // builder.mCharsetName = "UTF8";
hashBase64 = Base64ThirdPartAndroid(hash, true);
//hashBase64 = Base64ThirdPartAndroid(hash, true);
//Console.WriteLine("Hash (base64): " + hashBase64);
}
return hashBase64;
}
private static string Base64ThirdPartAndroid(byte[] arr, bool withoutPadding)
{
string base64String = System.Convert.ToBase64String(arr);
if (withoutPadding) base64String = base64String.TrimEnd('='); // Remove trailing "="-characters
base64String += "\n"; // Append LF (10)
//Console.WriteLine("Array as base64 encoded string: " + base64String);
return base64String;
}
}
此方法 100% 用于加密
public static string AESDecrypt(string text, string secretKey)
{
try
{
SHA1 sha = new SHA1CryptoServiceProvider();
RijndaelManaged aes = new RijndaelManaged();
byte[] finalKey = new byte[16]; // Used to generate finalKey
byte[] byteDataToDecrypt = Convert.FromBase64String(text); // Converting Base64 data to Byte Array
byte[] byteSecretKey = Encoding.UTF8.GetBytes(secretKey); // Converting secret key in byte array
byte[] hashSecretKey = sha.ComputeHash(byteSecretKey); // geneating hash in byte array of secret key
Array.Copy(hashSecretKey, finalKey, 16); // copying fist 16 bytes from hashed secret key to finaly key which to use in algo
aes.KeySize = 128;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.ECB;
aes.Key = finalKey;
using (ICryptoTransform decrypt = aes.CreateDecryptor(aes.Key, null)) //Passing IV as NULL because IV is not present in the Java Code
{
byte[] dest = decrypt.TransformFinalBlock(byteDataToDecrypt, 0, byteDataToDecrypt.Length);
decrypt.Dispose();
return Encoding.UTF8.GetString(dest);
}
}
catch (Exception ex)
{
throw ex;
}
}
我在尝试解密已在 Java 中使用以下属性(Java 代码)
加密的字符串时遇到问题public static Builder getDefaultBuilder(String key, String salt, byte[] iv) {
return new Builder()
.setIv(iv)
.setKey(key)
.setSalt(salt)
.setKeyLength(128)
.setKeyAlgorithm("AES")
.setCharsetName("UTF8")
.setIterationCount(1)
.setDigestAlgorithm("SHA1")
.setBase64Mode(Base64.DEFAULT)
.setAlgorithm("AES/CBC/PKCS5Padding")
.setSecureRandomAlgorithm("SHA1PRNG")
.setSecretKeyType("PBKDF2WithHmacSHA1");
}
到目前为止,这是我的代码 (C#)
public string DecryptText(string encryptedString)
{
using (myRijndael = new RijndaelManaged())
{
myRijndael.Key = Convert.FromBase64String(encryptionKey);
myRijndael.IV = new byte[16];
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
Byte[] ourEnc = Convert.FromBase64String(encryptedString);
string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);
return ourDec;
}
}
protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
但是当我尝试解密时出现以下异常“System.Security.Cryptography.CryptographicException: 'Specified key is not a valid size for this algorithm.' ".
Java 代码的来源在这里 https://github.com/simbiose/Encryption/blob/master/Encryption/main/se/simbio/encryption/Encryption.java
这是加密时的Java代码
public String encrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException {
if (data == null) return null;
SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
byte[] dataBytes = data.getBytes(mBuilder.getCharsetName());
Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
return Base64.encodeToString(cipher.doFinal(dataBytes), mBuilder.getBase64Mode());
}
private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance(mBuilder.getSecretKeyType());
KeySpec spec = new PBEKeySpec(key, mBuilder.getSalt().getBytes(mBuilder.getCharsetName()), mBuilder.getIterationCount(), mBuilder.getKeyLength());
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), mBuilder.getKeyAlgorithm());
}
private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance(mBuilder.getDigestAlgorithm());
messageDigest.update(key.getBytes(mBuilder.getCharsetName()));
return Base64.encodeToString(messageDigest.digest(), Base64.NO_PADDING).toCharArray();
}
我已经为这个问题苦苦挣扎了两天,因为我并没有真正使用加密,所以任何帮助将不胜感激。
谢谢!
更新: 整个class
public sealed class MyCryptoClass
{
protected RijndaelManaged myRijndael;
private static string encryptionKey = "random";
// Singleton pattern used here with ensured thread safety
protected static readonly MyCryptoClass _instance = new MyCryptoClass();
public static MyCryptoClass Instance
{
get { return _instance; }
}
public MyCryptoClass()
{
}
public string DecryptText(string encryptedString)
{
using (myRijndael = new RijndaelManaged())
{
myRijndael.Key = Convert.FromBase64String(encryptionKey);
myRijndael.IV = new byte[16];
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
Byte[] ourEnc = Convert.FromBase64String(encryptedString);
string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);
return ourDec;
}
}
public string EncryptText(string plainText)
{
using (myRijndael = new RijndaelManaged())
{
myRijndael.Key = HexStringToByte(encryptionKey);
myRijndael.IV = HexStringToByte(initialisationVector);
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
byte[] encrypted = EncryptStringToBytes(plainText, myRijndael.Key, myRijndael.IV);
string encString = Convert.ToBase64String(encrypted);
return encString;
}
}
protected byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
public static void GenerateKeyAndIV()
{
// This code is only here for an example
RijndaelManaged myRijndaelManaged = new RijndaelManaged();
myRijndaelManaged.Mode = CipherMode.CBC;
myRijndaelManaged.Padding = PaddingMode.PKCS7;
myRijndaelManaged.GenerateIV();
myRijndaelManaged.GenerateKey();
string newKey = ByteArrayToHexString(myRijndaelManaged.Key);
string newinitVector = ByteArrayToHexString(myRijndaelManaged.IV);
}
protected static byte[] HexStringToByte(string hexString)
{
try
{
int bytesCount = (hexString.Length) / 2;
byte[] bytes = new byte[bytesCount];
for (int x = 0; x < bytesCount; ++x)
{
bytes[x] = Convert.ToByte(hexString.Substring(x * 2, 2), 16);
}
return bytes;
}
catch
{
throw;
}
}
public static string ByteArrayToHexString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
}
因为你的
MyCryptoClass.encryptionKey
对应于你的Encryption.Builder.mKey
你必须在 C# 端生成密钥,即你必须在 C# 端为每个Java-这个过程涉及的方法。这些 Java 方法是getSecretKey(char[] key)
、hashTheKey(String key)
以及third.part.android.util.Base64.encodeToString(byte[] input, int flags)
.Java-方法
getSecretKey(char[] key)
的可能 C# 对应物:private static byte[] GetSecretKey() { string hashedKey = GetHashedKey(); byte[] saltBytes = Encoding.UTF8.GetBytes(salt); // builder.mCharsetName = "UTF8"; int iterations = 1; // builder.mIterationCount = 1 byte[] secretKey = null; using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(hashedKey, saltBytes, iterations)) // builder.mSecretKeyType = "PBKDF2WithHmacSHA1"; { secretKey = rfc2898.GetBytes(16); // builder.mKeyLength = 128; //Console.WriteLine("Key: " + ByteArrayToHexString(secretKey)); } return secretKey; }
此方法使用
PBKDF2WithHmacSHA1
以密钥、salt、迭代计数和密钥长度作为输入来导出密钥。此处使用的密钥(更准确地说是密码)是由GetHashedKey()
(见下文)提供的MyCryptoClass.encryptionKey
的 base64 编码的 SHA1 哈希。可能的 C#-Java-方法的对应部分
hashTheKey(String key)
:private static string GetHashedKey() { string hashBase64 = String.Empty; using (SHA1Managed sha1 = new SHA1Managed()) // builder.mDigestAlgorithm = "SHA1"; { byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey)); // builder.mCharsetName = "UTF8"; hashBase64 = Base64ThirdPartAndroid(hash, true); //Console.WriteLine("Hash (base64): " + hashBase64); } return hashBase64; }
此方法从
MyCryptoClass.encryptionKey
派生出SHA1
散列,并对该散列进行 base64 编码。对于 base64 编码,使用方法Base64ThirdPartAndroid(byte[] arr, bool withoutPadding)
(见下文)。Java-方法
third.part.android.util.Base64.encodeToString(byte[] input, int flags)
(https://github.com/simbiose/Encryption/blob/master/Encryption/main/third/part/android/util/Base64.java)可能的 C# 对应物:private static string Base64ThirdPartAndroid(byte[] arr, bool withoutPadding) { string base64String = System.Convert.ToBase64String(arr); if (withoutPadding) base64String = base64String.TrimEnd('='); // Remove trailing "="-characters base64String += "\n"; // Append LF (10) //Console.WriteLine("Array as base64 encoded string: " + base64String); return base64String; }
在 Java 代码中,
third.part.android.util.Base64.encodeToString(byte[] input, int flags)
与flags = Base64.NO_PADDING
一起使用,删除了 base64 编码字符串末尾的“=”字符。此外,还会附加一个换行符(LF,\n
,ASCII 值:10)。如果使用不删除“=”字符或没有终止换行符的 Base64 编码,解密将失败,因为哈希是后来生成的密钥的基础,必须在加密和解密方。据我所知,C# 端没有具有必要特征的 Base64 编码。但是,如果有这样的编码,您当然可以使用它。将所有三个 C# 对应项添加到您的
MyCryptoClass class
。此外(对静态字段
encryptionKey
)将静态字段initialisationVector
、salt
和secretKey
添加到您的MyCryptoClass
-class 并分配以下值用于测试目的:private static string encryptionKey = "A7zb534OPq59gU7q"; private static string salt = "JV5k9GoH"; private static byte[] initialisationVector = Encoding.UTF8.GetBytes("l4iG63jN9Dcg6537"); private static byte[] secretKey = GetSecretKey();
参数的类型对应Java代码中的类型(
encryptionKey
和salt
是字符串,initialisationVector
是字节数组)。GetSecretKey()
生成的密钥存储在字节数组secretKey
. 中
在您的 C#
DecryptText
- 和EncryptText
方法中将myRijndael.Key
和myRijndael.IV
设置为myRijndael.Key = secretKey; myRijndael.IV = initialisationVector;
测试修改如下:
用你的Java
encrypt
-方法加密以下纯文本:Test: The quick brown fox jumps over the lazy dog...
将上面的 key/salt/iv 与
结合使用mBuilder = Builder.getDefaultBuilder("A7zb534OPq59gU7q","JV5k9GoH","l4iG63jN9Dcg6537".getBytes("UTF-8"));
密文为:
mL4ajZtdRgD8CtGSfJGkT24Ebw4SrGUGKQI6bvBw1ziCO/J7SeLiyIw41zumTHMMD9GOYK+kR79CVcpoaHT9TQ==
使用 C#
DecryptText
方法对其进行解密再次给出纯文本。下面是两个测试用例:static void Main(string[] args) { // Test 1: Encrypted text from C# MyCryptoClass mcc = MyCryptoClass.Instance; string encryptedText = mcc.EncryptText("This is a plain text which needs to be encrypted..."); Console.WriteLine("Encrypted text (base64): " + encryptedText); string decryptedText = mcc.DecryptText(encryptedText); Console.WriteLine("Decrypted text: " + decryptedText); // Test 2: Encrypted text from Java string javaEncryptedText = "mL4ajZtdRgD8CtGSfJGkT24Ebw4SrGUGKQI6bvBw1ziCO/J7SeLiyIw41zumTHMMD9GOYK+kR79CVcpoaHT9TQ=="; Console.WriteLine("Encrypted text from Java (base64): " + javaEncryptedText); string javaDecryptedText = mcc.DecryptText(javaEncryptedText); Console.WriteLine("Decrypted text from Java: " + javaDecryptedText); }
以下是完整的 C# class,我可以像这样工作 java class 目前我只检查了加密部分
public sealed class MyCryptoClass
{
protected AesManaged myRijndael;
private static string encryptionKey = "MyKey";
private static string salt = "Mysalt";
private static byte[] initialisationVector = new byte[16];
//private static byte[] initialisationVector = Encoding.UTF8.GetBytes("l4iG63jN9Dcg6537");
private static byte[] secretKey = GetSecretKey();
// Singleton pattern used here with ensured thread safety
protected static readonly MyCryptoClass _instance = new MyCryptoClass();
public static MyCryptoClass Instance
{
get { return _instance; }
}
public MyCryptoClass()
{
}
public string DecryptText(string encryptedString)
{
using (myRijndael = new AesManaged())
{
myRijndael.Key = Convert.FromBase64String(encryptionKey);
myRijndael.IV = new byte[16];
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
Byte[] ourEnc = Convert.FromBase64String(encryptedString);
string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);
return ourDec;
}
}
public string EncryptText(string plainText)
{
using (myRijndael = new AesManaged())
{
myRijndael.Key = secretKey;
myRijndael.IV = initialisationVector;
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
byte[] encrypted = EncryptStringToBytes(plainText, myRijndael.Key, myRijndael.IV);
string encString = Convert.ToBase64String(encrypted);
return encString;
}
}
protected byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
public static void GenerateKeyAndIV()
{
// This code is only here for an example
AesManaged myRijndaelManaged = new AesManaged();
myRijndaelManaged.Mode = CipherMode.CBC;
myRijndaelManaged.Padding = PaddingMode.PKCS7;
myRijndaelManaged.GenerateIV();
myRijndaelManaged.GenerateKey();
string newKey = ByteArrayToHexString(myRijndaelManaged.Key);
string newinitVector = ByteArrayToHexString(myRijndaelManaged.IV);
}
protected static byte[] HexStringToByte(string hexString)
{
try
{
int bytesCount = (hexString.Length) / 2;
byte[] bytes = new byte[bytesCount];
for (int x = 0; x < bytesCount; ++x)
{
bytes[x] = Convert.ToByte(hexString.Substring(x * 2, 2), 16);
}
return bytes;
}
catch
{
throw;
}
}
public static string ByteArrayToHexString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
private static byte[] GetSecretKey()
{
string hashedKey = GetHashedKey();
byte[] saltBytes = Encoding.UTF8.GetBytes(salt); // builder.mCharsetName = "UTF8";
int iterations = 1; // builder.mIterationCount = 1
byte[] secretKey = null;
using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(hashedKey, saltBytes, iterations)) // builder.mSecretKeyType = "PBKDF2WithHmacSHA1";
{
secretKey = rfc2898.GetBytes(16); // builder.mKeyLength = 128;
//Console.WriteLine("Key: " + ByteArrayToHexString(secretKey));
}
return secretKey;
}
private static string GetHashedKey()
{
string hashBase64 = String.Empty;
using (SHA1Managed sha1 = new SHA1Managed()) // builder.mDigestAlgorithm = "SHA1";
{
byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey)); // builder.mCharsetName = "UTF8";
hashBase64 = Base64ThirdPartAndroid(hash, true);
//hashBase64 = Base64ThirdPartAndroid(hash, true);
//Console.WriteLine("Hash (base64): " + hashBase64);
}
return hashBase64;
}
private static string Base64ThirdPartAndroid(byte[] arr, bool withoutPadding)
{
string base64String = System.Convert.ToBase64String(arr);
if (withoutPadding) base64String = base64String.TrimEnd('='); // Remove trailing "="-characters
base64String += "\n"; // Append LF (10)
//Console.WriteLine("Array as base64 encoded string: " + base64String);
return base64String;
}
}
此方法 100% 用于加密
public static string AESDecrypt(string text, string secretKey)
{
try
{
SHA1 sha = new SHA1CryptoServiceProvider();
RijndaelManaged aes = new RijndaelManaged();
byte[] finalKey = new byte[16]; // Used to generate finalKey
byte[] byteDataToDecrypt = Convert.FromBase64String(text); // Converting Base64 data to Byte Array
byte[] byteSecretKey = Encoding.UTF8.GetBytes(secretKey); // Converting secret key in byte array
byte[] hashSecretKey = sha.ComputeHash(byteSecretKey); // geneating hash in byte array of secret key
Array.Copy(hashSecretKey, finalKey, 16); // copying fist 16 bytes from hashed secret key to finaly key which to use in algo
aes.KeySize = 128;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.ECB;
aes.Key = finalKey;
using (ICryptoTransform decrypt = aes.CreateDecryptor(aes.Key, null)) //Passing IV as NULL because IV is not present in the Java Code
{
byte[] dest = decrypt.TransformFinalBlock(byteDataToDecrypt, 0, byteDataToDecrypt.Length);
decrypt.Dispose();
return Encoding.UTF8.GetString(dest);
}
}
catch (Exception ex)
{
throw ex;
}
}