digisign.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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)
  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. namespace cryptohelper
  23. {
  24. #if defined(_USE_OPENSSL)
  25. //Create base 64 encoded digital signature of given data
  26. bool digiSign(StringBuffer &b64Signature, size32_t dataSz, const void *data, const CLoadedKey &signingKey)
  27. {
  28. OwnedEVPMdCtx signingCtx(EVP_MD_CTX_create());
  29. //initialize context for SHA-256 hashing function
  30. int rc = EVP_DigestSignInit(signingCtx, nullptr, EVP_sha256(), nullptr, signingKey);
  31. if (rc <= 0)
  32. throwEVPException(-1, "digiSign:EVP_DigestSignInit");
  33. //add string to the context
  34. if (EVP_DigestSignUpdate(signingCtx, data, dataSz) <= 0)
  35. throwEVPException(-1, "digiSign:EVP_DigestSignUpdate");
  36. //compute length of signature
  37. size_t encMsgLen;
  38. if (EVP_DigestSignFinal(signingCtx, nullptr, &encMsgLen) <= 0)
  39. throwEVPException(-1, "digiSign:EVP_DigestSignFinal1");
  40. if (encMsgLen == 0)
  41. throwEVPException(-1, "digiSign:EVP_DigestSignFinal length returned 0");
  42. //compute signature (signed digest)
  43. OwnedEVPMemory encMsg = OPENSSL_malloc(encMsgLen);
  44. if (encMsg == nullptr)
  45. throw MakeStringException(-1, "digiSign:OPENSSL_malloc(%u) returned NULL", (unsigned)encMsgLen);
  46. if (EVP_DigestSignFinal(signingCtx, (unsigned char *)encMsg.get(), &encMsgLen) <= 0)
  47. throwEVPException(-1, "digiSign:EVP_DigestSignFinal2");
  48. //convert to base64
  49. JBASE64_Encode(encMsg, encMsgLen, b64Signature, false);
  50. return true;
  51. }
  52. //Verify the given data was used to create the given digital signature
  53. bool digiVerify(const char *b64Signature, size32_t dataSz, const void *data, const CLoadedKey &verifyingKey)
  54. {
  55. OwnedEVPMdCtx verifyingCtx(EVP_MD_CTX_create());
  56. int rc = EVP_DigestVerifyInit(verifyingCtx, nullptr, EVP_sha256(), nullptr, verifyingKey);
  57. if (rc <= 0)
  58. throwEVPException(-1, "digiVerify:EVP_DigestVerifyInit");
  59. //decode base64 signature
  60. StringBuffer decodedSig;
  61. JBASE64_Decode(b64Signature, decodedSig);
  62. if (EVP_DigestVerifyUpdate(verifyingCtx, data, dataSz) <= 0)
  63. throwEVPException(-1, "digiVerify:EVP_DigestVerifyUpdate");
  64. return 1 == EVP_DigestVerifyFinal(verifyingCtx, (unsigned char *)decodedSig.str(), decodedSig.length());
  65. }
  66. class CDigitalSignatureManager : public CSimpleInterfaceOf<IDigitalSignatureManager>
  67. {
  68. private:
  69. Linked<CLoadedKey> pubKey, privKey;
  70. bool signingConfigured = false;
  71. bool verifyingConfigured = false;
  72. public:
  73. CDigitalSignatureManager(CLoadedKey *_pubKey, CLoadedKey *_privKey) : pubKey(_pubKey), privKey(_privKey)
  74. {
  75. signingConfigured = nullptr != privKey.get();
  76. verifyingConfigured = nullptr != pubKey.get();
  77. }
  78. virtual bool isDigiSignerConfigured() const override
  79. {
  80. return signingConfigured;
  81. }
  82. virtual bool isDigiVerifierConfigured() const override
  83. {
  84. return verifyingConfigured;
  85. }
  86. //Create base 64 encoded digital signature of given data
  87. virtual bool digiSign(StringBuffer & b64Signature, size32_t dataSz, const void *data) const override
  88. {
  89. if (!signingConfigured)
  90. throw MakeStringException(-1, "digiSign:Creating Digital Signatures not configured");
  91. return cryptohelper::digiSign(b64Signature, dataSz, data, *privKey);
  92. }
  93. virtual bool digiSign(StringBuffer & b64Signature, const char *text) const override
  94. {
  95. return digiSign(b64Signature, strlen(text), text);
  96. }
  97. //Verify the given data was used to create the given digital signature
  98. virtual bool digiVerify(const char *b64Signature, size32_t dataSz, const void *data) const override
  99. {
  100. if (!verifyingConfigured)
  101. throw MakeStringException(-1, "digiVerify:Verifying Digital Signatures not configured");
  102. return cryptohelper::digiVerify(b64Signature, dataSz, data, *pubKey);
  103. }
  104. virtual bool digiVerify(const char *b64Signature, const char *text) const override
  105. {
  106. return digiVerify(b64Signature, strlen(text), text);
  107. }
  108. virtual const char * queryKeyName() const override
  109. {
  110. return pubKey->queryKeyName();
  111. }
  112. };
  113. #else
  114. //Dummy implementation if no OPENSSL available.
  115. bool digiSign(StringBuffer &b64Signature, const char *text, const CLoadedKey &signingKey)
  116. {
  117. throwStringExceptionV(-1, "digiSign: unavailable without openssl");
  118. }
  119. bool digiVerify(const char *b64Signature, const char *text, const CLoadedKey &verifyingKey)
  120. {
  121. throwStringExceptionV(-1, "digiVerify: unavailable without openssl");
  122. }
  123. class CDigitalSignatureManager : public CSimpleInterfaceOf<IDigitalSignatureManager>
  124. {
  125. public:
  126. CDigitalSignatureManager(const char * _pubKeyBuff, const char * _privKeyBuff, const char * _passPhrase)
  127. {
  128. WARNLOG("CDigitalSignatureManager: Platform built without OPENSSL!");
  129. }
  130. virtual bool isDigiSignerConfigured() const override
  131. {
  132. return false;
  133. }
  134. virtual bool isDigiVerifierConfigured() const override
  135. {
  136. return false;
  137. }
  138. virtual bool digiSign(StringBuffer & b64Signature, size32_t dataSz, const void *data) const override
  139. {
  140. throwStringExceptionV(-1, "digiSign: unavailable without openssl");
  141. }
  142. virtual bool digiSign(StringBuffer & b64Signature, const char * text) const override
  143. {
  144. throwStringExceptionV(-1, "digiSign: unavailable without openssl");
  145. }
  146. virtual bool digiVerify(const char *b64Signature, const char * text) const override
  147. {
  148. throwStringExceptionV(-1, "digiVerify: unavailable without openssl");
  149. }
  150. virtual bool digiVerify(const char *b64Signature, size32_t dataSz, const void *data) const override
  151. {
  152. throwStringExceptionV(-1, "digiVerify: unavailable without openssl");
  153. }
  154. virtual const char * queryKeyName() const override
  155. {
  156. throwStringExceptionV(-1, "digiVerify: unavailable without openssl");
  157. }
  158. };
  159. #endif
  160. static IDigitalSignatureManager * dsm = nullptr;
  161. static std::once_flag dsmInitFlag;
  162. MODULE_INIT(INIT_PRIORITY_STANDARD)
  163. {
  164. return true;
  165. }
  166. MODULE_EXIT()
  167. {
  168. ::Release(dsm);
  169. }
  170. static void createDigitalSignatureManagerInstance(IDigitalSignatureManager * * ppDSM)
  171. {
  172. const char * pubKey = nullptr, *privKey = nullptr, *passPhrase = nullptr;
  173. queryHPCCPKIKeyFiles(nullptr, &pubKey, &privKey, &passPhrase);
  174. StringBuffer passPhraseDec;
  175. if (!isEmptyString(passPhrase))
  176. {
  177. decrypt(passPhraseDec, passPhrase);
  178. passPhrase = passPhraseDec.str();
  179. }
  180. *ppDSM = createDigitalSignatureManagerInstanceFromFiles(pubKey, privKey, passPhrase);
  181. }
  182. //Returns reference to singleton instance created from environment.conf key file settings
  183. IDigitalSignatureManager * queryDigitalSignatureManagerInstanceFromEnv()
  184. {
  185. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  186. std::call_once(dsmInitFlag, createDigitalSignatureManagerInstance, &dsm);
  187. return dsm;
  188. #else
  189. return nullptr;
  190. #endif
  191. }
  192. //Create using given key filespecs
  193. //Caller must release when no longer needed
  194. IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromFiles(const char * pubKeyFileName, const char *privKeyFileName, const char * passPhrase)
  195. {
  196. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  197. Owned<CLoadedKey> pubKey, privKey;
  198. Owned<IMultiException> exceptions;
  199. if (!isEmptyString(pubKeyFileName))
  200. {
  201. try
  202. {
  203. pubKey.setown(loadPublicKeyFromFile(pubKeyFileName, passPhrase));
  204. }
  205. catch (IException * e)
  206. {
  207. if (!exceptions)
  208. exceptions.setown(makeMultiException("createDigitalSignatureManagerInstanceFromFiles"));
  209. exceptions->append(* makeWrappedExceptionV(e, -1, "createDigitalSignatureManagerInstanceFromFiles:Cannot load public key file"));
  210. e->Release();
  211. }
  212. }
  213. if (!isEmptyString(privKeyFileName))
  214. {
  215. try
  216. {
  217. privKey.setown(loadPrivateKeyFromFile(privKeyFileName, passPhrase));
  218. }
  219. catch (IException * e)
  220. {
  221. if (!exceptions)
  222. exceptions.setown(makeMultiException("createDigitalSignatureManagerInstanceFromFiles"));
  223. exceptions->append(* makeWrappedExceptionV(e, -1, "createDigitalSignatureManagerInstanceFromFiles:Cannot load private key file"));
  224. e->Release();
  225. }
  226. }
  227. // NB: allow it continue if 1 of the keys successfully loaded.
  228. if (exceptions && exceptions->ordinality())
  229. {
  230. if (!pubKey && !privKey)
  231. throw exceptions.getClear();
  232. else
  233. EXCLOG(exceptions, nullptr);
  234. }
  235. return new CDigitalSignatureManager(pubKey, privKey);
  236. #else
  237. return nullptr;
  238. #endif
  239. }
  240. //Create using given PEM formatted keys
  241. //Caller must release when no longer needed
  242. IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromKeys(const char * pubKeyString, const char * privKeyString, const char * passPhrase)
  243. {
  244. #if defined(_USE_OPENSSL) && !defined(_WIN32)
  245. Owned<CLoadedKey> pubKey, privKey;
  246. Owned<IMultiException> exceptions;
  247. if (!isEmptyString(pubKeyString))
  248. {
  249. try
  250. {
  251. pubKey.setown(loadPublicKeyFromMemory(pubKeyString, passPhrase));
  252. }
  253. catch (IException * e)
  254. {
  255. if (!exceptions)
  256. exceptions.setown(makeMultiException("createDigitalSignatureManagerInstanceFromKeys"));
  257. exceptions->append(* makeWrappedExceptionV(e, -1, "createDigitalSignatureManagerInstanceFromFiles:Cannot load public key"));
  258. e->Release();
  259. }
  260. }
  261. if (!isEmptyString(privKeyString))
  262. {
  263. try
  264. {
  265. privKey.setown(loadPrivateKeyFromMemory(privKeyString, passPhrase));
  266. }
  267. catch (IException * e)
  268. {
  269. if (!exceptions)
  270. exceptions.setown(makeMultiException("createDigitalSignatureManagerInstanceFromKeys"));
  271. exceptions->append(* makeWrappedExceptionV(e, -1, "createDigitalSignatureManagerInstanceFromFiles:Cannot load private key"));
  272. e->Release();
  273. }
  274. }
  275. // NB: allow it continue if 1 of the keys successfully loaded.
  276. if (exceptions && exceptions->ordinality())
  277. {
  278. if (!pubKey && !privKey)
  279. throw exceptions.getClear();
  280. else
  281. EXCLOG(exceptions, nullptr);
  282. }
  283. return new CDigitalSignatureManager(pubKey, privKey);
  284. #else
  285. return nullptr;
  286. #endif
  287. }
  288. //Create using preloaded keys
  289. //Caller must release when no longer needed
  290. IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromKeys(CLoadedKey *pubKey, CLoadedKey *privKey)
  291. {
  292. #if defined(_USE_OPENSSL)
  293. return new CDigitalSignatureManager(pubKey, privKey);
  294. #else
  295. return nullptr;
  296. #endif
  297. }
  298. } // namespace cryptohelper