Java - 获取密钥失败:java.security.InvalidKeyException:无效的RSA私钥和DerInputStream.getLength():找到冗余长度字节

时间:2017-10-27 16:14:34

标签: java xml digital-signature keytool pfx

我在使用PFX文件进行XML数字签名时遇到了一些麻烦。运行此代码时出现异常:

KeyStore ks = KeyStore.getInstance("PKCS12");

fs = new FileInputStream(file);

ks.load(fs, "password".toCharArray());

// this line!
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("alias", new KeyStore.PasswordProtection("password".toCharArray()));

这是一个例外:

java.security.UnrecoverableKeyException: Get Key failed: 
java.security.InvalidKeyException: Invalid RSA private key
  at sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:435)
  at sun.security.pkcs12.PKCS12KeyStore.engineGetEntry(PKCS12KeyStore.java:1306)
  at java.security.KeyStore.getEntry(KeyStore.java:1521)
  at app.ubl.xml.GenerateSignature.makeSignatureXML(GenerateSignature.java:88)

Caused by: java.io.IOException: DerInputStream.getLength(): Redundant length bytes found
  at sun.security.util.DerInputStream.getLength(DerInputStream.java:606)
  at sun.security.util.DerInputStream.getLength(DerInputStream.java:569)
  at sun.security.util.DerInputStream.getPositiveBigInteger(DerInputStream.java:220)
  at sun.security.rsa.RSAPrivateCrtKeyImpl.parseKeyBits(RSAPrivateCrtKeyImpl.java:205)

真正的问题是代码在java 1.8.0_111中工作,但错误显示的任何更高版本。

运行keytool 时出现

异常

我发现的另一个问题是,当我使用keytool运行此命令时:

keytool -list -keystore file.pfx -storepass password -storetype PKCS12 -v

显示PFX文件的详细信息,然后它再次仅适用于java 1.8.0_111。否则我得到这个例外:

java.util.IllegalFormatConversionException: d != java.lang.String
    at java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4302)
    at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2793)
    at java.util.Formatter$FormatSpecifier.print(Formatter.java:2747)
    at java.util.Formatter.format(Formatter.java:2520)
    at java.util.Formatter.format(Formatter.java:2455)
    at java.lang.String.format(String.java:2940)
    at sun.security.tools.keytool.Main.withWeak(Main.java:3076)
    at sun.security.tools.keytool.Main.printX509Cert(Main.java:3125)
    at sun.security.tools.keytool.Main.doPrintEntry(Main.java:1924)
    at sun.security.tools.keytool.Main.doPrintEntries(Main.java:2236)
    at sun.security.tools.keytool.Main.doCommands(Main.java:1123)
    at sun.security.tools.keytool.Main.run(Main.java:366)
    at sun.security.tools.keytool.Main.main(Main.java:359)

我不知道这是否相关,但这就是我得到的。

有什么想法吗?

PD:抱歉我的英语不好。

3 个答案:

答案 0 :(得分:7)

8u121中的更严格的DER检查

这是Java 8u111之后的Java版本中引入的更改。 next version was Java 8u121

因此,不再接受基础ASN.1格式的某些编码。

8u121 release notes中提到了这种变化:

  

安全库
  更多检查添加到DER编码解析代码
  DER编码解析代码中添加了更多检查以捕获各种编码错误。此外,包含构造的无限长度编码的签名现在将在解析期间导致IOException。请注意,使用JDK默认提供程序生成的签名不受此更改的影响   JDK-8168714(非公开)

原因:CVE

此特定更改是由于漏洞:CVE-2016-5546

相关的源代码更改

您看到的错误消息(Redundant length bytes found)是作为this changeset部分引入的。总结如下:

8168714: Tighten ECDSA validation
Summary: Added additional checks to DER parsing code

不幸的是截至目前(2018年)变更集"Bug 8168714" is STILL marked private中提到的错误。 (如果没有登录,你将无法查看它。)

但是, 公开的另一个错误:OpenJDK: JDK-8175251: Failed to load RSA private key from pkcs12。 (此错误的完整版本也是on java.com。)

这个bug链接到上面提到的源代码变更集。

解决方法:运行OpenSSL洗衣机清理P12文件

某些P12文件中的编码似乎以某种方式被破坏了。解决方法是使用OpenSSL将P12的内容打包到PEM,然后将它们重新打包成新的P12文件。 (真正的解决方案是修复/更新生成P12文件的软件。)

以下命令为mentioned in the Java Bug ticket

  

Openssl能够在提取私钥时删除冗余0。我们可以使用以下2个命令来规范化受影响的pkcs12文件:

     
      
  1. openssl pkcs12 -in pkcs12-file -out key-and-cert -nodes -passin pass:abcXYZ

  2.   
  3. openssl pkcs12 -in key-and-cert -export -out new-pkcs12-file -passout pass:abcXYZ

  4.   

进一步阅读:Bouncy Castle邮件列表

更新2018-02-12Mon 我刚才意识到Bouncy Castle邮件列表中讨论的问题涉及一种不同类型的编码错误。即:在ASN.1中,您有T-L-V(标签长度值)编码。

但这两个问题都与0x00不必要(因而非法)填充有关。

这一变化在Java" Bouncy Castle"的邮件列表中进行了讨论。加密库:

相关问题

答案 1 :(得分:0)

要解决此问题,您可以使用Bouncycastle的ASN1内容重新编码签名字节。

            BigInteger r, s;
            // This works because bouncycastle isn't picky like the updated JCE is.
            try(ASN1InputStream is = new ASN1InputStream(origSigBytes)) {
                    ASN1Sequence seq = (ASN1Sequence)is.readObject();
                    ASN1Integer r_int = (ASN1Integer)seq.getObjectAt(0);
                    ASN1Integer s_int = (ASN1Integer)seq.getObjectAt(1);
                    r = r_int.getValue();
                    s = s_int.getValue();
            }
            DERSequence out = new DERSequence(new ASN1Encodable[] {new DERInteger(r), new DERInteger(s)});
            byte[] sigBytes = out.getEncoded();

如果您在签名为byte []且即将调用validate()的上下文中,则此方法有效。

答案 2 :(得分:0)

BouncyCastleProvider提供者添加到java.security.Security

        Security.addProvider(new BouncyCastleProvider());

它将起作用