File: x509certinfo.hpp

package info (click to toggle)
openvpn3-client 25%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,276 kB
  • sloc: cpp: 190,085; python: 7,218; ansic: 1,866; sh: 1,361; java: 402; lisp: 81; makefile: 17
file content (253 lines) | stat: -rw-r--r-- 7,825 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
//    OpenVPN -- An application to securely tunnel IP networks
//               over a single port, with support for SSL/TLS-based
//               session authentication and key exchange,
//               packet encryption, packet authentication, and
//               packet compression.
//
//    Copyright (C) 2012- OpenVPN Inc.
//
//    SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//
//
//
//  Generic functions for extracting X.509 Certificate info from
//  OpenSSL X509 objects

#pragma once

#include <cstring>
#include <string>
#include <vector>

#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/x509v3.h>
#include <openssl/x509.h>

#include "openvpn/common/hexstr.hpp"
#include "openvpn/common/uniqueptr.hpp"

namespace openvpn::OpenSSLPKI {

/**
 *  Retrieve the complete X.509 Certificate Subject field
 *
 *  OpenSSL supports two ways of representing the subject line.  The old
 *  format is deprecated, but there might be code expecting this old format.
 *  The old format looks like this:
 *
 *      /C=KG/ST=NA/O=OpenVPN-TEST/CN=Test-Server/emailAddress=me@myhost.mydomain
 *
 *  The new format is UTF-8 compliant and has a different formatting scheme:
 *
 *      C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Server,
 *emailAddress=me@myhost.mydomain
 *
 *
 * @param cert         Pointer to a native OpenSSL X509 object containing the
 *                     certificate
 * @param new_format   (optional, default: false) Which format to use,
 *                     true indicates the new format
 *
 * @return Returns a std::string containing the complete certificate subject.
 *         If it was not possible to retrieve the subject, and empty string
 *         is returned.
 */
static inline std::string x509_get_subject(::X509 *cert, bool new_format = false)
{
    if (!new_format)
    {
        unique_ptr_del<char> subject(
            X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0),
            [](char *p)
            { OPENSSL_free(p); });
        if (subject)
            return std::string(subject.get());
        else
            return std::string("");
    }

    unique_ptr_del<BIO> subject_bio(BIO_new(BIO_s_mem()),
                                    [](BIO *p)
                                    { BIO_free(p); });
    if (subject_bio == nullptr)
    {
        return std::string("");
    }

    X509_NAME_print_ex(subject_bio.get(),
                       X509_get_subject_name(cert),
                       0,
                       XN_FLAG_SEP_CPLUS_SPC
                           | XN_FLAG_FN_SN
                           | ASN1_STRFLGS_UTF8_CONVERT
                           | ASN1_STRFLGS_ESC_CTRL);
    if (BIO_eof(subject_bio.get()))
    {
        return std::string("");
    }

    BUF_MEM *subject_mem = nullptr;
    BIO_get_mem_ptr(subject_bio.get(), &subject_mem);
    return std::string(subject_mem->data,
                       subject_mem->data + subject_mem->length);
}

static inline std::string X509_get_pem_encoding(::X509 *cert)
{
    char *data;
    BIO *bio = BIO_new(BIO_s_mem());
    /* Even though PEM_write_bio_X509 should not modify the argument the official API does not have a const argument */
    PEM_write_bio_X509(bio, cert);
    size_t len = BIO_get_mem_data(bio, &data);
    std::string certpem{data, len};
    BIO_free(bio);
    return certpem;
}

/**
 * Retrives the algorithm used to sign a X509 certificate
 * @param cert 	OpenSSL certificate
 * @return
 */
static inline std::string x509_get_signature_algorithm(const ::X509 *cert)
{
    int nid = X509_get_signature_nid(cert);
    const char *sig = OBJ_nid2sn(nid);

    if (sig)
    {
        return sig;
    }
    else
        return "(error getting signature algorithm)";
}

/**
 *  Retrieves a specific portion of the X.509 Certificate subject field
 *
 * @param cert     Pointer to a native OpenSSL X509 object containing the
 *                 certificate
 * @param nid      Subject name ID to retrieve.  See openssl/obj_mac.h for
 *                 list of valid NID_* references.
 *
 * @return Returns the contents of the extracted field on success.  The
 *         resulting string may be empty if the extraction failed or the field
 *         is empty.
 */
static inline std::string x509_get_field(::X509 *cert, const int nid)
{
    static const char nullc = '\0';
    std::string ret;
    X509_NAME *x509_name = X509_get_subject_name(cert);
    int i = X509_NAME_get_index_by_NID(x509_name, nid, -1);
    if (i >= 0)
    {
        X509_NAME_ENTRY *ent = X509_NAME_get_entry(x509_name, i);
        if (ent)
        {
            ASN1_STRING *val = X509_NAME_ENTRY_get_data(ent);
            unsigned char *buf;
            buf = (unsigned char *)1; // bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8
                                      // requires this workaround
            const int len = ASN1_STRING_to_UTF8(&buf, val);
            if (len > 0)
            {
                if (std::strlen((char *)buf) == static_cast<unsigned int>(len))
                    ret = (char *)buf;
                OPENSSL_free(buf);
            }
        }
    }
    else
    {
        i = X509_get_ext_by_NID(cert, nid, -1);
        if (i >= 0)
        {
            X509_EXTENSION *ext = X509_get_ext(cert, i);
            if (ext)
            {
                BIO *bio = BIO_new(BIO_s_mem());
                if (bio)
                {
                    if (X509V3_EXT_print(bio, ext, 0, 0))
                    {
                        if (BIO_write(bio, &nullc, 1) == 1)
                        {
                            char *str;
                            const long len = BIO_get_mem_data(bio, &str);
                            if (std::strlen(str) == static_cast<size_t>(len))
                                ret = str;
                        }
                    }
                    BIO_free(bio);
                }
            }
        }
    }
    return ret;
}

/**
 *  Retrieves the X.509 certificate serial number
 *
 * @param cert     Pointer to a native OpenSSL X509 object containing the
 *                 certificate
 *
 * @return Returns the numeric representation of the certificate serial number
 *         as a std::string.
 */
static inline std::string x509_get_serial(::X509 *cert)
{
    const ASN1_INTEGER *asn1_i = X509_get_serialNumber(cert);
    BIGNUM *bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
    char *openssl_serial = BN_bn2dec(bignum);
    BN_free(bignum);

    if (openssl_serial)
    {
        const std::string ret = openssl_serial;
        OPENSSL_free(openssl_serial);
        return ret;
    }
    return std::string();
}

/**
 *  Retrieves the X.509 certificate serial number as hexadecimal
 *
 * @param cert     Pointer to a native OpenSSL X509 object containing the
 *                 certificate
 *
 * @return Returns the hexadecimal representation of the certificate
 *         serial number as a std::string.
 */
static inline std::string x509_get_serial_hex(::X509 *cert)
{
    const ASN1_INTEGER *asn1_i = X509_get_serialNumber(cert);
    return render_hex_sep(asn1_i->data, asn1_i->length, ':', false);
}

/**
 *  Retrieves the X.509 certificate SHA256 fingerprint as binary
 *
 * @return Returns a uint8_t std:vector containing the binary representation
 *         of the certificate's SHA256 fingerprint.
 */
static inline std::size_t x509_fingerprint_size()
{
    return EVP_MD_size(EVP_sha256());
}

static inline std::vector<uint8_t> x509_get_fingerprint(const ::X509 *cert)
{
    std::vector<uint8_t> fingerprint;
    fingerprint.resize(x509_fingerprint_size());

    if (::X509_digest(cert, EVP_sha256(), fingerprint.data(), NULL) != 1)
        throw OpenSSLException("OpenSSL error while calling X509_digest()");

    return fingerprint;
}

} // namespace openvpn::OpenSSLPKI