Skip to content

X509_NAME_oneline(): two structurally different certificate subjects render identically (no escaping of '/' or '+' in RDN values) #11392

Description

@vaintroub

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:

  1. An escaping copy loop in place of the second XMEMCPY.
  2. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions