I need to make a HTTPS call from client. I do not need to send certificates to the server, just need to validate the certificate from the server.
i researched this topic and this is my understanding. Can you please confirm? I do not have a test service yet, against which to verify my code... but still need to meet deadlines.. any advice/input will be helpful.
I added this to my class:
private static String appKeyFile = "/project/src/security/cert_file.jck";
private static String key = "password";
static {
System.setProperty("javax.net.ssl.keyStore", appKeyFile);
System.setProperty("javax.net.ssl.keyStorePassword",key);
System.setProperty("javax.net.ssl.keyStoreType","JCEKS");
}
And am making the HTTPS call as follows:
config = new DefaultClientConfig();
client = Client.create(config);
service = client.resource(UriBuilder.fromUri("https://localhost:8081/TestService").build());
clientResponse = service.path("rs").path("test").path("getCustomerDetail")
.accept(MediaType.APPLICATION_XML)
.post(ClientResponse.class, customerRequestType);
if (clientResponse.getStatus() == Response.Status.OK.getStatusCode()) {
custResponseType = clientResponse.getEntity(CustResponseType.class);
System.out.println("First Name" +
custResponseType.getFirstName());
}
Is this sufficient from SSL/HTTPS/certs etc point of view (other than debugging)? Is there anything else i need to do,like loading the keystore or initializing the SSLContext?
The javax.net.ssl.keyStore* properties (the keystore) are for the keys and certificates of the party using it. That is, on the server, it should contain the server certificate and its private key; on the client, it should contain the client certificates and their private keys.
In contrast the truststore (javax.net.ssl.trustStore* properties) contains the trusted certificates used to validate the remote party's certificate. On the client, it's what's used to determine whether you trust the server certificate (normally, via a chain to a CA certificate trusted by the client); on the server, it's what's used to verify a client certificate.
Both truststore and keystore are keystore files/objects (the terminology doesn't really help).
If you set javax.net.ssl.keyStore* on the client side, it will be used by the client to present its certificate (which can only be requested by the server, and which you don't seem to be using anyway). It will still use the default truststore (shipped/configured with the JRE), and it's unlikely to contain the specific certificate in cert_file.jck (which is presumably a self-signed certificate you've generated for the server). Instead, set the javax.net.ssl.trustStore* properties to point to that file.
(If you want the default CA certificates to be available too, I'd suggest copying the certificates in the default truststore, usually from $JAVA_HOME/lib/security/jssecacerts or $JAVA_HOME/lib/security/cacerts into your own truststore.)
In case you are using self-signed certificate, you may face issues related to SSL Certificate validation. This link discusses this.
Related
So I'm wanting to set up an SSL server/client in Java, my knowledge with networking is not very good I've created normal servers/clients in java many times and wanted to up it and setup SSL so a user can't just send false packets to my client thinking its the server sending them.
I've looked up a few examples etc and came across this GitHub repo which shows a basic server with SSL encryption between the client and server and set it up in my IDE.
Here is the repo:
https://github.com/AlphaGarden/SSL-Client-Server
My question is why does the client and server use both certificates? Can't a user just get the certificates from the client and use them to decrypt the SSL? Also in the client there's 2 strings, password & password2... Am I supposed to hide these from the person using my client too? If not am I supposed to hide anything client sided from the user that could help them decrypt the SSL traffic and feed my client false information?
Just some basics to explain a SSL/TLS connection: Wkipedia https://en.wikipedia.org/wiki/Transport_Layer_Security, for TLS 1.2 RFC5246: https://www.rfc-editor.org/rfc/rfc5246 and for TLS 1.3 RFC8446 https://www.rfc-editor.org/rfc/rfc8446.
The basic principle for a secure connection is to use (each) a certificate on server and client's side and exchange them. The certificate itself is useless as it does not prove that you are whom you say to be. To get trust in the certificate the usual way is to "buy" a certificate from a Certificate Agency (CA) that checks your identity.
The CA's root certificate usually is known to today browsers and so the server and client certificate can get checked
by the browser (client) and server against the CA's root certificate.
Let us see the code for the simple SSL Server & Client code. I'm for sure you noticed that the server and client are using
"twisted" sources for the keystore and the truststore and therefore they need two (different) passwords to get access to the two stores:
SSLServer:
String password = "abcdefg";
InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("server/certificate-server.p12");
// TrustManagerFactory
String password2 = "aabbcc";
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX", "SunJSSE");
InputStream inputStream1 = ClassLoader.getSystemClassLoader().getResourceAsStream("client/certificate-client.p12");
SSLClient:
String password = "aabbcc";
InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("client/certificate-client.p12");
// TrustManagerFactory ()
String password2 = "abcdefg";
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX", "SunJSSE");
InputStream inputStream1 = ClassLoader.getSystemClassLoader().getResourceAsStream("server/certificate-server.p12");
What are the keystore and the truststore good for? The keystore holds the (own) private key (of server and client) and in the truststore (all) trusted certificates (usually the CA root certificates) are saved. To check the received certificate from the other party the server and client prove the certificate chain up to the root certificate from the CA.
But here is the problem - as the programs are using self signed certificates there is not "real" and saved root certificate available in the trust store. To get the programs to run without an user interaction ("do you trust this certificate ?") both provide use a truststore with the "approved" certifcates and all is running.
To your second question "Can't a user just get the certificates from the client and use them to decrypt the SSL" the answer is simple: YES. But when securing a communication only between "allowed" partners - how should e.g. the servers knows that the client is the real one and not an attacker? For that reason the client is sending a certificate as well that can get checked by the servers
truststore.
I am running the Java process (Microservice) and trustStore configured in the JVM parameter. If the Microservice needs to connect the external URL which required the cert to be imported in trustStore.
Example:
example.co.uk -> examplecouk as alias in trustStore
example.com -> examplecom as alias in trustStore
example.in -> examplein as alias in trustStore
How does Java know that which certs and alias to be picked from the trustStore for the particular endpoint as I don't pass/mention the alias in the JVM params? Is it pick randomly?
user207421 is nearly correct. To be more exact:
When you as client open an SSL/TLS connection to a server, as part of the handshake the server sends its certificate 'chain' containing its own cert, plus usually one or more linked CA (Certificate Authority) certificates ending with a 'root' CA that should be trusted. See the neighboring Stack https://security.stackexchange.com/questions/20803/how-does-ssl-work/ for an epically complete explanation. Public servers normally use a cert issued and signed by a public CA like Digicert, GoDaddy, LetsEncrypt/ISRG which are already in the standard default truststore (for Java in the file JRE/lib/security/cacerts) so no action is needed. If a server uses a cert from a off-brand or private CA, or a self-signed cert (from no CA at all), then (for Java) some cert in the chain must be added to the client truststore, or otherwise overridden; this is only required to be the server cert in the case where the server cert is self-signed (which is a chain by itself and has no relevant CA certs).
Java/JSSE implements this through an SSLContext which contains among other things a TrustManager, more specifically an X509ExtendedTrustManager, which is initialized from a truststore. You can create an SSLContext explicitly in code from any set of trusted certs (which doesn't even need to be from a file), or use the default context which uses the default truststore file, which defaults to the filename above unless overridden by a system property.
When the server cert chain is received it is passed to the context's TrustManager for validation; among (many!) other checks, at each level of a normal chain, or the single level of a self-signed cert, the JSSE TrustManager looks for an anchor cert with the same Subject and (Subject)PublicKey and if so uses it to validate the cert chain. Note that a normal (CA-issued) leaf cert can have Subject empty if Subject Alternative Name is used instead -- see rfc5280 and rfc2818 -- but a self-signed cert cannot, because it has Subject = Issuer and Issuer must not be empty. Certs for different entities (e.g. different servers) normally are expected to have different keys, although a single entity can have multiple certs with either the same key or different keys, and might correspond to multiple server names and/or addresses.
If the certificate is determined valid in general, for some TLS applications, notably HTTPS, the validator also checks it is for the correct server, specifically that the CommonName attribute in the Subject field, or an entry in the Subject Alternative Name extension if present -- which for public CAs for at least a decade it is, matches the host DNS name or IP adddress in the URL. In older versions of Java (through 6 IIRC) this was not done in JSSE but rather in the calling application(s) or library, such as HttpsURLConnection, which as a legacy still has the option to use its own HostnameVerifier instead.
All of this can be altered by using a custom TrustManager in place of the standard one, and some things like Apache HttpClient do so validly, but you will find (too many) answers here and some other Stacks recommending you 'solve' TLS errors by using a neutered TrustManager that just accepts any cert, regardless of whether it is actually valid and correct, and thus happily connects and sends sensitive data to, or accepts changes from, any attacker who manages to intercept the IP traffic, which is often pretty easy nowadays.
Alias is a way to directly access the certificate but your keystore has other information regarding the certificate as well. X.509 certificates have a field called SAN (Subject Alternative name) which contains the DNS information of the certificate. When you try to connect to a specific URL, the keystore is looked up for the corresponding DNS name in SAN and correct certificate is picked up.
I hope it clarifies your doubt about java not asking for the alias. Rest assured, there is nothing random in this process.
I have written a Java client in SAAJ which works successfully when sending SOAP messages over HTTP however when I attempt to send any SOAP messages to a web service over HTTPS that requires a client certificate it doesnt work.
At the bottom of the page at the following link - SAAJ Security - it states the following:
From the SAAJ side, all you need to do is use URLs with HTTPS as the protocol. This will work only if the certificate was successfully imported into /jre/lib/security/cacerts; otherwise JSSE will not allow the connection
I imported the client certificate along with the associated root certificate into my Java cacerts as instructed to do so above and ran the program however I get the following error:
java.net.SocketException: Connection reset
I ran a wireshark trace on the traffic and noticed that the Java code isnt presenting a client certificate when asked to do so by the server therefore I have the following questions:
1) By only passing the HTTPS URL to the soapConnection.call() method along with importing the certs to my cacerts file, is this enough for authentication to occur i.e. does SAAJ handle this automatically? Or are there more steps required that arent described in the above link?
2) By importing the certificates into the cacerts file within my JAVA_HOME, does the Java SAAJ client automatically know to look here when calling the soapConnection.call()? Or do I need to explicitly tell my code what cacerts file to use?
3) If the Java SAAJ client is automatically using the cacerts file under my JAVA_HOME then how does it know what client certificate to use? Again, shouldnt I need to explicitly code this or does SAAJ handle this automatically?
Thanks in advance
I managed to figure this out. I used the following code:
static public void doTrustToCertificates() throws Exception
{
// Set truststore that contains root / intermediary certs
System.setProperty("javax.net.ssl.trustStore", "C:\\cert\\trusted.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
// Set keystore that contains private key
File pKeyFile = new File("C:\\cert\\privatekey.pfx");
String pKeyPassword = "Password01";
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pKeyPassword.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
// Set ssl context with private key and truststore details
SSLContext sc = SSLContext.getInstance("TLSv1");
sc.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
SSLSocketFactory sockFact = sc.getSocketFactory();
// Add ssl context to https connection
HttpsURLConnection.setDefaultSSLSocketFactory(sockFact);
}
Then called the doTrustToCertificates() method just before the soapConnection.call() method of SAAJ and it worked like so:
doTrustToCertificates();
SOAPMessage soapResponse = soapConnection.call(soapMsgXml, ENDPOINT_URL);
You weren't so instructed. The instructions are misleading, but they only apply to the situation where the server certificate is self-signed, and not at all to the situation where the client needs a certificate.
In this case, you need to create a client certificate in your client's keystore. Either you create a CSR and get it signed and import that back into the same keystore using the same alias as you started with when you generated the keypair and CSR, or else you need to generate a self-signed certificate (yuck) and export it and get it imported into the server's truststore.
You don't need to write any code. All you need to do is set the system properties javax.net.ssl.keyStore and friends to tell JSSE about your client keystore.
I'm having an issue sending our certificate (client) to another server during a web service call.
We're expected to return the certificate with the CN:
b2b-test
however whenever we receive the servers certificate and attempt to send our own, we're getting:
Unable to find valid certificate path to requested target
From my understanding, if I put our certificate b2b-test inside of our keystore jre/lib/security/cacerts, then when receiving the server request, it should send the appropriate certificate.
How does the client know which key from the keystore it should send to the server? Should I be specifying the exact certificate to send during my request, or is it enough for it to be in the keystore?
Currently the server is sending back the certificate
b2b-a
The certificate chain shows:
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: CN=b2b-a, OU=Web Technologies
The URL for the service is:
https://b2b-a/service
If I configure it like one way ssl, and put the servers certificate into our cacerts, then it picks up their cert and fails the handshake (I'm assuming because it's expecting our cert and not theirs back due to it being a two way setup).
I'm a little stumped here. Any clarification on what I've said is greatly appreciated.
The "Unable to find valid certificate path to requested target" error usually occurs when one side passes a certificate for which the other side doesn't trust any of the certs in the chain. You'll generally (always?) see the error message on the side that's receiving the cert (the side that's missing the truststore).
If you're the client and they're the server, one-way SSL would send their cert from them to you and you would validate it (making sure that the hostname matches the content in the cert), but it would not send any cert from you to them nor would they do any validation.
You'll want to make sure you're configuring a truststore that contains either the b2b-a certificate or a certificate that was used to sign it (but that cert doesn't appear to have any root CAs, so you're stuck with directly importing the cert itself). So yes, you want to do what you wrote in your post and put their certificate into your cacerts. And you'll also need to make sure that the b2b-a service trusts the cert you're sending (or a root CA that was used to sign your cert, if any), so you'll need to put your cert into their cacerts as well, or do something equivalent.
Note that putting a cert in cacerts (or in a truststore JKS) DOES NOT send any certs to anyone; any truststore is used only to allow you to validate that you trust the certificate someone else provides you, either because you trust the cert or because you trust a CA cert that was used to sign the cert. No certs are picked out of the cacerts directory to send to another machine.
If you're using a JKS rather than the JRE directory (which is generally a better idea, since you can specify a different set of trusted certs for your process without changing the default set for anyone running a Java project within your JRE), it's possible to use the same JKS as both a keystore and a truststore (you'll just provide the same filename and password for both the keystore and the truststore properties), or you can have two separate files; either approach will work.
From my understanding, if I put our certificate b2b-test inside of our keystore jre/lib/security/cacerts, then when receiving the server request, it should send the appropriate certificate.
Certainly not. That's a truststore. A source of trusted certificates. Don't mess with it. It isn't used for the purpose you require and in any case it is updated every Java dot release, which clobbers your changes.
Your own certificate and private/public key pair should go in a file of your own, a keystore, which is defined to JSSE by the javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword system properties, or by more complex means described in the JSSE Reference Guide.
The keystore is a precious and confidential file containing your private key. Guard and protect it. Private key leakage would compromise your security and identity.
How do I perform an HTTP request and sign it with a X.509 certificate using Java?
I usually program in C#. Now, what I would like to do is something similar to the following, only in Java:
private HttpWebRequest CreateRequest(Uri uri, X509Certificate2 cert)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.ClientCertificates.Add(cert);
/* ... */
return request;
}
In Java I have created a java.security.cert.X509Certificate instance but I cannot figure out how to associate it to a HTTP request. I can create a HTTP request using a java.net.URL instance, but I don't seem to be able to associate my certificate with that instance (and I'm not sure whether using java.net.URL is even appropriate).
I'm not a C# programmer, but I'm presuming that code makes a call using HTTPS/TLS and provides a client certificate for authentication? Aka, you're not asking how to use WS-Security, right?
In that case, I think the answers here and here will be of use to you. You need to use an openssl utility to import your certificate into a p12 client keystore. If your server is using a non-standard CA or self-signed cert, you'll need to set up a client truststore with those certificates as well.
At this point, look at the questions I've linked: you'll need to specify a whole slew of JVM arguments. Finally, try to make your call again (using standard Java objects or httpclient). The client should accept the server cert if your truststore is correct and the server should ask for a client cert. If your keystore is set up correctly, the client with authenticate with the X.509 client cert and you'll be good to go.
It looks like you're trying to use HTTPS with client-certificate authentication. I'm assuming that your server is configured to request this (because the client certificate can only be requested by the server).
In Java, java.security.cert.X509Certificate is really just the certificate (a public key certificate, without the private key). What you need on the client side is to configure the private key with it.
Assuming that your private key and certificate are in a keystore (to simplify, I'm assuming there's only one suitable certificate with its private key, perhaps with other certificates in the chain, in that keystore), and that you're using the default trust store:
KeyStore ks = ...
/* load the keystore */
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, null);
URL url = new URL("https://example/");
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setSSLSocketFactory(sslContext.getSSLSocketFactory());
Other libraries will allow you to set the SSLContext or the KeyStore slightly differently, but the principles should be the same.
(You could also use the javax.net.ssl.keyStore system properties if it's appropriate.)
I would recommend the open source HttpClient library from Apache Commons. Covers this use case and many others.