Java SSL 证书细节

关于SSL这块,网上很多,但很多都是讲原理或怎么生成证书实现简单通信,没有讲到实践时的诸多细节。
SSL, 即Secure Sockets Layer 安全套接层。本文介绍SSL的一些常见问题,用的语言主要是Java。

首先明确SSL的目的,就是加密。就是在Socket的基础上增加安全性。一般来讲,就是避免客户端向服务端传输数据时,被人拦截篡改,避免所谓的中间人攻击,防范钓鱼网站。
关于它的基础概念,可以看
【上】安全HTTPS-全面详解对称加密,非对称加密,数字签名,数字证书和HTTPS
【下】安全HTTPS-全面详解对称加密,非对称加密,数字签名,数字证书和HTTPS

问题1:证书生成和格式

网上搜索SSL,有很多生成证书的教程??墒腔岱⑾钟械氖怯肑ava的Keytool生成的cer,有的是用openssl来生成的pem。这是为什么呢?其实这些证书本质是一样的,只是文件格式不一样。所以如果想在Java里使用pem格式的证书,就要转化。转化的方式在这个问题里已有回答:
Convert a PEM-formatted String to a java.security.cert.X509Certificate
另外,公钥和私钥分别以什么形式存在?公钥存在于证书文件中,私钥存在于Keystore文件中。注意Java里的Keystore类可以是有私钥的Keystore,也可以是只有公钥的Keystore。
还有,为什么使用Java keytool生成的证书要设置密码?这个密码是JKS文件的密码,注意不是公钥或私钥哦,是用于防范别人随便乱拿的。

问题2:既然是加密,那么可不可以脱离Socket存在?

可以。可以直接使用公钥来加密数据,再用私钥解密数据。具体方法参考:
Java加密技术(八)——数字证书

问题3:单向认证,即只确认服务端是否真实可靠的话,要做什么?以SSLSocket举例。

如果只需要信任自己生成的证书
代码


    public class TestSSLSocketClient {  
        private static String path = "e:\\keytool\\sslclient.keystore";  
        private static char[] password = "aaaaaaa".toCharArray();  
      
        /** 
         * @param args 
         */  
        public static void main(String[] args) {  
            SSLContext context = null;  
            try {  
                KeyStore ts = KeyStore.getInstance("JKS");  
                ts.load(new FileInputStream(path), password);  
                TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");  
                tmf.init(ts);  
                TrustManager [] tm = tmf.getTrustManagers();  
                context = SSLContext.getInstance("SSL");  
                context.init(null, tm, null);  
            } catch (...... e) {         //省略捕获的异常信息  
                e.printStackTrace();  
            }   
            SSLSocketFactory ssf = context.getSocketFactory();  
            try {  
                SSLSocket ss = (SSLSocket) ssf.createSocket("localhost", 8000);  
                System.out.println("客户端就绪。");  
                ObjectInputStream br = new ObjectInputStream(ss.getInputStream());  
                try {  
                    System.out.println(br.readObject());  
                } catch (ClassNotFoundException e) {  
                    e.printStackTrace();  
                }  
                br.close();  
                ss.close();  
                System.out.println("客户端测试ok");  
            } catch (UnknownHostException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  

可以从代码看到,对于SSLContext,在做单向认证时,只需要TrustManagerFactory即可,
context.init(null, tm, null);
TrustManagerFactory是使用了服务端的证书的,即只使用了公钥来加密数据。

如果只需要信任系统自带的证书

            SSLContext sslContext = SSLContext.getDefault();

如果需要信任自己的以及系统的证书
引用:http://stackoverflow.com/questions/24555890/using-a-custom-truststore-in-java-as-well-as-the-default-one?noredirect=1


TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Using null here initialises the TMF with the default trust store.
tmf.init((KeyStore) null);

// Get hold of the default trust manager
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        defaultTm = (X509TrustManager) tm;
        break;
    }
}

FileInputStream myKeys = new FileInputStream("truststore.jks");

// Do the same with your trust store this time
// Adapt how you load the keystore to your needs
KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
myTrustStore.load(myKeys, "password".toCharArray());

myKeys.close();

tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(myTrustStore);

// Get hold of the default trust manager
X509TrustManager myTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        myTm = (X509TrustManager) tm;
        break;
    }
}

// Wrap it in your own class.
final X509TrustManager finalDefaultTm = defaultTm;
final X509TrustManager finalMyTm = myTm;
X509TrustManager customTm = new X509TrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        // If you're planning to use client-cert auth,
        // merge results from "defaultTm" and "myTm".
        return finalDefaultTm.getAcceptedIssuers();
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        try {
            finalMyTm.checkServerTrusted(chain, authType);
        } catch (CertificateException e) {
            // This will throw another CertificateException if this fails too.
            finalDefaultTm.checkServerTrusted(chain, authType);
        }
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        // If you're planning to use client-cert auth,
        // do the same as checking the server.
        finalDefaultTm.checkClientTrusted(chain, authType);
    }
};


SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { customTm }, null);

问题4:可以看到SSLContext.init()的参数有KeyManager和TrustManager,它俩的差异是?

KeyManager是服务端用的,用于在客户端请求时发送证书及其公钥。
可以看这个链接
http://stackoverflow.com/questions/13997419/difference-between-keystore-and-keymanager-trustmanager

问题5:服务器端会在连接时向客户端发送证书,那么如何从代码上获???

引用:http://www.xinotes.net/notes/note/1088/

import java.io.InputStream;
import java.io.OutputStream;

import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class RetrieveSSLCert {
    public static void main(String[] args) throws Exception {
    if (args.length < 2) {
        System.out.println("Usage: java RetrieveSSLCert <host> <port>");
        return;
    }

    String host = args[0];
    int port = Integer.parseInt(args[1]);

    // create custom trust manager to ignore trust paths
    TrustManager trm = new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
        return null;
        }

        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
    };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, new TrustManager[] { trm }, null);
        SSLSocketFactory factory =sc.getSocketFactory();
        SSLSocket socket =(SSLSocket)factory.createSocket(host, port);
        socket.startHandshake();
    SSLSession session = socket.getSession();
    java.security.cert.Certificate[] servercerts = session.getPeerCertificates();
    for (int i = 0; i < servercerts.length; i++) {
            System.out.print("-----BEGIN CERTIFICATE-----\n");
            System.out.print(new sun.misc.BASE64Encoder().encode(servercerts[i].getEncoded()));
            System.out.print("\n-----END CERTIFICATE-----\n");
    }

    socket.close();
    }
}
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容

  • 本文转载,出处如下:数字证书原理 文中首先解释了加密解密的一些基础知识和概念,然后通过一个加密通信过程的例子说明了...
    随安居士阅读 1,676评论 1 8
  • 一、作用 不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险。 (1)窃听风险...
    XLsn0w阅读 10,517评论 2 44
  • 博客原文传送门:支付网关接入中的ssl连接和签名调试tips 支付平台网关接入是个费力不讨好的活。做过的人都明白,...
    willheng阅读 931评论 0 0
  • 文/007同学 说好要坚持写博,终是没有坚持下来,现实的浮躁繁杂终究挤占了和自己的心灵真实对话的时间。重新搬了家,...
    007同学阅读 252评论 1 5
  • 有很多小伙伴在后台给我留言,说我文章的排版非常好看,想让我写一篇关于“微信公众号文章如何排版”的文章。 当我收到这...
    增长研究观阅读 935评论 6 10