Skip to content

X509Chain.Build() returns true for partial chain when AllowUnknownCertificateAuthority flag is present #49615

@stewartadam

Description

@stewartadam

Description

According this comment by @bartonjs in #26449 about the AllowUnknownCertificateAuthority flag (emphasis mine):

If AllowUnknownCertificateAuthority is the only flag set then chain.Build() will return true if

  • The chain correctly terminated in a self-signed certificate (via ExtraStore, or searched persisted stores)
  • ...

So, Build() returns true, you know that a time-valid non-revoked chain is present.

  1. the docs make no mention of it being required to be the only flag set, so if this is accurate docs need updated to reflect this.
  2. I cannot reproduce the above intended behavior, and instead see X509Chain.Build() returning true for any leaf certificate and any CA added to the ExtraStore combination, even the leaf certificate is not signed by the ExtraStore's CA.

Configuration

$ dotnet --info
.NET SDK (reflecting any global.json):
 Version:   5.0.201
 Commit:    a09bd5c86c

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  11.0
 OS Platform: Darwin
 RID:         osx.11.0-x64
 Base Path:   /usr/local/share/dotnet/sdk/5.0.201/

Host (useful for support):
  Version: 5.0.4
  Commit:  f27d337295

.NET SDKs installed:
  3.1.302 [/usr/local/share/dotnet/sdk]
  5.0.201 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.6 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.4 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.6 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download

Repro

Run in bash:

create_ca() {
  local CA_CN="$1"
  openssl genrsa -out ${CA_CN}.key.pem 2048 # Generate private key
  openssl req -x509 -new -nodes -key ${CA_CN}.key.pem -sha256 -days 1825 -out ${CA_CN}.cert.pem -subj "/CN=${CA_CN}/O=MyDevices/C=US" # Generate root certificate
}

create_leaf_cert() {
  local DEVICE_CN="$1"
  openssl genrsa -out ${DEVICE_CN}.key.pem 2048 # new private key
  openssl req -new -key ${DEVICE_CN}.key.pem -out ${DEVICE_CN}.csr.pem -subj "/CN=${DEVICE_CN}/O=MyDevices/C=US" # generate signing request for the CA
}

sign_leaf_cert() {
  local DEVICE_CN="$1"
  local CA_CN="$2"
  openssl x509 -req -in ${DEVICE_CN}.csr.pem -CA "${CA_CN}.cert.pem" -CAkey "${CA_CN}.key.pem" -set_serial 01 -out "${DEVICE_CN}.cert.pem" -days 825 -sha256 # sign the CSR
}

# Create one self-issued CA + signed cert
create_ca trust.mydevices.com
create_leaf_cert dev01.mydevices.com
sign_leaf_cert dev01.mydevices.com trust.mydevices.com

# Create another self-issued CA + signed cert
create_ca trust.different.com
create_leaf_cert dev01.different.com
sign_leaf_cert dev01.different.com trust.different.com

Expected behavior

I expect chain.Build() to return the boolean equivalent to the OpenSSL validations when paired with the various certificates:

openssl verify -CAfile trust.mydevices.com.cert.pem dev01.mydevices.com.cert.pem # true
openssl verify -CAfile trust.mydevices.com.cert.pem dev01.different.com.cert.pem # false - mismatched CA

openssl verify -CAfile trust.different.com.cert.pem dev01.different.com.cert.pem # true
openssl verify -CAfile trust.different.com.cert.pem dev01.mydevices.com.cert.pem # false - mismatched CA

Observed behavior

success=true, even when PartialChain is present in the status because the chain could not be verified

$ dotnet script main.csx -- trust.mydevices.com.cert.pem dev01.mydevices.com.cert.pem # true
Success: True
Chain Status: flag=UntrustedRoot info=The certificate was not trusted.

$ dotnet script main.csx -- trust.mydevices.com.cert.pem dev01.different.com.cert.pem # true - despite mismatched CA
Success: True
Chain Status: flag=PartialChain info=One or more certificates required to validate this certificate cannot be found.

$ dotnet script main.csx -- trust.different.com.cert.pem dev01.different.com.cert.pem # true
Success: True
Chain Status: flag=UntrustedRoot info=The certificate was not trusted.

$ dotnet script main.csx -- trust.different.com.cert.pem dev01.mydevices.com.cert.pem # true - despite mismatched CA
Success: True
Chain Status: flag=PartialChain info=One or more certificates required to validate this certificate cannot be found.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions