自簽SSL證書及服務端和客戶端的使用
基本生成步驟:
- 生成CA根證書
- 生成服務端證書
- 生成客戶端證書(如果需要做雙向認證的話)
1.生成根證書
# 生成root私鑰
openssl genrsa -out root.key 1024
# 根據私鑰創建根證書請求文件,需要輸入一些證書的元信息:郵箱、域名等
openssl req -new -out root.csr -key root.key
# 結合私鑰和請求文件,創建根證書,有效期10年
openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 3650
2.生成服務端證書
# 創建服務端私鑰
openssl genrsa -out server.key 1024
# 根據私鑰生成請求文件
openssl req -new -out server.csr -key server.key
# 結合私鑰和請求文件創建服務端證書,有效期10年
openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA ../root_ca/root.crt -CAkey ../root_ca/root.key -CAcreateserial -days 3650
如果需要只需要部署服務端證書端話,就可以結束了。拿着server.crt公鑰和server.key私鑰部署在服務器上,然后解析域名到改服務器指向到IP,證書就部署成功了。
3.生成客戶端證書
如果需要做雙向驗證的,也就是服務端要驗證客戶端證書的情況。那么需要在同一個根證書下再生成一個客戶端證書
# 生成私鑰
openssl genrsa -out client.key 1024
# 申請請求文件
openssl req -new -out client.csr -key client.key
# 生成證書
openssl x509 -req -in client.csr -out client.crt -signkey client.key -CA ../root_ca/root.crt -CAkey ../root_ca/root.key -CAcreateserial -days 3650
# 生成客戶端集成證書pkcs12格式的文件,方便瀏覽器或者http客戶端訪問(密碼:123456)
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
服務器配置
1.服務域名加證書
使用Caddy作為服務器來部署https服務
Caddyfile
{
admin off # 關閉運維接口
auto_https off # 自動配置https關閉
}
https://example.com {
tls <證書文件路徑> <私鑰文件路徑>
}
2.需要雙向證書認證
{
admin off # 關閉運維接口
auto_https off # 自動配置https關閉
}
https://example.com {
tls <證書文件路徑> <私鑰文件路徑> {
client_auth {
mode require_and_verify
trusted_ca_cert_file <根證書文件路徑>
}
}
}
客戶端配置
需要在客戶端設置根證書,並信任根證書。因為服務端的證書是自簽的證書,公共的CA是無法認證自簽的證書的。所以需要在客戶端添加並信任自己的根證書后才能通過https訪問服務端。如果服務器上部署的是公共CA簽發的證書,則不需要設置,因為系統中已經內置了大部分公共CA的證書
使用Postman
Settings -> Certificates -> CA Certificates -> PEM file
中選擇根證書文件。
配置好后就可以使用https訪問服務了
使用HttpClient訪問如何配置客戶端
使用程序來調用https會比較復雜,大概總結起來可以分為一下幾個步驟:
- 讀取根證書文件流
- 獲取KeyStore
- 將根證書加載到KeyStore中
- 獲取SSLContexts實例
- 將SSLContexts實例作為參數,設置協議http和https對應的處理socket鏈接工廠的對象
- 將鏈接工廠作為作為參數,生成鏈接管理器
- 將鏈接管理器作為參數,生成httpclient客戶端
public class SSLContextBuilder {
public static String build(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;
}
}
public class HttpClientInstanceBuilder {
public static CloseableHttpClient builde() {
// 如果密碼為空,則用"nopassword"代替
SSLContext sslcontext = SSLContextBuilder.build("<根證書文件路徑>", "<證書密碼>");
// 設置協議http和https對應的處理socket鏈接工廠的對象
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(sslcontext))
.build();
PoolingHttpClientConnectionManager connManager =
new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpClient clientInstance = HttpClients
.custom()
.setConnectionManager(connManager)
.build();
return clientInstance;
}
}
這樣就能拿到支持訪問自簽證書的httpClient客戶端了
https雙向認證
如果服務器開啟了雙向認證,那么客戶端還要提供同和服務器證書同屬於一個根證書的客戶端證書。
給客戶端添加客戶端證書和私鑰
客戶端添加證書可以采用PKCS格式證書,它會將證書和私鑰綁定到一個文件中
那如何加載.p12
或.pfx
文件到客戶端?
和加載根證書類似
public class SSLContextBuilder {
// 如果密碼為空,則用"nopassword"代替
public static String build(String keyStorePath, String keyStorepass) {
SSLContext sc = null;
FileInputStream instream = null;
KeyStore trustStore = null;
try {
KeyStore trustStore = KeyStore.getInstance("PKCS12");
instream = new FileInputStream(new File(keyStorePath));
trustStore.load(instream, keyStorepass.toCharArray());
// 和根證書加載到區別就在這里,加載證書的方式不同
sc = SSLContexts.custom().loadKeyMaterial(trustStore, keyStorepass.toCharArray()).build();
} catch (KeyStoreException | NoSuchAlgorithmException| CertificateException | IOException | KeyManagementException e) {
e.printStackTrace();
} finally {
try {
instream.close();
} catch (IOException e) {
}
}
return sc;
}
}