1 /*
2 * \copyright Copyright (c) 2017-2021 Governikus GmbH & Co. KG, Germany
3 */
4
5
6 #include "KeyPair.h"
7
8 #include "Randomizer.h"
9
10 #include <openssl/bio.h>
11 #include <openssl/pem.h>
12 #include <openssl/rsa.h>
13 #include <openssl/x509.h>
14
15 #include <QCoreApplication>
16 #include <QLoggingCategory>
17 #include <QScopedPointer>
18
19 using namespace governikus;
20
21 Q_DECLARE_LOGGING_CATEGORY(settings)
22
23 namespace
24 {
25 struct OpenSslCustomDeleter
26 {
cleanup__anone536d6840111::OpenSslCustomDeleter27 static inline void cleanup(EVP_PKEY* pData)
28 {
29 EVP_PKEY_free(pData);
30 }
31
32
cleanup__anone536d6840111::OpenSslCustomDeleter33 static inline void cleanup(EVP_PKEY_CTX* pData)
34 {
35 EVP_PKEY_CTX_free(pData);
36 }
37
38
cleanup__anone536d6840111::OpenSslCustomDeleter39 static inline void cleanup(BIO* pData)
40 {
41 BIO_free(pData);
42 }
43
44
cleanup__anone536d6840111::OpenSslCustomDeleter45 static inline void cleanup(X509_NAME* pData)
46 {
47 X509_NAME_free(pData);
48 }
49
50
51 };
52 } // namespace
53
KeyPair(const QSslKey & pKey,const QSslCertificate & pCert)54 KeyPair::KeyPair(const QSslKey& pKey, const QSslCertificate& pCert)
55 : mKey(pKey)
56 , mCertificate(pCert)
57 {
58 }
59
60
generate()61 KeyPair KeyPair::generate()
62 {
63 if (!Randomizer::getInstance().isSecureRandom())
64 {
65 qCCritical(settings) << "Cannot get enough entropy";
66 return KeyPair();
67 }
68
69 auto pkey = createKey();
70 if (pkey)
71 {
72 auto cert = createCertificate(pkey);
73 if (cert)
74 {
75 return KeyPair(QSslKey(Qt::HANDLE(pkey), QSsl::PrivateKey), QSslCertificate(rewriteCertificate(cert.data())));
76 }
77 else
78 {
79 OpenSslCustomDeleter::cleanup(pkey);
80 }
81 }
82
83 return KeyPair();
84 }
85
86
getKey() const87 const QSslKey& KeyPair::getKey() const
88 {
89 return mKey;
90 }
91
92
getCertificate() const93 const QSslCertificate& KeyPair::getCertificate() const
94 {
95 return mCertificate;
96 }
97
98
createKey()99 EVP_PKEY* KeyPair::createKey()
100 {
101 QScopedPointer<EVP_PKEY_CTX, OpenSslCustomDeleter> pkeyCtx(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr));
102
103 if (pkeyCtx.isNull())
104 {
105 qCCritical(settings) << "Cannot create EVP_PKEY_CTX";
106 return nullptr;
107 }
108
109 if (!EVP_PKEY_keygen_init(pkeyCtx.data()))
110 {
111 qCCritical(settings) << "Cannot init rsa key ctx";
112 return nullptr;
113 }
114
115 if (!EVP_PKEY_CTX_set_rsa_keygen_bits(pkeyCtx.data(), 2048))
116 {
117 qCCritical(settings) << "Cannot generate rsa key bits";
118 return nullptr;
119 }
120
121 EVP_PKEY* pkey = nullptr;
122 if (!EVP_PKEY_keygen(pkeyCtx.data(), &pkey))
123 {
124 qCCritical(settings) << "Cannot generate rsa key";
125 return nullptr;
126 }
127
128 return pkey;
129 }
130
131
createCertificate(EVP_PKEY * pPkey)132 QSharedPointer<X509> KeyPair::createCertificate(EVP_PKEY* pPkey)
133 {
134 QSharedPointer<X509> x509(X509_new(), &X509_free);
135 if (x509.isNull())
136 {
137 qCCritical(settings) << "Cannot create X509 structure";
138 return nullptr;
139 }
140
141 auto& randomizer = Randomizer::getInstance().getGenerator();
142 std::uniform_int_distribution<long> uni_long(1);
143 std::uniform_int_distribution<qulonglong> uni_qulonglong(1);
144
145 #if OPENSSL_VERSION_NUMBER < 0x10100000L
146 #define X509_getm_notBefore X509_get_notBefore
147 #define X509_getm_notAfter X509_get_notAfter
148 #endif
149
150 ASN1_INTEGER_set(X509_get_serialNumber(x509.data()), uni_long(randomizer));
151 // see: https://tools.ietf.org/html/rfc5280#section-4.1.2.5
152 ASN1_TIME_set_string(X509_getm_notBefore(x509.data()), "19700101000000Z");
153 ASN1_TIME_set_string(X509_getm_notAfter(x509.data()), "99991231235959Z");
154 X509_set_pubkey(x509.data(), pPkey);
155
156 auto randomSerial = QByteArray::number(uni_qulonglong(randomizer));
157 QScopedPointer<X509_NAME, OpenSslCustomDeleter> name(X509_NAME_dup(X509_get_subject_name(x509.data())));
158 X509_NAME_add_entry_by_txt(name.data(), "CN", MBSTRING_ASC,
159 reinterpret_cast<const uchar*>(QCoreApplication::applicationName().toLatin1().constData()), -1, -1, 0);
160 X509_NAME_add_entry_by_txt(name.data(), "serialNumber", MBSTRING_ASC,
161 reinterpret_cast<const uchar*>(randomSerial.constData()), -1, -1, 0);
162 X509_set_subject_name(x509.data(), name.data());
163 X509_set_issuer_name(x509.data(), name.data());
164
165 if (!X509_sign(x509.data(), pPkey, EVP_sha256()))
166 {
167 qCCritical(settings) << "Cannot sign certificate";
168 return nullptr;
169 }
170
171 return x509;
172 }
173
174
rewriteCertificate(X509 * pX509)175 QByteArray KeyPair::rewriteCertificate(X509* pX509)
176 {
177 QScopedPointer<BIO, OpenSslCustomDeleter> bio(BIO_new(BIO_s_mem()));
178 PEM_write_bio_X509(bio.data(), pX509);
179 char* data = nullptr;
180
181 // See macro BIO_get_mem_data(bio, &data); if BIO_ctrl fails.
182 // We do not use this to avoid -Wold-style-cast warning
183 const long len = BIO_ctrl(bio.data(), BIO_CTRL_INFO, 0, &data);
184 return QByteArray(data, static_cast<int>(len));
185 }
186
187
isValid() const188 bool KeyPair::isValid() const
189 {
190 return !mKey.isNull() && !mCertificate.isNull();
191 }
192