JavaScript: RSA and ECDSA Signatures

JavaScript — The Worst and Also One of The Best Software Languages

Photo by Gabriel Heinzer on Unsplash

JavaScript: RSA and ECDSA Signatures

JavaScript — The Worst and Also One of The Best Software Languages

There are a few technical things that are both a sinner and a saint. ARP is one example and is one of the most amazing protocols for connectivity but is a disaster for security. But, it is JavaScript that possibly holds the top stop for a programming language that is one of the best and one of the worst of all the other software languages. Overall, it has a horrible syntax and is a nightmare to debug, but it is just so useful.

The End of JavaScript? No way!

A few years ago, I predicted that JavaScript would eventually be replaced by more modern languages. How wrong was I? I thought that Flash Actionscript, Java and Microsoft XAML would take over from the horrible syntax of JavaScript. But all of these have failed, and it is JavaScript that is the king of the browser. And, now, with Node.js, we have a back-end language. For any serious person in Cybersecurity, the usage of JavaScript and/or Node.js is an essential part of scripting. And for cryptography, we can do things fast in the browser with JavaScript or do it on the back-end with Node.js. So, let’s look at implementing digital signatures in pure JavaScript.

RSA and ECDSA

The two main signatures we have are RSA and ECDSA. With ECDSA, we use an ECC key pair (sk and pk). With this, Bob uses a random nonce value (k), and then signs the hash of a message with his private key (sk). This produces a signature (r,s). Alice then receives this and uses Bob’s public key (pk) and the hash of the message to check the signature:

We can generate a key pair with:

var kp = KEYUTIL.generateKeypair("EC", name);

and then create an ECDSA signature with SHA-1:

var priv = KEYUTIL.getPEM(kp.prvKeyObj, "PKCS8PRV");

var pub = KEYUTIL.getPEM(kp.pubKeyObj, "PKCS8PUB");

var sig = new KJUR.crypto.Signature({ 'alg': 'SHA1withECDSA' });

sig.init(priv);

sig.updateString(message);

var hSigVal = sig.sign();

And then can verify it using the public key with:

var sig = new KJUR.crypto.Signature({ 'alg': 'SHA1withECDSA' }); 
sig.init(pub);
sig.updateString(message);
var isValid = sig.verify(hSigVal);

With RSA signatures, we generate two prime numbers (p and q), and then derive a modulus (N=pq). First we select a verification exponent (v), and which does not share a factor with:

PHI=(p-1)(q-1)

In most cases she will select:

v=65,537

Next we compute the signature exponent (s) with:

s.v=1 mod (PHI)

Next she takes a message (D) and signs with:

S=D^s (mod N)

Bob then checks the signature with:

M=S^v(mod N)

If M is equal to D, the signature matches.

Coding

The following will generate either RSA and ECC key pairs and then use these to sign for a message. We will use SHA-1 and SHA-256 for the signatures, and which are implemented in RSA or ECDSA. The main signatures implemented are SHA-1 with RSA, SHA-256 with RSA, SHA-1 with ECDSA, and SHA-256 with ECDSA. Overall, a private key is used to sign for a message, and the public key will be used to verify the signature. The JavaScript coding is [here]:

<script type="text/javascript">document.getElementById("m").value = "Hello";

    function gorsa(size, message, method) {
        document.getElementById("signature").innerHTML = "Message to sign: \'" + message + "\' Signature:\n";
        var kp = KEYUTIL.generateKeypair("RSA", size);

        var priv = KEYUTIL.getPEM(kp.prvKeyObj, "PKCS8PRV");

        document.getElementById("privatekey").innerHTML = priv;
        pub = KEYUTIL.getPEM(kp.pubKeyObj, "PKCS8PUB");
        document.getElementById("publickey").innerHTML = pub;
        var sig = new KJUR.crypto.Signature({ "alg": method });

        sig.init(priv);
        sig.updateString(message);
var hSigVal = sig.sign();
        document.getElementById("signature").innerHTML += method + " " + hSigVal;

        var sig = new KJUR.crypto.Signature({ "alg": method });
sig.init(pub);
sig.updateString(message);
var isValid = sig.verify(hSigVal);
        document.getElementById("signature").innerHTML += "\nValid signature: " + isValid;

    }
    function goecc(name, message, method) {
        document.getElementById("signature").innerHTML = "Message to sign: \'" + message + "\' Signature:\n";
        var kp = KEYUTIL.generateKeypair("EC", name);

        var priv = KEYUTIL.getPEM(kp.prvKeyObj, "PKCS8PRV");

        document.getElementById("privatekey").innerHTML = priv;
        var pub = KEYUTIL.getPEM(kp.pubKeyObj, "PKCS8PUB");
        document.getElementById("publickey").innerHTML = pub;

        var sig = new KJUR.crypto.Signature({ 'alg': method });
        sig.init(priv);
        sig.updateString(message);

        var hSigVal = sig.sign();
        document.getElementById("signature").innerHTML += method + " " + hSigVal;
        var sig = new KJUR.crypto.Signature({ 'alg': method });
sig.init(pub);
sig.updateString(message);
var isValid = sig.verify(hSigVal);
        document.getElementById("signature").innerHTML += "\nValid signature: " + isValid;
    }</script>

And the HTML code is [here]:

<div class="indented">
<table width="100%">
<tr>
<th>Method</th>
<td style="text-align:left">
                    <p>
RSA: <input type="button" class="btn btn-medium btn-success" onclick="gorsa(512,document.getElementById('m').value,'SHA1withRSA')" value="Generate RSA 512 SHA1"><input type="button" class="btn btn-medium btn-success" onclick="gorsa(738,document.getElementById('m').value,'SHA1withRSA')" value="Generate RSA 738 SHA1"><input type="button" class="btn btn-medium btn-success" onclick="gorsa(1024,document.getElementById('m').value,'SHA1withRSA')" value="Generate RSA 1024 SHA1">

                    </p>
<p>
RSA: <input type="button" class="btn btn-medium btn-success" onclick="gorsa(512,document.getElementById('m').value,'SHA256withRSA')" value="Generate RSA 512 SHA256"><input type="button" class="btn btn-medium btn-success" onclick="gorsa(738,document.getElementById('m').value,'SHA256withRSA')" value="Generate RSA 738 SHA256"><input type="button" class="btn btn-medium btn-success" onclick="gorsa(1024,document.getElementById('m').value,'SHA256withRSA')" value="Generate RSA 1024 SHA256">

                    </p>
                    <p>
ECC: <input type="button" class="btn btn-medium btn-warning" onclick="goecc('secp256r1',document.getElementById('m').value,'SHA1withECDSA')" value="Secp256r1 ECDSA SHA1">
<input type="button" class="btn btn-medium btn-warning" onclick="goecc('secp256k1',document.getElementById('m').value,'SHA1withECDSA')" value="Secp256k1 ECDSA SHA1">
                        <input type="button" class="btn btn-medium btn-warning" onclick="goecc('secp384r1',document.getElementById('m').value,'SHA1withECDSA')" value="secp384r1 ECDSA SHA1">
                        <input type="button" class="btn btn-medium btn-warning" onclick="goecc('secp521r1',document.getElementById('m').value,'SHA1withECDSA')" value="secp521r1 ECDSA SHA1">
                    </p>
<p>
ECC: <input type="button" class="btn btn-medium btn-warning" onclick="goecc('secp256r1',document.getElementById('m').value,'SHA256withECDSA')" value="Secp256r1 ECDSA SHA256">
<input type="button" class="btn btn-medium btn-warning" onclick="goecc('secp256k1',document.getElementById('m').value,'SHA256withECDSA')" value="Secp256k1 ECDSA SHA256">
                        <input type="button" class="btn btn-medium btn-warning" onclick="goecc('secp384r1',document.getElementById('m').value,'SHA256withECDSA')" value="secp384r1 ECDSA SHA256">
                        <input type="button" class="btn btn-medium btn-warning" onclick="goecc('secp521r1',document.getElementById('m').value,'SHA256withECDSA')" value="secp521r1 ECDSA SHA1">

                    </p>
                </td>
</tr>
<tr>
<th width="15%">Message to Sign</th>
<td>
                    <textarea cols="20" id="m" name="m" rows="2" style="width:100%"></textarea>
                </td>
</tr>
            <tr>
<th width="15%">Private Key</th>
<td>
                    <textarea cols="20" id="privatekey" name="privatekey" rows="4" style="width:100%"></textarea>
                </td>
</tr>
<tr>
<th>Public Key</th>
<td>
                    <textarea cols="20" id="publickey" name="publickey" rows="4" style="width:100%"></textarea>
                </td>
</tr>
<tr>
<th>Signature</th>
<td>
                    <textarea cols="20" id="signature" name="signature" rows="5" style="width:100%"></textarea>
                </td>
</tr>
        </table>

You can run the code here:

https://asecuritysite.com/javascript/sign

For ECC secp256k1 keys, we get:

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg2sf0a+4XAa3Nq6c9
zfq0Hn78pFyZJCMqeGgQWQzFi0uhRANCAASexo2iEsivjrwdvmf/rhc5qMKI4u6C
MZ9r1c80pyU87WfMcntBNyNXbgWCQ+ykOf4EpjkgxKTShtJvSCD45yiA
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnsaNohLIr468Hb5n/64XOajCiOLu
gjGfa9XPNKclPO1nzHJ7QTcjV24FgkPspDn+BKY5IMSk0obSb0gg+OcogA==
-----END PUBLIC KEY-----

And then to sign for the message for “Hello” and for ECDSA with a SHA-256 hash:

Message to sign: 'Hello' Signature:
SHA256withECDSA 304502204cb194444892297042a71dc7e50a30be571c25f2619ae0ec9a651419bbee8d460221008791663659382463a59e0c5c83a8d36253a48a54caf0a4f1113c244751c4408f
Valid signature: true

Conclusions

JavaScript has truly built our modern front-end and largely replaced the Windows approach of the past. If you need a cryptographic method, you’ll possibly find it implemented in JavaScript somewhere. It’s then an easy task to either implement it in JavaScript in the browser, or with Node.js in the back-end. The advantage of front-end code is that it is fast, but the code is revealed, whereas the back-end code will generally be slower, but more secure. My demo is here:

https://asecuritysite.com/javascript/sign