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