Implement SSL on Java

In order to implement SSL on server we need to do the following:


1. Generate a private key using Keytool.
Here is the sample command for keytool


keytool -genkey -alias tomcat -keyalg RSA -keystore keystore.jks


This will ask several questions. The first question is what is your name and the answer must be the fully qualified name of the domain. For example, if the domain is reveim.com, the name should be reveim.com.


If all other information is provided and a password is set, this will generate a file named keystore.jks and store a private-public key pair in this file. The alias name is important and  can be any word. This alias name will be required during certificating signing process.


The generate keystore.jks file is called the self-signed certificate. This can be used with custom clients. But if the client is a browser, self signed certificate should not be used because user will see a warning message for their first visit.


For production use, we need to obtain a certificate from well known CA authorities. For this we need to do the following.


2. Create a Certificate Sign Request(CSR):
The following command will generate a CSR.


keytool -certreq -v -alias tomcat -file server_csr.pem -keystore keystore.jks


Here alias name should match the alias name provided in the previous section. This command will create a csr file which need to be signed by a CA authority.


3. Submitting the CSR:
We need to submit this CSR to a CA. Go to rapidssl, GoDaddy, StartSSL or similar CA websites and submit the CSR file contents.
The CA authority will do some validation to make sure that you are the domain owner. For example, the might validate an email address which is under the domain you are using e.g admin@mydomain.com. After completion of the validation, the CA will sent you and email containing the server certificate and some other certificates. The deliverables vary depending on which server you are using. For netty, we need 3 files - two intermediate files and one server ssl file.


4. Import the received files into the keystore:
Take a backup of your keystore file with the following command.
cp keystore.jks keystore.jks.bkp


Now import the keys in the following order:
For the primary intermediate CA, first create a file named primary_inter.cer and put the content of the received file in it. You can use any file name of your choice.
Now import the contents of the primary ca file with the following command


keytool -import -trustcacerts -alias primaryca -keystore keystore.jks -file primary_inter.cer


As before, the alias name is up to you.


Next, we need to import the secondary CA file using the same process. First create a file named secondary_inter.cer and put the intermediate CA files content in it. Then import the key using the following command


keytool -import -trustcacerts -alias secondaryca -keystore keystore.jks -file secondary_inter.cer


Now import the server’s ssl certificate file. The following command will do that.


keytool -import -trustcacerts -alias your_alias_name -keystore keystore.jks -file your_certificate_filename


Here alias name must be the alias name used to generate the private key in the step 1.


After issuing these command, the keystore file will contain 3 keys. You can verify this with the following command.


keytool -list -v -keystore keystore.jks


Verify the following information:


  • The SSL certificate is imported into the alias with the "Entry Type" of PrivateKeyEntry or KeyEntry. If not, please import the certificate into the Private Key alias.
  • The Certificate chain length is 4.


If everything is OK, you are ready to go. Use the keystore.jks file where required.


Using SSl on Netty:
If you have generated the keystore file, you are almost done. Now you need to specify the keystore file and it’s password to netty. To do so, create the following classe:


public final class SSLKeyStore {
   
   private static final String filePath = "keystore.jks";
   private static final String password = "my_keystore_pass";
   
   public static InputStream asInputStream() {
       InputStream is = null;
       try {    
           is = new FileInputStream(filePath);
       } catch (FileNotFoundException ex) {
           //Logger.getLogger(SSLKeyStore.class.getName()).log(Level.SEVERE, null, ex);
           is = null;
       }
       return is;
   }


   public static char[] getCertificatePassword() {
       return password.toCharArray();
   }


   public static char[] getKeyStorePassword() {
       return password.toCharArray();
   }


   private SSLKeyStore() {
       // Unused
   }
}


public class SSLTrustManagerFactory extends TrustManagerFactorySpi {


   private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() {
       @Override
       public X509Certificate[] getAcceptedIssuers() {
           return new X509Certificate[0];
       }


       @Override
       public void checkClientTrusted(X509Certificate[] chain, String authType) {
           // Always trust - it is an example.
           // You should do something in the real world.
           // You will reach here only if you enabled client certificate auth,
           // as described in SecureChatSslContextFactory.
           System.err.println(
                   "UNKNOWN CLIENT CERTIFICATE: " + chain[0].getSubjectDN());
       }


       @Override
       public void checkServerTrusted(X509Certificate[] chain, String authType) {
           // Always trust - it is an example.
           // You should do something in the real world.
           System.err.println(
                   "UNKNOWN SERVER CERTIFICATE: " + chain[0].getSubjectDN());
       }
   };


   public static TrustManager[] getTrustManagers() {
       return new TrustManager[] { DUMMY_TRUST_MANAGER };
   }


   @Override
   protected TrustManager[] engineGetTrustManagers() {
       return getTrustManagers();
   }


   @Override
   protected void engineInit(KeyStore keystore) throws KeyStoreException {
       // Unused
   }


   @Override
   protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
           throws InvalidAlgorithmParameterException {
       // Unused
   }
}


public final class SSLContextFactory {


   private static final String PROTOCOL = "TLS";
   private static final SSLContext SERVER_CONTEXT;
   private static final SSLContext CLIENT_CONTEXT;


   static {
       String algorithm = SystemPropertyUtil.get("ssl.KeyManagerFactory.algorithm");
       if (algorithm == null) {
           algorithm = "SunX509";
       }


       SSLContext serverContext;
       SSLContext clientContext;
       try {
           KeyStore ks = KeyStore.getInstance("JKS");
           
           ks.load(SSLKeyStore.asInputStream(),
                   SSLKeyStore.getKeyStorePassword());


           // Set up key manager factory to use our key store
           KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
           
           kmf.init(ks, SSLKeyStore.getCertificatePassword());


           // Initialize the SSLContext to work with our key managers.
           serverContext = SSLContext.getInstance(PROTOCOL);
           serverContext.init(kmf.getKeyManagers(), null, null);
       } catch (Exception e) {
           throw new Error(
                   "Failed to initialize the server-side SSLContext", e);
       }


       try {
           clientContext = SSLContext.getInstance(PROTOCOL);
           clientContext.init(null, SSLTrustManagerFactory.getTrustManagers(), null);
       } catch (Exception e) {
           throw new Error(
                   "Failed to initialize the client-side SSLContext", e);
       }


       SERVER_CONTEXT = serverContext;
       CLIENT_CONTEXT = clientContext;
   }


   public static SSLContext getServerContext() {
       return SERVER_CONTEXT;
   }


   public static SSLContext getClientContext() {
       return CLIENT_CONTEXT;
   }


   private SSLContextFactory() {
       // Unused
   }
}


Now add the following code in the server initializer class. For example, my initializer class is like the following:


@Override
public void initChannel(SocketChannel ch) throws Exception
{
ChannelPipeline pipeline = ch.pipeline();


       // use the following for enabling SSL
       SSLEngine engine =
           SSLContextFactory.getServerContext().createSSLEngine();
       engine.setUseClientMode(false);


       pipeline.addLast("ssl", new SslHandler(engine));
       // end of ssl related tasks
       
pipeline.addLast("idleStateHandler", new IdleStateHandler(10, 10, 0));
pipeline.addLast("webSocketIdleStateHandler", new WebSocketIdleStateHandler());
pipeline.addLast("codec-http", new HttpServerCodec());
pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
pipeline.addLast(group,"handler", new WebSocketServerHandler());
}


Now you should be able to use SSL in your netty server.

Comments

Popular posts from this blog

Run tasks in background in Spring

Conditional field inclusion in Jackson and Spring Boot

How to configure Wildfly 10 to use MySQL