SSLHandshakeException unknown_ca apns java - java

I am trying to implement a standalone application for iphone; for which I wish to use Apple Push notification for iphone clients.
I am getting javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca
Following is my Java code to connect to apns gateway:
int port = 2195;
String hostname = "gateway.sandbox.push.apple.com";
char[] passwKey = "password".toCharArray();
KeyStore ts = KeyStore.getInstance("PKCS12");
ts.load(new FileInputStream("/path/to/file/Cert.p12"), passwKey);
KeyManagerFactory tmf = KeyManagerFactory.getInstance("SunX509");
tmf.init(ts, passwKey);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(tmf.getKeyManagers(), null, null);
SSLSocketFactory factory = sslContext.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(hostname,port);
String[] suites = socket.getSupportedCipherSuites();
socket.setEnabledCipherSuites(suites);
//start handshake
socket.startHandshake();
Please help me understand what and how the ssl certificate can be installed on my Linux machine.
EDIT :
It worked for me now, I recreated Cert.p12, and the program started working. I don't know what was the exact cause for not working, but I guess it would be corrupt Cert.p12 file.
Thank you all for your help.

unknown_ca:
Received a valid certificate chain or partial chain, but the certificate was not accepted because the CA certificate could not be located or could not be matched with a known, trusted CA. This message is always fatal.
You may need to add the certificate to your jre's cacerts file (generally located under lib/security). Look into the documentation for keytool and its -import option for more information.
You can probably find an example of how to do this by researching the cacerts file and keytool further.

had same issue.
import to IE/export with "all certificates included" solved it.

Related

force-sending client certificate

I'm using a Java SE Jersey client to connect to a HTTPS resource which uses two-way SSL.
My SSLContext looks like this:
private SSLContext getSSLContext() {
SslConfigurator sslConfig = SslConfigurator.newInstance()
.keyStoreFile("src/main/certificates/testcert.p12")
.keyPassword("mypassword");
return sslConfig.createSSLContext();
}
The problem is that the client certificate is never sent.
I get error "Warning: no suitable certificate found - continuing without client authentication" and I've tracked the reason to the fact that the client certificate isn't issued to one of the Cert Authorities listed in the server's CertificateRequest message to the client. I know from testing with cURL that the server will accept the certificate regardless. The endpoint is a public test system.
My question: How do I force-send my client certificate? (i.e. my Java SE client should ignore the fact that the testcert.p12 certificate's issuer is not the list of issuers that the server has said it would accept)
Please, don't point me to answers that are about disabling check of the server's certificate or about using self-signed certificates. The server's certificate is just fine.
UPDATE
It turned out my problem was another one. I debug by setting system property javax.net.debug=all. After examining the resulting output it looked to me as if the keystore was empty, even after doing the above. So no wonder why "no suitable certificate found".
Jersey has this 'clever' SslConfigurator class which is there to help you set up an SSLContext. Perhaps just too clever for me, because I couldn't make it work with the above code. So instead, I now configure my SSLContext like below:
KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream fis = new FileInputStream("src/main/certificates/testcert.p12");
ks.load(fis, "mypassword".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "mypassword".toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
// now use 'sc' in Jersey
This works for me where Jersey's helper class didn't. I fully sympathize with Jersey's idea of a helper class for SSLContext, because JSSE seems overly complex here for such a simple use case. Well, well.
You can't. It would be a TLS protocol violation, and therefore there is no API to support it. The various TLS APIs will only send a client certificate if:
It was requested, and
A client certificate can be found that conforms to what is specified in the CertificateRequest message.
You will have to arrange for your server to trust your client certificate, either by getting it signed by a CA or by exporting it to the server's trusted certificate store, whatever form that takes at the server.

Java TLS socket : No trusted certificate found

Let me explain quickly what I'm trying to do. I'm trying to build my own Apple's Push Notification service in java (for testing purposes). This service works thanks to TLS socket.
I have a java client to create a TLS socket to send push notifications to the APNs. I changed the host url to redirect the socket to localhost:2195. Now I'm trying to write a java socket server to get the notification request.
However, I get an exception during the handshake and can't find how to fix it.
Note : I'm using the same certificate on both sides, it's a standard .p12 file that works to send push notifications to the APNs.
Here is the client (simplified) :
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(certificatePath), password.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(ks, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
tmf.init((KeyStore)null);
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLSocketFactory ssf = sc.getSocketFactory();
SSLSocket socket = (SSLSocket) ssf.createSocket(InetAddress.getLocalHost(), 2195);
socket.startHandshake();
Here is the server :
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(certificatePath), password.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(ks, password.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), null, null);
SSLServerSocketFactory ssf = context.getServerSocketFactory();
serverSocket = (SSLServerSocket) ssf.createServerSocket(2195);
And here is the exception :
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
I guess the client isn't trusting the server's certificate. I tryed to set the client's TrustManager to accept the server's p12 and it worked, however I need this to work without editing the client (since it's working that way with the real APNs).
What kind of certificate needs the server to be trusted by the client ?
Thanks in advance.
EDIT: I WAS WRONG! tmf.init(null) DOES use the default keystore just like sslctx.init(,null,) !
That default is normally the cacerts file in JRE/lib/security which DOES trust many established CAs
so now I think we can be confident the real server is using a cert under an established CA (and so
is trusted by your client) while the cert in your p12 apparently does not;
but there are two possibilities here:
it is selfsigned, or issued by an unknown, obscure, or unproven CA
it is issued by a 'real' CA under an 'intermediate' CA that needs a chain cert (or several)
and you do not have the chain cert(s) in your p12. Note this could still work for client auth
to the real server, because the real server can easily have the chain cert(s) 'preloaded'
in its truststore even though they aren't in Java's.
To distinguish these, look at keytool -keystore file -storetype pkcs12 -list -v
and see what cert or sequence of certs you have there.
Then there may be several approaches to solution:
if you are only missing chain cert(s) for an established CA get them and add them.
keytool only allows you to replace the whole chain so you must get all needed certs;
openssl (if you have or get it) can break out the key and cert(s) from a pkcs12,
replace or add individual certs, and join them back together.
create a different store and key for the server and get it a cert (chain) from an established CA.
Usually costs some money and requires you prove control of the server's domain name.
(Your client can and should still use this p12. The two sides needn't be the same.)
locate the trust anchor (from the p12, or from somewhere else like the CA) and have it
in a truststore the client explicitly loads. You effectively tried this by using the
p12 as the truststore and say you don't want that.
put the trust anchor in the client's default truststore, so the client continues
using the default. If you don't mind modifying your JRE (and no other user or application
on your system is bothered) just add to JRE/lib/security/cacerts. Or, assuming you can set
system properties, put the anchor in a store or just leave it in the p12
and set javax.net.ssl.trustStore{,Password,Type} to point to that store.
(If you copy you should take only the cert; a p12 is a KEY AND cert not just a cert.
Don't just -importkeystore; -importcert a cert file, created with -exportcert if necessary.)
(You can System.setProperty in your code, but that's changing your code. If you run from
commandline you can use 'java -Dname=value...'. For other cases YMMV.)
There is one possible 'type' issue: if the cert was issued with ExtendedKeyUsage extension
and that value specifies only TLSclient and not TLSserver (which the CA can choose to do)
then using it for server probably won't work -- it appears JSSE enforces EKU restrictions.
But if that is a problem you'll get a very different Exception.
And you can see this also in the keytool -list -v above.
Since you (rightly) want to use this p12 for your client, your server logic similarly needs
to trust it. (Using it for outgoing auth does NOT automatically make it trusted for incoming auth.)
But only if/when clientAuth is actually done, which is not the default; does your server code
.setNeedClientAuth(true) on the SSLServerSocket before accepting the connection?
Possible approaches are equivalent to the above except skipping #2 as inapplicable.
If both client and server use the same JRE, that makes the cacerts way a little easier.
Finally, yes TrustManager 'PKIX' is newer and generally more featureful than 'SunX509'.
But for the basic test 'is the trust anchor in our truststore' they are equivalent.
Sorry again for the mislead.

Java: how to add SSL client-side authentication

I have this code to connect the server with a client using SSL, and now I want to add client-side authentication:
(I have a server keystore (JCEKS type) and a client keystore (JKS type), the server uses a truststore (cacerts) where I imported both certificates because I also want to use this truststore for client authentication)
Client code:
System.setProperty("javax.net.ssl.trustStore", cerServer);
System.setProperty("javax.net.ssl.trustStoreType","JCEKS");
System.setProperty("javax.net.ssl.trustStorePassword", pwdCacerts);
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("localhost", port);
Server Code:
KeyStore ks = LoadKeyStore(new File(serverKeyStore), pwdKeyStore, "JCEKS");
KeyManagerFactory kmf;
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, pwdKeyStore.toCharArray());
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(kmf.getKeyManagers(),null, null);
SSLServerSocketFactory ssf = sc.getServerSocketFactory();
sslserversocket = (SSLServerSocket) ssf.createServerSocket(port);
thanks in advance for any help.
edit:
I add this code in the server side:
System.setProperty("javax.net.ssl.trustStore", cacerts);
System.setProperty("javax.net.ssl.trustStoreType","JKS");
System.setProperty("javax.net.ssl.trustStorePassword", pwdCacerts);
but if I delete the client certificate in cacerts, the connection doesn't give me error and for that I think it's wrong that way
If you want your system to use client-certificate authentication, you'll need
the server to request (or require) a client certificate. This is done by setting setWantClientAuth(true) on the server socket (or setNeedClientAuth, respectively). You'll also need the server to advertise the CA it accepts, which is normally done by using a truststore on the server that contains the CA by which the client-certificate chain was issued (this seems to be what you've done by setting javax.net.ssl.trustStore* on the server).
the client to be configured with a keystore containing the client certificate (possible the chain if there are intermediate CAs) and its private key. This can be done by setting the javax.net.ssl.keyStore* (which may affect other connections) or by using a KeyManagerFactory in the same way as you've done it on the server side.
If you use setWantClientAuth(true), you might still not get an error, since the server will accept connections that don't have a client-certificate (the server would then check the SSLSession's peer certificates to see whether there was a cert or not). setNeedClientAuth(true) would break the connection when the client doesn't present a certificate.

Java SSL connection with self-signed certificate without copying complete keystore to client

I am setting up a licensing servlet in Java together with a client app that will post request for new licenses and validate existing licenses at that server. The servlet runs in Tomcat. I've configured Tomcat so that it only allows connections to the servlet over https, and this works just fine.
I have created a self-signed certificate using 'keytool -genkey -alias www.mysite.com -keyalg RSA -keystore license.store' which creates a file license.store and pointed tomcat to this keystoreFile with its password asdf1234.
When I just try to connect from the client to the servlet over https in Java, I receive the familiar PKIX path building failed because the certificate is not in the truststore. I tried to fixed this using this suggestion resulting in the code below:
private SSLSocketFactory getSSLFactory() throws Exception {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream is = this.getClass().getResourceAsStream("license.store");
if(is ==null) {
return null;
}
keyStore.load(is, "asdf1234".toCharArray());
is.close();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
return ctx.getSocketFactory();
}
After which I call:
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
con.setSSLSocketFactory(getSSLFactory());
which results in a succesfull connection.
Now the problem is that I only get this to work when I copy the license.store to the client and load that into the KeyStore.load(). It doesn't strike me as very safe to copy the private key and its password that the server uses to the client. Is there any way to extract only the public key from the license.store and use that? I've been searching this forum and others for a day now and just can't seem to get it.
You shouldn't be generating a public-private key pair, but rather import the certificate of the server into your (the client's) Java truststore. The certificate is not a secret, and thus does not provide a security risk on the client side. See the -import option for keytool. Here's in an example.

How can a Java client use the native Windows-MY store to provide its client certificate as required during a SSL handshake?

This question was also posted at TLS client authentication fails when using SunMSCAPI but has not found an answer.
I have a Java6 application running on a Windows Win2008R2 client that connects to a server requiring client certificates. The Java application is required to use the native Windows stores, i.e. Windows-Root and Windows-My, and employs the SunMSCAPI provider. The Windows certificate management console reports that both the client cert and CA cert that signed it are correct and enabled for all purposes.
Server verification works exactly as expected, but client authentication fails when the client throws a SSLException complaining that the privatekey is not an RSA private key. However, when the Java default provider is used instead, and the same client certificate is taken from a JKS keystore, i.e. SunMSCAPI and the Windows stores are not used, client authentication works just as expected and the SSL connection succeeds.
When executed, the application reports that it's using the SunMSCAPI provider and is able to print the correct client certificate as well as information about its RSAPrivateKey. Tracing indicates that the client exception occurs after the servers 'Hello Done' as its response to the server's certificate request is being prepared.
The relevant code bits for the client keystore portion of this are:
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManagerFactory kFac = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Provider pvdr = new sun.security.mscapi.SunMSCAPI();
Security.insertProviderAt(pvdr,1);
KeyStore kStore = KeyStore.getInstance("Windows-MY",pvdr);
kStore.load(null,null);
kFac.init(kstore,null);
sslContext.init(kFac.getKeyManagers(), <a trust factory>.getTrustManagers(), new java.security.SecureRandom());
SSLSocketFactory sockFactory = SSLSocketFactory(sslContext);
SSLSocket sslSock = (SSLSocket)sockFactory.createSocket(<some destination host>, <some destination port>);
BufferedInputStream bInStr = new BufferedInputStream(sslSock.getInputStream());
bInStr.read(<the read arguments>); <<< exception thrown in here
Any pointers or suggestions will be greatly appreciated!
The stacktrace shown in your link shows the problem to be at RSA's JSSL provider. Especially for this case you should use Sun/Oracle's JSSE provider. It's the default one anyway.
Call it as
SSLContext sslContext = SSLContext.getInstance("TLS", "SunJSSE");

Categories