digisign.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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. #ifdef _USE_OPENSSL
  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. #define EVP_CLEANUP(key,ctx) EVP_PKEY_free(key); \
  23. EVP_MD_CTX_destroy(ctx);
  24. #define EVP_THROW(str) { \
  25. char buff[120]; \
  26. ERR_error_string(ERR_get_error(), buff); \
  27. throw MakeStringException(-1, str, buff); \
  28. }
  29. class CDigitalSignatureManager : implements IDigitalSignatureManager, public CInterface
  30. {
  31. private:
  32. StringBuffer publicKeyBuff;
  33. StringBuffer privateKeyBuff;
  34. StringBuffer passphraseBuffEnc;
  35. bool signingConfigured;
  36. bool verifyingConfigured;
  37. #ifdef _USE_OPENSSL
  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. #endif
  99. public:
  100. IMPLEMENT_IINTERFACE;
  101. CDigitalSignatureManager(StringBuffer & _pubKeyBuff, StringBuffer & _privKeyBuff, const char * _passPhrase)
  102. : signingConfigured(false), verifyingConfigured(false)
  103. {
  104. #ifdef _USE_OPENSSL
  105. publicKeyBuff.set(_pubKeyBuff.str());
  106. privateKeyBuff.set(_privKeyBuff.str());
  107. passphraseBuffEnc.set(_passPhrase);//MD5 encrypted passphrase
  108. signingConfigured = !publicKeyBuff.isEmpty();
  109. verifyingConfigured = !privateKeyBuff.isEmpty();
  110. #else
  111. WARNLOG("CDigitalSignatureManager: Platform built without OPENSSL!");
  112. #endif
  113. }
  114. virtual ~CDigitalSignatureManager()
  115. {
  116. }
  117. bool isDigiSignerConfigured()
  118. {
  119. return signingConfigured;
  120. }
  121. bool isDigiVerifierConfigured()
  122. {
  123. return verifyingConfigured;
  124. }
  125. //Create base 64 encoded digital signature of given text string
  126. bool digiSign(const char * text, StringBuffer & b64Signature)
  127. {
  128. if (!signingConfigured)
  129. throw MakeStringException(-1, "digiSign:Creating Digital Signatures not configured");
  130. #ifdef _USE_OPENSSL
  131. EVP_MD_CTX * signingCtx;
  132. EVP_PKEY * signingKey;
  133. digiInit(true, passphraseBuffEnc.str(), &signingCtx, &signingKey);
  134. //add string to the context
  135. if (EVP_DigestSignUpdate(signingCtx, (size_t*)text, strlen(text)) <= 0)
  136. {
  137. EVP_CLEANUP(signingKey, signingCtx);
  138. EVP_THROW("digiSign:EVP_DigestSignUpdate: %s");
  139. }
  140. //compute length of signature
  141. size_t encMsgLen;
  142. if (EVP_DigestSignFinal(signingCtx, nullptr, &encMsgLen) <= 0)
  143. {
  144. EVP_CLEANUP(signingKey, signingCtx);
  145. EVP_THROW("digiSign:EVP_DigestSignFinal1: %s");
  146. }
  147. if (encMsgLen == 0)
  148. {
  149. EVP_CLEANUP(signingKey, signingCtx);
  150. EVP_THROW("digiSign:EVP_DigestSignFinal length returned 0: %s");
  151. }
  152. //compute signature (signed digest)
  153. unsigned char * encMsg = (unsigned char*) malloc(encMsgLen);
  154. if (encMsg == nullptr)
  155. {
  156. EVP_CLEANUP(signingKey, signingCtx);
  157. throw MakeStringException(-1, "digiSign:malloc(%u) returned NULL",(unsigned)encMsgLen);
  158. }
  159. if (EVP_DigestSignFinal(signingCtx, encMsg, &encMsgLen) <= 0)
  160. {
  161. free(encMsg);
  162. EVP_CLEANUP(signingKey, signingCtx);
  163. EVP_THROW("digiSign:EVP_DigestSignFinal2: %s");
  164. }
  165. //convert to base64
  166. JBASE64_Encode(encMsg, encMsgLen, b64Signature, false);
  167. //cleanup
  168. free(encMsg);
  169. EVP_CLEANUP(signingKey, signingCtx);
  170. return true;//success
  171. #else
  172. throw MakeStringException(-1, "digiSign:Platform built without OPENSSL");
  173. #endif
  174. }
  175. //Verify the given text was used to create the given digital signature
  176. bool digiVerify(const char * text, StringBuffer & b64Signature)
  177. {
  178. if (!verifyingConfigured)
  179. throw MakeStringException(-1, "digiVerify:Verifying Digital Signatures not configured");
  180. #ifdef _USE_OPENSSL
  181. EVP_MD_CTX * verifyingCtx;
  182. EVP_PKEY * verifyingKey;
  183. digiInit(false, passphraseBuffEnc.str(), &verifyingCtx, &verifyingKey);
  184. //decode base64 signature
  185. StringBuffer decodedSig;
  186. JBASE64_Decode(b64Signature.str(), decodedSig);
  187. if (EVP_DigestVerifyUpdate(verifyingCtx, text, strlen(text)) <= 0)
  188. {
  189. EVP_CLEANUP(verifyingKey, verifyingCtx);
  190. EVP_THROW("digiVerify:EVP_DigestVerifyUpdate: %s");
  191. }
  192. int match = EVP_DigestVerifyFinal(verifyingCtx, (unsigned char *)decodedSig.str(), decodedSig.length());
  193. EVP_CLEANUP(verifyingKey, verifyingCtx);
  194. return match == 1;
  195. #else
  196. throw MakeStringException(-1, "digiSign:Platform built without OPENSSL");
  197. #endif
  198. }
  199. };
  200. static IDigitalSignatureManager * dsm;
  201. static std::once_flag dsmInitFlag;
  202. MODULE_INIT(INIT_PRIORITY_STANDARD)
  203. {
  204. return true;
  205. }
  206. MODULE_EXIT()
  207. {
  208. ::Release(dsm);
  209. }
  210. static void createDigitalSignatureManagerInstance(IDigitalSignatureManager * * ppDSM)
  211. {
  212. const char * pubKey = nullptr, *privKey = nullptr, *passPhrase = nullptr;
  213. queryHPCCPKIKeyFiles(nullptr, &pubKey, &privKey, &passPhrase);
  214. *ppDSM = createDigitalSignatureManagerInstanceFromFiles(pubKey, privKey, passPhrase);
  215. }
  216. extern "C"
  217. {
  218. //Returns reference to singleton instance created from environment.conf key file settings
  219. DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromEnv()
  220. {
  221. #ifdef _USE_OPENSSL
  222. std::call_once(dsmInitFlag, createDigitalSignatureManagerInstance, &dsm);
  223. return dsm;
  224. #else
  225. return nullptr;
  226. #endif
  227. }
  228. //Create using given key filespecs
  229. //Caller must release when no longer needed
  230. DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromFiles(const char * _pubKey, const char *_privKey, const char * _passPhrase)
  231. {
  232. #ifdef _USE_OPENSSL
  233. StringBuffer privateKeyBuff;
  234. StringBuffer publicKeyBuff;
  235. if (!isEmptyString(_pubKey))
  236. {
  237. publicKeyBuff.loadFile(_pubKey);
  238. if (publicKeyBuff.isEmpty())
  239. throw MakeStringException(-1, "digiSign:Cannot load public key file");
  240. }
  241. if (!isEmptyString(_privKey))
  242. {
  243. privateKeyBuff.loadFile(_privKey);
  244. if (privateKeyBuff.isEmpty())
  245. throw MakeStringException(-1, "digiSign:Cannot load private key file");
  246. }
  247. return createDigitalSignatureManagerInstanceFromKeys(publicKeyBuff, privateKeyBuff, _passPhrase);
  248. #else
  249. return nullptr;
  250. #endif
  251. }
  252. //Create using given PEM formatted keys
  253. //Caller must release when no longer needed
  254. DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromKeys(StringBuffer & _pubKeyBuff, StringBuffer & _privKeyBuff, const char * _passPhrase)
  255. {
  256. #ifdef _USE_OPENSSL
  257. return new CDigitalSignatureManager(_pubKeyBuff, _privKeyBuff, _passPhrase);
  258. #else
  259. return nullptr;
  260. #endif
  261. }
  262. }