PublicKey.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import Utils from "../Utils.js";
  2. import {fromBase64} from "../lib/Base64";
  3. import {toHex, fromHex} from "../lib/Hex";
  4. import * as r from "jsrsasign";
  5. /**
  6. * Public Key operations.
  7. *
  8. * @author n1474335 [n1474335@gmail.com]
  9. * @copyright Crown Copyright 2016
  10. * @license Apache-2.0
  11. *
  12. * @namespace
  13. */
  14. const PublicKey = {
  15. /**
  16. * @constant
  17. * @default
  18. */
  19. X509_INPUT_FORMAT: ["PEM", "DER Hex", "Base64", "Raw"],
  20. /**
  21. * Parse X.509 certificate operation.
  22. *
  23. * @param {string} input
  24. * @param {Object[]} args
  25. * @returns {string}
  26. */
  27. runParseX509: function (input, args) {
  28. if (!input.length) {
  29. return "No input";
  30. }
  31. let cert = new r.X509(),
  32. inputFormat = args[0];
  33. switch (inputFormat) {
  34. case "DER Hex":
  35. input = input.replace(/\s/g, "");
  36. cert.readCertHex(input);
  37. break;
  38. case "PEM":
  39. cert.readCertPEM(input);
  40. break;
  41. case "Base64":
  42. cert.readCertHex(toHex(fromBase64(input, null, "byteArray"), ""));
  43. break;
  44. case "Raw":
  45. cert.readCertHex(toHex(Utils.strToByteArray(input), ""));
  46. break;
  47. default:
  48. throw "Undefined input format";
  49. }
  50. let sn = cert.getSerialNumberHex(),
  51. issuer = cert.getIssuerString(),
  52. subject = cert.getSubjectString(),
  53. pk = cert.getPublicKey(),
  54. pkFields = [],
  55. pkStr = "",
  56. sig = cert.getSignatureValueHex(),
  57. sigStr = "",
  58. extensions = "";
  59. // Public Key fields
  60. pkFields.push({
  61. key: "Algorithm",
  62. value: pk.type
  63. });
  64. if (pk.type === "EC") { // ECDSA
  65. pkFields.push({
  66. key: "Curve Name",
  67. value: pk.curveName
  68. });
  69. pkFields.push({
  70. key: "Length",
  71. value: (((new r.BigInteger(pk.pubKeyHex, 16)).bitLength()-3) /2) + " bits"
  72. });
  73. pkFields.push({
  74. key: "pub",
  75. value: PublicKey._formatByteStr(pk.pubKeyHex, 16, 18)
  76. });
  77. } else if (pk.type === "DSA") { // DSA
  78. pkFields.push({
  79. key: "pub",
  80. value: PublicKey._formatByteStr(pk.y.toString(16), 16, 18)
  81. });
  82. pkFields.push({
  83. key: "P",
  84. value: PublicKey._formatByteStr(pk.p.toString(16), 16, 18)
  85. });
  86. pkFields.push({
  87. key: "Q",
  88. value: PublicKey._formatByteStr(pk.q.toString(16), 16, 18)
  89. });
  90. pkFields.push({
  91. key: "G",
  92. value: PublicKey._formatByteStr(pk.g.toString(16), 16, 18)
  93. });
  94. } else if (pk.e) { // RSA
  95. pkFields.push({
  96. key: "Length",
  97. value: pk.n.bitLength() + " bits"
  98. });
  99. pkFields.push({
  100. key: "Modulus",
  101. value: PublicKey._formatByteStr(pk.n.toString(16), 16, 18)
  102. });
  103. pkFields.push({
  104. key: "Exponent",
  105. value: pk.e + " (0x" + pk.e.toString(16) + ")"
  106. });
  107. } else {
  108. pkFields.push({
  109. key: "Error",
  110. value: "Unknown Public Key type"
  111. });
  112. }
  113. // Format Public Key fields
  114. for (let i = 0; i < pkFields.length; i++) {
  115. pkStr += " " + pkFields[i].key + ":" +
  116. (pkFields[i].value + "\n").padStart(
  117. 18 - (pkFields[i].key.length + 3) + pkFields[i].value.length + 1,
  118. " "
  119. );
  120. }
  121. // Signature fields
  122. let breakoutSig = false;
  123. try {
  124. breakoutSig = r.ASN1HEX.dump(sig).indexOf("SEQUENCE") === 0;
  125. } catch (err) {
  126. // Error processing signature, output without further breakout
  127. }
  128. if (breakoutSig) { // DSA or ECDSA
  129. sigStr = " r: " + PublicKey._formatByteStr(r.ASN1HEX.getV(sig, 4), 16, 18) + "\n" +
  130. " s: " + PublicKey._formatByteStr(r.ASN1HEX.getV(sig, 48), 16, 18);
  131. } else { // RSA or unknown
  132. sigStr = " Signature: " + PublicKey._formatByteStr(sig, 16, 18);
  133. }
  134. // Extensions
  135. try {
  136. extensions = cert.getInfo().split("X509v3 Extensions:\n")[1].split("signature")[0];
  137. } catch (err) {}
  138. let issuerStr = PublicKey._formatDnStr(issuer, 2),
  139. nbDate = PublicKey._formatDate(cert.getNotBefore()),
  140. naDate = PublicKey._formatDate(cert.getNotAfter()),
  141. subjectStr = PublicKey._formatDnStr(subject, 2);
  142. return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)})
  143. Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn})
  144. Algorithm ID: ${cert.getSignatureAlgorithmField()}
  145. Validity
  146. Not Before: ${nbDate} (dd-mm-yy hh:mm:ss) (${cert.getNotBefore()})
  147. Not After: ${naDate} (dd-mm-yy hh:mm:ss) (${cert.getNotAfter()})
  148. Issuer
  149. ${issuerStr}
  150. Subject
  151. ${subjectStr}
  152. Public Key
  153. ${pkStr.slice(0, -1)}
  154. Certificate Signature
  155. Algorithm: ${cert.getSignatureAlgorithmName()}
  156. ${sigStr}
  157. Extensions
  158. ${extensions}`;
  159. },
  160. /**
  161. * PEM to Hex operation.
  162. *
  163. * @param {string} input
  164. * @param {Object[]} args
  165. * @returns {string}
  166. */
  167. runPemToHex: function(input, args) {
  168. if (input.indexOf("-----BEGIN") < 0) {
  169. // Add header so that the KEYUTIL function works
  170. input = "-----BEGIN CERTIFICATE-----" + input;
  171. }
  172. if (input.indexOf("-----END") < 0) {
  173. // Add footer so that the KEYUTIL function works
  174. input = input + "-----END CERTIFICATE-----";
  175. }
  176. let cert = new r.X509();
  177. cert.readCertPEM(input);
  178. return cert.hex;
  179. },
  180. /**
  181. * @constant
  182. * @default
  183. */
  184. PEM_HEADER_STRING: "CERTIFICATE",
  185. /**
  186. * Hex to PEM operation.
  187. *
  188. * @param {string} input
  189. * @param {Object[]} args
  190. * @returns {string}
  191. */
  192. runHexToPem: function(input, args) {
  193. return r.KJUR.asn1.ASN1Util.getPEMStringFromHex(input.replace(/\s/g, ""), args[0]);
  194. },
  195. /**
  196. * Hex to Object Identifier operation.
  197. *
  198. * @param {string} input
  199. * @param {Object[]} args
  200. * @returns {string}
  201. */
  202. runHexToObjectIdentifier: function(input, args) {
  203. return r.KJUR.asn1.ASN1Util.oidHexToInt(input.replace(/\s/g, ""));
  204. },
  205. /**
  206. * Object Identifier to Hex operation.
  207. *
  208. * @param {string} input
  209. * @param {Object[]} args
  210. * @returns {string}
  211. */
  212. runObjectIdentifierToHex: function(input, args) {
  213. return r.KJUR.asn1.ASN1Util.oidIntToHex(input);
  214. },
  215. /**
  216. * @constant
  217. * @default
  218. */
  219. ASN1_TRUNCATE_LENGTH: 32,
  220. /**
  221. * Parse ASN.1 hex string operation.
  222. *
  223. * @param {string} input
  224. * @param {Object[]} args
  225. * @returns {string}
  226. */
  227. runParseAsn1HexString: function(input, args) {
  228. let truncateLen = args[1],
  229. index = args[0];
  230. return r.ASN1HEX.dump(input.replace(/\s/g, ""), {
  231. "ommitLongOctet": truncateLen
  232. }, index);
  233. },
  234. /**
  235. * Formats Distinguished Name (DN) strings.
  236. *
  237. * @private
  238. * @param {string} dnStr
  239. * @param {number} indent
  240. * @returns {string}
  241. */
  242. _formatDnStr: function(dnStr, indent) {
  243. let output = "",
  244. fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//),
  245. maxKeyLen = 0,
  246. key,
  247. value,
  248. i,
  249. str;
  250. for (i = 0; i < fields.length; i++) {
  251. if (!fields[i].length) continue;
  252. key = fields[i].split("=")[0];
  253. maxKeyLen = key.length > maxKeyLen ? key.length : maxKeyLen;
  254. }
  255. for (i = 0; i < fields.length; i++) {
  256. if (!fields[i].length) continue;
  257. key = fields[i].split("=")[0];
  258. value = fields[i].split("=")[1];
  259. str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
  260. output += str.padStart(indent + str.length, " ");
  261. }
  262. return output.slice(0, -1);
  263. },
  264. /**
  265. * Formats byte strings by adding line breaks and delimiters.
  266. *
  267. * @private
  268. * @param {string} byteStr
  269. * @param {number} length - Line width
  270. * @param {number} indent
  271. * @returns {string}
  272. */
  273. _formatByteStr: function(byteStr, length, indent) {
  274. byteStr = toHex(fromHex(byteStr), ":");
  275. length = length * 3;
  276. let output = "";
  277. for (let i = 0; i < byteStr.length; i += length) {
  278. const str = byteStr.slice(i, i + length) + "\n";
  279. if (i === 0) {
  280. output += str;
  281. } else {
  282. output += str.padStart(indent + str.length, " ");
  283. }
  284. }
  285. return output.slice(0, output.length-1);
  286. },
  287. /**
  288. * Formats dates.
  289. *
  290. * @private
  291. * @param {string} dateStr
  292. * @returns {string}
  293. */
  294. _formatDate: function(dateStr) {
  295. return dateStr[4] + dateStr[5] + "/" +
  296. dateStr[2] + dateStr[3] + "/" +
  297. dateStr[0] + dateStr[1] + " " +
  298. dateStr[6] + dateStr[7] + ":" +
  299. dateStr[8] + dateStr[9] + ":" +
  300. dateStr[10] + dateStr[11];
  301. },
  302. };
  303. export default PublicKey;