This is a creation in Article, where the information may have evolved or changed.
Objective
A digital certificate is a file that is digitally signed by the Certificate Authority center that contains public key owner information and a public key. Certificate issuance involves knowledge of asymmetric cryptography, which describes the use of the X509 standard library in Golang for certificate self-issuance, and how to use Golang for two-way authentication after a certificate is issued.
Self-issued certificate
Generate Root Certificate
The root certificate is the certificate issued by the CA Certification Center to itself and is the starting point of the chain of trust. Here we do it ourselves. The CA uses the OpenSSL command to generate the root certificate.
First generate the private key
openssl genrsa -out key.pem 2048
Then extract the public key based on the private key
openssl rsa -in key.pem -pubout -out key.pub
Start to generate self-signed certificate in X509 format, you will be asked to enter the various information of the distinguished name DN (country, city, organization, name, email, etc.).
penssl req -x509 -new -days 365 -key rsakey.pem -out cert.crt
At this point, the root certificate is made.Let's start using golang to issue the next-level certificate based on the root certificate.
Self-signed certificate using golang
Regarding the process of self-signed certificate, here is a brief introduction. Golang's x509 standard library has a Certificate structure. This structure is the corresponding entity after the certificate is parsed. The new certificate needs to generate a key pair first, and then use the private key of the root certificate. For signature, certificate, private key and public key, here is the pem encoding method.
First read the certificate and private key of the root certificate
// Parse the root certificate
caFile, err: = ioutil.ReadFile (rootCa)
if err! = nil {
return
}
caBlock, _: = pem.Decode (caFile)
cert, err: = x509.ParseCertificate (caBlock.Bytes)
if err! = nil {
return
}
// Parse the private key
keyFile, err: = ioutil.ReadFile (rootKey)
if err! = nil {
return
}
keyBlock, _: = pem.Decode (keyFile)
praKey, err: = x509.ParsePKCS1PrivateKey (keyBlock.Bytes)
if err! = nil {
return
}
Then you need to generate a template for the new certificate.The fields inside are filled in according to your needs.
cer: = & x509.Certificate {
SerialNumber: big.NewInt (rd.Int63 ()), // certificate serial number
Subject: pkix.Name {
Country: [] string {"CN"},
Organization: [] string {"Easy"},
OrganizationalUnit: [] string {"Easy"},
Province: [] string {"ShenZhen"},
CommonName: equi.Code,
Locality: [] string {"ShenZhen"},
},
NotBefore: time.Now (), // Certificate validity start time
NotAfter: time.Now (). AddDate (1, 0, 0), // end time of certificate validity period
BasicConstraintsValid: true, // Basic validity constraints
IsCA: false, // Whether it is a root certificate
ExtKeyUsage: [] x509.ExtKeyUsage {x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, // certificate usage (client authentication, data encryption)
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment,
EmailAddresses: [] string {"test@test.com"},
IPAddresses: [] net.IP {net.ParseIP ("192.168.1.59")},
}
When this information is obtained, a certificate can be issued, and key and ca are the issued certificate and private key.
// Generate a public key private key pair
priKey, err: = rsa.GenerateKey (rand.Reader, 2048)
if err! = nil {
return
}
ca, err = x509.CreateCertificate (rand.Reader, equiCer, rootCa, & priKey.PublicKey, rootKey)
if err! = nil {
return
}
// Encode the certificate file and private key file
caPem: = & pem.Block {
Type: "CERTIFICATE",
Bytes: ca,
}
ca = pem.EncodeToMemory (caPem)
buf: = x509.MarshalPKCS1PrivateKey (priKey)
keyPem: = & pem.Block {
Type: "PRIVATE KEY",
Bytes: buf,
}
key = pem.EncodeToMemory (keyPem)
Two-way authentication using golang
Client
// Load the client certificate
// Loaded here is issued by the server
cert, err: = tls.LoadX509KeyPair ("client_cert.pem", "client_key.pem")
if err! = nil {
log.Fatalln (err)
}
config: = & tls.Config {
// The server certificate is not verified here, it is issued by yourself
InsecureSkipVerify: true,
Certificates: [] tls.Certificate {cert},
}
raddr, err: = net.ResolveTCPAddr ("tcp", "192.168.1.59:6001")
if err! = nil {
log.Fatalln (err)
}
conn, err: = net.DialTCP ("tcp", nil, raddr)
if err! = nil {
log.Fatalln (err)
}
tlsConn: = tls.Client (conn, config)
tlsConn is the same as net.Conn. When Wirte is called, a handshake is performed.If the server certificate does not meet the requirements, an error is returned.
Server
// The root certificate is read here
buf, err: = ioutil.ReadFile (d.conf.Tls.CA)
if err! = nil {
return
}
pool: = x509.NewCertPool ()
pool.AppendCertsFromPEM (buf)
// Load server certificate
cert, err: = tls.LoadX509KeyPair (d.conf.Tls.Cert, d.conf.Tls.Key)
if err! = nil {
return
}
tlsConfig: = & tls.Config
Certificates: [] tls.Certificate {cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: pool,
}
// After accept to conn
tlsConn: = tls.Server (conn, tlsConfig)
This tlsConn is the same as the client, and you can also manually call Handshake for handshake.
Conclusion
Here is only a brief introduction, and the knowledge about certificates is not limited to this. For example, the x509 standard library can also generate certificate issue requests, parse certificate revocation lists, and so on. You need to practice more to understand the certificate.