-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathecsignaturederstringverification.html
214 lines (200 loc) · 8.63 KB
/
ecsignaturederstringverification.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>EC P-256 signature SHA-256 DER (verification only)</title>
<style>
body {background-color: powderblue;}
h1 {color: blue;}
h2 {font-size: 200%;}
p {font-size: 150%; }
button {height: 100%; font-size: 150%;}
textarea {font-size: 150%;}
input[type="text"] {font-size: 100%; }
input[type="button"] {font-size: 100%; }
</style>
</head>
<body>
<h1>EC P-256 signature SHA-256 DER (verification only)</h1>
<hr>
<p>
<b>Important note: this program is doing what it promises but the programming itself is of very poor quality and for demonstration purposes only. Never ever use this program as source for your own programs because there are a lot of conversions to get it run.</b>
<br><br>Get more information about this program on <a href="https://github.com/java-crypto/cross_platform_crypto/blob/main/docs/ecdsa_signature_der_string.md" target="_blank">
my webpage <b>Elliptic key string signature (ECDSA) with DER encoding</b></a></p>
<hr>
<p>Insert the Public Key you received (PEM encoding):</p>
<textarea name="publickey-value" id="publickey-value" form= "myform" rows="5" cols="70">-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMZHnt1D1tddLNGlEHXEtEw5K/G5QHkgaLi+IV84oiV+THv/DqGxYDX2F5JOkfyv36iYSf5lfIC7q9el4YLnlwA==
-----END PUBLIC KEY-----</textarea>
<section class="import-key spki">
<p>Enter a message to verify:</p>
<input type="text" id="plaintext" name="plaintext" size="50" value="The quick brown fox jumps over the lazy dog">
<hr>
<p>Enter the signature to verify (the signature has to be in DER format and base64 encoded):</p>
<textarea name="spki-message" id="spki-message" form="myform" rows="4" cols="64">MEQCIPsioghihMGyEJGc0n9TSi+0TSgFWuKtXnQxE6QN5daRAiDkWEg8TvfLlp7q/R9YjURjMIJBie8ZlFw28Yep8/NG6g==</textarea>
<hr>
<p><b>Instructions:</b><br>
1.: insert the received public key and press the "Import key" button<br>
2.: if the public key is valid the "Verify" button is enabled<br>
3.: enter the plaintext message<br>
4.: enter the signature<br>
5.: press the "Verify button"<br>
6.: receive the verification status</p><hr>
<input class="import-key-button" type="button" value="Import Key">
<input class="verify-button hidden" type="button" value="Verify" disabled>
</section>
<p>verification status:</p>
<textarea name="verificationstatus" id="verificationstatus" form= "myform" rows="1" cols="50" readonly>verification status comes here...</textarea>
</div>
</p>
<hr><p><b>Technical note: this program uses the ECDSA Elliptic curve signature algorithm and curve SECP256R1 / NIST P-256 / PRIME256V1 / O.I.D. identifier 1.2.840.10045.3.1.7, the signature is encoded in DER format.</b></p><hr>
</body>
<SCRIPT LANGUAGE="JavaScript">
const bufToB64 = buf =>
btoa(Array.prototype.map.call(buf, ch => String.fromCharCode(ch)).join(""));
const b64ToBuf = b64 => {
const binstr = atob(b64),
buf = new Uint8Array(binstr.length);
Array.prototype.forEach.call(binstr, (ch, i) => {
buf[i] = ch.charCodeAt(0);
});
return buf;
};
/*
The unwrapped signing key.
*/
let publicKey;
const verifyButton = document.querySelector(".spki .verify-button");
/*
Convert a string into an ArrayBuffer
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
*/
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
/**
* Converts an ECDSA signature from P1363 to DER format
* source: https://developer.token.io/sdk/esdoc/file/Post_Quantum_Cryptography.PQC_Rainbow_Signature.src/security/CryptoBrowser.js.html
*
* @param {Uint8Array} sig - P1363 signature
* @return {Uint8Array} DER signature
* @private
*/
function p1363ToDer(sig) {
const signature = Array.from(sig, x => ('00' + x.toString(16)).slice(-2)).join('');
let r = signature.substr(0, signature.length / 2);
let s = signature.substr(signature.length / 2);
r = r.replace(/^(00)+/, '');
s = s.replace(/^(00)+/, '');
if ((parseInt(r, 16) & '0x80') > 0) r = `00${r}`;
if ((parseInt(s, 16) & '0x80') > 0) s = `00${s}`;
const rString = `02${(r.length / 2).toString(16).padStart(2, '0')}${r}`;
const sString = `02${(s.length / 2).toString(16).padStart(2, '0')}${s}`;
const derSig = `30${((rString.length + sString.length) / 2)
.toString(16).padStart(2, '0')}${rString}${sString}`;
return new Uint8Array(derSig.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16)));
}
/**
* Converts an ECDSA signature from DER to P1363 format
* source: https://developer.token.io/sdk/esdoc/file/Post_Quantum_Cryptography.PQC_Rainbow_Signature.src/security/CryptoBrowser.js.html
*
* @param {Uint8Array} sig - DER signature
* @return {Uint8Array} P1363 signature
* @private
*/
function derToP1363(sig) {
const signature = Array.from(sig, x => ('00' + x.toString(16)).slice(-2)).join('');
const rLength = parseInt(signature.substr(6, 2), 16) * 2;
let r = signature.substr(8, rLength);
let s = signature.substr(12 + rLength);
r = r.length > 64 ? r.substr(-64) : r.padStart(64, '0');
s = s.length > 64 ? s.substr(-64) : s.padStart(64, '0');
const p1363Sig = `${r}${s}`;
return new Uint8Array(p1363Sig.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16)));
}
/*
Fetch the contents of the "message" textbox, and encode it
in a form we can use for the encrypt operation.
*/
function getMessageEncoding() {
const messageBox = document.querySelector("#plaintext");
const message = messageBox.value;
const enc = new TextEncoder();
return enc.encode(message);
}
/*
Import a PEM encoded RSA public key, to use for RSA-OAEP encryption.
Takes a string containing the PEM encoded key, and returns a Promise
that will resolve to a CryptoKey representing the public key.
*/
function importPublicKey(pem) {
// fetch the part of the PEM string between header and footer
const pemHeader = "-----BEGIN PUBLIC KEY-----";
const pemFooter = "-----END PUBLIC KEY-----";
const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
// base64 decode the string to get the binary data
const binaryDerString = window.atob(pemContents);
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString);
return window.crypto.subtle.importKey(
"spki",
binaryDer,
{
name: "ECDSA",
namedCurve:"P-256",
hash: "SHA-256"
},
true,
["verify"]
);
}
/*
Fetch the encoded message-to-sign and verify it against the stored signature.
* If it checks out, set the "valid" class on the signature.
* Otherwise set the "invalid" class.
*/
async function verifyMessage() {
const signatureValue = derToP1363(b64ToBuf(document.getElementById("spki-message").value));
let encoded = getMessageEncoding();
let result = await window.crypto.subtle.verify(
{
name: "ECDSA",
hash: {name: "SHA-256"},
},
publicKey,
signatureValue,
encoded
);
document.getElementById("verificationstatus").value = result;
}
/*
Show and enable the verify button.
*/
function enableVerifyButton() {
verifyButton.classList.add('fade-in');
verifyButton.addEventListener('animationend', () => {
verifyButton.classList.remove('fade-in');
});
verifyButton.removeAttribute("disabled");
verifyButton.classList.remove("hidden");
}
/*
When the user clicks "Import Key"
- import the key
- enable the "Verify" button
*/
const importKeyButton = document.querySelector(".spki .import-key-button");
importKeyButton.addEventListener("click", async () => {
var pemEncodedKeyTextarea = document.getElementById("publickey-value").value;
publicKey = await importPublicKey(pemEncodedKeyTextarea);
enableVerifyButton();
});
verifyButton.addEventListener("click", verifyMessage);
</SCRIPT>
</html>