-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathquick RSA.html
754 lines (719 loc) · 46.2 KB
/
quick RSA.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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
<!DOCTYPE html>
<html>
<head>
<title>RSA-Gen</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {
color: white;
}
html {
background: rgb(48,48,48);
margin: 0;
}
html,body {
width: 100%;
}
#header {
text-align: center;
}
#inputContainer {
padding: 0 10%;
}
#messageContainer, #keyInputContainer, #tutorialDiv {
padding: 10px;
}
textarea, input {
background: rgb(24,24,24);
border: none;
outline:none;
padding: 10px;
margin: 10px 0;
}
textarea:focus, input:focus {
outline: 2px solid red;
}
button {
background: black;
border: 2px solid black;
padding: 10px;
transition-duration: 0.3s;
}
button:hover {
color: red;
border-color:red;
}
button:active {
color: rgb(0,255,0);
border-color: rgb(0,255,0);
transition-duration: 0s;
}
#keyInput , #messageInput, #publicKeyInput {
min-height: 12vh;
width: 80%;
box-sizing: border-box;
}
#keyStatus, #messageStatus {
width: 80%;
box-sizing: border-box;
padding: 10px;
min-height: 4vh;
}
details {
background: rgb(32,32,32);
margin: 10px 0;
}
summary {
padding: 10px;
font-size: 20px;
font-weight: bold;
border: 2px solid transparent;
transition-duration: 0.3s;
cursor: pointer;
}
summary:hover {
color: red;
border-color: red;
}
#passwordContainer {
display: none;
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background: rgba(128,128,128,0.3);
}
.passwordDialog {
box-sizing: border-box;
padding: 5vh;
background: rgb(32,32,32);
position: fixed;
top: 35vh;
height: 30vh;
width: 30vw;
left: 35vw;
text-align: center;
}
</style>
<script>
const privateFormat = "pkcs8";
const publicFormat = "spki";
const algName = "RSA-OAEP";
const alg = {
name: algName,
modulusLength: 6144,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
};
const algImport = {
name: algName,
hash: "SHA-256"
};
const actions = [
"encrypt",
"decrypt"
];
var keyPair;
var passwordResolve;
const privateKeyPrefix = "-----BEGIN PRIVATE KEY-----\n";
const privateKeySuffix = "\n-----END PRIVATE KEY-----";
const publicKeyPrefix = "-----BEGIN PUBLIC KEY-----\n";
const publicKeySuffix = "\n-----END PUBLIC KEY-----";
window.addEventListener('load', function () {
loadKeyPair();
document.getElementById('Generate').addEventListener('click', function () {
if (keyPair === undefined || confirm('You already have a Keypair. Do you want to create a new one?')) {
setKeyStatus('Generating...');
window.crypto.subtle.generateKey(
alg,
true,
actions,
).then(function (newKeyPair) {
saveKey(newKeyPair);
setKeyStatus('Keypair ready!');
});
}
});
document.getElementById('Clear').addEventListener('click', async function () {
if (confirm('Are you sure you want to clear your Keypair from this browser?')) {
keyPair = undefined;
window.localStorage.removeItem('keyPair');
setKeyStatus('No Keypair');
}
});
document.getElementById('Import').addEventListener('click', async function () {
var importString = document.getElementById('keyInput').value;
await importKeyPair(importString)
window.localStorage.setItem('keyPair', importString);
});
document.getElementById('Export').addEventListener('click', async function () {
var keyExport = window.localStorage.getItem('keyPair');
var keyInput = document.getElementById('keyInput');
keyInput.value = keyExport;
keyInput.select();
keyInput.setSelectionRange(0, 99999);
document.execCommand("copy");
setKeyStatus('Keypair exported and copied to clipboard');
});
document.getElementById('copyKey').addEventListener('click', async function () {
if (keyPair !== undefined) {
var keyExport = extractElementFromString(window.localStorage.getItem('keyPair'), "PUBLIC KEY");
keyExport = getElementString(keyExport, "PUBLIC KEY");
var keyInput = document.getElementById('keyInput');
keyInput.value = keyExport;
keyInput.select();
keyInput.setSelectionRange(0, 99999);
document.execCommand("copy");
setKeyStatus('Public Key exported and copied to clipboard');
} else {
setKeyStatus('No Keypair');
}
});
document.getElementById('Encrypt').addEventListener('click', async function () {
var input = document.getElementById('messageInput');
var public = await getRecipientKey();
var message = str2ab(input.value);
var crypt = await window.crypto.subtle.encrypt(alg, public, message);
input.value = window.btoa(ab2str(crypt));
input.select();
keyInput.setSelectionRange(0, 99999);
document.execCommand("copy");
setMessageStatus('Message Encrypted and copied to clipboard');
});
document.getElementById('Decrypt').addEventListener('click', async function () {
var input = document.getElementById('messageInput');
var crypt = str2ab(window.atob(input.value));
var message = await window.crypto.subtle.decrypt(alg, keyPair.privateKey, crypt);
input.value = ab2str(message);
setMessageStatus('Message Decrypted');
});
});
function setKeyStatus(status) {
document.getElementById('keyStatus').textContent = status;
}
function setMessageStatus(status) {
document.getElementById('messageStatus').textContent = status
}
async function saveKey(newKeyPair) {
if (newKeyPair !== undefined) {
keyPair = newKeyPair;
}
window.localStorage.setItem('keyPair', await exportKey(false));
}
function loadKeyPair() {
var keyPairJson = window.localStorage.getItem('keyPair');
if (keyPairJson !== null) {
importKeyPair(keyPairJson);
}
}
async function importKeyPair(importString) {
setKeyStatus('Importing Key...');
keyPair = {};
var privateKeyString = extractElementFromString(importString, "PRIVATE KEY")
// var privateKeyStart = importString.indexOf(privateKeyPrefix);
// var privateKeyStop = importString.indexOf(privateKeySuffix, privateKeyStart);
// var privateKeyString = importString.substring(privateKeyStart + privateKeyPrefix.length, privateKeyStop);
// var privateKeyData = str2ab(window.atob(privateKeyString));
var privateKeyData = await decryptWithPassword(privateKeyString);
var privateKey = await window.crypto.subtle.importKey(
privateFormat,
privateKeyData,
algImport,
true,
["decrypt"]
);
keyPair.privateKey = privateKey;
var publicKeyStart = importString.indexOf(publicKeyPrefix);
var publicKeyStop = importString.indexOf(publicKeySuffix, publicKeyStart);
var publicKeyString = importString.substring(publicKeyStart + publicKeyPrefix.length, publicKeyStop);
var publicKeyData = str2ab(window.atob(publicKeyString));
var publicKey = await window.crypto.subtle.importKey(
publicFormat,
publicKeyData,
algImport,
true,
["encrypt"]
);
keyPair.publicKey = publicKey;
setKeyStatus('Keypair ready!');
}
function getRecipientKey() {
var importString = document.getElementById('publicKeyInput').value;
var publicKeyStart = importString.indexOf(publicKeyPrefix);
var publicKeyStop = importString.indexOf(publicKeySuffix, publicKeyStart);
var publicKeyString = importString.substring(publicKeyStart + publicKeyPrefix.length, publicKeyStop);
var publicKeyData = str2ab(window.atob(publicKeyString));
return window.crypto.subtle.importKey(
publicFormat,
publicKeyData,
algImport,
true,
["encrypt"]
);
}
async function exportKey(publicOnly) {
if (keyPair !== undefined) {
var public = await window.crypto.subtle.exportKey(publicFormat, keyPair.publicKey);
public = window.btoa(ab2str(public));
public = publicKeyPrefix + public + publicKeySuffix;
var keyExport = public;
if (!publicOnly) {
var private = await window.crypto.subtle.exportKey(privateFormat, keyPair.privateKey);
private = await encryptWithPassword(private);
// private = window.btoa(ab2str(private));
private = getElementString(private, "PRIVATE KEY");
console.log(private);
keyExport += "\n" + private;
}
return keyExport;
}
}
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
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;
}
async function getKeyMaterial(password) {
if (password === undefined) {
document.getElementById('passwordContainer').setAttribute('style', 'display:block;');
password = await new Promise((resolve, reject) => {
passwordResolve = resolve;
});
document.getElementById('passwordContainer').removeAttribute('style');
}
return await window.crypto.subtle.importKey(
"raw",
str2ab(password),
"PBKDF2",
false,
["deriveBits", "deriveKey"]
);
}
function resolvePassword(e) {
console.log(e.type);
if (e.type === 'keyup') {
if (e.key !== 'Enter') {
return;
}
}
passwordResolve(document.getElementById('passwordInput').value);
}
async function getPasswordKey(password, salt) {
var keyMaterial = await getKeyMaterial(password);
return window.crypto.subtle.deriveKey(
{
"name": "PBKDF2",
"salt": salt,
"iterations": 100000,
"hash": "SHA-256"
},
keyMaterial,
{"name": "AES-GCM", "length": 256},
true,
["encrypt", "decrypt"]
);
}
async function encryptWithPassword(plaindata, password) {
var salt = crypto.getRandomValues(new Uint8Array(32));
var iv = crypto.getRandomValues(new Uint8Array(16));
var key = await getPasswordKey(password, salt);
var ciphertext = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv
},
key,
plaindata
);
var encodedMessage = getElementString(window.btoa(ab2str(salt)), "SALT");
encodedMessage += "\n" + getElementString(window.btoa(ab2str(iv)), "IV");
encodedMessage += "\n" + getElementString(window.btoa(ab2str(ciphertext)), "CIPHERDATA");
return encodedMessage;
}
async function decryptWithPassword(ciphertext, password) {
var iv = str2ab(window.atob(extractElementFromString(ciphertext, 'iv')))
var salt = str2ab(window.atob(extractElementFromString(ciphertext, 'salt')))
var key = await getPasswordKey(password, salt);
var cipherdata = str2ab(window.atob(extractElementFromString(ciphertext, 'CIPHERDATA')))
var plaindata = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv
},
key,
cipherdata
);
return plaindata;
}
function extractElementFromString(string, element) {
var startMarker = "-----BEGIN " + element.toUpperCase() + "-----\n";
var start = string.indexOf(startMarker);
var stop = string.indexOf("\n-----END " + element.toUpperCase() + "-----");
return string.substring(start + startMarker.length, stop);
}
function getElementString(string, element) {
return "-----BEGIN " + element.toUpperCase() + "-----\n" +
string +
"\n-----END " + element.toUpperCase() + "-----";
}
</script>
</head>
<body>
<div>
<div id="header">
<img src="data:image/png;base64,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
SgAAAABJRU5ErkJggg==
">
<h1 id = "headline">
acul's quick RSA
</h1>
</div>
</div>
<div id="inputContainer">
<details>
<summary>Tutorial</summary>
<div id="tutorialDiv">
<h3>Sending Messages</h3>
<p>1) Ask the person you want to send a message to for their public key
<p>2) Paste the public key into the upper textarea in the encryption section
<p>3) Write your message into the lower textarea
<p>4) Click on encrypt
<p>5) Send the encrypted message to the recipient
<h3>Receiving Messages</h3>
<p>1) Open the keypair settings and generate a key
<p>2) Click on copy public key
<p>3) Send the public key to the sender
<p>4) Paste the encrypted message into the lower textarea in the encryption section
<p>5) Click on decrypt
</div>
</details>
<details>
<summary>Keypair Settings</summary>
<div id="keyInputContainer">
<div id="keyStatus">No Keypair</div>
<textarea id="keyInput"></textarea>
<div id="keyControl">
<button id="copyKey">Copy Public Key</button>
<button id="Generate">Generate Keypair</button>
<button id="Export">Export Keypair</button>
<button id="Import">Import Keypair</button>
<button id="Clear">Clear Key</button>
</div>
<div id="keyControl"></div>
</div>
</details>
<details>
<summary>Message Encryption</summary>
<div id="messageContainer">
<h3>Public Key of Recipient (only needed for Encryption)</h3>
<textarea id="publicKeyInput"></textarea>
<h3>Message</h3>
<div id="messageStatus"></div>
<textarea id="messageInput"></textarea>
<div id="messageControl">
<button id="Encrypt">Encrypt</button>
<button id="Decrypt">Decrypt</button>
</div>
</div>
</details>
</div>
<div id="passwordContainer">
<div class="passwordDialog">
<h2>Enter Password</h2>
<div>
<input id="passwordInput" type="password" onkeyup="resolvePassword(event);">
</div>
<button id="passwordConfirm" onclick="resolvePassword(event);" >Confirm</button>
</div>
</div>
</body>
</html>