Расширение Subject Alternative Name (SAN)

Расширение Subject Alternative Name (SAN) является одним из ключевых элементов современных X.509 сертификатов, позволяющим связывать один сертификат с несколькими идентификаторами одновременно. В отличие от устаревшего подхода, где имя хоста фиксировалось исключительно в поле Common Name (CN), SAN предоставляет гибкий механизм указания множества доменов, IP-адресов и других типов идентификаторов в рамках одного сертификата.

В экосистеме JavaScript библиотека Jsrsasign предоставляет инструменты для формирования и встраивания расширений сертификатов, включая SAN, через высокоуровневые ASN.1-конструкторы.


Расширение Subject Alternative Name определяется стандартом X.509 и имеет OID:

  • 2.5.29.17

Его основная задача — описывать альтернативные имена субъекта сертификата. Это особенно важно для HTTPS-сертификатов, где один сертификат может обслуживать:

  • несколько доменов (example.com, www.example.com)
  • поддомены (*.example.com)
  • IP-адреса (192.168.0.1)
  • email-адреса
  • URI-идентификаторы

В современных TLS-реализациях именно SAN считается обязательным источником проверки имени хоста, тогда как Common Name игнорируется.


Типы идентификаторов в SAN

В структуре SAN используются GeneralName типы, каждый из которых кодируется отдельно в ASN.1:

DNS-имена

Используются для доменов:

  • example.com
  • api.example.com

IP-адреса

Поддерживаются как IPv4, так и IPv6:

  • 192.168.1.1
  • 2001:db8::1

Email

Используется реже, но поддерживается стандартом:

URI

Часто применяется в сложных инфраструктурах:


Представление SAN в ASN.1

В ASN.1 расширение SAN выглядит как последовательность GeneralName:

SubjectAltName ::= GeneralNames

GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName

Каждый GeneralName — это выбор (CHOICE), например:

  • dNSName
  • iPAddress
  • uniformResourceIdentifier
  • rfc822Name

Jsrsasign и работа с расширениями сертификатов

Библиотека Jsrsasign предоставляет пространство имен:

  • KJUR.asn1.x509

Для построения расширений используется класс Extension, а также вспомогательные структуры:

  • GeneralNames
  • GeneralName (в виде объектов JS)
  • Certificate builder

Формирование SAN через Jsrsasign

В Jsrsasign SAN обычно задается через объект расширения с именем subjectAltName.

Пример добавления DNS-имен

const extSAN = new KJUR.asn1.x509.Extension({
  extname: "subjectAltName",
  array: [
    { dns: "example.com" },
    { dns: "www.example.com" }
  ]
});

Добавление IP-адресов

const extSAN = new KJUR.asn1.x509.Extension({
  extname: "subjectAltName",
  array: [
    { ip: "192.168.1.10" },
    { ip: "10.0.0.1" }
  ]
});

Комбинированный SAN (DNS + IP + URI)

const extSAN = new KJUR.asn1.x509.Extension({
  extname: "subjectAltName",
  array: [
    { dns: "example.com" },
    { dns: "api.example.com" },
    { ip: "192.168.1.10" },
    { uri: "https://example.com/service" }
  ]
});

Встраивание SAN в сертификат

При генерации сертификата SAN добавляется в список расширений объекта Certificate.

const cert = new KJUR.asn1.x509.Certificate({
  version: 3,
  serial: { int: 1 },
  issuer: { str: "/CN=Test CA" },
  subject: { str: "/CN=example.com" },
  notbefore: "230101000000Z",
  notafter: "240101000000Z",
  sbjpubkey: pubKeyObj,

  ext: [
    extSAN
  ]
});

Особенности кодирования SAN в Jsrsasign

1. Автоматическое преобразование типов

Jsrsasign преобразует:

  • dns → dNSName
  • ip → iPAddress
  • email → rfc822Name
  • uri → uniformResourceIdentifier

Это позволяет работать с объектами JavaScript без ручного ASN.1 кодирования.


2. Кодировка IP-адресов

Важно учитывать, что IP-адреса в SAN кодируются не как строки, а как бинарное представление OCTET STRING.

Jsrsasign выполняет это преобразование автоматически, но только при корректной строковой форме IPv4/IPv6.


3. Совместимость с TLS-клиентами

Большинство браузеров и TLS-библиотек полностью игнорируют CN, если SAN присутствует. Поэтому:

  • отсутствие SAN делает сертификат недействительным для HTTPS
  • наличие SAN определяет доверенное имя

Частые ошибки при работе с SAN

Использование только Common Name

Современные клиенты не принимают CN как источник имени хоста, если SAN отсутствует.


Неверный формат IP

Нельзя использовать:

  • “192.168.1.1/24”
  • “localhost”

Допустимы только конкретные IP-адреса.


Отсутствие DNS SAN

Если сертификат предназначен для домена, DNS-имена должны быть явно указаны в SAN.


Генерация расширения SAN вручную через GeneralName

Jsrsasign позволяет более низкоуровневую форму:

const gn1 = { dNSName: "example.com" };
const gn2 = { iPAddress: "10.0.0.1" };

const extSAN = new KJUR.asn1.x509.Extension({
  extname: "subjectAltName",
  array: [gn1, gn2]
});

Такой подход полезен при динамической генерации сертификатов.


Проверка SAN в сертификате

После генерации сертификата SAN можно извлечь:

const cert = new X509();
cert.readCertPEM(pem);

const sans = cert.getExtSubjectAltName();
console.log(sans);

Результат обычно представляет список всех альтернативных имен, включенных в сертификат.


Практическое значение SAN в инфраструктуре

SAN используется в сценариях:

  • балансировка HTTPS на уровне одного сертификата
  • микросервисные архитектуры
  • wildcard + дополнительные домены
  • внутренние корпоративные PKI
  • API gateway сертификаты

Внутреннее представление в Jsrsasign

При сериализации сертификата Jsrsasign формирует ASN.1 структуру:

  • Extension SEQUENCE
  • OID 2.5.29.17
  • OCTET STRING с DER-кодированным GeneralNames

Это полностью соответствует RFC 5280, что обеспечивает совместимость с внешними TLS системами.


Поведение при отсутствии SAN

Если SAN не задан:

  • некоторые TLS клиенты отклоняют сертификат
  • браузеры могут выдавать ошибку ERR_CERT_COMMON_NAME_INVALID
  • сервер считается некорректно настроенным с точки зрения современных стандартов

Использование SAN в динамической генерации сертификатов

Jsrsasign часто применяется в сценариях:

  • генерация сертификатов на лету
  • тестовые CA
  • локальные HTTPS серверы

В таких случаях SAN формируется на основе входных параметров:

function buildSAN(domains, ips) {
  const arr = [];

  domains.forEach(d => arr.push({ dns: d }));
  ips.forEach(i => arr.push({ ip: i }));

  return new KJUR.asn1.x509.Extension({
    extname: "subjectAltName",
    array: arr
  });
}

Связь SAN и Subject Key Identifier

Хотя SAN не связан напрямую с ключами, он определяет область применения сертификата. В комбинации с:

  • Key Usage
  • Extended Key Usage

формируется полная модель доверия TLS-сертификата.


Важные аспекты совместимости

Jsrsasign-реализация SAN:

  • совместима с OpenSSL
  • корректно интерпретируется браузерами Chromium и Firefox
  • соответствует RFC 5280 без дополнительных преобразований

При этом любые нестандартные поля в SAN игнорируются клиентами без ошибки, но могут снижать доверие к сертификату в строгих средах.