So I have some of the ssl server and client side code. I'm not sure what to put in some of the methods though.
public void client() throws UnknownHostException, IOException{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream stream = new FileInputStream(new File("")); // need correct file
keyStore.load(stream, "Some Password".toCharArray());
// load in the appropriate keystore and truststore for the client
// get the X509KeyManager and X509TrustManager instances
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance("PKIX", "SunJSSE");
trustManagerFactory.init("NOT SURE WHAT TO PUT HERE");
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[]{"NOT SURE WHAT TO PUT HERE"},
new TrustManager[]{"NOT SURE WHAT TO PUT HERE"}, null);
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
SSLSocket socket =
(SSLSocket) socketFactory.createSocket("localhost", 25500);
socket.setEnabledProtocols(new String[]{"TLSv1"});
// read from the socket, etc
}
public void server() throws IOException{
// load in the appropriate keystore and truststore for the server
// get the X509KeyManager and X509TrustManager instances
SSLContext sslContext = SSLContext.getInstance("TLS");
// the final null means use the default secure random source
sslContext.init(new KeyManager[]{"NOT SURE WHAT TO PUT HERE"},
new TrustManager[]{"NOT SURE WHAT TO PUT HERE"}, null);
SSLServerSocketFactory serverSocketFactory =
sslContext.getServerSocketFactory();
SSLServerSocket serverSocket =
(SSLServerSocket) serverSocketFactory.createServerSocket(25500);
serverSocket.setNeedClientAuth(true);
// prevent older protocols from being used, especially SSL2 which is insecure
serverSocket.setEnabledProtocols(new String[]{"TLSv1"});
// you can now call accept() on the server socket, etc
}
Also, how do I generate certificates in java "like the code" and print it out to a file and have it use the same certificate over and over again.
Thx for any help.
Related
I have the client.p12 file and MyPassword, I am trying to establish the websocket connection using Netty code available over here. Currently I have the working example in OkHttpClient. But I am having a hard time to map that into netty.
My server gave me this domain to connect to "https://api.server.com"
In OkHttpClient the following code works
OkHttpClient client = getClient(info);
Request request = new Request.Builder().url("https://api.server.com" + "/messaging").build();
WebSocket webSocket = client.newWebSocket(request, listener);
Here the getClient code is following:
public static OkHttpClient getClient(ConnectionInfo info) {
KeyStore appKeyStore = KeyStore.getInstance("PKCS12");
appKeyStore.load(new FileInputStream("client.p12"), "MyPassword".toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(appKeyStore, info.getPassword().toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException(
"Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] {trustManager}, null);
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
OkHttpClient.Builder builder =
new OkHttpClient.Builder().sslSocketFactory(context.getSocketFactory(), trustManager);
builder.retryOnConnectionFailure(true);
return builder.build();
}
Now that code above works fine, I am trying to implement this in Netty. So looking at example code it only accepts the protocols ws and wss. While in the above example The HTTPS requests Upgraded to WebSocket using the appropriate headers. So my understanding is that If I provide the domain name as "wss:////api.server.com/messaging" Then it will first establish the https connection and then upgrade it to WebSocket.
Now I am not sure how to set the certificate and password.
// I have created a keyStore as following
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File("client.p12"));
try {
keyStore.load(instream, "MyPassword".toCharArray());
} finally {
instream.close();
}
final boolean ssl = "wss".equalsIgnoreCase(scheme);
final SslContext sslCtx;
if (ssl) {
// How to specify the above keystore with this client?
sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
} else {
sslCtx = null;
}
SSlContextBuilder has a method that takes a KeyManagerFactory:
SslContextBuilder.forClient()
.keyManager(keyManagerFactory)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
I'm trying to give server "A" the ability to connect to server "B" using the same X509 client certificate it received from the user. Here are the basics of where I am so far:
public int makeRemoteCall() {
URL url = new URL("https://host.com/service/request");
HttpsURLConnection conn = url.openConnection();
SSLSocketFactory factory = getFactoryFromSessionCert();
conn.setSSLSocketFactory(factory);
int responseCode = conn.getResponseCode();
return responseCode;
}
public static SSLSocketFactory getFactoryFromSessionCert() throws Exception {
HttpServletRequest request = getRequest();
X509Certificate[] certs = (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, null);
keyStore.setCertificateEntry("client_cert", certs[0]);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, null);
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, null);
return context.getSocketFactory();
}
I am able to retrieve the client's certificate without trouble, and can verify that it does indeed end up in keyStore. But the certificate doesn't seem to make it into keyManagerFactory.
I thought the issue was that I'm not providing a password in keyManagerFactory.init(keyStore, null), so I tried providing it but without success. And should I even have to? I understand that I would need a password if I were loading certificates and keys from a protected file, but here I'm just trying to pass along an already exposed public certificate.
As further background, this basic scheme works if I replace getFactoryFromSessionCert() with this:
public static SSLSocketFactory getFactory(File pKeyFile, String pKeyPassword) throws Exception {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pKeyPassword.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
return context.getSocketFactory();
}
So, what am I not understanding? And how should I pass along a client certificate?
There are three hosts that an android app do the authentication and authorization. Final host is the REST API. For the first time using Oauth authentication and authorization process it works without issue.
But if user kills the app after login and accessing the services provided by REST API and then again open the app, this issue arise. In this time authentication and authorization process is not happening, only the REST API. It caused to java.security.cert.CertPathValidatorException but it was working during the first use (login and then use the app).
Can someone explains the scenario behind this exception and whats wrong with the app. This works if certification exceptions are ignored as bellow according to this SO answer.
SSLSocketFactory sslSocketFactory = null;
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
// Initialise the TMF as you normally would, for example:
try {
tmf.init((KeyStore)null);
} catch(KeyStoreException e) {
e.printStackTrace();
}
TrustManager[] trustManagers = tmf.getTrustManagers();
final X509TrustManager origTrustmanager = (X509TrustManager)trustManagers[0];
// Create a trust manager that does not validate certificate chains
TrustManager[] wrappedTrustManagers = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return origTrustmanager.getAcceptedIssuers();
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
try {
origTrustmanager.checkClientTrusted(certs, authType);
} catch(CertificateException e) {
e.printStackTrace();
}
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
try {
origTrustmanager.checkServerTrusted(certs, authType);
} catch(CertificateException e) {
e.printStackTrace();
}
}
}
};
//TrustManager[] trustAllCerts = TrustManagerFactory.getInstance("SSL").getTrustManagers();
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, wrappedTrustManagers, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
sslSocketFactory = sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return sslSocketFactory;
I am using Okhttp 3 for the http requests. Any suggestion would help to solve the issue. And please let me know if I use above code snippet, is it a security violation? will it effect to the security of the app?
I am answering to this to give an idea about the scenario and solution as per the android developer site for others benefit. I have solved this using custom trust manager.
The problem was with the server certificate, it misses intermediate certificate authority. However with the first flow certificate path is completed somehow and result was successful certificate path validation.
There is a solution for this in android developer site. it suggest to use custom trust manager that trusts this server certificate or it suggest to server to include the intermediate CA in the server chain.
custom trust manager. source: https://developer.android.com/training/articles/security-ssl.html#UnknownCa
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the okhttp to use a SocketFactory from our SSLContext
OkHttpClient okHttpClient client = new OkHttpClient.Builder().sslSocketFactory(context.getSocketFactory()).build();
UPDATE: My problem was solved after intermediate certificate authority added to the certificate chain from the server side. It is the best solution, Bundling the certificate with the app requires app to be updated on certificate expiring or any other issues related with certificate management.
UPDATE:03/09/2017 Easiest way to load certificate file I found is use of raw resource.
InputStream caInput = new BufferedInputStream(context
.getResources().openRawResource(R.raw.certfilename));
where certfilename is the certificate file placed in resources/raw folder. Also okhttp's sslSocketFactory(SSLSocketFactory sslSocketFactory) has been deprecated and suggested approach in the okhttp api doc can be used.
Also when getting the certificate from the server it is better to use openssl.
openssl s_client -connect {server-address}:{port} -showcerts
Because I used to grab that from firefox and faced situation where it was altered by the virus guard.
Paste your cert.pem in raw folder
Create a method
private SSLSocketFactory getSSLSocketFactory(){
try {
CertificateFactory cf;
cf = CertificateFactory.getInstance("X.509");
Certificate ca;
InputStream cert = context.getResources().openRawResource(R.raw.cert);
ca = cf.generateCertificate(cert);
cert.close();
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
}
catch (Exception e){
return null;
}
}
Call like this
final OkHttpClient client = new OkHttpClient();
//pass getSSLSocketFactory() in params
client.setSslSocketFactory(getSSLSocketFactory());
String appURl = context.getString(R.string.apis_app_url);
final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(appURl).setClient(new OkClient(client)).
build();
I read about the SSLSocket when i had already finished a Chat program with java that use normal ServerSocket.
I am trying to replace the normal ServerSocket with SSlSocket, there is not much on the internet but i found something.
Now my WhServer class look like this:
This class is the one which start the Socket in a selected port, if you need to see other classes i will edit the question:
import java.io.IOException;
import java.net.*;
import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class WhServer extends Thread {
private int port;
private ServerSocket server;
private ChannelsManager manager;
SSLContext context;
SSLSocketFactory sslSf;
public WhServer(int port, ChannelsManager manager) throws IOException {
this.port = port;
this.manager = manager;
}
public void ServerStop() throws IOException{
server.close();
}
public WhServer(int port) throws IOException {
this(port, new ChannelsManager());
}
public int getPort() {
return port;
}
public void run() {
try {
while(true) {
ServerSocketFactory ssf = ServerSocketFactory.getDefault();
server = ssf.createServerSocket(port);
Socket socket = server.accept();
sslSf = context.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) sslSf.createSocket(socket, null,socket.getPort(), false);
sslSocket.setUseClientMode(false);
manager.initialite(socket);
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
Firstly, your SSLContext context instance variable is never initialised, so it is null. Nothing specific to SSL or sockets here, it's just a basic Java error: if you try to call anything on this, it will throw an NPE.
Secondly, even if it's not null (for example, you can create a new instance with context = SSLContext.getInstance("TLS"), see SSLContext section of the Java Cryptography Architecture Standard Algorithm Name Documentation as indicated in the SSLContext API doc), you still need to initialise the SSLContext via its init method.
Since you're trying to implement a server, you'll need to provide a non-null keymanager, otherwise you'll get an SSLHandshakeException saying "no cipher suites in common". You can find details about this in this answer for example.
In addition, you don't need to use plain Sockets and upgrade them to SSLSockets after accepting like you do. It's not necessarily wrong, but the following might be easier:
// Assuming you've initialised your SSLContext
SSLServerSocketFactory sslSf = context.getServerSocketFactory();
SSLServerSocket server = (SSLServerSocket) sslSf.createServerSocket(port);
SSLServerSocketFactory ssf = ServerSocketFactory.getDefault();
server = ssf.createServerSocket(port);
SSLSocket socket = (SSLSocket)server.accept();
Your socket coming from an SSLServerSocketFactory will already be in server mode.
Of course, there's generally no need for the factories to be within the while loop.
context is null. nowhere in your code it is being initialized.
Here's a few static methods you can use to initialize it.
http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLContext.html
static SSLContext getInstance(String protocol)
Returns a SSLContext object that implements the specified secure socket protocol.
static SSLContext getInstance(String protocol, Provider provider)
Returns a SSLContext object that implements the specified secure socket protocol.
static SSLContext getInstance(String protocol, String provider)
Returns a SSLContext object that implements the specified secure socket protocol.
Some valid values for the protocol string are "SSL", "SSLv2", "SSLv3"...
So, first of all, if you intend to keep that "context" variable as a member variable, make it final and initialize it in your constructor like this:
public WhServer(int port, ChannelsManager manager) throws IOException {
this.port = port;
this.manager = manager;
try {
context = SSLContext.getInstance("SSL"); //pick the SSL protocol you need.
} catch (Throwable t) { t.printStackTrace(); }
}
First you need to create SSLContext with below code:
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("test.jks"),"passphrase".toCharArray());
// Create key manager
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, "passphrase".toCharArray());
KeyManager[] km = keyManagerFactory.getKeyManagers();
// Create trust manager
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(keyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
// Initialize SSLContext
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(km, tm, null);
Replace the test.jks with your own keystore location.
To understand the SSL communication model in Java, you can refer to Java Secure Socket Extension (JSSE) Reference Guide.
A HTTPS client and HTTPS server demo in Java provides a quite demo on how to create SSL client and SSL server in Java.
I have a Client Server Communication scenario in JBOSS and browser as client(JAVA PROGRAM). Initially when the connection is made, Client sends its Certificate to Server. Server extracts the public key of client from certificate and thus communication will continue.
Now my question is
How to send certificate(.cer) from Client to Server?
How to receive the certificate and extract its public key in Server?
How to send certificate(.cer) from Client to Server?
Client certificate (.cer, .crt, .pem) and it's corresponding private key (.key) should be packaged into PKCS#12 (.p12, .pfx) or JKS (.jks) container first (keystore). You also should have server's CA certicate packaged as JKS (truststore).
Example using HttpClient 3.x:
HttpClient client = new HttpClient();
// truststore
KeyStore trustStore = KeyStore.getInstance("JKS", "SUN");
trustStore.load(TestSupertype.class.getResourceAsStream("/client-truststore.jks"), "amber%".toCharArray());
String alg = KeyManagerFactory.getDefaultAlgorithm();
TrustManagerFactory fac = TrustManagerFactory.getInstance(alg);
fac.init(trustStore);
// keystore
KeyStore keystore = KeyStore.getInstance("PKCS12", "SunJSSE");
keystore.load(X509Test.class.getResourceAsStream("/etomcat_client.p12"), "etomcat".toCharArray());
String keyAlg = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyFac = KeyManagerFactory.getInstance(keyAlg);
keyFac.init(keystore, "etomcat".toCharArray());
// context
SSLContext ctx = SSLContext.getInstance("TLS", "SunJSSE");
ctx.init(keyFac.getKeyManagers(), fac.getTrustManagers(), new SecureRandom());
SslContextedSecureProtocolSocketFactory secureProtocolSocketFactory = new SslContextedSecureProtocolSocketFactory(ctx);
Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) secureProtocolSocketFactory, 8443));
// test get
HttpMethod get = new GetMethod("https://127.0.0.1:8443/etomcat_x509");
client.executeMethod(get);
// get response body and do what you need with it
byte[] responseBody = get.getResponseBody();
You may find working example in this project see X509Test class.
With HttpClient 4.x configuration and syntax would be slightly different:
HttpClient httpclient = new DefaultHttpClient();
// truststore
KeyStore ts = KeyStore.getInstance("JKS", "SUN");
ts.load(PostService.class.getResourceAsStream("/truststore.jks"), "amber%".toCharArray());
// if you remove me, you've got 'javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated' on missing truststore
if(0 == ts.size()) throw new IOException("Error loading truststore");
// tmf
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);
// keystore
KeyStore ks = KeyStore.getInstance("PKCS12", "SunJSSE");
ks.load(PostService.class.getResourceAsStream("/" + certName), certPwd.toCharArray());
// if you remove me, you've got 'javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated' on missing keystore
if(0 == ks.size()) throw new IOException("Error loading keystore");
// kmf
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, certPwd.toCharArray());
// SSL
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
// socket
SSLSocketFactory socketFactory = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Scheme sch = new Scheme("https", 8443, socketFactory);
httpclient.getConnectionManager().getSchemeRegistry().register(sch);
// request
HttpMethod get = new GetMethod("https://localhost:8443/foo");
client.executeMethod(get);
IOUtils.copy(get.getResponseBodyAsStream(), System.out);
How to receive the certificate and extract its public key in Server?
You server must be configurated to require X.509 client certificate authentication. Then during SSL handshake servlet container will recieve certificate, check it against trustore and provide it to application as a request attribute.
In usual case with single certificate you could use this method in servlet environment to extract certificate:
protected X509Certificate extractCertificate(HttpServletRequest req) {
X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
if (null != certs && certs.length > 0) {
return certs[0];
}
throw new RuntimeException("No X.509 client certificate found in request");
}