rsa_cipher.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. #include "rsa_cipher.hpp"
  2. #include <mutex>
  3. #include <openssl/pem.h>
  4. #include <openssl/bio.h>
  5. #include "resource_traits/openssl/bio.hpp"
  6. #include "resource_traits/openssl/bignum.hpp"
  7. #if (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
  8. #include <openssl/encoder.h>
  9. #include <openssl/decoder.h>
  10. #include "resource_traits/openssl/encoder_ctx.hpp"
  11. #include "resource_traits/openssl/decoder_ctx.hpp"
  12. #endif
  13. #include "cp_converter.hpp"
  14. #include "exceptions/overflow_exception.hpp"
  15. #pragma comment(lib, "libcrypto")
  16. #pragma comment(lib, "crypt32") // required by libcrypto.lib
  17. #pragma comment(lib, "ws2_32") // required by libcrypto.lib
  18. #define NKG_CURRENT_SOURCE_FILE() u8".\\common\\rsa_cipher.cpp"
  19. #define NKG_CURRENT_SOURCE_LINE() __LINE__
  20. namespace nkg {
  21. #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
  22. RSA* rsa_cipher::_read_private_key_from_bio(BIO* p_bio) {
  23. resource_wrapper new_rsa
  24. { resource_traits::openssl::rsa{}, PEM_read_bio_RSAPrivateKey(p_bio, nullptr, nullptr, nullptr) };
  25. if (new_rsa.is_valid()) {
  26. return new_rsa.transfer();
  27. } else {
  28. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSAPrivateKey failed.")
  29. .push_hint(u8"Are you sure that you DO provide a valid RSA private key file?");
  30. }
  31. }
  32. RSA* rsa_cipher::_read_public_key_pem_from_bio(BIO* p_bio) {
  33. resource_wrapper new_rsa
  34. { resource_traits::openssl::rsa{}, PEM_read_bio_RSA_PUBKEY(p_bio, nullptr, nullptr, nullptr) };
  35. if (new_rsa.is_valid()) {
  36. return new_rsa.transfer();
  37. } else {
  38. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSA_PUBKEY failed.")
  39. .push_hint(u8"Are you sure that you DO provide a valid RSA public key file with PEM format?");
  40. }
  41. }
  42. RSA* rsa_cipher::_read_public_key_pkcs1_from_bio(BIO* p_bio) {
  43. resource_wrapper new_rsa
  44. { resource_traits::openssl::rsa{}, PEM_read_bio_RSAPublicKey(p_bio, nullptr, nullptr, nullptr) };
  45. if (new_rsa.is_valid()) {
  46. return new_rsa.transfer();
  47. } else {
  48. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSAPublicKey failed.")
  49. .push_hint(u8"Are you sure that you DO provide a valid RSA public key file with PKCS1 format?");
  50. }
  51. }
  52. void rsa_cipher::_write_private_key_to_bio(RSA* p_rsa, BIO* p_bio) {
  53. auto r = PEM_write_bio_RSAPrivateKey(p_bio, p_rsa, nullptr, nullptr, 0, nullptr, nullptr);
  54. if (r == 0) {
  55. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPrivateKey failed.");
  56. };
  57. }
  58. void rsa_cipher::_write_public_key_pem_to_bio(RSA* p_rsa, BIO* p_bio) {
  59. auto r = PEM_write_bio_RSA_PUBKEY(p_bio, p_rsa);
  60. if (r == 0) {
  61. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSA_PUBKEY failed.");
  62. }
  63. }
  64. void rsa_cipher::_write_public_key_pkcs1_to_bio(RSA* p_rsa, BIO* p_bio) {
  65. auto r = PEM_write_bio_RSAPublicKey(p_bio, p_rsa);
  66. if (r == 0) {
  67. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPublicKey failed.");
  68. }
  69. }
  70. #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
  71. [[nodiscard]]
  72. EVP_PKEY* rsa_cipher::_read_private_key_from_bio(BIO* p_bio) {
  73. resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} };
  74. resource_wrapper decoder_context
  75. { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "pkcs1", "RSA", OSSL_KEYMGMT_SELECT_PRIVATE_KEY, nullptr, nullptr) };
  76. if (!decoder_context.is_valid()) {
  77. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed.");
  78. }
  79. if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure
  80. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed.");
  81. }
  82. return new_rsa.transfer();
  83. }
  84. [[nodiscard]]
  85. EVP_PKEY* rsa_cipher::_read_public_key_pem_from_bio(BIO* p_bio) {
  86. resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} };
  87. resource_wrapper decoder_context
  88. { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "SubjectPublicKeyInfo", "RSA", OSSL_KEYMGMT_SELECT_PUBLIC_KEY, nullptr, nullptr) };
  89. if (!decoder_context.is_valid()) {
  90. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed.");
  91. }
  92. if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure
  93. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed.");
  94. }
  95. return new_rsa.transfer();
  96. }
  97. [[nodiscard]]
  98. EVP_PKEY* rsa_cipher::_read_public_key_pkcs1_from_bio(BIO* p_bio) {
  99. resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} };
  100. resource_wrapper decoder_context
  101. { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "pkcs1", "RSA", OSSL_KEYMGMT_SELECT_PUBLIC_KEY, nullptr, nullptr) };
  102. if (!decoder_context.is_valid()) {
  103. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed.");
  104. }
  105. if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure
  106. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed.");
  107. }
  108. return new_rsa.transfer();
  109. }
  110. void rsa_cipher::_write_private_key_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) {
  111. resource_wrapper encoder_context
  112. { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, "PEM", "pkcs1", nullptr) };
  113. if (!encoder_context.is_valid()) {
  114. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed.");
  115. }
  116. if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure
  117. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed.");
  118. }
  119. }
  120. void rsa_cipher::_write_public_key_pem_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) {
  121. resource_wrapper encoder_context
  122. { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, "PEM", "SubjectPublicKeyInfo", nullptr) };
  123. if (!encoder_context.is_valid()) {
  124. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed.");
  125. }
  126. if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure
  127. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed.");
  128. }
  129. }
  130. void rsa_cipher::_write_public_key_pkcs1_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) {
  131. resource_wrapper encoder_context
  132. { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, "PEM", "pkcs1", nullptr) };
  133. if (!encoder_context.is_valid()) {
  134. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed.");
  135. }
  136. if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure
  137. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed.");
  138. }
  139. }
  140. #else
  141. #error "rsa_cipher.cpp: Unexpected OpenSSL version."
  142. #endif
  143. rsa_cipher::rsa_cipher() = default;
  144. [[nodiscard]]
  145. size_t rsa_cipher::bits() const {
  146. if (m_rsa.get()) {
  147. #if (OPENSSL_VERSION_NUMBER & 0xfff00000) == 0x10000000 // openssl 1.0.x
  148. return BN_num_bits(m_rsa->n);
  149. #elif (OPENSSL_VERSION_NUMBER & 0xfff00000) == 0x10100000 // openssl 1.1.x
  150. return RSA_bits(m_rsa.get());
  151. #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // openssl 3.x.x
  152. return EVP_PKEY_get_bits(m_rsa.get());
  153. #else
  154. #error "rsa_cipher.cpp: uexpected OpenSSL version"
  155. #endif
  156. } else {
  157. throw no_key_assigned_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA key has not been assigned yet.");
  158. }
  159. }
  160. void rsa_cipher::generate_key(int bits, unsigned int e) {
  161. resource_wrapper bn_e{ resource_traits::openssl::bignum{}, BN_new() };
  162. if (bn_e.is_valid() == false) {
  163. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"BN_new failed.");
  164. }
  165. if (BN_set_word(bn_e.get(), e) == 0) {
  166. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BN_set_word failed.");
  167. }
  168. #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
  169. resource_wrapper new_rsa{ resource_traits::openssl::rsa{}, RSA_new() };
  170. if (!new_rsa.is_valid()) {
  171. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_new failed.");
  172. }
  173. if (RSA_generate_key_ex(new_rsa.get(), bits, bn_e.get(), nullptr) == 0) {
  174. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_generate_key_ex failed.");
  175. }
  176. m_rsa = std::move(new_rsa);
  177. #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
  178. resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) };
  179. if (!evp_pkey_context.is_valid()) {
  180. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new_id failed.");
  181. }
  182. if (EVP_PKEY_keygen_init(evp_pkey_context.get()) <= 0) { // 1 for success, 0 or a negative value for failure
  183. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_keygen_init failed.");
  184. }
  185. if (EVP_PKEY_CTX_set_rsa_keygen_bits(evp_pkey_context.get(), bits) <= 0) { // return a positive value for success and 0 or a negative value for failure
  186. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_keygen_bits failed.");
  187. }
  188. if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(evp_pkey_context.get(), bn_e.get()) <= 0) { // return a positive value for success and 0 or a negative value for failure
  189. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set1_rsa_keygen_pubexp failed.");
  190. }
  191. resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} };
  192. if (EVP_PKEY_keygen(evp_pkey_context.get(), new_rsa.unsafe_addressof()) <= 0) { // 1 for success, 0 or a negative value for failure
  193. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_keygen failed.");
  194. }
  195. m_rsa = std::move(new_rsa);
  196. #else
  197. #error "rsa_cipher.cpp: Unexpected OpenSSL version."
  198. #endif
  199. }
  200. void rsa_cipher::export_private_key_file(std::wstring_view file_path) const {
  201. resource_wrapper bio_file
  202. { resource_traits::openssl::bio{}, BIO_new_file(cp_converter<-1, CP_UTF8>::convert(file_path).c_str(), "w")};
  203. if (bio_file.is_valid()) {
  204. _write_private_key_to_bio(m_rsa.get(), bio_file.get());
  205. } else {
  206. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
  207. }
  208. }
  209. void rsa_cipher::export_private_key_file(const std::filesystem::path& file_path) const {
  210. export_private_key_file(static_cast<std::wstring_view>(file_path.native()));
  211. }
  212. void rsa_cipher::export_public_key_file_pem(std::wstring_view file_path) const {
  213. resource_wrapper bio_file
  214. { resource_traits::openssl::bio{}, BIO_new_file(cp_converter<-1, CP_UTF8>::convert(file_path).c_str(), "w")};
  215. if (bio_file.is_valid()) {
  216. _write_public_key_pem_to_bio(m_rsa.get(), bio_file.get());
  217. } else {
  218. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
  219. }
  220. }
  221. void rsa_cipher::export_public_key_file_pem(const std::filesystem::path& file_path) const {
  222. export_public_key_file_pem(static_cast<std::wstring_view>(file_path.native()));
  223. }
  224. void rsa_cipher::export_public_key_file_pkcs1(std::wstring_view file_path) const {
  225. resource_wrapper bio_file
  226. { resource_traits::openssl::bio{}, BIO_new_file(cp_converter<-1, CP_UTF8>::convert(file_path).c_str(), "w")};
  227. if (bio_file.is_valid()) {
  228. _write_public_key_pkcs1_to_bio(m_rsa.get(), bio_file.get());
  229. } else {
  230. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
  231. }
  232. }
  233. void rsa_cipher::export_public_key_file_pkcs1(const std::filesystem::path& file_path) const {
  234. export_public_key_file_pkcs1(static_cast<std::wstring_view>(file_path.native()));
  235. }
  236. void rsa_cipher::import_private_key_file(std::wstring_view file_path) {
  237. resource_wrapper bio_file
  238. { resource_traits::openssl::bio{}, BIO_new_file(cp_converter<-1, CP_UTF8>::convert(file_path).c_str(), "r") };
  239. if (bio_file.is_valid()) {
  240. m_rsa.set(_read_private_key_from_bio(bio_file.get()));
  241. } else {
  242. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
  243. }
  244. }
  245. void rsa_cipher::import_private_key_file(const std::filesystem::path& file_path) {
  246. import_private_key_file(static_cast<std::wstring_view>(file_path.native()));
  247. }
  248. void rsa_cipher::import_public_key_file_pem(std::wstring_view file_path) {
  249. resource_wrapper bio_file
  250. { resource_traits::openssl::bio{}, BIO_new_file(cp_converter<-1, CP_UTF8>::convert(file_path).c_str(), "r") };
  251. if (bio_file.is_valid()) {
  252. m_rsa.set(_read_public_key_pem_from_bio(bio_file.get()));
  253. } else {
  254. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
  255. }
  256. }
  257. void rsa_cipher::import_public_key_file_pem(const std::filesystem::path& file_path) {
  258. import_public_key_file_pem(static_cast<std::wstring_view>(file_path.native()));
  259. }
  260. void rsa_cipher::import_public_key_file_pkcs1(std::wstring_view file_path) {
  261. resource_wrapper bio_file
  262. { resource_traits::openssl::bio{}, BIO_new_file(cp_converter<-1, CP_UTF8>::convert(file_path).c_str(), "r") };
  263. if (bio_file.is_valid()) {
  264. m_rsa.set(_read_public_key_pkcs1_from_bio(bio_file.get()));
  265. } else {
  266. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
  267. }
  268. }
  269. void rsa_cipher::import_public_key_file_pkcs1(const std::filesystem::path& file_path) {
  270. import_public_key_file_pkcs1(static_cast<std::wstring_view>(file_path.native()));
  271. }
  272. [[nodiscard]]
  273. std::string rsa_cipher::export_private_key_string() const {
  274. resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
  275. if (bio_memory.is_valid()) {
  276. _write_private_key_to_bio(m_rsa.get(), bio_memory.get());
  277. const char* pch = nullptr;
  278. long lch = BIO_get_mem_data(bio_memory.get(), &pch);
  279. return std::string(pch, lch);
  280. } else {
  281. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
  282. }
  283. }
  284. [[nodiscard]]
  285. std::string rsa_cipher::export_public_key_string_pem() const {
  286. resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
  287. if (bio_memory.is_valid()) {
  288. _write_public_key_pem_to_bio(m_rsa.get(), bio_memory.get());
  289. const char* pch = nullptr;
  290. long lch = BIO_get_mem_data(bio_memory.get(), &pch);
  291. return std::string(pch, lch);
  292. } else {
  293. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
  294. }
  295. }
  296. [[nodiscard]]
  297. std::string rsa_cipher::export_public_key_string_pkcs1() const {
  298. resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
  299. if (bio_memory.is_valid()) {
  300. _write_public_key_pkcs1_to_bio(m_rsa.get(), bio_memory.get());
  301. const char* pch = nullptr;
  302. long lch = BIO_get_mem_data(bio_memory.get(), &pch);
  303. return std::string(pch, lch);
  304. } else {
  305. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
  306. }
  307. }
  308. void rsa_cipher::import_private_key_string(std::string_view key_string) {
  309. resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
  310. if (bio_memory.is_valid() == false) {
  311. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
  312. }
  313. if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
  314. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
  315. }
  316. m_rsa.set(_read_private_key_from_bio(bio_memory.get()));
  317. }
  318. void rsa_cipher::import_public_key_string_pem(std::string_view key_string) {
  319. resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
  320. if (bio_memory.is_valid() == false) {
  321. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
  322. }
  323. if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
  324. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
  325. }
  326. m_rsa.set(_read_public_key_pem_from_bio(bio_memory.get()));
  327. }
  328. void rsa_cipher::import_public_key_string_pkcs1(std::string_view key_string) {
  329. resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
  330. if (bio_memory.is_valid() == false) {
  331. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
  332. }
  333. if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
  334. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
  335. }
  336. m_rsa.set(_read_public_key_pkcs1_from_bio(bio_memory.get()));
  337. }
  338. size_t rsa_cipher::public_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const {
  339. #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
  340. if (plaintext_size <= INT_MAX) {
  341. int bytes_written =
  342. RSA_public_encrypt(static_cast<int>(plaintext_size), reinterpret_cast<const unsigned char*>(plaintext), reinterpret_cast<unsigned char*>(ciphertext), m_rsa.get(), padding);
  343. if (bytes_written != -1) {
  344. return bytes_written;
  345. } else {
  346. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
  347. }
  348. } else {
  349. throw exceptions::overflow_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX");
  350. }
  351. #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
  352. resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) };
  353. if (!evp_pkey_context.is_valid()) {
  354. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed.");
  355. }
  356. if (EVP_PKEY_encrypt_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure
  357. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt_init failed.");
  358. }
  359. if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure
  360. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed.");
  361. }
  362. size_t ciphertext_size = 0;
  363. if (EVP_PKEY_encrypt(evp_pkey_context.get(), nullptr, &ciphertext_size, reinterpret_cast<const unsigned char*>(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
  364. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt failed.");
  365. }
  366. if (EVP_PKEY_encrypt(evp_pkey_context.get(), reinterpret_cast<unsigned char*>(ciphertext), &ciphertext_size, reinterpret_cast<const unsigned char*>(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
  367. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt failed.");
  368. }
  369. return ciphertext_size;
  370. #else
  371. #error "rsa_cipher.cpp: Unexpected OpenSSL version."
  372. #endif
  373. }
  374. size_t rsa_cipher::private_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const {
  375. #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
  376. if (plaintext_size <= INT_MAX) {
  377. int bytes_written =
  378. RSA_private_encrypt(static_cast<int>(plaintext_size), reinterpret_cast<const unsigned char*>(plaintext), reinterpret_cast<unsigned char*>(ciphertext), m_rsa.get(), padding);
  379. if (bytes_written != -1) {
  380. return bytes_written;
  381. } else {
  382. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
  383. }
  384. } else {
  385. throw exceptions::overflow_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX");
  386. }
  387. #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
  388. resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) };
  389. if (!evp_pkey_context.is_valid()) {
  390. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed.");
  391. }
  392. if (EVP_PKEY_sign_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure
  393. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign_init failed.");
  394. }
  395. if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure
  396. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed.");
  397. }
  398. size_t ciphertext_size = 0;
  399. if (EVP_PKEY_sign(evp_pkey_context.get(), nullptr, &ciphertext_size, reinterpret_cast<const unsigned char*>(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
  400. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign failed.");
  401. }
  402. if (EVP_PKEY_sign(evp_pkey_context.get(), reinterpret_cast<unsigned char*>(ciphertext), &ciphertext_size, reinterpret_cast<const unsigned char*>(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
  403. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign failed.");
  404. }
  405. return ciphertext_size;
  406. #else
  407. #error "rsa_cipher.cpp: Unexpected OpenSSL version."
  408. #endif
  409. }
  410. size_t rsa_cipher::public_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const {
  411. #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
  412. if (ciphertext_size <= INT_MAX) {
  413. int bytes_written =
  414. RSA_public_decrypt(static_cast<int>(ciphertext_size), reinterpret_cast<const unsigned char*>(ciphertext), reinterpret_cast<unsigned char*>(plaintext), m_rsa.get(), padding);
  415. if (bytes_written != -1) {
  416. return bytes_written;
  417. } else {
  418. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.")
  419. .push_hint(u8"Are your sure you DO provide a correct public key?");
  420. }
  421. } else {
  422. throw exceptions::overflow_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX");
  423. }
  424. #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
  425. resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) };
  426. if (!evp_pkey_context.is_valid()) {
  427. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed.");
  428. }
  429. if (EVP_PKEY_verify_recover_init(evp_pkey_context.get())) { // return 1 for success, 0 or a negative value for failure
  430. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover_init failed.");
  431. }
  432. if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure
  433. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed.");
  434. }
  435. size_t plaintext_size = 0;
  436. if (EVP_PKEY_verify_recover(evp_pkey_context.get(), nullptr, &plaintext_size, reinterpret_cast<const unsigned char*>(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
  437. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover failed.")
  438. .push_hint(u8"Are your sure you DO provide a correct public key?");
  439. }
  440. if (EVP_PKEY_verify_recover(evp_pkey_context.get(), reinterpret_cast<unsigned char*>(plaintext), &plaintext_size, reinterpret_cast<const unsigned char*>(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
  441. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover failed.");
  442. }
  443. return plaintext_size;
  444. #else
  445. #error "rsa_cipher.cpp: Unexpected OpenSSL version."
  446. #endif
  447. }
  448. size_t rsa_cipher::private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const {
  449. #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
  450. if (ciphertext_size <= INT_MAX) {
  451. int bytes_written =
  452. RSA_private_decrypt(static_cast<int>(ciphertext_size), reinterpret_cast<const unsigned char*>(ciphertext), reinterpret_cast<unsigned char*>(plaintext), m_rsa.get(), padding);
  453. if (bytes_written != -1) {
  454. return bytes_written;
  455. } else {
  456. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.")
  457. .push_hint(u8"Are your sure you DO provide a correct private key?");
  458. }
  459. } else {
  460. throw exceptions::overflow_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX");
  461. }
  462. #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
  463. resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) };
  464. if (!evp_pkey_context.is_valid()) {
  465. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed.");
  466. }
  467. if (EVP_PKEY_decrypt_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure
  468. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt_init failed.");
  469. }
  470. if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure
  471. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed.");
  472. }
  473. size_t plaintext_size = 0;
  474. if (EVP_PKEY_decrypt(evp_pkey_context.get(), nullptr, &plaintext_size, reinterpret_cast<const unsigned char*>(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
  475. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt failed.")
  476. .push_hint(u8"Are your sure you DO provide a correct private key?");
  477. }
  478. if (EVP_PKEY_decrypt(evp_pkey_context.get(), reinterpret_cast<unsigned char*>(plaintext), &plaintext_size, reinterpret_cast<const unsigned char*>(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
  479. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt failed.");
  480. }
  481. return plaintext_size;
  482. #else
  483. #error "rsa_cipher.cpp: Unexpected OpenSSL version."
  484. #endif
  485. }
  486. rsa_cipher::backend_error::backend_error(std::string_view file, int line, std::string_view message) noexcept:
  487. ::nkg::exception::exception(file, line, message), m_error_code(0) {}
  488. rsa_cipher::backend_error::backend_error(std::string_view file, int line, error_code_t openssl_errno, std::string_view message) noexcept:
  489. ::nkg::exception::exception(file, line, message), m_error_code(openssl_errno)
  490. {
  491. static std::once_flag onceflag_load_crypto_strings;
  492. std::call_once(onceflag_load_crypto_strings, []() { ERR_load_crypto_strings(); });
  493. m_error_string = ERR_reason_error_string(m_error_code);
  494. }
  495. }
  496. #undef NKG_CURRENT_SOURCE_FILE
  497. #undef NKG_CURRENT_SOURCE_LINE