Skip to content

Instantly share code, notes, and snippets.

@reosablo
Last active May 19, 2023 08:35
Show Gist options
  • Select an option

  • Save reosablo/d620ffaec1485116a7d5da2cd9226de5 to your computer and use it in GitHub Desktop.

Select an option

Save reosablo/d620ffaec1485116a7d5da2cd9226de5 to your computer and use it in GitHub Desktop.
Equivalent code set for Web Crypto API and `node-forge` module
#!/usr/bin/env node
// @ts-check
/**
* @file Decrypts data from stdin using a private key.
*
* @example
* node decrypt-forge.mjs [private-key-path] < encrypted-data.txt
*
* The default private key path is "private-key.pem".
* The stdin data is expected to be base64 encoded.
* The decrypted data is written to stdout.
*/
import forge from "node-forge";
import { readFile } from "node:fs/promises";
import process from "node:process";
import { text } from "node:stream/consumers";
const [, , privateKeyPath = "private-key.pem"] = process.argv;
const [data, privateKeyPem] = await Promise.all([
text(process.stdin)
.then((input) => forge.util.decode64(input)),
readFile(privateKeyPath, "utf8"),
]);
const decrypted = forge.pki
.privateKeyFromPem(privateKeyPem)
.decrypt(data, "RSA-OAEP", { md: forge.md.sha512.create() });
process.stdout.write(decrypted);
#!/usr/bin/env node
// @ts-check
/**
* @file Decrypts data from stdin using a private key.
*
* @example
* node decrypt-web.mjs [private-key-path] < encrypted-data.txt
*
* The default private key path is "private-key.pem".
* The stdin data is expected to be base64 encoded.
* The decrypted data is written to stdout.
*/
import { Buffer } from "node:buffer";
import { webcrypto as crypto } from "node:crypto";
import { readFile } from "node:fs/promises";
import process from "node:process";
import { text } from "node:stream/consumers";
const [, , privateKeyPath = "private-key.pem"] = process.argv;
const [data, privateKey] = await Promise.all([
text(process.stdin)
.then((input) => Buffer.from(input, "base64")),
readFile(privateKeyPath, "utf8")
.then((pem) => {
const encodedDer = pem.replace(/-+[\w ]+-+|\s/g, "");
const der = Buffer.from(encodedDer, "base64");
return crypto.subtle.importKey(
"pkcs8",
der,
{ name: "RSA-OAEP", hash: "SHA-512" },
true,
["decrypt"],
);
}),
]);
const decrypted = await crypto.subtle.decrypt(
{ name: "RSA-OAEP" },
privateKey,
data,
);
process.stdout.write(Buffer.from(decrypted).toString());
#!/usr/bin/env node
// @ts-check
/**
* @file Encrypts data from stdin using a public key.
*
* @example
* node encrypt-forge.mjs [public-key-path] > encrypted-data.txt
*
* The default public key path is "public-key.pem".
* The stdout data is base64 encoded.
* The encrypted data is written to stdout.
*/
import forge from "node-forge";
import { readFile } from "node:fs/promises";
import process from "node:process";
import { text } from "node:stream/consumers";
const [, , publicKeyPath = "public-key.pem"] = process.argv;
const [data, publicKeyPem] = await Promise.all([
process.stdin.isTTY ? Date() : text(process.stdin),
readFile(publicKeyPath, "utf8"),
]);
const encrypted = forge.pki
.publicKeyFromPem(publicKeyPem)
.encrypt(data, "RSA-OAEP", { md: forge.md.sha512.create() });
process.stdout.write(forge.util.encode64(encrypted));
#!/usr/bin/env node
// @ts-check
/**
* @file Encrypts data from stdin using a public key.
*
* @example
* node encrypt-web.mjs [public-key-path] > encrypted-data.txt
*
* The default public key path is "public-key.pem".
* The stdout data is base64 encoded.
* The encrypted data is written to stdout.
*/
import { Buffer } from "node:buffer";
import { webcrypto as crypto } from "node:crypto";
import { readFile } from "node:fs/promises";
import process from "node:process";
import { buffer } from "node:stream/consumers";
const [, , publicKeyPath = "public-key.pem"] = process.argv;
const [data, publicKey] = await Promise.all([
process.stdin.isTTY ? Buffer.from(Date()) : buffer(process.stdin),
readFile(publicKeyPath, "utf8")
.then((pem) => {
const encodedDer = pem.replace(/-+[\w ]+-+|\s/g, "");
const der = Buffer.from(encodedDer, "base64");
return crypto.subtle.importKey(
"spki",
der,
{ name: "RSA-OAEP", hash: "SHA-512" },
true,
["encrypt"],
);
}),
]);
const encrypted = await crypto.subtle.encrypt(
{ name: "RSA-OAEP" },
publicKey,
data,
);
process.stdout.write(Buffer.from(encrypted).toString("base64"));
#!/usr/bin/env node
// @ts-check
/**
* @file Generates a RSA-OAEP/SHA-512 key pair files.
*
* @example
* node generate-key-pair-forge.mjs [public-key-path] [private-key-path]
*
* The default public key path is "public-key.pem".
* The default private key path is "private-key.pem".
*/
import forge from "node-forge";
import { writeFile } from "node:fs/promises";
import process from "node:process";
const [
,
,
publicKeyPath = "public-key.pem",
privateKeyPath = "private-key.pem",
] = process.argv;
const { privateKey, publicKey } = forge.pki.rsa.generateKeyPair({ bits: 2048 });
const publicKeyPem = forge.pki.publicKeyToPem(publicKey);
const rsaPrivateKey = forge.pki.privateKeyToAsn1(privateKey);
const privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey);
const privateKeyPem = forge.pki.privateKeyInfoToPem(privateKeyInfo);
await Promise.all([
writeFile(publicKeyPath, publicKeyPem, "utf8"),
writeFile(privateKeyPath, privateKeyPem, "utf8"),
]);
#!/usr/bin/env node
// @ts-check
/**
* @file Generates a RSA-OAEP/SHA-512 key pair files.
*
* @example
* node generate-key-pair-web.mjs [public-key-path] [private-key-path]
*
* The default public key path is "public-key.pem".
* The default private key path is "private-key.pem".
*/
import { Buffer } from "node:buffer";
import { webcrypto as crypto } from "node:crypto";
import { writeFile } from "node:fs/promises";
import process from "node:process";
const [
,
,
publicKeyPath = "public-key.pem",
privateKeyPath = "private-key.pem",
] = process.argv;
const { privateKey, publicKey } = await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["encrypt", "decrypt"],
);
await Promise.all(/**@type {const}*/([
["PRIVATE KEY", crypto.subtle.exportKey("pkcs8", privateKey), privateKeyPath],
["PUBLIC KEY", crypto.subtle.exportKey("spki", publicKey), publicKeyPath],
]).map(async ([type, derPromise, path]) => {
const encodedDer = Buffer.from(await derPromise).toString("base64");
const pem = [
`-----BEGIN ${type}-----`,
encodedDer.replace(/.{1,64}/g, "$&\n"),
`-----END ${type}-----`,
].join("\n");
await writeFile(path, pem, "utf8");
}));
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true
}
}
{
"private": true,
"description": "node-forge and Web Crypto API RSA-OAEP demo for Node.js",
"license": "Unlicense",
"scripts": {
"demo-forge": "node -p \"crypto.randomUUID()\" | node encrypt-forge.mjs | node decrypt-forge.mjs",
"demo-web": "node -p \"crypto.randomUUID()\" | node encrypt-web.mjs | node decrypt-web.mjs",
"prepare-forge": "node generate-key-pair-forge.mjs",
"prepare-web": "node generate-key-pair-web.mjs"
},
"dependencies": {
"node-forge": "^1.3.1"
},
"devDependencies": {
"@types/node-forge": "^1.3.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment