Source: security/certificate/public-key.js

  1. /**
  2. * Copyright (C) 2014-2018 Regents of the University of California.
  3. * @author: Jeff Thompson <jefft0@remap.ucla.edu>
  4. * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. * A copy of the GNU Lesser General Public License is in the file COPYING.
  19. */
  20. // Use capitalized Crypto to not clash with the browser's crypto.subtle.
  21. /** @ignore */
  22. var constants = require('constants'); /** @ignore */
  23. var Crypto = require('../../crypto.js'); /** @ignore */
  24. var Blob = require('../../util/blob.js').Blob; /** @ignore */
  25. var DerNode = require('../../encoding/der/der-node.js').DerNode; /** @ignore */
  26. var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
  27. var UnrecognizedKeyFormatException = require('../security-exception.js').UnrecognizedKeyFormatException; /** @ignore */
  28. var KeyType = require('../security-types.js').KeyType; /** @ignore */
  29. var EncryptAlgorithmType = require('../../encrypt/algo/encrypt-params.js').EncryptAlgorithmType; /** @ignore */
  30. var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
  31. var UseSubtleCrypto = require('../../use-subtle-crypto-node.js').UseSubtleCrypto; /** @ignore */
  32. var DigestAlgorithm = require('../security-types.js').DigestAlgorithm;
  33. /**
  34. * Create a new PublicKey by decoding the keyDer. Set the key type from the
  35. * decoding.
  36. * @param {Blob} keyDer The blob of the SubjectPublicKeyInfo DER.
  37. * @throws UnrecognizedKeyFormatException if can't decode the key DER.
  38. * @constructor
  39. */
  40. var PublicKey = function PublicKey(keyDer)
  41. {
  42. if (!keyDer) {
  43. this.keyDer = new Blob();
  44. this.keyType = null;
  45. return;
  46. }
  47. this.keyDer = keyDer;
  48. // Get the public key OID.
  49. var oidString = null;
  50. try {
  51. var parsedNode = DerNode.parse(keyDer.buf(), 0);
  52. var rootChildren = parsedNode.getChildren();
  53. var algorithmIdChildren = DerNode.getSequence(rootChildren, 0).getChildren();
  54. oidString = algorithmIdChildren[0].toVal();
  55. }
  56. catch (ex) {
  57. throw new UnrecognizedKeyFormatException(new Error
  58. ("PublicKey.decodeKeyType: Error decoding the public key: " + ex.message));
  59. }
  60. // Verify that the we can decode.
  61. if (oidString == PublicKey.RSA_ENCRYPTION_OID) {
  62. this.keyType = KeyType.RSA;
  63. // TODO: Check RSA decoding.
  64. }
  65. else if (oidString == PublicKey.EC_ENCRYPTION_OID) {
  66. this.keyType = KeyType.EC;
  67. // TODO: Check EC decoding.
  68. }
  69. };
  70. exports.PublicKey = PublicKey;
  71. /**
  72. * Encode the public key into DER.
  73. * @return {DerNode} The encoded DER syntax tree.
  74. */
  75. PublicKey.prototype.toDer = function()
  76. {
  77. return DerNode.parse(this.keyDer.buf());
  78. };
  79. /**
  80. * Get the key type.
  81. * @return {number} The key type as an int from KeyType.
  82. */
  83. PublicKey.prototype.getKeyType = function()
  84. {
  85. return this.keyType;
  86. };
  87. /**
  88. * Get the digest of the public key.
  89. * @param {number} digestAlgorithm (optional) The integer from DigestAlgorithm,
  90. * such as DigestAlgorithm.SHA256. If omitted, use DigestAlgorithm.SHA256 .
  91. * @return {Blob} The digest value.
  92. */
  93. PublicKey.prototype.getDigest = function(digestAlgorithm)
  94. {
  95. if (digestAlgorithm == undefined)
  96. digestAlgorithm = DigestAlgorithm.SHA256;
  97. if (digestAlgorithm == DigestAlgorithm.SHA256) {
  98. var hash = Crypto.createHash('sha256');
  99. hash.update(this.keyDer.buf());
  100. return new Blob(hash.digest(), false);
  101. }
  102. else
  103. throw new SecurityException(new Error("Wrong format!"));
  104. };
  105. /**
  106. * Get the raw bytes of the public key in DER format.
  107. * @return {Blob} The public key DER.
  108. */
  109. PublicKey.prototype.getKeyDer = function()
  110. {
  111. return this.keyDer;
  112. };
  113. /**
  114. * Encrypt the plainData using the keyBits according the encrypt algorithm type.
  115. * @param {Blob|Buffer} plainData The data to encrypt.
  116. * @param {number} algorithmType The algorithm type from the
  117. * EncryptAlgorithmType enum, e.g., RsaOaep.
  118. * @param {boolean} useSync (optional) If true then return a SyncPromise which
  119. * is already fulfilled. If omitted or false, this may return a SyncPromise or
  120. * an async Promise.
  121. * @return {Promise|SyncPromise} A promise which returns the encrypted Blob.
  122. */
  123. PublicKey.prototype.encryptPromise = function(plainData, algorithmType, useSync)
  124. {
  125. if (typeof plainData === 'object' && plainData instanceof Blob)
  126. plainData = plainData.buf();
  127. if (UseSubtleCrypto() && !useSync &&
  128. // Crypto.subtle doesn't implement PKCS1 padding.
  129. algorithmType != EncryptAlgorithmType.RsaPkcs) {
  130. if (algorithmType == EncryptAlgorithmType.RsaOaep) {
  131. if (this.keyType != KeyType.RSA)
  132. return Promise.reject(new Error("The key type must be RSA"));
  133. return crypto.subtle.importKey
  134. ("spki", this.keyDer.buf(), { name: "RSA-OAEP", hash: {name: "SHA-1"} },
  135. false, ["encrypt"])
  136. .then(function(publicKey) {
  137. return crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, plainData);
  138. })
  139. .then(function(result) {
  140. return Promise.resolve(new Blob(new Uint8Array(result), false));
  141. });
  142. }
  143. else
  144. return Promise.reject(new Error("unsupported padding scheme"));
  145. }
  146. else {
  147. // Encode the key DER as a PEM public key as needed by Crypto.
  148. var keyBase64 = this.keyDer.buf().toString('base64');
  149. var keyPem = "-----BEGIN PUBLIC KEY-----\n";
  150. for (var i = 0; i < keyBase64.length; i += 64)
  151. keyPem += (keyBase64.substr(i, 64) + "\n");
  152. keyPem += "-----END PUBLIC KEY-----";
  153. var padding;
  154. if (algorithmType == EncryptAlgorithmType.RsaPkcs) {
  155. if (this.keyType != KeyType.RSA)
  156. return SyncPromise.reject(new Error("The key type must be RSA"));
  157. padding = constants.RSA_PKCS1_PADDING;
  158. }
  159. else if (algorithmType == EncryptAlgorithmType.RsaOaep) {
  160. if (this.keyType != KeyType.RSA)
  161. return SyncPromise.reject(new Error("The key type must be RSA"));
  162. padding = constants.RSA_PKCS1_OAEP_PADDING;
  163. }
  164. else
  165. return SyncPromise.reject(new Error("unsupported padding scheme"));
  166. try {
  167. // In Node.js, publicEncrypt requires version v0.12.
  168. return SyncPromise.resolve(new Blob
  169. (Crypto.publicEncrypt({ key: keyPem, padding: padding }, plainData),
  170. false));
  171. } catch (err) {
  172. return SyncPromise.reject(err);
  173. }
  174. }
  175. };
  176. /**
  177. * Encrypt the plainData using the keyBits according the encrypt algorithm type.
  178. * @param {Blob|Buffer} plainData The data to encrypt.
  179. * @param {number} algorithmType The algorithm type from the
  180. * EncryptAlgorithmType enum, e.g., RsaOaep.
  181. * @return {Blob} The encrypted data.
  182. * @throws Error If encryptPromise doesn't return a SyncPromise which is
  183. * already fulfilled.
  184. */
  185. PublicKey.prototype.encrypt = function(plainData, algorithmType)
  186. {
  187. return SyncPromise.getValue(this.encryptPromise
  188. (plainData, algorithmType, true));
  189. };
  190. PublicKey.RSA_ENCRYPTION_OID = "1.2.840.113549.1.1.1";
  191. PublicKey.EC_ENCRYPTION_OID = "1.2.840.10045.2.1";