| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- // Package pki contains bits and pieces to work with OpenVPN PKI related operations.
- package pki
- import (
- "crypto/rand"
- "crypto/rsa"
- "crypto/x509"
- "crypto/x509/pkix"
- "encoding/asn1"
- //"github.com/Sirupsen/logrus"
- "bytes"
- "encoding/pem"
- "fmt"
- "math/big"
- "time"
- )
- const (
- _CrtExpireYears = 10
- _CrtKeyLength = 2024
- )
- // CertHolder encapsulates a public certificate and the corresponding private key.
- type CertHolder struct {
- Cert string // PEM Encoded Certificate
- Key string // PEM Encoded Private Key
- }
- // CA is a special type of CertHolder that also has a CSR in it.
- type CA struct {
- CertHolder
- CSR string
- }
- // NewCA returns a newly generated CA.
- //
- // This will generate a public/private RSA keypair and a authority certificate signed by itself.
- func NewCA() (*CA, error) {
- type basicConstraints struct {
- IsCA bool `asn1:"optional"`
- MaxPathLen int `asn1:"optional,default:-1"`
- }
- key, err := rsa.GenerateKey(rand.Reader, _CrtKeyLength)
- if err != nil {
- return nil, fmt.Errorf("private key cannot be created: %s", err)
- }
- val, err := asn1.Marshal(basicConstraints{true, 0})
- if err != nil {
- return nil, fmt.Errorf("can not marshal basic constraints: %s", err)
- }
- names := pkix.Name{CommonName: "CA"}
- var csrTemplate = x509.CertificateRequest{
- Subject: names,
- SignatureAlgorithm: x509.SHA512WithRSA,
- ExtraExtensions: []pkix.Extension{
- {
- Id: asn1.ObjectIdentifier{2, 5, 29, 19},
- Value: val,
- Critical: true,
- },
- },
- }
- csrCertificate, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, key)
- if err != nil {
- return nil, fmt.Errorf("can not create certificate request: %s", err)
- }
- csr := pem.EncodeToMemory(&pem.Block{
- Type: PEMCSRBlockType, Bytes: csrCertificate,
- })
- // Serial number
- serial, err := rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
- if err != nil {
- return nil, err
- }
- now := time.Now()
- // Create the request template
- template := x509.Certificate{
- SerialNumber: serial,
- Subject: names,
- NotBefore: now.Add(-10 * time.Minute).UTC(),
- NotAfter: now.Add(time.Duration(24*365*_CrtExpireYears) * time.Hour).UTC(),
- BasicConstraintsValid: true,
- IsCA: true,
- KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
- //ExtKeyUsage: []x509.ExtKeyUsage{x509.KeyUsageCertSign, x509.ExtKeyUsageClientAuth},
- }
- // Sign the certificate authority
- certificate, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
- if err != nil {
- return nil, fmt.Errorf("failed to generate certificate error: %s", err)
- }
- var request bytes.Buffer
- var privateKey bytes.Buffer
- if err := pem.Encode(&request, &pem.Block{Type: PEMCertificateBlockType, Bytes: certificate}); err != nil {
- return nil, err
- }
- if err := pem.Encode(&privateKey, &pem.Block{Type: PEMRSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(key)}); err != nil {
- return nil, err
- }
- return &CA{
- CertHolder: CertHolder{
- Key: privateKey.String(),
- Cert: request.String(),
- },
- CSR: string(csr),
- }, nil
- }
- // NewServerCertHolder generates a RSA key-pair and a x509 certificate signed by the CA for the server.
- func NewServerCertHolder(ca *CA) (*CertHolder, error) {
- return newCert(ca, true, "localhost")
- }
- // NewClientCertHolder generates a RSA key-pair and a x509 certificate signed by the CA for the client.
- func NewClientCertHolder(ca *CA, username string) (*CertHolder, error) {
- return newCert(ca, false, username)
- }
- // newCert generates a RSA key-pair and a x509 certificate signed by the CA.
- func newCert(ca *CA, server bool, cn string) (*CertHolder, error) {
- // Get CA private key
- block, _ := pem.Decode([]byte(ca.Key))
- if block == nil {
- return nil, fmt.Errorf("failed to parse ca private key")
- }
- caKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
- if err != nil {
- return nil, fmt.Errorf("failed to parse ca private key: %s", err)
- }
- caCert, err := ReadCertFromPEM(ca.Cert)
- if err != nil {
- return nil, fmt.Errorf("failed to parse ca cert: %v", err)
- }
- // Create new cert's key
- key, err := rsa.GenerateKey(rand.Reader, 2048)
- if err != nil {
- return nil, fmt.Errorf("private key cannot be created: %s", err)
- }
- serial, err := rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
- if err != nil {
- return nil, err
- }
- val, err := asn1.Marshal(asn1.BitString{Bytes: []byte{0x80}, BitLength: 2}) // setting nsCertType to Client Type
- if err != nil {
- return nil, fmt.Errorf("can not marshal nsCertType: %v", err)
- }
- now := time.Now()
- tml := x509.Certificate{
- NotBefore: now.Add(-10 * time.Minute).UTC(),
- NotAfter: now.Add(time.Duration(24*365*_CrtExpireYears) * time.Hour).UTC(),
- SerialNumber: serial,
- Subject: pkix.Name{
- CommonName: cn,
- Organization: []string{"OVPM"},
- },
- KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement,
- ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
- BasicConstraintsValid: true,
- ExtraExtensions: []pkix.Extension{
- {
- Id: asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 1, 1},
- Value: val,
- },
- },
- }
- if server {
- tml.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement | x509.KeyUsageKeyEncipherment
- tml.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
- val, err := asn1.Marshal(asn1.BitString{Bytes: []byte{0x40}, BitLength: 2}) // setting nsCertType to Server Type
- if err != nil {
- return nil, fmt.Errorf("can not marshal nsCertType: %v", err)
- }
- tml.ExtraExtensions[0].Id = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 1, 1}
- tml.ExtraExtensions[0].Value = val
- }
- // Sign with CA's private key
- cert, err := x509.CreateCertificate(rand.Reader, &tml, caCert, &key.PublicKey, caKey)
- if err != nil {
- return nil, fmt.Errorf("certificate cannot be created: %s", err)
- }
- priKeyPem := pem.EncodeToMemory(&pem.Block{
- Type: PEMRSAPrivateKeyBlockType,
- Bytes: x509.MarshalPKCS1PrivateKey(key),
- })
- certPem := pem.EncodeToMemory(&pem.Block{
- Type: PEMCertificateBlockType,
- Bytes: cert,
- })
- return &CertHolder{
- Key: string(priKeyPem[:]),
- Cert: string(certPem[:]),
- }, nil
- }
- // NewCRL takes in a list of certificate serial numbers to-be-revoked and a CA then makes a PEM encoded CRL and returns it as a string.
- func NewCRL(ca *CA, serials ...*big.Int) (string, error) {
- caCrt, err := ReadCertFromPEM(ca.Cert)
- if err != nil {
- return "", err
- }
- block, _ := pem.Decode([]byte(ca.Key))
- if block == nil {
- return "", fmt.Errorf("failed to parse ca private key")
- }
- priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
- if err != nil {
- return "", fmt.Errorf("failed to parse ca private key: %s", err)
- }
- var revokedCertList []pkix.RevokedCertificate
- for _, serial := range serials {
- revokedCert := pkix.RevokedCertificate{
- SerialNumber: serial,
- RevocationTime: time.Now().UTC(),
- }
- revokedCertList = append(revokedCertList, revokedCert)
- }
- crl, err := caCrt.CreateCRL(rand.Reader, priv, revokedCertList, time.Now().UTC(), time.Now().Add(365*24*60*time.Minute).UTC())
- if err != nil {
- return "", err
- }
- crlPem := pem.EncodeToMemory(&pem.Block{
- Type: PEMx509CRLBlockType,
- Bytes: crl,
- })
- return string(crlPem[:]), nil
- }
- // ReadCertFromPEM decodes a PEM encoded string into a x509.Certificate.
- func ReadCertFromPEM(s string) (*x509.Certificate, error) {
- block, _ := pem.Decode([]byte(s))
- var cert *x509.Certificate
- cert, _ = x509.ParseCertificate(block.Bytes)
- return cert, nil
- }
|