npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-secure-sign

v0.0.1

Published

react-native-secure-sign lets you generates and use cryptographic signatures backed by hardware security on iOS and Android

Readme

react-native-secure-sign

react-native-secure-sign provides a cross-platform API to generate and use non-exportable signing keys stored in Android Keystore(hardware-backed via TEE or StrongBox) and iOS Secure Enclave, with optional biometric authentication.

Why use hardware-backed signing keys?

Keys generated by react-native-secure-sign are stored inside the device’s secure hardware and can never be extracted, even by the application itself.

Hardware-backed means that cryptographic keys are generated, stored, and used inside dedicated secure hardware, not in normal app memory or software.

This provides strong security guarantees:

  • Private keys are protected from malware and memory dumps
    The key material never enters application memory and cannot be read or copied.

  • Signatures can only be created on the physical device
    A valid signature proves that the operation was performed on a specific device.

  • Biometric authentication can be enforced for every signing operation
    Each signature can require Face ID, Touch ID, or Android biometric authentication.

  • Compromising the app does not compromise the signing key
    Even with full app-level compromise, the private key remains inaccessible.

Hardware-backed keys provide a higher level of security than software-based key storage and are recommended for authentication, device binding, and high-trust cryptographic operations.

Installation

Important: This library requires React Native's New Architecture (Fabric + TurboModules) to be enabled. The legacy architecture is not supported.

npm install react-native-secure-sign
# or
yarn add react-native-secure-sign

React Native CLI

iOS

  1. If you generate key with requireUserAuthentication set to true you need to add Face ID usage description to your ios/YourApp/Info.plist:
<key>NSFaceIDUsageDescription</key>
<string>This app uses Face ID / Touch ID to securely authenticate key operations in the Secure Enclave.</string>

This permission is required for biometric authentication when using the Secure Enclave on iOS.

Expo (Bare Workflow)

This library supports Expo bare workflow (projects created with expo prebuild) and builds created with expo-dev-client. However, The app won't work with Expo Go.

iOS

  1. If you generate key with requireUserAuthentication then add Face ID usage description in your app.json or app.config.js:
{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSFaceIDUsageDescription": "This app uses Face ID / Touch ID to securely authenticate key operations in the Secure Enclave."
      }
    }
  }
}

Device Requirements

Note: The library requires devices with hardware-backed keystore support (most modern Android devices) and Secure Enclave support (iOS devices with Touch ID/Face ID). Use checkHardwareSupport() to verify device compatibility.

Usage

Methods

generate(keyId: string, options?: { requireUserAuthentication?: boolean }): Promise<string>

Generates a new key pair in the Secure Enclave (iOS) or Hardware Security Module (Android).

Parameters:

  • keyId (string): Unique identifier for the key
  • options (object, optional):
    • requireUserAuthentication (boolean, default: true): Require biometric authentication to use the key

Returns: Promise - Base64url-encoded SPKI DER public key

Example:

const publicKey = await generate('my-key', { requireUserAuthentication: true });

getPublicKey(keyId: string): Promise<string>

Retrieves the public key for an existing key pair.

Parameters:

  • keyId (string): Unique identifier for the key

Returns: Promise - Base64url-encoded SPKI DER public key

Example:

const publicKey = await getPublicKey('my-key');

removeKey(keyId: string): Promise<void>

Removes a key pair from the secure storage.

Parameters:

  • keyId (string): Unique identifier for the key

Example:

await removeKey('my-key');

checkHardwareSupport(): Promise<boolean>

Checks if the device supports hardware-backed secure storage.

Returns: Promise - Whether hardware-backed security is supported

Example:

const supported = await checkHardwareSupport();

sign(keyId: string, information: string): Promise<string>

Signs information using a private key stored in the hardware-backed secure storage.

Parameters:

  • keyId (string): Unique identifier for the key
  • information (Base64url string): Information to sign

Returns: Promise - Base64url-encoded P1363 signature

Error Codes

This library returns only error codes, not error messages. All error handling should be based on the numeric codes.

Error Code Structure

Error codes are organized by category:

  • 1001-1012: Key generation and management errors
  • 2001-2004: Biometric authentication errors
  • 3001: Decode error
  • 4001-4002: Signature conversion errors
  • 9999: Unknown/unexpected errors

Error Codes

Key Generation and Management (1001-1012)

| Code | Description | Possible Causes | | ------ | ------------------------------------ | -------------------------------------------------------------- | | 1001 | Key generation failed | Secure Enclave error, insufficient permissions, hardware issue | | 1002 | Public key extraction failed | Key exists but public key cannot be extracted | | 1003 | Access control creation failed | Invalid biometric settings, system error | | 1004 | Key deletion failed | Authentication error, invalid parameters | | 1005 | Key not found | The specified key was not found in the Keychain | | 1006 | Invalid key ID | The provided key identifier is invalid or cannot be processed | | 1007 | Invalid key reference | The retrieved key reference is not of the expected type | | 1008 | Authentication failed | Biometric or passcode authentication failed for key access | | 1009 | Keychain query failed | A general error occurred during a Keychain query operation | | 1010 | Public key format conversion failed | SEC1 to SPKI DER conversion failed (Rust FFI error) | | 1011 | Key already exists | Attempting to generate key with existing alias | | 1012 | Key info extraction failed (Android) | Cannot retrieve key properties from KeyStore |

Biometric Authentication (2001-2004)

| Code | Description | Possible Causes | | ------ | -------------------------------------- | ------------------------------------------------------- | | 2001 | Biometric authentication not available | Device doesn't support biometrics, disabled in settings | | 2002 | No biometric data enrolled | User hasn't set up Touch ID/Face ID | | 2003 | Biometric authentication locked out | Too many failed biometric attempts | | 2004 | Authentication cancelled | User cancelled the biometric authentication prompt |

Decode Error

| Code | Description | Possible Causes | | ------ | ------------ | ---------------------- | | 3001 | Decode error | Decode base64Url error |

Signature Conversion Errors (4001-4002)

| Code | Description | Possible Causes | | ------ | --------------------------- | ----------------------------------------------- | | 4001 | Invalid DER format | DER signature format is invalid or corrupted | | 4002 | Signature conversion failed | Failed to convert DER signature to P1363 format |

Unknown Errors (9999)

| Code | Description | Possible Causes | | ------ | ------------- | -------------------------------------------- | | 9999 | Unknown error | Unexpected system error, unhandled exception |

Usage in JavaScript

Import

import {
  generate,
  sign,
  getPublicKey,
  removeKey,
  checkHardwareSupport,
} from 'react-native-secure-sign';

checkHardwareSupport()

Check if the device supports hardware-backed secure storage before using other methods.

try {
  const supported = await checkHardwareSupport();
  if (supported) {
    console.log('Device supports hardware signing');
  } else {
    console.log('Device does not support hardware signing');
  }
} catch (error) {
  console.error('Error checking hardware support:', error);
}

generate()

Generate a new key pair in the Secure Enclave (iOS) or Hardware Security Module (Android).

try {
  const publicKey = await generate('my-unique-key-id', {
    requireUserAuthentication: true,
  });
  console.log('Public Key (Base64url):', publicKey);
} catch (error) {
  console.error('Error Code:', error.code);
  switch (error.code) {
    case '1011':
      console.log('Key already exists. Remove it first or use a different keyId');
      break;
    case '2001':
      console.log('Biometric authentication not available on this device');
      break;
    case '2002':
      console.log('Please set up Touch ID/Face ID in device settings');
      break;
    case '2003':
      console.log('Too many failed biometric attempts');
      break;
    case '1001':
      console.log('Failed to generate key');
      break;
    case '1003':
      console.log('Failed to create access control');
      break;
    case '1006':
      console.log('Invalid key ID provided');
      break;
    default:
      console.log('Unknown error:', error.code);
      break;
  }
}

getPublicKey()

Retrieve the public key for an existing key pair.

try {
  const publicKey = await getPublicKey('my-unique-key-id');
  console.log('Public Key (Base64url):', publicKey);
} catch (error) {
  console.error('Error Code:', error.code);
  switch (error.code) {
    case '1005':
      console.log('Key not found. Generate a key first');
      break;
    case '1006':
      console.log('Invalid key ID provided');
      break;
    case '1008':
      console.log('Authentication failed or cancelled');
      break;
    case '1002':
      console.log('Failed to extract public key');
      break;
    default:
      console.log('Unknown error:', error.code);
      break;
  }
}

sign()

Sign data using a private key stored in hardware-backed secure storage.

try {
  const dataToSign = 'Hello, World!';
  const dataBase64url = "SGVsbG8sIFdvcmxkIQ"
  
  const signature = await sign('my-unique-key-id', dataBase64url);
  console.log('Signature (Base64url P1363):', signature);
} catch (error) {
  console.error('Error Code:', error.code);
  switch (error.code) {
    case '1005':
      console.log('Key not found. Generate a key first');
      break;
    case '1008':
      console.log('Authentication failed or cancelled');
      break;
    case '2001':
      console.log('Biometric authentication not available');
      break;
    case '2002':
      console.log('Please set up biometric authentication');
      break;
      console.log('Too many failed attempts. Unlock device first');
      break;
    case '2004':
      console.log('User cancelled authentication');
      break;
    case '3001':
      console.log('Invalid input format. Provide Base64url encoded data');
      break;
    case '4001':
      console.log('Invalid signature format');
      break;
    case '4002':
      console.log('Failed to convert signature format');
      break;
    case '5001':
      console.log('Signing algorithm not supported');
      break;
    default:
      console.log('Unknown error:', error.code);
      break;
  }
}

removeKey()

Remove a key pair from hardware.

try {
  await removeKey('my-unique-key-id');
  console.log('Key removed successfully');
} catch (error) {
  console.error('Error Code:', error.code);
  switch (error.code) {
    case '1005':
      console.log('Key not found. It may already be removed');
      break;
    case '1004':
      console.log('Failed to delete key');
      break;
    case '1006':
      console.log('Invalid key ID provided');
      break;
    case '1008':
      console.log('Authentication required to remove key');
      break;
    default:
      console.log('Unknown error:', error.code);
      break;
  }
}

Testing with Backend

To test the complete signing process, you can run the backend and connect it to your mobile application:

  1. Clone the backend repository:

    git clone https://github.com/react-native-secure-sign/react-native-secure-sign-be.git
    cd react-native-secure-sign-be
  2. Run the backend:

    # Follow the instructions in the backend repository
    # The backend runs on port 8080 by default
  3. Configure the mobile application:

    • Ensure that the mobile app and backend are on the same network
    • Update API_BASE in your app code to point to your computer's IP address (e.g., http://192.168.1.100:8080)
  4. Test the signing process:

    • Run the mobile application
    • Execute the registration flow, which will generate a key, sign the challenge, and send the data to the backend
    • The backend will verify the signature and confirm the correctness of the process

The backend implements a complete registration flow with signature verification, allowing you to comprehensively test the entire signing process using hardware-backed security keys.

Example of a complete registration flow using the library:

import {
  generate,
  sign,
  getPublicKey,
  removeKey,
  checkHardwareSupport,
} from 'react-native-secure-sign';
import axios from 'axios';

const KEY_ID = 'com.example.app.key';

// react-native-secure-sign-be runs on port 8080
const API_BASE = 'http://X.X.X.X:8080'

async function registerAccount() {
  try {
    // 1. Initiate registration challenge with server
    const challengeResponse = await axios.post(
      `${API_BASE}/v1/register/initiate-challenge`
    );
    const { challengeId, informationToSign_b64u } = challengeResponse.data;

    // 2. Generate key pair (if not exists)
    let publicKey;
      publicKey = await generate(KEY_ID, {
        requireUserAuthentication: true,
      });

    // 3. Sign the challenge
    const signature = await sign(KEY_ID, informationToSign_b64u);

    // 4. Complete registration with server
    const finishResponse = await axios.post(
      `${API_BASE}/v1/register/finish-challenge`,
      {
        challengeId,
        signature,
        publicKey,
      }
    );

    console.log('Registration successful:', finishResponse.data);
    return finishResponse.data;
  } catch (error) {
      switch (error.code) {
        // Key Management Errors (1001-1012)
        case '1001':
          console.error('Key generation failed. Check device hardware support.');
          break;
        case '1002':
          console.error('Public key extraction failed.');
          break;
        case '1003':
          console.error('Access control creation failed. Check biometric settings.');
          break;
        case '1004':
          console.error('Key deletion failed.');
          break;
        case '1005':
          console.error('Key not found. Generate a key first.');
          break;
        case '1006':
          console.error('Invalid key ID provided.');
          break;
        case '1007':
          console.error('Invalid key reference.');
          break;
        case '1008':
          console.error('Authentication failed. Please try again.');
          break;
        case '1009':
          console.error('Keychain query failed. System error.');
          break;
        case '1010':
          console.error('Public key format conversion failed.');
          break;
        case '1011':
          console.error('Key already exists. Use existing key or remove it first.');
          break;
        case '1012':
          console.error('Key info extraction failed.');
          break;
        // Biometric Authentication Errors (2001-2004)
        case '2001':
          console.error('Biometric authentication not available. Enable biometrics in device settings.');
          break;
        case '2002':
          console.error('No biometric data enrolled. Please set up Touch ID/Face ID.');
          break;
        case '2003':
          console.error('Biometric authentication locked out. Please unlock device and try again.');
          break;
        case '2004':
          console.error('Authentication cancelled by user.');
          break;
        // Decode Error (3001)
        case '3001':
          console.error('Invalid input. Check base64Url format.');
          break;
        // Signature Conversion Errors (4001-4002)
        case '4001':
          console.error('Invalid DER format.');
          break;
        case '4002':
          console.error('Signature conversion failed.');
          break;
        // Unknown Error (9999)
        case '9999':
          console.error('Unknown error occurred.');
          break;
        default:
          console.error('Unhandled error occurred:', error);
      }
  }
}