Https的认证证书简介

来源:互联网 发布:法律讲堂网络 编辑:程序博客网 时间:2024/05/16 01:29

前言:

CA认证机构默认给150多个网站做了安全认证,也就是说有150多个证书是合法的,要想得到一个合法签名证书,需要到CA认证机构获取,但个人认为这并没有神马卵用(只是浪费钱,12306用的就是自己签名的证书),只是在浏览器访问时,不再提示非法网址罢了,而我们自己创建的网址,对客户端严格来说,是需要双向验证客户端和服务器的证书的。HTTPS也是为此而生的,Https的每次请求连接,都需要经过三次握手,其实就是对客户端和服务器是否正确的校验,握手成功后才会发送数据;HTTPS是HTTP的之下加入了SSL(Secure Socket Layer),安全的基础就靠这个SSL(可理解为签名证书)了,SSL/TLS证书包含身份识别(common name)和公钥,所以要生成SSL/TLS证书这里需要准备这两个东西。


ssl作用 :

  1. 认证用户和服务器,确保数据发送到正确的客户机和服务器;(验证证书)
  2. 加密数据以防止数据中途被窃取;(加密)
  3. 维护数据的完整性,确保数据在传输过程中不被改变。(摘要算法)

使用:

采用HTTPS协议的”服务器”必须要有一套数字证书(CA),可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,若客户端为看到有验证的代码,则很可能服务器用的是受信机构颁发的证书,而使用受信任的公司申请的证书则不会弹出提示页面。这套证书其实就是一对公钥和私钥。

  • 什么是自签名证书?
    就是没有通过受信任的证书颁发机构, 自己给自己颁发的证书.

  • 自签名证书怎么生成?

  • KeyTool 生成自签名证书:

    • 服务器生成证书:
      keytool -genkey -alias tomcat -keypass 123456 -keyalg RSA -keysize 1024 -validity 365 -keystore D:/keys/tomcat.keystore -storepass 123456
  • 客户端生成证书:
    keytool -genkey -alias client1 -keypass 123456 -keyalg RSA -keysize 1024 -validity 365 -storetype PKCS12 -keystore D:/keys/client1.p12 -storepass 123456

  • 让服务器信任客户端证书:必须先把客户端证书导出为一个单独的CER文件,使用如下命令:
    keytool -export -alias client1 -keystore D:/keys/client.p12 -storetype PKCS12 -keypass 123456 -file D:/keys/client.cer(CER文件的密码可能需要重新输入)

  • 然后将两个文件导入到服务器的证书库,添加为信任证书:keytool -import -v -file D:/keys/client.cer -keystore D:/keys/tomcat.keystore -storepass 123456

  • 补充:

    1. 所有公钥证书都有两种格式:纯文本的.crt格式或是二进制的.cer格式,两种都可以用。将生成的文件.keystore 复制到项目路径 /androidappdir/res/raw/ 中;
    2. 两种证书的生成用的是默认的公钥私钥,新的公钥和私钥的生成方法:若安装了git,点开git->Git Bash输入ssh-keygen,然后一直点回车,这样就会在c/Users/Administrator/.ssh中生成两个文件:id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥;
    3. 查看”签名文件”信息方法:
      在dos或Terminal中的输入(jdk中的keytool)命令:
      keytool -v -list -keystore keystore绝对路径,或直接进入到目录下在dos或Terminal中执行:
      keytool -v -list -keystore debug.keystore
  • 如何实现Https的签名验证功能呢?

  • okhttp3方式:
    new OkHttpClient.Builder().sslSocketFactory(getSSLSocketFactory(context, null)) //服务器单项校验,注意这里用.socketFactory()方式是不行的。

参考方式一(服务器单项校验):

  /**   * 获取Https证书Factory   * 参数:certificates:可以设置多个https证书   **/  protected static SSLSocketFactory getSSLSocketFactory(Context context, @Nullable int[] certificates) {    if (context == null) {      throw new NullPointerException("context == null");    }    CertificateFactory certificateFactory;    try {      certificateFactory = CertificateFactory.getInstance("X.509");      KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());      keyStore.load(null, null);      InputStream certificate = context.getAssets().open("pay.jlcxtx.com.cer");       //单个证书的读取方式时使用      //for (int i = 0; i < certificates.length; i++) {  //多个证书的读取方式时使用      //  InputStream certificate = context.getResources().openRawResource(certificates[i]);        keyStore.setCertificateEntry("httpsCert",certificateFactory.generateCertificate(certificate));        if (certificate != null) {          certificate.close();        }      //}      SSLContext sslContext = SSLContext.getInstance("TLS");      TrustManagerFactory trustManagerFactory =          TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());      trustManagerFactory.init(keyStore);      //服务器校验为null,因为服务器使用的是受信机构颁发的证书      sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());       return sslContext.getSocketFactory();    } catch (Exception e) {      e.printStackTrace();    }    return null;  }



方式二(服务器、客户端双项校验):

 public class MySSLSocketFactory {      private static final String KEY_STORE_TYPE_BKS = "bks";//证书类型      private static final String KEY_STORE_TYPE_P12 = "PKCS12";//证书类型      private static final String KEY_STORE_PASSWORD = "****";//证书密码(应该是客户端证书密码)      private static final String KEY_STORE_TRUST_PASSWORD = "***";//授信证书密码(应该是服务端证书密码)      public static SSLSocketFactory getSocketFactory(Context context) {          InputStream trust_input = context.getResources().openRawResource(R.raw.trust);//服务器授信证书          InputStream client_input = context.getResources().openRawResource(R.raw.client);//客户端证书          try {                      SSLContext sslContext = SSLContext.getInstance("TLS");                       KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); //服务器的                    trustStore.load(trust_input, KEY_STORE_TRUST_PASSWORD.toCharArray());                       KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);    //客户端的                    keyStore.load(client_input, KEY_STORE_PASSWORD.toCharArray());                      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());                      trustManagerFactory.init(trustStore);  //客户端校验服务器                    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());                     keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray());                     sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());                     SSLSocketFactory factory = sslContext.getSocketFactory();                     return factory;                 } catch (Exception e) {                             e.printStackTrace();                             return null;                 } finally {                             try {                                  trust_input.close();                                   client_input.close();                            } catch (IOException e) {                                  e.printStackTrace();                              }                  }  }  
  • 除以上,andorid客户端还有证书锁定功能:

       1. 证书锁定好处:你想破解该通信,需要首先拿到客户端,然后对其反编译,修改后再重新打包签名,相比原先的做法,这无疑是增加了破解难度。   2. 除此之外,由于证书锁定可以使用自签名的证书,那就意味着我们不需要再向Android认可的证书颁发机构购买证书了,这样就可以剩下每年1000多块钱的证书费用;也就是说证书锁定就是在代码中验证当前服务器是否持有某张指定的证书,如果不是则强行断开链接。   3. Retrofit的https证书设置及验证:(okhttp的请求,在不验证时,默认直接通过所有的https连接)
       4. 实现方法:            OkHttpClient client = new OkHttpClient.Builder()                .certificatePinner(new CertificatePinner.Builder()                .add("sbbic.com", "sha1/C8xoaOSEzPC6BgGmxAt/EAcsajw=") //可添加多个证书                .add("closedevice.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")                .build());


有关网址推荐 :

http://blog.csdn.net/sk719887916/article/details/51597816
http://blog.csdn.net/dd864140130/article/details/52625666
http://www.cnblogs.com/zhangzb/p/5200418.html
证书传递、验证和数据加密、解密过程解析:http://blog.csdn.net/clh604/article/details/22179907


  • OkHttpUtils方式:
     try {              InputStream is1 = getAssets().open("api.jlcxtx.com.cer");              OkHttpUtils.getInstance().setCertificates(is1);            } catch (Exception e) {                LogUtil.showLog("https e = "+e);            }


请求中携带参数的好处(简):

请求中带有version是为了:不同APP版本,某个接口的参数类型等调整后,旧版本的APP会引起崩溃问题而设定的,有了version那么服务器可以根据version传回不同的数据;
请求中带有time时间戳是为了:限制某个请求的有效性,如若是某个请求被劫持后发送给服务器,那么时间很可能已经超时了,那么该请求服务器可认为是无效的请求了;
请求中带有APPID项目ID是为了:告诉服务器,向前请求是哪个项目APP发出的,需要用这个项目对应的APPKey进行(解析或加密后比对数据),若APP传过来的密文和自己加密后生产的密文不同,则表示数据已被中间人篡改了,然后忽略该请求即可,有了此步校验,这样便防止了数据被截获后,循环式请求数据;
这里的version和time可以放到请求头中,传给服务器;

总结:

1.浏览器将自己支持的一套加密算法、HASH算法发送给服务器。
2.服务器从中选出一组加密算法与HASH算法,并将自己的身份信息(加密公钥、证书颁发机构、过期时间、公钥等)以证书的形式发回给前端。
3.客户端首先向一个权威的服务器检查证书的合法性,如果证书合法,客户端产生一段随机数,这个随机数就作为通信的密钥,我们称之为对称密钥,
4.用公钥加密这段随机数并发送到服务器,这段随机数以后用于对所有数据进行对称加密使用;若不合法,则检查本地是否有设置服务器证书,对比后一致,也会生成一个随机数,并用服务器发来的公钥加密后给服务器;
5.服务器用密钥解密获取获取到该随机数,然后,双方就以对称密钥(该随机数)进行加密解密通信了。