bc-java
bc-java copied to clipboard
Microsoft GovCloud fails HostnameUtil.checkHostname
Code:
`import java.net.URL;
import java.io.InputStream;
public class BCExample {
public static void main(String[] args){
if (args.length != 1) {
System.out.println("Usage: "+BCExample.class.getName()+" <URL> ");
System.out.println("Example: java -Djdk.tls.trustNameService=true BCExample https://login.microsoftonline.us");
System.exit(1);
}
try{
InputStream response =new URL(args[0]).openStream();
}
catch(Exception e){
System.out.println(e);
}
}
} `
Problem Using BC we found the following situation when we try to go against https://login.microsoftonline.us
org.bouncycastle.tls.TlsFatalAlert: certificate_unknown(46) at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.checkServerTrusted(ProvSSLSocketDirect.java:135) at org.bouncycastle.jsse.provider.ProvTlsClient$1.notifyServerCertificate(ProvTlsClient.java:360) at org.bouncycastle.tls.TlsUtils.processServerCertificate(TlsUtils.java:4889) at org.bouncycastle.tls.TlsClientProtocol.handleServerCertificate(TlsClientProtocol.java:788) at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(TlsClientProtocol.java:671) at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(TlsProtocol.java:708) at org.bouncycastle.tls.TlsProtocol.processRecord(TlsProtocol.java:584) at org.bouncycastle.tls.RecordStream.readRecord(RecordStream.java:245) at org.bouncycastle.tls.TlsProtocol.safeReadRecord(TlsProtocol.java:856) at org.bouncycastle.tls.TlsProtocol.blockForHandshake(TlsProtocol.java:417) at org.bouncycastle.tls.TlsClientProtocol.connect(TlsClientProtocol.java:88) at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(ProvSSLSocketDirect.java:445) at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(ProvSSLSocketDirect.java:426) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:197) at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1570) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:268) at java.net.URL.openStream(URL.java:1068) at BCExample.main(BCExample.java:14) Caused by: java.security.cert.CertificateException: No subject alternative name found matching domain name login.microsoftonline.us at org.bouncycastle.jsse.provider.HostnameUtil.checkHostname(HostnameUtil.java:97) at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkEndpointID(ProvX509TrustManager.java:334) at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkEndpointID(ProvX509TrustManager.java:453) at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkExtendedTrust(ProvX509TrustManager.java:362) at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkTrusted(ProvX509TrustManager.java:275) at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkServerTrusted(ProvX509TrustManager.java:182) at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.checkServerTrusted(ProvSSLSocketDirect.java:131) ... 19 more
This works if we try it with https://login.microsoftonline.com and we found that BC always try to match hostname in the URL with 2nd entry in "SubjectAlternativeName/DNSName" in the certificate.
-
SubjectAlternativeName [ DNSName: login.microsoftonline.us DNSName: certauth.login.microsoftonline.us DNSName: device.login.microsoftonline.us DNSName: logincert.microsoftonline.us ]
-
login.microsoftonline.com SubjectAlternativeName [ DNSName: stamp2.login.microsoftonline.com DNSName: login.microsoftonline-int.com DNSName: login.microsoftonline-p.com DNSName: login.microsoftonline.com DNSName: login2.microsoftonline-int.com DNSName: login2.microsoftonline.com DNSName: loginex.microsoftonline-int.com DNSName: loginex.microsoftonline.com DNSName: stamp2.login.microsoftonline-int.com ]
How we can see the 2nd entry in https://login.microsoftonline.us/ has certauth and it doesn’t match, but in https://login.microsoftonline.com/ the 2nd entry has login and in this case works.
Step to reproduce
- Generate the app with the code shared
- Configure BC in your environment
- Run the following command /DIRECTORY-OF-YOUR-APP/java -Djdk.tls.trustNameService=true BCExample https://login.microsoftonline.us
From v1.70, use -Dorg.bouncycastle.jsse.client.assumeOriginalHostName=true instead of -Djdk.tls.trustNameService=true. The new property additionally sets the SNI hostname, and is specific to BCJSSE, which is probably desirable. If stuck on an earlier version you would probably have to use e.g. Apache HttpClient instead, which doesn't suffer from the HttpsURLConnection bug relating to hostname setting on third-party JSSE providers (https://github.com/bcgit/bc-java/issues/660).
The SNI hostname makes the difference in this case because https://login.microsoftonline.us is apparently a virtual server that relies on the SNI information to choose its certificate. There follow some redirects ending up on an azure portal, which complicates things a bit, but with the new property it all works smoothly.
Appears dealt with.
Is there someplace in the jdk/jre directory where I can set: org.bouncycastle.jsse.client.assumeOriginalHostName=true and have all java apps pick that up? Adding the setting to java.security does not seem to work. This might be because that file is loaded after the bc jar files? Ah! Adding to JAVA_TOOL_OPTIONS in my Dockerfile ENV seems to work.
Glad to hear it. Yes, at the moment this one is only settable as a system property - it seems better to make it case by case.
Later java versions use SNI by default, right? Shouldn't bc-java do the same? ie: make this the default? at least on older java releases like java 7.
The issue is that HttpsURLConnection does not give BCJSSE the original hostname (SunJSSE is granted special access in two separate ways). The hostname we can access might not be the original; it might be a DNS lookup result. Therefore we cannot simply use it without the user confirming they know what they are doing.