前面简单的学习了证书的一些概念。继续学习证书的相关知识及其应用。
提到证书的应用,可以在输入的地址栏看到https,亦http+SSL/TLS。https在http上多了一个安全性。
当我们创建好证书,并且在服务器端配置好后,输入https的URL,此时浏览器会为我们完成SSL/TLS握手协议。
SSL/TLS握手协议分为3次,亦三次握手。
第一次:客户端浏览器会产生随机数RNC,然后发送SSL信息,算法信息,随机数RNC给服务端。服务端也产生随机数RNC,然后发送SSL信息,算法信息,随机数RNS给客户端,服务端证书,客户端证书请求(可有)。 --算法协商
第二次:客户端验证证书。若有客户端证书请求,客户端会发送证书给服务器,服务器验证。 --身份验证
第三次:客户端产生随机数PMS,使用服务端公钥加密随机数PMS,发送随机数PMS加密信息。服务端用私钥解密,获得PMS。然后双方使用随机数RNC,RNS,和PMS建立主密钥MS。主密钥是对称密钥。主密钥生成后,双方会用主密钥构建会话,通知终止握手。 --确定密钥
握手完后,就使用主密钥加密,解密信息进行通信了,证书在这里的作用就是确认你访问的地址可靠性。可以在这篇文章更加了解握手的内容http://www.infoq.com/cn/articles/HTTPS-Connection-Jeff-Moser。
开始总有一个疑问,既然公钥,私钥可以加密解密,为什么还要弄一个对称密钥出来做加密,解密。查下资料,人家说非对称密钥加密,解密效率低,对称密钥效率高一些,具体怎么高就要去把数学学好了,呵呵,就这样知道就有了。
接下来看一下代码是如何实现。我们的代码是运行在容器当中,连接主要靠容器做的,默认情况下都是http开头。若要https开头,就要在容器里面配置一下。我就讲下tomcat的配置。
tomcat负责连接的是connector,到tomcat的conf目录下打开server.xml文件,找到connector这一块,配置https:
https默认端口443,keystorefile指定了密钥库,clientAuth指客户端证书请求,默认false。
配置完后,你的地址就是使用https开头,并且访问该地址就要通过SSL/TSL协议了。
问题:我开始配置的时候,属性protocol="HTTP/1.1",这是会报错,说tomcat not find a matching property。我上面配置NIO,当然也可以配置成BIO。
浏览器帮我们做了SSL/TLS的功能,客户端用程序访问服务器,是如何做的呢?
那就要用到HttpsURLConnection和SSLSocketFactory这二个类了。
HttpsURLConnection用于建立连接,SSLSocketFactory做验证。
步骤很简单,通过URL获得HttpsURLConnection,然后设置一下SSLSocketFactory。SSLClientSocket是自己以前写的,用于获取SSLSocketFactory,这一个socketFactory的并没有做验证服务器证书操作。
开始的时候一直一个错误的理解,认为客户端这边已经得到服务器端证书,而且要验证的话必须给服务端证书,而且写在getAcceptedIssuers里。
在客户端,在checkServerTrusted里面,做服务端证书验证。在服务端,在checkClientTrusted里面做客户端证书验证。
在看一下服务器和客户端都使用程序通信,怎么实现SSL通信
客户端
可以根据该方法获取服务器证书
服务端
我代码里面初始化KeyManager/TrustManager用了二种方式,一种是从factory里面创建,另一种是直接构建他们的实现类。keystore我初学的时候已经写了怎么获得,keytool是自己写的类。
提到证书的应用,可以在输入的地址栏看到https,亦http+SSL/TLS。https在http上多了一个安全性。
当我们创建好证书,并且在服务器端配置好后,输入https的URL,此时浏览器会为我们完成SSL/TLS握手协议。
SSL/TLS握手协议分为3次,亦三次握手。
第一次:客户端浏览器会产生随机数RNC,然后发送SSL信息,算法信息,随机数RNC给服务端。服务端也产生随机数RNC,然后发送SSL信息,算法信息,随机数RNS给客户端,服务端证书,客户端证书请求(可有)。 --算法协商
第二次:客户端验证证书。若有客户端证书请求,客户端会发送证书给服务器,服务器验证。 --身份验证
第三次:客户端产生随机数PMS,使用服务端公钥加密随机数PMS,发送随机数PMS加密信息。服务端用私钥解密,获得PMS。然后双方使用随机数RNC,RNS,和PMS建立主密钥MS。主密钥是对称密钥。主密钥生成后,双方会用主密钥构建会话,通知终止握手。 --确定密钥
握手完后,就使用主密钥加密,解密信息进行通信了,证书在这里的作用就是确认你访问的地址可靠性。可以在这篇文章更加了解握手的内容http://www.infoq.com/cn/articles/HTTPS-Connection-Jeff-Moser。
开始总有一个疑问,既然公钥,私钥可以加密解密,为什么还要弄一个对称密钥出来做加密,解密。查下资料,人家说非对称密钥加密,解密效率低,对称密钥效率高一些,具体怎么高就要去把数学学好了,呵呵,就这样知道就有了。
接下来看一下代码是如何实现。我们的代码是运行在容器当中,连接主要靠容器做的,默认情况下都是http开头。若要https开头,就要在容器里面配置一下。我就讲下tomcat的配置。
tomcat负责连接的是connector,到tomcat的conf目录下打开server.xml文件,找到connector这一块,配置https:
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true" maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keystoreFile="conf/liu.keystore" keystorePass="123456"/>
https默认端口443,keystorefile指定了密钥库,clientAuth指客户端证书请求,默认false。
配置完后,你的地址就是使用https开头,并且访问该地址就要通过SSL/TSL协议了。
问题:我开始配置的时候,属性protocol="HTTP/1.1",这是会报错,说tomcat not find a matching property。我上面配置NIO,当然也可以配置成BIO。
浏览器帮我们做了SSL/TLS的功能,客户端用程序访问服务器,是如何做的呢?
那就要用到HttpsURLConnection和SSLSocketFactory这二个类了。
HttpsURLConnection用于建立连接,SSLSocketFactory做验证。
URL url = new URL("httpsUrl");
HttpsURLConnection httpsConn = (HttpsURLConnection) url
.openConnection();
SSLClientSocket client = new SSLClientSocket();
SSLSocketFactory sslSocketFactory = client.getFactoryNoPath();
httpsConn.setSSLSocketFactory(sslSocketFactory);
httpsConn.getInputStream();
public SSLSocketFactory getFactoryNoPath(){
SSLContext context = null;
try {
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[] {tm}, null);
} catch (KeyManagementException ex) {
Logger.getLogger(SSLClientSocket.class.getName()).log(Level.SEVERE, null, ex);
}catch (NoSuchAlgorithmException ex) {
Logger.getLogger(SSLClientSocket.class.getName()).log(Level.SEVERE, null, ex);
}
SSLSocketFactory ssf = context.getSocketFactory();
return ssf;
}
步骤很简单,通过URL获得HttpsURLConnection,然后设置一下SSLSocketFactory。SSLClientSocket是自己以前写的,用于获取SSLSocketFactory,这一个socketFactory的并没有做验证服务器证书操作。
开始的时候一直一个错误的理解,认为客户端这边已经得到服务器端证书,而且要验证的话必须给服务端证书,而且写在getAcceptedIssuers里。
在客户端,在checkServerTrusted里面,做服务端证书验证。在服务端,在checkClientTrusted里面做客户端证书验证。
在看一下服务器和客户端都使用程序通信,怎么实现SSL通信
客户端
public void clientSocket(String path) {
SSLContext context = null;
try {
KeyTool keytool = new KeyTool();
KeyStore keyStore = keytool.getKeyStore(path);
X509Certificate[] x509 = new X509Certificate[]{(X509Certificate)keytool.getCertificate(keyStore, "serverkey")};
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(keyStore);
TrustManager[] tm = tmf.getTrustManagers();
context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[]{new SSLTrustManager(x509)}, null);
//context.init(null,tm, null);
} catch (KeyManagementException ex) {
Logger.getLogger(SSLClientSocket.class.getName()).log(Level.SEVERE, null, ex);
} catch (KeyStoreException ex) {
Logger.getLogger(SSLClientSocket.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(SSLClientSocket.class.getName()).log(Level.SEVERE, null, ex);
}
SSLSocketFactory ssf = context.getSocketFactory();
try {
SSLSocket ss = (SSLSocket) ssf.createSocket("localhost", 8000);
System.out.println("customer already");
ObjectInputStream os = new ObjectInputStream(ss.getInputStream());
System.out.println(os.readObject());
os.close();
ss.close();
System.out.println("ok");
} catch (ClassNotFoundException ex) {
Logger.getLogger(SSLClientSocket.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(SSLClientSocket.class.getName()).log(Level.SEVERE, null, ex);
}
}
可以根据该方法获取服务器证书
Certificate[] aCerts = ss.getSession().getPeerCertificates();
服务端
public void sslServerSocket(String path) {
boolean flag = true;
SSLContext context = null;
try {
KeyTool keytool = new KeyTool();
KeyStore keyStore = keytool.getKeyStore(path);
PrivateKey pk = keytool.getPrivateKey(keyStore, "serverkey");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keyStore, "123456".toCharArray());
X509Certificate[] x509 = new X509Certificate[]{(X509Certificate)keytool.getCertificate(keyStore, "serverkey")};
KeyManager[] km = kmf.getKeyManagers();
// TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
// tmf.init(keyStore);
// TrustManager[] tm = tmf.getTrustManagers();
context = SSLContext.getInstance("SSL");
context.init(new KeyManager[]{new SSLKeyManager(pk,x509)}, null, null);
//context.init(km, null, null);
} catch (KeyManagementException ex) {
Logger.getLogger(SSLServerSockets.class.getName()).log(Level.SEVERE, null, ex);
} catch (KeyStoreException ex) {
Logger.getLogger(SSLServerSockets.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnrecoverableKeyException ex) {
Logger.getLogger(SSLServerSockets.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(SSLServerSockets.class.getName()).log(Level.SEVERE, null, ex);
}
SSLServerSocketFactory ssf = context.getServerSocketFactory();
try {
SSLServerSocket ss = (SSLServerSocket) ssf.createServerSocket(8000);
System.out.println("wait for customer connect");
while (flag) {
Socket s = ss.accept();
System.out.println("accept customer connecting");
ObjectOutputStream os = new ObjectOutputStream(s.getOutputStream());
os.writeObject("echo: hello");
os.flush();
os.close();
System.out.println();
s.close();
}
ss.close();
} catch (IOException ex) {
Logger.getLogger(SSLServerSockets.class.getName()).log(Level.SEVERE, null, ex);
}
}
我代码里面初始化KeyManager/TrustManager用了二种方式,一种是从factory里面创建,另一种是直接构建他们的实现类。keystore我初学的时候已经写了怎么获得,keytool是自己写的类。