<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
</head>
<body>
<div dir="ltr">
<div></div>
<div>
<div>
<div dir="ltr">
<div dir="ltr">This look to me like a bug in the PKCS11 code or - if it is documented - in the application. Why do you think it is in JCE?</div>
<div dir="ltr"><br>
</div>
<div dir="ltr">Gruss</div>
<div dir="ltr">Bernd<span id="ms-outlook-ios-cursor"></span></div>
</div>
</div>
<div id="ms-outlook-mobile-signature">
<div><br>
</div>
<div><br>
</div>
<div style="direction:ltr">-- </div>
<div style="direction:ltr">http://bernd.eckenfels.net</div>
</div>
</div>
</div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>Von:</b> security-dev <security-dev-retn@mail.openjdk.org> im Auftrag von Lothar Kimmeringer <job@kimmeringer.de><br>
<b>Gesendet:</b> Wednesday, June 15, 2022 11:15:27 AM<br>
<b>An:</b> security-dev@openjdk.java.net <security-dev@openjdk.java.net><br>
<b>Betreff:</b> Private Keys are cached "forever" leading to inop HTTP-TLS-servers</font>
<div> </div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt;">
<div class="PlainText">Hi,<br>
<br>
I first thought this to be a bug in Jetty[1] but it seems to be within the JVM.<br>
<br>
I have the situation that the Jetty HTTP server is configured to use a<br>
cloud-based HSM as KeyStore for Private Keys. This works but if the<br>
connector stays idle for some time, subsequent handshakes fail with<br>
a ProviderException:<br>
<br>
2022-06-10 20:18:29,595 DEBUG [qtp705913731-178] SslConnection: DecryptedEndPoint@1b2a1192[{l=/127.0.0.1:4433,r=/127.0.0.1:3254,OPEN,fill=-,flush=-,to=24/180000}] stored flush exception<br>
java.security.ProviderException: Initialization failed<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.P11PSSSignature.initialize(P11PSSSignature.java:310)<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.P11PSSSignature.ensureInitialized(P11PSSSignature.java:216)<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.P11PSSSignature.engineUpdate(P11PSSSignature.java:507)<br>
        at java.base/java.security.Signature$Delegate.engineUpdate(Signature.java:1394)<br>
        at java.base/java.security.Signature.update(Signature.java:903)<br>
        at java.base/java.security.Signature.update(Signature.java:872)<br>
        at java.base/sun.security.ssl.ECDHServerKeyExchange$ECDHServerKeyExchangeMessage.updateSignature(ECDHServerKeyExchange.java:462)<br>
        at java.base/sun.security.ssl.ECDHServerKeyExchange$ECDHServerKeyExchangeMessage.<init>(ECDHServerKeyExchange.java:173)<br>
        at java.base/sun.security.ssl.ECDHServerKeyExchange$ECDHServerKeyExchangeProducer.produce(ECDHServerKeyExchange.java:488)<br>
        at java.base/sun.security.ssl.ClientHello$T12ClientHelloConsumer.consume(ClientHello.java:1091)<br>
        at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.onClientHello(ClientHello.java:843)<br>
        at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.consume(ClientHello.java:802)<br>
        at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)<br>
        at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)<br>
        at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1074)<br>
        at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1061)<br>
        at java.base/java.security.AccessController.doPrivileged(Native Method)<br>
        at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1008)<br>
        at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:629)<br>
[...]<br>
Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_USER_NOT_LOGGED_IN<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.wrapper.PKCS11.C_SignInit(Native Method)<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.P11PSSSignature.initialize(P11PSSSignature.java:303)<br>
        ... 34 common frames omitted<br>
<br>
Used Java: java -version<br>
openjdk version "11.0.14" 2022-01-18 LTS<br>
OpenJDK Runtime Environment Zulu11.54+23-CA (build 11.0.14+9-LTS)<br>
OpenJDK 64-Bit Server VM Zulu11.54+23-CA (build 11.0.14+9-LTS, mixed mode)<br>
<br>
I assume that the driver or the HSM-server is running into a timeout invalidating<br>
some session and thus make the key no longer usable. The KeyStore itself can<br>
still be used. If the same key is retrieved by "getKey" somewhere else the<br>
returned key doesn't get "invalidated", either. I've created a test class where<br>
this can be seen:<br>
<br>
------------------- snip<br>
import java.io.File;<br>
import java.io.FileWriter;<br>
import java.io.IOException;<br>
import java.io.UnsupportedEncodingException;<br>
import java.nio.charset.StandardCharsets;<br>
import java.security.AuthProvider;<br>
import java.security.GeneralSecurityException;<br>
import java.security.KeyStore;<br>
import java.security.PrivateKey;<br>
import java.security.Provider;<br>
import java.security.Security;<br>
import java.security.Signature;<br>
import java.util.Arrays;<br>
import java.util.Date;<br>
<br>
import javax.security.auth.callback.Callback;<br>
import javax.security.auth.callback.CallbackHandler;<br>
import javax.security.auth.callback.PasswordCallback;<br>
import javax.security.auth.callback.UnsupportedCallbackException;<br>
import javax.security.auth.login.LoginException;<br>
<br>
import com.ebd.util.text.TextTools;<br>
<br>
public class HSMAccessTest {<br>
     <br>
     public final static void main(String[] args) throws Exception {<br>
         String config =<br>
                 "name = TestUtimaco\r\n" +<br>
                 "library = D:/Program Files/Utimaco/CryptoServer/Lib/cs_pkcs11_R2.dll\r\n" +<br>
                 "";<br>
         <br>
         File tempConfig = File.createTempFile("hsmconfig_", ".properties");<br>
         try {<br>
             try (FileWriter fw = new FileWriter(tempConfig, StandardCharsets.ISO_8859_1)) {<br>
                 fw.write(config);<br>
                 fw.flush();<br>
             }<br>
             <br>
             Provider p = Security.getProvider("SunPKCS11");<br>
             AuthProvider prov = (AuthProvider) p.configure(tempConfig.getAbsolutePath());<br>
             KeyStore ks = KeyStore.getInstance("PKCS11", prov);<br>
             char[] pswd = "654321".toCharArray();<br>
             ks.load(null, pswd);<br>
             String certAlias = ks.aliases().nextElement();<br>
             System.out.println(certAlias);<br>
             <br>
             PrivateKey key = (PrivateKey) ks.getKey(certAlias, null);<br>
             for (long wait = 16 * 60000; wait < 3600000; ) {<br>
//                performWorkaround(prov, pswd);<br>
                 performSign(prov, key);<br>
                 System.out.println("wait " + (wait / 60000) + " mins");<br>
                 Thread.sleep(wait);<br>
             }<br>
         }<br>
         finally {<br>
             tempConfig.delete();<br>
         }<br>
         <br>
     }<br>
<br>
     static void performSign(AuthProvider prov, PrivateKey key) throws GeneralSecurityException, UnsupportedEncodingException {<br>
         System.out.println(new Date() + ": Sign data. Key is destroyed: " + key.isDestroyed());<br>
         Signature sig = Signature.getInstance("SHA1WithRSA", prov);<br>
         sig.initSign(key);<br>
         sig.update("signdata".getBytes("8859_1"));<br>
         <br>
         System.out.println(new Date() + ": " + TextTools.join(sig.sign(), ""));<br>
     }<br>
<br>
     static void performWorkaround(AuthProvider prov, char[] pswd) throws LoginException {<br>
         prov.login(null, new CallbackHandler() {<br>
             @Override<br>
             public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {<br>
                 Arrays.stream(callbacks)<br>
                     .filter(cb -> cb instanceof PasswordCallback)<br>
                     .map(cb -> (PasswordCallback) cb)<br>
                     .forEach(cb -> cb.setPassword(pswd));<br>
             }<br>
         });<br>
     }<br>
}<br>
------------------- snip<br>
<br>
The class in this version will run into an error after 16 minutes (32 minutes at the latest)<br>
<br>
------------------- snip<br>
testcert<br>
Mon Jun 13 17:01:33 CEST 2022: Sign data. Key is destroyed: false<br>
Mon Jun 13 17:01:33 CEST 2022: a8726a4ed3c3f77aced24b078931167214a7f64b1dac2e7774874aec7ec973d289bf72920b342b52c6b799ebee793332332f531994f1fd9ec77a986ac253e54771d410acc4c3dc71ca97b8c5ac445262b9da1177db0eaedb66b9a0af5dfc2f5c761ab514dde5ab90afc53aa7bcd54edfb93f247855794c127e6cff86562652ab5a3493c7e49756cc0d309495b5d365bbfc8eeeb30d46e4419727421c4eec5011560fe17fa6894ea967280470ec62366d56839ece086502471bb537a27a708a4654df2ba0a0234bd20f0d80519843e7ed4ca5beb76af5d66e886977a07092e20e73478c4bbdf1742475092b79b53c9a202d072a70ee112ef405138df7c1368b16<br>
wait 16 mins<br>
Mon Jun 13 17:17:33 CEST 2022: Sign data. Key is destroyed: false<br>
Mon Jun 13 17:17:33 CEST 2022: a8726a4ed3c3f77aced24b078931167214a7f64b1dac2e7774874aec7ec973d289bf72920b342b52c6b799ebee793332332f531994f1fd9ec77a986ac253e54771d410acc4c3dc71ca97b8c5ac445262b9da1177db0eaedb66b9a0af5dfc2f5c761ab514dde5ab90afc53aa7bcd54edfb93f247855794c127e6cff86562652ab5a3493c7e49756cc0d309495b5d365bbfc8eeeb30d46e4419727421c4eec5011560fe17fa6894ea967280470ec62366d56839ece086502471bb537a27a708a4654df2ba0a0234bd20f0d80519843e7ed4ca5beb76af5d66e886977a07092e20e73478c4bbdf1742475092b79b53c9a202d072a70ee112ef405138df7c1368b16<br>
wait 16 mins<br>
Mon Jun 13 17:33:33 CEST 2022: Sign data. Key is destroyed: false<br>
Exception in thread "main" java.security.ProviderException: Initialization failed<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.P11Signature.initialize(P11Signature.java:375)<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.P11Signature.engineInitSign(P11Signature.java:502)<br>
        at java.base/java.security.Signature$Delegate.engineInitSign(Signature.java:1351)<br>
        at java.base/java.security.Signature.initSign(Signature.java:636)<br>
        at HSMAccessTest.performSign(HSMAccessTest.java:76)<br>
        at HSMAccessTest.main(HSMAccessTest.java:50)<br>
Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_USER_NOT_LOGGED_IN<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.wrapper.PKCS11.C_SignInit(Native Method)<br>
        at jdk.crypto.cryptoki/sun.security.pkcs11.P11Signature.initialize(P11Signature.java:366)<br>
        ... 5 more<br>
------------------- snip<br>
<br>
Putting the getKey-call inside the for-loop solves the problem. A workaround is<br>
regularily calling authprovider.login. This can be seen if the commented-line<br>
in the for-loop is activated.<br>
<br>
Not sure if this should be considered a bug in the PKCS#11-DLL of the HSM-provider<br>
but if a key-instance must be valid forever, shouldn't that be mentioned in the<br>
interface's Javadoc?<br>
<br>
<br>
Thanks and best regards,<br>
<br>
Lothar Kimmeringer<br>
<br>
[1] <a href="https://github.com/eclipse/jetty.project/issues/8157">https://github.com/eclipse/jetty.project/issues/8157</a><br>
</div>
</span></font></div>
</body>
</html>