-
Zdravko Iliev authoredZdravko Iliev authored
CryptoServiceNode.ts 5.55 KiB
// All functions are async in order to be compatible with the ICryptoService API
import md5 from "js-md5";
import * as crypto from "crypto";
import { AESGCMOutput, RSAKeys, ICryptoService } from "./ICryptoService";
import { ensureUint8Array } from "../../utils/common";
const AES_GCM_ALGO = "aes-256-gcm";
const getBytes = (
value: string | ArrayBuffer,
encoding: BufferEncoding
): Buffer => {
let bytes;
if (typeof value === "string") {
const valueString = value as string;
bytes = Buffer.from(valueString, encoding);
} else {
bytes = Buffer.from(value);
}
return bytes;
};
class CryptoServiceNode implements ICryptoService {
public async encryptAESGCM(data: string): Promise<AESGCMOutput>;
public async encryptAESGCM(data: ArrayBuffer): Promise<AESGCMOutput>;
public async encryptAESGCM(
data: string | ArrayBuffer,
key?: Buffer,
iv?: Buffer
): Promise<AESGCMOutput> {
if (!key && !iv) {
key = crypto.randomBytes(32);
iv = crypto.randomBytes(12);
}
const cipher = crypto.createCipheriv(AES_GCM_ALGO, key, iv);
let encrypted;
if (typeof data === "string") {
encrypted = cipher.update(data, "utf8");
} else {
encrypted = cipher.update(ensureUint8Array(data));
}
cipher.final();
const authTag = cipher.getAuthTag();
const encryptedWithTag = Buffer.concat([
Buffer.from(encrypted),
Buffer.from(authTag),
]);
return {
key: key,
iv: iv,
data: encryptedWithTag,
};
}
public async decryptAESGCM(
data: ArrayBuffer,
key: ArrayBuffer,
iv: ArrayBuffer
): Promise<string>;
public async decryptAESGCM(
data: ArrayBuffer,
key: ArrayBuffer,
iv: ArrayBuffer,
returnBuffer: true
): Promise<ArrayBuffer>;
public async decryptAESGCM(
data: ArrayBuffer,
key: ArrayBuffer,
iv: ArrayBuffer,
returnBuffer?: boolean
): Promise<string | ArrayBuffer> {
const decipher = crypto.createDecipheriv(
AES_GCM_ALGO,
Buffer.from(key),
Buffer.from(iv)
);
const authTag = data.slice(data.byteLength - 16, data.byteLength);
const encrypted = data.slice(0, data.byteLength - 16);
if (returnBuffer) {
const decrypted: Buffer = decipher.update(ensureUint8Array(encrypted));
decipher.setAuthTag(Buffer.from(authTag));
return Buffer.concat([decrypted, decipher.final()]);
}
let str = decipher.update(ensureUint8Array(encrypted), null, "utf8");
decipher.setAuthTag(Buffer.from(authTag));
str += decipher.final("utf8");
return str;
}
public async generateRSAKeys(): Promise<RSAKeys> {
const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
modulusLength: 4096,
publicKeyEncoding: {
type: "spki",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem",
},
});
return {
publicKeyPEM: publicKey,
privateKeyPEM: privateKey,
};
}
public async encryptRSA(
publicKeyPEM: string,
data: ArrayBuffer
): Promise<ArrayBuffer> {
const encryptedData = crypto.publicEncrypt(
{
key: publicKeyPEM,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
// We convert the data string to a buffer using `Buffer.from`
Buffer.from(data)
);
return encryptedData.buffer;
}
public async decryptRSA(
privateKeyPEM: string,
data: ArrayBuffer
): Promise<ArrayBuffer> {
const encryptedData = crypto.privateDecrypt(
{
key: privateKeyPEM,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
// We convert the data string to a buffer using `Buffer.from`
Buffer.from(data)
);
return encryptedData.buffer;
}
public async signRSA(
privateKeyPEM: string,
data: ArrayBuffer
): Promise<ArrayBuffer> {
const sign = crypto.createSign("SHA256");
sign.update(Buffer.from(data));
sign.end();
return sign.sign(privateKeyPEM).buffer;
}
public async SHA1(
value: string | ArrayBuffer,
encoding: BufferEncoding = "utf8"
): Promise<ArrayBuffer> {
const bytes = getBytes(value, encoding);
return crypto.createHash("sha1").update(bytes).digest();
}
public async SHA256(
value: string | ArrayBuffer,
encoding: BufferEncoding = "utf8"
): Promise<ArrayBuffer> {
const bytes = getBytes(value, encoding);
return crypto.createHash("sha256").update(bytes).digest();
}
public async SHA384(
value: string | ArrayBuffer,
encoding: BufferEncoding = "utf8"
): Promise<ArrayBuffer> {
const bytes = getBytes(value, encoding);
return crypto.createHash("sha384").update(bytes).digest();
}
public async SHA512(
value: string | ArrayBuffer,
encoding: BufferEncoding = "utf8"
): Promise<ArrayBuffer> {
const bytes = getBytes(value, encoding);
return crypto.createHash("sha512").update(bytes).digest();
}
public async MD5(
value: string | ArrayBuffer,
encoding: BufferEncoding = "utf8"
): Promise<ArrayBuffer> {
const bytes = getBytes(value, encoding);
return md5.arrayBuffer(bytes);
}
public async verifyRSASignature(
publicKeyPEM: string,
data: ArrayBuffer,
signature: ArrayBuffer
): Promise<boolean> {
const verify = crypto.createVerify("SHA256");
verify.update(Buffer.from(data));
verify.end();
const publicKey = crypto.createPublicKey(
Buffer.from(publicKeyPEM, "utf-8")
);
return verify.verify(publicKey, Buffer.from(signature));
}
}
export default CryptoServiceNode;