Nginx Tomcat 构造https服务 ssl支持改进

由于苹果对加密算法的要求

ATS要求服务器必须支持传输层安全(TLS)协议1.2以上版本;证书必须使用SHA256或更高的哈希算法签名;必须使用2048位以上RSA密钥或256位以上ECC算法等等,不满足条件的证书,ATS都会拒绝连接。

前一篇文章构造的自签名证书的加密算法过低不符合要求,这篇文章做一下改进。

1.openssl genrsa -aes256 -out ca.key 8192
2.openssl req -sha256 -new -x509 -days 1826 -key ca.key -out ca.crt
3.openssl pkcs12 -export -clcerts -in ca.crt -inkey ca.key -out root.p12
4.keytool -import -v -trustcacerts -storepass holly.wang -alias root -file ca.crt -keystore root.jks

说一下证书相关的东东(SSL(TLS)、X.509、PEM、DER、CRT、CER、KEY、CSR、P12等)

SSL

Secure Sockets Layer,现在应该叫"TLS",但由于习惯问题,我们还是叫"SSL"比较多.http协议默认情况下是不加密内容的,这样就很可能在内容传播的时候被别人监听到,对于安全性要求较高的场合,必须要加密,https就是带加密的http协议,而https的加密是基于SSL的,它执行的是一个比较下层的加密,也就是说,在加密前,你的服务器程序在干嘛,加密后也一样在干嘛,不用动,这个加密对用户和开发者来说都是透明的.维基百科解释 | RFC定义的标准

OpenSSL

OpenSSL 是一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。
  OpenSSL被曝出现严重安全漏洞后,发现多数通过SSL协议加密的网站使用名为OpenSSL的开源软件包。OpenSSL漏洞不仅影响以https开头的网站,黑客还可利用此漏洞直接对个人电脑发起“心脏出血”(Heartbleed)攻击。据分析,Windows上有大量软件使用了存在漏洞的OpenSSL代码库,可能被黑客攻击抓取用户电脑上的内存数据。

证书标准X.509

X.509 - 这是一种证书标准,主要定义了证书中应该包含哪些内容.数字证书的格式遵循X.509标准。X.509是由国际电信联盟(ITU-T)制定的数字证书标准

编码格式

同样的X.509证书,可能有不同的编码格式,目前有以下两种编码格式.
  PEM - Privacy Enhanced Mail,Apache和NGINX服务器偏向于使用这种编码格式.打开看文本格式,以"-----BEGIN..."开头, "-----END..."结尾,内容是BASE64编码。
查看PEM格式证书的信息:

openssl x509 -in certificate.pem -text -noout

DER - Distinguished Encoding Rules,Java和Windows服务器偏向于使用这种编码格式.打开看是二进制格式,不可读.查看DER格式证书的信息:

openssl x509 -in certificate.der **-inform der** -text -noout

相关的文件扩展名

CRT - certificate的三个字母,证书的意思,常见于NGINX系统,有可能是PEM编码,也有可能是DER编码,大多数应该是PEM编码.
  CER - 还是certificate,还是证书,常见于Windows系统,同样的,可能是PEM编码,也可能是DER编码,大多数应该是DER编码.
  KEY - 通常用来存放一个私钥,并非X.509证书,编码同样的,可能是PEM,也可能是DER.
PEM查看KEY的办法:

openssl rsa -in mykey.key -text -noout

DER查看KEY的办法:

openssl rsa -in mykey.key -text -noout  -inform der

CSR - Certificate Signing Request,即证书签名请求,这个并不是证书,而是向权威证书颁发机构获得签名证书的申请,其核心内容是一个公钥(当然还附带了一些别的信息),在生成这个申请的时候,同时也会生成一个私钥,私钥要自己保管好.
PEM查看CSR的办法:

openssl req -noout -text -in my.csr 

DER查看CSR的办法:

openssl req -noout -text -in my.csr -inform der

PFX/P12 - predecessor of PKCS#12,对ngnix服务器来说,一般CRT和KEY是分开存放在不同文件中的,但Windows的IIS则将它们存在一个PFX文件中,(因此这个文件包含了证书及私钥)这样会不会不安全?应该不会,PFX通常会有一个"提取密码",你想把里面的东西读取出来的话,它就要求你提供提取密码,
生成服务端p12格式根证书

openssl pkcs12 -export -clcerts -in server-cert.cer -inkey server-key.key -out server.p12

PFX使用的时DER编码
把PFX转换为PEM编码方式

openssl pkcs12 -in for-iis.pfx -out for-iis.pem -nodes

这个时候会提示你输入提取代码. for-iis.pem就是可读的文本.
生成pfx的命令类似这样

openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt

其中CACert.crt是CA(权威证书颁发机构)的根证书,有的话也通过-certfile参数一起带进去.这么看来,PFX其实是个证书密钥库.

JKS - 即Java Key Storage,这是Java的专利,跟OpenSSL关系不大,利用Java的一个叫"keytool"的工具,可以将PFX转为JKS,当然了,keytool也能直接生成JKS

 keytool -import -v -trustcacerts -storepass 123456 -alias server -file server-cert.cer -keystore
server.jks

证书编码的转换

PEM转为DER openssl x509 -in cert.crt -outform der -out cert.der
DER转为PEM openssl x509 -in cert.crt -inform der -outform pem -out cert.pem
(提示:要转换KEY文件也类似,只不过把x509换成rsa,要转CSR的话,把x509换成req)

这里附上java利用httpclient实现实现对服务端的https请求
第一种方式 绕过证书的方式(主要是生成 SSLContext 的时候对所有证书都是信任方式)

/** 
     * 模拟请求 
     *  
     * @param url       资源地址 
     * @param map   参数列表 
     * @param encoding  编码 
     * @return 
     * @throws NoSuchAlgorithmException  
     * @throws KeyManagementException  
     * @throws IOException  
     * @throws ClientProtocolException  
     */  
    @SuppressWarnings("deprecation")
    public static String ignoreVerifySSL(String url,String json) {  
        String body = "";  
        try {
            //采用绕过验证的方式处理https请求  
            SSLContext sslcontext = createIgnoreVerifySSL();  
      
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext,
                    new String[] { "SSLv3","TLSv1","TLSv1.1", "TLSv1.2" } ,//SSLv3 TLSv1 TLSv1.1 TLSv1.2
                    null,
                    SSLConnectionSocketFactory.getDefaultHostnameVerifier());
   
            CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
   
            HttpPost httpPost = new HttpPost(url);
            StringEntity stringEntity = new StringEntity(json);
            httpPost.setEntity(stringEntity);
            httpPost.setHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON);
    
            //执行请求操作,并拿到结果(同步阻塞)  
            CloseableHttpResponse response = client.execute(httpPost);  
            //获取结果实体  
            HttpEntity entity = response.getEntity();  
            if (entity != null) {  
                //按指定编码转换结果实体为String类型  
                body = EntityUtils.toString(entity, HTTP.UTF_8);
            }  
            EntityUtils.consume(entity);  
            //释放链接  
            response.close();  
        } catch (ParseException | IOException | KeyManagementException | NoSuchAlgorithmException e) {
            log.error(e);
        }
        return body;  
    }
    
    /** 
     * 绕过验证 
     *   
     * @return 
     * @throws NoSuchAlgorithmException  
     * @throws KeyManagementException  
     */  
    public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {  
        SSLContext sc = SSLContext.getInstance("TLS");  
      
        // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法  
        X509TrustManager trustManager = new X509TrustManager() {  
            @Override  
            public void checkClientTrusted(  
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,  
                    String paramString) throws CertificateException {  
            }  
      
            @Override  
            public void checkServerTrusted(  
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,  
                    String paramString) throws CertificateException {  
            }  
      
            @Override  
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {  
                return null;  
            }  
        };  
      
        sc.init(null, new TrustManager[] { trustManager }, null);  
        return sc;  
    }

第二种方式信任自签名证书

 /** 
     * 设置信任自签名证书 
     *   
     * @param keyStorePath      密钥库路径 
     * @param keyStorepass      密钥库密码 
     * @return 
     */  
    public static SSLContext custom(String keyStorePath, String keyStorepass){  
        SSLContext sc = null;  
        FileInputStream instream = null;  
        KeyStore trustStore = null;  
        try {  
            trustStore = KeyStore.getInstance(KeyStore.getDefaultType());  
            instream = new FileInputStream(new File(keyStorePath));  
            trustStore.load(instream, keyStorepass.toCharArray());  
            // 相信自己的CA和所有自签名的证书  
            sc = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();  
        } catch (KeyStoreException | NoSuchAlgorithmException| CertificateException | IOException | KeyManagementException e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                instream.close();  
            } catch (IOException e) {  
            }  
        }  
        return sc;  
    }
    /** 
     * 模拟请求 
     *  
     * @param url       资源地址 
     * @param map   参数列表 
     * @param encoding  编码 
     * @return 
     * @throws ParseException 
     * @throws IOException 
     * @throws KeyManagementException  
     * @throws NoSuchAlgorithmException  
     * @throws ClientProtocolException  
     */  
    public static String send(String url, String json,String encoding) throws ClientProtocolException, IOException {  
        String body = "";  
          
        //tomcat是我自己的密钥库的密码,你可以替换成自己的  
        //如果密码为空,则用"nopassword"代替  
        SSLContext sslcontext = custom("D:\\work\\jiezhike\\root.jks", "holly.wang");  
          
      /* // 设置协议http和https对应的处理socket链接工厂的对象  
       Registry socketFactoryRegistry = RegistryBuilder.create()
               .register("http", PlainConnectionSocketFactory.INSTANCE)  
               .register("https", new SSLConnectionSocketFactory(sslcontext))  
               .build();  
       PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);  
       HttpClients.custom().setConnectionManager(connManager);  */
       
       // Allow TLSv1 protocol only
       SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
               sslcontext,
               new String[] { "SSLv3","TLSv1","TLSv1.1", "TLSv1.2" } ,//SSLv3 TLSv1 TLSv1.1 TLSv1.2
               null,
               SSLConnectionSocketFactory.getDefaultHostnameVerifier());
      
           //创建自定义的httpclient对象  
//        CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();  
        CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();  
//      CloseableHttpClient client = HttpClients.createDefault();  
          
        //创建post方式请求对象
        HttpPost httpPost = new HttpPost(url);
        StringEntity stringEntity = new StringEntity(json);
        httpPost.setEntity(stringEntity);
        httpPost.setHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON);
        
        //执行请求操作,并拿到结果(同步阻塞)  
        CloseableHttpResponse response = client.execute(httpPost);  
        //获取结果实体  
        HttpEntity entity = response.getEntity();  
        if (entity != null) {  
            //按指定编码转换结果实体为String类型  
            body = EntityUtils.toString(entity, encoding);  
        }  
        EntityUtils.consume(entity);  
        //释放链接  
        response.close();  
        return body;  
    }

你可能感兴趣的:(Nginx Tomcat 构造https服务 ssl支持改进)