英文:
How to selectively use approved-only mode with BouncyCastle JSSE provider + FIPS provider?
问题
我正在尝试在运行Java 8的一小段示例代码中,遵循bc-fips-1.0.2.jar用户指南中的建议,Legion of the Bouncy Castle Inc. BC-FJA 1.0.2(Bouncy Castle FIPS Java API)用户指南日期:09/14/19:
> 注意:从Java 1.9开始,对FIPS模式的支持似乎已经开始消失,已经确认在Java 11中它是一个空操作。对于Java 11或更高版本,如果需要FIPS支持,我们建议使用BCJSSE提供程序。
(第11页的脚注)。我正在在一个“混合”环境中进行此操作,因此大多数线程在默认模式下运行,而一些线程在FIPS批准的模式下运行。
但是,似乎不起作用:似乎有一个“批准”的线程试图重用由一个“未批准”的线程创建的实例。
具体来说,当以编程方式启用bc-fips-1.0.2.jar的BCFIPS和bctls-fips-1.0.10.jar的BCJSSE,并在标准未修改的OpenJDK 8(在我的情况下是Corretto 8u232)上以编程方式移除内置的SunJSSE提供程序,然后在一个新线程中以未批准模式进行HTTPS连接,然后在另一个新线程中以批准模式进行HTTPS连接时,会导致异常:
Caused by: org.bouncycastle.crypto.fips.FipsUnapprovedOperationError: Attempt to use unapproved implementation in approved thread: SHA-512
at org.bouncycastle.crypto.internal.io.Utils.approvedModeCheck(Unknown Source)
at org.bouncycastle.crypto.internal.io.DigestOutputStream.write(Unknown Source)
at org.bouncycastle.crypto.UpdateOutputStream.update(Unknown Source)
at org.bouncycastle.jcajce.provider.BaseMessageDigest.engineUpdate(Unknown Source)
at java.security.MessageDigest.update(MessageDigest.java:335)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider$NonceEntropySource$NonceEntropySourceSpi.runDigest(Unknown Source)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider$NonceEntropySource$NonceEntropySourceSpi.engineNextBytes(Unknown Source)
at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaNonceGenerator.<init>(Unknown Source)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.createNonceGenerator(Unknown Source)
at org.bouncycastle.tls.AbstractTlsContext.createNonceGenerator(Unknown Source)
at org.bouncycastle.tls.AbstractTlsContext.<init>(Unknown Source)
at org.bouncycastle.tls.TlsClientContextImpl.<init>(Unknown Source)
[etc]
我尝试的完整复制场景在https://gist.github.com/marnix/834610d0fb92e53ce507edfce96bacb9中提供,有关详细信息,请参阅类的javadoc。
所以,我的问题是:如何使HTTPS连接工作,其中一些在默认模式下运行,而其他一些在FIPS批准的模式下运行,使用BCJSSE而不是SunJSSE?
或者,这在Java 8上不起作用,我必须等到Java 9+才能开始工作吗?(为什么?)
(我的最终目标不是HTTPS连接,而是其他一些事情,其中大多数将在默认模式下运行,而一些线程将在批准的模式下运行。)
英文:
In a small piece of sample code running on Java 8, I'm trying to follow the advice from the bc-fips-1.0.2.jar user guide, Legion of the Bouncy Castle Inc. BC-FJA 1.0.2 (Bouncy Castle FIPS Java API) User Guide Date: 09/14/19:
> Note: support for FIPS mode appears to have started disappearing with
> Java 1.9, it has been confirmed that it is a null operation as of Java
> 11. For Java 11, or later, we recommend using the BCJSSE provider if you need FIPS support.
(footnote on page 11). And I'm doing this in a 'mixed' context, so most threads run in default mode, and some run in FIPS approved-only mode.
However, that doesn't seem to work: It seems an 'approved' thread is trying to reuse and instance created by an 'unapproved' thread.
Specifically, when programmatically enabling bc-fips-1.0.2.jar's BCFIPS and bctls-fips-1.0.10.jar's BCJSSE, on a standard unmodified OpenJDK 8 (Corretto 8u232 in my case), and programmatically removing the built-in SunJSSE provider, then doing an HTTPS connection on a new thread in unapproved mode, then on another new thread in approved mode, results in an exception:
Caused by: org.bouncycastle.crypto.fips.FipsUnapprovedOperationError: Attempt to use unapproved implementation in approved thread: SHA-512
at org.bouncycastle.crypto.internal.io.Utils.approvedModeCheck(Unknown Source)
at org.bouncycastle.crypto.internal.io.DigestOutputStream.write(Unknown Source)
at org.bouncycastle.crypto.UpdateOutputStream.update(Unknown Source)
at org.bouncycastle.jcajce.provider.BaseMessageDigest.engineUpdate(Unknown Source)
at java.security.MessageDigest.update(MessageDigest.java:335)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider$NonceEntropySource$NonceEntropySourceSpi.runDigest(Unknown Source)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider$NonceEntropySource$NonceEntropySourceSpi.engineNextBytes(Unknown Source)
at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaNonceGenerator.<init>(Unknown Source)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.createNonceGenerator(Unknown Source)
at org.bouncycastle.tls.AbstractTlsContext.createNonceGenerator(Unknown Source)
at org.bouncycastle.tls.AbstractTlsContext.<init>(Unknown Source)
at org.bouncycastle.tls.TlsClientContextImpl.<init>(Unknown Source)
[etc]
Full reproduction scenario of my attempts is at https://gist.github.com/marnix/834610d0fb92e53ce507edfce96bacb9, see the class's javadoc for details.
So my question is: How do I make HTTPS connections work, where some are in the default mode and others are in FIPS approved-only mode, using BCJSSE instead of SunJSSE?
Or will this not work on Java 8, and do I have to wait for Java 9+ for this to start working? (Why?)
(My ultimate goal is not for HTTPS connections but for other things, where most will run in the default mode, and some threads will run in approved-only mode.)
答案1
得分: 4
使用单独的SSLContext实例是正确的方法,但在这里使用SSLContext.setDefault是非常不正确的。部分原因是一旦删除线程加入,将会出现设置器之间的竞争条件。更重要的是,HttpsURLConnection只设置其默认的SSLSocketFactory 一次,并且最多调用一次SSLContext.getDefault来设置它,因此在此之后调用SSLContext.setDefault将不会影响HttpsURLConnection,并且将会导致原始SSLContext和FIPS错误的共享。
删除:
SSLContext.setDefault(context);
并替换为:
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(context.getSocketFactory());
英文:
Using separate SSLContext instances is the right approach, but using SSLContext.setDefault is very wrong here. Partly because once you remove the thread joins, there will be race conditions b/w the setters. More importantly though, HttpsURLConnection only sets its default SSLSocketFactory once, and will call SSLContext.getDefault at most once to set it, so calling SSLContext.setDefault after that will have no effect on HttpsURLConnection, and you will get sharing of the original SSLContext and FIPS errors.
Remove:
SSLContext.setDefault(context);
and replace:
url.openConnection()
with:
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(context.getSocketFactory());
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论