Open kacper43 opened 3 years ago
@kacper43 Do you use NodeJS or Browser runtime?
@microshine browser
@kacper43 Could you say which algorithms throw that exception?
Here is a simplified JS file that allows run tests in NodeJS
/* eslint-disable */
import assert from "assert";
import * as asn1js from "asn1js";
import { Crypto } from "@peculiar/webcrypto";
import * as utils from "pvutils";
import * as pkijs from "./src/index.js";
const name = "@peculiar/webcrypto";
const crypto = new Crypto();
pkijs.setEngine(name, new pkijs.CryptoEngine({ name, crypto, subtle: crypto.subtle }), new pkijs.CryptoEngine({ name, crypto, subtle: crypto.subtle }));
let hashAlg = "SHA-1";
let signAlg = "RSASSA-PKCS1-v1_5";
let oaepHashAlg = "SHA-1";
const encAlg = {
name: "AES-CBC",
length: 128
};
//#region Create CERT
async function createCertificateInternal() {
const certificate = new pkijs.Certificate();
//#region Put a static values
certificate.version = 2;
certificate.serialNumber = new asn1js.Integer({ value: 1 });
certificate.issuer.typesAndValues.push(new pkijs.AttributeTypeAndValue({
type: "2.5.4.6", // Country name
value: new asn1js.PrintableString({ value: "RU" })
}));
certificate.issuer.typesAndValues.push(new pkijs.AttributeTypeAndValue({
type: "2.5.4.3", // Common name
value: new asn1js.BmpString({ value: "Test" })
}));
certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
type: "2.5.4.6", // Country name
value: new asn1js.PrintableString({ value: "RU" })
}));
certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
type: "2.5.4.3", // Common name
value: new asn1js.BmpString({ value: "Test" })
}));
certificate.notBefore.value = new Date(2016, 1, 1);
certificate.notAfter.value = new Date(2019, 1, 1);
certificate.extensions = []; // Extensions are not a part of certificate by default, it's an optional array
//#region "BasicConstraints" extension
const basicConstr = new pkijs.BasicConstraints({
cA: true,
pathLenConstraint: 3
});
certificate.extensions.push(new pkijs.Extension({
extnID: "2.5.29.19",
critical: true,
extnValue: basicConstr.toSchema().toBER(false),
parsedValue: basicConstr // Parsed value for well-known extensions
}));
//#endregion
//#region "KeyUsage" extension
const bitArray = new ArrayBuffer(1);
const bitView = new Uint8Array(bitArray);
bitView[0] |= 0x02; // Key usage "cRLSign" flag
bitView[0] |= 0x04; // Key usage "keyCertSign" flag
const keyUsage = new asn1js.BitString({ valueHex: bitArray });
certificate.extensions.push(new pkijs.Extension({
extnID: "2.5.29.15",
critical: false,
extnValue: keyUsage.toBER(false),
parsedValue: keyUsage // Parsed value for well-known extensions
}));
//#endregion
//#endregion
//#region Create a new key pair
//#region Get default algorithm parameters for key generation
const algorithm = pkijs.getAlgorithmParameters(signAlg, "generatekey");
if ("hash" in algorithm.algorithm)
algorithm.algorithm.hash.name = hashAlg;
//#endregion
const { publicKey, privateKey } = await crypto.subtle.generateKey(algorithm.algorithm, true, algorithm.usages);
//#endregion
//#region Exporting public key into "subjectPublicKeyInfo" value of certificate
await certificate.subjectPublicKeyInfo.importKey(publicKey)
//#endregion
//#region Signing final certificate
await certificate.sign(privateKey, hashAlg);
//#endregion
return {
certificate,
privateKey,
publicKey
}
}
//#endregion
//#region Encrypt input data
async function envelopedEncryptInternal(cert, valueBuffer) {
const cmsEnveloped = new pkijs.EnvelopedData({
originatorInfo: new pkijs.OriginatorInfo({
certs: new pkijs.CertificateSet({
certificates: [cert.certificate]
})
})
});
cmsEnveloped.addRecipientByCertificate(cert.certificate, { oaepHashAlgorithm: oaepHashAlg });
await cmsEnveloped.encrypt(encAlg, valueBuffer);
const cmsContentSimpl = new pkijs.ContentInfo();
cmsContentSimpl.contentType = "1.2.840.113549.1.7.3";
cmsContentSimpl.content = cmsEnveloped.toSchema();
return cmsContentSimpl;
}
//#endregion
//#region Decrypt input data
async function envelopedDecryptInternal(cert, cms) {
//#region Decode CMS Enveloped content
const cmsEnvelopedSimp = new pkijs.EnvelopedData({ schema: cms.content });
//#endregion
const result = await cmsEnvelopedSimp.decrypt(0,
{
recipientCertificate: cert.certificate,
recipientPrivateKey: await crypto.subtle.exportKey("pkcs8", cert.privateKey)
});
return result;
}
//#endregion
context("How To Encrypt CMS via Certificate", () => {
//#region Initial variables
const hashAlgs = ["SHA-1", "SHA-256", "SHA-384", "SHA-512"];
const oaepHashAlgs = ["SHA-1", "SHA-256", "SHA-384", "SHA-512"];
const signAlgs = ["RSASSA-PKCS1-V1_5", "ECDSA", "RSA-PSS"];
const encAlgs = ["AES-CBC", "AES-GCM"];
const encLens = [128, 192, 256];
// const hashAlgs = ["SHA-512"];
// const oaepHashAlgs = ["SHA-1"];
// const signAlgs = ["ECDSA"];
// const encAlgs = ["AES-GCM"];
// const encLens = [256];
const iterations = 1;
const valueBuffer = (new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09])).buffer;
//#endregion
encAlgs.forEach(_encAlg => {
encLens.forEach(_encLen => {
signAlgs.forEach(_signAlg => {
hashAlgs.forEach(_hashAlg => {
oaepHashAlgs.forEach(_oaepHashAlg => {
const testName = `${_encAlg} with ${_encLen}, ${_hashAlg} + ${_signAlg}, OAEP hash: ${_oaepHashAlg}`;
let count = iterations;
while (count--) {
it(testName, async () => {
hashAlg = _hashAlg;
signAlg = _signAlg;
oaepHashAlg = _oaepHashAlg;
encAlg.name = _encAlg;
encAlg.length = _encLen;
const cert = await createCertificateInternal();
const cms = await envelopedEncryptInternal(cert, valueBuffer);
const message = await envelopedDecryptInternal(cert, cms);
assert.strictEqual(utils.isEqualBuffer(message, valueBuffer), true, "Decrypted value must be equal with initially encrypted value");
});
}
});
});
});
});
});
});
I can't reproduce that error. I ran 2880 tests (each mechanism 10 iterations)
Here is the same test in the browser. I don't have any errors https://codesandbox.io/s/faulty-certificate-generation-issue-307-xgu83?file=/main.js
Hi, I ran into a strange problem when generating certificates. Sometimes I couldn't use keys properly. I was encountering decryption error while using private key. I've made some tests to find out what is wrong and the conclusions are quite surprising. I wrote a simple service that encrypts given string with public key and decrypts it with private key. Function returns true value if given string is exactly the same as decrypted string. If something will go wrong it will return false. I am using that in a loop to find out how many iterations will it take to generate faulty certificate. Here are some test results:![image](https://user-images.githubusercontent.com/44780308/109481749-6d688880-7a7d-11eb-8b9e-fd8d71ce5f80.png)
You can see that library sometimes creates incorrect Keys Pair. To eliminate that problem in app, I created simple if statement that checks if keys pair is correct, if not, app generates another pair.
Keys Testing service uses code from https://github.com/PeculiarVentures/PKI.js/tree/master/examples/HowToEncryptCMSviaCertificate to encrypt and decrypt string value. It can be triggered by simple function:
startTest(publicKey: string, privateKey: string, testMessage: string): Observable<boolean> { let correctness = new Subject<boolean>(); let correctnessObs = correctness.asObservable(); this.publicKey = publicKey; this.privateKey = privateKey; this.testMessage = testMessage; this.envelopedEncrypt().then(isEncrypted => { if(isEncrypted) { this.envelopedDecrypt().then(decryptionStatus => { correctness.next(decryptionStatus); }) } else { correctness.next(false); } });
That function is triggered when new Certificate is generated: `createCertificate() { return this.createCertificateInternal().then(() => { var certificateString = String.fromCharCode.apply(null, new Uint8Array(this.certificateBuffer)); var resultString = "".concat(this.formatPEM(toBase64(certificateString))); this.publicKey = resultString;
I hope that it will be helpful for someone :) Good luck!