encryption-helpers
v22.0.0
Published
Angular encryption utilities: symmetric (AES) and asymmetric (RSA-OAEP) encryption services, encrypted-string detection, and secure random helpers
Readme
Encryption Helpers
Angular encryption utilities: symmetric (AES) and asymmetric (RSA-OAEP) encryption services, an encrypted-string detector, and cryptographically-secure random helpers.
Installation
npm install encryption-helpersConfiguration
The symmetric service derives its default key from the app id you provide via EncryptionHelpersModule.forRoot(...). If no app id is provided, a warning is logged and encrypt/decrypt return empty/null.
import { NgModule } from '@angular/core';
import { EncryptionHelpersModule } from 'encryption-helpers';
@NgModule({
imports: [
EncryptionHelpersModule.forRoot({ appId: '550e8400-e29b-41d4-a716-446655440000' })
]
})
export class AppModule { }Exports
| File | Exports |
|------|---------|
| symmetrical-encryption.service.ts | SymmetricalEncryptionService |
| asymmetrical-encryption.service.ts | AsymmetricalEncryptionService |
| encryption-test.service.ts | EncryptionTestService |
| random.ts | Random, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomStr, RandomSignature, UUID, UUID_STR |
| encryption-helpers.module.ts | EncryptionHelpersModule |
| models/encryption-config.model.ts | EncryptionConfig, EncryptionConfigInterface |
| models/encryption-config.token.ts | ENCRYPTION_CONFIG_TOKEN |
| app.service.ts | AppService |
SymmetricalEncryptionService (AES)
Fast AES encryption for data at rest. Backed by crypto-js.
Implementation
- Algorithm: AES with
keySize: 16,mode: ECB,padding: Pkcs7. - Key/IV: derived from the provided key string. Both the key and IV use the same value.
- Key alignment: the key/IV UTF-8
WordArrayis zero-padded up to a 4-byte boundary (alignKey) so any key length round-trips correctly (keys whose byte length isn't a multiple of 4 previously failed silently). - Serialization:
encryptrunsJSON.stringify(str)internally — pass any value;decryptreturns the JSON string (parse it yourself if needed). - ⚠️ Security note: AES-ECB with a shared key/IV and an 8-char cipher key is deliberately weak for convenience. Do not use this for high-security payloads.
Methods
| Method | Params | Returns | Description |
|--------|--------|---------|-------------|
| encrypt | str: any, key = this.appID | string \| undefined | Encrypts (JSON-stringifies then AES-ECB). Returns '' if str or key is falsy; undefined on error. |
| decrypt | str: string, key = this.appID | string \| null | Decrypts AES ciphertext back to the JSON string. Returns null if input/key falsy, on malformed ciphertext, or on empty result. |
| generateCipherKey | — | string | 8-char random key from two RandomSignature() values. |
| createSignature | url: string, len = 16 | string | SHA-256 of url, truncated to len chars, uppercase hex. |
| normalizeURL | url: string | string | Strips http(s)://, www., and trailing slashes. |
| generateSignature | url: string | string | SHA-256 hex of the normalized URL (stable across protocol/www/trailing-slash variants). |
Samples
import { Component, inject } from '@angular/core';
import { SymmetricalEncryptionService } from 'encryption-helpers';
@Component({ selector: 'app-demo', template: '...' })
export class DemoComponent {
private enc = inject(SymmetricalEncryptionService);
roundTrip() {
const cipher = this.enc.encrypt({ secret: 'sensitive', id: 123 });
console.log(cipher); // base64 AES ciphertext
const plain = this.enc.decrypt(cipher!); // '{"secret":"sensitive","id":123}'
console.log(JSON.parse(plain!)); // { secret: 'sensitive', id: 123 }
}
signatures() {
this.enc.createSignature('https://example.com/path'); // 16-char uppercase hex
this.enc.createSignature('https://example.com/path', 8); // custom length
// normalizeURL strips protocol/www/trailing slash
this.enc.normalizeURL('https://www.example.com/path/'); // 'example.com/path'
// generateSignature is stable regardless of protocol/www/trailing-slash form
this.enc.generateSignature('https://www.example.com/path/') ===
this.enc.generateSignature('http://example.com/path'); // true
}
}AsymmetricalEncryptionService (RSA-OAEP)
Public/private key encryption for secure key exchange and payload encryption. Backed by the Web Crypto API (crypto.subtle) and returns RxJS Observables.
Implementation
- Algorithm:
RSA-OAEP,modulusLength1024/2048/4096 (default 2048),publicExponent65537,hashSHA-256. - Key usability: keys are
extractablewith["encrypt", "decrypt"]. - Encryption output:
encryptDatareturns the ciphertext as a base64 string (not raw bytes). - Key formats: PEM helpers support
pkcs8/spki/rawformats.
Methods
| Method | Params | Returns | Description |
|--------|--------|---------|-------------|
| generateKeyPair | modulusLength = 2048 | Observable<CryptoKeyPair> | Generates an RSA-OAEP key pair. |
| encryptData | publicKey: CryptoKey, data: string | Observable<string> | Encrypts a string; returns base64 ciphertext. |
| decryptData | privateKey: CryptoKey, encryptedData: string | Observable<string> | Decrypts base64 ciphertext back to a string. |
| pemToArrayBuffer | pem: string | ArrayBuffer | Parses a PEM string (strips headers/footers/newlines, base64-decodes). |
| base64ToArrayBuffer | base64: string | ArrayBuffer | Decodes a base64 string. |
| arrayBufferToBase64 | buffer: ArrayBuffer | string | Encodes bytes to base64. |
| base64ToPEM | base64Key: string, publicKey = true | string | Wraps base64 into BEGIN/END PUBLIC/PRIVATE KEY PEM with 64-char lines. |
| pemToCryptoKey | pem, algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[], format = "pkcs8" | Observable<CryptoKey> | Imports a PEM key via crypto.subtle.importKey. |
Samples
import { Component, inject } from '@angular/core';
import { firstValueFrom } from 'rxjs';
import { AsymmetricalEncryptionService } from 'encryption-helpers';
@Component({ selector: 'app-rsa-demo', template: '...' })
export class RsaDemoComponent {
private rsa = inject(AsymmetricalEncryptionService);
async fullRoundTrip() {
const { publicKey, privateKey } = await firstValueFrom(this.rsa.generateKeyPair());
const cipher = await firstValueFrom(this.rsa.encryptData(publicKey, 'hello secret'));
const plain = await firstValueFrom(this.rsa.decryptData(privateKey, cipher));
console.log(plain); // 'hello secret'
}
async importPem() {
const algorithm: RsaHashedKeyGenParams = {
name: 'RSA-OAEP',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
};
const { publicKey } = await firstValueFrom(this.rsa.generateKeyPair());
const exported = await crypto.subtle.exportKey('spki', publicKey);
const pem = this.rsa.base64ToPEM(this.rsa.arrayBufferToBase64(exported), true);
const key = await firstValueFrom(
this.rsa.pemToCryptoKey(pem, algorithm, true, ['encrypt'], 'spki')
);
// key is now usable with encryptData()
}
}EncryptionTestService
Heuristic detector for whether a string "looks" encrypted.
Methods
| Method | Params | Returns | Description |
|--------|--------|---------|-------------|
| isEncrypted | str: string | boolean | true if str matches a Base64 pattern, a hexadecimal pattern (even length), or has high entropy (> 60% unique characters). Non-string input → false. |
⚠️ Heuristic only — can produce false positives. Not for cryptographic verification.
import { inject } from '@angular/core';
import { EncryptionTestService } from 'encryption-helpers';
const test = inject(EncryptionTestService);
test.isEncrypted('U2FsdGVkX1+abc=='); // true (base64)
test.isEncrypted('deadbeef'); // true (hex)
test.isEncrypted('hello world'); // false (low entropy)Random Utilities
Cryptographically secure random helpers using the Web Crypto API.
| Function | Signature | Returns | Description |
|----------|-----------|---------|-------------|
| Random | () | number | Random float in [0, 1) from crypto.getRandomValues. |
| RandomNumber | (min, max) | number | Random integer in [min, max] inclusive. |
| RandomNumbers | (min, max, length) | number[] | Array of length random integers in [min, max]. |
| RandomNumbersUnique | (min, max, length) | number[] | Array of unique random integers; clamps length to range size; throws if max attempts exceeded. |
| RandomStr | () | string | Base-36 string from a random Uint32Array value. |
| RandomSignature | () | number | Random unsigned 32-bit integer. |
| UUID | () | string | RFC-4122 UUID via crypto.randomUUID(). |
| UUID_STR | () | string | Legacy (non-crypto-API) UUID fallback. |
import {
Random, RandomNumber, RandomNumbers, RandomNumbersUnique,
RandomStr, RandomSignature, UUID, UUID_STR,
} from 'encryption-helpers';
Random(); // 0.7382
RandomNumber(1, 100); // 42
RandomNumbers(1, 100, 5); // [23, 87, 5, 64, 41]
RandomNumbersUnique(1, 50, 10); // 10 unique numbers in 1..50
RandomStr(); // '3k8d2j'
RandomSignature(); // 2847593
UUID(); // '550e8400-e29b-41d4-a716-446655440000'
UUID_STR(); // 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'Key Pair Generation on Mac
Generate an RSA key pair with the built-in openssl command-line tool (available on macOS and most Unix-like systems).
Generate the private key
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048Extract the public key
openssl rsa -pubout -in private_key.pem -out public_key.pemView the keys
cat private_key.pem # private key
cat public_key.pem # public keyThe resulting PEM files can be imported with AsymmetricalEncryptionService.pemToCryptoKey(...) for use in encryptData/decryptData.
Security Considerations
- Key management — never commit private keys or
appIdsecrets to source control; use environment variables or a vault. - Unique
appId— must be unique per application. - Client-side limitation — browser encryption is not a substitute for server-side security.
- AES-ECB weakness — the symmetric service uses ECB with a shared key/IV and short cipher key; avoid for high-security data.
- Key rotation — rotate RSA key pairs and
appIdperiodically. EncryptionTestService— heuristic only; false positives possible.
Development
npm run test:encryption-helpers # vitest suite
ng build encryption-helpers # build with ng-packagr