算法HmacPBESHA256不可用。

25

所以,我编写了一些代码,用于通过Bouncycastle将PEM添加到PKCS密钥库,然后使用Java加密将PKCS密钥库值导入JKS密钥库。

我发誓昨天我执行了这些步骤并成功地通过了单元测试,但今天早上我开始遇到了这个问题。

Caused by: java.security.NoSuchAlgorithmException: Algorithm HmacPBESHA256 not available
    at javax.crypto.Mac.getInstance(Mac.java:181) ~[na:1.8.0_60]
    at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2039) ~[na:1.8.0_65]

现在,底层可能发生了某些变化,但我无法找出是什么。看起来我曾经用于该算法的任何提供者都已经消失了。

这是我的java.security文件摘录:

security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
security.provider.3=sun.security.ec.SunEC
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=com.sun.security.sasl.Provider
security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.9=sun.security.smartcardio.SunPCSC
security.provider.10=apple.security.AppleProvider

代码并不复杂。首先,我通过bouncycastle创建PKCS密钥库,添加pem并保存为PKCS12格式。然后通过Java加密导入,又将其保存为JKS格式。

   public KeystoreBuilder createJksFromPem(String pemPrivate, String pemPublic, String alias) throws Exception
    {
        Preconditions.checkState(StringUtils.isNotEmpty(pemPrivate), "pemPrivate must not be empty");
        Preconditions.checkState(StringUtils.isNotEmpty(pemPublic), "pemPublic must not be empty");
        Preconditions.checkState(StringUtils.isNotEmpty(alias), "alias must not be empty");

        String pkcsFilename = filename + ".pkcs";
        convertPemToPkcs(pemPrivate, pemPublic, pkcsFilename);

        importPkcsIntoJks(pkcsFilename);

        return this;
    }

    private void importPkcsIntoJks(String pkcsFilename) throws Exception
    {
        KeyStore pkcs = KeyStore.getInstance("PKCS12");
        File pkcsFile = new File(pkcsFilename);
        try (FileInputStream fis = new FileInputStream(pkcsFile))
        {
            pkcs.load(fis, password.toCharArray());
        }
        pkcsFile.delete();

        KeyStore jks = KeyStore.getInstance("JKS");
        jks.load(null);

        Enumeration<String> aliases = pkcs.aliases();
        while (aliases.hasMoreElements())
        {
            String alias = aliases.nextElement();
            if (!pkcs.isKeyEntry(alias))
            {
                continue;
            }
            Key key = pkcs.getKey(alias, password.toCharArray());
            Certificate[] chain = pkcs.getCertificateChain(alias);

            jks.setKeyEntry(alias, key, password.toCharArray(), chain);
        }

        persist(jks);
    }

    private void convertPemToPkcs(String pemPrivate, String pemPublic, String pkcsFilename) throws IOException, NoSuchAlgorithmException, OperatorCreationException, PKCSException, FileNotFoundException
    {
        Security.addProvider(new BouncyCastleProvider());

        X509CertificateHolder cert = (X509CertificateHolder) readObject(pemPublic);
        PEMKeyPair keyPair = (PEMKeyPair) readObject(pemPrivate);

        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
        PKCS12SafeBagBuilder pkcs12BagBuilder = new PKCS12SafeBagBuilder(cert);
        pkcs12BagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Kafka SSL Certificate"));
        pkcs12BagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, extUtils.createSubjectKeyIdentifier(keyPair.getPublicKeyInfo()));

        PKCS12PfxPduBuilder builder = new PKCS12PfxPduBuilder();

        builder.addData(pkcs12BagBuilder.build());

        builder.addEncryptedData(new JcePKCSPBEOutputEncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC).setProvider("BC").build(password.toCharArray()), pkcs12BagBuilder.build());

        PKCS12PfxPdu pfx = builder.build(new JcePKCS12MacCalculatorBuilder(NISTObjectIdentifiers.id_sha256), password.toCharArray());

        try (FileOutputStream fos = new FileOutputStream(new File(pkcsFilename)))
        {
            fos.write(pfx.getEncoded(ASN1Encoding.DL));
        }
    }

然后它突然爆炸了

pkcs.load(fis, password.toCharArray());

正如您所看到的,BouncyCastleProvider是显式添加的。有任何建议吗?

更新:感谢dave_thompson_085的建议。我简直不敢相信我没有看到那个重载方法,但解决方案是在调用getKeystore.getInstance("PKCS12", "BC")中指定提供程序。


通过互联网搜索显示,这个错误可能与之相关。此外,我没有看到Bouncy Castle提供程序的任何地方,也许那会有所帮助? - Maarten Bodewes
编辑一些代码以重现问题会很有帮助。 - Maarten Bodewes
1
添加BC提供程序并不会使其具有优先级;您的堆栈跟踪证实您正在获取SunJSSE提供程序的实现。尝试使用KeyStore.getInstance("PKCS12","BC"),或者如果您想要冒险,在位置4或以下插入BC提供程序。或者,像其他人一样,在PKCS12上使用SHA1进行PB-MAC。而且在我看来,您正在强烈加密证书--为什么? - dave_thompson_085
啊啊啊啊,问题解决了,谢谢dave_thompson_085!! - Adam Morgan
@dave_thompson_085,你具体是指哪段代码需要“强加密”?是 'addEncryptedData()' 函数调用吗? - Adam Morgan
显示剩余2条评论
5个回答

20

打开Android Studio并转到文件 > 设置 > 构建、执行、开发 > 构建工具 > Gradle,然后将Gradle JDK更改为jdk16,然后单击“同步Gradle”。

这样就能在没有任何错误的情况下构建项目。


在Android Studio中使用JDK 15可以解决这个问题。 JDK 16会对某些依赖项抛出错误。 - Vidyesh Churi
我正在使用JDK16解决这个问题。我的React应用程序现在可以运行了。 - Damian Jimenez

6
HmacPBESHA256听起来像是解密文件后验证完整性的算法。您可以解密您的密钥库并重新加密,这可能不使用HmacPBESHA256
在我的情况下,我使用Windows工具(即“Microsoft Strong Cryptographic Provider”)生成密钥。默认情况下,pfx使用HmacPBESHA256,而Java 11不支持该算法。
解决方案是创建一个新的密钥库,但使用openssl。为了做到这一点,我运行:
openssl pkcs12 -in "your_keystore.pfx" -nodes | openssl pkcs12 -export -out "new_keystore.pfx"

通过将new_keystore.pfx作为密钥库提供,可以在jarsign/apksign上运行该程序。


1
非常感谢您的修复。请注意,在我的情况下,它清除了我的“alias”值(将其更改为“1”),我必须使用以下步骤重新应用别名:https://dev59.com/WHA75IYBdhLWcg3wFE2b#5707518。奇怪的是,使用Java 11.0.4作为基线测试有问题的文件时,会显示HmacPBESHA256错误,但当我切换到11.0.16时,错误消失了。出于兼容性原因,我仍然采取了上述openssl步骤,并且现在该文件可以在两个版本中使用。 - tresf

5

这个问题可能是由于较旧的OpenJDK Java版本引起的。(Keytool)
在我的情况下,这个版本解决了问题:openjdk version "11.0.17" 2022-10-18


我也遇到了11.0.17的相同问题(来自2022年10月)。但是使用corretto 11.0.13_8解决了我的问题(来自2021年10月)。 - Maik
我也遇到过这个问题。对我来说,使用jdk 17修复了。 - undefined
11.0.20对我来说解决了问题。 在Android Studio中显示的版本是11.0.20,所以我使用了这个版本。 - undefined
谢谢。我迁移到了OpenJDK 17,现在它可以正常工作了。 - undefined

4
正如dave_thompson_085指出的那样,我可以指定我想要用于密钥库的提供者。最初我没有意识到这一点,因为我错过了getInstance()的重载方法。
所以总结一下,调用以下语句解决了我的问题:
KeyStore.getInstance("PKCS12","BC")
太容易了。

4

另外,标准的Java(SunJCE提供者)支持HmacPBESHA256算法,从Java 12开始。我会像这样使用它:

//run with Java 12!
public void test() throws NoSuchAlgorithmException {
    Provider provider = java.security.Security.getProvider("SunJCE");
    System.out.println(provider.getName());
    Mac m = Mac.getInstance("HmacPBESHA256",provider);
    System.out.println(m.getAlgorithm());
}

我正在使用Java 16版本,但仍然遇到错误。 - ThinkAndCode
但是如果您查看文档,它是可用的。您是否正确设置了提供程序? - Lonzak
是的,那里提到了。但是这个提供者是什么,应该在哪里设置呢? - ThinkAndCode

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接