【问题标题】:AES Java Multithreading exceptionsAES Java 多线程异常
【发布时间】:2012-02-17 22:32:32
【问题描述】:

我注意到我正在运行的应用程序在同时解密时抛出异常。我写了以下内容来测试:

public void run() {
    for(int i=0; i<100000; i++){
        String encrypted = Crypt.encrypt(
                "Lorem ipsum dolor sit amet.",
                "password"
        );

        String decrypted = Crypt.decrypt(encrypted, "password")[0];
        System.out.println(decrypted);
    }
}


public static void main(String[] args) {

    Thread t1 = new Thread(new Main());
    Thread t2 = new Thread(new Main());

    t1.start();
    t2.start();

}

地穴方法如下:

public static String encrypt(String input, String key){

    try {

        byte[] ivBytes = new byte[16];
        SecureRandom.getInstance("SHA1PRNG").nextBytes(ivBytes);

        IvParameterSpec ips = new IvParameterSpec(ivBytes);
        byte[] keybytes = md5(key);//This isn't final. Don't worry ;)
        byte[] crypted = null;
        SecretKeySpec skey = new SecretKeySpec(keybytes, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skey, ips);
        byte[] ptext = input.getBytes("UTF-8");
        crypted = cipher.doFinal(ptext);

        return Base64.encodeBase64String(ivBytes)+Base64.encodeBase64String(crypted);
    }catch(Exception e){
        e.printStackTrace();
    }
    return null;
}

public static String[] decrypt(String input, String key){

    String iv = input.substring(0, 24);
    String encrypted = input.substring(24);
    try {
        IvParameterSpec ips = new IvParameterSpec(Base64.decodeBase64(iv));
        byte[] keybytes = md5(key);//This isn't final. Don't worry ;)
        byte[] output = null;
        SecretKeySpec skey = new SecretKeySpec(keybytes, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skey, ips);
        output = cipher.doFinal(Base64.decodeBase64(encrypted));
        if(output==null){
            throw new Exception();
        }

        return new String[]{new String(output),iv};
    }catch(Exception e){
        e.printStackTrace();
    }
}

果然第一次尝试两个线程都失败了:

javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at common.Crypt.decrypt(Crypt.java:122)
at bootstrap.Main.run(Main.java:427)
at java.lang.Thread.run(Thread.java:680)

在每个线程上大约有 20 次尝试成功(大概是在不安全调用不相交的地方),然后抛出异常。这种模式似乎还在继续。

如果有帮助,我会在 OS X 10.7.2 上运行它。

如果我理解正确,这可能只是供应商问题,因为它使用的是 Sun JDK,我可以轻松换掉它,但我认为最好就此获得一些更有经验的意见并提出来,以防有人遇到偶然发现了同样的问题。

如果有人能指出线程安全加密+解密方案的方向,它可以达到相同的结果,我将不胜感激。

谢谢, 马库斯

【问题讨论】:

  • 你有一个错误(虽然无关):new String(output) 应该是new String(output, "UTF-8")。我没有在我的 Windows 7 机器上重现它,JDK 1.6.0_26,md5 只做 key.getBytes("UTF-8) 和来自 commons-codec 的 Base64。也许问题来自你的 md5 或 Base64 实现,你不说。(我用“passwordpassword”作为key)
  • 我的 Crypt 类中的 javax.crypto.Cipher.doFinal 行抛出了异常。这绝对是一个比我的代码更低级别的问题。如果我插入一个有效的加密字符串,而不是使用 encrypt() 创建一个带有随机 IV 的字符串(这可能是问题所在),问题仍然会间歇性发生,这排除了流氓 Base64 字符串或 md5 问题的可能性。我尝试在我躺着的 Ubuntu 机器上运行它,但出现了同样的问题。
  • 原来我的 md5() 方法随机中断导致错误。感谢您指出问题可能一直存在。
  • 不完全。该问题的问题是在线程之间使用单个实例,我每个线程使用一个实例。值得注意的是那些发现这个问题的人。谢谢。

标签: java multithreading cryptography aes


【解决方案1】:

问题是由用于从密码生成密钥的这一行引起的。

byte[] keybytes = md5(key);

使用 MessageDigest 类的 md5 函数很容易返回垃圾,这些垃圾随后被送入解密,最终失败。

【讨论】:

    【解决方案2】:
    public static String[] decrypt(String input, String key){
    
        String iv = input.substring(0, 24);
        String encrypted = input.substring(24);
        try {
            IvParameterSpec ips = new IvParameterSpec(Base64.decodeBase64(iv));
            byte[] keybytes = md5(key);//This isn't final. Don't worry ;)
            byte[] output = null;
            SecretKeySpec skey = new SecretKeySpec(keybytes, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); //Change here
            cipher.init(Cipher.DECRYPT_MODE, skey, ips);
            output = cipher.doFinal(Base64.decodeBase64(encrypted));
            if(output==null){
                throw new Exception();
            }
    
            return new String[]{new String(output),iv};
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    

    【讨论】:

    • 更改填充将如何影响稳定性?这个问题最终是我一直在使用的一个错误的 MD5 函数。
    猜你喜欢
    • 2015-08-31
    • 2011-10-03
    • 2011-12-16
    • 1970-01-01
    • 1970-01-01
    • 2018-09-18
    • 2018-06-16
    • 2018-04-24
    • 2015-01-26
    相关资源
    最近更新 更多