Reproduction steps for https://github.com/orgs/openbao/discussions/2721
The Puppet server CA fails to preserve the encoding of the CA certificate subject name when issuing new certificates.
The issuer field in issued certificates is always re-encoded as a UTF8String.
When Go crypto builds a chain to a trust anchor, it compares the raw bytes of a certificate's issuer to the subject of its issuing certificate. Because the CA re-encodes this field, validation fails due to mismatching DNs.
This general problem was discussed in Go issue golang#31440, where it was concluded that this behavior complies with the specification (comment link).
The following issue relates to this problem:
- Puppetserver CA changes String types in Host certs (printablestring -> utf8) resulting in invalid cert validation https://puppet.atlassian.net/browse/SERVER-2518
To reproduce the issue with latest of openvox puppet server source code, follow the steps below:
git clone https://github.com/OpenVoxProject/openvox-server.git
cd openvox-server
# Initialize git submodules
git submodule init && git submodule update
# Install vendored JRuby gems and create dev puppet.conf
./dev-setupEnable autosign so that submitted agent CSRs are signed immediately
sed -i '/^\[main\]/a autosign = true' ~/.puppetlabs/etc/puppet/puppet.conf
Generate CA key and self-signed cert with PrintableString subject.
CADIR=~/.puppetlabs/etc/puppetserver/ca
mkdir -p "$CADIR/signed" "$CADIR/requests"
openssl genrsa -out "$CADIR/ca_key.pem" 4096 2>/dev/null
openssl req -new -x509 -key "$CADIR/ca_key.pem" -out "$CADIR/ca_crt.pem" -days 3650 \
-config <(printf '[req]\ndistinguished_name=d\nstring_mask=default\nprompt=no\n[d]\nCN=Puppet CA: localhost\n') 2>/dev/null
Verify the CA uses PrintableString subject name:
openssl x509 -in ~/.puppetlabs/etc/puppetserver/ca/ca_crt.pem -noout -subject -nameopt show_type
The output is subject=CN=PRINTABLESTRING:Puppet CA: localhost
Initialize remaining mandatory CA directory files required by puppet server
echo "01" > "$CADIR/crlnumber"
openssl ca -gencrl -keyfile "$CADIR/ca_key.pem" -cert "$CADIR/ca_crt.pem" \
-out "$CADIR/ca_crl.pem" -crldays 3650 \
-config <(printf "[ca]\ndefault_ca=d\n[d]\ndatabase=/dev/null\ncrlnumber=$CADIR/crlnumber\ndefault_md=sha256\n") 2>/dev/null
echo "02" > "$CADIR/serial"
touch "$CADIR/inventory.txt"
mkdir -p ~/.puppetlabs/etc/puppet/ssl
ln -sf ~/.puppetlabs/etc/puppetserver/ca ~/.puppetlabs/etc/puppet/ssl/ca
cp "$CADIR/ca_crl.pem" ~/.puppetlabs/etc/puppet/ssl/crl.pem
Start the server and wait for server certificate to appear
lein trampoline run \
--config ./dev/puppetserver.conf \
--bootstrap-config ./dev/bootstrap.cfg
Verify server certificate issuer encoding
openssl x509 -in ~/.puppetlabs/etc/puppet/ssl/certs/localhost.pem -noout -issuer -nameopt show_type
The server has converted issuer name to UTF8String instead of actual PrintableString: issuer=CN=UTF8STRING:Puppet CA: localhost
Simulate agent certificate signing by generating agent key and CSR:
openssl genrsa -out agent-key.pem 2048 2>/dev/null
openssl req -new -key agent-key.pem -out agent.csr -subj "/CN=test-agent" 2>/dev/null
Submit CSR to the Puppet CA API:
curl -sk -X PUT \
--cert ~/.puppetlabs/etc/puppet/ssl/certs/localhost.pem \
--key ~/.puppetlabs/etc/puppet/ssl/private_keys/localhost.pem \
--cacert ~/.puppetlabs/etc/puppetserver/ca/ca_crt.pem \
-H "Content-Type: text/plain" \
--data-binary @agent.csr \
"https://localhost:8140/puppet-ca/v1/certificate_request/test-agent"
Fetch the signed agent certificate:
curl -sk \
--cert ~/.puppetlabs/etc/puppet/ssl/certs/localhost.pem \
--key ~/.puppetlabs/etc/puppet/ssl/private_keys/localhost.pem \
--cacert ~/.puppetlabs/etc/puppetserver/ca/ca_crt.pem \
"https://localhost:8140/puppet-ca/v1/certificate/test-agent" > agent-cert.pem
Verify agent cert issuer encoding:
openssl x509 -in agent-cert.pem -noout -issuer -nameopt show_type
The server has converted issuer name to UTF8String instead of actual PrintableString: issuer=CN=UTF8STRING:Puppet CA: localhost
The encoding changes when issuer is treated as Java string and not maintained in original encoding. Refer to the following code:
-
Puppet Server signs certificates using an SSL utils library, treating the CA name as a string. See first parameter of
utils/sign-certificatecalls in https://github.com/OpenVoxProject/openvox-server/blob/main/src/clj/puppetlabs/puppetserver/certificate_authority.clj -
The implementation of
SSLUtils::signCertificate()in SSL utils library receives theString issuerDnand constructs a BouncyCastleX500Nameobject. See https://github.com/OpenVoxProject/jvm-ssl-utils/blob/main/src/java/com/puppetlabs/ssl_utils/SSLUtils.java#L258-L273
The problem could be fixed in puppetserver and jvm-ssl-utils by passing either the complete CA certificate or the issuer DN in its original encoded format.