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