Setup
Two certificates, same CA, deliberately different subject structure:
- Cert A: one RDN.
CN's value is the literal string foo/O=bar.
- Cert B: two RDNs,
CN=foo and O=bar.
openssl req -new -key leaf-key.pem -out certA.csr -subj '/CN=foo\/O=bar'
openssl req -new -key leaf-key.pem -out certB.csr -subj '/CN=foo/O=bar'
# both signed by the same CA
Confirmed structurally different via OpenSSL's own multi-line print:
$ openssl x509 -in certA.pem -noout -subject -nameopt sep_multiline
CN=foo/O=bar
$ openssl x509 -in certB.pem -noout -subject -nameopt sep_multiline
CN=foo
O=bar
Reproduction
Same program (load cert, X509_get_subject_name(), X509_NAME_oneline()), compiled twice - once against wolfSSL 5.9.1 (OPENSSL_EXTRA, HAVE_OPENSSL), once against OpenSSL 3.5.5:
X509_NAME *name = X509_get_subject_name(cert);
char *oneline = X509_NAME_oneline(name, NULL, 0);
printf("%s -> X509_NAME_oneline() = \"%s\"\n", path, oneline);
Results, same two certificates
|
Cert A (one RDN, literal / in value) |
Cert B (two RDNs) |
| wolfSSL 5.9.1 |
/CN=foo/O=bar |
/CN=foo/O=bar |
| OpenSSL 3.5.5 |
/CN=foo\/O=bar |
/CN=foo/O=bar |
OpenSSL 3 escapes the literal / inside the value with a backslash, correctly distinguishing the two certificates. wolfSSL produces byte-identical output for both - the two structurally different subjects become indistinguishable through this API.
Root cause
GetRDN() in wolfcrypt/src/asn.c copies the AVA value verbatim into the flat string:
XMEMCPY(&full[*idx], typeStr, typeStrLen); /* e.g. "/CN=" */
*idx += typeStrLen;
XMEMCPY(&full[*idx], str, strLen); /* value, raw, no escaping */
*idx += strLen;
No escaping of / (the separator this format uses between RDNs) or + (used between AVAs in a multi-valued RDN) is performed.
Suggested fix
Escape a literal / and + (and ideally \ itself, for reversibility) with a preceding backslash when copying the AVA value into full, matching OpenSSL 3's convention, instead of the raw XMEMCPY. This needs:
- An escaping copy loop in place of the second
XMEMCPY.
- Adjusting the capacity check just above it (
(typeStrLen + strLen) < (word32)(WC_ASN_NAME_MAX - *idx)) for the worst case where every byte needs escaping.
Note: GetRDN()'s loop appears to process one AttributeTypeAndValue per RDN with no +-joining logic - multi-valued RDNs don't seem to be modeled at all today, so + escaping is more about forward-consistency than an active gap.
Setup
Two certificates, same CA, deliberately different subject structure:
CN's value is the literal stringfoo/O=bar.CN=fooandO=bar.Confirmed structurally different via OpenSSL's own multi-line print:
Reproduction
Same program (load cert,
X509_get_subject_name(),X509_NAME_oneline()), compiled twice - once against wolfSSL 5.9.1 (OPENSSL_EXTRA,HAVE_OPENSSL), once against OpenSSL 3.5.5:Results, same two certificates
/in value)/CN=foo/O=bar/CN=foo/O=bar/CN=foo\/O=bar/CN=foo/O=barOpenSSL 3 escapes the literal
/inside the value with a backslash, correctly distinguishing the two certificates. wolfSSL produces byte-identical output for both - the two structurally different subjects become indistinguishable through this API.Root cause
GetRDN()inwolfcrypt/src/asn.ccopies the AVA value verbatim into the flat string:No escaping of
/(the separator this format uses between RDNs) or+(used between AVAs in a multi-valued RDN) is performed.Suggested fix
Escape a literal
/and+(and ideally\itself, for reversibility) with a preceding backslash when copying the AVA value intofull, matching OpenSSL 3's convention, instead of the rawXMEMCPY. This needs:XMEMCPY.(typeStrLen + strLen) < (word32)(WC_ASN_NAME_MAX - *idx)) for the worst case where every byte needs escaping.Note:
GetRDN()'s loop appears to process one AttributeTypeAndValue per RDN with no+-joining logic - multi-valued RDNs don't seem to be modeled at all today, so+escaping is more about forward-consistency than an active gap.