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