365 lines
12 KiB
Go
365 lines
12 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package msp
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"math/big"
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func (msp *bccspmsp) validateIdentity(id *identity) error {
|
|
id.validationMutex.Lock()
|
|
defer id.validationMutex.Unlock()
|
|
|
|
// return cached validation value if already validated
|
|
if id.validated {
|
|
return id.validationErr
|
|
}
|
|
|
|
id.validated = true
|
|
|
|
validationChain, err := msp.getCertificationChainForBCCSPIdentity(id)
|
|
if err != nil {
|
|
id.validationErr = errors.WithMessage(err, "could not obtain certification chain")
|
|
mspLogger.Warnf("Could not validate identity: %s (certificate subject=%s issuer=%s serialnumber=%d)", id.validationErr, id.cert.Subject, id.cert.Issuer, id.cert.SerialNumber)
|
|
return id.validationErr
|
|
}
|
|
|
|
err = msp.validateIdentityAgainstChain(id, validationChain)
|
|
if err != nil {
|
|
id.validationErr = errors.WithMessage(err, "could not validate identity against certification chain")
|
|
mspLogger.Warnf("Could not validate identity: %s (certificate subject=%s issuer=%s serialnumber=%d)", id.validationErr, id.cert.Subject, id.cert.Issuer, id.cert.SerialNumber)
|
|
return id.validationErr
|
|
}
|
|
|
|
err = msp.internalValidateIdentityOusFunc(id)
|
|
if err != nil {
|
|
id.validationErr = errors.WithMessage(err, "could not validate identity's OUs")
|
|
mspLogger.Warnf("Could not validate identity: %s (certificate subject=%s issuer=%s serialnumber=%d)", id.validationErr, id.cert.Subject, id.cert.Issuer, id.cert.SerialNumber)
|
|
return id.validationErr
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (msp *bccspmsp) validateCAIdentity(id *identity) error {
|
|
if !id.cert.IsCA {
|
|
return errors.New("Only CA identities can be validated")
|
|
}
|
|
|
|
validationChain, err := msp.getUniqueValidationChain(id.cert, msp.getValidityOptsForCert(id.cert))
|
|
if err != nil {
|
|
return errors.WithMessage(err, "could not obtain certification chain")
|
|
}
|
|
if len(validationChain) == 1 {
|
|
// validationChain[0] is the root CA certificate
|
|
return nil
|
|
}
|
|
|
|
return msp.validateIdentityAgainstChain(id, validationChain)
|
|
}
|
|
|
|
func (msp *bccspmsp) validateTLSCAIdentity(cert *x509.Certificate, opts *x509.VerifyOptions) error {
|
|
if !cert.IsCA {
|
|
return errors.New("Only CA identities can be validated")
|
|
}
|
|
|
|
validationChain, err := msp.getUniqueValidationChain(cert, *opts)
|
|
if err != nil {
|
|
return errors.WithMessage(err, "could not obtain certification chain")
|
|
}
|
|
if len(validationChain) == 1 {
|
|
// validationChain[0] is the root CA certificate
|
|
return nil
|
|
}
|
|
|
|
return msp.validateCertAgainstChain(cert, validationChain)
|
|
}
|
|
|
|
func (msp *bccspmsp) validateIdentityAgainstChain(id *identity, validationChain []*x509.Certificate) error {
|
|
return msp.validateCertAgainstChain(id.cert, validationChain)
|
|
}
|
|
|
|
func (msp *bccspmsp) validateCertAgainstChain(cert *x509.Certificate, validationChain []*x509.Certificate) error {
|
|
// here we know that the identity is valid; now we have to check whether it has been revoked
|
|
|
|
// identify the SKI of the CA that signed this cert
|
|
SKI, err := getSubjectKeyIdentifierFromCert(validationChain[1])
|
|
if err != nil {
|
|
return errors.WithMessage(err, "could not obtain Subject Key Identifier for signer cert")
|
|
}
|
|
|
|
// check whether one of the CRLs we have has this cert's
|
|
// SKI as its AuthorityKeyIdentifier
|
|
for _, crl := range msp.CRL {
|
|
aki, err := getAuthorityKeyIdentifierFromCrl(crl)
|
|
if err != nil {
|
|
return errors.WithMessage(err, "could not obtain Authority Key Identifier for crl")
|
|
}
|
|
|
|
// check if the SKI of the cert that signed us matches the AKI of any of the CRLs
|
|
if bytes.Equal(aki, SKI) {
|
|
// we have a CRL, check whether the serial number is revoked
|
|
for _, rc := range crl.TBSCertList.RevokedCertificates {
|
|
if rc.SerialNumber.Cmp(cert.SerialNumber) == 0 {
|
|
// We have found a CRL whose AKI matches the SKI of
|
|
// the CA (root or intermediate) that signed the
|
|
// certificate that is under validation. As a
|
|
// precaution, we verify that said CA is also the
|
|
// signer of this CRL.
|
|
err = validationChain[1].CheckCRLSignature(crl)
|
|
if err != nil {
|
|
// the CA cert that signed the certificate
|
|
// that is under validation did not sign the
|
|
// candidate CRL - skip
|
|
mspLogger.Warningf("Invalid signature over the identified CRL, error %+v", err)
|
|
continue
|
|
}
|
|
|
|
// A CRL also includes a time of revocation so that
|
|
// the CA can say "this cert is to be revoked starting
|
|
// from this time"; however here we just assume that
|
|
// revocation applies instantaneously from the time
|
|
// the MSP config is committed and used so we will not
|
|
// make use of that field
|
|
return errors.New("The certificate has been revoked")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (msp *bccspmsp) validateIdentityOUsV1(id *identity) error {
|
|
// Check that the identity's OUs are compatible with those recognized by this MSP,
|
|
// meaning that the intersection is not empty.
|
|
if len(msp.ouIdentifiers) > 0 {
|
|
found := false
|
|
|
|
for _, OU := range id.GetOrganizationalUnits() {
|
|
certificationIDs, exists := msp.ouIdentifiers[OU.OrganizationalUnitIdentifier]
|
|
|
|
if exists {
|
|
for _, certificationID := range certificationIDs {
|
|
if bytes.Equal(certificationID, OU.CertifiersIdentifier) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
if len(id.GetOrganizationalUnits()) == 0 {
|
|
return errors.New("the identity certificate does not contain an Organizational Unit (OU)")
|
|
}
|
|
return errors.Errorf("none of the identity's organizational units %s are in MSP %s", OUIDs(id.GetOrganizationalUnits()), msp.name)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (msp *bccspmsp) validateIdentityOUsV11(id *identity) error {
|
|
// Run the same checks as per V1
|
|
err := msp.validateIdentityOUsV1(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Perform V1_1 additional checks:
|
|
//
|
|
// -- Check for OU enforcement
|
|
if !msp.ouEnforcement {
|
|
// No enforcement required
|
|
return nil
|
|
}
|
|
|
|
// Make sure that the identity has only one of the special OUs
|
|
// used to tell apart clients or peers.
|
|
counter := 0
|
|
for _, OU := range id.GetOrganizationalUnits() {
|
|
// Is OU.OrganizationalUnitIdentifier one of the special OUs?
|
|
var nodeOU *OUIdentifier
|
|
switch OU.OrganizationalUnitIdentifier {
|
|
case msp.clientOU.OrganizationalUnitIdentifier:
|
|
nodeOU = msp.clientOU
|
|
case msp.peerOU.OrganizationalUnitIdentifier:
|
|
nodeOU = msp.peerOU
|
|
default:
|
|
continue
|
|
}
|
|
|
|
// Yes. Then, enforce the certifiers identifier is this is specified.
|
|
// It is not specified, it means that any certification path is fine.
|
|
if len(nodeOU.CertifiersIdentifier) != 0 && !bytes.Equal(nodeOU.CertifiersIdentifier, OU.CertifiersIdentifier) {
|
|
return errors.Errorf("certifiersIdentifier does not match: %v, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
|
|
}
|
|
counter++
|
|
if counter > 1 {
|
|
break
|
|
}
|
|
}
|
|
|
|
// the identity should have exactly one OU role, return an error if the counter is not 1.
|
|
if counter == 0 {
|
|
return errors.Errorf("the identity does not have an OU that resolves to client or peer. OUs: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
|
|
}
|
|
if counter > 1 {
|
|
return errors.Errorf("the identity must be a client or a peer identity to be valid, not a combination of them. OUs: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (msp *bccspmsp) validateIdentityOUsV142(id *identity) error {
|
|
// Run the same checks as per V1
|
|
err := msp.validateIdentityOUsV1(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// -- Check for OU enforcement
|
|
if !msp.ouEnforcement {
|
|
// No enforcement required
|
|
return nil
|
|
}
|
|
|
|
// Make sure that the identity has only one of the special OUs
|
|
// used to tell apart clients, peers and admins.
|
|
counter := 0
|
|
validOUs := make(map[string]*OUIdentifier)
|
|
if msp.clientOU != nil {
|
|
validOUs[msp.clientOU.OrganizationalUnitIdentifier] = msp.clientOU
|
|
}
|
|
if msp.peerOU != nil {
|
|
validOUs[msp.peerOU.OrganizationalUnitIdentifier] = msp.peerOU
|
|
}
|
|
if msp.adminOU != nil {
|
|
validOUs[msp.adminOU.OrganizationalUnitIdentifier] = msp.adminOU
|
|
}
|
|
if msp.ordererOU != nil {
|
|
validOUs[msp.ordererOU.OrganizationalUnitIdentifier] = msp.ordererOU
|
|
}
|
|
|
|
for _, OU := range id.GetOrganizationalUnits() {
|
|
// Is OU.OrganizationalUnitIdentifier one of the special OUs?
|
|
nodeOU := validOUs[OU.OrganizationalUnitIdentifier]
|
|
if nodeOU == nil {
|
|
continue
|
|
}
|
|
|
|
// Yes. Then, enforce the certifiers identifier in this is specified.
|
|
// If is not specified, it means that any certification path is fine.
|
|
if len(nodeOU.CertifiersIdentifier) != 0 && !bytes.Equal(nodeOU.CertifiersIdentifier, OU.CertifiersIdentifier) {
|
|
return errors.Errorf("certifiersIdentifier does not match: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
|
|
}
|
|
counter++
|
|
if counter > 1 {
|
|
break
|
|
}
|
|
}
|
|
|
|
// the identity should have exactly one OU role, return an error if the counter is not 1.
|
|
if counter == 0 {
|
|
return errors.Errorf("the identity does not have an OU that resolves to client, peer, orderer, or admin role. OUs: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
|
|
}
|
|
if counter > 1 {
|
|
return errors.Errorf("the identity must have a client, a peer, an orderer, or an admin OU role to be valid, not a combination of them. OUs: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (msp *bccspmsp) getValidityOptsForCert(cert *x509.Certificate) x509.VerifyOptions {
|
|
// First copy the opts to override the CurrentTime field
|
|
// in order to make the certificate passing the expiration test
|
|
// independently from the real local current time.
|
|
// This is a temporary workaround for FAB-3678
|
|
|
|
var tempOpts x509.VerifyOptions
|
|
tempOpts.Roots = msp.opts.Roots
|
|
tempOpts.DNSName = msp.opts.DNSName
|
|
tempOpts.Intermediates = msp.opts.Intermediates
|
|
tempOpts.KeyUsages = msp.opts.KeyUsages
|
|
tempOpts.CurrentTime = cert.NotBefore.Add(time.Second)
|
|
|
|
return tempOpts
|
|
}
|
|
|
|
/*
|
|
This is the definition of the ASN.1 marshalling of AuthorityKeyIdentifier
|
|
from https://www.ietf.org/rfc/rfc5280.txt
|
|
|
|
AuthorityKeyIdentifier ::= SEQUENCE {
|
|
keyIdentifier [0] KeyIdentifier OPTIONAL,
|
|
authorityCertIssuer [1] GeneralNames OPTIONAL,
|
|
authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
|
|
|
|
KeyIdentifier ::= OCTET STRING
|
|
|
|
CertificateSerialNumber ::= INTEGER
|
|
|
|
*/
|
|
|
|
type authorityKeyIdentifier struct {
|
|
KeyIdentifier []byte `asn1:"optional,tag:0"`
|
|
AuthorityCertIssuer []byte `asn1:"optional,tag:1"`
|
|
AuthorityCertSerialNumber big.Int `asn1:"optional,tag:2"`
|
|
}
|
|
|
|
// getAuthorityKeyIdentifierFromCrl returns the Authority Key Identifier
|
|
// for the supplied CRL. The authority key identifier can be used to identify
|
|
// the public key corresponding to the private key which was used to sign the CRL.
|
|
func getAuthorityKeyIdentifierFromCrl(crl *pkix.CertificateList) ([]byte, error) {
|
|
aki := authorityKeyIdentifier{}
|
|
|
|
for _, ext := range crl.TBSCertList.Extensions {
|
|
// Authority Key Identifier is identified by the following ASN.1 tag
|
|
// authorityKeyIdentifier (2 5 29 35) (see https://tools.ietf.org/html/rfc3280.html)
|
|
if reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 35}) {
|
|
_, err := asn1.Unmarshal(ext.Value, &aki)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal AKI")
|
|
}
|
|
|
|
return aki.KeyIdentifier, nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("authorityKeyIdentifier not found in certificate")
|
|
}
|
|
|
|
// getSubjectKeyIdentifierFromCert returns the Subject Key Identifier for the supplied certificate
|
|
// Subject Key Identifier is an identifier of the public key of this certificate
|
|
func getSubjectKeyIdentifierFromCert(cert *x509.Certificate) ([]byte, error) {
|
|
var SKI []byte
|
|
|
|
for _, ext := range cert.Extensions {
|
|
// Subject Key Identifier is identified by the following ASN.1 tag
|
|
// subjectKeyIdentifier (2 5 29 14) (see https://tools.ietf.org/html/rfc3280.html)
|
|
if reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 14}) {
|
|
_, err := asn1.Unmarshal(ext.Value, &SKI)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal Subject Key Identifier")
|
|
}
|
|
|
|
return SKI, nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("subjectKeyIdentifier not found in certificate")
|
|
}
|