-
Notifications
You must be signed in to change notification settings - Fork 84
Description
Summary
X509ExtensionFactory#create_ext has special-case handling for several Netscape extension OIDs (e.g. nsCertType at line 196), but nsComment (OID 2.16.840.1.113730.1.13) is not handled and falls through to the else branch at line 214-215:
value = new DEROctetString(new DEROctetString(ByteList.plain(valuex)).getEncoded(ASN1Encoding.DER));This produces a double-wrapped OCTET STRING containing raw ASCII bytes:
OCTET STRING { OCTET STRING { raw ASCII } }
DER: 04 2A 04 28 50 75 70 70 65 74 ...
CRuby's OpenSSL bindings correctly produce an IA5String via X509V3_EXT_nconf():
OCTET STRING { IA5String "Puppet Server Internal Certificate" }
DER: 04 24 16 22 50 75 70 70 65 74 ...
Impact
When a certificate with this malformed nsComment is parsed by BouncyCastle (e.g. during mTLS authentication in puppetserver), it crashes with:
java.io.IOException: corrupted stream - out of bounds length found: 117 >= 34
This happens because BouncyCastle successfully parses the outer OCTET STRING, then tries to interpret the inner raw ASCII bytes as ASN.1. The byte 0x50 (P) is interpreted as an ASN.1 tag and 0x75 (u = 117) as a length field, but only 34 bytes remain.
How we found this
We are building a Kubernetes operator (openvox-operator) that runs puppetserver ca setup inside the JRuby runtime of the puppetserver process, rather than via the CRuby-based CLI. This means the certificate extensions are created through JRuby-OpenSSL instead of CRuby's native OpenSSL bindings.
The generated CA server certificate (puppet.pem) contains an nsComment extension set by openvoxserver-ca:
["nsComment", "Puppet Server Internal Certificate", false]When the operator later calls the PUT /puppet-ca/v1/certificate_status/{certname} endpoint to sign certificates via mTLS, the CA parses the client certificate extensions and hits the BouncyCastle crash.
As a workaround, we patch jvm-ssl-utils at build time to add a try-catch fallback in asn1ObjToObj (see slauger/openvox-operator#148).
How to reproduce
require 'openssl'
ef = OpenSSL::X509::ExtensionFactory.new
ext = ef.create_extension('nsComment', 'Puppet Server Internal Certificate', false)
puts ext.to_der.bytes.map { |b| '%02x' % b }.join(' ')Under CRuby: the DER contains 16 22 (IA5String tag + length).
Under JRuby: the DER contains 04 28 (OCTET STRING tag + length) with raw ASCII.
Suggested fix
Add a case for nsComment (MiscObjectIdentifiers.netscapeCertComment) in the OID switch in create_ext, similar to the existing nsCertType handling, encoding the value as DERIA5String instead of falling through to the generic OCTET STRING path.