欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

certificate 学习 博客分类: secure sslcertificate证书 

程序员文章站 2024-02-28 21:13:58
...
  前面简单的学习了证书的一些概念。继续学习证书的相关知识及其应用。
  提到证书的应用,可以在输入的地址栏看到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是自己写的类。