digitalbazaar / forge

A native implementation of TLS in Javascript and tools to write crypto-based and network-heavy webapps

Home Page:https://digitalbazaar.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

PKCS#7 sign/verify doesn't work with special chars

stefan-11 opened this issue · comments

commented

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");