简体   繁体   English

从Java到c#PBKDF2WithHmacSHA1填充无效,无法删除

[英]from java to c# PBKDF2WithHmacSHA1 Padding is invalid and cannot be removed

I have to decrypt data coming from a java application that uses PBKDF2WithHmacSHA1. 我必须解密来自使用PBKDF2WithHmacSHA1的Java应用程序的数据。 I do not have any control over the java code but I have a copy of the source. 我对Java代码没有任何控制权,但是我有源代码的副本。

The C# part i have based on this example: http://steelmon.wordpress.com/2013/07/01/simple-interoperable-encryption-in-java-and-net/ It works fine if I encrypt/decrypt from c#, but when I try to decrypt in c#, then I get the "Padding is invalid and cannot be removed." 我基于此示例拥有的C#部分: http : //steelmon.wordpress.com/2013/07/01/simple-interoperable-encryption-in-java-and-net/如果我从c#进行加密/解密,则效果很好,但是当我尝试使用c#解密时,我得到“填充无效并且无法删除”。 exception.. 例外..

I have tried all the relevant examples I could find. 我已经尝试了所有可以找到的相关示例。 Made sure the salt and key are the same in java and c#, checked that I am using PBKDF2 on both ends and anything else I could think of. 确保salt和key在java和c#中相同,并检查我在两端都使用PBKDF2以及其他我能想到的东西。

salt: cdWSu23E9BLbNXWUTnrznFgc 盐:cdWSu23E9BLbNXWUTnrznFgc

key: gygp6yevKWKBUwFy4GXpuFwT 密钥:gygp6yevKWKBUwFy4GXpuFwT

Below is the java code: 以下是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);
    }
}   
}

And here is the c# code i use for decrypting 这是我用于解密的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);
    }

Crypto is designed to fail if any parameters do not match. 如果任何参数都不匹配,则加密将失败。 You need to check all inputs to both encryption and decryption methods to ensure that they are the same. 您需要检查加密和解密方法的所有输入,以确保它们相同。 This is not just checking the Base64 characters, but checking the byte[] outputs from the two key generation methods and the IV conversions. 这不仅检查Base64字符,还检查两种密钥生成方法和IV转换的byte[]输出。 The byte arrays need to match exactly, bit for bit, since even a single bit error will wreck decryption. 字节数组需要一点一点地精确匹配,因为即使是一个位错误也会破坏解密。

Start by simplifying parts of your code. 首先简化部分代码。 Using an explicitly coded key: 使用显式编码的密钥:

byte[] myKey = new byte[] {0x00, 0x01, 0x02 ... 0x0F};

will remove any differences in the two key generation methods. 将消除两种密钥生成方法中的任何差异。 You are moving between systems so you cannot rely on default settings for parameters. 您在系统之间移动,因此您不能依赖默认设置作为参数。 You need to explicitly set every parameter on both sides. 您需要在两侧显式设置每个参数。

I doubt you use the exact same input to the ciphers in both java and c# - for starters the key you generate in c# is 24 bytes (196 bits), but you specify a keysize of 256. That can't be right. 我怀疑您在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;

Do a raw print out of the generated keys in both java (the bytes from tmpSecretKey.getEncoded() ) and in c# (the value of byte[] key ) and compare. 从java( tmpSecretKey.getEncoded()的字节)和c#( byte[] key的值)中生成的键中进行原始打印,然后进行比较。 If/when those match but it still don´t work do the same with the iv and the byte value of the data to encrypt. 如果/当这些匹配但仍然不起作用时,对iv和要加密的数据的字节值执行相同的操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM