*/ private const MAP_OID_TO_CLASS = [ self::OID_AUTHORITY_KEY_IDENTIFIER => AuthorityKeyIdentifierExtension::class, self::OID_SUBJECT_KEY_IDENTIFIER => SubjectKeyIdentifierExtension::class, self::OID_KEY_USAGE => KeyUsageExtension::class, self::OID_CERTIFICATE_POLICIES => CertificatePoliciesExtension::class, self::OID_POLICY_MAPPINGS => PolicyMappingsExtension::class, self::OID_SUBJECT_ALT_NAME => SubjectAlternativeNameExtension::class, self::OID_ISSUER_ALT_NAME => IssuerAlternativeNameExtension::class, self::OID_SUBJECT_DIRECTORY_ATTRIBUTES => SubjectDirectoryAttributesExtension::class, self::OID_BASIC_CONSTRAINTS => BasicConstraintsExtension::class, self::OID_NAME_CONSTRAINTS => NameConstraintsExtension::class, self::OID_POLICY_CONSTRAINTS => PolicyConstraintsExtension::class, self::OID_EXT_KEY_USAGE => ExtendedKeyUsageExtension::class, self::OID_CRL_DISTRIBUTION_POINTS => CRLDistributionPointsExtension::class, self::OID_INHIBIT_ANY_POLICY => InhibitAnyPolicyExtension::class, self::OID_FRESHEST_CRL => FreshestCRLExtension::class, self::OID_NO_REV_AVAIL => NoRevocationAvailableExtension::class, self::OID_TARGET_INFORMATION => TargetInformationExtension::class, self::OID_AUTHORITY_INFORMATION_ACCESS => AuthorityInformationAccessExtension::class, self::OID_AA_CONTROLS => AAControlsExtension::class, self::OID_SUBJECT_INFORMATION_ACCESS => SubjectInformationAccessExtension::class, ]; /** * Mapping from extensions ID to short name. * * @internal * * @var array */ private const MAP_OID_TO_NAME = [ self::OID_AUTHORITY_KEY_IDENTIFIER => 'authorityKeyIdentifier', self::OID_SUBJECT_KEY_IDENTIFIER => 'subjectKeyIdentifier', self::OID_KEY_USAGE => 'keyUsage', self::OID_PRIVATE_KEY_USAGE_PERIOD => 'privateKeyUsagePeriod', self::OID_CERTIFICATE_POLICIES => 'certificatePolicies', self::OID_POLICY_MAPPINGS => 'policyMappings', self::OID_SUBJECT_ALT_NAME => 'subjectAltName', self::OID_ISSUER_ALT_NAME => 'issuerAltName', self::OID_SUBJECT_DIRECTORY_ATTRIBUTES => 'subjectDirectoryAttributes', self::OID_BASIC_CONSTRAINTS => 'basicConstraints', self::OID_NAME_CONSTRAINTS => 'nameConstraints', self::OID_POLICY_CONSTRAINTS => 'policyConstraints', self::OID_EXT_KEY_USAGE => 'extKeyUsage', self::OID_CRL_DISTRIBUTION_POINTS => 'cRLDistributionPoints', self::OID_INHIBIT_ANY_POLICY => 'inhibitAnyPolicy', self::OID_FRESHEST_CRL => 'freshestCRL', self::OID_NO_REV_AVAIL => 'noRevAvail', self::OID_TARGET_INFORMATION => 'targetInformation', self::OID_AUTHORITY_INFORMATION_ACCESS => 'authorityInfoAccess', self::OID_AA_CONTROLS => 'aaControls', self::OID_SUBJECT_INFORMATION_ACCESS => 'subjectInfoAccess', self::OID_LOGOTYPE => 'logotype', ]; /** * @param string $oid Extension OID * @param bool $critical Whether extension is critical */ protected function __construct( private readonly string $oid, private readonly bool $critical ) { } public function __toString(): string { return $this->extensionName(); } /** * Initialize from ASN.1. */ public static function fromASN1(Sequence $seq): self { $idx = 0; $extnID = $seq->at($idx++) ->asObjectIdentifier() ->oid(); $critical = false; if ($seq->has($idx, Element::TYPE_BOOLEAN)) { $critical = $seq->at($idx++) ->asBoolean() ->value(); } $data = $seq->at($idx) ->asOctetString() ->string(); if (array_key_exists($extnID, self::MAP_OID_TO_CLASS)) { $cls = self::MAP_OID_TO_CLASS[$extnID]; return $cls::fromDER($data, $critical); } return UnknownExtension::fromRawString($extnID, $critical, $data); } /** * Get extension OID. */ public function oid(): string { return $this->oid; } /** * Check whether extension is critical. */ public function isCritical(): bool { return $this->critical; } /** * Generate ASN.1 structure. */ public function toASN1(): Sequence { $elements = [ObjectIdentifier::create($this->oid)]; if ($this->critical) { $elements[] = Boolean::create(true); } $elements[] = $this->extnValue(); return Sequence::create(...$elements); } /** * Get short name of the extension. */ public function extensionName(): string { if (array_key_exists($this->oid, self::MAP_OID_TO_NAME)) { return self::MAP_OID_TO_NAME[$this->oid]; } return $this->oid(); } /** * Get ASN.1 structure of the extension value. */ abstract protected function valueASN1(): Element; /** * Parse extension value from DER. * * @param string $data DER data * @param bool $critical Whether extension is critical */ abstract protected static function fromDER(string $data, bool $critical): static; /** * Get the extnValue element. */ protected function extnValue(): OctetString { return OctetString::create($this->valueASN1()->toDER()); } }