Skip to content

用golang生成密钥和签名证书  #14

@timest

Description

@timest

go version:

$ go version
go version go1.10 darwin/amd64

注意每个代码片段里的变量,在全文里是共用的。比如第一步里生成的私钥,在后面签名将直接拿来使用。

生成ECDSA密钥

type ecdsaGen struct {
	curve elliptic.Curve
}

func (e *ecdsaGen) KeyGen() (key *ecdsa.PrivateKey, err error) {
	privKey, err := ecdsa.GenerateKey(e.curve, rand.Reader)
	if err != nil {
		return nil, err
	}
	return privKey, nil
}

func checkError(err error) {
	if err != nil {
		log.Panic(err)
	}
}

func main() {
	// 生成ecdsa
	e := &ecdsaGen{ curve: elliptic.P256()}
	priKey, err := e.KeyGen()
	checkError(err)
}

priKey是我们生成的密钥,有时候我们想保存为一个pem格式的密钥文件:

priKeyEncode, err := x509.MarshalECPrivateKey(priKey)
checkError(err)
f, err := os.Create("ec.pem")
checkError(err)
pem.Encode(f, &pem.Block{Type: "EC PRIVATE KEY", Bytes: priKeyEncode})
f.close()

经过x509.MarshalECPrivateKey处理过后返回的是[]byte类型的密钥,然后通过pem.Encode写入到文件里。

然后我们可以用:$ openssl ec -text -in ec.pem -noout 命令来查看生成的密钥文件是否正确,后面同样有可以用openssl来检查生成的证书是否正确。
更多openssl命令可以查看openssl常用命令之创建、检查密钥和证书

证书

这里举两个例子,一个是用自己的密钥进行为证书签名,简称自签。还有一种现实中经常遇到的情况,是使用用户提供的密钥生成证书

自签

// 根据ecdsa密钥生成特征标识码
func priKeyHash(priKey *ecdsa.PrivateKey) []byte{
	hash := sha256.New()
	hash.Write(elliptic.Marshal(priKey.Curve, priKey.PublicKey.X, priKey.PublicKey.Y))
	return hash.Sum(nil)
}

func main() {
	template := x509.Certificate{
		SerialNumber:          serialNumber,
		NotBefore:             notBefore,
		NotAfter:              notBefore.Add(expiry).UTC(),
		BasicConstraintsValid: true,
		IsCA:                  true,
		KeyUsage: x509.KeyUsageDigitalSignature |
			x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign |
			x509.KeyUsageCRLSign,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
		Subject: pkix.Name{
			Country:            []string{"CN"},
			Locality:           []string{"zhongguancun"},
			Province:           []string{"Beijing"},
			OrganizationalUnit: []string{"tect"},
			Organization:       []string{"paradise"},
			StreetAddress:      []string{"street", "address", "demo"},
			PostalCode:         []string{"310000"},
			CommonName:         "demo.example.com",
		},
	}
	template.SubjectKeyId = priKeyHash(priKey)
	x509certEncode, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey, priKey)
	checkError(err)
	crt, err := os.Create("cert.crt")
	checkError(err)
	pem.Encode(crt, &pem.Block{Type: "CERTIFICATE", Bytes: x509certEncode})
	crt.Close()
}

先生成x509的证书模板,然后通过x509.CreateCertificate创建证书,看下这个函数的定义:

func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv interface{}) (cert []byte, err error) {
    ...
}

当参数templateparent一样时,即为自签,代码最后将证书保存为cert.crt
如果想用openssl命令自签请参考:创建证书(自签名)

使用用户提供的密钥生成证书

我们假设有个bob,想让我们为他的密钥提供证书签名:

func main() {
   // 使用bob的密钥进行证书签名
	bobPriKey, _ := e.KeyGen()
	bobPriKeyEncode, err := x509.MarshalECPrivateKey(bobPriKey)
	checkError(err)
	bobf, err := os.Create("bob.pem")
	checkError(err)
	pem.Encode(bobf, &pem.Block{Type: "EC PRIVATE KEY", Bytes: bobPriKeyEncode})
	bobf.Close()

	bobPubKey := bobPriKey.Public()
	bobSerialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
	notBefore = time.Now().Add(-5 * time.Minute).UTC()
	bobTemplate := x509.Certificate{
		SerialNumber:          bobSerialNumber,
		NotBefore:             notBefore,
		NotAfter:              notBefore.Add(expiry).UTC(),
		BasicConstraintsValid: true,
		IsCA:                  false,
		KeyUsage: x509.KeyUsageDigitalSignature |
			x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign |
			x509.KeyUsageCRLSign,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
		Subject: pkix.Name{
			Country:            []string{"CN"},
			Locality:           []string{"Locality"},
			Province:           []string{"Beijing"},
			OrganizationalUnit: []string{"tect"},
			Organization:       []string{"paradise"},
			StreetAddress:      []string{"street", "address", "demo"},
			PostalCode:         []string{"310000"},
			CommonName:         "demo.example.com",
		},
	}
	bobTemplate.SubjectKeyId = priKeyHash(bobPriKey)
	parent, err := x509.ParseCertificate(x509certEncode)
	checkError(err)
	// 这里的第三个参数是我们上面自签的证书,公钥是bob的公钥
	bobCertEncode, err := x509.CreateCertificate(rand.Reader, &bobTemplate, parent, bobPubKey, priKey)
	checkError(err)

	bcrt, _ := os.Create("bob.crt")
	pem.Encode(bcrt, &pem.Block{Type: "CERTIFICATE", Bytes: bobCertEncode})
	bcrt.close()
}

代码和自签差不多,要注意的是这一句代码:

// 这里的第三个参数是我们上面自签的证书,公钥是bob的公钥
bobCertEncode, err := x509.CreateCertificate(rand.Reader, &bobTemplate, parent, bobPubKey, priKey)

最后附上全文代码:Gist !

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions