I'm trying to connect to a ssl server using Java. I've already managed to do that in Python, however I've got a PEM file which isn't supported by Java. Converting it to PKCS12 didn't work
Error when trying to connect was:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
My question is: Can you give me the Java equivalent? (Using another library is also ok)
import ssl
import socket
mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysslsock = ssl.wrap_socket(mysock, keyfile='mykey.pem', certfile='mycert.pem')
mysslsock.connect(("SOMEHOST", XXXXX))
Please note that the server requires client authentication.
Edit
That's what I did in Java:
I used openssl to convert my certificate into PKCS12 format:
openssl pkcs12 -export -out mystore.p12 -inkey mykey.pem -in mycert.pem
Then I've used the keytool that comes with the JDK to convert it into JKS:
keytool -importkeystore -destkeystore mystore.jks -srcstoretype PKCS12 -srckeystore mystore.p12
And that's my Java code:
System.setProperty("javax.net.ssl.keyStore", "mystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "123456");
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
SSLSocketFactory socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) socketFactory.createSocket(HOST, PORT);
socket.startHandshake(); // That's the line I get the exception
socket.close();
I'm sure I'm making some really stupid mistake as I don't have any experience with SSL.
Edit:
Probably I've somehow the wrong certificates so that's what they look like:
<mykey.pem>
-----BEGIN RSA PRIVATE KEY-----
ljnoabndibnwzb12387uGJBEIUQWBIDAB
....... (Some more lines)
-----END RSA PRIVATE KEY-----
<mycert.pem>
Bag Attributes
localKeyId: XX XX XX XX
subject:...
issuer:...
-----BEGIN CERTIFICATE-----
LAinaw8921hnA.......
.....
-----END CERTIFICATE-----
don't you need to load the Key into the Java Keystore?
that is a seperate program.
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html
-importcert {-alias alias} {-file cert_file} [-keypass keypass] {-noprompt}
{-trustcacerts} {-storetype storetype} {-keystore keystore} [-storepass
storepass] {-providerName provider_name} {-providerClass provider_class_name
{-providerArg provider_arg}} {-v} {-protected} {-Jjavaoption}
Reads the certificate or certificate chain (where the latter is supplied
in a PKCS#7 formatted reply or a sequence of X.509 certificates) from the
file cert_file, and stores it in the keystore entry identified by alias. If
no file is given, the certificate or certificate chain is read from stdin.
keytool can import X.509 v1, v2, and v3 certificates, and PKCS#7
formatted certificate chains consisting of certificates of that type. The
data to be imported must be provided either in binary encoding format, or in
printable encoding format (also known as Base64 encoding) as defined by the
Internet RFC 1421 standard. In the latter case, the encoding must be bounded
at the beginning by a string that starts with "-----BEGIN", and bounded at
the end by a string that starts with "-----END".
Related
I bought an SSL certificate in GoDaddy. I need to use it to start my Spark Java self-contained server through a secure connection. According to the documentation in http://sparkjava.com/documentation#examples-and-faq, I need to do the following:
String keyStoreLocation = "deploy/keystore.jks";
String keyStorePassword = "password";
secure(keyStoreLocation, keyStorePassword, null, null);
But when I download the certificate from GoDaddy I got the files:
11111.pem
11111.crt
bundle-g2-g1.crt
What do I need to do to convert these files is something compatible to use as the first parameter of secure(keyStoreLocation, keyStorePassword, null, null);?
IF the 1111.pem file is your private key (check the first line is 5 hyphens, BEGIN, optionally a word like RSA EC or ENCRYPTED, PRIVATE KEY, and 5 hyphens) then start with
openssl pkcs12 -export -in 1111.crt -inkey 1111.pem -certfile bundle-g2-g1.crt -out my.p12
Nearly all java programs since 2018 can actually use a PKCS12 instead of JKS for a keystore, but if this code really does need a JKS then do
keytool -importkeystore -srckeystore my.p12 -destkeystore my.jks -deststoretype jks
# if using very old Java (below 8u40 or so) add -srcstoretype pkcs12
Mostly dupe (but somewhat updated from)
Combined .pem certificate to truststore/keystore.jsk
convert certificate from pem into jks
How do I generate X.509 certificate from key generated by openssl and more linked there
https://serverfault.com/questions/483465/import-of-pem-certificate-chain-and-key-to-java-keystore
I have tried to use keytool -importcert -alias (my cert alias) -file (cert file path.pem) -keystore (jre keystore path) and I get an error that says my .pem file is not an x509 certificate. I think it is because my .pem file is not formatted properly or because it contains both private key and certificate. Any suggestions? My .pem file looks like the following:
Bag Attributes
Microsoft Local Key set: <No Values>
localKeyID:
friendlyName:
Microsoft CSP Name:
X509v3 Key Usage: 10
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG...
-----END PRIVATE KEY-----
Bag Attributes
...
-----BEGIN CERTIFICATE-----
MIIHQzCCBiugAwIBAgIQDEtIx...
-----END CERTIFICATE-----
I finally was able to import the certificate into my JRE cacerts file. First I had to create a new .pem file with only the certificate:
-----BEGIN CERTIFICATE-----
MIIHQzCCBiugAwIBAgIQDEtIx...
-----END CERTIFICATE-----
Then I had to copy the cacerts file into a new location, and then run the following code to import it:
keytool -import -v -trustcacerts -alias serveralias -file /Applications/certificate.pem -keystore /cacerts
After importing the certifcate I then copied the new cacerts file back into the JRE security folder, and success!
Have a look at this page on the PEM file format Since your file contains references to Microsoft I suggest making sure the file uses Unix line endings (\n) instead of Windows line endings (\r\n)
Unfortunately I have not been able to import the certificate into my cacerts file, however I was able to configure my CloseableHttpClient so that it will consume HTTPS URLS that do not have "valid" certificates. I Just used the following code when implementing my client:
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, ((certificate, authType) -> true)).build();
CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
I know that this is not the proper way to handle the situation but I'm not concerned about validating the certificate from the server I am communicating with in this specific instance.
I want to call some web services through an HTTPS connection in Java. The certificate authority gave me a PKCS12 file but I get a
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
due to
javax.net.ssl.SSLHandshakeException
Here is the code I am executing :
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
System.setProperty("javax.net.ssl.keyStore", "path/toto2.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "pwd");
System.setProperty("javax.net.ssl.trustStoreType", "JKS");
System.setProperty("javax.net.ssl.trustStore", "path/toto2.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "pwd");
System.setProperty("proxySet","true") ;
System.setProperty("https.proxyHost", "XXX") ;
System.setProperty("https.proxyPort", "XXX") ;
I first tried to give the PKCS12 file, then the JKS but I had the same error.
I tried another solution, with curl. So I extracted certificates from the PKCS12 and the requests successed.
Here are the commands that I used to extract the different files from the P12 :
openssl pkcs12 -in file.p12 -nocerts -nodes -out clientcert.key
openssl pkcs12 -in file.p12 -clcerts -nokeys -out clientcert.cer
openssl pkcs12 -in file.p12 -cacerts -nokeys -chain -out cacerts.cer
Am I wrong with the way I use those files in Java ?
PKCS12 normally contains a privatekey and cert (chain) combination, and is used to authenticate your system i.e. the client, which is done with the javax.net.ssl.keyStore* properties. You should specify type PKCS12, although some Java8 versions (only!) can read PKCS12 when you specify JKS.
It typically does NOT authenticate the server(s), which is what javax.net.ssl.trustStore* is for, so don't use it there. Depending on the server(s?), you may or may not need a custom truststore different from the P12 and also different from Java's default.
Look at the cert chain used by the server with a browser or other tool like curl or keytool -printcert -sslserver host[:port]
and determine whether it uses a public root CA like Verisign-now-Symantec-now-Digicert or GoDaddy, which will already be in Java's default truststore, or a 'private' CA or even a self-signed cert, in which case you should either add that cert to the default truststore or if you can't or don't want to modify your JVM put that cert in a keystore file you use as the truststore.
PS: the ValidatorException causes the SSLHandshakeException not the reverse.
I would like to have SSL termination on HAProxy, using my own self-signed certificates, and to validate client access using client certificates I create.
I create the server (which is also the CA) certificates the following way:
openssl genrsa -out ca.key 1024
openssl req -new -key ca.key -out ca.csr
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
and:
cat ca.crt ca.key > haproxy.pem
at HAProxy, I configure:
bind *:443 ssl crt /path/server.pem ca-file /path/ca.crt verify required crt-ignore-err all
I create the client certificates in a similar way:
openssl req -new -key client.key -out client.csr
openssl x509 -req -days 365 -in client.csr -signkey ca.key -out client.crt
cat client.crt client.key > client.pem
My logic is: I'm creating a client key, a certificate signing request for it, and then I sign it using the CA (which is also the server certificate, so there's a simple chain that the server would recognize).
To test, I first try with the server certificate as the client cert:
curl https://my.service:443/ping -E ./haproxy.pem -k
pong
ok, it works. Now I try with the client certificate as the client certificate:
curl https://my.service:443/ping -E ./client.pem -k
curl: (58) unable to set private key file: './client.pem' type PEM
My question:
1) I would like to create a client certificate that this server will accpet, and test it using curl.
2) I would like to import this certificate and the CA into a new java keystore / truststore using keytool, so that Java (Jersey client) code could access the same content.
I have spent 2 days on 1/2.
I'm pretty sure someone that's done this before could answer this in 5m. Or so I hope. :)
Thanks!
1. create client cert
wrong: openssl x509 -req -signkey creates a self-signed cert, which by definition means the key in the cert (the subject key) is the public half of the same key whose private half signs the cert. The documentation for the cert (not req) case is clear that it replaces the key previously in the cert with the signing key. The -req doc is less clear, but it does the same thing; it puts in the cert the subject name from the CSR, also as the issuer, and the key from -signkey. You have used a CSR containing the client name, but a -signkey containing the CA key, producing an unusable chimera.
right: to sign a "child" (not self-signed) cert with x509, use -CA and possibly -CAkey as described in the documentation https://www.openssl.org/docs/apps/x509.html#SIGNING-OPTIONS (or man [where] x509 on any Unix with openssl doc installed). If there is or ever will be more than one child cert for a given CA (defined by its DN), either use the serial-number file scheme to automatically and conveniently assign sequential serial numbers, or use -set_serial to manually assign unique serial numbers (sequential is the easiest way to do unique, but if you a prefer another way that's okay).
aside: for the self-signed CA (and server?!) cert, you don't need separate req -new and x509 -req -signkey steps, you can do it in one req -new -x509. See the doc/manpage for req. In fact you don't need a separate genrsa step, req -newkey [-nodes] -x509 can do that as well. One note: in OpenSSL 1.0.0+ this generates the generic PKCS#8 format keyfile instead of the "legacy" PKCS#1 format used by genrsa (and rsa); all OpenSSL functions can accept either, but some other things might not. In particular last I checked (a while ago) the Wireshark option to decrypt SSL/TLS using server key for akRSA (there are other options too) accepted only PKCS#1 not PKCS#8.
2. use in Java (Jersey). Note that any SSL/TLS client doing client authentication, including Java, needs both the certificate and the privatekey, and in most cases the certificate uses "chain" or "intermediate" certs which you need also. Some people (cough) Microsoft (cough) encourage you to misunderstand and ignore this important distinction, but if you try to use only a certificate it won't work at all. On the other hand a truststore entry needs only the certificate, almost always only the root (CA) certificate, and usually must have only the certificate. Your situation where the same person operates the CA and server and client(s) is somewhat unusual for PKC.
2a. maybe just convert to pkcs12. Java does not directly support the openssl format(s) for keys, but both Java and openssl support PKCS#12 (and so do Microsoft, Mozilla, Apple, and probably others). Since you combined client key and (leaf) cert in client.pem do
openssl pkcs12 -export <client.pem -CA ca.crt [-name whatever] >client.p12
# if you use separate key,cert files see the doc about -in and -inkey
Java crypto (JCE and JSSE) can use this PKCS#12 as a keystore, if you can configure the keystore "type" (as pkcs12). The default SSLSocketFactory supports this, and so do other apps I've used, but I don't use Jersey and don't know what it does here. PKCS#12 isn't generally supported to carry "separate" certs (without privatekey), but in your case the CA cert for the client is also the cert for the server, so it will happen to work as your truststore as well; otherwise you would need to import the server CA or server selfsigned cert (only cert not privatekey) into a JKS truststore (which might be the default truststore in JRE/lib/security/[jsse]cacerts).
2b. maybe further convert to JKS. If Jersey cannot use PKCS#12 directly, Java can convert it to JKS which any sane Java code can use, like:
keytool -importkeystore -srckeystore client.p12 -srcstoretype pkcs12 -destkeystore client.jks
UPDATE 2018: after this answer was written Java support of PKCS12 increased, making it less often necessary to convert to JKS. 8u60 released fall 2017 and up still defaults to keystore type JKS, but as a special feature(?) type JKS can actually read (though not write) PKCS12; see the release notes and item keystore.type.compat in file JRE/lib/security/java.security. Java9 released 2017 makes the default keystore type PKCS12 which (as expected) reads and writes PKCS12, although explicit JKS no longer reads PKCS12. But if you do need to convert with Java9 for some reason, you now need to specify -deststoretype jks but no longer need to specify -srcstoretype pkcs12.
I am coding an application where I control the code of both the client and the server.
I am using SSLSockets to implement it.
I have the protocol already running with normal unsecured sockets, but when I try to switch to SSLSockets (using exactly the same protocol), I keep getting the following stack trace:
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:168)
at com.sun.net.ssl.internal.ssl.InputRecord.readFully(InputRecord.java:293)
at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:331)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:782)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:739)
For some reason, the exact same code works perfectly with unsecured sockets. Why could this be?
Any feedback would be appreciated. Thank you.
Pablo
From your post it is not possile to detect the problem.
When you switch to secure sockets the most secure ciphers are used by default.
If you have not configured your truststore/keystore correctly (or have not enabled the non-authenticated suites) then the SSL handshake will fail.
The exception seems to indicate that.
What you can do is run your program using javax.net.debug=ssl,handshake to enable SSL debugging info and post the debugging info and your code if you expect someone to help you.
Depending on what OS you are using, it may require admin/root priveledges to bind to or listen to the SSL port. Trying running your application with admin rights (in Windows) or sudo'd (on Linux).
Reasons can vary, -Djavax.net.debug=ssl is your friend, as suggested by Vladimir Dyuzhev.
Anyway, it may be a certificate problem -- make sure you have correct keystore and trustore. You will require one entry in keystore with:
private key
certificate
complete chain of issuer of the certificate
And a truststore:
complete chain of certificates for server certificate
I have problems generating proper keystore (trustore is easy -- just use keytool). For keystore you need st like this (Linux with openssl + java):
# convert all to PEM
openssl x509 -in ${ca}.der -inform DER -outform PEM -out ${ca}.pem
openssl x509 -in ${subca}.der -inform DER -outform PEM -out ${subca}.pem
# create one large PEM file containing certificate chain
cat ${ca}.pem ${subca}.pem > tmp_cert_chain.pem
# generate PKCS#12 BUNDLE
openssl pkcs12 -export -in ${cert}.pem -inkey ${key}.pem -certfile tmp_cert_chain.pem -out tmp_pkcs12.pfx
# convert PKCS#12 bundle to JKS
keytool -importkeystore -srckeystore tmp_pkcs12.pfx -srcstoretype pkcs12 -srcstorepass ${storepass} -destkeystore $keystore -deststoretype jks -deststorepass ${storepass}
# print out JKS keystore
keytool -list -keystore $keystore -storepass $storepass