momosecurity/rhizobia_J

RSAUtils代码存在问题

LeadroyaL opened this issue · 4 comments

src/main/java/com/immomo/rhizobia/rhizobia_J/crypto/RSAUtils.java

你好,在这个文件里出现了两处密码学的误用,如下:

第一处:

    public String keyAlgorithm = "RSA"; 

RSA加解密时候

  • 不使用Padding,在密文短的时候是可以攻击的(理论上);
  • 不使用分组模式,在密文较长(超过N 的长度)的时候是无法被加密的;

第二处:

    /**
     * @Description: 签名(私钥加密)
     * @Param: oriData 待签名数据
     * @return: byte[] 数字签名
     */
    public byte[] sign(String oriData) throws Exception {
        byte[] data = oriData.getBytes();
        // 对数据加密
        KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] encrypted = cipher.doFinal(data);
        return encrypted;
    }


    /**
     * @Description: 验签(公钥解密)
     * @Param: sign 签名
     * @return: String 待校验数据
     */
    public String verify(byte[] sign) throws Exception {
        // 对数据解密
        KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] original = cipher.doFinal(sign);
        String originalString = new String(original);
        return originalString;
    }

加签是对整个数据完整性的保证,输出一个长度较短的摘要,代码里的sign函数是用私钥把原文完全加密一遍,生成比明文还要长的数据,完全不是加签。

验签同样,按理说要返回true/false表示是否成功,这里返回明文,和我理解的不大一样。

建议把这个文件重写,不要误导后人。

@LeadroyaL ,非常感谢你的宝贵意见! づ ̄ 3 ̄)づ
第一处问题我会尽快想办法解决,如已有正确方案或可参考方案,还请直接把代码贴出来,不要小气哟
第二处问题,我会按照true/false返回值,再重载一个函数,不知可否接受

第一处:
https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
RSA/ECB/PKCS1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
选取其中的一种即可,我自己实现时选的"RSA/ECB/OAEPWithSHA-1AndMGF1Padding"

第二处我的想法是在加签/验签函数里,主动把明文哈希,对该哈希进行加密/解密,这样包装起来更好用一点。
因为业务开发一般不懂密码学的使用,我看到很多的封装基本都是这个样子:“把明文传进来、返回一个简短的字符串,更加友好”;而不是将哈希传进来、进行加密。加签和加密是不一样的。

@LeadroyaL 二处问题已修改。第二处没有封装哈希,在README中有提示用户可自行选用摘要算法;“返回一个简短的字符串”,排除使用压缩,返回的签名字符串大小是由RSA密钥的长度决定的,所以如果要返回简短的字符串,密钥长度应选择较短的。
如果仍有问题,还恳请继续指出

@LeadroyaL ,最近更新了如下三点内容:
1、源码所用的signature类中,已经封装了摘要算法,所以可以不必再生成摘要
2、RSA加解密时,明文是有长度限制的,明文字符串限制长度 = 密钥长度(byte) - padding占用大小(byte)
padding大小如下:
RSA/ECB/PKCS1Padding or RSA : 11
RSA/ECB/OAEPWithSHA-1AndMGF1Padding : 42
RSA/ECB/OAEPWithSHA-256AndMGF1Padding : 66
于是,新增加了encryptWithouLimit/edecryptWithoutLimit方法,摆脱明文长度的限制
3、新增加了ECDSA加验签方法,比RSA更快、签名更短等等,可参考如下:
https://blog.cloudflare.com/ecdsa-the-digital-signature-algorithm-of-a-better-internet/

如有疑问恳请继续指出,如无疑问该issue将于本周关闭,也可通过邮件联系projectone@immomo.com