openssl 與 keytool 都是證書的相關(guān)工具。但是兩者使用方法各有不同。
openssl 使用方法
第一步、生成根證書:
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 3650 -out ca.crt
或者直接使用下面命令生成兩個(gè) crt 和 key
openssl req -x509 -new -nodes -keyout ca.key -out ca.crt -days 3650
參數(shù)
-nodes: 不使用密碼
第二步、生成服務(wù)端證書:
# server.key 為私鑰
openssl genrsa -out server.key 2048
#這里的/cn是必須添加的 是服務(wù)端的域名或者是etc/hosts中的ip別名 也可以是使用*
openssl req -new -key server.key -subj "/CN=bgi_bc" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650
第三步、生成客戶端證書:
這一步與第二部相同,只是一個(gè)是用于服務(wù)端,一個(gè)是用于客戶端使用
opensslgenrsa -out client.key 2048
# /cn是服務(wù)端的域名 或者是etc/hosts中的ip別名 也可以是使用*
openssl req -new -key client.key -subj "/CN=*" -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 3650
keytool 使用方法
keytool 是個(gè)密鑰和證書管理工具。它使用戶能夠管理自己的公鑰/私鑰對(duì)及相關(guān)證書,用于(通過數(shù)字簽名)自我認(rèn)證(用戶向別的用戶/服務(wù)認(rèn)證自己)或數(shù)據(jù)完整性以及認(rèn)證服務(wù)。
keystore:keytool將密鑰和證書存在一個(gè)稱為keystore的文件中。
在keystore包含兩種數(shù)據(jù):
- 密鑰實(shí)體——密鑰(secret key)又或者是私鑰和配對(duì)密鑰(非對(duì)稱加密)
- 可信任的證書實(shí)體——只包含公鑰
Alias(別名):每個(gè)keystore都關(guān)聯(lián)這一個(gè)獨(dú)一無二的alias,通常不區(qū)分大小寫。
keystore存儲(chǔ)位置:在沒有制定生成位置的情況下,keystore會(huì)存到用戶的系統(tǒng)默認(rèn)目錄。
keytool 生成證書的命令
1、生成證書,存儲(chǔ)到 jks 文件中
keytool -genkey -alias [這里是證書別名] -keyalg RSA -keystore [這里寫證書的存儲(chǔ)文件和位置格式為jks] -validity [這里填有效天數(shù)] -keysize 2048
2、查看存儲(chǔ)文件中是否有證書
keytool -list -v -keystore [這里寫證書的存儲(chǔ)文件和位置格式為jks]
3、從 store 文件中導(dǎo)出證書,這里導(dǎo)出的是二進(jìn)制證書
keytool -export -alias [這里是證書別名] -keystore selfsigned.jks -file [證書文件名]
4、如果要導(dǎo)出 pem 格式的十六進(jìn)制證書需要加參數(shù) -rfc
keytool -export -alias [這里是證書別名] -keystore selfsigned.jks -rfc -file [證書文件名]
5、導(dǎo)出 pkcs12 客戶端使用證書,導(dǎo)出的證書文件格式一般是 .p12 或者 .pfx 。
keytool -alias [這里是證書別名] -v -importkeystore -srckeystore [這里是jks 證書存儲(chǔ)文件] -srcstoretype jks -destkeystore [pfx 文件名] -deststoretype pkcs12
6、導(dǎo)出私鑰
jks文件中的私鑰不能直接得到,需要通過openssl將jks文件轉(zhuǎn)換成pkcs12格式后再進(jìn)行提取。
openssl pkcs12 -in [pfx 類型的文件,或者p12類型文件] -nocerts -nodes -out [私鑰文件]
keytool 示例
在生成證書過程中會(huì)需要設(shè)置密碼,地區(qū),公司組織等相關(guān)信息,自己使用是可以隨便輸入。
keytool -genkey -alias ville -keyalg RSA -keystore ville.jks -validity 3650 -keysize 2048
keytool -list -v -keystore ville.jks
keytool -export -alias ville -keystore ville.jks -rfc -file ville.crt
keytool -alias ville -v -importkeystore -srckeystore ville.jks -srcstoretype jks -destkeystore ville.pfx -deststoretype pkcs12
openssl pkcs12 -in ville.pfx -nocerts -nodes -out ville.key
生成如下文件:
ville.jks ville.key ville.crt ville.pfx
其中 ville.key 是私鑰,ville.crt 是提公鑰, ville.pfx 是瀏覽器之類使用私鑰的文件。
Golang 證書認(rèn)證
在 openssl 和 key 中我們生成了 ca , server, client , ville 等相關(guān)的證書。
使用場(chǎng)景
我們這里假設(shè) server 的證書是與 client 證書不是同一個(gè)地方生成的,他們是兩個(gè)不同的公司提供的,ca 、client 和 ville 是屬于客戶端公司的證書,server 是屬于服務(wù)端公司的證書??蛻舳斯拘枰?qǐng)求服務(wù)端公司的接口。1、首先客戶端公司要啊把他們的證書(公鑰 .crt)提供給服務(wù)端公司,服務(wù)端公司要把客戶端提供的證書加載到服務(wù)中,這樣服務(wù)請(qǐng)求的時(shí)候就好校驗(yàn)客戶端的請(qǐng)求。
2、服務(wù)端公司也要把他們的服務(wù)證書(公鑰 .crt)提供給客戶公司,客戶端公司要加載到客戶端程序,用以校驗(yàn)請(qǐng)求到的數(shù)據(jù)是服務(wù)公司的。
下面我們就以 Golang 的雙向認(rèn)證來做示例
SSL 雙向認(rèn)證
1、 服務(wù)端程序
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
)
type httpsHandler struct {
}
func addTrust(pool*x509.CertPool, path string) {
aCrt, err := ioutil.ReadFile(path)
if err!= nil {
fmt.Println("ReadFile err:",err)
return
}
pool.AppendCertsFromPEM(aCrt)
}
func (*httpsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "golang https server!!!")
}
func main() {
pool := x509.NewCertPool()
// 這里是加載客戶端提供的證書,最好是加載客戶端提供的根證書
addTrust(pool,"../sslfile/ca.crt")
addTrust(pool,"../sslfile/ville.crt")
s := &http.Server{
Addr: ":8080",
Handler: &httpsHandler{},
TLSConfig: &tls.Config{
ClientCAs: pool,
ClientAuth: tls.RequireAndVerifyClientCert,
},
}
// 這里是加載 服務(wù)端自己的證書和私鑰
if err := s.ListenAndServeTLS("../sslfile/server.crt", "../sslfile/server.key"); err != nil {
log.Fatal("ListenAndServeTLS err:", err)
}
}
2、客戶端
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
)
func addTrust(pool*x509.CertPool, path string) {
aCrt, err := ioutil.ReadFile(path)
if err!= nil {
fmt.Println("ReadFile err:",err)
return
}
pool.AppendCertsFromPEM(aCrt)
}
func TwoWaySSlCheck(){
pool := x509.NewCertPool()
// 這里加載服務(wù)端提供的證書,用于校驗(yàn)服務(wù)端返回的數(shù)據(jù)
addTrust(pool,"../sslfile/server.crt")
// 這里加載客戶端自己的證書,要與提供給服務(wù)端的證書一致,不然服務(wù)端校驗(yàn)會(huì)不通過
cliCrt, err := tls.LoadX509KeyPair("../sslfile/ville.crt", "../sslfile/ville.key")
if err != nil {
fmt.Println("Loadx509keypair err:", err)
return
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
Certificates: []tls.Certificate{cliCrt},
},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://localhost:8080")
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
func main(){
TwoWaySSlCheck()
}