Source: digitalSignature.js

/**
 * Digital Signature for Candy
 * uses SHA256withRSA algorithm
 * required forge.min.js
 * https://github.com/digitalbazaar/forge
 */

'use strict';

//unify browser and node
if(typeof _this === 'undefined') {
    var _this = this;
}

/**
 * Crypto signs methods
 * @param {string} dataToSign Optional: data for sign
 * @return {DigitalSignature}
 * @constructor
 */
function DigitalSignature(dataToSign) { //data in string format
    if(_this.window === undefined) {
        this.forge = require('node-forge');
    } else {
        this.forge = forge;
    }

    /**
     * RSA keys for sign
     */
    this.keysPair = {};

    /**
     * Sign
     */
    this.sign = '';

    /**
     * Data format as presented in 'block data'
     */
    this.signedData = {
        data: dataToSign,     //incoming data
        sign: '',              //sign in HEX format
        pubkey: ''             //Public key in pem PKCS#1
    };

    /**
     * Generate pair of keys for signing
     * @param {number} len Length of the key
     */
    this.generate = (len = 2048) => {

        let rsa = this.forge.pki.rsa;
        let keypair = this.forge.rsa.generateKeyPair({len});
        keypair = {
            public: repairKey(fix(this.forge.pki.publicKeyToRSAPublicKeyPem(keypair.publicKey, 72))),
            private: repairKey(fix(this.forge.pki.privateKeyToPem(keypair.privateKey, 72)))
        };
        this.keysPair = keypair;
        console.log('Info: Keypair generated');
        return keypair;
    };


    /**
     * PEM key fixing
     * @param str
     * @return {string}
     */
    function fix(str) {
        return str.replace(/\r/g, '') + '\n'
    }

    /**
     * Repair bad generated key
     * @param key
     * @return {string}
     */
    function repairKey(key) {
        if(key[key.length - 1] !== "\n") {
            key += "\n";
        }
        return key.replace(new RegExp("\n\n", 'g'), "\n");
    }

    /**
     * Signs data
     * @param {data} data for signing
     * @param {key} key
     */


    /**
     * Sign data
     * @param {string} data Data
     * @param {string} key Private key
     * @return {{data: {string}, sign:{string}}} Data - signable data, sign - Sign
     */
    this.signData = (data = dataToSign, key = this.keysPair.private) => {
        if(!data) {
            console.log('No data to sign');
            return '';
        }
        let md = this.forge.md.sha256.create();
        md.update(data, 'utf8');
        let privateKey = this.forge.pki.privateKeyFromPem(key);
        this.sign = privateKey.sign(md);
        console.log('Info: Data signed');
        return {data: data, sign: this.forge.util.bytesToHex(this.sign)};
    };


    /**
     * Signs data
     * @param {string} data Signed data for verify
     * @param {string} sign Sign
     * @param {string} key Public key
     */
    this.verifyData = (data = this.signedData, sign = this.signedData.sign, key = this.signedData.pubkey) => {
        if(typeof data === 'object') {
            sign = data.sign;
            data = data.data;
        }
        try {
            let publicKey = this.forge.pki.publicKeyFromPem(repairKey(fix(key)));
            let md = this.forge.md.sha256.create();
            md.update(data, 'utf8');
            return publicKey.verify(md.digest().bytes(), this.forge.util.hexToBytes(sign)); //verifying only in bytes format
        } catch (e) {
            console.log(e);
            return false;
        }
    };

    if(dataToSign !== undefined) {
        this.keysPair = this.generate();
        this.signedData.pubkey = this.keysPair.public;
        this.signedData.sign = this.signData().sign;
        if(this.verifyData() === false) {
            console.log('Sign self-validation error! Invalid key or sign checking');
        }
    }

    return this;
}

//unify browser and node
if(this.window === undefined) {
    module.exports = DigitalSignature;
}