怎么 在客户端 验证 self-signed 证书. -- FTPS

  • 在 Linux Server 上安装好了 FTPS server, ( vsftpd 工具 )
  • 在Server上生成 SSL 证书, 签证的时候我是 自签名的 self-signed。(当然,可以花钱由CA中心签发电子证书)

          如何 生成 self-signed certificate 见我前面 叙述的文章.

          假定我们现在生成的 证书 为 server.cer

  • 然后在 Client 上根据 server.cer 证书生成客户端的 trustStore

          如果 是self-signed 的证书,一定要在客户端生成相应的trustStore, 因为

          Man-In-Middle-Attact, 就是说,在Server返回Certificate的时候,第三者在中间截断,然后生成一个伪冒的证书发送给Client,这样,Client就会用这张伪冒的证书里面的Public Key去加密生成的发送给Server的Secret Key. 这样Client发送的任何东西,第三方均可以获取.

          所以,我们需要实现建立好trustStore,并用它去验证Server证书的合法性.

          keytool -import -alias ca -file server.cer -keystore jassecacerts

          这里需要密码来 import,可以随便自己定义,但得牢记

          这样我们根据服务器端的 self-signed certificate 生成了 自己的trustStore 叫 jassecacerts
  • Client 创建FPTS实例链接 Server. 验证过程如何?

          根据官方文档,我们可以有两种选择

          1. 把 jassecacerts 部署到 %JAVA_HOME%/lib/security 目录中

          2. 或者用程序     System.setProperty( "javax.net.ssl.trustStore", ".../jssecacerts"); 导入

          然后当 Client 链接 Server (hand shakes) 的时候, jsse 会根据 设置的 TrustStore 去自动校验 certificate的合法性. 具体步骤是这样的, 先在 %JAVA_HOME%/lib/security里面查找 jssecacerts里面有没有,然后再找cacerts里面有没有. 这样去验证 合法性...

          事实真是这样的吗???? 经过我具体的测试,JSSE会去找,但是合法性验证了没,这里,我用的是FTPS做的实验,我打包票的说,没有.... 那JSSE只做了些什么呢? 就是 去找了下有没有..... 如果没有,就直接加入.

 

          看如下的验证过程:

          我先导入Gmail的证书, 用这个证书作为我自己Server证书的一个fake版本的.

          Gmail证书如何生成的,很简单,需要用到 Openssl,

          1. Get the certificate from Gmail

              openssl s_client -showcerts -connect gmail.google.com:443

          2. 然后将 -----BEGIN CERTIFICATE----- 和 -----END CERTIFICATE----- 之间,也包括这两行 直接拷贝到 gmail.cer

          3. keytool -import -alias fake gmail.cert -keystore fakestore

 

          这样,我们就生成了一个 针对我自己server的一个假冒的truststore

 

          然后我把这个假冒的trust store 导入到 %JAVA_HOME%/lib/security中,当然得重命名为 jssecacerts,然后我运行下面的这段测试代码:

 

          FTPS  _ftps = new FTPSClient();

          _ftps.connect( _myserveraddr, 21 );

         System.out.println( "Reply Code:" +_ftps.getReplyCode + "; Reply String: "+ _ftps.getReplyString() );

          String[] enabledProtocols = _ftps.getEnabledProtocols();
          for( String p : enabledProtocols ){
                System.out.println("Enabled protocol for this connection: "+p);
          }

         呵呵,居然返回 234 Proceed with negotiation. 链接成功............

         Enabled protocol for this connection: TLS
         Enabled protocol for this connection: TLSv1

 

         这里我用了一个假的证书放到我的truststore里面,而真的证书根本就没放,它也连接成功。

 

         然后我用 Djavax

         java -Djavax.net.debug=SSL,handshake,data,trustmanager MyApp
从控制台里面发现,似乎 证书被加载到 truststore里面了..

所以试想,如果这里真的有第三者,充当 Man-In-Middle-attact 的角色,
当服务器 将它自己的self-signed证书给客户端的时候,被截取,而截取者将证书改成自己的假冒证书(类似于上面的gmail.cer),里面有自己的public key,
这样,如果当shake hands结束的时候,客户端 exchange的private key就会被 截取这截获了..
所以,我们不能用 JSSE 默认的验证方法.
  • 所以,我们应该怎样去验证证书的合法性呢?我们需要手动创建TrustManager去验证.

          来吧,我们自己去验证合法性... 通过创建一个自己的 TrustManager, 根据我们从服务器上的cer导入的 trustStore

          看下面的代码

         下面是我的核心代码片段.

         _true_cert 是我真正的非仿冒的 truststore

         _true_cert_pwd 是根据server.cer导入truststore时用的密码

         注意,我们创建 一个keystore实例,然后倒入truely证书的truststore,然后根据导入了的keystore生成我们的TrustManager,然后将这个trustmanger set 到我们的FTPSClient中... OK.. 这样我们的客户端在connect的时候,会根据这个TrustManager去做验证了,如果从Server上得到的self-singed certificate和我本地Truststore里面放的certificate不同,就会出错...

 

         有兴趣可以将_true_cert换成我上面的那个假的证书_fake_cert,一验证,发现通过不了.....

javax.net.ssl.SSLHandshakeException: com.ibm.jsse2.util.h: No trusted certificate found

呵呵.......................  仔细看下面的代码吧.. 原来真理都那么简单,可是找的过程却如此复杂

 

        // Set the trust store by System Property
        System.setProperty( "com.ibm.ssl.trustStore",   new File( _true_cert ).getAbsolutePath() );
        System.setProperty( "javax.net.ssl.trustStore", new File( _true_cert ).getAbsolutePath() );

        FTPSClient _ftps = null;
       
        try{   
            _ftps = new FTPSClient();
           
            KeyStore keyStore = KeyStore.getInstance( (KeyStore.getDefaultType()) );   
            keyStore.load(new FileInputStream(  _true_cert ), _true_cert_pwd.toCharArray());   
            java.security.cert.Certificate ca = keyStore.getCertificate( "ca" );
            // To check the certificate is really a fake one.
            System.out.println(ca.toString());
           
            // We need the trustStore to verify the server's certificate manual if it's a self-signed certificate.
            // Set the keyStore for performance enhance when connecting. Optional step for the key manager.
            KeyManagerFactory kmf = KeyManagerFactory.getInstance( "IbmX509" );
            kmf.init( keyStore, _true_cert_pwd.toCharArray());
            KeyManager[] kms = kmf.getKeyManagers();
            _ftps.setKeyManager( kms[0] );

            // Create the trustStore, It's must for verify the self-signed certificate
            TrustManagerFactory tmf    = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );       
            tmf.init(keyStore);
            TrustManager[] tms = tmf.getTrustManagers();

            _ftps.setTrustManager( tms[0] ); // --  very important step to verify the self-signed certificate
           
           
            //Otherwise, if not set the trust manager to verify the self-signed certificate
            //The FTPS client is not so smart to know whether it's a fake or not but just to loaded the certificate into its trustStore
           
            /*
             * Shake hands starting
             */
            _ftps.connect( "prtdevmq.pok.ibm.com", 21);
           
            Assert.assertEquals("Connect sucessfull", true, true );
            System.out.println( "Connect sucessfull, Reply Code:" + _ftps.getReplyCode() +"; Reply String:"+ _ftps.getReplyString() );
           
            String[] enabledProtocols = _ftps.getEnabledProtocols();
            for( String p : enabledProtocols ){
                System.out.println("Enabled protocol for this connection: "+p);
            }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值