• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 5211182863

08 Jun 2023 01:05PM UTC coverage: 91.749% (+0.008%) from 91.741%
5211182863

push

github

web-flow
Merge pull request #3579 from randombit/fix/cli_xmss_for_x509

FIX: Missing update of XMSS private key in CLI's X.509 operations

76215 of 83069 relevant lines covered (91.75%)

12243571.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.71
/src/cli/x509.cpp
1
/*
2
* (C) 2010,2014,2015,2018 Jack Lloyd
3
* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "cli.h"
9

10
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
11

12
   #include <botan/certstor.h>
13
   #include <botan/data_src.h>
14
   #include <botan/pk_keys.h>
15
   #include <botan/pkcs8.h>
16
   #include <botan/x509_ca.h>
17
   #include <botan/x509cert.h>
18
   #include <botan/x509path.h>
19
   #include <botan/x509self.h>
20

21
   #if defined(BOTAN_HAS_OCSP)
22
      #include <botan/ocsp.h>
23
   #endif
24

25
   #if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
26
      #include <botan/certstor_system.h>
27
   #endif
28

29
   #include <fstream>
30

31
namespace Botan_CLI {
32

33
namespace {
34

35
std::unique_ptr<Botan::Private_Key> load_private_key(const std::string& key_file, const std::string& passphrase) {
35✔
36
   Botan::DataSource_Stream key_stream(key_file);
35✔
37
   auto key = Botan::PKCS8::load_key(key_stream, passphrase);
35✔
38

39
   if(!key) {
35✔
40
      throw CLI_Error("Failed to load key from " + key_file);
×
41
   }
42

43
   return key;
35✔
44
}
35✔
45

46
void update_stateful_private_key(const Botan::Private_Key& key,
35✔
47
                                 Botan::RandomNumberGenerator& rng,
48
                                 const std::string& key_file,
49
                                 const std::string& pass) {
50
   if(!key.stateful_operation()) {
35✔
51
      return;
32✔
52
   }
53

54
   std::ofstream updated_key(key_file);
3✔
55
   if(pass.empty()) {
3✔
56
      updated_key << Botan::PKCS8::PEM_encode(key);
9✔
57
   } else {
58
      updated_key << Botan::PKCS8::PEM_encode(key, rng, pass);
×
59
   }
60
}
3✔
61

62
}  // namespace
63

64
   #if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
65

66
class Trust_Root_Info final : public Command {
67
   public:
68
      Trust_Root_Info() : Command("trust_roots --dn --dn-only --display") {}
6✔
69

70
      std::string group() const override { return "x509"; }
1✔
71

72
      std::string description() const override { return "List certs in the system trust store"; }
1✔
73

74
      void go() override {
2✔
75
         Botan::System_Certificate_Store trust_roots;
2✔
76

77
         const auto dn_list = trust_roots.all_subjects();
2✔
78

79
         if(flag_set("dn-only")) {
2✔
80
            for(const auto& dn : dn_list) {
138✔
81
               output() << dn << "\n";
137✔
82
            }
83
         } else {
84
            for(const auto& dn : dn_list) {
138✔
85
               // Some certstores have more than one cert with a particular DN
86
               for(const auto& cert : trust_roots.find_all_certs(dn, std::vector<uint8_t>())) {
276✔
87
                  if(flag_set("dn")) {
139✔
88
                     output() << "# " << dn << "\n";
×
89
                  }
90

91
                  if(flag_set("display")) {
139✔
92
                     output() << cert.to_string() << "\n";
×
93
                  }
94

95
                  output() << cert.PEM_encode() << "\n";
417✔
96
               }
137✔
97
            }
98
         }
99
      }
2✔
100
};
101

102
BOTAN_REGISTER_COMMAND("trust_roots", Trust_Root_Info);
3✔
103

104
   #endif
105

106
class Sign_Cert final : public Command {
107
   public:
108
      Sign_Cert() :
14✔
109
            Command(
110
               "sign_cert --ca-key-pass= --hash= "
111
               "--duration=365 --emsa= ca_cert ca_key pkcs10_req") {}
28✔
112

113
      std::string group() const override { return "x509"; }
1✔
114

115
      std::string description() const override { return "Create a CA-signed X.509 certificate from a PKCS #10 CSR"; }
1✔
116

117
      void go() override {
13✔
118
         Botan::X509_Certificate ca_cert(get_arg("ca_cert"));
26✔
119

120
         const std::string key_file = get_arg("ca_key");
13✔
121
         const std::string pass = get_passphrase_arg("Password for " + key_file, "ca-key-pass");
26✔
122
         const std::string emsa = get_arg("emsa");
13✔
123
         const std::string hash = get_arg("hash");
13✔
124

125
         auto key = load_private_key(key_file, pass);
13✔
126

127
         Botan::X509_CA ca(ca_cert, *key, hash, emsa, rng());
13✔
128

129
         Botan::PKCS10_Request req(get_arg("pkcs10_req"));
26✔
130

131
         auto now = std::chrono::system_clock::now();
13✔
132

133
         Botan::X509_Time start_time(now);
13✔
134

135
         typedef std::chrono::duration<int, std::ratio<86400>> days;
13✔
136

137
         Botan::X509_Time end_time(now + days(get_arg_sz("duration")));
13✔
138

139
         Botan::X509_Certificate new_cert = ca.sign_request(req, rng(), start_time, end_time);
13✔
140
         update_stateful_private_key(*key, rng(), key_file, pass);
13✔
141

142
         output() << new_cert.PEM_encode();
26✔
143
      }
39✔
144
};
145

146
BOTAN_REGISTER_COMMAND("sign_cert", Sign_Cert);
14✔
147

148
class Cert_Info final : public Command {
149
   public:
150
      Cert_Info() : Command("cert_info --fingerprint file") {}
4✔
151

152
      std::string group() const override { return "x509"; }
1✔
153

154
      std::string description() const override { return "Parse X.509 certificate and display data fields"; }
1✔
155

156
      void go() override {
1✔
157
         const std::string arg_file = get_arg("file");
1✔
158

159
         std::vector<uint8_t> data = slurp_file(get_arg("file"));
2✔
160

161
         Botan::DataSource_Memory in(data);
1✔
162

163
         while(!in.end_of_data()) {
3✔
164
            try {
2✔
165
               Botan::X509_Certificate cert(in);
2✔
166

167
               try {
1✔
168
                  output() << cert.to_string() << std::endl;
3✔
169
               } catch(Botan::Exception& e) {
×
170
                  // to_string failed - report the exception and continue
171
                  output() << "X509_Certificate::to_string failed: " << e.what() << "\n";
×
172
               }
×
173

174
               if(flag_set("fingerprint")) {
1✔
175
                  output() << "Fingerprint: " << cert.fingerprint("SHA-256") << std::endl;
3✔
176
               }
177
            } catch(Botan::Exception& e) {
2✔
178
               if(!in.end_of_data()) {
1✔
179
                  output() << "X509_Certificate parsing failed " << e.what() << "\n";
×
180
               }
181
            }
1✔
182
         }
183
      }
3✔
184
};
185

186
BOTAN_REGISTER_COMMAND("cert_info", Cert_Info);
2✔
187

188
   #if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_HTTP_UTIL)
189

190
class OCSP_Check final : public Command {
191
   public:
192
      OCSP_Check() : Command("ocsp_check --timeout=3000 subject issuer") {}
2✔
193

194
      std::string group() const override { return "x509"; }
1✔
195

196
      std::string description() const override {
1✔
197
         return "Verify an X.509 certificate against the issuers OCSP responder";
1✔
198
      }
199

200
      void go() override {
×
201
         Botan::X509_Certificate subject(get_arg("subject"));
×
202
         Botan::X509_Certificate issuer(get_arg("issuer"));
×
203
         std::chrono::milliseconds timeout(get_arg_sz("timeout"));
×
204

205
         Botan::Certificate_Store_In_Memory cas;
×
206
         cas.add_certificate(issuer);
×
207
         Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, timeout);
×
208

209
         auto status = resp.status_for(issuer, subject, std::chrono::system_clock::now());
×
210

211
         if(status == Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD) {
×
212
            output() << "OCSP check OK\n";
×
213
         } else {
214
            output() << "OCSP check failed " << Botan::Path_Validation_Result::status_string(status) << "\n";
×
215
         }
216
      }
×
217
};
218

219
BOTAN_REGISTER_COMMAND("ocsp_check", OCSP_Check);
1✔
220

221
   #endif  // OCSP && HTTP
222

223
class Cert_Verify final : public Command {
224
   public:
225
      Cert_Verify() : Command("cert_verify subject *ca_certs") {}
20✔
226

227
      std::string group() const override { return "x509"; }
1✔
228

229
      std::string description() const override {
1✔
230
         return "Verify if the passed X.509 certificate passes path validation";
1✔
231
      }
232

233
      void go() override {
9✔
234
         Botan::X509_Certificate subject_cert(get_arg("subject"));
18✔
235
         Botan::Certificate_Store_In_Memory trusted;
9✔
236

237
         for(const auto& certfile : get_arg_list("ca_certs")) {
19✔
238
            trusted.add_certificate(Botan::X509_Certificate(certfile));
10✔
239
         }
9✔
240

241
         Botan::Path_Validation_Restrictions restrictions;
18✔
242

243
         Botan::Path_Validation_Result result = Botan::x509_path_validate(subject_cert, restrictions, trusted);
9✔
244

245
         if(result.successful_validation()) {
9✔
246
            output() << "Certificate passes validation checks\n";
6✔
247
         } else {
248
            output() << "Certificate did not validate - " << result.result_string() << "\n";
9✔
249
         }
250
      }
9✔
251
};
252

253
BOTAN_REGISTER_COMMAND("cert_verify", Cert_Verify);
10✔
254

255
class Gen_Self_Signed final : public Command {
256
   public:
257
      Gen_Self_Signed() :
10✔
258
            Command(
259
               "gen_self_signed key CN --country= --dns= "
260
               "--organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash= --emsa= --der") {}
20✔
261

262
      std::string group() const override { return "x509"; }
1✔
263

264
      std::string description() const override { return "Generate a self signed X.509 certificate"; }
1✔
265

266
      void go() override {
9✔
267
         const std::string key_file = get_arg("key");
9✔
268
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "key-pass");
18✔
269
         auto key = load_private_key(key_file, passphrase);
9✔
270

271
         const uint32_t lifetime = static_cast<uint32_t>(get_arg_sz("days") * 24 * 60 * 60);
9✔
272

273
         Botan::X509_Cert_Options opts("", lifetime);
9✔
274

275
         opts.common_name = get_arg("CN");
9✔
276
         opts.country = get_arg("country");
9✔
277
         opts.organization = get_arg("organization");
9✔
278
         opts.email = get_arg("email");
9✔
279
         opts.more_dns = Command::split_on(get_arg("dns"), ',');
9✔
280
         const bool der_format = flag_set("der");
9✔
281

282
         std::string emsa = get_arg("emsa");
9✔
283

284
         if(emsa.empty() == false) {
9✔
285
            opts.set_padding_scheme(emsa);
×
286
         }
287

288
         if(flag_set("ca")) {
9✔
289
            opts.CA_key(get_arg_sz("path-limit"));
18✔
290
         }
291

292
         Botan::X509_Certificate cert = Botan::X509::create_self_signed_cert(opts, *key, get_arg("hash"), rng());
18✔
293
         update_stateful_private_key(*key, rng(), key_file, passphrase);
9✔
294

295
         if(der_format) {
9✔
296
            auto der = cert.BER_encode();
×
297
            output().write(reinterpret_cast<const char*>(der.data()), der.size());
×
298
         } else {
×
299
            output() << cert.PEM_encode();
27✔
300
         }
301
      }
27✔
302
};
303

304
BOTAN_REGISTER_COMMAND("gen_self_signed", Gen_Self_Signed);
10✔
305

306
class Generate_PKCS10 final : public Command {
307
   public:
308
      Generate_PKCS10() :
14✔
309
            Command(
310
               "gen_pkcs10 key CN --country= --organization= "
311
               "--ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash= --emsa=") {}
28✔
312

313
      std::string group() const override { return "x509"; }
1✔
314

315
      std::string description() const override { return "Generate a PKCS #10 certificate signing request (CSR)"; }
1✔
316

317
      void go() override {
13✔
318
         const std::string key_file = get_arg("key");
13✔
319
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "key-pass");
26✔
320

321
         auto key = load_private_key(key_file, passphrase);
13✔
322

323
         Botan::X509_Cert_Options opts;
13✔
324

325
         opts.common_name = get_arg("CN");
13✔
326
         opts.country = get_arg("country");
13✔
327
         opts.organization = get_arg("organization");
13✔
328
         opts.email = get_arg("email");
13✔
329
         opts.more_dns = Command::split_on(get_arg("dns"), ',');
13✔
330

331
         if(flag_set("ca")) {
13✔
332
            opts.CA_key(get_arg_sz("path-limit"));
10✔
333
         }
334

335
         for(const std::string& ext_ku : Command::split_on(get_arg("ext-ku"), ',')) {
13✔
336
            opts.add_ex_constraint(ext_ku);
×
337
         }
13✔
338

339
         std::string emsa = get_arg("emsa");
13✔
340

341
         if(emsa.empty() == false) {
13✔
342
            opts.set_padding_scheme(emsa);
×
343
         }
344

345
         Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, get_arg("hash"), rng());
26✔
346
         update_stateful_private_key(*key, rng(), key_file, passphrase);
13✔
347

348
         output() << req.PEM_encode();
26✔
349
      }
39✔
350
};
351

352
BOTAN_REGISTER_COMMAND("gen_pkcs10", Generate_PKCS10);
14✔
353

354
}  // namespace Botan_CLI
355

356
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc