master
Armored Dragon 2023-11-24 00:59:09 -06:00
parent 0e3e3b2232
commit df8292832a
6 changed files with 389 additions and 2 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.vscode/settings.json

View File

@ -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.

103
index.css Normal file
View File

@ -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%;
}
}

74
index.html Normal file
View File

@ -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>

92
index.js Normal file
View File

@ -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));
}

117
index.scss Normal file
View File

@ -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%;
}
}
}
}