我正在使用Android,我試圖解密在C夏普服務器中加密的郵件。在C夏普的AES加密和在Android中使用PBKDF2WithHmacSHA1解密
以下是C#Cryptor的代碼,它使用256位長密鑰,128位長IV,5000次迭代。它使用Rfc2898DeriveBytes類,因此它與Android中的PBKDF2WithHmacSHA1相同。
C#Cryptor的解密函數將其作爲IV作爲256位長密鑰的(反向的)前128位。
namespace CompanyName.Framework.Encryption
{
internal class SymmetricCryptor : ISymmetricCryptor
{
internal static int KeyLengthInBytes = 32;
internal int Iterations = 5000;
#region Private Fields
// RijndaelManaged aes; old version
AesManaged aes;
int IVLength = KeyLengthInBytes >> 1;
#endregion Private Fields
#region Internal Constructors
internal SymmetricCryptor()
{
aes = new AesManaged
{
Mode = CipherMode.CBC,
KeySize= KeyLengthInBytes<<3,
Padding = PaddingMode.PKCS7,
};
//aes.KeySize = KeyLengthInBytes << 3;
//aes.Padding = PaddingMode.Zeros; //PKCS7 can not be used with stream
}
#endregion Internal Constructors
#region Public Methods
public byte[] Decrypt(byte[] cryptedData, string password, IVMode ivmode)
{
using (MemoryStream ms = new MemoryStream(cryptedData))
{
using (MemoryStream data = new MemoryStream())
{
Decrypt(ms, data, password,ivmode);
return data.ToArray();
}
}
}
public void Encrypt(Stream data, Stream trgStream, string password, IVMode ivmode)
{
try
{
var key = GetKey(password);
var iv = (ivmode == IVMode.Auto)
?key.GetBytes(IVLength).Reverse().ToArray()
: new byte[IVLength];
var dc = aes.CreateEncryptor(key.GetBytes(KeyLengthInBytes), iv);
using (CryptoStream cryptor = new CryptoStream(trgStream, dc, CryptoStreamMode.Write))
{
data.CopyTo(cryptor);
cryptor.FlushFinalBlock();
cryptor.Close();
}
}
catch (Exception)
{
throw new InvalidOperationException("Invalid password.");
}
}
public void Decrypt(Stream cryptedData, Stream trgStream, string password, IVMode ivmode)
{
try
{
var key= GetKey(password);
var iv = (ivmode == IVMode.Auto)
? key.GetBytes(IVLength).Reverse().ToArray()
: new byte[IVLength];
var dc = aes.CreateDecryptor(key.GetBytes(KeyLengthInBytes),iv);
using (CryptoStream cryptor = new CryptoStream(cryptedData, dc, CryptoStreamMode.Read))
{
cryptor.CopyTo(trgStream);
cryptor.Close();
}
}
catch (Exception)
{
throw new InvalidOperationException("Invalid password.");
}
}
public byte[] Encrypt(byte[] data, string password, IVMode ivmode)
{
using (MemoryStream ms = new MemoryStream(data))
{
using (MemoryStream cData = new MemoryStream())
{
Encrypt(ms, cData, password,ivmode);
return cData.ToArray();
}
}
}
#endregion Public Methods
#region Private Methods
private Rfc2898DeriveBytes GetKey(string password)
{
try
{
var iv =
CompanyName.Framework.Cryptography.Digest.SHA1.Compute(password);
return new Rfc2898DeriveBytes(password, iv, Iterations);
}
catch (Exception)
{
throw;
}
}
#endregion Private Methods
}
}
我的Android Cryptor,它試圖解密由上述C夏普Cryptor看起來像這樣加密的消息,我試圖將C夏普Cryptor的解密方法,包括:
public class Cryptor {
private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";
private static final String AES = "AES";
private static final String RANDOM_ALGO = "SHA1PRNG";
private static final int KEY_LENGTH_IN_BITS = 256;
private static final int IV_LENGTH = 16;
private static final int PBE_ITERATION_COUNT = 5000;
private static final int PBE_SALT_LENGTH_INT_BITS = 128;
private static final String PBE_ALGO = "PBKDF2WithHmacSHA1";
public static byte[] generateKeyFromPassword(String password, int Size) throws GeneralSecurityException {
byte[] salt = generateSalt();
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, PBE_ITERATION_COUNT, Size);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBE_ALGO);
byte[] data = keyFactory.generateSecret(keySpec).getEncoded();
return data;
}
private static byte[] generateSalt() throws GeneralSecurityException {
return randomBytes(PBE_SALT_LENGTH_INT_BITS);
}
private static byte[] randomBytes(int length) throws GeneralSecurityException {
SecureRandom random = SecureRandom.getInstance(RANDOM_ALGO);
byte[] b = new byte[length];
random.nextBytes(b);
return b;
}
public static byte[] decrypt(byte[] cipherText, String password) throws GeneralSecurityException {
byte[] keyBytes = generateKeyFromPassword(password, 256);
byte[] ivBytes = generateKeyFromPassword(password, 128);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
ivBytes = reverse(ivBytes);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, AES);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decrypted = cipher.doFinal(cipherText);
return decrypted;
}
public static byte[] reverse(byte[] array) {
if (array == null) {
return null;
}
int i = 0;
int j = array.length - 1;
byte tmp;
while (j > i) {
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
j--;
i++;
}
return array;
}
但它沒有工作,當最後被稱爲我得到一個
javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT
例外。我不知道我在做什麼錯,因爲我在Android中的Decrypt方法與C Sharp中的Decrypt方法完全相同:首先,我從由Csharp服務器和我共享的密碼生成密鑰。然後我生成一個隨機的128位IV,反轉它不是必須的,但C Sharp實現反轉它,所以我也這樣做。誰能告訴我我做錯了什麼?這裏是我使用Cryptor背景:
//open the client channel, read and return the response as byte[]
Channel clientChannel = new Channel(serverAddress);
byte[] result = clientChannel.execute(serviceID.toString(), data);
//result[] is encrypted data. firstTen is the shared Password
byte[] decrypted = Cryptor.decrypt(result, firstTen);
服務器返回結果爲Base64加密,通過它進行解密之前,我通過結果[]數組:它配備爲Base64字符串 。我得到的結果[]數組通過:
Base64.decode(result, Base64.NO_WRAP);
而不是試圖找出C#和Android之間的區別,你最好先解決你的加密問題。初始化向量應該是隨機數據,不依賴於密碼。否則,它不能達到目的,因爲相同的純文本和密碼將始終導致相同的輸出。 PBKDF2是爲了散列需要存儲的密碼而構建的。當你從密碼中得到鹽時,你會失敗鹽析的目的。而在目前的情況下,我根本看不到PBKDF2的任何需求。什麼是「反向」?你認爲它會增加安全性嗎? – Codo
是的你是對的,我已經更新了我的代碼,現在隨機產生一個128位的IV。在C Sharp Code中,它已經隨機生成。但我仍然得到提到的例外。你是什麼意思,你沒有看到PBKDF2的需要。我們與C Sharp服務器有共享密碼,我需要使用這個共享密碼來生成密鑰和IV密碼?除了使用PBKDF2以外,我該怎麼做?反轉是沒有必要的,但服務器做到了,所以我也做到了。 C代碼不是我的代碼,Java代碼是我的代碼 – Gravity
如果您使用密碼生成salt或IV,則您尚未理解salt和IV的用途。它打敗了它的目的。請研究加密的基礎知識並重新訪問您的設計。 (對不起,我太生硬了......) – Codo