OpenVPN 3 Core Library
Loading...
Searching...
No Matches
pkey.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// Wrap an OpenSSL EVP_PKEY object
13
14#pragma once
15
16#include <string>
17#include <utility>
18
19#include <openssl/bio.h>
20#include <openssl/opensslv.h>
21#include <openssl/ssl.h>
22
30
31namespace openvpn::OpenSSLPKI {
32
33class PKey
34{
35 public:
37 : pkey_(nullptr)
38 {
39 }
40
41 PKey(const std::string &pkey_txt, const std::string &title, SSLLib::Ctx ctx)
42 : pkey_(nullptr)
43 {
44 parse_pem(pkey_txt, title, ctx);
45 }
46
47 PKey(const PKey &other)
48 : pkey_(dup(other.pkey_)),
50 {
51 }
52
53 PKey(PKey &&other) noexcept
54 : pkey_(other.pkey_),
55 priv_key_pwd(std::move(other.priv_key_pwd))
56 {
57 other.pkey_ = nullptr;
58 }
59
60 PKey &operator=(const PKey &other)
61 {
62 if (this != &other)
63 {
64 erase();
65 pkey_ = dup(other.pkey_);
67 }
68 return *this;
69 }
70
71 PKey &operator=(PKey &&other) noexcept
72 {
73 if (this != &other)
74 {
75 erase();
76 pkey_ = other.pkey_;
77 other.pkey_ = nullptr;
78 priv_key_pwd = std::move(other.priv_key_pwd);
79 }
80 return *this;
81 }
82
83 bool defined() const
84 {
85 return pkey_ != nullptr;
86 }
87 ::EVP_PKEY *obj() const
88 {
89 return pkey_;
90 }
91
93 {
94 switch (::EVP_PKEY_id(pkey_))
95 {
96 case EVP_PKEY_RSA:
97 case EVP_PKEY_RSA2:
98 return PKType::PK_RSA;
99 case EVP_PKEY_EC:
100 return PKType::PK_EC;
101 case EVP_PKEY_DSA:
102 case EVP_PKEY_DSA1:
103 case EVP_PKEY_DSA2:
104 case EVP_PKEY_DSA3:
105 case EVP_PKEY_DSA4:
106 return PKType::PK_DSA;
107 case EVP_PKEY_NONE:
108 return PKType::PK_NONE;
109 default:
110 return PKType::PK_UNKNOWN;
111 }
112 }
113
114 size_t key_length() const
115 {
116 int ret = ::i2d_PrivateKey(pkey_, NULL);
117 if (ret < 0)
118 return 0;
119
120 /* convert to bits */
121 return ret * 8;
122 }
123
124 void set_private_key_password(const std::string &pwd)
125 {
126 priv_key_pwd = pwd;
127 }
128
129 void parse_pem(const std::string &pkey_txt, const std::string &title, SSLLib::Ctx libctx)
130 {
131 BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(pkey_txt.c_str()), numeric_cast<int>(pkey_txt.length()));
132 if (!bio)
133 throw OpenSSLException();
134
135 ::EVP_PKEY *pkey = ::PEM_read_bio_PrivateKey_ex(bio, nullptr, pem_password_callback, this, libctx, nullptr);
136 ::BIO_free(bio);
137 if (!pkey)
138 throw OpenSSLException(std::string("PKey::parse_pem: error in ") + title + std::string(":"));
139
140 erase();
141 pkey_ = pkey;
142 }
143
144 std::string render_pem() const
145 {
146 if (pkey_)
147 {
148 BIO *bio = ::BIO_new(BIO_s_mem());
149 const int ret = ::PEM_write_bio_PrivateKey(bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr);
150 if (ret == 0)
151 {
152 ::BIO_free(bio);
153 throw OpenSSLException("PKey::render_pem");
154 }
155
156 {
157 char *temp;
158 const size_t buf_len = ::BIO_get_mem_data(bio, &temp);
159 std::string ret = std::string(temp, buf_len);
160 ::BIO_free(bio);
161 return ret;
162 }
163 }
164 else
165 return "";
166 }
167
169 {
170 erase();
171 }
172
173 private:
174 static int pem_password_callback(char *buf, int size, int rwflag, void *userdata)
175 {
176 // get this
177 const PKey *self = (PKey *)userdata;
178 if (buf)
179 {
180 string::strncpynt(buf, self->priv_key_pwd.c_str(), size);
181 auto len = std::strlen(buf);
182 if (is_safe_conversion<int>(len))
183 return static_cast<int>(len);
184 }
185 return 0;
186 }
187
188 void erase()
189 {
190 if (pkey_)
191 ::EVP_PKEY_free(pkey_);
192 }
193
194#if OPENSSL_VERSION_NUMBER < 0x30000000L
195 static ::EVP_PKEY *dup(const ::EVP_PKEY *pkey)
196 {
197 // No OpenSSL EVP_PKEY_dup method so we roll our own
198 if (pkey)
199 {
200 ::EVP_PKEY *pDupKey = ::EVP_PKEY_new();
201 ::RSA *pRSA = ::EVP_PKEY_get1_RSA(const_cast<::EVP_PKEY *>(pkey));
202 ::RSA *pRSADupKey = ::RSAPrivateKey_dup(pRSA);
203 ::RSA_free(pRSA);
204 ::EVP_PKEY_set1_RSA(pDupKey, pRSADupKey);
205 ::RSA_free(pRSADupKey);
206 return pDupKey;
207 }
208 else
209 return nullptr;
210 }
211#else
212 static ::EVP_PKEY *dup(const ::EVP_PKEY *pkey)
213 {
214 if (pkey)
215 return EVP_PKEY_dup(const_cast<EVP_PKEY *>(pkey));
216 return nullptr;
217 }
218#endif
219
220 ::EVP_PKEY *pkey_;
221 std::string priv_key_pwd;
222};
223} // namespace openvpn::OpenSSLPKI
PKey(PKey &&other) noexcept
Definition pkey.hpp:53
static int pem_password_callback(char *buf, int size, int rwflag, void *userdata)
Definition pkey.hpp:174
size_t key_length() const
Definition pkey.hpp:114
PKey & operator=(const PKey &other)
Definition pkey.hpp:60
void set_private_key_password(const std::string &pwd)
Definition pkey.hpp:124
PKey & operator=(PKey &&other) noexcept
Definition pkey.hpp:71
::EVP_PKEY * obj() const
Definition pkey.hpp:87
void parse_pem(const std::string &pkey_txt, const std::string &title, SSLLib::Ctx libctx)
Definition pkey.hpp:129
PKey(const PKey &other)
Definition pkey.hpp:47
PKType::Type key_type() const
Definition pkey.hpp:92
bool defined() const
Definition pkey.hpp:83
PKey(const std::string &pkey_txt, const std::string &title, SSLLib::Ctx ctx)
Definition pkey.hpp:41
std::string render_pem() const
Definition pkey.hpp:144
std::string priv_key_pwd
Definition pkey.hpp:221
::EVP_PKEY * dup(const ::EVP_PKEY *pkey)
Definition pkey.hpp:195
static EVP_PKEY * PEM_read_bio_PrivateKey_ex(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u, void *libctx, const char *propq)
Definition compat.hpp:74
void strncpynt(char *dest, const char *src, size_t maxlen)
Definition string.hpp:58
std::string ret