SSLSocket Java Part2

1、SSLSocket Java Server使用SSLContext

package com.ats.ssl.socket;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class ServerWithContext {

	static String delimiter = "=========================================================";

	public static void startListen(String keyStorePath, String keyStorePwd, int port) throws IOException, KeyStoreException, NoSuchAlgorithmException,
			CertificateException, UnrecoverableKeyException, KeyManagementException {

		KeyStore keyStore = KeyStore.getInstance("JKS");
		keyStore.load(new FileInputStream(keyStorePath), keyStorePwd.toCharArray());
		KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
		keyManagerFactory.init(keyStore, keyStorePwd.toCharArray());

		//SSLContext sslContext = SSLContext.getInstance("TLSv1");
		SSLContext sslContext = SSLContext.getInstance("SSLv3");
		sslContext.init(keyManagerFactory.getKeyManagers(), new TrustManager[0], null);

		SSLServerSocketFactory sslserversocketfactory = sslContext.getServerSocketFactory();
		SSLServerSocket sslserversocket = (SSLServerSocket) sslserversocketfactory.createServerSocket(port);

		while (true) {
			SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();

			DisplaySecurityLevel(sslsocket);
			DisplayCertificateInformation(sslsocket);

			try {
				InputStream inputstream = sslsocket.getInputStream();
				InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
				BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

				System.out.println(delimiter);
				String string = null;
				while ((string = bufferedreader.readLine()) != null) {
					System.out.println(string);
					System.out.flush();
				}
				System.out.println(delimiter);
			} catch (Exception ex) {
				ex.printStackTrace();
			} finally {
				sslsocket.close();
			}
		}
	}

	static void DisplaySecurityLevel(SSLSocket sslsocket) {
		System.out.println(delimiter);
		SSLSession session = sslsocket.getSession();
		System.out.println("通讯协议: " + session.getProtocol());
		System.out.println("加密方式: " + session.getCipherSuite());
		System.out.println(delimiter);
	}

	static void DisplayCertificateInformation(SSLSocket sslsocket) {
		System.out.println(delimiter);
		Certificate[] localCertificates = sslsocket.getSession().getLocalCertificates();
		if (localCertificates == null || localCertificates.length == 0) {
			System.out.println("本地证书为空");
		} else {
			Certificate cert = localCertificates[0];
			System.out.println("本地证书类型: " + cert.getType());
			if (cert.getType().equals("X.509")) {
				X509Certificate x509 = (X509Certificate) cert;
				System.out.println("本地证书签发者: " + x509.getIssuerDN());
				System.out.println("本地证书有效期: " + x509.getNotBefore() + "至" + x509.getNotAfter());
			}
		}

		try {
			Certificate[] peerCertificates = sslsocket.getSession().getPeerCertificates();

			if (peerCertificates == null || peerCertificates.length == 0) {
				System.out.println("远程证书为空");
			} else {
				Certificate cert = peerCertificates[0];
				System.out.println("远程证书类型: " + cert.getType());
				if (cert.getType().equals("X.509")) {
					X509Certificate x509 = (X509Certificate) cert;
					System.out.println("远程证书签发者: " + x509.getIssuerDN());
					System.out.println("远程证书有效期: " + x509.getNotBefore() + "至" + x509.getNotAfter());
				}
			}
		} catch (SSLPeerUnverifiedException e) {
			// e.printStackTrace();
			System.out.println("远程证书为空");
		}

		System.out.println(delimiter);
	}

	public static void main(String[] arstring) {
		try {
			URL url = ServerWithContext.class.getClassLoader().getResource("myKeyStore.jks");
			String jks = url.getFile();
			startListen(jks, "sslTestPwd", 9999);

		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}
}

2、SSLSocket Java Client使用SSLContext

package com.ats.ssl.socket;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;

public class ClientWithContext {
	
	public static void connectAndSend(String trustStorePath,
			String trustStorePwd, String ip, int port, String msg) throws IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException, UnrecoverableKeyException{
	
		KeyStore trustStore = KeyStore.getInstance("JKS");
		trustStore.load(new FileInputStream(trustStorePath), trustStorePwd.toCharArray());
		TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
		trustManagerFactory.init(trustStore);
        
		SSLContext sslContext = SSLContext.getInstance("TLSv1");
		//SSLContext sslContext = SSLContext.getInstance("SSLv3");
		
		sslContext.init(new KeyManager[0], trustManagerFactory.getTrustManagers(), null);
		SSLContext.setDefault(sslContext);
		
		SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();  
		SSLSocket sslsocket = (SSLSocket) sslSocketFactory.createSocket(
				"localhost", 9999);

		try {
			OutputStream outputstream = sslsocket.getOutputStream();
			OutputStreamWriter outputstreamwriter = new OutputStreamWriter(
					outputstream);
			BufferedWriter bufferedwriter = new BufferedWriter(
					outputstreamwriter);

			bufferedwriter.write(msg);
			bufferedwriter.flush();
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			sslsocket.close();
		}
	}

	public static void main(String[] args) throws Exception {
		try {
			URL url = Server.class.getClassLoader().getResource(
					"myTrustStore.jks");
			String jks = url.getFile();

			connectAndSend(jks, "sslTestPwd", "127.0.0.1", 9999,
					"This msg is from Java SSL Client :)");

		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}
}

SSLSocket Java Part1

1、使用环境变量,最基本的SSLSocket Server

package com.ats.ssl.socket;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

public class Server {

	static String delimiter = "=========================================================";

	public static void startListen(String keyStorePath, String keyStorePwd, int port) throws IOException {
		System.setProperty("javax.net.ssl.keyStore", keyStorePath);
		System.setProperty("javax.net.ssl.keyStorePassword", keyStorePwd);

		SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
		SSLServerSocket sslserversocket = (SSLServerSocket) sslserversocketfactory.createServerSocket(port);

		while (true) {
			SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();

			String protocols[] = { "TLSv1" };
			// String protocols[] = {"SSLv2Hello","TLSv1","SSLv3"};
			// String protocols[] = {"SSLv3"};
			sslsocket.setEnabledProtocols(protocols);

			DisplaySecurityLevel(sslsocket);
			DisplayCertificateInformation(sslsocket);

			try {
				InputStream inputstream = sslsocket.getInputStream();
				InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
				BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

				System.out.println(delimiter);
				String string = null;
				while ((string = bufferedreader.readLine()) != null) {
					System.out.println(string);
					System.out.flush();
				}
				System.out.println(delimiter);
			} catch (Exception ex) {
				ex.printStackTrace();
			} finally {
				sslsocket.close();
			}
		}
	}

	static void DisplaySecurityLevel(SSLSocket sslsocket) {
		System.out.println(delimiter);
		SSLSession session = sslsocket.getSession();
		System.out.println("通讯协议: " + session.getProtocol());
		System.out.println("加密方式: "+session.getCipherSuite());
		System.out.println(delimiter);
	}

	static void DisplayCertificateInformation(SSLSocket sslsocket) {
		System.out.println(delimiter);
		Certificate[] localCertificates = sslsocket.getSession().getLocalCertificates();
		if (localCertificates == null || localCertificates.length == 0) {
			System.out.println("本地证书为空");
		} else {
			Certificate cert = localCertificates[0];
			System.out.println("本地证书类型: " + cert.getType());
			if (cert.getType().equals("X.509")) {
				X509Certificate x509 = (X509Certificate) cert;
				System.out.println("本地证书签发者: " + x509.getIssuerDN());
				System.out.println("本地证书有效期: " + x509.getNotBefore() + "至" + x509.getNotAfter());
			}
		}

		try {
			Certificate[] peerCertificates = sslsocket.getSession().getPeerCertificates();

			if (peerCertificates == null || peerCertificates.length == 0) {
				System.out.println("远程证书为空");
			} else {
				Certificate cert = peerCertificates[0];
				System.out.println("远程证书类型: " + cert.getType());
				if (cert.getType().equals("X.509")) {
					X509Certificate x509 = (X509Certificate) cert;
					System.out.println("远程证书签发者: " + x509.getIssuerDN());
					System.out.println("远程证书有效期: " + x509.getNotBefore() + "至" + x509.getNotAfter());
				}
			}
		} catch (SSLPeerUnverifiedException e) {
			// e.printStackTrace();
			System.out.println("远程证书为空");
		}

		System.out.println(delimiter);
	}

	public static void main(String[] arstring) {
		try {
			URL url = Server.class.getClassLoader().getResource("myKeyStore.jks");
			String jks = url.getFile();
			startListen(jks, "sslTestPwd", 9999);

		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}
}

2、相应的,使用环境变量进行设置的,SSLSocket Client

package com.ats.ssl.socket;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.URL;

public class Client {
	public static void connectAndSend(String trustStorePath,
			String trustStorePwd, String ip, int port, String msg)
			throws IOException {
		System.setProperty("javax.net.ssl.trustStore", trustStorePath);
		System.setProperty("javax.net.ssl.trustStorePassword", trustStorePwd);

		SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory
				.getDefault();
		SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(
				"localhost", 9999);

		//String protocols[] = {"TLSv1"};
		String protocols[] = {"SSLv2Hello","TLSv1","SSLv3"};
		//String protocols[] = {"SSLv3"};
		sslsocket.setEnabledProtocols(protocols);

		try {
			OutputStream outputstream = sslsocket.getOutputStream();
			OutputStreamWriter outputstreamwriter = new OutputStreamWriter(
					outputstream);
			BufferedWriter bufferedwriter = new BufferedWriter(
					outputstreamwriter);

			bufferedwriter.write(msg);
			bufferedwriter.flush();
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			sslsocket.close();
		}
	}

	public static void main(String[] arstring) {
		try {
			URL url = Server.class.getClassLoader().getResource(
					"myTrustStore.jks");
			String jks = url.getFile();

			connectAndSend(jks, "sslTestPwd", "127.0.0.1", 9999,
					"This msg is from Java SSL Client :)");

		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}
}

自签名证书HTTPS

1、生成自签名的CA私钥及自签名证书,并导出为der、p12、jks格式

set OPENSSL_CONF=C:\ProgramerTools\OpenSSL-Win64\bin\openssl.cfg

#生成私钥
openssl genrsa 1024 > NMyCA1024.key

#生成自签名证书
openssl req -new -x509 -nodes -key NMyCA1024.key -days 1095 -subj "/C=CN/ST=ShangHai/L=ShangHai/O=NEOHOPE/OU=Development/CN=NMyCA1024" > NMyCA1024.pem

#转der格式,生成trust store
openssl x509 -outform der -in NMyCA1024.pem -out NMyCA1024.crt
keytool -import -trustcacerts -file NMyCA1024.crt -keystore NMyCA1024_trust.jks -storepass 123456

#转p12格式,生成key sotre
openssl pkcs12 -export -out NMyCA1024.p12 -in NMyCA1024.pem -inkey NMyCA1024.key
keytool -importkeystore -srckeystore NMyCA1024.p12 -srcstoretype PKCS12 -deststoretype  JKS -destkeystore NMyCA1024_key.jks

2、生成网站私钥,并生成CA签名的证书,并导出为der、p12、jks格式

#生成私钥
openssl genrsa 1024 > server.key

#从CA请求证书
openssl req -new -key server.key -subj "/C=CN/ST=ShangHai/L=ShangHai/O=NEOHOPE/OU=Development/CN=127.0.0.1" > server.csr

#生成CA签名的证书
openssl x509 -req -in server.csr -CA NMyCA1024.pem -CAkey NMyCA1024.key -CAcreateserial -days 365 > serversigned.crt

#生成trust store
keytool -import -trustcacerts -file serversigned.crt -keystore serversigned_trust.jks -storepass 123456

#转p12格式,生成key sotre
openssl pkcs12 -export -out serversigned.p12 -in serversigned.crt -inkey server.key
keytool -importkeystore -srckeystore serversigned.p12 -srcstoretype PKCS12 -deststoretype  JKS -destkeystore serversigned_key.jks

3、在server端使用serversigned.p12或serversigned_key.jks

4、在浏览器端,导入NMyCA1024.crt为CA根证书,浏览器就可以正常打开HTTPS网站了

5、如果是要用Java Client端进行认证,则需要将CA证书导入到对应JDK或JRE的CA列表中,用serversigned_trust.jks就可以正常访问了

keytool -import -trustcacerts -file NMyCA1024.crt -alias NMyCA1024 -keystore %JRE_HOME%\lib\security\cacerts -storepass changeit

openssl获取网站证书及验证证书链

1、获取网站证书信息

set OPENSSL_CONF=C:\ProgramerTools\OpenSSL-Win64\bin\openssl.cfg
#获取淘宝证书信息
openssl s_client -showcerts -connect www.taobao.com:443
#获取淘宝ssl2证书信息
openssl s_client -showcerts -ssl2 -connect www.taobao.com:443

2、验证证书链
比如,我有一个自签名的三层证书系统:
NMyCA1024(RootCA,自签名认证)
NMySubCA1024(NMySubCA1024是是中级CA,是NMyCA1024认证过的)
Server(Server是服务器证书,是NMySubCA1024认证过的)

可以用如下方法验证证书链:

#会告诉你这是一个自签名证书
openssl verify NMyCA1024.pem

#L1中方的是NMyCA1024的证书
openssl verify -CAfile L1.pem NMySubCA1024.pem

#L2中方的是NMyCA1024及NMySubCA1024的证书
openssl verify -CAfile L2.pem Server.pem

#只用中级证书,会导致证书链不完整,无法通过验证
openssl verify -CAfile NMySubCA1024.pem Server.pem

JKS密码验证

下面的程序用来验证JKS的文件及密码是否正确

public static URL getStoreURL(String storePath) throws IOException
{
	URL url = null;
	// First see if this is a URL
	try
	{
		url = new URL(storePath);
	}
	catch (MalformedURLException e)
	{
		// Not a URL or a protocol without a handler so...
		// next try to locate this as file path
		File tst = new File(storePath);
		if (tst.exists() == true)
		{
			url = tst.toURL();
		} else
		{
			// not a file either, lastly try to locate this as a classpath
			// resource
			if (url == null)
			{
				ClassLoader loader = Thread.currentThread().getContextClassLoader();
				url = loader.getResource(storePath);
			}
		}
	}
	// Fail if no valid key store was located
	if (url == null)
	{
		String msg = "Failed to find url=" + storePath + " as a URL, file or resource";
		throw new MalformedURLException(msg);
	}
	return url;
}

public static KeyStore loadKeyStore(String storeType, URL storePathURL, String storePassword) throws Exception
{
	KeyStore keyStore = null;
	String provider = null;
	String providerName = null;

	if (provider != null)
	{
		keyStore = KeyStore.getInstance(storeType, provider);
	} else
		if (providerName != null)
		{
			keyStore = KeyStore.getInstance(storeType, providerName);
		} else
		{
			keyStore = KeyStore.getInstance(storeType);
		}
	if (storePathURL == null) { throw new Exception("Can not find store file for url because store url is null."); }
	// now that keystore instance created, need to load data from file
	InputStream keyStoreInputStream = null;
	try
	{
		keyStoreInputStream = storePathURL.openStream();
		// is ok for password to be null, as will just be used to check
		// integrity of store
		char[] password = storePassword != null ? storePassword.toCharArray() : null;
		keyStore.load(keyStoreInputStream, password);
	}
	finally
	{
		if (keyStoreInputStream != null)
		{
			try
			{
				keyStoreInputStream.close();
			}
			catch (IOException e)
			{
				// no op
			}
			keyStoreInputStream = null;
		}
	}
	return keyStore;
}

public static String verifyP12(String p12Path,String p12Pwd)
{
            String ret = "验证成功";
            try
            {
	URL ksURL = getStoreURL(p12Path);
                if(ksURL==null)throw new Exception(p12Path+"文件未找到");
                    
	loadKeyStore("PKCS12",ksURL,p12Pwd);
            }
            catch(Exception ex)
            {
                ret = ex.getMessage();
                ex.printStackTrace();
            }
            return ret;
}

public static String verifyJks(String jksPath,String jksPwd)
{
            String ret = "验证成功";
            try
            {
	URL ksURL = getStoreURL(jksPath);
	loadKeyStore("JKS",ksURL,jksPwd);
                
                if(ksURL==null)throw new Exception(jksPath+"文件未找到");
            }
            catch(Exception ex)
            {
                ret = ex.getMessage();
                ex.printStackTrace();
            }
            
            return ret;
}

AXIS2客户端支持TLS

只要设置下面几个环境变量就好啦;)

public static final String TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword";
public static final String TRUST_STORE = "javax.net.ssl.trustStore";
public static final String TRUST_STORE_TYPE = "javax.net.ssl.trustStoreType";
public static final String KEY_STORE_TYPE = "javax.net.ssl.keyStoreType";
public static final String KEY_STORE_PASSWORD = "javax.net.ssl.keyStorePassword";
public static final String KEY_STORE = "javax.net.ssl.keyStore";

openssl生成key

生成私钥及自签名证书(自签名这样就可以咯)

set OPENSSL_CONF=%OPENSSL_HOME%\bin\openssl.cfg
openssl genrsa 1024 > test.key
openssl req -new -x509 -nodes -key test.key -days 1095 -subj "/C=CN/ST=ShangHai/L=ShangHai/O=NEOHOPE/OU=Development/CN=NMyCA1024" > test.pem

生成私钥、证书请求及自签名证书(通常是把csr文件发给第三方机构申请证书,这里仍然是自签名)

set OPENSSL_CONF=%OPENSSL_HOME%\bin\openssl.cfg
openssl genrsa -out test1.key 1024
openssl req -new -key test.key -out test1.csr -subj -subj "/C=CN/ST=ShangHai/L=ShangHai/O=NEOHOPE/OU=Development/CN=NMyCA1024"
openssl x509 -req -days 3650 -in test1.csr -signkey test1.key -out test1.pem

这里请注意,自签名证书的话,上面两种方式是一样的。但这里只有一层,也就是没有CA的存在,如果需要CA及服务器两层的话,就要:
1、生成CA的私钥及证书
2、生成服务器私钥及证书
3、用CA的私钥对服务器证书签名
4、所有客户端信任CA证书

pem转为p12(私钥+证书)

set OPENSSL_CONF=%OPENSSL_HOME%\bin\openssl.cfg
openssl pkcs12 -export -out test.p12 -in test.pem -inkey test.key

pem转为jks的truststore(ca证书)

keytool -import -v -trustcacerts -file test.pem -keystore test.jks -storepass 123456 -alias caRoot
keytool -list -v -keystore test.jks -storepass 123456

p12转为jks的keystore(私钥+证书)

keytool -importkeystore -srckeystore test.p12 -destkeystore test1.jks -srcstoretype PKCS12 -deststoretype JKS -srcstorepass 123456 -deststorepass 123456
keytool -list -v -keystore test1.jks -storepass 123456

这里请注意,jks与p12的密码要设成一样的,否则有些时候会无法使用。

keytool生成key

生成keystore及cert

#生成私钥
keytool -genkey -validity 10000 -keyalg RSA -dname "CN=neohope OU=neohope O=neohope L=Shanghai C=CN" -keystore node1.jks -alias node1 -keypass password -storepass password 
#导出证书
keytool -export -file node1.crt -keystore node1.jks -alias node1 -keypass password -storepass password 
#生成truststore
keytool -import -trustcacerts -file node1.crt -keystore trust.jks -alias node1 -keypass password -storepass password 
#查看
keytool -list -keystore node1.jks
keytool -list -keystore trust.jks

jks转p12

keytool -importkeystore -srckeystore node1.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore node1.p12 -srcstorepass password -deststorepass password

JKS与P12证书互转

::JKS → P12
keytool -importkeystore -srckeystore keystore.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore keystore.p12

::P12 → JKS
keytool -importkeystore -srckeystore keystore.p12 -srcstoretype PKCS12 -deststoretype JKS -destkeystore keystore.jks

在这里,有一点大家一定要记住,P12的密码和JKS的密码一定要一致,否则很多容器(如Tomcat)无法加载。一般来说,JKS要求密码至少为6位,所以如果P12的密码位数太短,就要修改P12的密码啦:

openssl pkcs12 -in keystore.p12 -out keystore.pem -nodes
openssl pkcs12 -export -out keystore1.p12 -in keystore.pem

JBoss配置TLS

文件%JBOSS_HOME%\server\default\deploy\jboss-web.deployer\server.xml
增加下面陪孩子

<Connector port="8443" address="${jboss.bind.address}"
          protocol="HTTP/1.1" SSLEnabled="true" 
          maxThreads="100" strategy="ms" maxHttpHeaderSize="8192"
      	  emptySessionPath="true"
      	  scheme="https" secure="true" clientAuth="false" 
      	  disableUploadTimeout="true" 
      	  keystoreFile="${jboss.server.home.dir}/conf/node1.jks"
      	  keystorePass="passward"
      	  keyAlias="node1"
      	  sslProtocol = "TLS" />