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

@mdaemon/rsa-message

v2.2.1

Published

RSA message encryption, signing, decryption, and verification using webcrypto or node crypto

Downloads

108

Readme

@mdaemon/rsa-message - RSA message encryption, signing, decryption, verification, and ECDH key exchange using webcrypto or node crypto

Dynamic JSON Badge Static Badge install size Dynamic JSON Badge Node.js CI

[ @mdaemon/rsa-message on npm ]

Note

This library uses the browser's Web Crypto API for key generation and encryption. This means that it is not compatible with older browsers that do not support the Web Crypto API.

Keys are not persistent between browser sessions. This means that if you close your browser and reopen it, you will need to re-initialize the RSAMessage instance.

This library is ideal for temporary use cases where you don't need to persist keys between sessions. If you need to persist keys between sessions, you should use a library like localForage to store them in the browser's local storage.

Install

$ npm install @mdaemon/rsa-message --save

Usage

Node CommonJS

const RSAMessage = require("@mdaemon/emitter/dist/rsa-message.cjs");

Node Modules

import RSAMessage from "@mdaemon/emitter/dist/rsa-message.mjs";

Web

<script type="text/javascript" src="/path_to_modules/dist/rsa-message.umd.js">

Usage

// Initialize RSA for a user
const sender = new RSAMessage();
const { publicKey, verifyKey } = await sender.init();

const recipient = new RSAMessage();
const { publicKey: theirPublicKey, verifyKey: theirVerifyKey } = await recipient.init();
// Store public key from another user to use for encryption and verification
sender.setPublicKey('otherUserId', theirPublicKey, theirVerifyKey);
recipient.setPublicKey('senderId', publicKey, verifyKey);

// Encrypt a message
const encrypted = await rsa.encryptMessage('Hello, World!', 'otherUserId');

// Encode a message for transport
const encoded = rsa.exportEncryptedMessage(encrypted);

// Decode a message from transport
const decoded = rsa.importEncryptedMessage(encoded);

// Decrypt a message
const decrypted = await rsa.decryptMessage(decoded, 'senderId');

Derived Key Encryption (ECDH + AES-GCM)

For scenarios requiring perfect forward secrecy and efficient symmetric encryption, you can use ECDH key exchange:

// Initialize RSA instances for both users
const alice = new RSAMessage();
const bob = new RSAMessage();

await alice.init();
await bob.init();

// Generate ECDH key pairs for both users
const aliceECDHPublicKey = await alice.generateECDHKeyPair();
const bobECDHPublicKey = await bob.generateECDHKeyPair();

// Exchange ECDH public keys
alice.setECDHPublicKey('bob', bobECDHPublicKey);
bob.setECDHPublicKey('alice', aliceECDHPublicKey);

// Derive shared keys
await alice.deriveSharedKey('bob');
await bob.deriveSharedKey('alice');

// Encrypt and decrypt messages using shared keys
const encrypted = await alice.encryptWithSharedKey('Hello, Bob!', 'bob');
const decrypted = await bob.decryptWithSharedKey(encrypted, 'alice');

// Export/import shared key data for persistence
const sharedKeyData = alice.exportSharedKeyData('bob');
// Later, import the shared key data
alice.importSharedKeyData('bob', sharedKeyData);

Master AES Key Encryption (Group/Room Scenarios)

For scenarios where multiple users need to share the same encryption key (like group chats or rooms), you can use the master AES key functionality:

// Initialize RSA instances for group members
const admin = new RSAMessage();
const member1 = new RSAMessage();
const member2 = new RSAMessage();

await admin.init();
await member1.init();
await member2.init();

// Set up public keys for encryption/verification
admin.setPublicKey('self', admin.publickey, admin.verifykey);
admin.setPublicKey('member1', member1.publickey, member1.verifykey);
admin.setPublicKey('member2', member2.publickey, member2.verifykey);

// Admin generates a master AES key for the group
const masterKey = await admin.generateAndSetMasterAESKey();

// Admin shares the master key with group members
const keyForMember1 = await admin.exportMasterAESKeyForUser('member1');
const keyForMember2 = await admin.exportMasterAESKeyForUser('member2');

// Members receive and set up the master key
member1.setPublicKey('admin', admin.publickey, admin.verifykey);
member1.setVerifyKey('admin', admin.verifykey);
await member1.setMasterAESKeyFromEncrypted(keyForMember1, 'admin');

member2.setPublicKey('admin', admin.publickey, admin.verifykey);
member2.setVerifyKey('admin', admin.verifykey);
// Alternative: using setEncryptedMasterAESKey with encryptor parameter
member2.setEncryptedMasterAESKey(keyForMember2, 'admin');

// Now all members can encrypt/decrypt using the shared master key
const message = 'Hello, group!';

// Using dedicated master key methods (no RSA overhead)
const encrypted = await admin.encryptWithMasterAESKey(message);
const decrypted1 = await member1.decryptWithMasterAESKey(encrypted, 'admin');
const decrypted2 = await member2.decryptWithMasterAESKey(encrypted, 'admin');

// Or using regular methods with master key flag (backwards compatible)
const encrypted2 = await member1.encryptMessage(message, 'member2', true);
const decrypted3 = await member2.decryptMessage(encrypted2, 'member1', true);

API Reference

new RSAMessage()

Creates a new instance of the RSAMessage class.

init(publicKey?: string, privateKey?: string): Promise<string>

Initializes the keys for the user. Can either generate new keys or use existing keys.

  • publicKey: Optional base64 encoded public key
  • privateKey: Optional base64 encoded private key
  • Returns: Base64 encoded public key

publickey: string

Getter that returns the base64 encoded public key.

privatekey: string

Getter that returns the base64 encoded private key.

verifykey: string

Getter that returns the base64 encoded verification key used for signature verification.

signkey: string

Getter that returns the base64 encoded signing key used for creating message signatures.

signMessage(message: string): Promise<ArrayBuffer>

Signs a message using the private signing key.

  • message: The message to sign
  • Returns: Signature as ArrayBuffer

verifySignature(signature: ArrayBuffer, message: string, userId: string): Promise<boolean>

Verifies a message signature using the sender's public verification key.

  • signature: The signature to verify as ArrayBuffer
  • message: The original message that was signed
  • userId: The sender's user ID
  • Returns: Promise resolving to true if signature is valid, false otherwise

setPublicKey(userId: string, publicKey: string, verifyKey: string): void

Stores another user's public key.

  • userId: Unique identifier for the other user
  • publicKey: Base64 encoded public key
  • verifyKey: Base64 encoded verification key

hasPublicKey(userId: string): boolean

Checks if a user's public key is stored.

  • userId: Unique identifier for the user
  • Returns: true if the user's public key is stored, false otherwise

encryptMessage(message: string, userId: string, useMasterKey?: boolean): Promise<IRSAEncryptedMessage>

Encrypts and signs a message for a specific user.

  • message: The message to encrypt
  • userId: The recipient's user ID
  • useMasterKey: Optional - if true and master key is set, uses master AES key instead of generating new AES key
  • Returns: Encrypted message object

decryptMessage(encryptedData: IRSAEncryptedMessage, sender: string, useMasterKey?: boolean): Promise<string>

Decrypts and verifies a message from a specific user.

  • encryptedData: The encrypted message object
  • sender: The sender's user ID
  • useMasterKey: Optional - if true and master key is set, uses master AES key for decryption
  • Returns: Decrypted message

exportEncryptedMessage(message: IRSAEncryptedMessage): string

Exports an encrypted message object to a base64 encoded string for transport or storage.

  • message: The encrypted message object containing iv, encryptedMessage, encryptedAESKey and signature
  • Returns: Base64 encoded string representation of the encrypted message

importEncryptedMessage(encoded: string): IRSAEncryptedMessage

Imports a base64 encoded encrypted message string back into an encrypted message object.

  • encoded: Base64 encoded string previously created by exportEncryptedMessage
  • Returns: Decoded IRSAEncryptedMessage object containing iv, encryptedMessage, encryptedAESKey and signature

Master AES Key Methods

The master AES key functionality allows for efficient group or room-based encryption scenarios where multiple users share the same AES key, eliminating the need for RSA encryption/decryption of individual AES keys.

generateAndSetMasterAESKey(): Promise<string>

Generates a new AES master key, encrypts it with the current user's RSA public key, and stores it.

  • Returns: Base64 encoded encrypted master AES key

setEncryptedMasterAESKey(encryptedKey: string, encryptor?: string): void

Sets an encrypted master AES key (encrypted with this user's RSA public key).

  • encryptedKey: Base64 encoded encrypted master AES key
  • encryptor: Optional user ID who encrypted the key (defaults to "self")

getDecryptedMasterAESKey(): Promise<CryptoKey>

Decrypts and returns the master AES key as a CryptoKey for direct use.

  • Returns: The decrypted AES-GCM CryptoKey
  • Throws: Error if no master key is set

exportMasterAESKeyForUser(userId: string): Promise<string>

Encrypts the current master AES key with another user's RSA public key for sharing.

  • userId: The user ID to encrypt the master key for
  • Returns: Base64 encoded encrypted master AES key for the specified user
  • Throws: Error if no master key is set or user's public key not found

setMasterAESKeyFromEncrypted(encryptedKey: string, encryptor?: string): Promise<void>

Sets the master AES key from an encrypted key received from another user and immediately validates it by attempting decryption. Re-encrypts the key with the self key, so that if the from user's verifyKey changes the key can still be decrypted

  • encryptedKey: Base64 encoded encrypted master AES key
  • encryptor: Optional user ID who encrypted the key (defaults to "self")

Note: This method is equivalent to setEncryptedMasterAESKey() but performs immediate validation by testing decryption. Both methods now accept the same parameters for consistency.

encryptWithMasterAESKey(message: string): Promise<IRSAEncryptedMessage>

Encrypts a message using the master AES key (no RSA encryption of AES key).

  • message: The plaintext message to encrypt
  • Returns: Encrypted message object without encryptedAESKey field
  • Throws: Error if no master key is set

decryptWithMasterAESKey(encryptedData: IRSAEncryptedMessage, sender: string): Promise<string>

Decrypts a message using the master AES key (no RSA decryption needed).

  • encryptedData: The encrypted message object (should not include encryptedAESKey)
  • sender: The user ID who sent the message (for signature verification)
  • Returns: Decrypted message
  • Throws: Error if no master key is set or signature verification fails

decryptWithMasterAESKeyUnsafe(encryptedData: IRSAEncryptedMessage, sender: string): Promise<IDecryptionResult>

Decrypts a message using the master AES key without throwing on signature verification failure.

  • encryptedData: The encrypted message object (should not include encryptedAESKey)
  • sender: The user ID who sent the message (for signature verification)
  • Returns: Object with { message: string, verified: boolean }
  • Throws: Error if no master key is set or decryption fails (but not signature verification)

Enhanced encryptMessage(message: string, userId: string, useMasterKey?: boolean): Promise<IRSAEncryptedMessage>

The standard encrypt method now supports an optional parameter to use the master AES key.

  • message: The message to encrypt
  • userId: The recipient's user ID
  • useMasterKey: If true and master key is set, uses master AES key instead of generating new AES key
  • Returns: Encrypted message object (encryptedAESKey will be undefined if master key is used)

Enhanced decryptMessage(encryptedData: IRSAEncryptedMessage, sender: string, useMasterKey?: boolean): Promise<string>

The standard decrypt method now supports an optional parameter to use the master AES key.

  • encryptedData: The encrypted message object
  • sender: The sender's user ID
  • useMasterKey: If true and master key is set, uses master AES key for decryption
  • Returns: Decrypted message

Derived Key Encryption Methods (ECDH + PBKDF2)

generateECDHKeyPair(): Promise<string>

Generates an ECDH key pair for key exchange and returns the public key.

  • Returns: Base64 encoded ECDH public key

setECDHPublicKey(userId: string, publicKey: string): Promise<void>

Imports and stores another user's ECDH public key for key derivation.

  • userId: Unique identifier for the other user
  • publicKey: Base64 encoded ECDH public key

deriveSharedKey(userId: string, salt?: Uint8Array): Promise<void>

Derives a shared AES key using ECDH key exchange and PBKDF2 key derivation.

  • userId: The other user's identifier
  • salt: Optional salt for key derivation (generates random salt if not provided)

encryptWithSharedKey(message: string, userId: string): Promise<string>

Encrypts a message using the shared key derived with the specified user.

  • message: The message to encrypt
  • userId: The user identifier for the shared key
  • Returns: Base64 encoded encrypted message

decryptWithSharedKey(encryptedMessage: string, userId: string): Promise<string>

Decrypts a message using the shared key derived with the specified user.

  • encryptedMessage: Base64 encoded encrypted message
  • userId: The user identifier for the shared key
  • Returns: Decrypted message

decryptWithSharedKeyUnsafe(encryptedMessage: string, userId: string): Promise<IDecryptionResult>

Unsafe variant that returns verification status. Note: SharedKeyData doesn't include signatures, so verified is always true.

  • encryptedMessage: Base64 encoded encrypted message
  • userId: The user identifier for the shared key
  • Returns: Object with { message: string, verified: boolean } (verified is always true for shared keys)

exportSharedKeyData(userId: string): string

Exports shared key data for storage or transport.

  • userId: The user identifier for the shared key
  • Returns: Base64 encoded shared key data

importSharedKeyData(userId: string, data: string): Promise<void>

Imports previously exported shared key data.

  • userId: The user identifier for the shared key
  • data: Base64 encoded shared key data

hasSharedKey(userId: string): boolean

Checks if a shared key exists for the specified user.

  • userId: The user identifier
  • Returns: true if shared key exists, false otherwise

hasECDHPublicKey(userId: string): boolean

Checks if an ECDH public key is stored for the specified user.

  • userId: The user identifier
  • Returns: true if ECDH public key exists, false otherwise

removeSharedKey(userId: string): boolean

Removes the shared key for the specified user.

  • userId: The user identifier
  • Returns: true if key was removed, false if key didn't exist

removeECDHPublicKey(userId: string): boolean

Removes the ECDH public key for the specified user.

  • userId: The user identifier
  • Returns: true if key was removed, false if key didn't exist

Security Features

  • Uses RSA-OAEP for encryption and RSA-PSS for signatures
  • AES-GCM for symmetric message encryption
  • Implements message signing and signature verification
  • Master AES key functionality for efficient group/room encryption scenarios
  • ECDH key exchange with P-256 elliptic curve for perfect forward secrecy
  • PBKDF2 key derivation with 100,000 iterations for enhanced security
  • Derived key encryption using AES-GCM with 256-bit keys
  • Base64 encoding for message transport

Unsafe Decryption Methods

For scenarios where you want to decrypt a message even if signature verification fails, the library provides "unsafe" variants of the decrypt methods. These methods return both the decrypted message and verification status instead of throwing errors on signature verification failure.

IDecryptionResult Interface

All unsafe methods return an object implementing the IDecryptionResult interface:

interface IDecryptionResult {
  message: string;    // The decrypted message
  verified: boolean;  // Whether signature verification succeeded
}

Available Unsafe Methods

  • decryptMessageUnsafe() - For regular RSA-AES encrypted messages
  • decryptWithMasterAESKeyUnsafe() - For master AES key encrypted messages
  • decryptWithSharedKeyUnsafe() - For shared key encrypted messages (always verified: true)

Usage Example

try {
  const result = await receiver.decryptMessageUnsafe(encryptedData, 'sender');
  
  if (result.verified) {
    console.log('Message verified:', result.message);
  } else {
    console.log('Message unverified but readable:', result.message);
    console.warn('Signature verification failed - treat with caution');
  }
} catch (error) {
  console.error('Decryption failed:', error.message);
}

These methods are useful when you want to read the message content regardless of signature verification status, while still being informed about the verification result.

License

Published under the LGPL-2.1 license.

Published by MDaemon Technologies, Ltd. Simple Secure Email https://www.mdaemon.com