I've been looking for a solution but haven't found anything, Is it possible to reload the java keystore without restarting JVM.
I'm not sure if this is exactly what you mean, but you can interact with (create, load, update) a Java keystore via the KeyStore class.
https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html
Example of loading a keystore:
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("newKeyStoreFileName.jks"), pwdArray);
Related
I know there is many similar questions, but i could not find any help in any previous question. I am trying to communicate with a wcf service from a java client, i used the auto generated stubs created by netbeans to achieve that. the wcf service is protected with client certificate authentication using TLS(1.2). I cannot make it work to use the SunMSCAPI provider (meaning i want to use the windows user local store to use the certificate as a client. (if there is another way or library to make it possible to use windows certificate local stores please share with me).
This SSLContext is not working to call the web service:
IService servicePort = service.getWSHttpBindingIService();
SSLContext sslContext = SSLContext.getInstance(sslVersion);
**KeyStore keystore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
keystore.load(null, null);**
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore, null);
KeyManager[] keyManagers = kmf.getKeyManagers();
sslContext.init(keyManagers, null, null);
BindingProvider bindingProvider = ((BindingProvider) servicePort);
bindingProvider.getRequestContext()
.put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory",sslContext.getSocketFactory());
The error i get is, when debugging i can see the keys and the certificates in the KeyManagers array correctly:
com.sun.xml.internal.ws.client.ClientTransportException: The server sent HTTP status code 403: Forbidden
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.checkStatusCode(HttpTransportPipe.java:310)
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.createResponsePacket(HttpTransportPipe.java:259)
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:217)
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java:130)
at com.sun.xml.internal.ws.transport.DeferredTransportPipe.processRequest(DeferredTransportPipe.java:124)
at com.sun.xml.internal.ws.api.pipe.Fiber.__doRun(Fiber.java:1121)
at com.sun.xml.internal.ws.api.pipe.Fiber._doRun(Fiber.java:1035)
at com.sun.xml.internal.ws.api.pipe.Fiber.doRun(Fiber.java:1004)
at com.sun.xml.internal.ws.api.pipe.Fiber.runSync(Fiber.java:862)
at com.sun.xml.internal.ws.client.Stub.process(Stub.java:448)
at com.sun.xml.internal.ws.client.sei.SEIStub.doProcess(SEIStub.java:178)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:93)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:77)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:147)
the pfx is installed in user local store correctly, because i used it in c# and it worked many times.
in the java code above also, when i use pfx directly instead of SunMSCAPI provider, it works:
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(pfxStream, pfxPassword.toCharArray());
kmf.init(ks, pfxPassword.toCharArray());
I am using windows 10. any help would be appriciated.
I got the SunMSCAPi idea from the following link:
Digital Signature SunMSCAPI provider & MS Crypto API
It looks like the issue was that there is many certificates in my localstore and according to my research KeyManager selects the first Key found that meet the criteria, meaning that it will not necessarily pick my certificate among them. i solved this issue, by creating a decorator over X509KeyManager that chooses my desired certificate alias. got the idea from this QA:
How I can tell alias of the wanted key-entry to SSLSocket before connecting?
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.
I believe I have done everything I should here according to related articles and yet I still get this error. I have created my own CA and signed the server certificates with this CA. On the Android side I have created a custom TrustManager using a custom truststore which has this CA root certificate in it. Using System.setProperty("javax.net.debug", "ssl") on the server side (it doesn't work on the Android side unfortunately even in 4.4) I get a little bit more information. I get past the server hello and the exchange of the secret keys. Then Android gives me the above error (Trust anchor for certification path not found) and on the server side I get
javax.net.ssl.SSLException: Inbound closed before receiving peer's close_notify: possible truncation attack?
I have mirrored the application (client) on my PC and it works.
Is it that Android does not support 2048 keys with SHA512withRSA?
I have no problem with self-signed certificates (1024 and SHA1withRSA); have not tried self-signed certificates with 2048 and SHA512.
Somehow I believe this is a shortcoming of Android that is not documented or hard to find (kind of like System.setProperty("javax.net.debug", "ssl") not working).
I actually implement my own KeyManager and TrustManager and keystores and truststores because eventually I will need mutual TLS ... all working on a PC. Hoped it would be an easy migration to Android.
Here is the Android setup of the Keystores/Truststores (gets the files and loads them)
LoadFile(getString(R.string.truststore_filename), R.raw.androidtruststore);
LoadFile(getString(R.string.keystore_filename), R.raw.androidkeystore);
String basePath = getFilesDir().getAbsolutePath() + "/";
SecureRawHttpWanSender.setSecureProperties(basePath + getString(R.string.truststore_filename),
getString(R.string.truststore_password),
basePath + getString(R.string.keystore_filename),
getString(R.string.keystore_password),
true);
Here is the setting of the TrustManagers etc. done in setSecureProperties() which is also used on the PC. Only the loading of the files is different (PC uses jks and Android uses bks)
FileInputStream fIS = null;
try
{
// On Android this is "BKS". Otherwise Sun Java is "JKS"
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
Manager.log.log(Level.Info, Task.WanSecure, "Type of truststore: " + trustStore.getType());
fIS = new FileInputStream(trustStoreFileName);
trustStore.load(fIS, trustStorePassword.toCharArray());
fIS.close();
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
if(keyStoreFileName != null)
{
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
Manager.log.log(Level.Info, Task.WanSecure, "Type of keystore: " + keyStore.getType());
fIS = new FileInputStream(keyStoreFileName);
keyStore.load(fIS, keyStorePassword.toCharArray());
fIS.close();
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword.toCharArray());
}
return true;
}
Thanks for any help (and the discovery of anything I did that was really stupid!)
I found a solution to the above problem but I don't like it. I may have actually seen it mentioned somewhere else in one of the questions related to this topic. What I did was add to the android truststore the server's certificate that is signed by the CA. So now in my truststore I have TWO certificates for the server, the server's certificate signed by the CA and the CA root certificate.
What I don't like is the inconsistency. On my Windows 7 PC I do not need the server's certificate signed by the CA, it is sufficient to have the CA root. I can understand the need for intermediary certificates IF the server certificate was signed by a CA that perhaps is validated by the root CA in the truststore but that intermediary CA's certificate is not in the truststore. The chain is then broken.
Why I need the server certificate signed by the CA root plus the root CA on Android but
only need the root CA on my PC is point of significant confusion. I take it as a typically incomplete implementation of Android. As I am finding out is the case for XML schema validation!
I need to setup a javax.net.ssl.SSLContext for use in a Jersey-Client application. All I want to do is the context to accept a custom root ca certificate. Is is really true that there is no way around of generating a keystore file and importing the CA certificate?
Is is really true that there is no way around of generating a keystore
file and importing the CA certificate?
There are way to do it without a keystore file, but since you would have to load the CA certificate you want to trust one way or another, you'll have to load a file or resource somehow.
(You could also certainly implement your own TrustManager that makes all the calls to use the Certification Path API, without using the KeyStore API at all, but that would only increase the complexity of your code, not reduce it. You would also need to understand the Java PKI Programmer's Guide to do this correctly.)
If you really don't want a keystore file, you could use the KeyStore API in memory and load the certificate directly.
Something along these lines should work (not tested):
InputStream is = new FileInputStream("cacert.crt");
// You could get a resource as a stream instead.
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate)cf.generateCertificate(is);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null); // You don't need the KeyStore instance to come from a file.
ks.setCertificateEntry("caCert", caCert);
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
(Remember to close everything and handle the exceptions.)
Whether loading the certificate this way or loading the certificate into a similar KeyStore instance from a keystore file is more convenient is up to you to decide.
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.