@@ -158,6 +158,38 @@ pub enum SanType {
158
158
DnsName ( Ia5String ) ,
159
159
URI ( Ia5String ) ,
160
160
IpAddress ( IpAddr ) ,
161
+ OtherName ( ( Vec < u64 > , OtherNameValue ) ) ,
162
+ }
163
+
164
+ /// An `OtherName` value, defined in [RFC 5280§4.1.2.4].
165
+ ///
166
+ /// While the standard specifies this could be any ASN.1 type rcgen limits
167
+ /// the value to a UTF-8 encoded string as this will cover the most common
168
+ /// use cases, for instance smart card user principal names (UPN).
169
+ ///
170
+ /// [RFC 5280§4.1.2.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
171
+ #[ derive( Debug , PartialEq , Eq , Hash , Clone ) ]
172
+ #[ non_exhaustive]
173
+ pub enum OtherNameValue {
174
+ /// A string encoded using UTF-8
175
+ Utf8String ( String ) ,
176
+ }
177
+
178
+ impl OtherNameValue {
179
+ fn write_der ( & self , writer : DERWriter ) {
180
+ writer. write_tagged ( Tag :: context ( 0 ) , |writer| match self {
181
+ OtherNameValue :: Utf8String ( s) => writer. write_utf8_string ( s) ,
182
+ } ) ;
183
+ }
184
+ }
185
+
186
+ impl < T > From < T > for OtherNameValue
187
+ where
188
+ T : Into < String > ,
189
+ {
190
+ fn from ( t : T ) -> Self {
191
+ OtherNameValue :: Utf8String ( t. into ( ) )
192
+ }
161
193
}
162
194
163
195
#[ cfg( feature = "x509-parser" ) ]
@@ -174,6 +206,7 @@ fn ip_addr_from_octets(octets: &[u8]) -> Result<IpAddr, Error> {
174
206
impl SanType {
175
207
#[ cfg( feature = "x509-parser" ) ]
176
208
fn try_from_general ( name : & x509_parser:: extensions:: GeneralName < ' _ > ) -> Result < Self , Error > {
209
+ use x509_parser:: der_parser:: asn1_rs:: { self , FromDer , Tag , TaggedExplicit } ;
177
210
Ok ( match name {
178
211
x509_parser:: extensions:: GeneralName :: RFC822Name ( name) => {
179
212
SanType :: Rfc822Name ( ( * name) . try_into ( ) ?)
@@ -185,13 +218,31 @@ impl SanType {
185
218
x509_parser:: extensions:: GeneralName :: IPAddress ( octets) => {
186
219
SanType :: IpAddress ( ip_addr_from_octets ( octets) ?)
187
220
} ,
221
+ x509_parser:: extensions:: GeneralName :: OtherName ( oid, value) => {
222
+ let oid = oid. iter ( ) . ok_or ( Error :: CouldNotParseCertificate ) ?;
223
+ // We first remove the explicit tag ([0] EXPLICIT)
224
+ let ( _, other_name) = TaggedExplicit :: < asn1_rs:: Any , _ , 0 > :: from_der ( & value)
225
+ . map_err ( |_| Error :: CouldNotParseCertificate ) ?;
226
+ let other_name = other_name. into_inner ( ) ;
227
+
228
+ let other_name_value = match other_name. tag ( ) {
229
+ Tag :: Utf8String => OtherNameValue :: Utf8String (
230
+ std:: str:: from_utf8 ( other_name. data )
231
+ . map_err ( |_| Error :: CouldNotParseCertificate ) ?
232
+ . to_owned ( ) ,
233
+ ) ,
234
+ _ => return Err ( Error :: CouldNotParseCertificate ) ,
235
+ } ;
236
+ SanType :: OtherName ( ( oid. collect ( ) , other_name_value) )
237
+ } ,
188
238
_ => return Err ( Error :: InvalidNameType ) ,
189
239
} )
190
240
}
191
241
192
242
fn tag ( & self ) -> u64 {
193
243
// Defined in the GeneralName list in
194
244
// https://tools.ietf.org/html/rfc5280#page-38
245
+ const TAG_OTHER_NAME : u64 = 0 ;
195
246
const TAG_RFC822_NAME : u64 = 1 ;
196
247
const TAG_DNS_NAME : u64 = 2 ;
197
248
const TAG_URI : u64 = 6 ;
@@ -202,6 +253,7 @@ impl SanType {
202
253
SanType :: DnsName ( _name) => TAG_DNS_NAME ,
203
254
SanType :: URI ( _name) => TAG_URI ,
204
255
SanType :: IpAddress ( _addr) => TAG_IP_ADDRESS ,
256
+ Self :: OtherName ( _oid) => TAG_OTHER_NAME ,
205
257
}
206
258
}
207
259
}
@@ -879,6 +931,14 @@ impl CertificateParams {
879
931
SanType :: IpAddress ( IpAddr :: V6 ( addr) ) => {
880
932
writer. write_bytes ( & addr. octets ( ) )
881
933
} ,
934
+ SanType :: OtherName ( ( oid, value) ) => {
935
+ // otherName SEQUENCE { OID, [0] explicit any defined by oid }
936
+ // https://datatracker.ietf.org/doc/html/rfc5280#page-38
937
+ writer. write_sequence ( |writer| {
938
+ writer. next ( ) . write_oid ( & ObjectIdentifier :: from_slice ( & oid) ) ;
939
+ value. write_der ( writer. next ( ) ) ;
940
+ } ) ;
941
+ } ,
882
942
} ,
883
943
) ;
884
944
}
0 commit comments