133 lines
3.7 KiB
TypeScript
133 lines
3.7 KiB
TypeScript
import * as crypto from "crypto";
|
|
import { Convert } from "pvtsutils";
|
|
import * as core from "webcrypto-core";
|
|
import { RsaCrypto } from "./crypto";
|
|
import { RsaPrivateKey } from "./private_key";
|
|
import { RsaPublicKey } from "./public_key";
|
|
import { setCryptoKey, getCryptoKey } from "../storage";
|
|
|
|
export class RsaEsProvider extends core.ProviderCrypto {
|
|
public name = "RSAES-PKCS1-v1_5";
|
|
public usages = {
|
|
publicKey: ["encrypt", "wrapKey"] as core.KeyUsages,
|
|
privateKey: ["decrypt", "unwrapKey"] as core.KeyUsages,
|
|
};
|
|
|
|
public override async onGenerateKey(
|
|
algorithm: RsaKeyGenParams,
|
|
extractable: boolean,
|
|
keyUsages: KeyUsage[]
|
|
): Promise<CryptoKeyPair> {
|
|
const keys = await RsaCrypto.generateKey(
|
|
{
|
|
...algorithm,
|
|
name: this.name,
|
|
},
|
|
extractable,
|
|
keyUsages
|
|
);
|
|
|
|
return {
|
|
privateKey: setCryptoKey(keys.privateKey as RsaPrivateKey),
|
|
publicKey: setCryptoKey(keys.publicKey as RsaPublicKey),
|
|
};
|
|
}
|
|
|
|
public override checkGenerateKeyParams(algorithm: RsaKeyGenParams) {
|
|
// public exponent
|
|
this.checkRequiredProperty(algorithm, "publicExponent");
|
|
if (
|
|
!(
|
|
algorithm.publicExponent &&
|
|
algorithm.publicExponent instanceof Uint8Array
|
|
)
|
|
) {
|
|
throw new TypeError("publicExponent: Missing or not a Uint8Array");
|
|
}
|
|
const publicExponent = Convert.ToBase64(algorithm.publicExponent);
|
|
if (!(publicExponent === "Aw==" || publicExponent === "AQAB")) {
|
|
throw new TypeError("publicExponent: Must be [3] or [1,0,1]");
|
|
}
|
|
|
|
// modulus length
|
|
this.checkRequiredProperty(algorithm, "modulusLength");
|
|
switch (algorithm.modulusLength) {
|
|
case 1024:
|
|
case 2048:
|
|
case 4096:
|
|
break;
|
|
default:
|
|
throw new TypeError("modulusLength: Must be 1024, 2048, or 4096");
|
|
}
|
|
}
|
|
|
|
public override async onEncrypt(
|
|
algorithm: Algorithm,
|
|
key: RsaPublicKey,
|
|
data: ArrayBuffer
|
|
): Promise<ArrayBuffer> {
|
|
const options = this.toCryptoOptions(key);
|
|
const enc = crypto.publicEncrypt(options, new Uint8Array(data));
|
|
return new Uint8Array(enc).buffer;
|
|
}
|
|
|
|
public override async onDecrypt(
|
|
algorithm: Algorithm,
|
|
key: RsaPrivateKey,
|
|
data: ArrayBuffer
|
|
): Promise<ArrayBuffer> {
|
|
const options = this.toCryptoOptions(key);
|
|
const dec = crypto.privateDecrypt(options, new Uint8Array(data));
|
|
return new Uint8Array(dec).buffer;
|
|
}
|
|
|
|
public override async onExportKey(
|
|
format: KeyFormat,
|
|
key: CryptoKey
|
|
): Promise<JsonWebKey | ArrayBuffer> {
|
|
return RsaCrypto.exportKey(format, getCryptoKey(key));
|
|
}
|
|
|
|
public override async onImportKey(
|
|
format: KeyFormat,
|
|
keyData: JsonWebKey | ArrayBuffer,
|
|
algorithm: RsaHashedImportParams,
|
|
extractable: boolean,
|
|
keyUsages: KeyUsage[]
|
|
): Promise<CryptoKey> {
|
|
const key = await RsaCrypto.importKey(
|
|
format,
|
|
keyData,
|
|
{ ...algorithm, name: this.name },
|
|
extractable,
|
|
keyUsages
|
|
);
|
|
return setCryptoKey(key);
|
|
}
|
|
|
|
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
|
|
super.checkCryptoKey(key, keyUsage);
|
|
const internalKey = getCryptoKey(key);
|
|
if (
|
|
!(
|
|
internalKey instanceof RsaPrivateKey ||
|
|
internalKey instanceof RsaPublicKey
|
|
)
|
|
) {
|
|
throw new TypeError("key: Is not RSA CryptoKey");
|
|
}
|
|
}
|
|
|
|
private toCryptoOptions(key: RsaPrivateKey): crypto.RsaPrivateKey;
|
|
private toCryptoOptions(key: RsaPublicKey): crypto.RsaPublicKey;
|
|
private toCryptoOptions(key: RsaPrivateKey | RsaPublicKey) {
|
|
const type = key.type.toUpperCase();
|
|
return {
|
|
key: `-----BEGIN ${type} KEY-----\n${getCryptoKey(key).data.toString(
|
|
"base64"
|
|
)}\n-----END ${type} KEY-----`,
|
|
padding: crypto.constants.RSA_PKCS1_PADDING,
|
|
};
|
|
}
|
|
}
|