I need to create a web service client with a keystore. But this is the error:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
My code:
private SSLSocketFactory getFactory() throws Exception {
File pKeyFile = new ClassPathResource("jks/dex-client-issuer-wss.jks").getFile();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX");
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pass.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pass.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
return context.getSocketFactory();
}
this is the http conection:
URL url = new URL("https://dexxis-demo1.gemalto.com/DEX/communication/issuer-interface");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(getFactory());
I only have a dex-client-issuer-wss.jks file which works with soap ui, how can I create a connection with this certificate file?
'unable to find valid certification path' (during SSL/TLS handshake) has nothing to do with your keystore -- it means that the server cert does not validate against your truststore, which since you passed null as the 2nd arg to context.init is the JRE's default, which is the file specified by sysprop javax.net.ssl.trustStore if set otherwise JRE/lib/security/cacerts (or .../jssecacerts if it exists).
That server is using a cert issued under the root cert Gemalto Business Root Certificate Authority which is not included in the cacerts supplied with Oracle Java packages, or any other established truststores I know, and is not known by the public transparency logs (per https://crt.sh). SoapUI, presumably because it is designed as a development, test and debug tool, ignores invalid or suspect certs, but a browser or curl or wget on that URL should fail unless the truststore(s) on your system(s) have been modified or are otherwise unusual.
OTOH openssl s_client which is also primarily a debug tool reports an error but makes the connection anyway.
If you do want to trust that CA, or one of its (two) intermediates, or the site anyway, you need to have the appropriate cert in your truststore. The usual practice is to trust the root CA cert, but Java supports using any cert in the chain, including the EE cert, as an anchor. Since you are already creating your own context, and keymanager, you might as well create your own trustmanager rather than modifying the JRE default, which is (usually) platform/environment dependent and sometimes tricky. You could fetch the desired cert and put it in a separate keystore file (see below), load that and use it to create a trustmanager. Or since you already have a keystore file for your own key+cert(s), Java supports having both a privateKey entry or entries and a trustedCert entry or entries for other party/parties in the same file, so you could just add their cert to your existing JKS and use the same KeyStore object in a TrustManagerFactory just like you have for KeyManagerFactory to create the trustmanager array for your context.
Easy ways to get the cert are:
openssl s_client -showcerts -connect host:port </dev/null (on Windows <NUL:) -- with port 443; many servers nowadays need the Server Name Indication extension (aka SNI) and for OpenSSL below 1.1.1 you must add -servername host to provide it, but this server doesn't. This outputs a PEM block for each cert from the server, with labels before each showing s: (subjectname) and i: (issuername). Note that after the first cert, which is as required the EE cert, this server sends the CA certs in top-down order not bottom-up. This is technically in violation of RFC 5246 (or earlier 4346 or 2246); RFC 8446 for TLS1.3 allows it because it was already a common extension, and Java JSSE in particular supports it.
keytool -printcert -rfc -sslserver host -- also supports host:port but the default is 443 which is okay for you. This outputs only the PEM blocks, so if you didn't already know from my previous item about the order, you would have to decode each one to find out which is which, or just experiment until you found the correct one.
In either case take the desired PEM block and put it in a file, and do
keytool -importcert -keystore jksfile -alias somealias -file thecertfile
somealias is only required to be unique within the file (which is trivial if you put it in its own file) and case-insensitive (conventionally lowercase), but should be descriptive or mnemonic, and alphanumeric only if possible. Then use the resulting file as above.
Related
I'm a bit lost of how I can use certificate in WidlFly 11. I re the doccumentation and found a lot of terms like JSSE, OpenSSL, Elytron, ApplicationRealm.
The problem occurs when I execute the code
final URL url = new URL("https://someUrl");
HttpsURLConnection httpURLConnection = (HttpsURLConnection)url.openConnection();
This exception is thrown sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
So, what exactly need to configure? I tried the section "Enable One-way SSL/TLS for Applications" in Elytron Doccumentation but didn't works.
ps: I'm using java 9.01
ps2: I'm using standalone-full.xml
let me know if you need more informations
This is unrelated to WildFly - you need to configure certificates trusted by java URL connections - you need to create and configure truststore:
create keystore containing certificate of server (if it is self-signed certificate), or better, certificate of its CA:
keytool -import -file myCA.cert -alias myCA -storepass mypassword -noprompt -keystore my.truststore
start using created keystore file as truststore in WildFly by setting javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword system properties:
bin/jboss-cli.sh -c
/system-property=javax.net.ssl.trustStore:add(value="/path/to/my.truststore")
/system-property=javax.net.ssl.trustStorePassword:add(value="mypassword")
Elytron documentation you mention is related only to server side - but this is client side configuration, which is not currently handled by it.
The certificate is not trusted, iirc there is a self-signed certificate in WildFly 11 so yo need to trust it or install a real certificate.
Accept server's self-signed ssl certificate in Java client
Currently I try to make a play application be able to communicate SSL encrypted with the database.
I created self-signed certificate and CA for mysql. There is no problem with that normally, I can make the application communicate with the db server encrypted, when I add this CA to the JAVA_OPTS
-Djavax.net.ssl.trustStore=/app/path/conf/truststore
Everything goes well until my application not tries to communicate with other sites via SSL:
java.net.ConnectException: General SSLEngine problem to https://api.twitter.com/1/statuses/oembed.json?<meh>
[...]
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
[...]
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
[...]
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
[...]
The error is pretty obvious: twitter's ssl cert cannot be checked, because my truststore doesn't contains the CA which signed twitters certificate. IIRC I cannot add more than one "javax.net.ssl.trustStore" parameter for JVM, so I have to inject my CA's into the play framework. For luck, play framework supports ssl, and regarding the documentation I can add multiple truststores: https://www.playframework.com/documentation/2.4.x/WsSSL
I created a config file for ssl:
play.ws.ssl {
trustManager = {
stores = [
{ path: /path/to/truststore, type: "JKS", password = "<whatever is it" }
{ path: ${java.home}/lib/security/cacerts } # Default trust store
]
}
But when I start the server, I got the following error message:
Oops, cannot start the server.
java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
And now, I am stuck. I can inject my truststore for JVM, but when I do this, the app cannot communicate with other SSL enabled hosts - there's no cert, but when I try to add my truststore for play framework it doesn't accept it, because nobody signed my cert.
Is there a way to solve this somehow? I suspect if I take the system wide cacert file (which is used by java) and my truststore, and then I merge them with keytool, it will solve this, but this is not the best way - it would be more sane if I could pack my self signed certs next to the application.
I had the same problem for some reason my own trust store wouldn't be recognized so I ended up adding my certificate to the default keystore (${java.home}/lib/security/cacerts). You can use this command to do this (BTW cacerts default password is changeit):
keytool -v -importkeystore -srckeystore alice.p12 -srcstoretype PKCS12 -destkeystore "c:\Program Files\Java\jre1.8.0_71\lib\security\cacerts" -deststoretype JKS
Then you need to have the trustManager and the key manager defined. The trustManager points to the trust store and the keyManager to your certificate.
play.ws.ssl {
trustManager = {
stores = [
{
path: ${java.home}/lib/security/cacerts
}
]
}
keyManager = {
stores = [
{
type: "PKCS12",
path: "/Users/work/Documents/path/to/certificate/certificate.p12",
password: "pass"
}
]
}
}
That error means Play can't find the stores. Is the path correct?
I had the same error message: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty caused by empty store. According to Key store docs there could be password field in the configuration but it was ignored therefore certificates were not read from the store. I had to use PEM format or you can include certificates in config see Configure store. The password field is added/put back in commit #e867e729. I'm on Play 2.6 so I couldn't test it.
When SMTP client connects to remote SMTP server and issues STARTTLS command, which has self-signed certificate - I get the error in client side:
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
There is one solution to trust all certificates like:
SSLContext trustAllSSLContext;
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
try {
trustAllSSLContext = SSLContext.getInstance("SSL");
trustAllSSLContext.init(null, trustAllCerts, null);
} catch (NoSuchAlgorithmException | KeyManagementException ex) {
//...
}
But this causes security vulnerabilities. I think maybe this is better than to send unencrypted data in this case?
Compared to unencrypted, it does protect against a passive attacker who can only eavesdrop, but not an active one.
Basically duplicate of How to handle invalid SSL certificates with Apache HttpClient? but that includes quite a bit you don't need.
The best approach of course is for the server to get a 'real' cert. Without that, to trust their selfsigned cert but not anybody else's, get their cert by some process that you are sure isn't tampered with and put it in your truststore.
If this is a personal machine, or a personal install of Java, the JSSE default truststore is the file JRELOC/lib/security/jssecacerts if it exists, and SAME/cacerts otherwise. The cacerts file comes in the Java package and includes several dozen well-known public CAs like Verisign, GoDaddy, etc., so connections you make to nearly any public host on the internet (which use those CAs) will work, but you can add to or delete from it as you wish. In this case get the desired cert in either DER or PEM format in a file and:
keytool -keystore JRELOC/lib/security/cacerts -importcert -file the_added_cert
# or JRELOC/bin/keytool if that directory isn't in your search path
# enter changeit for the password, and confirm that you want to trust this cert
If this (machine and) Java is shared with other users, changing the default truststore affects them also. If you or they don't want that, create your own file (by copying the standard one and modifying) and then use that truststore by running the program with -Djavax.net.ssl.trustStore=filename (as described in http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#Customization) or if you don't run the 'java' yourself tell whatever does to set that system property.
so here's what I'm trying to do:
I connect to my SSL server
It sends me two certificates, and one of them is self-signed
At this stage, there are no problems, I've:
X509Certificate[] myTwoCerts;
// with 2 certificates in there, and I'm sure it's there because, I print them in my console.
Now I want to create .p12 file with 2 certificates inside.
Until now, I've tried something like this:
KeyStore pkcs12Store = KeyStore.getInstance("PKCS12");
pkcs12Store.load(null, null);
X509Certificate cert1 = myTwoCerts[0];
X509Certificate cert2 = myTwoCerts[1]
String alias1 = "FIRST";
String alias2 = "SECOND";
pkcs12Store.setCertificateEntry(alias1, cert2);
pkcs12Store.setCertificateEntry(alias2, cert2);
But I'm getting the following Exception:
Exception in thread "main" java.security.KeyStoreException: TrustedCertEntry not supported
Can anybody help me plz??
The PKCS12 keystore in Java cannot be used to store certificates or certificate chains without their private keys.
See this note in the JCA reference guide:
"pkcs12" is another option. This is a cross platform keystore based on the RSA PKCS12 Personal Information Exchange Syntax Standard. This standard is primarily meant for storing or transporting a user's private keys, certificates, and miscellaneous secrets. As of JDK 6, standards for storing Trusted Certificates in "pkcs12" have not been established yet, and thus "jks" or "jceks" should be used for trusted certificates.
(Despite mentioning JDK 6, this still is in the JDK 7 documentation.)
If you want to store a private key + certificate (chain) in a PKCS#12 store in Java, there needs to be a private key and you need to use the setKeyEntry method.
keytool -trustcacerts -keystore keystore.p12 -storetype pkcs12 -alias root -genkeypair
KeyStore pkcs12 = KeyStore.getInstance("PKCS12");
String filename = "/tmp/keystore.p12";
keyStore.load(
new FileInputStream(/*"myKeyStore.jks"*/filename),
password);
I want to make a server SSL socket connection using the following code:
int port = 12000;
ServerSocketFactory ssocketFactory = SSLServerSocketFactory.getDefault();
ServerSocket ssocket = ssocketFactory.createServerSocket(port);
// Listen for connections
Socket socket = ssocket.accept();
I get a "javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled." when doing the accept.
I created a Keystore that contains a RSA key using:
keytool -genkeypair -alias ClubConnectionCert -keyalg RSA -validity 7 -keystore ClubConnectionKeystore
and I start my Program with the following options:
-Djavax.net.ssl.keyStore=ClubConnectionKeystore -Djavax.net.ssl.keyStorePassword=mypassword
Do I miss some code to read in the Keystore, or how can I test/debug that the given keystore is actually used?
I executed your code and I confirm it's working fine.
Make sure javax.net.ssl.keyStore points exactly to your keystore file.
I put my keystore file at the root of my project.
Perhaps try absolute path to your keystore.
Make sure the -D parameters are set as JVM params, not Program options (in your IDE).
Good luck, you're about to make it work.