Skip to content

This is a Encryption library written in native i.e swift & kotlin for React Native, including usage examples for AES, RSA, Hashing, HMAC, Random String, and Base64 utilities.

License

Notifications You must be signed in to change notification settings

rishabhyadavv/react-native-encryption

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

📚 React Native Integration Guide for rn-encryption Library

This guide explains how to directly access methods from the rn-encryption library in a React Native project, including usage examples for AES, RSA, Hashing, HMAC, Random String, and Base64 utilities.

  • Mobile (iOS & Android): Utilizes native implementations through JSI (JavaScript Interface) via Turbo Modules for encryption.
  • Web: Leverages crypto.subtle for encryption functionality. https://www.npmjs.com/package/web-secure-encryption is being used to support encryption for web.

📑 Table of Contents

  1. Library Installation
  2. Requirements
  3. Setup in React Native
  4. Direct Method Import
  5. API Overview
  6. Usage Examples
  7. Troubleshooting
  8. Best Practices
  9. FAQ
  10. Security Best Practices

🚀 1. Library Installation

1.1 Add Dependency

Install the library using npm or yarn:

expo install @yourorg/native-encryption

# OR
npm install rn-encryption --save
# OR
yarn add rn-encryption

1.2 Rebuild the Project

For Android:

cd android && ./gradlew clean && cd ..
npx react-native run-android

For iOS:

cd ios && pod install && cd ..
npx react-native run-ios

2.Requirements

2.1 New Architecture required

New architecture required. React native >= 0.76.5 Works with Expo Bare Workflow & Vanilla React Native

2.2 iOS: Cryptokit from swift is being used for encryption. Minimin support iOS version is 13.0


⚙️ 3. Setup in React Native

No additional configuration is required. The methods can be directly imported and used.


📦 4. Direct Method Import

You can directly import the methods you need:

import {
  encryptAES,
  decryptAES,
  encryptRSA,
  decryptRSA,
  hashSHA256,
  hashSHA512,
  hmacSHA256,
  base64Encode,
  base64Decode,
  generateRandomString,
  generateAESKey,
  generateRSAKeyPair,
  generateECDSAKeyPair,
  signDataECDSA,
  verifySignatureECDSA,
  encryptAsyncAES,
  decryptAsyncAES,
  encryptAsyncRSA,
  decryptAsyncRSA,
  encryptFile,
  decryptFile,
} from 'rn-encryption';;
  • Each method can be accessed directly without a default object wrapper.
  • Please note that encryptFile/decryptFile methods are not available for web yet.
  • All web methods have promises while few native methods can be called without promises.

📚 5. API Overview

🔒 AES Encryption/Decryption

  • generateAESKey(keySize: number): string
  • encryptAES(data: string, key: string): string
  • decryptAES(data: string, key: string): string

🔑 RSA Encryption/Decryption

  • generateRSAKeyPair(): keypair
  • encryptRSA(data: string, publicKey: string): string
  • decryptRSA(data: string, privateKey: string): string

🛡️ SHA Hashing

  • hashSHA256(input: string): string
  • hashSHA512(input: string): string

📝 HMAC-SHA256

  • hmacSHA256(data: string, key: string): string

🎲 Random String Generation

  • generateRandomString(input: number): string

📝 Base64 Encoding/Decoding

  • base64Encode(input: string): string
  • base64Decode(input: string): string

🔒 ECDA Encryption/Decryption

  • generateECDSAKeyPair(): keypair
  • signDataECDSA(data: string, key: string): string
  • verifySignatureECDSA(data: string,signatureBase64: string, key: string): boolean

🔒 Asynchronous Methods

  • encryptAsyncAES(data: string, key: string): Promise<string>
  • decryptAsyncAES(data: string, key: string): Promise<string>
  • encryptAsyncRSA(data: string, key: string): Promise<string>
  • decryptAsyncRSA(data: string, key: string): Promise<string>
  • encryptFile(inputPath: string,outputPath: string, key: string): Promise<string>
  • decryptFile(inputPath: string, key: string): Promise<string>

🛠️ 6.Native Usage Examples

import { useState } from 'react';
import { View, StyleSheet, Text, Button } from 'react-native';
import {
  encryptAES,
  decryptAES,
  encryptRSA,
  decryptRSA,
  hashSHA256,
  hashSHA512,
  hmacSHA256,
  base64Encode,
  base64Decode,
  generateRandomString,
  generateAESKey,
  generateRSAKeyPair,
  generateECDSAKeyPair,
  signDataECDSA,
  verifySignatureECDSA,
  encryptAsyncAES,
  decryptAsyncAES,
  encryptAsyncRSA,
  decryptAsyncRSA,
  encryptFile,
  decryptFile
} from 'rn-encryption';
import RNFS from 'react-native-fs';

interface EncryptionError {
  name: string;
  message: string;
}
export default function DashboardScreen() {
  const [result, setResult] = useState(''); // Encryption/Decryption result

  const inputPath = `${RNFS.DocumentDirectoryPath}/data.txt`;
const outputPath = `${RNFS.DocumentDirectoryPath}/data.enc`;
const decryptedPath = `${RNFS.DocumentDirectoryPath}/data-decrypted.txt`;

  function handleRSAEncryption() {
    const plaintext = 'Hello, RSA Encryption!';
    const generatedKeys = generateRSAKeyPair();
    try {
      // Step 1: Encrypt the plaintext using the Public Key
      const encryptedData = encryptRSA(plaintext, generatedKeys.publicKey);
      // Step 2: Decrypt the encrypted data using the Private Key
      const decryptedData = decryptRSA(encryptedData, generatedKeys.privateKey);
      // Step 3: Validation
      if (decryptedData === plaintext) {
        console.log('✅ RSA Encryption and Decryption Successful!');
      } else {
        console.error('❌ Decrypted data does not match original plaintext!');
      }
    } catch (error) {
      console.error('⚠️ RSA Error:', error);
    }
  }

  async function handleAsyncRSAEncryption() {
    const plaintext = 'Hello, RSA Encryption!';
    const generatedKeys = generateRSAKeyPair();
    try {
      // Step 1: Encrypt the plaintext using the Public Key
      const encryptedData = await encryptAsyncRSA(
        plaintext,
        generatedKeys.publicKey
      );
      // Step 2: Decrypt the encrypted data using the Private Key
      const decryptedData = await decryptAsyncRSA(
        encryptedData,
        generatedKeys.privateKey
      );
      // Step 3: Validation
      if (decryptedData === plaintext) {
        console.log('✅ RSA Encryption and Decryption Successful!');
      } else {
        console.error('❌ Decrypted data does not match original plaintext!');
      }
    } catch (error) {
      console.error('⚠️ RSA Error:', error);
    }
  }

  const handleAESEncryption = () => {
    const sampleObject = {
      name: 'John Doe',
      age: 30,
      roles: ['admin', 'editor'],
    };
    try {
      const generatedKey = generateAESKey(256);
      const jsonString = JSON.stringify(sampleObject);
      const encryptedString = encryptAES(jsonString, generatedKey);

      // Decrypt and parse JSON
      const decryptedJsonString = decryptAES(encryptedString, generatedKey);
      const decryptedObject = JSON.parse(decryptedJsonString);
      console.log('Decrypted Object:', decryptedObject);
    } catch (err: unknown) {
      if (err instanceof Error) {
        let error = err.cause as EncryptionError;
        console.log('❌ Error:123', error.message);
      } else {
        console.log('❌ Unknown Error:', err);
      }
      setResult('An error occurred during encryption/decryption.');
    }
  };

  const handleAsyncESEncryption = async () => {
    const sampleObject = {
      name: 'John Doe',
      age: 30,
      roles: ['admin', 'editor'],
    };
    try {
      const generatedKey = generateAESKey(256);
      const jsonString = JSON.stringify(sampleObject);
      const encryptedString = await encryptAsyncAES(jsonString, generatedKey);
      console.log('encrypted Object:', encryptedString);

      // Decrypt and parse JSON
      const decryptedJsonString = await decryptAsyncAES(
        encryptedString,
        generatedKey
      );
      const decryptedObject = JSON.parse(decryptedJsonString);
      console.log('Decrypted Object:', decryptedObject);
    } catch (err: unknown) {
      if (err instanceof Error) {
        let error = err.cause as EncryptionError;
        console.log('❌ Error:123', error.message);
      } else {
        console.log('❌ Unknown Error:', err);
      }
      setResult('An error occurred during encryption/decryption.');
    }
  };

  const hashing = () => {
    try {
      console.log('--- Hashing ---');
      const sha256Hash = hashSHA256('Hello Hashing');
      console.log('SHA-256 Hash:', sha256Hash);

      const sha512Hash = hashSHA512('Hello Hashing');
      console.log('SHA-512 Hash:', sha512Hash);
    } catch (err) {
      console.log('error is', err);
    }
  };

  const hmac = () => {
    try {
      console.log('--- HMAC ---');
      const hmackey = generateHMACKey(256);
      const hmachash = hmacSHA256('Hello HMAC', hmackey);

      const hmackey512 = generateHMACKey(512);
      const hmachash512 = hmacSHA256('Hello HMAC', hmackey512);
      console.log('HMAC-SHA256:', hmachash, hmachash512);
    } catch (err) {
      console.log('error is', err);
    }
  };

  const signData = () => {
    const keyPair = generateECDSAKeyPair();
    const data = 'Hello, ECDSA!';
    const signature = signDataECDSA(data, keyPair.privateKey);
    const isValid = verifySignatureECDSA(data, signature, keyPair.publicKey);

    console.log('Signature:', signature);
    console.log('Is Valid Signature:', isValid);
  };

  const base64 = () => {
    try {
      console.log('--- Base64 Encoding/Decoding ---');
      const base64Encoded = base64Encode('Hello Base64 Encoding');
      console.log('Base64 Encoded:', base64Encoded);

      const base64Decoded = base64Decode(base64Encoded);
      console.log('Base64 Decoded:', base64Decoded);
    } catch (err) {
      console.log('error is', err);
    }
  };

  const createRandomString = () => {
    try {
      console.log('--- Utilities ---');
      const randomString = generateRandomString(16);
      console.log('Random String:', randomString);
    } catch (err) {
      console.log('error is', err);
    }
  };

  async function handleEncryptFileAES() {
    try {
      // Step 1: Write Sample Data to a File
      await RNFS.writeFile(inputPath, 'This is a sensitive file content.', 'utf8');
      console.log(`File written at: ${inputPath}`);

      const generatedKey = generateAESKey(256);
      console.log('generatedKey ', generatedKey);

  
      // Step 2: Encrypt the File
      const encryptedFilePath = await encryptFile(inputPath, outputPath, generatedKey);
      console.log('Encrypted File Path:', encryptedFilePath);
  
      // Step 3: Verify Encrypted File
      const encryptedFileExists = await RNFS.exists(outputPath);
      console.log('Encrypted File Exists:', encryptedFileExists);

      const decryptedContent = await decryptFile(outputPath, generatedKey);
      console.log('Decrypted File Content:', decryptedContent);
  
      // Step 5: Write Decrypted Content to a New File
      await RNFS.writeFile(decryptedPath, decryptedContent, 'utf8');
      console.log(`Decrypted file saved at: ${decryptedPath}`);
    } catch (error) {
      console.error('Encryption Error:', error);
    }
  }


  return (
    <View style={{ flex: 1, alignItems: 'center', paddingTop: 80 }}>
      <Button title="Encrypt & Decrypt AES" onPress={handleAESEncryption} />
      <Button
        title="Async Encrypt & Decrypt AES"
        onPress={handleAsyncESEncryption}
      />

<Button
        title="Encrypt & Decrypt File"
        onPress={handleEncryptFileAES}
      />

      <Button title="Encrypt & Decrypt RSA" onPress={handleRSAEncryption} />
      <Button
        title="Encrypt & Decrypt RSA"
        onPress={handleAsyncRSAEncryption}
      />

      <Button title="Hashing" onPress={hashing} />

      <Button title="HMAC" onPress={hmac} />

      <Button title="Base64 Encoding" onPress={base64} />

      <Button title="Generate random" onPress={createRandomString} />

      <Button title="Sign & Validate data" onPress={signData} />

      <Text style={styles.resultText}>{result}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  inputContainer: {
    marginVertical: 20,
    alignItems: 'center',
    width: '80%',
  },
  textInput: {
    borderWidth: 1,
    borderColor: '#ccc',
    borderRadius: 5,
    padding: 10,
    width: '100%',
    marginTop: 10,
  },
  resultText: {
    marginVertical: 20,
    textAlign: 'center',
    fontSize: 16,
  },
  counterWrapper: {
    height: 150,
    justifyContent: 'center',
    alignItems: 'center',
  },
  counterView: {
    width: 280,
    height: 140,
  },
  text: {
    marginBottom: 20,
    fontSize: 16,
  },
});

🛠️ 7.Web Usage Examples

import { View, Text,  StyleSheet, Button } from 'react-native';
import  { generateAESKey, encryptAES, decryptAES, generateRSAKeyPair, encryptRSA, decryptRSA, generateECDSAKeyPair, signDataECDSA, verifySignatureECDSA, generateHMACKey, hmacSHA256, hmacSHA512, hashSHA256, hashSHA512, generateRandomString, base64Decode, base64Encode } from 'rn-encryption';

export default function HomeScreen() {

  const handleAESEncryption = async () => {
    const sampleObject = {
      name: 'John Doe',
      age: 30,
      roles: ['admin', 'editor'],
    };
    try {
      const generatedKey = await generateAESKey();
      const jsonString = JSON.stringify(sampleObject);
      const encryptedString = await encryptAES(jsonString, generatedKey);

      // Decrypt and parse JSON
      const decryptedJsonString = await decryptAES(encryptedString, generatedKey);
      const decryptedObject = JSON.parse(decryptedJsonString);
      console.log('Decrypted Object:', generatedKey, );
    } catch (err: unknown) {
     
        console.log('❌ Error:123', err);
     
    }
  };

  async function handleAsyncRSAEncryption() {
    const plaintext = 'Hello, RSA Encryption!';
    const generatedKeys = await generateRSAKeyPair();
    try {
      // Step 1: Encrypt the plaintext using the Public Key
      const encryptedData = await encryptRSA(
        plaintext,
        generatedKeys.publicKey
      );
      // Step 2: Decrypt the encrypted data using the Private Key
      const decryptedData = await decryptRSA(
        encryptedData,
        generatedKeys.privateKey
      );
      // Step 3: Validation
      if (decryptedData === plaintext) {
        console.log('✅ RSA Encryption and Decryption Successful!');
      } else {
        console.error('❌ Decrypted data does not match original plaintext!');
      }
    } catch (error) {
      console.error('⚠️ RSA Error:', error);
    }
  }

  const hashing = async () => {
    try {
      console.log('--- Hashing ---');
      const sha256Hash = await hashSHA256('Hello Hashing');
      console.log('SHA-256 Hash:', sha256Hash);

      const sha512Hash = await hashSHA512('Hello Hashing');
      console.log('SHA-512 Hash:', sha512Hash);
    } catch (err) {
      console.log('error is', err);
    }
  };

  const hmac = async() => {
    try {
      const macKey = await generateHMACKey(256)
      console.log('--- HMAC ---',macKey);

      const hmachash = await hmacSHA256('Hello HMAC', macKey);
      console.log('HMAC-SHA256:', hmachash);
    } catch (err) {
      console.log('error is', err);
    }
  };

  const base64 = async () => {
    try {
      console.log('--- Base64 Encoding/Decoding ---');
      const base64Encoded = await base64Encode('Hello Base64 Encoding');
      console.log('Base64 Encoded:', base64Encoded);

      const base64Decoded =await base64Decode(base64Encoded);
      console.log('Base64 Decoded:', base64Decoded);
    } catch (err) {
      console.log('error is', err);
    }
  };

  const createRandomString = async () => {
    try {
      console.log('--- Utilities ---');
      const randomString = await generateRandomString(16);
      console.log('Random String:', randomString);
    } catch (err) {
      console.log('error is', err);
    }
  };

  const signData = async () => {
    const keyPair = await generateECDSAKeyPair();
    const data = 'Hello, ECDSA!';
    const signature = await signDataECDSA(data, keyPair.privateKey);
    const isValid = await verifySignatureECDSA(data, signature, keyPair.publicKey);

    console.log('Signature:', signature);
    console.log('Is Valid Signature:', isValid);
  };

  return (
    <View style={styles.container}>
      <Text style={styles.header}>Dynamic Routing Example</Text>
      <Button title="Encrypt & Decrypt AES" onPress={handleAESEncryption} />
      <Button title="Encrypt & Decrypt RSA" onPress={handleAsyncRSAEncryption} />
      <Button title="Sign data" onPress={signData} />
      <Button title="Hashing" onPress={hashing} />
      <Button title="HMAC" onPress={hmac} />
      <Button title="Base64 Encoding" onPress={base64} />
      <Button title="Generate random" onPress={createRandomString} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    justifyContent: 'center',
    backgroundColor: '#f4f4f4',
  },
  header: {
    fontSize: 20,
    fontWeight: 'bold',
    marginBottom: 16,
    textAlign: 'center',
  },
  item: {
    padding: 16,
    marginVertical: 8,
    backgroundColor: '#fff',
    borderRadius: 8,
    shadowColor: '#000',
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 2,
  },
  text: {
    fontSize: 16,
  },
});

Keychain Integration for keys

  • Key Storage: It is recommended to save encryption keys in Keychain (iOS) and Keystore (Android) for enhanced security.
  • Example Implementation: You can refer to an example in this repository for guidance.
  • Customization: The provided example serves as a sample implementation and can be modified according to specific requirements.

🐞 8. Troubleshooting

  1. Library Not Found:

    • Run npx react-native link rn-encryption.
    • Clean and rebuild the project.
  2. AES Key Size Error:

    • Ensure the AES key is 16, 24, or 32 characters.
  3. RSA Key Parsing Issue:

    • Verify the RSA key is in Base64-encoded PEM format.
  4. Permission Issues:

    • Ensure native permissions are set correctly in AndroidManifest.xml or iOS Podfile.

9. Best Practices

  1. Do Not Hardcode Keys: Use .env or secure storage for keys.
  2. Handle Errors Gracefully: Wrap calls in try-catch blocks.
  3. Validate Key Sizes: Ensure AES and RSA keys meet size requirements.

10. FAQ

Q: Does the library support both Android and iOS?
A: Partially, rn-encryption fully supports ios and encryptAES & decryptAES for Android platforms.

Q: Can I use the library in Expo?
A: Yes, if you're using Expo Bare Workflow.

Q: How do I debug encryption issues?
A: Add console logs and verify that keys and data are correctly passed.


11. Security Best Practices

  1. Use Strong Keys: Always use AES-256 for symmetric encryption and RSA-2048 for asymmetric encryption.
  2. Key Storage: Store keys securely using Android Keystore and iOS Keychain.
  3. Avoid Hardcoding Keys: Do not hardcode encryption keys directly in the app.

📚 Encryption Mechanisms: Android (JCA) vs iOS (CryptoKit)

Feature Android (JCA) iOS (CryptoKit)
Symmetric Encryption ✅ AES-256-GCM ✅ AES-256-GCM
Asymmetric Encryption ✅ RSA-2048 ✅ RSA-2048
Key Derivation ✅ PBKDF2 ✅ PBKDF2 / ✅ HKDF
Hashing ✅ SHA-256, ✅ SHA-512 ✅ SHA-256, ✅ SHA-512
Message Authentication ✅ HMAC-SHA256 ✅ HMAC-SHA256
Digital Signatures ✅ ECDSA ✅ ECDSA (via CryptoKit)
Key Management ✅ Android Keystore ✅ iOS Keychain
Initialization Vector (IV) ✅ SecureRandom (12/16 Bytes) ✅ Randomized IV (12 Bytes)
Authentication Tag ✅ Built-in (GCM Mode) ✅ Built-in (GCM Mode)
Error Handling ✅ Strong Validation ✅ Strong Validation
Performance ⚡ Optimized for Android ⚡ Optimized for iOS
Parallel Processing ✅ Supported in GCM ✅ Supported in GCM
Cryptographic Library ✅ Java Cryptography (JCA) ✅ CryptoKit