~/blog/nodejs-crypto-module-usage
Published on

Nodejs - KeyPair generation and usage of keys

1279 words7 min read–––
Views
Authors
  • avatar
    Name
    Saikrishna Reddy
    Instagram

Cryptography

  • Refer this repositary for all files.
  • In case of -> Data protection :
    • Encrypt with public key and decrypt with private key.
    • Refer :
      • Encryption > File : encrypt.js > encryptWithPublicKey()
      • Decryption > File : decrypt.js > decryptWithPrivateKey()
    • To test :
      • run node main.js
  • In case of -> Digital Signatures x| JWTs ...
    • Encrypt with private key and decrypt with public key
    • Here, after encrypting, the encrypted data need to transferred to receiver along with the algorithm used, original data and signed message. Using the receiver/application can calculate the hash and the compare it with the hash received/calculated from the received data.
    • If verifed successfully, it means the data is correct and sender is not valid.
    • Refer :
      • Encryption > File : encrypt.js > encryptWithPrivateKey()
      • Decryption > File : decrypt.js > decryptWithPublicKey()
    • To test :
      • run node verifyIdentity.js
  • This is how the JWT token is structured (three dots).
    • first dot : algorithm used, second dot : payload data(orignal data in our case), and third dot : signature
    • Using this token, server can calculate and verify wether the sender and data is valid or not.

NOTE:

  • Here, we are sending data(orignalData) directly to verifIdentity.js file. But this data can also be structured in a way that JWT token is srtuctured or in some other format.

Main files are :

encrypt.js
const crypto = require('crypto')

/**
 * In case of 'Data' protection, encrypt with public key
 * In case of digital signatures(identities), encrypt with private key
 *      - for digital sig, there are two main steps involved
 *      - 1. Sign the message. (so that the receiver can know that the person who signed is the actual one and data has not been tampared with)
 *      - 2. Verify the signature.
 */

/**
 *
 * @param {*} publicKey
 * @param {*} message message you want to encrypt
 * @returns encrypted message
 */
function encryptWithPublicKey(publicKey, message) {
  const bufferMessage = Buffer.from(message, 'utf8')

  return crypto.publicEncrypt(publicKey, bufferMessage)
}

function encryptWithPrivateKey(privateKey, message) {
  const bufferMessage = Buffer.from(message, 'utf8')

  return crypto.privateEncrypt(privateKey, bufferMessage)
}

module.exports = {
  encryptWithPublicKey,
  encryptWithPrivateKey,
}
decrypt.js
const crypto = require('crypto')

function decryptWithPrivateKey(privateKey, encryptedMessage) {
  return crypto.privateDecrypt(privateKey, encryptedMessage)
}

function decryptWithPublicKey(publicKey, encryptedMessage) {
  return crypto.publicDecrypt(publicKey, encryptedMessage)
}

module.exports = {
  decryptWithPrivateKey,
  decryptWithPublicKey,
}

Digital signature : Signing data digitally and verifying it on receiver side.

Signing :

signMessage.js
const crypto = require('crypto')
const hash = crypto.createHash('sha256')
const fs = require('fs')
const { encryptWithPrivateKey } = require('./encrypt')

const myData = {
  name: 'Saikrishna Reddy',
  mobile: 1111111111,
  securityNumber:
    'Never keep anything personal or secret in a digitally signed message. Because this form of cryptgraphy does not hide data and others can see it!.',
}

const myDataString = JSON.stringify(myData)
hash.update(myDataString) // update method requires data to be in string format so...

//hashed data in hex format
const hashedData = hash.digest('hex')

const senderPrivateKey = fs.readFileSync(__dirname + '/id_rsa_private.pem', 'utf8')

const signedMessage = encryptWithPrivateKey(senderPrivateKey, hashedData)

// for a reciever to verify that the data is correct and has not been tampered with, he/she needs :
// algorith used, original data and signedMessage.
// So, using this data reciever can decrypt the message with the sender's public key and verify.(check verifyIdentity.js)
const packageOfDataToSend = {
  algorithm: 'sha256',
  originalData: myData,
  signedAndEncryptedData: signedMessage,
}

module.exports = packageOfDataToSend

Verifying :

verifyIdentity.js
const crypto = require('crypto')
const fs = require('fs')
const { decryptWithPublicKey } = require('./decrypt')
let packageOfDataToSend = require('./signMessage')

// uncomment this line to tamper data🤣
// packageOfDataToSend.originalData.mobile = 2222222222

//decrypt with the senders public key as the message signed with senders private key.
const receivedData = packageOfDataToSend

const hash = crypto.createHash(receivedData.algorithm)

const senderPublicKey = fs.readFileSync(__dirname + '/id_rsa_pub.pem', 'utf8')

// here, decryptedMessage is the hash of the original data.
const decryptedMessage = decryptWithPublicKey(
  senderPublicKey,
  packageOfDataToSend.signedAndEncryptedData,
)

//once we get the hash of signedmessage, then calculate hash for the original data. Then these two hashes must be equal to verify successfully. If verified successfully, that means data is correct and signed by the person who said he/she signed it

const hashOriginal = hash.update(
  JSON.stringify(packageOfDataToSend.originalData),
)
const hexOfHash = hash.digest('hex')

if (hexOfHash === decryptedMessage.toString()) {
  console.log('verified successfully....data is correct and sender is valid!')
} else {
  console.log('Data has been tampered or incorrect signature')
}