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

randombit / botan / 5123321399

30 May 2023 04:06PM UTC coverage: 92.213% (+0.004%) from 92.209%
5123321399

Pull #3558

github

web-flow
Merge dd72f7389 into 057bcbc35
Pull Request #3558: Add braces around all if/else statements

75602 of 81986 relevant lines covered (92.21%)

11859779.3 hits per line

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

82.72
/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
namespace Botan_CLI {
30

31
   #if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
32

33
class Trust_Root_Info final : public Command {
34
   public:
35
      Trust_Root_Info() : Command("trust_roots --dn --dn-only --display") {}
6✔
36

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

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

41
      void go() override {
2✔
42
         Botan::System_Certificate_Store trust_roots;
2✔
43

44
         const auto dn_list = trust_roots.all_subjects();
2✔
45

46
         if(flag_set("dn-only")) {
2✔
47
            for(const auto& dn : dn_list) {
125✔
48
               output() << dn << "\n";
124✔
49
            }
50
         } else {
51
            for(const auto& dn : dn_list) {
125✔
52
               // Some certstores have more than one cert with a particular DN
53
               for(const auto& cert : trust_roots.find_all_certs(dn, std::vector<uint8_t>())) {
248✔
54
                  if(flag_set("dn")) {
124✔
55
                     output() << "# " << dn << "\n";
×
56
                  }
57

58
                  if(flag_set("display")) {
124✔
59
                     output() << cert.to_string() << "\n";
×
60
                  }
61

62
                  output() << cert.PEM_encode() << "\n";
372✔
63
               }
124✔
64
            }
65
         }
66
      }
2✔
67
};
68

69
BOTAN_REGISTER_COMMAND("trust_roots", Trust_Root_Info);
3✔
70

71
   #endif
72

73
class Sign_Cert final : public Command {
74
   public:
75
      Sign_Cert() :
13✔
76
            Command(
77
               "sign_cert --ca-key-pass= --hash= "
78
               "--duration=365 --emsa= ca_cert ca_key pkcs10_req") {}
26✔
79

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

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

84
      void go() override {
12✔
85
         Botan::X509_Certificate ca_cert(get_arg("ca_cert"));
24✔
86

87
         const std::string key_file = get_arg("ca_key");
12✔
88
         const std::string pass = get_passphrase_arg("Password for " + key_file, "ca-key-pass");
24✔
89
         const std::string emsa = get_arg("emsa");
12✔
90
         const std::string hash = get_arg("hash");
12✔
91

92
         std::unique_ptr<Botan::Private_Key> key;
12✔
93
         Botan::DataSource_Stream key_stream(key_file);
12✔
94
         if(!pass.empty()) {
12✔
95
            key = Botan::PKCS8::load_key(key_stream, pass);
×
96
         } else {
97
            key = Botan::PKCS8::load_key(key_stream);
12✔
98
         }
99

100
         if(!key) {
12✔
101
            throw CLI_Error("Failed to load key from " + key_file);
×
102
         }
103

104
         Botan::X509_CA ca(ca_cert, *key, hash, emsa, rng());
12✔
105

106
         Botan::PKCS10_Request req(get_arg("pkcs10_req"));
24✔
107

108
         auto now = std::chrono::system_clock::now();
12✔
109

110
         Botan::X509_Time start_time(now);
12✔
111

112
         typedef std::chrono::duration<int, std::ratio<86400>> days;
12✔
113

114
         Botan::X509_Time end_time(now + days(get_arg_sz("duration")));
12✔
115

116
         Botan::X509_Certificate new_cert = ca.sign_request(req, rng(), start_time, end_time);
12✔
117

118
         output() << new_cert.PEM_encode();
24✔
119
      }
24✔
120
};
121

122
BOTAN_REGISTER_COMMAND("sign_cert", Sign_Cert);
13✔
123

124
class Cert_Info final : public Command {
125
   public:
126
      Cert_Info() : Command("cert_info --fingerprint file") {}
4✔
127

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

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

132
      void go() override {
1✔
133
         const std::string arg_file = get_arg("file");
1✔
134

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

137
         Botan::DataSource_Memory in(data);
1✔
138

139
         while(!in.end_of_data()) {
3✔
140
            try {
2✔
141
               Botan::X509_Certificate cert(in);
2✔
142

143
               try {
1✔
144
                  output() << cert.to_string() << std::endl;
3✔
145
               } catch(Botan::Exception& e) {
×
146
                  // to_string failed - report the exception and continue
147
                  output() << "X509_Certificate::to_string failed: " << e.what() << "\n";
×
148
               }
×
149

150
               if(flag_set("fingerprint")) {
1✔
151
                  output() << "Fingerprint: " << cert.fingerprint("SHA-256") << std::endl;
3✔
152
               }
153
            } catch(Botan::Exception& e) {
2✔
154
               if(!in.end_of_data()) {
1✔
155
                  output() << "X509_Certificate parsing failed " << e.what() << "\n";
×
156
               }
157
            }
1✔
158
         }
159
      }
3✔
160
};
161

162
BOTAN_REGISTER_COMMAND("cert_info", Cert_Info);
2✔
163

164
   #if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_HTTP_UTIL)
165

166
class OCSP_Check final : public Command {
167
   public:
168
      OCSP_Check() : Command("ocsp_check --timeout=3000 subject issuer") {}
2✔
169

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

172
      std::string description() const override {
1✔
173
         return "Verify an X.509 certificate against the issuers OCSP responder";
1✔
174
      }
175

176
      void go() override {
×
177
         Botan::X509_Certificate subject(get_arg("subject"));
×
178
         Botan::X509_Certificate issuer(get_arg("issuer"));
×
179
         std::chrono::milliseconds timeout(get_arg_sz("timeout"));
×
180

181
         Botan::Certificate_Store_In_Memory cas;
×
182
         cas.add_certificate(issuer);
×
183
         Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, timeout);
×
184

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

187
         if(status == Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD) {
×
188
            output() << "OCSP check OK\n";
×
189
         } else {
190
            output() << "OCSP check failed " << Botan::Path_Validation_Result::status_string(status) << "\n";
×
191
         }
192
      }
×
193
};
194

195
BOTAN_REGISTER_COMMAND("ocsp_check", OCSP_Check);
1✔
196

197
   #endif  // OCSP && HTTP
198

199
class Cert_Verify final : public Command {
200
   public:
201
      Cert_Verify() : Command("cert_verify subject *ca_certs") {}
18✔
202

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

205
      std::string description() const override {
1✔
206
         return "Verify if the passed X.509 certificate passes path validation";
1✔
207
      }
208

209
      void go() override {
8✔
210
         Botan::X509_Certificate subject_cert(get_arg("subject"));
16✔
211
         Botan::Certificate_Store_In_Memory trusted;
8✔
212

213
         for(const auto& certfile : get_arg_list("ca_certs")) {
17✔
214
            trusted.add_certificate(Botan::X509_Certificate(certfile));
9✔
215
         }
8✔
216

217
         Botan::Path_Validation_Restrictions restrictions;
16✔
218

219
         Botan::Path_Validation_Result result = Botan::x509_path_validate(subject_cert, restrictions, trusted);
8✔
220

221
         if(result.successful_validation()) {
8✔
222
            output() << "Certificate passes validation checks\n";
5✔
223
         } else {
224
            output() << "Certificate did not validate - " << result.result_string() << "\n";
9✔
225
         }
226
      }
8✔
227
};
228

229
BOTAN_REGISTER_COMMAND("cert_verify", Cert_Verify);
9✔
230

231
class Gen_Self_Signed final : public Command {
232
   public:
233
      Gen_Self_Signed() :
9✔
234
            Command(
235
               "gen_self_signed key CN --country= --dns= "
236
               "--organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash= --emsa= --der") {}
18✔
237

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

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

242
      void go() override {
8✔
243
         const std::string key_file = get_arg("key");
8✔
244
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "key-pass");
16✔
245
         Botan::DataSource_Stream key_stream(key_file);
8✔
246
         std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(key_stream, passphrase);
8✔
247

248
         if(!key) {
8✔
249
            throw CLI_Error("Failed to load key from " + get_arg("key"));
×
250
         }
251

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

254
         Botan::X509_Cert_Options opts("", lifetime);
8✔
255

256
         opts.common_name = get_arg("CN");
8✔
257
         opts.country = get_arg("country");
8✔
258
         opts.organization = get_arg("organization");
8✔
259
         opts.email = get_arg("email");
8✔
260
         opts.more_dns = Command::split_on(get_arg("dns"), ',');
8✔
261
         const bool der_format = flag_set("der");
8✔
262

263
         std::string emsa = get_arg("emsa");
8✔
264

265
         if(emsa.empty() == false) {
8✔
266
            opts.set_padding_scheme(emsa);
×
267
         }
268

269
         if(flag_set("ca")) {
8✔
270
            opts.CA_key(get_arg_sz("path-limit"));
16✔
271
         }
272

273
         Botan::X509_Certificate cert = Botan::X509::create_self_signed_cert(opts, *key, get_arg("hash"), rng());
16✔
274

275
         if(der_format) {
8✔
276
            auto der = cert.BER_encode();
×
277
            output().write(reinterpret_cast<const char*>(der.data()), der.size());
×
278
         } else {
×
279
            output() << cert.PEM_encode();
24✔
280
         }
281
      }
24✔
282
};
283

284
BOTAN_REGISTER_COMMAND("gen_self_signed", Gen_Self_Signed);
9✔
285

286
class Generate_PKCS10 final : public Command {
287
   public:
288
      Generate_PKCS10() :
13✔
289
            Command(
290
               "gen_pkcs10 key CN --country= --organization= "
291
               "--ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash= --emsa=") {}
26✔
292

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

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

297
      void go() override {
12✔
298
         Botan::DataSource_Stream key_stream(get_arg("key"));
24✔
299
         std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(key_stream, get_arg("key-pass"));
24✔
300

301
         if(!key) {
12✔
302
            throw CLI_Error("Failed to load key from " + get_arg("key"));
×
303
         }
304

305
         Botan::X509_Cert_Options opts;
12✔
306

307
         opts.common_name = get_arg("CN");
12✔
308
         opts.country = get_arg("country");
12✔
309
         opts.organization = get_arg("organization");
12✔
310
         opts.email = get_arg("email");
12✔
311
         opts.more_dns = Command::split_on(get_arg("dns"), ',');
12✔
312

313
         if(flag_set("ca")) {
12✔
314
            opts.CA_key(get_arg_sz("path-limit"));
8✔
315
         }
316

317
         for(const std::string& ext_ku : Command::split_on(get_arg("ext-ku"), ',')) {
12✔
318
            opts.add_ex_constraint(ext_ku);
×
319
         }
12✔
320

321
         std::string emsa = get_arg("emsa");
12✔
322

323
         if(emsa.empty() == false) {
12✔
324
            opts.set_padding_scheme(emsa);
×
325
         }
326

327
         Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, get_arg("hash"), rng());
24✔
328

329
         output() << req.PEM_encode();
24✔
330
      }
24✔
331
};
332

333
BOTAN_REGISTER_COMMAND("gen_pkcs10", Generate_PKCS10);
13✔
334

335
}  // namespace Botan_CLI
336

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