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