Botan 3.0.0
Crypto and TLS for C&
x509_dn.cpp
Go to the documentation of this file.
1/*
2* X509_DN
3* (C) 1999-2007,2018 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/pkix_types.h>
9#include <botan/der_enc.h>
10#include <botan/ber_dec.h>
11#include <botan/internal/stl_util.h>
12#include <ostream>
13#include <sstream>
14#include <cctype>
15
16namespace Botan {
17
18namespace {
19
20namespace {
21
22bool caseless_cmp(char a, char b)
23 {
24 return (std::tolower(static_cast<unsigned char>(a)) ==
25 std::tolower(static_cast<unsigned char>(b)));
26 }
27
28bool is_space(char c)
29 {
30 return std::isspace(static_cast<unsigned char>(c));
31 }
32
33}
34
35/*
36* X.500 String Comparison
37*/
38bool x500_name_cmp(std::string_view name1, std::string_view name2)
39 {
40 auto p1 = name1.begin();
41 auto p2 = name2.begin();
42
43 while((p1 != name1.end()) && is_space(*p1)) ++p1;
44 while((p2 != name2.end()) && is_space(*p2)) ++p2;
45
46 while(p1 != name1.end() && p2 != name2.end())
47 {
48 if(is_space(*p1))
49 {
50 if(!is_space(*p2))
51 return false;
52
53 while((p1 != name1.end()) && is_space(*p1)) ++p1;
54 while((p2 != name2.end()) && is_space(*p2)) ++p2;
55
56 if(p1 == name1.end() && p2 == name2.end())
57 return true;
58 if(p1 == name1.end() || p2 == name2.end())
59 return false;
60 }
61
62 if(!caseless_cmp(*p1, *p2))
63 return false;
64 ++p1;
65 ++p2;
66 }
67
68 while((p1 != name1.end()) && is_space(*p1)) ++p1;
69 while((p2 != name2.end()) && is_space(*p2)) ++p2;
70
71 if((p1 != name1.end()) || (p2 != name2.end()))
72 return false;
73 return true;
74 }
75
76}
77
78/*
79* Add an attribute to a X509_DN
80*/
81void X509_DN::add_attribute(std::string_view type,
82 std::string_view str)
83 {
85 }
86
87/*
88* Add an attribute to a X509_DN
89*/
90void X509_DN::add_attribute(const OID& oid, const ASN1_String& str)
91 {
92 if(str.empty())
93 return;
94
95 m_rdn.push_back(std::make_pair(oid, str));
96 m_dn_bits.clear();
97 }
98
99/*
100* Get the attributes of this X509_DN
101*/
102std::multimap<OID, std::string> X509_DN::get_attributes() const
103 {
104 std::multimap<OID, std::string> retval;
105
106 for(auto& i : m_rdn)
107 multimap_insert(retval, i.first, i.second.value());
108 return retval;
109 }
110
111/*
112* Get the contents of this X.500 Name
113*/
114std::multimap<std::string, std::string> X509_DN::contents() const
115 {
116 std::multimap<std::string, std::string> retval;
117
118 for(auto& i : m_rdn)
119 {
120 multimap_insert(retval, i.first.to_formatted_string(), i.second.value());
121 }
122 return retval;
123 }
124
125bool X509_DN::has_field(std::string_view attr) const
126 {
127 try
128 {
129 const OID o = OID::from_string(deref_info_field(attr));
130 if(o.has_value())
131 return has_field(o);
132 }
133 catch(Lookup_Error&) {}
134
135 return false;
136 }
137
138bool X509_DN::has_field(const OID& oid) const
139 {
140 for(auto& i : m_rdn)
141 {
142 if(i.first == oid)
143 return true;
144 }
145
146 return false;
147 }
148
149std::string X509_DN::get_first_attribute(std::string_view attr) const
150 {
151 const OID oid = OID::from_string(deref_info_field(attr));
152 return get_first_attribute(oid).value();
153 }
154
156 {
157 for(auto& i : m_rdn)
158 {
159 if(i.first == oid)
160 {
161 return i.second;
162 }
163 }
164
165 return ASN1_String();
166 }
167
168/*
169* Get a single attribute type
170*/
171std::vector<std::string> X509_DN::get_attribute(std::string_view attr) const
172 {
173 const OID oid = OID::from_string(deref_info_field(attr));
174
175 std::vector<std::string> values;
176
177 for(auto& i : m_rdn)
178 {
179 if(i.first == oid)
180 {
181 values.push_back(i.second.value());
182 }
183 }
184
185 return values;
186 }
187
188/*
189* Deref aliases in a subject/issuer info request
190*/
191std::string X509_DN::deref_info_field(std::string_view info)
192 {
193 if(info == "Name" || info == "CommonName" || info == "CN") return "X520.CommonName";
194 if(info == "SerialNumber" || info == "SN") return "X520.SerialNumber";
195 if(info == "Country" || info == "C") return "X520.Country";
196 if(info == "Organization" || info == "O") return "X520.Organization";
197 if(info == "Organizational Unit" || info == "OrgUnit" || info == "OU")
198 return "X520.OrganizationalUnit";
199 if(info == "Locality" || info == "L") return "X520.Locality";
200 if(info == "State" || info == "Province" || info == "ST") return "X520.State";
201 if(info == "Email") return "RFC822";
202 return std::string(info);
203 }
204
205/*
206* Compare two X509_DNs for equality
207*/
208bool operator==(const X509_DN& dn1, const X509_DN& dn2)
209 {
210 auto attr1 = dn1.get_attributes();
211 auto attr2 = dn2.get_attributes();
212
213 if(attr1.size() != attr2.size()) return false;
214
215 auto p1 = attr1.begin();
216 auto p2 = attr2.begin();
217
218 while(true)
219 {
220 if(p1 == attr1.end() && p2 == attr2.end())
221 break;
222 if(p1 == attr1.end()) return false;
223 if(p2 == attr2.end()) return false;
224 if(p1->first != p2->first) return false;
225 if(!x500_name_cmp(p1->second, p2->second))
226 return false;
227 ++p1;
228 ++p2;
229 }
230 return true;
231 }
232
233/*
234* Compare two X509_DNs for inequality
235*/
236bool operator!=(const X509_DN& dn1, const X509_DN& dn2)
237 {
238 return !(dn1 == dn2);
239 }
240
241/*
242* Induce an arbitrary ordering on DNs
243*/
244bool operator<(const X509_DN& dn1, const X509_DN& dn2)
245 {
246 auto attr1 = dn1.get_attributes();
247 auto attr2 = dn2.get_attributes();
248
249 // If they are not the same size, choose the smaller as the "lessor"
250 if(attr1.size() < attr2.size())
251 return true;
252 if(attr1.size() > attr2.size())
253 return false;
254
255 // We know they are the same # of elements, now compare the OIDs:
256 auto p1 = attr1.begin();
257 auto p2 = attr2.begin();
258
259 while(p1 != attr1.end() && p2 != attr2.end())
260 {
261 if(p1->first != p2->first)
262 {
263 return (p1->first < p2->first);
264 }
265
266 ++p1;
267 ++p2;
268 }
269
270 // We know this is true because maps have the same size
271 BOTAN_ASSERT_NOMSG(p1 == attr1.end());
272 BOTAN_ASSERT_NOMSG(p2 == attr2.end());
273
274 // Now we know all elements have the same OIDs, compare
275 // their string values:
276
277 p1 = attr1.begin();
278 p2 = attr2.begin();
279 while(p1 != attr1.end() && p2 != attr2.end())
280 {
281 BOTAN_DEBUG_ASSERT(p1->first == p2->first);
282
283 // They may be binary different but same by X.500 rules, check this
284 if(!x500_name_cmp(p1->second, p2->second))
285 {
286 // If they are not (by X.500) the same string, pick the
287 // lexicographic first as the lessor
288 return (p1->second < p2->second);
289 }
290
291 ++p1;
292 ++p2;
293 }
294
295 // if we reach here, then the DNs should be identical
296 BOTAN_DEBUG_ASSERT(dn1 == dn2);
297 return false;
298 }
299
300std::vector<uint8_t> X509_DN::DER_encode() const
301 {
302 std::vector<uint8_t> result;
303 DER_Encoder der(result);
304 this->encode_into(der);
305 return result;
306 }
307
308/*
309* DER encode a DistinguishedName
310*/
312 {
313 der.start_sequence();
314
315 if(!m_dn_bits.empty())
316 {
317 /*
318 If we decoded this from somewhere, encode it back exactly as
319 we received it
320 */
321 der.raw_bytes(m_dn_bits);
322 }
323 else
324 {
325 for(const auto& dn : m_rdn)
326 {
327 der.start_set()
329 .encode(dn.first)
330 .encode(dn.second)
331 .end_cons()
332 .end_cons();
333 }
334 }
335
336 der.end_cons();
337 }
338
339/*
340* Decode a BER encoded DistinguishedName
341*/
343 {
344 std::vector<uint8_t> bits;
345
346 source.start_sequence()
347 .raw_bytes(bits)
348 .end_cons();
349
350 BER_Decoder sequence(bits);
351
352 m_rdn.clear();
353
354 while(sequence.more_items())
355 {
356 BER_Decoder rdn = sequence.start_set();
357
358 while(rdn.more_items())
359 {
360 OID oid;
361 ASN1_String str;
362
363 rdn.start_sequence()
364 .decode(oid)
365 .decode(str) // TODO support Any
366 .end_cons();
367
368 add_attribute(oid, str);
369 }
370 }
371
372 // Have to assign last as add_attribute zaps m_dn_bits
373 m_dn_bits = bits;
374 }
375
376namespace {
377
378std::string to_short_form(const OID& oid)
379 {
380 std::string long_id = oid.to_formatted_string();
381
382 if(long_id == "X520.CommonName")
383 return "CN";
384
385 if(long_id == "X520.Country")
386 return "C";
387
388 if(long_id == "X520.Organization")
389 return "O";
390
391 if(long_id == "X520.OrganizationalUnit")
392 return "OU";
393
394 return long_id;
395 }
396
397}
398
399std::string X509_DN::to_string() const
400 {
401 std::ostringstream out;
402 out << *this;
403 return out.str();
404 }
405
406std::ostream& operator<<(std::ostream& out, const X509_DN& dn)
407 {
408 auto info = dn.dn_info();
409
410 for(size_t i = 0; i != info.size(); ++i)
411 {
412 out << to_short_form(info[i].first) << "=\"";
413 for(char c : info[i].second.value())
414 {
415 if(c == '\\' || c == '\"')
416 {
417 out << "\\";
418 }
419 out << c;
420 }
421 out << "\"";
422
423 if(i + 1 < info.size())
424 {
425 out << ",";
426 }
427 }
428 return out;
429 }
430
431std::istream& operator>>(std::istream& in, X509_DN& dn)
432 {
433 in >> std::noskipws;
434 do
435 {
436 std::string key;
437 std::string val;
438 char c;
439
440 while(in.good())
441 {
442 in >> c;
443
444 if(std::isspace(c) && key.empty())
445 continue;
446 else if(!std::isspace(c))
447 {
448 key.push_back(c);
449 break;
450 }
451 else
452 break;
453 }
454
455 while(in.good())
456 {
457 in >> c;
458
459 if(!std::isspace(c) && c != '=')
460 key.push_back(c);
461 else if(c == '=')
462 break;
463 else
464 throw Invalid_Argument("Ill-formed X.509 DN");
465 }
466
467 bool in_quotes = false;
468 while(in.good())
469 {
470 in >> c;
471
472 if(std::isspace(c))
473 {
474 if(!in_quotes && !val.empty())
475 break;
476 else if(in_quotes)
477 val.push_back(' ');
478 }
479 else if(c == '"')
480 in_quotes = !in_quotes;
481 else if(c == '\\')
482 {
483 if(in.good())
484 in >> c;
485 val.push_back(c);
486 }
487 else if(c == ',' && !in_quotes)
488 break;
489 else
490 val.push_back(c);
491 }
492
493 if(!key.empty() && !val.empty())
495 else
496 break;
497 }
498 while(in.good());
499 return in;
500 }
501}
#define BOTAN_ASSERT_NOMSG(expr)
Definition: assert.h:67
#define BOTAN_DEBUG_ASSERT(expr)
Definition: assert.h:122
const std::string & value() const
Definition: asn1_obj.h:436
bool empty() const
Definition: asn1_obj.h:440
BER_Decoder start_set()
Definition: ber_dec.h:122
BER_Decoder & decode(bool &out)
Definition: ber_dec.h:193
bool more_items() const
Definition: ber_dec.cpp:201
BER_Decoder & end_cons()
Definition: ber_dec.cpp:304
BER_Decoder start_sequence()
Definition: ber_dec.h:117
DER_Encoder & start_set()
Definition: der_enc.h:71
DER_Encoder & start_sequence()
Definition: der_enc.h:66
DER_Encoder & raw_bytes(const uint8_t val[], size_t len)
Definition: der_enc.cpp:233
DER_Encoder & end_cons()
Definition: der_enc.cpp:196
DER_Encoder & encode(bool b)
Definition: der_enc.cpp:290
std::string to_formatted_string() const
Definition: asn1_oid.cpp:120
bool has_value() const
Definition: asn1_obj.h:286
static OID from_string(std::string_view str)
Definition: asn1_oid.cpp:78
bool has_field(const OID &oid) const
Definition: x509_dn.cpp:138
void decode_from(BER_Decoder &) override
Definition: x509_dn.cpp:342
std::multimap< std::string, std::string > contents() const
Definition: x509_dn.cpp:114
void encode_into(DER_Encoder &) const override
Definition: x509_dn.cpp:311
std::vector< std::string > get_attribute(std::string_view attr) const
Definition: x509_dn.cpp:171
void add_attribute(std::string_view key, std::string_view val)
Definition: x509_dn.cpp:81
std::multimap< OID, std::string > get_attributes() const
Definition: x509_dn.cpp:102
ASN1_String get_first_attribute(const OID &oid) const
Definition: x509_dn.cpp:155
const std::vector< std::pair< OID, ASN1_String > > & dn_info() const
Definition: pkix_types.h:71
std::vector< uint8_t > DER_encode() const
Definition: x509_dn.cpp:300
std::string to_string() const
Definition: x509_dn.cpp:399
static std::string deref_info_field(std::string_view key)
Definition: x509_dn.cpp:191
Definition: alg_id.cpp:12
bool operator<(const OID &a, const OID &b)
Definition: asn1_oid.cpp:141
std::ostream & operator<<(std::ostream &out, const OID &oid)
Definition: asn1_oid.cpp:150
bool operator!=(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition: alg_id.cpp:81
bool operator==(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition: alg_id.cpp:64
void multimap_insert(std::multimap< K, V > &multimap, const K &key, const V &value)
Definition: stl_util.h:94
int operator>>(int fd, Pipe &pipe)
Definition: fd_unix.cpp:40