![](/img/trans.png)
[英]AES encryption in C Sharp and decryption in Android using PBKDF2WithHmacSHA1
[英]from java to c# PBKDF2WithHmacSHA1 Padding is invalid and cannot be removed
我必須解密來自使用PBKDF2WithHmacSHA1的Java應用程序的數據。 我對Java代碼沒有任何控制權,但是我有源代碼的副本。
我基於此示例擁有的C#部分: http : //steelmon.wordpress.com/2013/07/01/simple-interoperable-encryption-in-java-and-net/如果我從c#進行加密/解密,則效果很好,但是當我嘗試使用c#解密時,我得到“填充無效並且無法刪除”。 例外..
我已經嘗試了所有可以找到的相關示例。 確保salt和key在java和c#中相同,並檢查我在兩端都使用PBKDF2以及其他我能想到的東西。
鹽:cdWSu23E9BLbNXWUTnrznFgc
密鑰:gygp6yevKWKBUwFy4GXpuFwT
以下是Java代碼:
public class Cryption {
private static int KEYLEN_BITS = 256;
private static int ITERATIONS = 128;
static private Log log = LogFactory.getLog(Cryption.class);
private static SecretKeyFactory secretKeyFactory = null;
private static Map<String, SecretKey> secretKeyMap = new HashMap<String, SecretKey>();
private String password = null;
private SecretKey secretKey = null;
private Cipher cipher = null;
private synchronized static SecretKeyFactory createSecretKeyFactory() {
try {
if(secretKeyFactory == null) {
secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
}
} catch(Exception e) {
log.error("createSecretKeyFactory", e);
}
return secretKeyFactory;
}
private static SecretKey createSecretKey(String password, byte []salt) {
SecretKey secretKey = secretKeyMap.get(password);
if(secretKey == null) {
try {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEYLEN_BITS);
SecretKey tmpSecretKey = createSecretKeyFactory().generateSecret(spec);
secretKey = new SecretKeySpec(tmpSecretKey.getEncoded(), "AES");
secretKeyMap.put(password, secretKey);
} catch(Exception e) {
log.error("getSecretKey", e);
}
}
return secretKey;
}
private boolean createCipher(String password, byte []salt, byte[] iv, int mode) {
try {
this.password = password;
secretKey = createSecretKey(password, salt);
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
if(iv != null) {
cipher.init(mode, secretKey, new IvParameterSpec(iv));
} else {
cipher.init(mode, secretKey);
}
} catch(Exception e) {
log.error("getCipher", e);
return false;
}
return true;
}
public String getPassword() {
return password;
}
public byte[] getCipherIV() {
byte [] iv = null;
try {
AlgorithmParameters params = cipher.getParameters();
iv = params.getParameterSpec(IvParameterSpec.class).getIV();
} catch(Exception e) {
log.error("getCipherIV", e);
}
return iv;
}
public byte[] execute(byte []in) {
byte [] out = null;
try {
out = cipher.doFinal(in);
} catch(Exception e) {
log.error("execute", e);
}
return out;
}
static Cryption create(String password, byte[] salt, byte[] iv, int mode) {
Cryption cryption = new Cryption();
cryption.createCipher(password, salt, iv, mode);
return cryption;
}
static void init(Cryption cryption, byte[] iv, int mode) {
try {
if(iv != null) {
cryption.cipher.init(mode, cryption.secretKey, new IvParameterSpec(iv));
} else {
cryption.cipher.init(mode, cryption.secretKey);
}
} catch(Exception e) {
log.error("init", e);
}
}
}
這是我用於解密的C#代碼
private void btnDecrypt_Click(object sender, EventArgs e)
{
byte[] salt = Encoding.UTF8.GetBytes("cdWSu23E9BLbNXWUTnrznFgc");
int iterations = 128;
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes("gygp6yevKWKBUwFy4GXpuFwT", salt, iterations);
byte[] key = rfc2898.GetBytes(24);
AesManaged aesCipher = new AesManaged();
aesCipher.KeySize = 256;
aesCipher.BlockSize = 128;
aesCipher.Mode = CipherMode.CBC;
aesCipher.Padding = PaddingMode.PKCS7;
aesCipher.Key = key;
String cipherB64 = "cFSGQv/mEnzaU9fAKIyFTvlIMxKnN8yRgzXm/E7MLoBF3g8iE6tloBcI84po+cT3r2Yz+wothDhYoM02yppPLHcv8Mj0FLF3frtAlOGq3TormcbwmSzx3JdB+GtFtliZkxNCyeTiGWi3jqnHsTRo7G3lQPEEUXEt03kFjErZfqEf8IwcD+PNqwtsU1fCn0gNgVvcvJck795U304QfDCOfkVEjNomGhbz4hTy4HPgokXUSWOEQEihTjz3j70+JZvLhsYGRnIxmRad8gsn7sVBr8vfG9KnL8i1CUh9vKuGLjiZCIhz7r00j4bmQYrjoj9yoKJmPVQsxW7FfTnOJFwlYw==";
String ivB64 = "wWJLcuahcy4ZLLkW6CIqaA==";
byte[] cipherText = Convert.FromBase64String(cipherB64);
aesCipher.IV = Convert.FromBase64String(ivB64);
ICryptoTransform decryptTransform = aesCipher.CreateDecryptor();
byte[] plainText = decryptTransform.TransformFinalBlock(cipherText, 0, cipherText.Length);
txtOutput.Text = System.Text.Encoding.UTF8.GetString(plainText);
}
如果任何參數都不匹配,則加密將失敗。 您需要檢查加密和解密方法的所有輸入,以確保它們相同。 這不僅檢查Base64字符,還檢查兩種密鑰生成方法和IV轉換的byte[]
輸出。 字節數組需要一點一點地精確匹配,因為即使是一個位錯誤也會破壞解密。
首先簡化部分代碼。 使用顯式編碼的密鑰:
byte[] myKey = new byte[] {0x00, 0x01, 0x02 ... 0x0F};
將消除兩種密鑰生成方法中的任何差異。 您在系統之間移動,因此您不能依賴默認設置作為參數。 您需要在兩側顯式設置每個參數。
我懷疑您在java和c#中都使用了與密碼完全相同的輸入-對於初學者,您在c#中生成的密鑰為24字節(196位),但是您指定的密鑰大小為256。那是不對的。
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes("gygp6yevKWKBUwFy4GXpuFwT", salt, iterations);
>>> byte[] key = rfc2898.GetBytes(24);
AesManaged aesCipher = new AesManaged();
>>> aesCipher.KeySize = 256;
aesCipher.BlockSize = 128;
aesCipher.Mode = CipherMode.CBC;
aesCipher.Padding = PaddingMode.PKCS7;
>>> aesCipher.Key = key;
從java( tmpSecretKey.getEncoded()
的字節)和c#( byte[] key
的值)中生成的鍵中進行原始打印,然后進行比較。 如果/當這些匹配但仍然不起作用時,對iv和要加密的數據的字節值執行相同的操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.