由于蘋果對加密算法的要求
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<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>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;
}