github.com/microsoft/moc@v0.17.1/pkg/certs/certificateAuthority.go (about)

     1  // Copyright (c) Microsoft Corporation. All rights reserved.
     2  // Licensed under the Apache v2.0 license.
     3  package certs
     4  
     5  //go:generate mockgen -destination mock/mock_certificateAuthority.go github.com/microsoft/moc/pkg/certs Revocation
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/rand"
    10  	"crypto/sha256"
    11  	"crypto/tls"
    12  	"crypto/x509"
    13  	"crypto/x509/pkix"
    14  	"encoding/asn1"
    15  	"encoding/pem"
    16  	"math"
    17  	"math/big"
    18  	"time"
    19  
    20  	"github.com/microsoft/moc/pkg/errors"
    21  )
    22  
    23  var (
    24  	//  1.3.6.1.4.1.311.104  subtree is defined for CIA MSK8S
    25  	oidRenewCertificates   = []int{1, 3, 6, 1, 4, 1, 311, 104, 1, 1}
    26  	oidOriginalCertificate = []int{1, 3, 6, 1, 4, 1, 311, 104, 1, 2}
    27  	oidRenewCount          = []int{1, 3, 6, 1, 4, 1, 311, 104, 1, 3}
    28  
    29  	// RFC 5755
    30  	OidAccessIdentity = []int{1, 3, 6, 1, 5, 5, 7, 10, 2}
    31  )
    32  
    33  type Revocation interface {
    34  	IsRevoked(cert *x509.Certificate) error
    35  }
    36  
    37  type CAConfig struct {
    38  	RootSigner      *tls.Certificate
    39  	CrossRootCert   *x509.Certificate   // OPTIONAL
    40  	AdditionalRoots []*x509.Certificate // OPTIONAL
    41  	Revocation      Revocation          // OPTIONAL
    42  }
    43  
    44  type CertificateAuthority struct {
    45  	rootSigner    *tls.Certificate
    46  	rootCert      *x509.Certificate
    47  	crossRootCert *x509.Certificate
    48  	rootsPool     *x509.CertPool
    49  	revocation    Revocation
    50  }
    51  
    52  func parseCertsPEM(pemCerts []byte) ([]*x509.Certificate, error) {
    53  	ok := false
    54  	certs := []*x509.Certificate{}
    55  	for len(pemCerts) > 0 {
    56  		var block *pem.Block
    57  		block, pemCerts = pem.Decode(pemCerts)
    58  		if block == nil {
    59  			break
    60  		}
    61  		// Only use PEM "CERTIFICATE" blocks without extra headers
    62  		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
    63  			continue
    64  		}
    65  
    66  		cert, err := x509.ParseCertificate(block.Bytes)
    67  		if err != nil {
    68  			return certs, err
    69  		}
    70  
    71  		certs = append(certs, cert)
    72  		ok = true
    73  	}
    74  
    75  	if !ok {
    76  		return certs, errors.Wrapf(errors.InvalidInput, "data does not contain any valid certificates")
    77  	}
    78  	return certs, nil
    79  }
    80  
    81  // NewCertificateAuthority creates a CertificateAuthority
    82  func NewCertificateAuthority(config *CAConfig) (*CertificateAuthority, error) {
    83  	var err error
    84  
    85  	ca := CertificateAuthority{
    86  		rootSigner:    config.RootSigner,
    87  		crossRootCert: config.CrossRootCert,
    88  		revocation:    config.Revocation,
    89  	}
    90  
    91  	ca.rootCert = ca.rootSigner.Leaf
    92  	if ca.rootCert == nil {
    93  		ca.rootCert, err = x509.ParseCertificate(ca.rootSigner.Certificate[0])
    94  		if err != nil {
    95  			return nil, errors.Wrapf(errors.Failed, "unable to parse rootSigner: %v", err)
    96  		}
    97  	}
    98  
    99  	ca.rootsPool = x509.NewCertPool()
   100  	ca.rootsPool.AddCert(ca.rootCert)
   101  
   102  	for _, cert := range config.AdditionalRoots {
   103  		ca.rootsPool.AddCert(cert)
   104  	}
   105  
   106  	return &ca, nil
   107  }
   108  
   109  // VerifyClientCertificate verifies rawCerts(ASN encoded) using the CertificateAuthority
   110  func (ca *CertificateAuthority) VerifyClientCertificate(rawCerts [][]byte) error {
   111  	if len(rawCerts) == 0 {
   112  		return errors.Wrapf(errors.InvalidInput, "Certificate list empty, nothing to verify")
   113  	}
   114  	certs := []*x509.Certificate{}
   115  	for _, rawcert := range rawCerts {
   116  		cert, err := x509.ParseCertificate(rawcert)
   117  		if err != nil {
   118  			return err
   119  		}
   120  		certs = append(certs, cert)
   121  	}
   122  	// TODO Need more clarification
   123  	intermediatesPool := x509.NewCertPool()
   124  	if len(certs) > 1 {
   125  		for _, cert := range certs[1:] {
   126  			intermediatesPool.AddCert(cert)
   127  		}
   128  	}
   129  
   130  	leaf := certs[0]
   131  
   132  	// TODO Current Time
   133  	verifyOptions := x509.VerifyOptions{
   134  		Intermediates: intermediatesPool,
   135  		Roots:         ca.rootsPool,
   136  		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
   137  	}
   138  
   139  	_, err := leaf.Verify(verifyOptions)
   140  
   141  	if err != nil {
   142  		return errors.Wrapf(err, "unable to verify client certificate %s", leaf.Subject.CommonName)
   143  	}
   144  
   145  	if ca.revocation != nil {
   146  		if err = ca.revocation.IsRevoked(leaf); err != nil {
   147  			return errors.Wrapf(err, "certificate is revoked %s", leaf.Subject.CommonName)
   148  		}
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  // SignRequest signs the CSR using Certificate Authority
   155  // if oldCertPem is provided it is validated against CA
   156  func (ca *CertificateAuthority) SignRequest(csrPem []byte, oldCertPem []byte, conf *SignConfig) (retCert []byte, err error) {
   157  	keyUsage := x509.KeyUsageDigitalSignature
   158  	extKeyUsage := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
   159  
   160  	if conf != nil && conf.ServerAuth {
   161  		keyUsage |= x509.KeyUsageKeyEncipherment
   162  		extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth)
   163  	}
   164  
   165  	if conf != nil && conf.IsCA {
   166  		keyUsage |= x509.KeyUsageCertSign
   167  	}
   168  
   169  	csr, err := DecodeCertRequestPEM(csrPem)
   170  	if err != nil {
   171  		return
   172  	}
   173  	var oldCert *x509.Certificate
   174  	if oldCertPem != nil || len(oldCertPem) != 0 {
   175  		if err = ca.VerifyClientCertificate([][]byte{oldCertPem}); err != nil {
   176  			return nil, errors.Wrapf(errors.InvalidInput, "Old certificate verification failed : %v", err)
   177  		}
   178  		oldCert, err = DecodeCertPEM(oldCertPem)
   179  	}
   180  	err = csr.CheckSignature()
   181  	if err != nil {
   182  		return nil, errors.Wrapf(errors.InvalidInput, "Invalid CSR signature: %v", err)
   183  	}
   184  
   185  	serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  
   190  	offset := (time.Hour * 24 * 365)
   191  	accessIdentity := []byte{}
   192  	isCA := false
   193  	if conf != nil {
   194  		offset = conf.Offset
   195  		accessIdentity = []byte(conf.Identity)
   196  		isCA = conf.IsCA
   197  	}
   198  	now := time.Now().UTC()
   199  
   200  	template := x509.Certificate{
   201  		SerialNumber:          serial,
   202  		Subject:               csr.Subject,
   203  		NotBefore:             now,
   204  		NotAfter:              now.Add(offset), // 1 year
   205  		KeyUsage:              keyUsage,
   206  		ExtKeyUsage:           extKeyUsage,
   207  		BasicConstraintsValid: true,
   208  		DNSNames:              csr.DNSNames,
   209  		IPAddresses:           csr.IPAddresses,
   210  		IsCA:                  isCA,
   211  		MaxPathLenZero:        isCA, // Enable MaxPathLenZero only when is CA
   212  	}
   213  
   214  	csrRenewCertsPEM := []byte{}
   215  
   216  	for _, ext := range csr.Extensions {
   217  		if ext.Id.Equal(oidRenewCertificates) {
   218  			csrRenewCertsPEM = ext.Value
   219  			break
   220  		}
   221  	}
   222  
   223  	if len(csrRenewCertsPEM) != 0 {
   224  
   225  		csrRenewCert, err := parseCertsPEM(csrRenewCertsPEM)
   226  		if err != nil || len(csrRenewCert) < 2 {
   227  			return nil, errors.Wrapf(errors.InvalidInput, "missing CSR renew certificates")
   228  		}
   229  
   230  		// csrRenewCert[0] is signed by csrRenewCert[1]
   231  		// csrRenewCert[1] is cert to be renewed
   232  		// csrRenewCert[2] ... optional intermediate certificates to verify csrRenewCert
   233  
   234  		certToRenew := csrRenewCert[1]
   235  
   236  		// The certToRenew must also be used as the clientAuthCert
   237  		if oldCert != nil {
   238  			if !bytes.Equal(certToRenew.Raw, oldCert.Raw) {
   239  				return nil, errors.Wrapf(errors.InvalidInput, "certToRenew wasn't used for clientAuthCert")
   240  			}
   241  		}
   242  
   243  		intermediatesPool := x509.NewCertPool()
   244  		if len(csrRenewCert) > 2 {
   245  			for _, cert := range csrRenewCert[2:] {
   246  				intermediatesPool.AddCert(cert)
   247  			}
   248  		}
   249  
   250  		verifyOptions := x509.VerifyOptions{
   251  			Intermediates: intermediatesPool,
   252  			Roots:         ca.rootsPool,
   253  			KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
   254  		}
   255  
   256  		_, err = certToRenew.Verify(verifyOptions)
   257  		if err != nil {
   258  			if time.Now().After(certToRenew.NotAfter) {
   259  				return nil, errors.Wrapf(errors.Expired, "unable to verify certificate to be renewed: Certificate Expired: %v", err)
   260  			} else {
   261  				return nil, errors.Wrapf(errors.Failed, "unable to verify certificate to be renewed: %v", err)
   262  			}
   263  		}
   264  
   265  		err = certToRenew.CheckSignature(csrRenewCert[0].SignatureAlgorithm,
   266  			csrRenewCert[0].RawTBSCertificate, csrRenewCert[0].Signature)
   267  		if err != nil {
   268  			return nil, errors.Wrapf(errors.Failed, "unable to verify signature of CSR Key certificate: %v", err)
   269  		}
   270  
   271  		// Check that the public key in the CSR matches the public key in the CSR Key certificate
   272  		if !bytes.Equal(csr.RawSubjectPublicKeyInfo, csrRenewCert[0].RawSubjectPublicKeyInfo) {
   273  			return nil, errors.Wrapf(errors.Failed, "public key in CSR and CSR Key certificate don't match")
   274  		}
   275  
   276  		if ca.revocation != nil {
   277  			if err = ca.revocation.IsRevoked(certToRenew); err != nil {
   278  				return nil, errors.Wrapf(err, "certificate is revoked")
   279  			}
   280  		}
   281  
   282  		// We can now use the content from the certificate to be renewed
   283  		template = *certToRenew
   284  
   285  		// Not reusing the serial number. New serial should be generated
   286  		template.SerialNumber = serial
   287  
   288  		// We are using the same validity as the certificate being renewed
   289  		validity := template.NotAfter.Sub(template.NotBefore)
   290  
   291  		template.NotBefore = time.Now()
   292  		template.NotAfter = template.NotBefore.Add(validity)
   293  		spkiHash := sha256.Sum256(csr.RawSubjectPublicKeyInfo)
   294  		template.SubjectKeyId = spkiHash[:]
   295  		template.AuthorityKeyId = nil
   296  		template.SignatureAlgorithm = x509.UnknownSignatureAlgorithm
   297  
   298  		origCertDER := certToRenew.Raw
   299  		var renewCount int64 = 0
   300  
   301  		for _, ext := range certToRenew.Extensions {
   302  			if ext.Id.Equal(oidOriginalCertificate) {
   303  				origCertDER = ext.Value
   304  			} else if ext.Id.Equal(oidRenewCount) {
   305  				asn1.Unmarshal(ext.Value, &renewCount)
   306  			}
   307  		}
   308  
   309  		renewCount++
   310  		renewCountDER, _ := asn1.Marshal(renewCount)
   311  		template.ExtraExtensions = []pkix.Extension{
   312  			{
   313  				Id:       oidOriginalCertificate,
   314  				Critical: false,
   315  				Value:    origCertDER,
   316  			},
   317  			{
   318  				Id:       oidRenewCount,
   319  				Critical: false,
   320  				Value:    renewCountDER,
   321  			},
   322  		}
   323  	}
   324  	template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{
   325  		Id:       OidAccessIdentity,
   326  		Critical: false,
   327  		Value:    accessIdentity,
   328  	})
   329  	cert, err := x509.CreateCertificate(rand.Reader, &template, ca.rootCert, csr.PublicKey, ca.rootSigner.PrivateKey)
   330  	if err != nil {
   331  		return
   332  	}
   333  
   334  	x509Cert, err := x509.ParseCertificate(cert)
   335  	if err != nil {
   336  		return
   337  	}
   338  	retCert = EncodeCertPEM(x509Cert)
   339  	return
   340  }