Click here to Skip to main content
12,506,841 members (57,522 online)
Click here to Skip to main content
Add your own
alternative version

Stats

3.9K views
98 downloads
7 bookmarked
Posted

Sign and Verify a form with Javascript

, 5 Jul 2016 CPOL
Rate this:
Please Sign up or sign in to vote.
Client side signing and verification

Introduction

Α demonstration on how you create a form, require your user to sign it with their private key, and then verify the signature. I assume that you already know the pki mechanisms. I 've searched in various places, one helpful article is http://stackoverflow.com/questions/36018233/how-to-load-a-pkcs12-digital-certificate-with-javascript-webcrypto-api and others.

The APIs discussed here only work in a secure (or a local file) connection.

Signing with WebCrypto API

Usually we have a form which the user will fill, and we want a digital signature on it. For example, a form like that:

//
<form name="form1" id="form1" method="post" action="">
  <label for="firstname">First name:</label>
  <input type="text" name="firstname" id="firstname" required><br>
  <label for="lastname">Last name:</label>
  <input type="text" name="lastname" id="lastname" required><br>
</form>
//

We can get the contains of this form with jQuery:

//
$('#form1').serialize();
//

We will also have the user choose his PFX file and enter a Private Key password:

<label for="pfx">Select PFX/P12 file:</label><br>
<input name="pfx" type="file" id="pfx" accept=".pfx,.p12" required /><br>
 <label for="pfxp">Enter Private Key password:</label><br>
<input name="pfxp" type="password" id="pfxp" /><br>

We now need to read the PFX file with forge.js into a structure:

// Get PFX
    var fileInput = document.getElementById('pfx');
    var file = fileInput.files[0];

    // Read it
    var reader = new FileReader();
    reader.onload = function(e) 
        {
        var contents = e.target.result;
        var pkcs12Der = arrayBufferToString(contents)
        var pkcs12B64 = forge.util.encode64(pkcs12Der);
        var privateKey;
        var pkcs12Asn1 = forge.asn1.fromDer(pkcs12Der);
        var password = $('#pfxp').val();
        
        var pkcs12 = forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, password);
        // load keys
        for(var sci = 0; sci < pkcs12.safeContents.length; ++sci) 
            {
            var safeContents = pkcs12.safeContents[sci];
            for(var sbi = 0; sbi < safeContents.safeBags.length; ++sbi) 
                {
                var safeBag = safeContents.safeBags[sbi];
                if(safeBag.type === forge.pki.oids.keyBag) 
                    {
                    //Found plain private key
                    privateKey = safeBag.key;
                    } 
                else 
                if(safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag) 
                    {
                    // found encrypted private key
                    privateKey = safeBag.key;
                    } 
                else 
                if(safeBag.type === forge.pki.oids.certBag) 
                    {
                    // this bag has a certificate...
                    cert = safeBag.cert;
                    }    
                }
            }
       }
reader.readAsArrayBuffer(file); 

This will read our private key and the certificate into two variables, privatekey and cert. Now  we need to import it to a WebCrypto PKCS#8:

 

function importCryptoKeyPkcs8(privateKey,extractable) 
    {
    var privateKeyInfoDerBuff = privateKeyToPkcs8(privateKey);

    //Importa la clave en la webcrypto
    return crypto.subtle.importKey(
        'pkcs8',
        privateKeyInfoDerBuff,
        { name: "RSASSA-PKCS1-v1_5", hash:{name:"SHA-256"}},
        extractable,
        ["sign"]);
    }

 

And now we can sign:

importCryptoKeyPkcs8(privateKey,true).then(function(cryptoKey) 
            {
            // Imported!
            
            // Empty stuff
            var digestToSignBuf = stringToArrayBuffer(ser);
            var pem = forge.pki.certificateToPem(cert);
            $('#pfxc').val(forge.util.encode64(pem));

            crypto.subtle.sign(
                {name: "RSASSA-PKCS1-v1_5"},
                cryptoKey,
                digestToSignBuf)
                .then(function(signature){
                    sign = arrayBufferToString(signature);
                    signatureB64 = forge.util.encode64(sign);
            });
        
        });

And we can store the original text, the certificate as PEM and the signature as base64.

Verifying the signature

For verification, we need the three items (data,signature,certificate):

 

// From Public Key to a PKCS#8
function publicKeyToPkcs8(pk) 
    {
    var subjectPublicKeyInfo = forge.pki.publicKeyToAsn1(pk);
    var der = forge.asn1.toDer(subjectPublicKeyInfo).getBytes();
    return stringToArrayBuffer(der);
    }

// Verify it
function Verify()
    {
    var pem = ...
    var signature64 = ...
    var signature = forge.util.decode64(signature64);
    var data = ...
    var cert = forge.pki.certificateFromPem(pem);

    // Import the certifcate
    window.crypto.subtle.importKey("spki",publicKeyToPkcs8(cert.publicKey),
        {   
        name: "RSASSA-PKCS1-v1_5",
        hash: {name: "SHA-256"}, 
        },
        false,
        ["verify"]
        ).then(function(k)
            {
            window.crypto.subtle.verify(
                {
                name: "RSASSA-PKCS1-v1_5",
                },
                k, //from generateKey or importKey above
                stringToArrayBuffer(signature), //ArrayBuffer of the signature
                stringToArrayBuffer(data) //ArrayBuffer of the data
                ).then(function(isvalid)
                {
                //returns a boolean on whether the signature is true or not
                   if (!isvalid)
                       {
                       }
                else
                       {
                         // Valid signature
                       }
                }).catch(function(err)
                    {
                       // Invalid sig or something not worked
                    });
                }
                
            );
    }

The HTML file

You can experiment with the attached HTML file. As of this release, not all certificates work with this procedure. If you find a bug, let me know!

History

06 - 07 - 2016 : First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Michael Chourdakis
Engineer
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS and Android.

I 've a PhD in Digital Signal Processing and I specialize in Pro Audio applications.

My home page: http://www.michaelchourdakis.com

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionQuestion about WebCrypto API Pin
Tomice7-Jul-16 20:50
memberTomice7-Jul-16 20:50 
AnswerRe: Question about WebCrypto API Pin
Michael Chourdakis7-Jul-16 22:59
mvpMichael Chourdakis7-Jul-16 22:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160927.1 | Last Updated 6 Jul 2016
Article Copyright 2016 by Michael Chourdakis
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid