Package Exports
- @mdaemon/rsa-message
- @mdaemon/rsa-message/dist/rsa-message.mjs
- @mdaemon/rsa-message/dist/rsa-message.umd.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@mdaemon/rsa-message) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@mdaemon/rsa-message - RSA message encryption, signing, decryption, verification, and ECDH key exchange using webcrypto or node crypto
[ @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 keyprivateKey
: 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 ArrayBuffermessage
: The original message that was signeduserId
: 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 userpublicKey
: Base64 encoded public keyverifyKey
: 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 encryptuserId
: The recipient's user IDuseMasterKey
: 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 objectsender
: The sender's user IDuseMasterKey
: 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 keyencryptor
: 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.
encryptedKey
: Base64 encoded encrypted master AES keyencryptor
: 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 encryptuserId
: The recipient's user IDuseMasterKey
: 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 objectsender
: The sender's user IDuseMasterKey
: 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 userpublicKey
: 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 identifiersalt
: 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 encryptuserId
: 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 messageuserId
: 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 messageuserId
: 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 keydata
: 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 messagesdecryptWithMasterAESKeyUnsafe()
- For master AES key encrypted messagesdecryptWithSharedKeyUnsafe()
- 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