Coverage for pki_tools/types/name.py: 100%
59 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-11-02 22:00 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-11-02 22:00 +0000
1from collections import defaultdict
3from cryptography import x509
4from cryptography.hazmat._oid import NameOID
5from cryptography.hazmat.bindings._rust import ObjectIdentifier
6from pydantic import ConfigDict, Field
8from pki_tools.types.crypto_parser import CryptoParser
11class Name(CryptoParser):
12 """
13 Name type describes e.g. certificate subject or issuer.
14 The attributes are following the
15 [RFC5280#Section-4.1.2.4](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4)
17 Note that every attribute is a list of string in order to support
18 multivalued RDNs.
20 Attributes:
21 c: Country Name (2.5.4.6)
22 o: Organization Name (2.5.4.10)
23 ou: Organizational Unit Name (2.5.4.11)
24 dnq: Distinguished Name Qualifier (2.5.4.46)
25 s: State Or Province Name (2.5.4.8)
26 cn: Common Name (2.5.4.3)
27 serial: Serial Number (2.5.4.5)
28 ln: Locality Name (2.5.4.7)
29 t: Title (2.5.4.12)
30 sn: Surname (2.5.4.4)
31 gn: Given Name (2.5.4.42)
32 i: Initials (2.5.4.43)
33 p: Pseudonym (2.5.4.65)
34 gq: Generation Qualifier (2.5.4.44)
35 dc: Domain Component (0.9.2342.19200300.100.1.25)
36 """
38 model_config = ConfigDict(populate_by_name=True)
40 c: list[str] = Field(alias=NameOID.COUNTRY_NAME.dotted_string, default=[])
41 o: list[str] = Field(
42 alias=NameOID.ORGANIZATION_NAME.dotted_string, default=[]
43 )
44 ou: list[str] = Field(
45 alias=NameOID.ORGANIZATIONAL_UNIT_NAME.dotted_string, default=[]
46 )
47 dnq: list[str] = Field(
48 alias=NameOID.DN_QUALIFIER.dotted_string, default=[]
49 )
50 s: list[str] = Field(
51 alias=NameOID.STATE_OR_PROVINCE_NAME.dotted_string, default=[]
52 )
53 cn: list[str] = Field(alias=NameOID.COMMON_NAME.dotted_string, default=[])
54 serial: list[str] = Field(
55 alias=NameOID.SERIAL_NUMBER.dotted_string, default=[]
56 )
58 ln: list[str] = Field(
59 alias=NameOID.LOCALITY_NAME.dotted_string, default=[]
60 )
61 t: list[str] = Field(alias=NameOID.TITLE.dotted_string, default=[])
62 sn: list[str] = Field(alias=NameOID.SURNAME.dotted_string, default=[])
63 gn: list[str] = Field(alias=NameOID.GIVEN_NAME.dotted_string, default=[])
64 i: list[str] = Field(alias=NameOID.INITIALS.dotted_string, default=[])
65 p: list[str] = Field(alias=NameOID.PSEUDONYM.dotted_string, default=[])
66 gq: list[str] = Field(
67 alias=NameOID.GENERATION_QUALIFIER.dotted_string, default=[]
68 )
69 dc: list[str] = Field(
70 alias=NameOID.DOMAIN_COMPONENT.dotted_string, default=[]
71 )
73 @classmethod
74 def from_cryptography(cls: type["Name"], name: x509.Name) -> "Name":
75 """
76 Create a Name instance from a cryptography Name object.
78 Args:
79 name: The cryptography Name object.
81 Returns:
82 The Name instance.
83 """
84 subject = defaultdict(set)
85 for attribute in name:
86 for att in name.get_attributes_for_oid(attribute.oid):
87 subject[att.oid.dotted_string].add(att.value)
88 subject = dict(subject)
89 subject["_x509_obj"] = name
90 return cls(**subject)
92 def _to_cryptography(self) -> x509.Name:
93 name_attributes = []
94 for name, field in Name.model_fields.items():
95 object_identifier = ObjectIdentifier(field.alias)
96 field_vals = getattr(self, name)
97 for val in field_vals:
98 name_attr = x509.NameAttribute(object_identifier, val)
99 name_attributes.append(name_attr)
100 return x509.Name(name_attributes)
102 def _string_dict(self):
103 ret = defaultdict(list)
104 for a in set(self.model_dump()):
105 for val in getattr(self, a):
106 ret[a.upper()].append(val)
107 return ret
109 def __str__(self):
110 name_list = []
111 for k in sorted(self._string_dict()):
112 v = self._string_dict()[k]
113 name_list.append(f"{k}: {','.join(v)}")
114 return ", ".join(name_list)
116 def __eq__(self, other):
117 for key in self.model_dump():
118 val_list = getattr(self, key)
119 if set(val_list) != set(getattr(other, key)):
120 return False
121 return True