Consider the following code
const data = await Deno.readFile("./README.md");
const certificate = (await loadPEM("./playground/domain.pem"))[0] as Certificate;
const privateKey = (await loadPEM("./playground/domain-pkcs8-nocrypt.key", "PRIVATE KEY"))[0] as CryptoKey;
const signedData = await signData(data.buffer, certificate, privateKey);
// verify same
const ok = await signedData.verify({
signer: 0,
checkChain: true,
trustedCerts: [certificate],
data: data.buffer
})
console.log(ok); // false :(
// verify external signed like this
// openssl cms -sign -signer domain.pem -inkey domain-pkcs8-nocrypt.key -binary -in README.md -outform der -out signature
const cms = ContentInfo.fromBER(await Deno.readFile("./playground/signature")) as ContentInfo;
if (cms.contentType !== ContentInfo.SIGNED_DATA)
throw new Error("CMS is not Signed Data");
const signedData1 = new SignedData({ schema: cms.content });
const ok1 = await signedData1.verify({
signer: 0,
checkChain: true,
trustedCerts: [certificate],
data: data.buffer
})
console.log(ok1); // true
In the first part I sign data and then I verify it against loaded certificate and it FAILS
In the second part I load a generated signature with openssl with same certificate and private key used in the first part and the verification against the loaded certificate is OK.
So, since verification method is the same in both examples, I guess my signature method has something wrong.
Here is the code for signature creation
export async function signData(data:ArrayBuffer, certificate: Certificate, privateKey: CryptoKey):Promise<SignedData>{
const cmsSigned = new SignedData({
encapContentInfo: new EncapsulatedContentInfo({
eContentType: ContentInfo.DATA,
//eContent: new ans1js.OctetString({ valueHex: data })
}),
signerInfos: [
new SignerInfo({
sid: new IssuerAndSerialNumber({
issuer: certificate.issuer,
serialNumber: certificate.serialNumber
})
})
],
certificates: [certificate]
});
await cmsSigned.sign(privateKey, 0, "SHA-256", data);
return cmsSigned;
}
Here is how I load certificate and key:
export async function loadPEM(src:string,
type: PEMType = "CERTIFICATE" ):Promise<PkiObject[] | CryptoKey[]> {
const buffer = pvtsutils.BufferSourceConverter.toArrayBuffer(await Deno.readFile(src));
const binary = pvtsutils.Convert.ToBinary(buffer);
const bers = decodePEM(binary, type);
const ret = [];
switch (type) {
case "CERTIFICATE":
return bers.map(ber => Certificate.fromBER(ber) as Certificate);
case "PRIVATE KEY":
for( const ber of bers)
ret.push(await crypto.importKey("pkcs8", ber, {
name: "RSA-PSS",
hash: "SHA-256",
},
true,
["sign"]));
return ret;
case "PUBLIC KEY":
return bers.map(ber => PublicKeyInfo.fromBER(ber) as PublicKeyInfo);
}
}
ok, the problem was the algorithm used while importing the key. By using RSASSA-PKCS1-v1_5 it now works: