Skip to content

Commit 7538be6

Browse files
authored
Merge pull request #1275 from cplussharp/ec-asn1
2 parents 8ab2256 + 21ac516 commit 7538be6

File tree

7 files changed

+978
-0
lines changed

7 files changed

+978
-0
lines changed

src/core/config/Categories.json

+4
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@
181181
"RSA Verify",
182182
"RSA Encrypt",
183183
"RSA Decrypt",
184+
"Generate ECDSA Key Pair",
185+
"ECDSA Signature Conversion",
186+
"ECDSA Sign",
187+
"ECDSA Verify",
184188
"Parse SSH Host Key",
185189
"Parse CSR",
186190
"Public Key from Certificate",

src/core/operations/ECDSASign.mjs

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* @author cplussharp
3+
* @copyright Crown Copyright 2021
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import { fromHex } from "../lib/Hex.mjs";
10+
import { toBase64 } from "../lib/Base64.mjs";
11+
import r from "jsrsasign";
12+
13+
/**
14+
* ECDSA Sign operation
15+
*/
16+
class ECDSASign extends Operation {
17+
18+
/**
19+
* ECDSASign constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "ECDSA Sign";
25+
this.module = "Ciphers";
26+
this.description = "Sign a plaintext message with a PEM encoded EC key.";
27+
this.infoURL = "https://wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm";
28+
this.inputType = "string";
29+
this.outputType = "string";
30+
this.args = [
31+
{
32+
name: "ECDSA Private Key (PEM)",
33+
type: "text",
34+
value: "-----BEGIN EC PRIVATE KEY-----"
35+
},
36+
{
37+
name: "Message Digest Algorithm",
38+
type: "option",
39+
value: [
40+
"SHA-256",
41+
"SHA-384",
42+
"SHA-512",
43+
"SHA-1",
44+
"MD5"
45+
]
46+
},
47+
{
48+
name: "Output Format",
49+
type: "option",
50+
value: [
51+
"ASN.1 HEX",
52+
"P1363 HEX",
53+
"JSON Web Signature",
54+
"Raw JSON"
55+
]
56+
}
57+
];
58+
}
59+
60+
/**
61+
* @param {string} input
62+
* @param {Object[]} args
63+
* @returns {string}
64+
*/
65+
run(input, args) {
66+
const [keyPem, mdAlgo, outputFormat] = args;
67+
68+
if (keyPem.replace("-----BEGIN EC PRIVATE KEY-----", "").length === 0) {
69+
throw new OperationError("Please enter a private key.");
70+
}
71+
72+
const internalAlgorithmName = mdAlgo.replace("-", "") + "withECDSA";
73+
const sig = new r.KJUR.crypto.Signature({ alg: internalAlgorithmName });
74+
const key = r.KEYUTIL.getKey(keyPem);
75+
if (key.type !== "EC") {
76+
throw new OperationError("Provided key is not an EC key.");
77+
}
78+
if (!key.isPrivate) {
79+
throw new OperationError("Provided key is not a private key.");
80+
}
81+
sig.init(key);
82+
const signatureASN1Hex = sig.signString(input);
83+
84+
let result;
85+
switch (outputFormat) {
86+
case "ASN.1 HEX":
87+
result = signatureASN1Hex;
88+
break;
89+
case "P1363 HEX":
90+
result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
91+
break;
92+
case "JSON Web Signature":
93+
result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
94+
result = toBase64(fromHex(result), "A-Za-z0-9-_"); // base64url
95+
break;
96+
case "Raw JSON": {
97+
const signatureRS = r.KJUR.crypto.ECDSA.parseSigHexInHexRS(signatureASN1Hex);
98+
result = JSON.stringify(signatureRS);
99+
break;
100+
}
101+
}
102+
103+
return result;
104+
}
105+
}
106+
107+
export default ECDSASign;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* @author cplussharp
3+
* @copyright Crown Copyright 2021
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import { fromBase64, toBase64 } from "../lib/Base64.mjs";
10+
import { fromHex, toHexFast } from "../lib/Hex.mjs";
11+
import r from "jsrsasign";
12+
13+
/**
14+
* ECDSA Sign operation
15+
*/
16+
class ECDSASignatureConversion extends Operation {
17+
18+
/**
19+
* ECDSASignatureConversion constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "ECDSA Signature Conversion";
25+
this.module = "Ciphers";
26+
this.description = "Convert an ECDSA signature between hex, asn1 and json.";
27+
this.infoURL = "https://wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm";
28+
this.inputType = "string";
29+
this.outputType = "string";
30+
this.args = [
31+
{
32+
name: "Input Format",
33+
type: "option",
34+
value: [
35+
"Auto",
36+
"ASN.1 HEX",
37+
"P1363 HEX",
38+
"JSON Web Signature",
39+
"Raw JSON"
40+
]
41+
},
42+
{
43+
name: "Output Format",
44+
type: "option",
45+
value: [
46+
"ASN.1 HEX",
47+
"P1363 HEX",
48+
"JSON Web Signature",
49+
"Raw JSON"
50+
]
51+
}
52+
];
53+
}
54+
55+
/**
56+
* @param {string} input
57+
* @param {Object[]} args
58+
* @returns {string}
59+
*/
60+
run(input, args) {
61+
let inputFormat = args[0];
62+
const outputFormat = args[1];
63+
64+
// detect input format
65+
let inputJson;
66+
if (inputFormat === "Auto") {
67+
try {
68+
inputJson = JSON.parse(input);
69+
if (typeof(inputJson) === "object") {
70+
inputFormat = "Raw JSON";
71+
}
72+
} catch {}
73+
}
74+
75+
if (inputFormat === "Auto") {
76+
const hexRegex = /^[a-f\d]{2,}$/gi;
77+
if (hexRegex.test(input)) {
78+
if (input.substring(0, 2) === "30" && r.ASN1HEX.isASN1HEX(input)) {
79+
inputFormat = "ASN.1 HEX";
80+
} else {
81+
inputFormat = "P1363 HEX";
82+
}
83+
}
84+
}
85+
86+
let inputBase64;
87+
if (inputFormat === "Auto") {
88+
try {
89+
inputBase64 = fromBase64(input, "A-Za-z0-9-_", false);
90+
inputFormat = "JSON Web Signature";
91+
} catch {}
92+
}
93+
94+
// convert input to ASN.1 hex
95+
let signatureASN1Hex;
96+
switch (inputFormat) {
97+
case "Auto":
98+
throw new OperationError("Signature format could not be detected");
99+
case "ASN.1 HEX":
100+
signatureASN1Hex = input;
101+
break;
102+
case "P1363 HEX":
103+
signatureASN1Hex = r.KJUR.crypto.ECDSA.concatSigToASN1Sig(input);
104+
break;
105+
case "JSON Web Signature":
106+
if (!inputBase64) inputBase64 = fromBase64(input, "A-Za-z0-9-_");
107+
signatureASN1Hex = r.KJUR.crypto.ECDSA.concatSigToASN1Sig(toHexFast(inputBase64));
108+
break;
109+
case "Raw JSON": {
110+
if (!inputJson) inputJson = JSON.parse(input);
111+
if (!inputJson.r) {
112+
throw new OperationError('No "r" value in the signature JSON');
113+
}
114+
if (!inputJson.s) {
115+
throw new OperationError('No "s" value in the signature JSON');
116+
}
117+
signatureASN1Hex = r.KJUR.crypto.ECDSA.hexRSSigToASN1Sig(inputJson.r, inputJson.s);
118+
break;
119+
}
120+
}
121+
122+
// convert ASN.1 hex to output format
123+
let result;
124+
switch (outputFormat) {
125+
case "ASN.1 HEX":
126+
result = signatureASN1Hex;
127+
break;
128+
case "P1363 HEX":
129+
result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
130+
break;
131+
case "JSON Web Signature":
132+
result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
133+
result = toBase64(fromHex(result), "A-Za-z0-9-_"); // base64url
134+
break;
135+
case "Raw JSON": {
136+
const signatureRS = r.KJUR.crypto.ECDSA.parseSigHexInHexRS(signatureASN1Hex);
137+
result = JSON.stringify(signatureRS);
138+
break;
139+
}
140+
}
141+
142+
return result;
143+
}
144+
}
145+
146+
export default ECDSASignatureConversion;

0 commit comments

Comments
 (0)