func verifyCertKeyPair()

in traffic_ops/traffic_ops_golang/deliveryservice/keys.go [397:566]


func verifyCertKeyPair(pemCertificate string, pemPrivateKey string, rootCA string, allowEC bool) (string, string, bool, bool, bool, error) {
	// decode, verify, and order certs for storage
	cleanPemPrivateKey := ""
	certs := strings.SplitAfter(pemCertificate, PemCertEndMarker)
	if len(certs) <= 1 {
		return "", "", false, false, false, errors.New("no certificate chain to verify")
	}

	// decode and verify the server certificate
	block, _ := pem.Decode([]byte(certs[0]))
	if block == nil {
		return "", "", false, false, false, errors.New("could not decode pem-encoded server certificate")
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return "", "", false, false, false, errors.New("could not parse the server certificate: " + err.Error())
	}

	// Common x509 certificate validation
	err = commonX509CertificateValidation(cert)
	if err != nil {
		return "", "", false, false, false, err
	}

	switch cert.PublicKeyAlgorithm {
	case x509.RSA:
		var rsaPrivateKey *rsa.PrivateKey

		// RSA is both a digital signature and encryption algorithm, hence the key encipherment
		// usage must be indicated in the certificate.
		// The keyUsage and extended Key Usage does not exist in version 1 of the x509 specificication.
		if cert.Version > 1 && !(cert.KeyUsage&x509.KeyUsageKeyEncipherment > 0) {
			return "", "", false, false, false, errors.New("cert/key (rsa) validation: no keyEncipherment keyUsage extension present in x509v3 server certificate")
		}

		// Extract the RSA public key from the x509 certificate
		certPublicKey, ok := cert.PublicKey.(*rsa.PublicKey)
		if !ok || certPublicKey == nil {
			return "", "", false, false, false, errors.New("cert/key (rsa) validation error: could not extract public RSA key from certificate")
		}

		// Attempt to decode the RSA private key
		rsaPrivateKey, cleanPemPrivateKey, err = decodeRSAPrivateKey(pemPrivateKey)
		if err != nil {
			return "", "", false, false, false, err
		}

		// Check RSA private key modulus against the x509 RSA public key modulus
		if rsaPrivateKey != nil && certPublicKey != nil && !bytes.Equal(rsaPrivateKey.N.Bytes(), certPublicKey.N.Bytes()) {
			return "", "", false, false, false, errors.New("cert/key (rsa) mismatch error: RSA public N modulus value mismatch")
		}

	case x509.ECDSA:
		var ecdsaPrivateKey *ecdsa.PrivateKey

		// Only permit ECDSA support for DNS* DSTypes until the Traffic Router can support it
		if !allowEC {
			return "", "", false, false, false, errors.New("cert/key validation error: ECDSA public key algorithm unsupported for non-DNS delivery service type")
		}

		// DSA and ECDSA is not an encryption algorithm and only a signing algorithm, hence the
		// certificate only needs to have the DigitalSignature KeyUsage indicated.
		if cert.Version > 1 && !(cert.KeyUsage&x509.KeyUsageDigitalSignature > 0) {
			return "", "", false, false, false, errors.New("cert/key (ecdsa) validation error: no digitalSignature keyUsage extension present in x509v3 server certificate")
		}

		// Attempt to decode the ECDSA private key
		ecdsaPrivateKey, cleanPemPrivateKey, err = decodeECDSAPrivateKey(pemPrivateKey)
		if err != nil {
			return "", "", false, false, false, err
		}

		// Extract the ECDSA public key from the x509 certificate
		certPublicKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
		if !ok || certPublicKey == nil {
			return "", "", false, false, false, errors.New("cert/key (ecdsa) validation error: could not get extract public ECDSA key from certificate")
		}

		// Compare the ECDSA curve name contained within the x509.PublicKey against the curve name indicated in the private key
		if certPublicKey.Params().Name != ecdsaPrivateKey.Params().Name {
			return "", "", false, false, false, errors.New("cert/key (ecdsa) mismatch error: ECDSA curve name in cert does not match curve name in private key")
		}

		// Verify that ECDSA public value X matches in both the cert.PublicKey and the private key.
		if !bytes.Equal(certPublicKey.X.Bytes(), ecdsaPrivateKey.X.Bytes()) {
			return "", "", false, false, false, errors.New("cert/key (ecdsa) mismatch error: ECDSA public X value mismatch")
		}

		// Verify that ECDSA public value Y matches in both the cert.PublicKey and the private key.
		if !bytes.Equal(certPublicKey.Y.Bytes(), ecdsaPrivateKey.Y.Bytes()) {
			return "", "", false, false, false, errors.New("cert/key (ecdsa) mismatch error: ECDSA public Y value mismatch")
		}

	case x509.DSA:
		return "", "", false, false, false, errors.New("cert/key validation error: DSA public key algorithm unsupported")

	case x509.UnknownPublicKeyAlgorithm:
		fallthrough
	default:
		return "", "", false, false, false, errors.New("cert/key validation error: Unknown public key algorithm")
	}

	bundle := ""
	parsedCerts := []*x509.Certificate{}
	for i := 0; i < len(certs)-1; i++ {
		bundle += certs[i]
		blk, _ := pem.Decode([]byte(certs[i]))
		c, err := x509.ParseCertificate(blk.Bytes)
		if err != nil {
			return "", "", false, false, false, errors.New("unable to parse intermediate certificate: " + err.Error())
		}
		parsedCerts = append(parsedCerts, c)
	}

	intermediatePool := x509.NewCertPool()
	if !intermediatePool.AppendCertsFromPEM([]byte(bundle)) {
		return "", "", false, false, false, errors.New("certificate CA bundle is empty")
	}

	opts := x509.VerifyOptions{
		Intermediates: intermediatePool,
	}

	if rootCA != "" {
		// verify the certificate chain.
		rootPool := x509.NewCertPool()
		if !rootPool.AppendCertsFromPEM([]byte(rootCA)) {
			return "", "", false, false, false, errors.New("unable to parse root CA certificate")
		}
		opts.Roots = rootPool
	}

	for i := 0; i < len(parsedCerts)-1; i++ {
		current := parsedCerts[i]
		next := parsedCerts[i+1]
		if current.Issuer.String() != next.Subject.String() {
			return "", "", false, false, true, nil
		}
	}

	chain, err := cert.Verify(opts)
	if err != nil {
		if _, ok := err.(x509.UnknownAuthorityError); ok {

			return pemCertificate, cleanPemPrivateKey, true, false, false, nil
		}
		return "", "", false, false, false, errors.New("could not verify the certificate chain: " + err.Error())
	}
	if len(chain) < 1 {
		return "", "", false, false, false, errors.New("can't find valid chain for cert in file in request")
	}
	pemEncodedChain := ""
	for _, link := range chain[0] {
		// Include all certificates in the chain, since verification was successful.
		block := &pem.Block{Type: "CERTIFICATE", Bytes: link.Raw}
		pemEncodedChain += string(pem.EncodeToMemory(block))
	}
	pemCertificate = strings.TrimSpace(pemCertificate)
	pemEncodedChain = strings.TrimSpace(pemEncodedChain)

	if len(pemEncodedChain) < 1 {
		return "", "", false, false, false, errors.New("invalid empty certificate chain in request")
	}

	if pemEncodedChain != pemCertificate {
		return pemCertificate, cleanPemPrivateKey, false, true, false, nil
	}

	return pemCertificate, cleanPemPrivateKey, false, false, false, nil
}