Botan  2.11.0
Crypto and TLS for C++11
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/x509_dn.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 
18 namespace Botan {
19 
20 /*
21 * Add an attribute to a X509_DN
22 */
23 void 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 */
32 void 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 */
44 std::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 */
56 std::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  std::string str_value = OIDS::oid2str(i.first);
63 
64  if(str_value.empty())
65  str_value = i.first.to_string();
66  multimap_insert(retval, str_value, i.second.value());
67  }
68  return retval;
69  }
70 
71 bool X509_DN::has_field(const std::string& attr) const
72  {
74  }
75 
76 bool 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 
87 std::string X509_DN::get_first_attribute(const std::string& attr) const
88  {
89  const OID oid = OIDS::lookup(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 */
109 std::vector<std::string> X509_DN::get_attribute(const std::string& attr) const
110  {
111  const OID oid = OIDS::lookup(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 */
129 std::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 */
146 bool 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 */
174 bool 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 */
182 bool 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 
291  rdn.start_cons(SEQUENCE)
292  .decode(oid)
293  .decode(str) // TODO support Any
294  .end_cons().verify_end("Invalid X509_DN, data follows RDN");
295 
296  add_attribute(oid, str);
297  }
298  }
299 
300  m_dn_bits = bits;
301  }
302 
303 namespace {
304 
305 std::string to_short_form(const OID& oid)
306  {
307  const std::string long_id = OIDS::oid2str(oid);
308 
309  if(long_id.empty())
310  return oid.to_string();
311 
312  if(long_id == "X520.CommonName")
313  return "CN";
314 
315  if(long_id == "X520.Country")
316  return "C";
317 
318  if(long_id == "X520.Organization")
319  return "O";
320 
321  if(long_id == "X520.OrganizationalUnit")
322  return "OU";
323 
324  return long_id;
325  }
326 
327 }
328 
329 std::string X509_DN::to_string() const
330  {
331  std::ostringstream out;
332  out << *this;
333  return out.str();
334  }
335 
336 std::ostream& operator<<(std::ostream& out, const X509_DN& dn)
337  {
338  auto info = dn.dn_info();
339 
340  for(size_t i = 0; i != info.size(); ++i)
341  {
342  out << to_short_form(info[i].first) << "=\"";
343  for(char c : info[i].second.value())
344  {
345  if(c == '\\' || c == '\"')
346  {
347  out << "\\";
348  }
349  out << c;
350  }
351  out << "\"";
352 
353  if(i + 1 < info.size())
354  {
355  out << ",";
356  }
357  }
358  return out;
359  }
360 
361 std::istream& operator>>(std::istream& in, X509_DN& dn)
362  {
363  in >> std::noskipws;
364  do
365  {
366  std::string key;
367  std::string val;
368  char c;
369 
370  while(in.good())
371  {
372  in >> c;
373 
374  if(std::isspace(c) && key.empty())
375  continue;
376  else if(!std::isspace(c))
377  {
378  key.push_back(c);
379  break;
380  }
381  else
382  break;
383  }
384 
385  while(in.good())
386  {
387  in >> c;
388 
389  if(!std::isspace(c) && c != '=')
390  key.push_back(c);
391  else if(c == '=')
392  break;
393  else
394  throw Invalid_Argument("Ill-formed X.509 DN");
395  }
396 
397  bool in_quotes = false;
398  while(in.good())
399  {
400  in >> c;
401 
402  if(std::isspace(c))
403  {
404  if(!in_quotes && !val.empty())
405  break;
406  else if(in_quotes)
407  val.push_back(' ');
408  }
409  else if(c == '"')
410  in_quotes = !in_quotes;
411  else if(c == '\\')
412  {
413  if(in.good())
414  in >> c;
415  val.push_back(c);
416  }
417  else if(c == ',' && !in_quotes)
418  break;
419  else
420  val.push_back(c);
421  }
422 
423  if(!key.empty() && !val.empty())
425  else
426  break;
427  }
428  while(in.good());
429  return in;
430  }
431 }
bool operator!=(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition: alg_id.cpp:90
ASN1_String get_first_attribute(const OID &oid) const
Definition: x509_dn.cpp:93
int operator<<(int fd, Pipe &pipe)
Definition: fd_unix.cpp:17
const std::vector< std::pair< OID, ASN1_String > > & dn_info() const
Definition: x509_dn.h:56
static std::string deref_info_field(const std::string &key)
Definition: x509_dn.cpp:129
void add_attribute(const std::string &key, const std::string &val)
Definition: x509_dn.cpp:23
#define BOTAN_ASSERT_NOMSG(expr)
Definition: assert.h:68
BER_Decoder & raw_bytes(std::vector< uint8_t, Alloc > &out)
Definition: ber_dec.h:156
void encode_into(class DER_Encoder &) const override
Definition: x509_dn.cpp:241
MechanismType type
DER_Encoder & end_cons()
Definition: der_enc.cpp:191
BER_Decoder & decode(bool &out)
Definition: ber_dec.h:170
DER_Encoder & raw_bytes(const uint8_t val[], size_t len)
Definition: der_enc.cpp:228
void decode_from(class BER_Decoder &) override
Definition: x509_dn.cpp:272
std::string to_string() const
Definition: asn1_oid.cpp:50
DER_Encoder & encode(bool b)
Definition: der_enc.cpp:285
bool operator<(const OID &a, const OID &b)
Definition: asn1_oid.cpp:105
std::vector< std::string > get_attribute(const std::string &attr) const
Definition: x509_dn.cpp:109
BER_Decoder & end_cons()
Definition: ber_dec.cpp:300
#define BOTAN_DEBUG_ASSERT(expr)
Definition: assert.h:123
bool x500_name_cmp(const std::string &name1, const std::string &name2)
Definition: parsing.cpp:228
BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag=UNIVERSAL)
Definition: ber_dec.cpp:290
Definition: alg_id.cpp:13
const std::string & value() const
Definition: asn1_str.h:27
bool has_field(const OID &oid) const
Definition: x509_dn.cpp:76
std::string oid2str(const OID &oid)
Definition: oids.h:48
BER_Decoder & verify_end()
Definition: ber_dec.cpp:208
DER_Encoder & start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag=UNIVERSAL)
Definition: der_enc.cpp:181
int operator>>(int fd, Pipe &pipe)
Definition: fd_unix.cpp:40
bool operator==(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition: alg_id.cpp:75
void multimap_insert(std::multimap< K, V > &multimap, const K &key, const V &value)
Definition: stl_util.h:76
std::multimap< OID, std::string > get_attributes() const
Definition: x509_dn.cpp:44
bool more_items() const
Definition: ber_dec.cpp:198
bool empty() const
Definition: asn1_str.h:31
std::string lookup(const OID &oid)
Definition: oids.cpp:113
std::string to_string() const
Definition: x509_dn.cpp:329
std::multimap< std::string, std::string > contents() const
Definition: x509_dn.cpp:56