Skip to content

base-aes 🔐

npm package

NPM versionNPM Downloadsjsdelivr

Zero-dependency AES encryption library.

Features

  • Support for AES-ECB/CBC mode encryption/decryption
  • Complete PKCS#7 padding scheme implementation
  • Cross-platform byte-level data conversion tools
  • Type-safe array buffer operations
  • Tree-shakable modular architecture

Installation

bash
npm add base-aes
bash
pnpm add base-aes
bash
yarn add base-aes
html
<script src="https://cdn.jsdelivr.net/npm/base-aes/dist/index.umd.min.js"></script>
<!-- Global variable BaseAes available -->

Core API

Encryption Mode Comparison

ModeIV RequiredSecurityUse Cases
ECB❌ No IV⚠️ Weak (Same plaintext blocks produce same ciphertext)Fast encryption/low security needs
CBC✅ Yes✅ RecommendedStandard encryption scenarios

Encryption Modules

ts
// Base AES module
import { AES } from 'base-aes';
const cipher = new AES(key: Uint8Array);
cipher.encrypt(data: Uint8Array): Uint8Array;
cipher.decrypt(data: Uint8Array): Uint8Array;

// ECB mode
import { ECB } from 'base-aes';
const ecb = new ECB(key: Uint8Array);
ecb.encrypt(data: Uint8Array): Uint8Array;
ecb.decrypt(data: Uint8Array): Uint8Array;

// CBC mode
import { CBC } from 'base-aes';
const cbc = new CBC(key: Uint8Array, iv: Uint8Array);
cbc.encrypt(data: Uint8Array): Uint8Array;
cbc.decrypt(data: Uint8Array): Uint8Array;

Data Conversion Tools

FunctionPurposeExample
toUTF8BytesUTF-8 string to byte arraytoUTF8Bytes('hello')
fromUTF8BytesByte array to UTF-8 stringfromUTF8Bytes(bytes)
toHexBytesHex string to byte arraytoHexBytes('00ff')
fromHexBytesByte array to hex stringfromHexBytes(bytes)

Padding Schemes

ts
import { padPKCS7Padding, stripPKCS7Padding } from 'base-aes';
const padded = padPKCS7Padding(data, blockSize);  // Add padding
const original = stripPKCS7Padding(padded);      // Remove padding

Usage Examples

ECB Mode Encryption/Decryption

ts
import { ECB, padPKCS7Padding, stripPKCS7Padding, toUTF8Bytes, fromUTF8Bytes, fromHexBytes } from 'base-aes';

// Generate 16-byte key
const key = toUTF8Bytes('\x00\x01\x02\x03\x04\x05\x06\x07\b\t\n\v\f\r\x0E\x0F');

// Data to encrypt
const text = 'TextMustBe16Byte';

// Create ECB instance
const ecb = new ECB(key);

// Encrypt data
const encrypted = ecb.encrypt(padPKCS7Padding(toUTF8Bytes(text)));
console.log('Ciphertext:', fromHexBytes(encrypted));
// Ciphertext: 61e6335e9518e20fd16aa30871e211e6954f64f2e4e86e9eee82d20216684899

// Decrypt data
const decrypted = ecb.decrypt(encrypted);
console.log('Plaintext:', fromUTF8Bytes(stripPKCS7Padding(decryptedBytes)));
// Plaintext: TextMustBe16Byte

CBC Mode Encryption/Decryption

ts
import { CBC, padPKCS7Padding, stripPKCS7Padding, toUTF8Bytes, fromUTF8Bytes, fromHexBytes } from 'base-aes';

// Generate key and IV (16 bytes each)
const key = toUTF8Bytes('\x00\x01\x02\x03\x04\x05\x06\x07\b\t\n\v\f\r\x0E\x0F');
const iv = toUTF8Bytes('\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F !"#$');
const text = 'TextMustBe16Byte';

// Create CBC instance
const cbc = new CBC(key, iv);

// Encrypt data
const encrypted = cbc.encrypt(padPKCS7Padding(toUTF8Bytes(text)));
console.log('Ciphertext:', toHexBytes(encrypted));
// Ciphertext: 0605fda3e80da8724d66811725a98f961bf3ca2e1fadf6af8f7223425c74bc69

// Decrypt data
const decrypted = cbc.decrypt(encrypted);
console.log('Plaintext:', fromUTF8Bytes(stripPKCS7Padding(decryptedBytes)));
// Plaintext: TextMustBe16Byte

Comparison with Go Implementation

Click to view Golang code
go
package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/hex"
	"encoding/base64"
	"fmt"
)

func main() {
	// 测试向量 - AES-128-CBC 互操作性测试
	key := []byte("\x00\x01\x02\x03\x04\x05\x06\x07\b\t\n\v\f\r\x0E\x0F")
	iv := []byte("\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F !\"#$")
	text := "TextMustBe16Byte"
	ecbCipherText := "61e6335e9518e20fd16aa30871e211e6954f64f2e4e86e9eee82d20216684899"
	cbcCipherText := "0605fda3e80da8724d66811725a98f961bf3ca2e1fadf6af8f7223425c74bc69"

	// ECB 加密测试
	ecbCipher := ECBEncrypt([]byte(text), key)
	fmt.Printf("ECB 加密结果: %s\n", hex.EncodeToString(ecbCipher))
	fmt.Printf("ECB 加密测试结果 %v\n", hex.EncodeToString(ecbCipher) == ecbCipherText)
	fmt.Printf("\n")

	// CBC 加密测试
	cbcCipher := CBCEncrypt([]byte(text), key, iv)
	fmt.Printf("CBC 加密结果: %s\n", hex.EncodeToString(cbcCipher))
	fmt.Printf("CBC 加密测试结果 %v\n", hex.EncodeToString(cbcCipher) == cbcCipherText)
	fmt.Printf("\n")

	// 解密测试
	fmt.Printf("ECB 解密结果: %s\n", string(ECBDecrypt(ecbCipher, key)))
	fmt.Printf("CBC 解密结果: %s\n", string(CBCDecrypt(cbcCipher, key, iv)))
	fmt.Printf("\n")
	
	fmt.Println("base64编码密文", base64.StdEncoding.EncodeToString(cbcCipher))
  // BgX9o+gNqHJNZoEXJamPlhvzyi4frfavj3IjQlx0vGk=
}

// AESEncryptECB AES-ECB 加密
func ECBEncrypt(plaintext []byte, key []byte) []byte {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// PKCS7 填充
	padded := padPKCS7Padding(plaintext, block.BlockSize())

	// 加密
	ciphertext := make([]byte, len(padded))
	for i := 0; i < len(padded); i += block.BlockSize() {
		block.Encrypt(ciphertext[i:i+block.BlockSize()], padded[i:i+block.BlockSize()])
	}

	return ciphertext
}

// AESDecryptECB AES-ECB 解密
func ECBDecrypt(ciphertext []byte, key []byte) []byte {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	if len(ciphertext)%block.BlockSize() != 0 {
		panic("ciphertext length is not a multiple of block size")
	}

	// 解密
	plaintext := make([]byte, len(ciphertext))
	for i := 0; i < len(ciphertext); i += block.BlockSize() {
		block.Decrypt(plaintext[i:i+block.BlockSize()], ciphertext[i:i+block.BlockSize()])
	}

	// 去除填充
	return stripPKCS7Padding(plaintext)
}

// CBCEncrypt AES-CBC 加密
func CBCEncrypt(plaintext []byte, key []byte, iv []byte) []byte {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// PKCS7 填充
	padded := padPKCS7Padding(plaintext, block.BlockSize())

	// CBC 加密
	mode := cipher.NewCBCEncrypter(block, iv)
	ciphertext := make([]byte, len(padded))
	mode.CryptBlocks(ciphertext, padded)

	return ciphertext
}

// CBCDecrypt AES-CBC 解密
func CBCDecrypt(ciphertext []byte, key []byte, iv []byte) []byte {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	if len(ciphertext)%block.BlockSize() != 0 {
		panic("ciphertext length is not a multiple of block size")
	}

	// CBC 解密
	mode := cipher.NewCBCDecrypter(block, iv)
	plaintext := make([]byte, len(ciphertext))
	mode.CryptBlocks(plaintext, ciphertext)

	// 去除填充
	return stripPKCS7Padding(plaintext)
}

// PKCS7Padding PKCS#7 填充
func padPKCS7Padding(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	if padding == 0 {
		padding = blockSize
	}
	pad := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(data, pad...)
}

// stripPKCS7Padding 移除 PKCS#7 填充
func stripPKCS7Padding(data []byte) []byte {
	if len(data) == 0 {
		panic("empty data")
	}

	padding := int(data[len(data)-1])
	if padding > len(data) || padding > aes.BlockSize {
		panic("invalid padding")
	}

	return data[:len(data)-padding]
}

Last updated: