OpenVPN 3 Core Library
Loading...
Searching...
No Matches
x509certinfo.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11//
12//
13// Generic functions for extracting X.509 Certificate info from
14// OpenSSL X509 objects
15
16#pragma once
17
18#include <cstring>
19#include <string>
20#include <vector>
21
22#include <openssl/ssl.h>
23#include <openssl/bio.h>
24#include <openssl/x509v3.h>
25#include <openssl/x509.h>
26
29
30namespace openvpn::OpenSSLPKI {
31
32using BIGNUM_ptr = std::unique_ptr<::BIGNUM, decltype(&::BN_free)>;
33
58static inline std::string x509_get_subject(::X509 *cert, bool new_format = false)
59{
60 if (!new_format)
61 {
63 X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0),
64 [](char *p)
65 { OPENSSL_free(p); });
66 if (subject)
67 return std::string(subject.get());
68 return std::string("");
69 }
70
71 unique_ptr_del<BIO> subject_bio(BIO_new(BIO_s_mem()),
72 [](BIO *p)
73 { BIO_free(p); });
74 if (subject_bio == nullptr)
75 {
76 return std::string("");
77 }
78
79 X509_NAME_print_ex(subject_bio.get(),
80 X509_get_subject_name(cert),
81 0,
82 XN_FLAG_SEP_CPLUS_SPC
83 | XN_FLAG_FN_SN
84 | ASN1_STRFLGS_UTF8_CONVERT
85 | ASN1_STRFLGS_ESC_CTRL);
86 if (BIO_eof(subject_bio.get()))
87 {
88 return std::string("");
89 }
90
91 BUF_MEM *subject_mem = nullptr;
92 BIO_get_mem_ptr(subject_bio.get(), &subject_mem);
93 return std::string(subject_mem->data,
94 subject_mem->data + subject_mem->length);
95}
96
97static inline std::string X509_get_pem_encoding(::X509 *cert)
98{
99 char *data;
100 BIO *bio = BIO_new(BIO_s_mem());
101 /* Even though PEM_write_bio_X509 should not modify the argument the official API does not have a const argument */
102 PEM_write_bio_X509(bio, cert);
103 size_t len = BIO_get_mem_data(bio, &data);
104 std::string certpem{data, len};
105 BIO_free(bio);
106 return certpem;
107}
108
114static inline std::string x509_get_signature_algorithm(const ::X509 *cert)
115{
116 int nid = X509_get_signature_nid(cert);
117 const char *sig = OBJ_nid2sn(nid);
118
119 if (sig)
120 {
121 return sig;
122 }
123 return "(error getting signature algorithm)";
124}
125
138static inline std::string x509_get_field(::X509 *cert, const int nid)
139{
140 static const char nullc = '\0';
141 std::string ret;
142
143 auto *x509_name = X509_get_subject_name(cert);
144 int i = X509_NAME_get_index_by_NID(x509_name, nid, -1);
145 if (i >= 0)
146 {
147 const X509_NAME_ENTRY *ent = X509_NAME_get_entry(x509_name, i);
148 if (ent)
149 {
150 const ASN1_STRING *val = X509_NAME_ENTRY_get_data(ent);
151 unsigned char *buf;
152 buf = (unsigned char *)1; // bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8
153 // requires this workaround
154 const int len = ASN1_STRING_to_UTF8(&buf, val);
155 if (len > 0)
156 {
157 if (std::strlen((char *)buf) == static_cast<unsigned int>(len))
158 ret = (char *)buf;
159 OPENSSL_free(buf);
160 }
161 }
162 }
163 else
164 {
165 i = X509_get_ext_by_NID(cert, nid, -1);
166 if (i >= 0)
167 {
168 /* auto is used here to get const with newer OpenSSL 4.0 and without const
169 * otherwise to match the call to X509V3_EXT_print */
170 auto *ext = X509_get_ext(cert, i);
171 if (ext)
172 {
173 BIO *bio = BIO_new(BIO_s_mem());
174 if (bio)
175 {
176 if (X509V3_EXT_print(bio, ext, 0, 0))
177 {
178 if (BIO_write(bio, &nullc, 1) == 1)
179 {
180 char *str;
181 const long len = BIO_get_mem_data(bio, &str);
182 if (std::strlen(str) == static_cast<size_t>(len))
183 ret = str;
184 }
185 }
186 BIO_free(bio);
187 }
188 }
189 }
190 }
191 return ret;
192}
193
205static inline std::string x509_get_serial(::X509 *cert)
206{
207 const ASN1_INTEGER *asn1_i = X509_get_serialNumber(cert);
208 const OpenSSLPKI::BIGNUM_ptr bignum(ASN1_INTEGER_to_BN(asn1_i, nullptr), BN_free);
209 if (!bignum)
210 {
211 return {};
212 }
213
214 char *openssl_serial = BN_bn2dec(bignum.get());
215
216 if (!openssl_serial)
217 {
218 return {};
219 }
220
221 const std::string ret = openssl_serial;
222 OPENSSL_free(openssl_serial);
223 return ret;
224}
225
237static inline std::string x509_get_serial_hex(::X509 *cert)
238{
239 const ASN1_INTEGER *asn1_i = X509_get0_serialNumber(cert);
240 const OpenSSLPKI::BIGNUM_ptr serial(ASN1_INTEGER_to_BN(asn1_i, nullptr), BN_free);
241
242 if (!serial)
243 return {};
244
245 int numbytesoutput = BN_num_bytes(serial.get());
246
247 std::unique_ptr<unsigned char[]> buf(new unsigned char[numbytesoutput]);
248
249 BN_bn2binpad(serial.get(), buf.get(), numbytesoutput);
250 return render_hex_sep(buf.get(), numbytesoutput, ':', false);
251}
252
259static inline std::size_t x509_fingerprint_size()
260{
261 return EVP_MD_size(EVP_sha256());
262}
263
264static inline std::vector<uint8_t> x509_get_fingerprint(const ::X509 *cert)
265{
266 std::vector<uint8_t> fingerprint;
267 fingerprint.resize(x509_fingerprint_size());
268
269 if (::X509_digest(cert, EVP_sha256(), fingerprint.data(), NULL) != 1)
270 throw OpenSSLException("OpenSSL error while calling X509_digest()");
271
272 return fingerprint;
273}
274
275} // namespace openvpn::OpenSSLPKI
static std::size_t x509_fingerprint_size()
static std::string x509_get_serial_hex(::X509 *cert)
static std::string x509_get_field(::X509 *cert, const int nid)
std::unique_ptr<::BIGNUM, decltype(&::BN_free)> BIGNUM_ptr
static std::string X509_get_pem_encoding(::X509 *cert)
static std::string x509_get_serial(::X509 *cert)
static std::vector< uint8_t > x509_get_fingerprint(const ::X509 *cert)
static std::string x509_get_subject(::X509 *cert, bool new_format=false)
static std::string x509_get_signature_algorithm(const ::X509 *cert)
std::unique_ptr< T, std::function< void(T *)> > unique_ptr_del
Definition uniqueptr.hpp:21
std::string render_hex_sep(const unsigned char *data, size_t size, const char sep, const bool caps=false)
Definition hexstr.hpp:178
void ext(const std::string &path)
Definition test_path.cpp:24