【问题标题】:from java to c# PBKDF2WithHmacSHA1 Padding is invalid and cannot be removed从java到c# PBKDF2WithHmacSHA1 填充无效,不能去掉
【发布时间】:2014-06-11 05:57:46
【问题描述】:

我必须解密来自使用 PBKDF2WithHmacSHA1 的 java 应用程序的数据。 我对 java 代码没有任何控制权,但我有源代码的副本。

我基于此示例的 C# 部分: http://steelmon.wordpress.com/2013/07/01/simple-interoperable-encryption-in-java-and-net/ 如果我从 c# 加密/解密它工作正常,但是当我尝试在 c# 中解密时,我得到“填充无效并且无法删除”。例外..

我已经尝试了所有我能找到的相关示例。 确保 java 和 c# 中的 salt 和 key 相同,检查我是否在两端使用 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);
    }

【问题讨论】:

    标签: c# java encryption interop padding


    【解决方案1】:

    我怀疑您在 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 和要加密的数据的字节值执行相同的操作。

    【讨论】:

    • 你就是男人!我一定是盯着自己看,错过了(DOH!)将密钥更改为 32 字节,它按预期工作
    【解决方案2】:

    如果任何参数不匹配,加密就会失败。您需要检查加密和解密方法的所有输入,以确保它们相同。这不仅仅是检查 Base64 字符,而是检查两种密钥生成方法和 IV 转换的 byte[] 输出。字节数组需要逐位精确匹配,因为即使是单个位错误也会破坏解密。

    首先要简化部分代码。使用显式编码的密钥:

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

    将消除两种密钥生成方法中的任何差异。您正在系统之间移动,因此您不能依赖参数的默认设置。您需要在两边显式设置每个参数。

    【讨论】:

      猜你喜欢
      • 2019-01-31
      • 1970-01-01
      • 1970-01-01
      • 2019-07-26
      • 2014-01-05
      • 1970-01-01
      • 1970-01-01
      • 2021-03-15
      • 2020-01-17
      相关资源
      最近更新 更多