golang证书认证通信
本文内容纲要:
-1.Golang中证书相关包
-2.认证过程
-3.单向认证
-4.双向认证
-5.跳过证书验证
1.Golang中证书相关包
-
crypto/tls实现tls1.2和tls1.3。
typeConfigstruct{
...... //Certificatescontainsoneormorecertificatechainstopresenttothe //othersideoftheconnection.Thefirstcertificatecompatiblewiththe //peer'srequirementsisselectedautomatically. // //ServerconfigurationsmustsetoneofCertificates,GetCertificateor
//GetConfigForClient.Clientsdoingclient-authenticationmayseteither //CertificatesorGetClientCertificate. // //Note:iftherearemultipleCertificates,andtheydon'thavethe //optionalfieldLeafset,certificateselectionwillincurasignificant //per-handshakeperformancecost. Certificates[]Certificate //RootCAsdefinesthesetofrootcertificateauthorities //thatclientsusewhenverifyingservercertificates. //IfRootCAsisnil,TLSusesthehost'srootCAset. RootCAs*x509.CertPool //ServerNameisusedtoverifythehostnameonthereturned //certificatesunlessInsecureSkipVerifyisgiven.Itisalsoincluded //intheclient'shandshaketosupportvirtualhostingunlessitis //anIPaddress. ServerNamestring //ClientAuthdeterminestheserver'spolicyfor //TLSClientAuthentication.ThedefaultisNoClientCert. ClientAuthClientAuthType //ClientCAsdefinesthesetofrootcertificateauthorities //thatserversuseifrequiredtoverifyaclientcertificate //bythepolicyinClientAuth. ClientCAs*x509.CertPool //InsecureSkipVerifycontrolswhetheraclientverifiesthe //server'scertificatechainandhostname. //IfInsecureSkipVerifyistrue,TLSacceptsanycertificate //presentedbytheserverandanyhostnameinthatcertificate. //Inthismode,TLSissusceptibletoman-in-the-middleattacks. //Thisshouldbeusedonlyfortesting. InsecureSkipVerifybool ...... }
LoadX509KeyPairreadsandparsesapublic/privatekeypairfromapairoffiles.
funcLoadX509KeyPair(certFile,keyFilestring)(Certificate,error)
-
crypot/x509解析X.509格式的密钥和证书。
typeCertPoolstruct{ //containsfilteredorunexportedfields }
CertPoolisasetofcertificates.
funcNewCertPool()*CertPool
NewCertPool返回一个空的CertPool。
funcSystemCertPool()(*CertPool,error)
SystemCertPoolreturnsacopyofthesystemcertpool.
func(s*CertPool)AppendCertsFromPEM(pemCerts[]byte)(okbool)
AppendCertsFromPEMattemptstoparseaseriesofPEMencodedcertificates.Itappendsanycertificatesfoundtosandreportswhetheranycertificatesweresuccessfullyparsed.
typeCertificatestruct{
Raw[]byte//CompleteASN.1DERcontent(certificate,signaturealgorithmandsignature).
RawTBSCertificate[]byte//CertificatepartofrawASN.1DERcontent.RawSubjectPublicKeyInfo[]byte//DERencodedSubjectPublicKeyInfo.
RawSubject[]byte//DERencodedSubject
RawIssuer[]byte//DERencodedIssuer
Signature[]byte
SignatureAlgorithmSignatureAlgorithm
PublicKeyAlgorithmPublicKeyAlgorithm
PublicKeyinterface{}
Versionint
SerialNumber*big.Int
Issuerpkix.Name
Subjectpkix.Name
NotBefore,NotAftertime.Time//Validitybounds.
KeyUsageKeyUsage
//ExtensionscontainsrawX.509extensions.Whenparsingcertificates,
//thiscanbeusedtoextractnon-criticalextensionsthatarenot
//parsedbythispackage.Whenmarshalingcertificates,theExtensions
//fieldisignored,seeExtraExtensions.
Extensions[]pkix.Extension//Go1.2
//ExtraExtensionscontainsextensionstobecopied,raw,intoany
//marshaledcertificates.Valuesoverrideanyextensionsthatwould
//otherwisebeproducedbasedontheotherfields.TheExtraExtensions
//fieldisnotpopulatedwhenparsingcertificates,seeExtensions.
ExtraExtensions[]pkix.Extension//Go1.2
//UnhandledCriticalExtensionscontainsalistofextensionIDsthat
//werenot(fully)processedwhenparsing.Verifywillfailifthis
//sliceisnon-empty,unlessverificationisdelegatedtoanOS
//librarywhichunderstandsallthecriticalextensions.
//
//UserscanaccesstheseextensionsusingExtensionsandcanremove
//elementsfromthissliceiftheybelievethattheyhavebeen
//handled.
UnhandledCriticalExtensions[]asn1.ObjectIdentifier//Go1.5
ExtKeyUsage[]ExtKeyUsage//Sequenceofextendedkeyusages.
UnknownExtKeyUsage[]asn1.ObjectIdentifier//Encounteredextendedkeyusagesunknowntothispackage.
//BasicConstraintsValidindicateswhetherIsCA,MaxPathLen,
//andMaxPathLenZeroarevalid.
BasicConstraintsValidbool
IsCAbool
//MaxPathLenandMaxPathLenZeroindicatethepresenceand
//valueoftheBasicConstraints'"pathLenConstraint".
//
//Whenparsingacertificate,apositivenon-zeroMaxPathLen
//meansthatthefieldwasspecified,-1meansitwasunset,
//andMaxPathLenZerobeingtruemeanthatthefieldwas
//explicitlysettozero.ThecaseofMaxPathLen==0withMaxPathLenZero==false
//shouldbetreatedequivalentto-1(unset).
//
//Whengeneratingacertificate,anunsetpathLenConstraint
//canberequestedwitheitherMaxPathLen==-1orusingthe
//zerovalueforbothMaxPathLenandMaxPathLenZero.
MaxPathLenint//MaxPathLenZeroindicatesthatBasicConstraintsValid==true
//andMaxPathLen==0shouldbeinterpretedasanactual
//maximumpathlengthofzero.Otherwise,thatcombinationis
//interpretedasMaxPathLennotbeingset.
MaxPathLenZerobool//Go1.4
SubjectKeyId[]byte
AuthorityKeyId[]byte
//RFC5280,4.2.2.1(AuthorityInformationAccess)
OCSPServer[]string//Go1.2
IssuingCertificateURL[]string//Go1.2
//SubjectAlternateNamevalues.(Notethatthesevaluesmaynotbevalid
//ifinvalidvalueswerecontainedwithinaparsedcertificate.For
//example,anelementofDNSNamesmaynotbeavalidDNSdomainname.)DNSNames[]string
EmailAddresses[]string
IPAddresses[]net.IP//Go1.1
URIs[]*url.URL//Go1.10
//Nameconstraints
PermittedDNSDomainsCriticalbool//iftruethenthenameconstraintsaremarkedcritical.
PermittedDNSDomains[]string
ExcludedDNSDomains[]string//Go1.9
PermittedIPRanges[]*net.IPNet//Go1.10
ExcludedIPRanges[]*net.IPNet//Go1.10PermittedEmailAddresses[]string//Go1.10
ExcludedEmailAddresses[]string//Go1.10
PermittedURIDomains[]string//Go1.10
ExcludedURIDomains[]string//Go1.10
//CRLDistributionPoints
CRLDistributionPoints[]string//Go1.2
PolicyIdentifiers[]asn1.ObjectIdentifier
}
示例:
caCertPath:="cert/ca.crt"
caCrt,err:=ioutil.ReadFile(caCertPath)
iferr!=nil{
fmt.Println("ReadFileerr:",err)
return
}
pool.AppendCertsFromPEM(caCrt)
- net/http提供HTTP的client和server实现。
客户端定制:ForcontroloverHTTPclientheaders,redirectpolicy等:
client:=&http.Client{
CheckRedirect:redirectPolicyFunc,
Transport:tr,
}
Transport定制:Forcontroloverproxies,TLSconfiguration,keep-alives,compression等:
tr:=&http.Transport{
MaxIdleConns:10,
IdleConnTimeout:30*time.Second,
DisableCompression:true,
TLSClientConfig:&tls.Config{
RootCAs:pool,
Certificates:[]tls.Certificate{clicrt}
}
}
client:=&http.Client{Transport:tr}
resp,err:=client.Get("https://example.com")
服务端定制:
s:=&http.Server{
Addr:":8080",
Handler:myHandler,
ReadTimeout:10*time.Second,
WriteTimeout:10*time.Second,
MaxHeaderBytes:1<<20,
TLSConfig:&tls.Config{
ClientCAs:pool,
ClientAuth:tls.RequireAndVerifyClientCert,
}
}
//log.Fatal(s.ListenAndServe())
log.Fatal(s.ListenAndServe("server.crt","server.key"))
HTTPS监听
Filescontainingacertificateandmatchingprivatekeyfortheservermustbeprovided.
funcListenAndServeTLS(addr,certFile,keyFilestring,handlerHandler)error
ListenAndServeTLSlistensontheTCPnetworkaddresssrv.AddrandthencallsServeTLStohandlerequestsonincomingTLSconnections.AcceptedconnectionsareconfiguredtoenableTCPkeep-alives.
func(srv*Server)ListenAndServeTLS(certFile,keyFilestring)error
ListenAndServeTLSalwaysreturnsanon-nilerror.AfterShutdownorClose,thereturnederrorisErrServerClosed.Server需要定制时使用。
2.认证过程
单向认证过程:
客户点包含ca.crt,服务端包含server.key和server.crt。
客户端:客户端生成一个随机数random-client,传到服务器端;
服务端:服务器端接收消息之后,生成一个随机数random-server和包含公钥的证书,一起回馈给客户端;
客户端:客户端收到的东西原封不动,加上premastersecret(通过random-client、random-server经过一定算法生成的数据),再一次送给服务器端,这次传过去的东西是经过服务端的公钥进行加密后数据;
服务端:服务端经过私钥(server.key),进行解密,获取premastersecret(协商密钥过程);
此时客户端和服务器端都拥有了三个要素:random-client、random-server和premastersecret,安全通道已经建立,以后的交流都会校检上面的三个要素通过算法算出的sessionkey。
双向认证过程相当于客户端和服务端反过来再执行认证、加解密、协商一遍。
3.单向认证
单向认证只需要服务器端有证书即可。
CA****证书
opensslgenrsa-outca.key4096
opensslreq-x509-new-nodes-keyca.key-subj"/CN=wang.com"-days365-outca.crt
Server****证书
用CA证书签发server证书。
opensslgenrsa-outserver.key2048
opensslreq-new-keyserver.key-subj"/CN=server"-outserver.csr
opensslx509-req-inserver.csr-CAca.crt-CAkeyca.key-set_serial01-outserver.crt-days365
Server.go
服务器监听在:https://server:8088,域名是server证书申请的CN。
单向认证时服务端只需要使用http.ListenAndServeTLS()或srv.ListenAndServeTLS()导入证书即可。一般情况下,不需要配置Server,直接采用默认的http.ListenAndServeTLS()。
packagemain
import(
"fmt"
"net/http"
"os"
)
varAddrstring=":8088"
funchandler(whttp.ResponseWriter,r*http.Request){
w.Write([]byte("Hello"))
}
funcmain(){
http.HandleFunc("/",handler)
_,err:=os.Open("cert/server.crt")
iferr!=nil{
fmt.Println("Can'topenserver.crt")
panic(err)
}
fmt.Printf("listen...[%s]\n",Addr)
err=http.ListenAndServeTLS(Addr,"cert/server.crt","cert/server.key",nil)
iferr!=nil{
fmt.Println(err)
}
}
Client.go
需要提前将server添加到/etc/hosts中以便本地测试。
单向认证时client端需导入CA根证书,需要定制http.Transport。
Golang默认支持HTTP/2协议,只要使用TLS则默认启动HTTP/2特性,但对httpClient做一些定制化配置后,会覆盖掉http库的默认行为,导致开启HTTP/1.1。
packagemain
import(
"fmt"
"crypto/tls"
"crypto/x509"
"flag"
"io/ioutil"
"log"
"net/http"
"golang.org/x/net/http2"
)
varaddr=flag.String("addr","https://server:8088?numa=4&numb=6","connectto")
varhttpVer=flag.Int("httpVer",2,"HTTPversion")
funcmain(){
flag.Parse()
client:=&http.Client{}
caCert,err:=ioutil.ReadFile("cert/ca.crt")
iferr!=nil{
log.Fatalf("Readingservercertificate:%s",err)
}
pool:=x509.NewCertPool()
pool.AppendCertsFromPEM(caCert)
tlsConfig:=&tls.Config{
RootCAs:pool,
}
switch*httpVer{
case1:
client.Transport=&http.Transport{
TLSClientConfig:tlsConfig,
}
case2:
client.Transport=&http2.Transport{
TLSClientConfig:tlsConfig,
}
}
resp,err:=client.Get(*addr)
iferr!=nil{
log.Fatalf("Failedget:%s",err)
}
deferresp.Body.Close()
body,err:=ioutil.ReadAll(resp.Body)
iferr!=nil{
log.Fatalf("Failedreadingresponsebody:%s",err)
}
fmt.Printf("Response%d:%s\nbody:%s\n",resp.StatusCode,resp.Proto,string(body))
}
Curl的-k参数可忽略证书验证:
$curl--cacert"cert/ca.crt"https://server:8088
Hi,Thisisanexampleofhttpsserviceingolang!
$curl-khttps://server:8088
Hi,Thisisanexampleofhttpsserviceingolang!
$curl-vhttps://server:8088
*RebuiltURLto:https://server:8088/
*Trying127.0.0.1...
*Connectedtoserver(127.0.0.1)port8088(#0)
*found133certificatesin/etc/ssl/certs/ca-certificates.crt
*found403certificatesin/etc/ssl/certs
*ALPN,offeringhttp/1.1
*SSLconnectionusingTLS1.2/ECDHE_RSA_AES_128_GCM_SHA256
*servercertificateverificationfailed.CAfile:/etc/ssl/certs/ca-certificates.crtCRLfile:none
*Closingconnection0
如果/CN使用IP地址,就会报如下类似错误:
Gethttps://10.183.47.206:8081:x509:cannotvalidatecertificatefor10.183.47.206becauseitdoesn'tcontainanyIPSANs
Client测试
$gorunclient.go
Response200:HTTP/2.0
body:Hello
$gorunclient.go-httpVer=1
Response200:HTTP/1.1
body:Hello
HTTP/1.1非加密(10数据帧)
HTTP/1.1单向认证(17数据帧)
HTTP/2单向认证(22数据帧)
4.双向认证
双向认证要求客户端和服务端都要有证书,且都用CA证书验证对端证书。
Client****证书
用CA证书签发client证书,而非server证书签发。
注意生成client端证书的时候,要多添加一个字段,golang的server端认证程序会对这个字段进行认证:
opensslgenrsa-outclient.key2048
opensslreq-new-keyclient.key-subj"/CN=client"-outclient.csr
echoextendedKeyUsage=clientAuth>extfile.cnf
opensslx509-req-inclient.csr-CAca.crt-CAkeyca.key-set_serial02-extfileextfile.cnf-outclient.crt-days365
Server.go
双向认证时需定制http.Server,增加CA证书等。
定制的http.Server的Handler是一个interface,需要实现ServeHTTP()接口函数;
加载服务端的公钥和私钥用于解密客户端发送过来的随机字符;
加载CA证书是为了验证客户端的证书是否合格;
packagemain
import(
"fmt"
"net/http"
"io/ioutil"
"crypto/tls"
"crypto/x509"
)
typemyhandlerstruct{
}
func(h*myhandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){
fmt.Fprintf(w,
"Hi,Thisisanexampleofhttpsserviceingolang!\n")
}
funcmain(){
pool:=x509.NewCertPool()
caCertPath:="cert/ca.crt"
caCrt,err:=ioutil.ReadFile(caCertPath)
iferr!=nil{
fmt.Println("ReadFileerr:",err)
return
}
pool.AppendCertsFromPEM(caCrt)
s:=&http.Server{
Addr:":8088",
Handler:&myhandler{},
TLSConfig:&tls.Config{
ClientCAs:pool,
ClientAuth:tls.RequireAndVerifyClientCert,
},
}
fmt.Println("listen...")
err=s.ListenAndServeTLS("cert/server.crt","cert/server.key")
iferr!=nil{
fmt.Println(err)
}
}
Client.go
Client需要定制http.Transport以加载客户端证书和CA证书。
Client的证书需要tls.LoadX509KeyPair()导入。
packagemain
import(
"fmt"
"io/ioutil"
"net/http"
"crypto/tls"
"crypto/x509"
"golang.org/x/net/http2"
)
funcmain(){
//x509.Certificate
pool:=x509.NewCertPool()
caCertPath:="cert/ca.crt"
caCrt,err:=ioutil.ReadFile(caCertPath)
iferr!=nil{
fmt.Println("ReadFileerr:",err)
return
}
pool.AppendCertsFromPEM(caCrt)
cliCrt,err:=tls.LoadX509KeyPair("cert/client.crt","cert/client.key")
iferr!=nil{
fmt.Println("LoadX509keypairerr:",err)
return
}
//tr:=&http2.Transport{//http2协议
tr:=&http.Transport{//http1.1协议
TLSClientConfig:&tls.Config{
RootCAs:pool,
Certificates:[]tls.Certificate{cliCrt},
},
}
client:=&http.Client{Transport:tr}
//resp,err:=client.Get("https://localhost:8088")
resp,err:=client.Get("https://server:8088")
iferr!=nil{
fmt.Println("httpgeterror:",err)
panic(err)
}
body,_:=ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
fmt.Println(resp.Status)
}
Curl验证
$curl--cacertcert/ca.crt--certcert/client.crt--keycert/client.keyhttps://server:8088
Hi,Thisisanexampleofhttpsserviceingolang!
Client测试
$gorundul_client.go
Hi,Thisisanexampleofhttpsserviceingolang
200OK
HTTP/1.1双向认证(19数据帧)
HTTP/2双向认证(24数据帧)
5.跳过证书验证
POSTMAN或curl中都可以跳过单向证书验证,golang也可以滴。
tr:=&http.Transport{
TLSClientConfig:&tls.Config{InsecureSkipVerify:true},
}
client:=&http.Client{Transport:tr}
参考:
-
Golang之双向认证简书
-
https原理以及golang基本实现
-
Go语言的http/2服务器功能及客户端使用
-
gRPC+gRPCGateway能不能不用证书?net/http2必须使用TLS交互;
“h2c”标识允许通过明文TCP运行HTTP/2的协议,此标识符用于HTTP/1.1升级标头字段以及标识HTTP/2overTCP。
通过GoLang全面了解HTTPS
Golang设置https访问,以及http如何重定向到https
本文内容总结:1.Golang中证书相关包,2.认证过程,3.单向认证,4.双向认证,5.跳过证书验证,
原文链接:https://www.cnblogs.com/embedded-linux/p/12585854.html