digisign.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jliball.hpp"
  14. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  15. #include <openssl/pem.h>
  16. #include <openssl/err.h>
  17. #include <openssl/evp.h>
  18. #endif
  19. #include "jencrypt.hpp"
  20. #include "digisign.hpp"
  21. #include <mutex>
  22. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  23. #define EVP_CLEANUP(key,ctx) EVP_PKEY_free(key); \
  24. EVP_MD_CTX_destroy(ctx);
  25. #define EVP_THROW(str) { \
  26. char buff[120]; \
  27. ERR_error_string(ERR_get_error(), buff); \
  28. throw MakeStringException(-1, str, buff); \
  29. }
  30. class CDigitalSignatureManager : implements IDigitalSignatureManager, public CInterface
  31. {
  32. private:
  33. StringBuffer publicKeyBuff;
  34. StringBuffer privateKeyBuff;
  35. StringBuffer passphraseBuffEnc;
  36. bool signingConfigured;
  37. bool verifyingConfigured;
  38. bool digiInit(bool isSigning, const char * passphraseEnc, EVP_MD_CTX * * ctx, EVP_PKEY * * PKey)
  39. {
  40. //To avoid threading issues, the keys are created on each call. Otherwise would require
  41. //serialization with a critical section or implementing locking callbacks
  42. const char * keyBuff = isSigning ? privateKeyBuff.str() : publicKeyBuff.str();
  43. //create an RSA object from public key
  44. BIO * keybio = BIO_new_mem_buf((void*) keyBuff, -1);
  45. if (nullptr == keybio)
  46. {
  47. EVP_THROW("digiSign:BIO_new_mem_buf: %s");
  48. }
  49. RSA * rsa;
  50. if (isSigning)
  51. {
  52. StringBuffer ppDec;
  53. if (!isEmptyString(passphraseEnc))
  54. decrypt(ppDec, passphraseEnc);
  55. rsa = PEM_read_bio_RSAPrivateKey(keybio, nullptr, nullptr, (void*)ppDec.str());
  56. }
  57. else
  58. rsa = PEM_read_bio_RSA_PUBKEY(keybio, nullptr, nullptr, nullptr);
  59. BIO_free_all(keybio);
  60. if (nullptr == rsa)
  61. {
  62. if (isSigning)
  63. EVP_THROW("digiSign:PEM_read_bio_RSAPrivateKey: %s")
  64. else
  65. EVP_THROW("digiSign:PEM_read_bio_RSA_PUBKEY: %s")
  66. }
  67. EVP_PKEY* pKey = EVP_PKEY_new();
  68. if (nullptr == pKey)
  69. {
  70. RSA_free(rsa);
  71. EVP_THROW("digiSign:EVP_PKEY_new: %s");
  72. }
  73. EVP_PKEY_assign_RSA(pKey, rsa);//take ownership of the rsa. pKey will free rsa
  74. EVP_MD_CTX * RSACtx = EVP_MD_CTX_create();//allocate, initializes and return a digest context
  75. if (nullptr == RSACtx)
  76. {
  77. EVP_PKEY_free(pKey);
  78. EVP_THROW("digiSign:EVP_MD_CTX_create: %s");
  79. }
  80. //initialize context for SHA-256 hashing function
  81. int rc;
  82. if (isSigning)
  83. rc = EVP_DigestSignInit(RSACtx, nullptr, EVP_sha256(), nullptr, pKey);
  84. else
  85. rc = EVP_DigestVerifyInit(RSACtx, nullptr, EVP_sha256(), nullptr, pKey);
  86. if (rc <= 0)
  87. {
  88. EVP_CLEANUP(pKey, RSACtx);//cleans allocated key and digest context
  89. if (isSigning)
  90. EVP_THROW("digiSign:EVP_DigestSignInit: %s")
  91. else
  92. EVP_THROW("digiSign:EVP_DigestVerifyInit: %s")
  93. }
  94. *ctx = RSACtx;
  95. *PKey = pKey;
  96. return true;
  97. }
  98. public:
  99. IMPLEMENT_IINTERFACE;
  100. CDigitalSignatureManager(const char * _pubKeyBuff, const char * _privKeyBuff, const char * _passPhrase)
  101. : signingConfigured(false), verifyingConfigured(false)
  102. {
  103. publicKeyBuff.set(_pubKeyBuff);
  104. privateKeyBuff.set(_privKeyBuff);
  105. passphraseBuffEnc.set(_passPhrase);//MD5 encrypted passphrase
  106. signingConfigured = !publicKeyBuff.isEmpty();
  107. verifyingConfigured = !privateKeyBuff.isEmpty();
  108. }
  109. bool isDigiSignerConfigured()
  110. {
  111. return signingConfigured;
  112. }
  113. bool isDigiVerifierConfigured()
  114. {
  115. return verifyingConfigured;
  116. }
  117. //Create base 64 encoded digital signature of given text string
  118. bool digiSign(const char * text, StringBuffer & b64Signature)
  119. {
  120. if (!signingConfigured)
  121. throw MakeStringException(-1, "digiSign:Creating Digital Signatures not configured");
  122. EVP_MD_CTX * signingCtx;
  123. EVP_PKEY * signingKey;
  124. digiInit(true, passphraseBuffEnc.str(), &signingCtx, &signingKey);
  125. //add string to the context
  126. if (EVP_DigestSignUpdate(signingCtx, (size_t*)text, strlen(text)) <= 0)
  127. {
  128. EVP_CLEANUP(signingKey, signingCtx);
  129. EVP_THROW("digiSign:EVP_DigestSignUpdate: %s");
  130. }
  131. //compute length of signature
  132. size_t encMsgLen;
  133. if (EVP_DigestSignFinal(signingCtx, nullptr, &encMsgLen) <= 0)
  134. {
  135. EVP_CLEANUP(signingKey, signingCtx);
  136. EVP_THROW("digiSign:EVP_DigestSignFinal1: %s");
  137. }
  138. if (encMsgLen == 0)
  139. {
  140. EVP_CLEANUP(signingKey, signingCtx);
  141. EVP_THROW("digiSign:EVP_DigestSignFinal length returned 0: %s");
  142. }
  143. //compute signature (signed digest)
  144. unsigned char * encMsg = (unsigned char*) malloc(encMsgLen);
  145. if (encMsg == nullptr)
  146. {
  147. EVP_CLEANUP(signingKey, signingCtx);
  148. throw MakeStringException(-1, "digiSign:malloc(%u) returned NULL",(unsigned)encMsgLen);
  149. }
  150. if (EVP_DigestSignFinal(signingCtx, encMsg, &encMsgLen) <= 0)
  151. {
  152. free(encMsg);
  153. EVP_CLEANUP(signingKey, signingCtx);
  154. EVP_THROW("digiSign:EVP_DigestSignFinal2: %s");
  155. }
  156. //convert to base64
  157. JBASE64_Encode(encMsg, encMsgLen, b64Signature, false);
  158. //cleanup
  159. free(encMsg);
  160. EVP_CLEANUP(signingKey, signingCtx);
  161. return true;//success
  162. }
  163. //Verify the given text was used to create the given digital signature
  164. bool digiVerify(const char * text, StringBuffer & b64Signature)
  165. {
  166. if (!verifyingConfigured)
  167. throw MakeStringException(-1, "digiVerify:Verifying Digital Signatures not configured");
  168. EVP_MD_CTX * verifyingCtx;
  169. EVP_PKEY * verifyingKey;
  170. digiInit(false, passphraseBuffEnc.str(), &verifyingCtx, &verifyingKey);
  171. //decode base64 signature
  172. StringBuffer decodedSig;
  173. JBASE64_Decode(b64Signature.str(), decodedSig);
  174. if (EVP_DigestVerifyUpdate(verifyingCtx, text, strlen(text)) <= 0)
  175. {
  176. EVP_CLEANUP(verifyingKey, verifyingCtx);
  177. EVP_THROW("digiVerify:EVP_DigestVerifyUpdate: %s");
  178. }
  179. int match = EVP_DigestVerifyFinal(verifyingCtx, (unsigned char *)decodedSig.str(), decodedSig.length());
  180. EVP_CLEANUP(verifyingKey, verifyingCtx);
  181. return match == 1;
  182. }
  183. };
  184. #else
  185. //Dummy implementation if no OPENSSL available.
  186. class CDigitalSignatureManager : implements IDigitalSignatureManager, public CInterface
  187. {
  188. public:
  189. IMPLEMENT_IINTERFACE;
  190. CDigitalSignatureManager(const char * _pubKeyBuff, const char * _privKeyBuff, const char * _passPhrase)
  191. {
  192. WARNLOG("CDigitalSignatureManager: Platform built without OPENSSL!");
  193. }
  194. bool isDigiSignerConfigured()
  195. {
  196. return false;
  197. }
  198. bool isDigiVerifierConfigured()
  199. {
  200. return false;
  201. }
  202. //Create base 64 encoded digital signature of given text string
  203. bool digiSign(const char * text, StringBuffer & b64Signature)
  204. {
  205. //convert to base64
  206. JBASE64_Encode(text, strlen(text), b64Signature, false);
  207. return true;//success
  208. }
  209. //Verify the given text was used to create the given digital signature
  210. bool digiVerify(const char * text, StringBuffer & b64Signature)
  211. {
  212. //decode base64 signature
  213. StringBuffer decodedSig;
  214. JBASE64_Decode(b64Signature.str(), decodedSig);
  215. return streq(text, decodedSig);
  216. }
  217. };
  218. #endif
  219. static IDigitalSignatureManager * dsm;
  220. static std::once_flag dsmInitFlag;
  221. static std::once_flag dsmAddAlgoFlag;
  222. MODULE_INIT(INIT_PRIORITY_STANDARD)
  223. {
  224. return true;
  225. }
  226. MODULE_EXIT()
  227. {
  228. ::Release(dsm);
  229. }
  230. static void createDigitalSignatureManagerInstance(IDigitalSignatureManager * * ppDSM)
  231. {
  232. const char * pubKey = nullptr, *privKey = nullptr, *passPhrase = nullptr;
  233. queryHPCCPKIKeyFiles(nullptr, &pubKey, &privKey, &passPhrase);
  234. *ppDSM = createDigitalSignatureManagerInstanceFromFiles(pubKey, privKey, passPhrase);
  235. }
  236. static void addAlgorithms()
  237. {
  238. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  239. OpenSSL_add_all_algorithms();
  240. #endif
  241. }
  242. extern "C"
  243. {
  244. //Returns reference to singleton instance created from environment.conf key file settings
  245. DIGISIGN_API IDigitalSignatureManager * queryDigitalSignatureManagerInstanceFromEnv()
  246. {
  247. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  248. std::call_once(dsmInitFlag, createDigitalSignatureManagerInstance, &dsm);
  249. return dsm;
  250. #else
  251. return nullptr;
  252. #endif
  253. }
  254. //Create using given key filespecs
  255. //Caller must release when no longer needed
  256. DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromFiles(const char * _pubKey, const char *_privKey, const char * _passPhrase)
  257. {
  258. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  259. StringBuffer privateKeyBuff;
  260. StringBuffer publicKeyBuff;
  261. if (!isEmptyString(_pubKey))
  262. {
  263. try
  264. {
  265. publicKeyBuff.loadFile(_pubKey);
  266. }
  267. catch (IException * e)
  268. {
  269. e->Release();
  270. }
  271. if (publicKeyBuff.isEmpty())
  272. throw MakeStringException(-1, "digiSign:Cannot load public key file");
  273. }
  274. if (!isEmptyString(_privKey))
  275. {
  276. try
  277. {
  278. privateKeyBuff.loadFile(_privKey);
  279. }
  280. catch (IException * e)
  281. {
  282. e->Release();
  283. }
  284. if (privateKeyBuff.isEmpty())
  285. throw MakeStringException(-1, "digiSign:Cannot load private key file");
  286. }
  287. return createDigitalSignatureManagerInstanceFromKeys(publicKeyBuff, privateKeyBuff, _passPhrase);
  288. #else
  289. return nullptr;
  290. #endif
  291. }
  292. //Create using given PEM formatted keys
  293. //Caller must release when no longer needed
  294. DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromKeys(StringBuffer & _pubKeyBuff, StringBuffer & _privKeyBuff, const char * _passPhrase)
  295. {
  296. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  297. std::call_once(dsmAddAlgoFlag, addAlgorithms);
  298. return new CDigitalSignatureManager(_pubKeyBuff, _privKeyBuff, _passPhrase);
  299. #else
  300. return nullptr;
  301. #endif
  302. }
  303. }