由于苹果对加密算法的要求
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;
}