Init
parent
0e3e3b2232
commit
df8292832a
|
@ -0,0 +1 @@
|
||||||
|
.vscode/settings.json
|
|
@ -1,3 +1,3 @@
|
||||||
# Website-keysign-example
|
# Website Key signing and Encryption example
|
||||||
|
|
||||||
An example showcasing basic cryptography in-browser without using third-party libraries.
|
An example website showcasing basic cryptography in-browser without using third-party libraries.
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #20242b;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.page .disclaimer {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.page .data-area {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 10px;
|
||||||
|
background-color: #0d0f11;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.page .data-area .data-title {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.page .data-area textarea {
|
||||||
|
resize: vertical;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.page .action-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #0d0f11;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.page .action-area button {
|
||||||
|
width: 130px;
|
||||||
|
height: 100%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.page .result-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
place-content: space-around;
|
||||||
|
}
|
||||||
|
.page .result-area .result {
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 49%;
|
||||||
|
flex-grow: 1;
|
||||||
|
min-height: 100px;
|
||||||
|
background-color: #0d0f11;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: gray;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
word-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.page .result-area .result .result-title {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1210px) {
|
||||||
|
.page {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
.page .result-area .result {
|
||||||
|
min-width: initial;
|
||||||
|
max-width: initial;
|
||||||
|
width: 45%;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 1010px) {
|
||||||
|
.page {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
.page .result-area .result {
|
||||||
|
min-width: initial;
|
||||||
|
max-width: initial;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||||
|
<script src="index.js"></script>
|
||||||
|
<title>In-Browser Keysigning</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page">
|
||||||
|
<div class="disclaimer data-area">
|
||||||
|
This online key signing and encryption example should
|
||||||
|
<u> NEVER </u>
|
||||||
|
be used in a production environment. This was made for educational purposes in exploring the use of cryptography in the web without the use of
|
||||||
|
third-party libraries. You can find the source code for this website <a href="https://armored-dragon.github.io/Website-keysign-example/index">here</a>.
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="disclaimer data-area">
|
||||||
|
The resulting data is manipulated to allow for viewing in-line in the browser so the relationships can be more easily observed.
|
||||||
|
</div>
|
||||||
|
<div class="data-area">
|
||||||
|
<div class="data-title">Your unencrypted text</div>
|
||||||
|
<textarea id="my-secret-string">I want to keep this message a secret! </textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action-area">
|
||||||
|
<button onclick="generateRSAKeyPair()"><span>Generate Keys</span></button>
|
||||||
|
<button onclick="executeTests()"><span>Execute Tests</span></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result-area">
|
||||||
|
<div class="result">
|
||||||
|
<div class="result-title">Encrypted String</div>
|
||||||
|
<div id="encrypted-string"></div>
|
||||||
|
</div>
|
||||||
|
<div class="result">
|
||||||
|
<div class="result-title">Decrypted String</div>
|
||||||
|
<div id="decrypted-string"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result">
|
||||||
|
<div class="result-title">Signature String</div>
|
||||||
|
<div id="signature-string"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result">
|
||||||
|
<div class="result-title">Public Key (Encryption)</div>
|
||||||
|
<div id="pub-key-enc"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result">
|
||||||
|
<div class="result-title">Private Key (Encryption)</div>
|
||||||
|
<div id="priv-key-enc"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result">
|
||||||
|
<div class="result-title">Public Key (Signing)</div>
|
||||||
|
<div id="pub-key-sign"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result">
|
||||||
|
<div class="result-title">Private Key (Signing)</div>
|
||||||
|
<div id="priv-key-sign"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result">
|
||||||
|
<div class="result-title">Results</div>
|
||||||
|
<div id="results"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,92 @@
|
||||||
|
let public_key;
|
||||||
|
let private_key;
|
||||||
|
|
||||||
|
let public_signing_key;
|
||||||
|
let private_signing_key;
|
||||||
|
|
||||||
|
const qs = (selector) => document.querySelector(selector);
|
||||||
|
|
||||||
|
async function generateRSAKeyPair() {
|
||||||
|
// From what I can gather from online sources,
|
||||||
|
// the in browser crypto module can not generate keys that can be used for both signing and encryption.
|
||||||
|
// For this example we will just generate two key pairs, and use them for their recommended purpose.
|
||||||
|
|
||||||
|
// Create Encryption/Decryption keypair
|
||||||
|
const encryption_keypair = await window.crypto.subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: "RSA-OAEP",
|
||||||
|
modulusLength: 2048,
|
||||||
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
|
||||||
|
hash: "SHA-256",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
["encrypt", "decrypt"]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create signing keypair
|
||||||
|
const signing_keypair = await window.crypto.subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: "RSASSA-PKCS1-v1_5",
|
||||||
|
modulusLength: 2048,
|
||||||
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
|
||||||
|
hash: "SHA-256",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
["sign", "verify"]
|
||||||
|
);
|
||||||
|
|
||||||
|
// These keys will be used to encrypt data
|
||||||
|
public_key = encryption_keypair.publicKey;
|
||||||
|
private_key = encryption_keypair.privateKey;
|
||||||
|
|
||||||
|
// These keys will be used to "sign" messages
|
||||||
|
public_signing_key = signing_keypair.publicKey;
|
||||||
|
private_signing_key = signing_keypair.privateKey;
|
||||||
|
|
||||||
|
// Update displays
|
||||||
|
qs("#pub-key-enc").innerText = await _exportKey(public_key, "spki");
|
||||||
|
qs("#priv-key-enc").innerText = await _exportKey(private_key, "jwk");
|
||||||
|
|
||||||
|
qs("#pub-key-sign").innerText = await _exportKey(public_signing_key, "spki");
|
||||||
|
qs("#priv-key-sign").innerText = await _exportKey(private_signing_key, "jwk");
|
||||||
|
}
|
||||||
|
async function executeTests() {
|
||||||
|
const user_string = document.querySelector("#my-secret-string").value;
|
||||||
|
|
||||||
|
// Get our data with our generated keys and user-submitted text
|
||||||
|
const encrypted_string = await encryptString(user_string);
|
||||||
|
const decrypted_string = await decryptString(encrypted_string);
|
||||||
|
const signed_message = await signMessage(user_string);
|
||||||
|
const message_verified = await verifyMessage(user_string, signed_message);
|
||||||
|
|
||||||
|
// Generate Results
|
||||||
|
let results = `Message Signature Verified? ${message_verified}<br>`;
|
||||||
|
|
||||||
|
qs("#encrypted-string").innerText = new Uint8Array(encrypted_string).toString();
|
||||||
|
qs("#decrypted-string").innerText = decrypted_string;
|
||||||
|
qs("#signature-string").innerText = new Uint8Array(signed_message).toString();
|
||||||
|
qs("#results").innerHTML = results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function encryptString(string_d) {
|
||||||
|
return await window.crypto.subtle.encrypt({ name: "RSA-OAEP" }, public_key, new TextEncoder().encode(string_d));
|
||||||
|
}
|
||||||
|
async function decryptString(string_d) {
|
||||||
|
return new TextDecoder().decode(await window.crypto.subtle.decrypt({ name: "RSA-OAEP" }, private_key, string_d));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signMessage(string_d) {
|
||||||
|
return await window.crypto.subtle.sign({ name: "RSASSA-PKCS1-v1_5" }, private_signing_key, new TextEncoder().encode(string_d));
|
||||||
|
}
|
||||||
|
async function verifyMessage(string_d, signed_message) {
|
||||||
|
return await window.crypto.subtle.verify("RSASSA-PKCS1-v1_5", public_signing_key, signed_message, new TextEncoder().encode(string_d));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is just used to turn the data into string that can be read.
|
||||||
|
// Ordinarily you won't need this, as the keys won't need to be visible in the way this website wants them to be.
|
||||||
|
// (Hint, this is very bad! Do not do this!)
|
||||||
|
async function _exportKey(key, method) {
|
||||||
|
const exported = await window.crypto.subtle.exportKey(method, key);
|
||||||
|
if (exported.e) return JSON.stringify(exported, null, 2);
|
||||||
|
return String.fromCharCode.apply(null, new Uint8Array(exported));
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #20242b;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
.disclaimer {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-area {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 10px;
|
||||||
|
background-color: #0d0f11;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
.data-title {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #0d0f11;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 130px;
|
||||||
|
height: 100%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
place-content: space-around;
|
||||||
|
|
||||||
|
.result {
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 49%;
|
||||||
|
flex-grow: 1;
|
||||||
|
min-height: 100px;
|
||||||
|
background-color: #0d0f11;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: gray;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
word-wrap: anywhere;
|
||||||
|
|
||||||
|
.result-title {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1210px) {
|
||||||
|
.page {
|
||||||
|
width: 95%;
|
||||||
|
.result-area {
|
||||||
|
.result {
|
||||||
|
min-width: initial;
|
||||||
|
max-width: initial;
|
||||||
|
width: 45%;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1010px) {
|
||||||
|
.page {
|
||||||
|
width: 95%;
|
||||||
|
.result-area {
|
||||||
|
.result {
|
||||||
|
min-width: initial;
|
||||||
|
max-width: initial;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue