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

randombit / botan / 23225340130

18 Mar 2026 01:53AM UTC coverage: 89.677% (-0.001%) from 89.678%
23225340130

push

github

web-flow
Merge pull request #5456 from randombit/jack/clang-tidy-22

Fix various warnings from clang-tidy 22

104438 of 116460 relevant lines covered (89.68%)

11819947.55 hits per line

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

79.38
/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) {
32✔
36
   Botan::DataSource_Stream key_stream(key_file);
32✔
37
   auto key = Botan::PKCS8::load_key(key_stream, passphrase);
32✔
38

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

43
   return key;
32✔
44
}
32✔
45

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

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

62
   #if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
63

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

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

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

72
      void go() override {
2✔
73
         const Botan::System_Certificate_Store trust_roots;
2✔
74

75
         const auto dn_list = trust_roots.all_subjects();
2✔
76

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

89
                  if(flag_set("display")) {
146✔
90
                     output() << cert.to_string() << "\n";
×
91
                  }
92

93
                  output() << cert.PEM_encode() << "\n";
438✔
94
               }
146✔
95
            }
96
         }
97
      }
2✔
98
};
99

100
BOTAN_REGISTER_COMMAND("trust_roots", Trust_Root_Info);
3✔
101

102
   #endif
103

104
class Sign_Cert final : public Command {
105
   public:
106
      Sign_Cert() :
13✔
107
            Command("sign_cert --ca-key-pass= --hash= --padding= --emsa= --duration=365 ca_cert ca_key pkcs10_req") {}
26✔
108

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

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

113
      void go() override {
12✔
114
         const Botan::X509_Certificate ca_cert(get_arg("ca_cert"));
24✔
115

116
         const std::string key_file = get_arg("ca_key");
12✔
117
         const std::string pass = get_passphrase_arg("Password for " + key_file, "ca-key-pass");
24✔
118

119
         // TODO(Botan4) remove --emsa option and this logic
120
         const std::string padding = [&]() {
×
121
            auto p = get_arg("padding");
12✔
122
            auto e = get_arg("emsa");
12✔
123
            if(e.empty() || p == e) {
12✔
124
               return p;
12✔
125
            } else if(p.empty()) {
×
126
               return e;
×
127
            } else {
128
               throw CLI_Usage_Error("Use either --padding or --emsa not both");
×
129
            }
130
         }();
24✔
131

132
         const std::string hash = get_arg("hash");
12✔
133

134
         auto key = load_private_key(key_file, pass);
12✔
135

136
         const Botan::X509_CA ca(ca_cert, *key, hash, padding, rng());
12✔
137

138
         const Botan::PKCS10_Request req(get_arg("pkcs10_req"));
24✔
139

140
         auto now = std::chrono::system_clock::now();
12✔
141

142
         const Botan::X509_Time start_time(now);
12✔
143

144
         typedef std::chrono::duration<int, std::ratio<86400>> days;
12✔
145

146
         const Botan::X509_Time end_time(now + days(get_arg_sz("duration")));
12✔
147

148
         const Botan::X509_Certificate new_cert = ca.sign_request(req, rng(), start_time, end_time);
12✔
149
         update_stateful_private_key(*key, rng(), key_file, pass);
12✔
150

151
         output() << new_cert.PEM_encode();
24✔
152
      }
24✔
153
};
154

155
BOTAN_REGISTER_COMMAND("sign_cert", Sign_Cert);
13✔
156

157
class Cert_Info final : public Command {
158
   public:
159
      Cert_Info() : Command("cert_info --fingerprint file") {}
4✔
160

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

163
      std::string description() const override { return "Parse X.509 certificate and display data fields"; }
2✔
164

165
      void go() override {
1✔
166
         const std::vector<uint8_t> data = slurp_file(get_arg("file"));
2✔
167

168
         Botan::DataSource_Memory in(data);
1✔
169

170
         while(!in.end_of_data()) {
3✔
171
            try {
2✔
172
               const Botan::X509_Certificate cert(in);
2✔
173

174
               try {
1✔
175
                  output() << cert.to_string() << "\n";
3✔
176
               } catch(Botan::Exception& e) {
×
177
                  // to_string failed - report the exception and continue
178
                  output() << "X509_Certificate::to_string failed: " << e.what() << "\n";
×
179
               }
×
180

181
               if(flag_set("fingerprint")) {
1✔
182
                  output() << "Fingerprint: " << cert.fingerprint("SHA-256") << "\n";
3✔
183
               }
184
            } catch(Botan::Exception& e) {
2✔
185
               if(!in.end_of_data()) {
1✔
186
                  output() << "X509_Certificate parsing failed " << e.what() << "\n";
×
187
               }
188
            }
1✔
189
         }
190
      }
2✔
191
};
192

193
BOTAN_REGISTER_COMMAND("cert_info", Cert_Info);
2✔
194

195
   #if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_HTTP_UTIL)
196

197
class OCSP_Check final : public Command {
198
   public:
199
      OCSP_Check() : Command("ocsp_check --timeout=3000 subject issuer") {}
2✔
200

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

203
      std::string description() const override {
1✔
204
         return "Verify an X.509 certificate against the issuers OCSP responder";
1✔
205
      }
206

207
      void go() override {
×
208
         const Botan::X509_Certificate subject(get_arg("subject"));
×
209
         const Botan::X509_Certificate issuer(get_arg("issuer"));
×
210
         const std::chrono::milliseconds timeout(get_arg_sz("timeout"));
×
211

212
         Botan::Certificate_Store_In_Memory cas;
×
213
         cas.add_certificate(issuer);
×
214
         const Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, timeout);
×
215

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

218
         if(status == Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD) {
×
219
            output() << "OCSP check OK\n";
×
220
         } else {
221
            output() << "OCSP check failed " << Botan::Path_Validation_Result::status_string(status) << "\n";
×
222
         }
223
      }
×
224
};
225

226
BOTAN_REGISTER_COMMAND("ocsp_check", OCSP_Check);
1✔
227

228
   #endif  // OCSP && HTTP
229

230
class Cert_Verify final : public Command {
231
   public:
232
      Cert_Verify() : Command("cert_verify subject *ca_certs") {}
22✔
233

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

236
      std::string description() const override {
1✔
237
         return "Verify if the passed X.509 certificate passes path validation";
1✔
238
      }
239

240
      void go() override {
10✔
241
         const Botan::X509_Certificate subject_cert(get_arg("subject"));
20✔
242
         Botan::Certificate_Store_In_Memory trusted;
10✔
243

244
         for(const auto& certfile : get_arg_list("ca_certs")) {
19✔
245
            trusted.add_certificate(Botan::X509_Certificate(certfile));
9✔
246
         }
10✔
247

248
         const Botan::Path_Validation_Restrictions restrictions;
20✔
249

250
         const Botan::Path_Validation_Result result = Botan::x509_path_validate(subject_cert, restrictions, trusted);
10✔
251

252
         if(result.successful_validation()) {
10✔
253
            output() << "Certificate passes validation checks\n";
5✔
254
         } else {
255
            output() << "Certificate did not validate - " << result.result_string() << "\n";
15✔
256
         }
257
      }
10✔
258
};
259

260
BOTAN_REGISTER_COMMAND("cert_verify", Cert_Verify);
11✔
261

262
class Gen_Self_Signed final : public Command {
263
   public:
264
      Gen_Self_Signed() :
9✔
265
            Command(
266
               "gen_self_signed key CN --country= --dns= "
267
               "--organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash= --padding= --emsa= --der") {
9✔
268
      }
9✔
269

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

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

274
      void go() override {
8✔
275
         const std::string key_file = get_arg("key");
8✔
276
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "key-pass");
16✔
277
         auto key = load_private_key(key_file, passphrase);
8✔
278

279
         const uint32_t lifetime = static_cast<uint32_t>(get_arg_sz("days") * 24 * 60 * 60);
8✔
280

281
         Botan::X509_Cert_Options opts("", lifetime);
8✔
282

283
         opts.common_name = get_arg("CN");
8✔
284
         opts.country = get_arg("country");
8✔
285
         opts.organization = get_arg("organization");
8✔
286
         opts.email = get_arg("email");
8✔
287
         opts.more_dns = Command::split_on(get_arg("dns"), ',');
8✔
288
         const bool der_format = flag_set("der");
8✔
289

290
         // TODO(Botan4) remove --emsa option and this logic
291
         const std::string padding = [&]() {
24✔
292
            auto p = get_arg("padding");
8✔
293
            auto e = get_arg("emsa");
8✔
294
            if(e.empty() || p == e) {
8✔
295
               return p;
8✔
296
            } else if(p.empty()) {
×
297
               return e;
×
298
            } else {
299
               throw CLI_Usage_Error("Use either --padding or --emsa not both");
×
300
            }
301
         }();
16✔
302

303
         if(padding.empty() == false) {
8✔
304
            opts.set_padding_scheme(padding);
×
305
         }
306

307
         if(flag_set("ca")) {
8✔
308
            opts.CA_key(get_arg_sz("path-limit"));
16✔
309
         }
310

311
         const Botan::X509_Certificate cert = Botan::X509::create_self_signed_cert(opts, *key, get_arg("hash"), rng());
16✔
312
         update_stateful_private_key(*key, rng(), key_file, passphrase);
8✔
313

314
         if(der_format) {
8✔
315
            auto der = cert.BER_encode();
×
316
            output().write(reinterpret_cast<const char*>(der.data()), der.size());
×
317
         } else {
×
318
            output() << cert.PEM_encode();
16✔
319
         }
320
      }
16✔
321
};
322

323
BOTAN_REGISTER_COMMAND("gen_self_signed", Gen_Self_Signed);
9✔
324

325
class Generate_PKCS10 final : public Command {
326
   public:
327
      Generate_PKCS10() :
13✔
328
            Command(
329
               "gen_pkcs10 key CN --country= --organization= "
330
               "--ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash= --padding= --emsa=") {}
26✔
331

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

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

336
      void go() override {
12✔
337
         const std::string key_file = get_arg("key");
12✔
338
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "key-pass");
24✔
339

340
         auto key = load_private_key(key_file, passphrase);
12✔
341

342
         Botan::X509_Cert_Options opts;
12✔
343

344
         opts.common_name = get_arg("CN");
12✔
345
         opts.country = get_arg("country");
12✔
346
         opts.organization = get_arg("organization");
12✔
347
         opts.email = get_arg("email");
12✔
348
         opts.more_dns = Command::split_on(get_arg("dns"), ',');
12✔
349

350
         if(flag_set("ca")) {
12✔
351
            opts.CA_key(get_arg_sz("path-limit"));
8✔
352
         }
353

354
         for(const std::string& ext_ku : Command::split_on(get_arg("ext-ku"), ',')) {
12✔
355
            opts.add_ex_constraint(ext_ku);
×
356
         }
12✔
357

358
         // TODO(Botan4) remove --emsa option and this logic
359
         const std::string padding = [&]() {
36✔
360
            auto p = get_arg("padding");
12✔
361
            auto e = get_arg("emsa");
12✔
362
            if(e.empty() || p == e) {
12✔
363
               return p;
12✔
364
            } else if(p.empty()) {
×
365
               return e;
×
366
            } else {
367
               throw CLI_Usage_Error("Use either --padding or --emsa not both");
×
368
            }
369
         }();
24✔
370

371
         if(padding.empty() == false) {
12✔
372
            opts.set_padding_scheme(padding);
×
373
         }
374

375
         const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, get_arg("hash"), rng());
24✔
376
         update_stateful_private_key(*key, rng(), key_file, passphrase);
12✔
377

378
         output() << req.PEM_encode();
24✔
379
      }
24✔
380
};
381

382
BOTAN_REGISTER_COMMAND("gen_pkcs10", Generate_PKCS10);
13✔
383

384
}  // namespace
385

386
}  // namespace Botan_CLI
387

388
#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

© 2026 Coveralls, Inc