silk.tls.certificate_text.

Text forms of certificate fields as OpenSSL prints them: object names, distinguished names, validity times, and serial numbers.

When to use#

Use it to show a certificate the way OpenSSL and Node.js do, such as X509Certificate.subject, issuer, validFrom, serialNumber, and signatureAlgorithm in Node.js, or the DirName entries of subjectAltName.

Details#

CertificateText.objectName looks up the short and long names of an object identifier in the object table of OpenSSL 3.0, and CertificateText.writeObjectIdentifier writes the dotted form, such as 1.2.840.113549.1.1.11.

CertificateText.writeName writes a DER Name, such as Certificate.subjectDer, in one of the NameTextFormat layouts. It decodes BMPString, UniversalString, and the 8-bit string types to UTF-8 and escapes as RFC 2253 does. It checks the strings as OpenSSL does when it reads a name, so a name that OpenSSL rejects is an error here too.

CertificateText.writeTime writes a CertificateTime as Jan 2 03:04:05 2025 GMT, and CertificateText.writeSerial writes a serial number in upper-case hexadecimal.

Errors#

Gotchas#

The text is for display and logs. Compare names by their DER, not by this text: two different names can print the same. The object table is the one of OpenSSL 3.0, so a newer identifier prints in dotted form.

Examples#

silk
import silk.core.result { Result }
import silk.collections.slice { Slice }
import silk.tls.certificate_text { CertificateText, CertificateTextError, NameTextFormat }

pub fn main() -> i32 {
  // SEQUENCE { SET { SEQUENCE { commonName, UTF8String "a,b" } } }
  let name = b"\x30\x0e\x31\x0c\x30\x0a\x06\x03\x55\x04\x03\x0c\x03\x61\x2c\x62"
  let mut text: [u8; 16] = [0; 16]
  let length = match CertificateText.writeName(name, NameTextFormat.Multiline, &mut text) {
    Result<usize, CertificateTextError>.Success { value } => value
    Result<usize, CertificateTextError>.Failure { error: _ } => {
      return 1
    }
  }
  // The comma is escaped: CN=a\,b
  if length != 7 || text[4] != 92 {
    return 2
  }
  return 42
}

Import as CertificateText with import silk.tls.certificate_text { CertificateText }.

Public declarations: 4.

CertificateTextError#

silk
pub enum CertificateTextError

The reason that a certificate field could not be written as text.

InvalidEncoding#

silk
InvalidEncoding

The DER is not valid, not minimal, has bytes after it, or holds a string that OpenSSL rejects, such as a UTF8String that is not UTF-8.

OutputTooSmall#

silk
OutputTooSmall

The output is shorter than the text. Its first bytes may have been written.

NameTextFormat#

silk
pub enum NameTextFormat

How CertificateText.writeName lays out a distinguished name.

Multiline#

silk
Multiline

One attribute per line in the order of the DER, such as C=US\nO=Example\nCN=example.com, with + between the attributes of one RDN. Control characters are escaped as \XX. Node.js uses it for X509Certificate.subject and issuer (OpenSSL's XN_FLAG_SEP_MULTILINE with ASN1_STRFLGS_ESC_2253, ESC_CTRL, and UTF8_CONVERT).

Rfc2253#

silk
Rfc2253

RFC 2253: the RDNs in reverse order, separated by ,, with + inside an RDN, such as CN=example.com,O=Example,C=US. An attribute with an unknown type, or a value that is not a string, is written as # and the hexadecimal DER of the value. Control characters are not escaped. Node.js uses it for the DirName entries of subjectAltName (OpenSSL's XN_FLAG_RFC2253 without ESC_CTRL and ESC_MSB).

ObjectName#

silk
pub struct ObjectName

The names of an object identifier in the object table of OpenSSL.

Field identifier#

silk
pub identifier: &'static [u8]

The DER content of the object identifier, such as \x55\x04\x03 for 2.5.4.3.

Field shortName#

silk
pub shortName: &'static [u8]

The short name, such as CN or RSA-SHA256.

Field longName#

silk
pub longName: &'static [u8]

The long name, such as commonName or sha256WithRSAEncryption.

Implementation Copy for ObjectName#

silk
impl Copy for ObjectName

CertificateText#

silk
pub struct CertificateText

Functions that write certificate fields as text.

Implementation CertificateText#

silk
impl CertificateText

Associated function objectName#

silk
pub fn objectName(identifier: &[u8]) -> Option<ObjectName>

Returns the names of the object identifier whose DER content is identifier, or None when the object table of OpenSSL has no entry for it.

Associated function namedObject#

silk
pub fn namedObject(name: &[u8]) -> Option<ObjectName>

Returns the object whose short name or long name is name, as OpenSSL's OBJ_txt2nid finds it: short names first, then long names. The match is exact and case-sensitive.

Associated function objectIdentifierLength#

silk
pub fn objectIdentifierLength(identifier: &[u8]) -> Result<usize, CertificateTextError>

Returns the length in bytes of CertificateText.writeObjectIdentifier.

Errors#

Associated function writeObjectIdentifier#

silk
pub fn writeObjectIdentifier(identifier: &[u8], output: &mut [u8]) -> Result<
    usize,
    CertificateTextError,
  >

Writes the dotted form of the object identifier whose DER content is identifier, such as 2.5.4.3, to the start of output and returns its length, as OpenSSL's OBJ_obj2txt does with numeric output.

Errors#

  • InvalidEncoding if identifier is empty, a sub-identifier has a leading 0x80 byte or is not finished, or an arc has more than 1024 decimal digits.

  • OutputTooSmall if output is shorter than the text.

Associated function nameLength#

silk
pub fn nameLength(name: &[u8], format: NameTextFormat) -> Result<usize, CertificateTextError>

Returns the length in bytes of CertificateText.writeName.

Errors#

Associated function writeName#

silk
pub fn writeName(name: &[u8], format: NameTextFormat, output: &mut [u8]) -> Result<
    usize,
    CertificateTextError,
  >

Writes the DER Name name as text in format to the start of output and returns its length. An empty name is empty text.

Details#

Each attribute is its short name from the object table, or its dotted identifier, then = and the value. A UTF8String is written as it is, the 8-bit string types as Latin-1, and BMPString and UniversalString as UTF-8. ", +, ,, ;, <, >, and \ are escaped with \, and so are a leading space or # and a trailing space.

Errors#

  • InvalidEncoding if the DER is not valid or not minimal, or a value is a type or string that OpenSSL rejects in a name: a UTF8String that is not UTF-8, a BMPString or UniversalString with a surrogate or a bad length, or a tag such as INTEGER.

  • OutputTooSmall if output is shorter than the text.

Associated function writeTime#

silk
pub fn writeTime(time: CertificateTime, output: &mut [u8; 24]) -> usize

Writes time as OpenSSL's ASN1_TIME_print does, such as Jan 2 03:04:05 2025 GMT, to the start of output and returns its length. The day is padded to two places with a space and the year is not padded.

Associated function serialLength#

silk
pub fn serialLength(serial: &[u8]) -> Result<usize, CertificateTextError>

Returns the length in bytes of CertificateText.writeSerial.

Errors#

  • InvalidEncoding if serial is empty.

Associated function writeSerial#

silk
pub fn writeSerial(serial: &[u8], output: &mut [u8]) -> Result<usize, CertificateTextError>

Writes the big-endian signed integer serial, such as Certificate.serial, in upper-case hexadecimal to the start of output and returns its length, as OpenSSL's BN_bn2hex does: whole bytes without leading zero bytes, such as 0A1B, 0 for zero, and - before the magnitude of a negative number.

Errors#

  • InvalidEncoding if serial is empty.

  • OutputTooSmall if output is shorter than the text.

Category: TLS and certificates.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.

View the source of this page