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

1from collections import defaultdict 

2 

3from cryptography import x509 

4from cryptography.hazmat._oid import NameOID 

5from cryptography.hazmat.bindings._rust import ObjectIdentifier 

6from pydantic import ConfigDict, Field 

7 

8from pki_tools.types.crypto_parser import CryptoParser 

9 

10 

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) 

16 

17 Note that every attribute is a list of string in order to support 

18 multivalued RDNs. 

19 

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 """ 

37 

38 model_config = ConfigDict(populate_by_name=True) 

39 

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 ) 

57 

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 ) 

72 

73 @classmethod 

74 def from_cryptography(cls: type["Name"], name: x509.Name) -> "Name": 

75 """ 

76 Create a Name instance from a cryptography Name object. 

77 

78 Args: 

79 name: The cryptography Name object. 

80 

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) 

91 

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) 

101 

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 

108 

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) 

115 

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