digitalbazaar / forge

A native implementation of TLS in Javascript and tools to write crypto-based and network-heavy webapps
https://digitalbazaar.com/
Other
5k stars 767 forks source link

Special characters error #652

Open edmava opened 5 years ago

edmava commented 5 years ago

Hello, i'm triying to sign a document with "forge" almost everything works fine, my problem is with the special characters, when i try to open a .cer file, se this characters ó i use FileReader to open, with the parameter readAsDataURL, and already parse the base 64 for use forge.util.decode64, when i use forge.util.decode64 i see this characters ó

When i use another library for decode the base 64 i see the correct characters but when i pass forge.asn1.fromDer sent me a message that say Error: Too few bytes to read ASN.1 value.

Anybody knows how can i solve this encoding issue? is there anyway to solve the message Error: Too few bytes to read ASN.1 value.

Thanks.

davidlehn commented 5 years ago
edmava commented 5 years ago
  • Are you using a recent forge version? Old versions had many bugs related to ASN.1 parsing.
  • Would it be possible to provide a test cert and code that shows the issue?

I'm using the version 7.0 of forge and this is the example

var readerCert = new FileReader();
readerCert.onload = function(e) 
   {
        // decode using ReadAsDataURL
        certBase64 = readerCert.result.split(',')[1];
        var cerDer = forge.util.decode64(certBase64);

    console.log(cerDer);
        var cerAsn1 = forge.asn1.fromDer(cerDer);
        console.log(cerAsn1);
        cert = forge.pki.certificateFromAsn1(cerAsn1);
        p7.addCertificate(cert);
        p7.addSigner({
          key: privateKey,
          certificate:cert,
          digestAlgorithm: forge.pki.oids.sha1,
          authenticatedAttributes: [{
            type: forge.pki.oids.contentType,
            value: forge.pki.oids.data
          }, {
            type: forge.pki.oids.messageDigest
          }, {
            type: forge.pki.oids.signingTime,
            value: new Date()
          }]
        });
        p7.sign();
        var bufferOut = forge.asn1.toDer(p7.toAsn1()).getBytes();
        var finalDoc = convertBinaryStringToUint8Array(bufferOut);
        saveByteArray([finalDoc], document.getElementById('fileDoc').files[0].name+'.p7m');

   }

readerCert.readAsDataURL(fileCer.files[0]);

this is the .cer file i'm using

cayr4604083f2.cer.zip

Thank you.

davidlehn commented 5 years ago
const forge = require('node-forge');
const fs = require('fs');

// decode using ReadAsDataURL
//certBase64 = readerCert.result.split(',')[1];
//var cerDer = forge.util.decode64(certBase64);

const cerDerString = fs.readFileSync('cayr4604083f2.cer', 'binary');
const cerDer = forge.util.createBuffer(cerDerString, 'raw');
console.log('DER', forge.util.bytesToHex(cerDer));

// make a fake keypair for signing
const keypair = forge.pki.rsa.generateKeyPair({bits: 2048});

const cerAsn1 = forge.asn1.fromDer(cerDer);
console.log('ASN.1', forge.asn1.prettyPrint(cerAsn1));

const cert = forge.pki.certificateFromAsn1(cerAsn1);
console.log('CERT', cert);

const p7 = forge.pkcs7.createSignedData();
p7.content = forge.util.createBuffer('my content.', 'utf8');
p7.addCertificate(cert);
p7.addSigner({
  key: keypair.privateKey,
  certificate: cert,
  digestAlgorithm: forge.pki.oids.sha1,
  authenticatedAttributes: [{
    type: forge.pki.oids.contentType,
    value: forge.pki.oids.data
  }, {
    type: forge.pki.oids.messageDigest
  }, {
    type: forge.pki.oids.signingTime,
    value: new Date()
  }]
});
p7.sign();
const bufferOut = forge.asn1.toDer(p7.toAsn1()).getBytes();
console.log('OUT', forge.util.bytesToHex(bufferOut));
edmava commented 5 years ago

This is the complete code

var keyBase64;
var certBase64;
var privateKey;
var cert;
var p7 = forge.pkcs7.createSignedData();
p7.content = forge.util.createBuffer(sampleBytes, 'utf8');
var reader = new FileReader();
reader.onload = function(e) 
   {
        keyBase64 = reader.result.split('base64,')[1];
        var keyDer = forge.util.decode64(keyBase64);
        var keyAsn1 = forge.asn1.fromDer(keyDer);
        var privateKeyInfo = forge.pki.decryptPrivateKeyInfo(keyAsn1,password);

        privateKey = forge.pki.privateKeyFromAsn1(privateKeyInfo);

        var readerCert = new FileReader();
        readerCert.onload = function(e) 
           {
                // decode utilizando ReadAsDataURL
                certBase64 = readerCert.result.split(',')[1];
                var cerDer = forge.util.decode64(certBase64);

                var cerAsn1 = forge.asn1.fromDer(cerDer);
                console.log('ASN.1', forge.asn1.prettyPrint(cerAsn1));

                cert = forge.pki.certificateFromAsn1(cerAsn1);

                p7.addCertificate(cert);
                p7.addSigner({
                  key: privateKey,
                  certificate:cert,
                  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
                  }, {
                    type: forge.pki.oids.signingTime,
                    // value can also be auto-populated at signing time
                    value: new Date()
                  }]
                });
                p7.sign();

                var bufferOut = forge.asn1.toDer(p7.toAsn1()).bytes();
                console.log(forge.asn1.prettyPrint(p7.toAsn1()))
                var finalDoc = convertBinaryStringToUint8Array(bufferOut);

                saveByteArray([finalDoc], document.getElementById('fileDoc').files[0].name+'.p7m');

           }
        readerCert.readAsDataURL(fileCer.files[0]);
   }
reader.readAsDataURL(fileKey.files[0]);

i see the prettyPrint and in there the special characters are good printed but i add another console.log(forge.asn1.prettyPrint(p7.toAsn1())) at the end of the function and i see again the character broken, i don`t know where this characters appear, it's so strange and i don't know if it's a library problem.

i add all the files used.

File.zip

davidlehn commented 5 years ago

I'm having trouble understanding what the issue is. It's a field the prints ok from the cert but not ok from the signed p7 data? Do you know which field it is? To avoid confusion, do you know what the hex value of the bytes should be?

Can the code be reduced any further to just the simplest thing that clearly shows what the problem is? Ideally a single self contained html+script or node.js script. I'm unclear if this is just some display issue while debugging or if there's an actual problem with the output? (I don't know what to do with the output file.)

edmava commented 5 years ago

Hello David i see where the forge util made all the encoding issue.

I'm using this example:

var p7 = forge.pkcs7.createSignedData();
p7.content = forge.util.createBuffer('Some content to be signed.', 'utf8');
p7.addCertificate(certOrCertPem);
p7.addSigner({
  key: privateKeyAssociatedWithCert,
  certificate: certOrCertPem,
  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
  }, {
    type: forge.pki.oids.signingTime,
    // value can also be auto-populated at signing time
    value: new Date()
  }]
});
p7.sign();
var pem = forge.pkcs7.messageToPem(p7);

When i add the first time the cert with this p7.addCertificate(certOrCertPem); everything works fine with the encoding.

But when i pass again the same cert in this function:

p7.addSigner({
  key: privateKeyAssociatedWithCert,
  certificate: certOrCertPem,
  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
  }, {
    type: forge.pki.oids.signingTime,
    // value can also be auto-populated at signing time
    value: new Date()
  }]
});

The certOrCertPem change the encoding of the byte string, is there anyway to force the utf8 encoding in the addSigner Function? is there a bug of the forge library?

Thank's a lot

SWBennett06 commented 4 years ago

I'm encountering a similar issue. It does occur without base64 encoding, as well.

const forge = require('node-forge');

const CIPHER_ALGORITHM = 'AES-CBC';

function encrypt(plaintext, key, iv) {
  const cipher = forge.cipher.createCipher(CIPHER_ALGORITHM, key);

  cipher.start({ iv });
  cipher.update(new forge.util.ByteStringBuffer(plaintext));
  cipher.finish();

  return forge.util.encode64(cipher.output.data);
}

function decrypt(encryptedBase64, key, iv) {
  const encrypted = forge.util.decode64(encryptedBase64);

  const decipher = forge.cipher.createDecipher(CIPHER_ALGORITHM, key);
  decipher.start({ iv });
  decipher.update(new forge.util.ByteStringBuffer(encrypted));
  decipher.finish();

  return decipher.output.data;
}

const key = forge.random.getBytesSync(16);
const iv = forge.random.getBytesSync(16);

console.log(`key: ${key}`);
console.log(`iv: ${iv}`);

const encryptedResult = encrypt('Hello “🦃”', key, iv);
const decryptedResult = decrypt(encryptedResult, key, iv);

console.log(`encrypted result: ${encryptedResult}`);
console.log(`decrypted result: ${decryptedResult}`); // logs: HelloÄ>£
igoosham commented 3 years ago

working sample for AES:

function encrypt(string) { var cipher = forge.cipher.createCipher("AES-CBC", key);

cipher.start({ iv: iv });

cipher.update(new forge.util.ByteStringBuffer(forge.util.encodeUtf8(string)));

cipher.finish();

return forge.util.encode64(cipher.output.data); }

function decrypt(encrypted) { var decipher = forge.cipher.createDecipher("AES-CBC", key);

decipher.start({ iv: iv });

decipher.update(forge.util.createBuffer(forge.util.decode64(encrypted)));

if (!decipher.finish()) return; // check 'result' for true/false

return forge.util.decodeUtf8(decipher.output.data); }