I'm integrating cxf webservices,no spring, messages are signed. As a guide I've used this tutorial CXF Security.
I want to store x509 certifications or the keystore in the db.
I've overridden WSS4JOutInterceptor.loadCryptoFromPropertiesFile method.
#Override
protected Crypto loadCryptoFromPropertiesFile(String propFilename, RequestData reqData) throws WSSecurityException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(...);
X509Certificate certificate = (X509Certificate) ks.getCertificate("client");
X509Certificate[] certificates = { certificate};
CertificateStore cs = new CertificateStore(certificates);
return cs;
}
But a few issues here:
1. There's an error like
Caused by: java.lang.NullPointerException: provided null name at
javax.security.auth.x500.X500Principal.(X500Principal.java:172)
I've checked and subjectDN is not null
2. I'm not sure if this a way to go and should I attach somehow private key to sign the message
The error is because CertificationStore doesn't support aliases
The better way would be to implement custom provider by extending CryptoBase
Related
I am receiving the following String from a certificate stored in Azure Key Vault. I am using the Secret API in order to retrieve both the certificate and the private key related to this cert.
Initially the certificate was uploaded using a .pfx file to Azure Key vault. Now I need to create a Certificate and a PrivateKey to allow client authentication to a 3rd party system and I am using the given String retrieved from the API, however I am note sure how to get around that in Java.
I took some hints from this link in C# however I am pretty certain that this method doesn't work like that in Java. In particular an X509Certificate or a Certificate in general doesn't hold any information about the PrivateKey in Java, unlike C#, and I am not sure how to extract that information from given String in Java.
This works as expected to retrieve the certificate from the String retrieved from the API
String secret = azureSecret.getValue();
byte[] certkey = Base64.getDecoder().decode(secret);
ByteArrayInputStream inputStream = new ByteArrayInputStream(certkey);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(inputStream);
The azureSecret.getValue() format is like the following however I am not sure how to get PrivateKey out of the given String
MIIKvgIBaaZd6Euf3EYwYdHrIIKYzCC...
YES, Java X509Certificate and Certificate is only the certificate. Instead use KeyStore which can contain multiple entries each of which is either a 'trusted' certificate (for someone else), or a privatekey plus certificate plus other chain cert(s) (if applicable) for yourself, or (not relevant here) a 'secret' (symmetric) key. PKCS12 is supported as one type of KeyStore along with others not relevant here, so after the base64-decoding you already have do something like:
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(inputstreamfromvaultvalue, password);
// then
PrivateKey pkey = (PrivateKey) ks.getKey(alias, password);
// and
Certificate cert = ks.getCertificate(alias); // if you only need the leaf cert
// or
Certificate[] chain = ks.getCertificateChain(alias); // usually
But if you want to do client authentication in TLS/SSL (including HTTPS), you give the JSSE KeyManager the whole keystore object not the individual pieces (privatekey and certificates). Similarly to verify the peer in TLS/SSL, you give TrustManager a keystore containing trusted certificates, usually root CAs and often defaulted to a built-in set of public root CAs.
Im trying to validate self-signed certificate against self-signed root CA (given by service provider who will make calls to my service). Now, if i enable two way ssl all works fine but SSL requirement is globally enforced. I need to use it just for one request, and for one user. Other services should not be affected and since there is no way (unless im mistaken) of enabling it for specific paths, i deactivated two ssl and instead, in my controller im doing this:
String auth = request.getHeader("Authorization");
X509Certificate[] cert = (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
ResourceLoader resourceLoader = new DefaultResourceLoader();
try {
KeyStore trustStore = KeyStore.getInstance("JKS");
InputStream certInStream = resourceLoader.getClassLoader().getResourceAsStream("keystore/CaCertificate.jks");
trustStore.load(certInStream, "changeit".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
for (TrustManager tm: trustManagerFactory.getTrustManagers())
((X509TrustManager)tm).checkClientTrusted(cert, "RSA");
}
Thus my question is, is this method of validating certificate same as default?
Another thing is that unless i configure the following correctly:
server.ssl.trust-store=keystore/truststore.jks
server.ssl.trust-store-password=pass
server.ssl.trust-store-type= JKS
server.ssl.client-auth=want
then getAttribute method returns null. And i very much want to know why it happens. So the certificate still goes validation but request doesnt fail and its attrbute(SSL one) is not set if client certificate is not trusted? So essentially the code i wrote is useless and i can just check if following is null?
X509Certificate[] cert = (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
please help im thoroughly lost.
EDIT:
For testing purposes im using .net 4.5 HttpWebRequest. With this:
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) =>
{
return true;
}
A module I'm adding to our large Java application has to converse with another company's SSL-secured website. The problem is that the site uses a self-signed certificate. I have a copy of the certificate to verify that I'm not encountering a man-in-the-middle attack, and I need to incorporate this certificate into our code in such a way that the connection to the server will be successful.
Here's the basic code:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
Without any additional handling in place for the self-signed certificate, this dies at conn.getOutputStream() with the following exception:
Exception in thread "main" 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
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Ideally, my code needs to teach Java to accept this one self-signed certificate, for this one spot in the application, and nowhere else.
I know that I can import the certificate into the JRE's certificate authority store, and that will allow Java to accept it. That's not an approach I want to take if I can help; it seems very invasive to do on all of our customer's machines for one module they may not use; it would affect all other Java applications using the same JRE, and I don't like that even though the odds of any other Java application ever accessing this site are nil. It's also not a trivial operation: on UNIX I have to obtain access rights to modify the JRE in this way.
I've also seen that I can create a TrustManager instance that does some custom checking. It looks like I might even be able to create a TrustManager that delegates to the real TrustManager in all instances except this one certificate. But it looks like that TrustManager gets installed globally, and I presume would affect all other connections from our application, and that doesn't smell quite right to me, either.
What is the preferred, standard, or best way to set up a Java application to accept a self-signed certificate? Can I accomplish all of the goals I have in mind above, or am I going to have to compromise? Is there an option involving files and directories and configuration settings, and little-to-no code?
Create an SSLSocket factory yourself, and set it on the HttpsURLConnection before connecting.
...
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslFactory);
conn.setMethod("POST");
...
You'll want to create one SSLSocketFactory and keep it around. Here's a sketch of how to initialize it:
/* Load the keyStore that includes self-signed cert as a "trusted" entry. */
KeyStore keyStore = ...
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslFactory = ctx.getSocketFactory();
If you need help creating the key store, please comment.
Here's an example of loading the key store:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(trustStore, trustStorePassword);
trustStore.close();
To create the key store with a PEM format certificate, you can write your own code using CertificateFactory, or just import it with keytool from the JDK (keytool won't work for a "key entry", but is just fine for a "trusted entry").
keytool -import -file selfsigned.pem -alias server -keystore server.jks
I read through LOTS of places online to solve this thing.
This is the code I wrote to make it work:
ByteArrayInputStream derInputStream = new ByteArrayInputStream(app.certificateString.getBytes());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
String alias = "alias";//cert.getSubjectX500Principal().getName();
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null);
trustStore.setCertificateEntry(alias, cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(trustStore, null);
KeyManager[] keyManagers = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
URL url = new URL(someURL);
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
app.certificateString is a String that contains the Certificate, for example:
static public String certificateString=
"-----BEGIN CERTIFICATE-----\n" +
"MIIGQTCCBSmgAwIBAgIHBcg1dAivUzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE" +
"BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE" +
... a bunch of characters...
"5126sfeEJMRV4Fl2E5W1gDHoOd6V==\n" +
"-----END CERTIFICATE-----";
I have tested that you can put any characters in the certificate string, if it is self signed, as long as you keep the exact structure above. I obtained the certificate string with my laptop's Terminal command line.
If creating a SSLSocketFactory is not an option, just import the key into the JVM
Retrieve the public key:
$openssl s_client -connect dev-server:443, then create a file dev-server.pem that looks like
-----BEGIN CERTIFICATE-----
lklkkkllklklklklllkllklkl
lklkkkllklklklklllkllklkl
lklkkkllklk....
-----END CERTIFICATE-----
Import the key: #keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem.
Password: changeit
Restart JVM
Source: How to solve javax.net.ssl.SSLHandshakeException?
We copy the JRE's truststore and add our custom certificates to that truststore, then tell the application to use the custom truststore with a system property. This way we leave the default JRE truststore alone.
The downside is that when you update the JRE you don't get its new truststore automatically merged with your custom one.
You could maybe handle this scenario by having an installer or startup routine that verifies the truststore/jdk and checks for a mismatch or automatically updates the truststore. I don't know what happens if you update the truststore while the application is running.
This solution isn't 100% elegant or foolproof but it's simple, works, and requires no code.
I've had to do something like this when using commons-httpclient to access an internal https server with a self-signed certificate. Yes, our solution was to create a custom TrustManager that simply passed everything (logging a debug message).
This comes down to having our own SSLSocketFactory that creates SSL sockets from our local SSLContext, which is set up to have only our local TrustManager associated with it. You don't need to go near a keystore/certstore at all.
So this is in our LocalSSLSocketFactory:
static {
try {
SSL_CONTEXT = SSLContext.getInstance("SSL");
SSL_CONTEXT.init(null, new TrustManager[] { new LocalSSLTrustManager() }, null);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to initialise SSL context", e);
} catch (KeyManagementException e) {
throw new RuntimeException("Unable to initialise SSL context", e);
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
LOG.trace("createSocket(host => {}, port => {})", new Object[] { host, new Integer(port) });
return SSL_CONTEXT.getSocketFactory().createSocket(host, port);
}
Along with other methods implementing SecureProtocolSocketFactory. LocalSSLTrustManager is the aforementioned dummy trust manager implementation.
I am trying to load & use a self-signed, dynamically generated PEM (or DER) X.509 certificate from a file and use that to load content via HTTPClient or HttpsURLConnection in order to inject it into a WebView.
I have searched for how to use a WebView with a custom certificate, but apparently there is only the possibility to ignore the SSL error completely (which is not what I want to do - I am looking for certificate validation).
Coding along the lines of this Android developers training I always get exceptions from the key functions: TrustManagerFactory, KeyStore and CertificateFactory.
For TrustManagerFactory:
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
-> Unhandled exception type NoSuchAlgorithmException in getInstance()
For KeyStore I tried the syntax from the tutorial as well as syntax as described in the documentation:
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
-> Unhandled exception type KeyStoreException in getInstance()
and
//Init with empty KeyStore
KeyStore keyStoreData;
//Use no protection for key store since it contains public cert
KeyStore.ProtectionParameter keyStoreProtect = null;
//Use default type
String keyStoreType = KeyStore.getDefaultType();
KeyStore.Builder builder = KeyStore.Builder.newInstance(keyStoreType, null, keyStoreProtect);
keyStoreData = builder.getKeyStore();
-> Unhandled exception type KeyStoreException in getKeyStore()
For CertificateFactory:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
-> Unhandled exception type CertificateException
In conclusion, I cannot use any of the functions required for the job because they all fail with exceptions even though I am using default tutorial code (which should be still valid according to documentation).
Switching the IDE (tried Android Studio and Eclipse with ADT), switching from OpenJDK to Oracle JDK, nothing seems to help here.
Also, similar questions on StackOverflow and on the Net are all answered with the use of one or more of these functions - so they don't help me.
If anybody knows what I am doing wrong, I would be glad for any help!
A module I'm adding to our large Java application has to converse with another company's SSL-secured website. The problem is that the site uses a self-signed certificate. I have a copy of the certificate to verify that I'm not encountering a man-in-the-middle attack, and I need to incorporate this certificate into our code in such a way that the connection to the server will be successful.
Here's the basic code:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
Without any additional handling in place for the self-signed certificate, this dies at conn.getOutputStream() with the following exception:
Exception in thread "main" 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
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Ideally, my code needs to teach Java to accept this one self-signed certificate, for this one spot in the application, and nowhere else.
I know that I can import the certificate into the JRE's certificate authority store, and that will allow Java to accept it. That's not an approach I want to take if I can help; it seems very invasive to do on all of our customer's machines for one module they may not use; it would affect all other Java applications using the same JRE, and I don't like that even though the odds of any other Java application ever accessing this site are nil. It's also not a trivial operation: on UNIX I have to obtain access rights to modify the JRE in this way.
I've also seen that I can create a TrustManager instance that does some custom checking. It looks like I might even be able to create a TrustManager that delegates to the real TrustManager in all instances except this one certificate. But it looks like that TrustManager gets installed globally, and I presume would affect all other connections from our application, and that doesn't smell quite right to me, either.
What is the preferred, standard, or best way to set up a Java application to accept a self-signed certificate? Can I accomplish all of the goals I have in mind above, or am I going to have to compromise? Is there an option involving files and directories and configuration settings, and little-to-no code?
Create an SSLSocket factory yourself, and set it on the HttpsURLConnection before connecting.
...
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslFactory);
conn.setMethod("POST");
...
You'll want to create one SSLSocketFactory and keep it around. Here's a sketch of how to initialize it:
/* Load the keyStore that includes self-signed cert as a "trusted" entry. */
KeyStore keyStore = ...
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslFactory = ctx.getSocketFactory();
If you need help creating the key store, please comment.
Here's an example of loading the key store:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(trustStore, trustStorePassword);
trustStore.close();
To create the key store with a PEM format certificate, you can write your own code using CertificateFactory, or just import it with keytool from the JDK (keytool won't work for a "key entry", but is just fine for a "trusted entry").
keytool -import -file selfsigned.pem -alias server -keystore server.jks
I read through LOTS of places online to solve this thing.
This is the code I wrote to make it work:
ByteArrayInputStream derInputStream = new ByteArrayInputStream(app.certificateString.getBytes());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
String alias = "alias";//cert.getSubjectX500Principal().getName();
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null);
trustStore.setCertificateEntry(alias, cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(trustStore, null);
KeyManager[] keyManagers = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
URL url = new URL(someURL);
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
app.certificateString is a String that contains the Certificate, for example:
static public String certificateString=
"-----BEGIN CERTIFICATE-----\n" +
"MIIGQTCCBSmgAwIBAgIHBcg1dAivUzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE" +
"BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE" +
... a bunch of characters...
"5126sfeEJMRV4Fl2E5W1gDHoOd6V==\n" +
"-----END CERTIFICATE-----";
I have tested that you can put any characters in the certificate string, if it is self signed, as long as you keep the exact structure above. I obtained the certificate string with my laptop's Terminal command line.
If creating a SSLSocketFactory is not an option, just import the key into the JVM
Retrieve the public key:
$openssl s_client -connect dev-server:443, then create a file dev-server.pem that looks like
-----BEGIN CERTIFICATE-----
lklkkkllklklklklllkllklkl
lklkkkllklklklklllkllklkl
lklkkkllklk....
-----END CERTIFICATE-----
Import the key: #keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem.
Password: changeit
Restart JVM
Source: How to solve javax.net.ssl.SSLHandshakeException?
We copy the JRE's truststore and add our custom certificates to that truststore, then tell the application to use the custom truststore with a system property. This way we leave the default JRE truststore alone.
The downside is that when you update the JRE you don't get its new truststore automatically merged with your custom one.
You could maybe handle this scenario by having an installer or startup routine that verifies the truststore/jdk and checks for a mismatch or automatically updates the truststore. I don't know what happens if you update the truststore while the application is running.
This solution isn't 100% elegant or foolproof but it's simple, works, and requires no code.
I've had to do something like this when using commons-httpclient to access an internal https server with a self-signed certificate. Yes, our solution was to create a custom TrustManager that simply passed everything (logging a debug message).
This comes down to having our own SSLSocketFactory that creates SSL sockets from our local SSLContext, which is set up to have only our local TrustManager associated with it. You don't need to go near a keystore/certstore at all.
So this is in our LocalSSLSocketFactory:
static {
try {
SSL_CONTEXT = SSLContext.getInstance("SSL");
SSL_CONTEXT.init(null, new TrustManager[] { new LocalSSLTrustManager() }, null);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to initialise SSL context", e);
} catch (KeyManagementException e) {
throw new RuntimeException("Unable to initialise SSL context", e);
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
LOG.trace("createSocket(host => {}, port => {})", new Object[] { host, new Integer(port) });
return SSL_CONTEXT.getSocketFactory().createSocket(host, port);
}
Along with other methods implementing SecureProtocolSocketFactory. LocalSSLTrustManager is the aforementioned dummy trust manager implementation.