github.com/grailbio/base@v0.0.11/security/tls/certificateauthority/tls.go (about) 1 // Copyright 2018 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache-2.0 3 // license that can be found in the LICENSE file. 4 5 // Package certificateauthority implements an x509 certificate authority. 6 package certificateauthority 7 8 import ( 9 "crypto/rand" 10 "crypto/rsa" 11 "crypto/x509" 12 "crypto/x509/pkix" 13 "encoding/pem" 14 "errors" 15 "math/big" 16 "net" 17 "time" 18 19 "github.com/grailbio/base/security/keycrypt" 20 ) 21 22 // CertificateAuthority is a x509 certificate authority. 23 type CertificateAuthority struct { 24 // The amount of allowable clock drift between the systems between 25 // which certificates are exchanged. 26 DriftMargin time.Duration 27 // The keycrypt secret that contains the PEM-encoded signing 28 // certificate and public key. 29 Signer keycrypt.Secret 30 // The x509 certificate. Populated by Init(). 31 Cert *x509.Certificate 32 33 key *rsa.PrivateKey 34 } 35 36 // Init initializes the certificate authority. Init extracts the the 37 // authority certificate and private key from ca.Signer. 38 func (ca *CertificateAuthority) Init() error { 39 pemBlock, err := ca.Signer.Get() 40 if err != nil { 41 return err 42 } 43 for { 44 var derBlock *pem.Block 45 derBlock, pemBlock = pem.Decode(pemBlock) 46 if derBlock == nil { 47 break 48 } 49 switch derBlock.Type { 50 case "CERTIFICATE": 51 ca.Cert, err = x509.ParseCertificate(derBlock.Bytes) 52 if err != nil { 53 return err 54 } 55 case "RSA PRIVATE KEY": 56 ca.key, err = x509.ParsePKCS1PrivateKey(derBlock.Bytes) 57 if err != nil { 58 return err 59 } 60 } 61 } 62 if ca.Cert == nil || ca.key == nil { 63 return errors.New("incomplete certificate") 64 } 65 return nil 66 } 67 68 // Issue a new certificate with both client and server authentication 69 // key usage extensions. 70 func (ca CertificateAuthority) Issue(commonName string, ttl time.Duration, ips []net.IP, dnss []string) ([]byte, *rsa.PrivateKey, error) { 71 return ca.IssueWithKeyUsage(commonName, ttl, ips, dnss, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}) 72 } 73 74 // IssueWithKeyUsage a new certificate with the indicated key usage extensions. 75 func (ca CertificateAuthority) IssueWithKeyUsage(commonName string, ttl time.Duration, ips []net.IP, dnss []string, keyUsage []x509.ExtKeyUsage) ([]byte, *rsa.PrivateKey, error) { 76 maxSerial := new(big.Int).Lsh(big.NewInt(1), 128) 77 serial, err := rand.Int(rand.Reader, maxSerial) 78 if err != nil { 79 return nil, nil, err 80 } 81 now := time.Now().Add(-ca.DriftMargin) 82 template := x509.Certificate{ 83 SerialNumber: serial, 84 Subject: pkix.Name{ 85 CommonName: commonName, 86 }, 87 NotBefore: now, 88 NotAfter: now.Add(ca.DriftMargin + ttl), 89 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 90 ExtKeyUsage: keyUsage, 91 BasicConstraintsValid: true, 92 } 93 template.IPAddresses = append(template.IPAddresses, ips...) 94 template.DNSNames = append(template.DNSNames, dnss...) 95 key, err := rsa.GenerateKey(rand.Reader, 2048) 96 if err != nil { 97 return nil, nil, err 98 } 99 cert, err := x509.CreateCertificate(rand.Reader, &template, ca.Cert, &key.PublicKey, ca.key) 100 if err != nil { 101 return nil, nil, err 102 } 103 return cert, key, nil 104 }