Secure your data: cryptography in Nodejs
Tags: javascript, typescript, node
Published: 2023-03-26 11:16:37

Cryptography is a branch of computer science that deals with using mathematical algorithms to encrypt and decrypt data. It is used to secure data from unauthorized access, ensure its integrity, and authenticate its origin. Cryptography is also used to create digital signatures that can be used to verify the authenticity of data.

Nodejs, a runtime that can execute JavaScript on the server. Nodejs has become very popular for backend development. Nodejs has a built-in `cryptography` module that helps us with data encryption and more. 

In this piece we will explore some cryptography concepts with regards to;how we can implement them in NodeJS.

### Hashing
 
Hashing is a computer science concept that is used to map data of any size to a fixed length value. It is typically used to generate a unique identifier for a piece of data, often referred to as a "hash" or "hash code". Hashing is a one-way process, meaning that once a hash is generated, it cannot be reversed to get the original data.

Here is an example of hashing in Typescript using the built-in crypto module:

```javascript
// Import the crypto module 
const crypto = require('crypto');

// Create a variable to store the data to be hashed 
let data = 'My data to be hashed';

// Create a hash object 
let hash = crypto.createHash('sha256');

// Update the hash object with the data 
hash.update(data);

// Generate the hash code 
let hashCode = hash.digest('hex');

// Output the generated hash code 
console.log(hashCode); // Outputs: c1b1a8d37f0f5b3e3e3a3c9a9d3f60a14a25bc
```
Hashing can be used in a variety of real-world applications, such as user authentication and data storage. For example, when users register for an account on a website, their password can be hashed with a secure algorithm before being stored in the database. When the user attempts to log in, the entered password can be hashed and compared to the stored hash. If the hashes match, then the user is authenticated. Hashing can also be used to store data securely, as the original data cannot be retrieved from the hash code.

You should know that there are a few drawbacks to only hashing the user's password for authentication. First, if the user's password is compromised, the attacker can still gain access to the user's account, as the hashed password cannot be reversed. Additionally, if two users have the same password, they will generate the same hash code, which could lead to security vulnerabilities. Finally, hashing is a computationally expensive process, which could slow down user authentication.

To improve user authentication, a technique called salted hashing can be used. In this approach, a random string of characters (the "salt") is combined with the user's password before being hashed. This ensures that even if two users have the same password, they will generate different hash codes, as the salt is unique for each user. Additionally, salted hashing can help mitigate brute-force attacks and rainbow tables. Which leads us to;

### Salt

A salt in cryptography is a random string of characters that is used to add additional complexity to a hashing algorithm. By adding a unique salt to each user's password before hashing, it ensures that even if two users have the same password, they will generate different hash codes. Salted hashing is a more secure method of authentication than plain hashing, as it helps to prevent brute-force attacks and rainbow tables.

```javascript
// Import the crypto module 
const crypto = require('crypto');

// Generate a 16 byte random salt 
let salt = crypto.randomBytes(16).toString('hex');

// Output the generated salt 
console.log(salt); // Outputs: 8b4e8d7e17a51f3a7f85de3c0c7d
```

We can combine this with the hash function we discussed above to generate a true random hash.

```javascript
// Create a variable to store the data to be hashed 
let data = 'My data to be hashed';

// Generate a 16 byte random salt 
let salt = crypto.randomBytes(16).toString('hex');

// Create a hash object 
let hash = crypto.createHash('sha256');

// Update the hash object with the data and salt 
hash.update(data + salt);

// Generate the hash code 
let hashCode = hash.digest('hex');

// Output the generated hash code 
console.log(hashCode); // Outputs: c1b1a8d37f0f5b3e3e3a3c9a9d3f60a14a25bc
```

So far we have only looked making data unreadable, we wil turn our attention to hiding data and making it readable later which leads us to our next heading, encryption.

### Encryption

Encryption in software development is the process of encoding information or data in a way that prevents unauthorized access. Through the use of encryption algorithms and other security measures, software developers can ensure that only those with the correct credentials or authorization can access the data. Encryption is used to protect sensitive data such as passwords, credit card numbers, bank account information, and other private information.

There are two main types of encryption: symmetric encryption and asymmetric encryption. Symmetric encryption uses the same key to both encrypt and decrypt data, while asymmetric encryption uses different keys for encryption and decryption.

When data is encrypted the resulting product is called a cipher Text. What really is this cipher? Cipher text is the encrypted form of plain text, which is the readable form of data or information. Cipher text can only be decrypted using the correct key, which is known only to the sender and the receiver. Cipher text is usually represented as a series of characters or numbers, and is generally unreadable to the naked eye.

Let's take a deep look at symmetric encryption.

### Symmetric Encryption

Symmetric encryption is a type of encryption that uses the same key for both encryption and decryption. This key is shared between two or more parties and must be kept secure. The data is encrypted using an algorithm, and the key is used to decrypt the data. Symmetric encryption is fast and efficient, but the security of the data relies on the key remaining secure.

```javascript
const crypto = require('crypto');
let algorithm = 'aes-256-cbc';
let key = crypto.randomBytes(32);
let iv = crypto.randomBytes(16);
 
let cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update('text to be encrypted', 'utf8', 'hex');
encrypted += cipher.final('hex');
console.log(encrypted);
```
 This code example uses the crypto module to create a symmetric cipher text. First, the algorithm, key, and initialization vector are set. Then, the createCipheriv() function is used to create the cipher with the specified algorithm, key, and iv. Finally, the update() and final() functions are used to encrypt the text and return the cipher text in hexadecimal format.

The cipher text can be decrypted using the same key and algorithm used to encrypt it. The createDecipheriv() function can be used to create a decipher object, and the update() and final() functions can be used to decrypt the cipher text and return the decrypted text.

```javascript
let decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
```

### Asymmetric Encryption

Asymmetric encryption is a type of encryption that uses two different keys for encryption and decryption. One key is known as the public key and is used for encryption, while the other key is known as the private key and is used for decryption. Asymmetric encryption is slower than symmetric encryption, but it is more secure as the private key is never shared with anyone. This means that only the person with the private key can decrypt the data.

First we need to generate a public key and a private key.

```javascript
const crypto = require('crypto');

let algorithm = 'RSA';
let bits = 2048;

let keypair = crypto.generateKeyPairSync(algorithm, {
    modulusLength: bits,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs8',
        format: 'pem',
    }
});

let publicKey = keypair.publicKey;
let privateKey = keypair.privateKey;

console.log(publicKey);
console.log(privateKey);
```

We can use the publicKey and privateKey generated above to encrypt and decrypt data.

```javascript
let encrypted = crypto.publicEncrypt(publicKey, Buffer.from(plainText));
let decrypted = crypto.privateDecrypt(privateKey, encrypted);
console.log(encrypted.toString('hex'));
console.log(decrypted.toString('utf8'));
```
One thing with asymmetric encryption is that only the party with the private key can decrypt the data. Sometimes we are more concerned with the credibility of the message. In this case we encrypt data with the private key, then we use the public key to verify it that the message is authentic

```javascript
const signer = crypto.createSign("rsa-sha256");

const message = "some ungodly secrete" 

signer.update(message)

const signatutre = signer.sign(privateKey, "hex");

const verifier = crypto.createVerify("rsa-sha256")

verifier.update(message)

const verified = verifier.verify(publicKey, signatutre, "hex");

console.log(verified);
```
The snippet above creates a signer object using the createSign() function, which is used to sign the message using the private key. Then, a verifier object is created using the createVerify() function, which is used to verify the signature using the public key. Finally, the verify() function is used to check if the signature is valid, and the result is stored in the verified variable. If the message is authentic then verified will be true, otherwise false.

In this article, we discussed hashing, salts, encryption in software development and the two main types of encryption: symmetric and asymmetric. We looked at code examples of how to create symmetric and asymmetric cipher text using the crypto module provided in node.js. We also discussed how to create a public and private key pair, and how to create an asymmetric cipher text using the publicEncrypt and privateDecrypt functions.

Encryption is an essential component of software development, as it ensures that data is kept secure and only accessible to those with the correct credentials. With the various encryption algorithms and security measures available, software developers can ensure that their data is kept secure and private. With encryption, developers can ensure that their users' data is safe and secure.