PKCS#7 sign/verify doesn't work with special chars
stefan-11 opened this issue · comments
I'm trying to sing and verify with PKCS#7. It works well for simple payloads like abcabc but fails if some special characters appear in the payload. One of those characters that breaks the verification is §
It seems to be related to some conversion that might happen somewhere but I'm not able to find if that is something I'm doing wrong or if it is some conversion happening within forge?
The script I'm using is the one shown below. It works with setting myContent to "abcabc" but fails if some § character appears like with "abc§abc".
const forge = require('node-forge');
const crypto = require("crypto");
let myContent = "abcabc";
const certOrPemString = "-----BEGIN CERTIFICATE-----\r\n"+
"MIIDRTCCAi0CFFkT96Hqq1uj3EpooMub8WBrNIvhMA0GCSqGSIb3DQEBCwUAMF4x\r\n"+
"CzAJBgNVBAYTAkRFMRMwEQYDVQQIDApTb21lLVN0YXRlMQ8wDQYDVQQKDAZteSBv\r\n"+
"cmcxEDAOBgNVBAsMB215IHVuaXQxFzAVBgNVBAMMDm15LWNvbW1vbi1uYW1lMCAX\r\n"+
"DTIzMDcxOTA3NDg0NFoYDzIwNTAxMjAzMDc0ODQ0WjBeMQswCQYDVQQGEwJERTET\r\n"+
"MBEGA1UECAwKU29tZS1TdGF0ZTEPMA0GA1UECgwGbXkgb3JnMRAwDgYDVQQLDAdt\r\n"+
"eSB1bml0MRcwFQYDVQQDDA5teS1jb21tb24tbmFtZTCCASIwDQYJKoZIhvcNAQEB\r\n"+
"BQADggEPADCCAQoCggEBAJoObKCKVe89yOW4I/aHkC2Mi1ad18W8/jcI6Oz+LXZn\r\n"+
"SNdX+u6WjEBVtsvlLZLtA2U7gAi2KrrP/8Dw4CT9MHy7M3RixzMoN2wavxxPqzvt\r\n"+
"JQTgKtZR5fkjUzHFfDLwg8ZM3zY5/5/Pmg2mPF2D184xJHCwmxceYD4FqNJ+Enz7\r\n"+
"iuuLo6VkMGV/THtOFV62qgGSZn1kh5+4H+K2Hgalteg2vvMOZwFdtkdXmK0tYnL8\r\n"+
"Jc1onfBiq1kdgdWRp1Gx5EiFfLKbopYKptPYWljHXJqZsEZkIrdTMQ7pSAea8li0\r\n"+
"hY87kRzqgRUxXqFRUrlc2WIuaxrKkBmTiLgbk9oBgCcCAwEAATANBgkqhkiG9w0B\r\n"+
"AQsFAAOCAQEAdMkjMFtPnb+a4lovBhQZZXSo9anfBQ4zX1FNTWkRnX9RQ9gmPRGv\r\n"+
"9thx6HLy+bfsav2vah5XROcEg7d0opaCZcmcLGOmer5j72kdELDSstCulosKwHgs\r\n"+
"i4AcEbEJgfI6OiZDxDIbReeUxEBaw+nBQUZxVaXxwY9G8LCVbuV0gR0LbFdTTiML\r\n"+
"EBp9+qYSRh1nk+EgDOmQwLKfl1vOxG8spf5QxMmcoji6OxyJ/GWuwl3PC8K7U1gz\r\n"+
"BmGr8EufYkTdO7a1hsHRzTwjjxM9yE52A+3ryJHsdjp7dfXvob8G7Uz8mQSJFVK3\r\n"+
"YrB/Myb3+RIq9IJTRA671Weyh2I6rA7BfA==\r\n"+
"-----END CERTIFICATE-----";
const privateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n"+
"MIIEowIBAAKCAQEAmg5soIpV7z3I5bgj9oeQLYyLVp3Xxbz+Nwjo7P4tdmdI11f6\r\n"+
"7paMQFW2y+Utku0DZTuACLYqus//wPDgJP0wfLszdGLHMyg3bBq/HE+rO+0lBOAq\r\n"+
"1lHl+SNTMcV8MvCDxkzfNjn/n8+aDaY8XYPXzjEkcLCbFx5gPgWo0n4SfPuK64uj\r\n"+
"pWQwZX9Me04VXraqAZJmfWSHn7gf4rYeBqW16Da+8w5nAV22R1eYrS1icvwlzWid\r\n"+
"8GKrWR2B1ZGnUbHkSIV8spuilgqm09haWMdcmpmwRmQit1MxDulIB5ryWLSFjzuR\r\n"+
"HOqBFTFeoVFSuVzZYi5rGsqQGZOIuBuT2gGAJwIDAQABAoIBADeKGblrBf4hTSsc\r\n"+
"TsLTLrRtJdLNRvv/3bpjmO3P2P4F0GnqSwn29os/G464icydbArz/32khxUgZbje\r\n"+
"XkCXkwJ0zuEXt28HAawUnG+NfVM2dJEka+0mS32dMaIQ62zF7wvvrM007aZkxspC\r\n"+
"8yJXpNauOs/xq4gTKGlTywBP+URbMA9syIY3IN+B7aBAY7VILSDgYFSZHJlQS3dh\r\n"+
"Bejp0cJ6GGHMvGzfkbvO5LtpjZaWqg8ahDM+oVzgGWht1YVSB6RZq5zEaM0k+k2h\r\n"+
"MLF4aPvdn5rmbEjUVFDWqK2B/PV9VxX7zqUO2Tx5DNNZSBsmo1QBXDFy68AjSdva\r\n"+
"cEoXaQECgYEAyPnSn+rti6R7lg/7HnQIdUScE2P62ZWjRHUv9uiIsSLmCA7ZP5HV\r\n"+
"srSUjrmMV7HloO34EsV+ZvTcu5sTB3ONQFq9v5eG7Ea2sQc7doFP8iv5t6k53+gJ\r\n"+
"R+SYGe/O18tKL/edCL31g06Og3Fpz0CZEvNzi4nni4hUl0KaZX0cTIECgYEAxDwQ\r\n"+
"UvRQy7W/iXOn9IaWYtc7VLQAr5sZXe3IwLdVu567s4tm9dvRJOBNNmpbSS8w7YrB\r\n"+
"49VcjSpNVwXEwqhO/rj3fT07l9BjBBsV+qn60YZWFO5c29Sfrfi256Q+tkIFkqe4\r\n"+
"qPmfRZ6m1FXFj/iAhw0hkNy1lZKGI/LoCWufmKcCgYBrUy4thFGyvvXYn1QlYSWb\r\n"+
"KjaHDF9LjuOPjV0959QHdHGPYA6YKhMKyfCDf4b41eGg3TQbIihsZtrxvNCerBCE\r\n"+
"i3DFPfApgWHSi+AvWsMMunsyn0Zu0gUSxXqMb0nwZ3mIwBy2LGtN4cetXgV5Ti/w\r\n"+
"tN3BaLWkB/vUgxbcl8FjAQKBgG6EnXT64F46eCtDlf4jpqL4MRQdZJ2CLqE2AHB5\r\n"+
"ULGgTnpqMoyZRosMQLA66jqnd1jxYw2b5soiXvh56Tzwab0QE2LXj66K0JlJX/GS\r\n"+
"tg43KFgVfvrYHy5t+yUu3ZrsSBM2nsub+tXM6ox/2gqhnFFzVcouY55frWilr4VO\r\n"+
"tc3VAoGBAJIQZr87GU4RR1iGy4iIxZb4mJcgr6Mc9HYw/bQ+QiWCYbZzgjS1vNVX\r\n"+
"Yuuz1drW6S9N6IcK7wHnoqMq+srSV7lEDLom9FXF2q4tIFbayzYs3ueA5tpz4Tfw\r\n"+
"HKqdNr7Gwify1rr2HWU6ya+zA0ty6mUzDsRJQUC41wjB8vDmj095\r\n"+
"-----END RSA PRIVATE KEY-----\r\n";
console.log("*** sign ***");
const p7 = forge.pkcs7;
const signedData = p7.createSignedData();
var signCert = forge.pki.certificateFromPem(certOrPemString);
var signPrivateKey = forge.pki.privateKeyFromPem(privateKey);
signedData.content = forge.util.createBuffer(myContent, 'binary');
//console.log(signedData.content);
signedData.addCertificate(signCert);
signedData.addSigner({
key: signPrivateKey,
certificate: signCert,
digestAlgorithm: forge.pki.oids.sha256,
authenticatedAttributes: [{
type: forge.pki.oids.contentType,
value: forge.pki.oids.data
},
{
type: forge.pki.oids.messageDigest, // value will be auto-populated at signing time
value: ""
},
{
type: forge.pki.oids.signingTime, // value can also be auto-populated at signing time
value: new Date()
}]
});
//console.log(signedData.content);
signedData.sign();
//get the signed data in pem format
const signedPem = forge.pkcs7.messageToPem(signedData);
//console.log(signedPem);
console.log("*** verify ***");
const asn1 = forge.asn1;
const msg = forge.pkcs7.messageFromPem(signedPem);
const attrs = msg.rawCapture.authenticatedAttributes;
const set = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs);
const buf = Buffer.from(asn1.toDer(set).data, 'binary');
const sig = msg.rawCapture.signature;
const hashAlgorithmOid = forge.asn1.derToOid(msg.rawCapture.digestAlgorithm);
const hashAlgorithm = forge.pki.oids[hashAlgorithmOid]; //e.g. sha256
let res = true;
//check with the cert contained in the message
console.log(`${msg.certificates.length} certificates provided in pkcs7 message`);
const cert = forge.pki.certificateToPem(msg.certificates[msg.certificates.length-1]);
const verifier1 = crypto.createVerify(`RSA-${hashAlgorithm.toUpperCase()}`);
verifier1.update(buf);
const validWithMessageCert = verifier1.verify(cert, sig, "binary");
if (!validWithMessageCert) {
console.log('signature verify failed with message cert');
res = false;
}
//check with provided/input cert
const verifier2 = crypto.createVerify(`RSA-${hashAlgorithm.toUpperCase()}`);
verifier2.update(buf);
const validWithInputCert = verifier2.verify(certOrPemString, sig, "binary");
if (!validWithInputCert) {
console.log('signature verify failed with provided/input cert');
res = false;
}
//compare attr digest
const hash = crypto.createHash(hashAlgorithm);
const signedContent = msg.rawCapture.content.value[0].value;
hash.update(signedContent);
const signedContentDigest = hash.digest("binary");
let attrDigest = null;
for (var i = 0, l = attrs.length; i < l; ++i) {
if (asn1.derToOid(attrs[i].value[0].value) === forge.pki.oids.messageDigest) {
attrDigest = attrs[i].value[1].value[0].value;
}
}
console.log(Buffer.from(signedContent, "binary").toString('ascii'));
console.log(Buffer.from(attrDigest, "binary").toString('hex'));
console.log(Buffer.from(signedContentDigest, "binary").toString('hex'));
if (signedContentDigest !== attrDigest) {
console.log('Wrong content digest');
res = false;
}
if (!res) {
console.log("verify failed");
}
Solved, the issue was that I got a utf8 string in the pkcs7.
Due to that I need to get the signedContent
like this:
const signedContent = Buffer.from(msg.rawCapture.content.value[0].value, "binary").toString("utf8");