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.
Related
I've been at this way too long and have searched high and low for a solution.
I am trying to do mutual SSL. The target is using a certificate signed by an intermediate that is valid and trusted. My side, a Java web service client using httpclient 4.3.6, is also using a certificate signed by the intermediate.
The intermediate is in a JKS format keystore and has the full chain. My certificate is also in a JSK keystore.
I am also running Wireshark to follow the SSL process, since the Java exceptions are very unhelpful. I can see that the Server Hello finishes (a line in Wireshark called "Server Hello, Certificate, Server Key Exchange, Certificate Request, Server Hello Done"). A couple of packets later, there is a "Certificate, Client Key Exchange" line that seems to run ok. Two packets later, there is an alert about a handshake failure (40). When I go backward two packets to the "Certificate, Client Key Exchange" line and inspect it closely, I can see that while the key was sent off, there is a line that says "Certificates Length: 0" in the packet inspection window.
So it appears that my certificate is not even being sent? I saw on another question here that it may be because my side doesn't see a valid path to my cert, and so doesn't bother including it? (The answer here https://stackoverflow.com/a/14876605/5136448). I do include the trust store that has my intermediate in it when using SSLContext and SSLConnectionSocketFactory.
A colleague of mine got a successful connection using openssl when including my keystore and intermediate together, so I'm trying to add the certificate chain from the intermediate keystore to my own keystore to see if that will solve it. I have no found any successful way of doing this (openssl, keytool, Keystore Explorer), so I may not have the right idea as to properly solve this.
To sum up, using Apache http-client 4.3.6, I need to do mutual SSL to a server where the server is trusted, but the client doesn't seem to be sending its certificate (which was signed by the same entity that signed the server's certificate).
Should it be relevant, here is the client-side code:
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream(new File("/path/to/test.keystore")), "abc123".toCharArray());
KeyStore trustStore = KeyStore.getInstance("PKCS12");
trustStore.load(new FileInputStream(new File("/path/to/test.truststore")), "123abc".toCharArray());
SSLContext sslContext = SSLContexts
.custom()
.loadKeyMaterial(keystore, "abc123".toCharArray(), null)
.loadTrustMaterial(trustStore, null)
.build();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
new String[]{"TLSv1.2"},
null,
SSLConnectionSocketFactory.STRICT_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient1 = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
I'm working on encrypting a tcp connection between a server and a client. In the course of research and testing I'm leaning towards using secret key encryption. My problem is that I cannot find any tutorials on how to implement this feature. The tutorials I have found revolve around one-shot https requests, all I need is a SSL Socket.
The code I've written so far is below. I'm almost certain that it needs to be extended, I just don't know how. Any help is appreciated.
private ServerSocketFactory factory;
private SSLServerSocket serverSocket;
factory = SSLServerSocketFactory.getDefault();
serverSocket = (SSLServerSocket) factory.createServerSocket( <portNum> );
Server code for accepting client connections
SSLSocket socket = (SSLSocket) serverSocket.accept();
socket.startHandshake();
I just don't know how to actually do the handshake.
reference: http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html
SSL socket connections are well supported in Java and are likely a good choice for you. The one thing to understand in advance is that SSL provides both encryption and server authentication; you can't easily get just the encryption. For reference, the encryption protects against network eavesdropping, while the server authentication protects against "man in the middle" attacks, where the attacker acts as a proxy between the client and the server.
Since authentication is an integral part of SSL, the server will need to provide an SSL certificate, and the client will need to be able to authenticate the certificate. The server will need a "key store" file where its certificate is stored. The client will need a "trust store" file where it stores the certificates it trusts, one of which must either be the server's certificate, or a certificate from which a "chain of trust" can be traced to the server's certificate.
Note that you do not have to know anything about the ins and outs of SSL in order to use Java SSL sockets. I do think it is interesting to read through information on how SSL works, for example in the Wikipedia article on TLS, but the complicated multistep handshake and the setup of the actual connection encryption is all handled under the covers by the SSLServerSocket and SSLSocket classes.
The code
All of the above is just background information to explain some of the following code. The code assumes some familiarity with regular, unencrypted sockets. On the server, you will need code like this:
/**
* Returns an SSLServerSocket that uses the specified key store file
* with the specified password, and listens on the specified port.
*/
ServerSocket getSSLServerSocket(
File keyStoreFile,
char[] keyStoreFilePassword,
int port
) throws GeneralSecurityException, IOException {
SSLContext sslContext
= SSLConnections.getSSLContext(keyStoreFile, keyStoreFilePassword);
SSLServerSocketFactory sslServerSocketFactory
= sslContext.getServerSocketFactory();
SSLServerSocket sslServerSocket
= (SSLServerSocket) sslServerSocketFactory.createServerSocket(port);
return sslServerSocket;
}
The SSLServerSocket can then be used exactly like you would use any other ServerSocket; the authentication, encryption and decryption will be completely transparent to the calling code. In fact, the cognate function in my own code declares a return type of just plain ServerSocket, so the calling code can't get confused.
Note: if you want to use the JRE's default cacerts file as your key store file, you can skip the line creating the SSLContext, and use ServerSocketFactory.getDefault() to get the ServerSocketFactory. You will still have to install the server's public/private key pair into the key store file, in this case the cacerts file.
On the client, you will need code like this:
SSLSocket getSSLSocket(
File trustStoreFile,
char[] trustStoreFilePassword,
InetAddress serverAddress,
port serverPort
) throws GeneralSecurityException, IOException {
SSLContext sslContext
= SSLConnections.getSSLContext(trustStoreFile, trustStoreFilePassword);
SSLSocket sslSocket
= (SSLSocket) sslContext.getSocketFactory().createSocket
(serverAddress, serverPort);
sslSocket.startHandshake();
return sslSocket;
}
As in the case of the SSLServerSocket in the server code, the returned SSLSocket here is used just like a regular Socket; I/O into and out of the SSLSocket is done with unencrypted data, and all the cryptographic stuff is done inside.
As with the server code, if you want to use the default JRE cacerts file as your trust store, you can skip creation of the SSLContext and use SSLSocketFactory.getDefault() instead of sslContext.getSocketFactory(). In this case, you will only need to install the server's certificate if the server's certificate was self signed or not otherwise issued by one of the major certificating authorities. In addition, to ensure you aren't trusting a certificate that is legitimately issued within a certificate chain you trust, but to an entirely different domain than you are trying to get to, you should add the following (untested) code just after the line where you create the SSLSocket:
sslSocket.getSSLParameters().setEndpointIdentificationAlgorithm("HTTPS");
This would also apply if you are using your own trust store file, but trusting all certificates issued by one or more certificating authorities in that file, or trusting a number of certificates for different servers in that file.
Certificates, key stores, and trust stores
Now for the hard, or at least, slightly harder part: generating and installing the certificates. I recommend using the Java keytool, preferably version 1.7 or above, to do this work.
If you are creating a self signed certificate, first generate the server's keypair from the command line with a command like the following: keytool -genkey -alias server -keyalg rsa -dname "cn=server, ou=unit, o=org, l=City, s=ST, c=US" -validity 365242 -keystore server_key_store_file -ext san=ip:192.168.1.129 -v. Substitute your own names and values. In particular, this command creates a key pair that expires in 365242 days - 1000 years - for a server that will be at IP address 192.168.1.129. If the clients will be finding the server through the domain name system, use something like san=dns:server.example.com instead of san=ip:192.168.1.129. For more information on keytool options, use man keytool.
You will be prompted for the key store's password - or to set the key store's password, if this is a new key store file - and to set the password for the new key pair.
Now, export the server's certificate using keytool -export -alias server -file server.cer -keystore server_key_store_file -rfc -v. This creates a server.cer file containing the certificate with the server's public key.
Finally, move the server.cer file to the client machine and install the certificate into the client's trust store, using something like, keytool -import -alias server -file server.cer -keystore client_trust_store_file -v. You will be prompted for the password to the trust store file; the prompt will say "Enter keystore password", since the Java keytool works with both key store files and trust store files. Note that if you are using the default JRE cacerts file, the initial password is changeit, I believe.
If you are using a certificate purchased from a generally recognized certificating authority, and you're using the default JRE cacerts file on the client, you only have to install the certificate in the server's key store file; you don't have to mess with the client's files. Server installation instructions should be provided by the certificating authority, or again you can check man keytool for instructions.
There's a tremendous amount of mystique surrounding sockets and, especially, SSL sockets, but they're actually quite easy to use. In many cases, ten lines of code will avoid the need for complex and fragile messaging or message queueing infrastructure. Good for you for considering this option.
You've started the handshake. That's all you have to do: in fact you don't even have to do that, as it will happen automatically. All you have to do now is normal input and output, same as you would with a plaintext socket.
Requirment:
Need to write a web service client to interact with server using the SSL encryption.
Provided:
provided with the key store file and the server cert.
Coding:
Have written a java code, forming the soap request.For adding(signing the soap) the encryption data, used the key store to get the public key using the certificates and encrypted with digital signature algorithm.
Assumption In steps:
signing the soap message using the key store and sending to the server.
trust store(with certificates) by Certified authority validates the request and process the request.
signing the response with the same key store(used by the client) in the server side to send to the client.
client receives the response and validates using the trust store(with certificates) by Certified authority in the /JAVA_HOME/jre/lib/security/cacerts.
In this case, do we need to import the server.cert file from the requirement to the /JAVA_HOME/jre/lib/security/cacerts.?
from which the response is validated?
Please correct me if i am wrong at any step.
If your server and client already trusts Certificate Authority(CA) that signing your certificates, then you dont have to import your server/client certificates to each others trust stores. It should work normally because your signed CA is already trusted by client and Server.
If CA is not trusted by client/server, then there are two ways to solve this:
1) import CA certificate to client/server trust stores, so that any certificate signed by this CA are trusted by both client/server.
2) OR import server.cert to client trust store(in your case /JAVA_HOME/jre/lib/security/cacerts) and client.cert to sever trust store for 2-way validation.
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.
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.