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

@h-ai/crypto

v0.1.0-alpha.30

Published

Hai Framework cryptography module (asymmetric, symmetric, and hash).

Readme

@h-ai/crypto

加密模块,提供非对称加密、哈希、对称加密与密码哈希能力。

支持的能力

  • 非对称加密(密钥生成、加解密、签名验签)
  • 哈希(哈希、HMAC、验证)
  • 对称加密(ECB/CBC 模式)
  • 密码哈希(加盐迭代哈希)
  • 传输加密(crypto.transport:服务端管理器 + 客户端 encryptedFetch
  • 前后端通用(Node.js / 浏览器)

安全声明

  • SM4 默认加密返回结构化密文crypto.symmetric.encrypt(data, key) 默认使用 CBC + 随机 IV,返回 { mode, ciphertext, iv, encoding };解密时把该对象传给 crypto.symmetric.decrypt(payload, key)
  • SM4 deriveKey(password, salt) 不是 KDF:仅为单次 SM3 哈希,不具备密码爆破抗性,禁止用于密码存储。如需密码哈希,使用 crypto.password.hash(password);如需从密码派生密钥,请采用 PBKDF2 / scrypt / Argon2。
  • IV 必须唯一:相同密钥下禁止复用 IV;推荐使用默认 CBC 或 encryptWithIV() 每次自动生成。密文与 IV 需一同传输与存储(IV 可公开,不必保密)。

快速开始

import { crypto } from '@h-ai/crypto'

// 初始化(使用前必须调用)
await crypto.init()

非对称加密(加解密 / 签名验签)

// 生成密钥对
const keyPair = crypto.asymmetric.generateKeyPair()
if (!keyPair.success)
  throw new Error(keyPair.error.message)
const { publicKey, privateKey } = keyPair.data

// 加密 / 解密
const encrypted = crypto.asymmetric.encrypt('Hello', publicKey)
if (encrypted.success) {
  const decrypted = crypto.asymmetric.decrypt(encrypted.data, privateKey)
  // decrypted.data === 'Hello'
}

// 输出 base64 格式密文
const b64 = crypto.asymmetric.encrypt('Hello', publicKey, { outputFormat: 'base64' })

// 签名 / 验签
const sig = crypto.asymmetric.sign('important data', privateKey)
if (sig.success) {
  const valid = crypto.asymmetric.verify('important data', sig.data, publicKey)
  // valid.data === true
}

// 校验密钥格式
crypto.asymmetric.isValidPublicKey(publicKey) // true
crypto.asymmetric.isValidPrivateKey(privateKey) // true

哈希(Hash / HMAC / 验证)

// 字符串哈希
const hash = crypto.hash.hash('Hello!')
// hash.data → 64 字符十六进制哈希值

// Uint8Array 输入
const buf = new TextEncoder().encode('Hello!')
const hashBuf = crypto.hash.hash(buf)

// 十六进制编码输入
const hashHex = crypto.hash.hash('48656c6c6f21', { inputEncoding: 'hex' })

// HMAC
const hmac = crypto.hash.hmac('message', 'secret-key')

// 验证哈希是否匹配
if (hash.success) {
  const matched = crypto.hash.verify('Hello!', hash.data)
  // matched.data === true
}

对称加密(CBC 默认 / 结构化密文)

// 生成随机密钥和 IV
const key = crypto.symmetric.generateKey()
const iv = crypto.symmetric.generateIV()

// 默认:CBC + 自动随机 IV,返回结构化密文
const safeEnc = crypto.symmetric.encrypt('data', key)
if (safeEnc.success) {
  // safeEnc.data: { mode: 'cbc', ciphertext, iv, encoding: 'hex' }
  const safeDec = crypto.symmetric.decrypt(safeEnc.data, key)
  // safeDec.data === 'data'
}

// CBC 指定 IV(仍然返回结构化密文)
const cbcEnc = crypto.symmetric.encrypt('data', key, { mode: 'cbc', iv })
if (cbcEnc.success) {
  const cbcDec = crypto.symmetric.decrypt(cbcEnc.data, key)
}

// 结构化结果:自动生成 IV 的 CBC 加解密
const withIV = crypto.symmetric.encryptWithIV('data', key)
if (withIV.success) {
  const dec = crypto.symmetric.decryptWithIV(withIV.data.ciphertext, key, withIV.data.iv)
}

// ECB 模式(❌ 不安全;只有底层协议明确要求时才显式指定)
const ecbEnc = crypto.symmetric.encrypt('data', key, { mode: 'ecb' })
if (ecbEnc.success) {
  const ecbDec = crypto.symmetric.decrypt(ecbEnc.data, key)
}

// 输出 base64 格式(ciphertext 字段为 base64,encoding 字段会标明)
const b64Enc = crypto.symmetric.encrypt('data', key, { outputFormat: 'base64' })

// ⚠️ 已弃用:从密码派生密钥(仅为单次 SM3 哈希,不是 KDF,禁止用于密码存储场景)
// 密码散列请用 crypto.password.hash();如需从密码派生密钥,请在应用层自行实现 PBKDF2 / scrypt / Argon2
const derivedKey = crypto.symmetric.deriveKey('my-password', 'random-salt')

// 校验密钥/IV 格式
crypto.symmetric.isValidKey(key) // true
crypto.symmetric.isValidIV(iv) // true

密码哈希(加盐迭代)

// 哈希密码(输出格式: $hai$<iterations>$<salt>$<hash>)
const hashed = crypto.password.hash('myPassword123')

// 自定义盐值长度和迭代次数
const custom = crypto.password.hash('myPassword123', {
  saltLength: 32,
  iterations: 20000,
})

// 验证密码
if (hashed.success) {
  const ok = crypto.password.verify('myPassword123', hashed.data)
  // ok.data === true

  const wrong = crypto.password.verify('wrongPassword', hashed.data)
  // wrong.data === false
}

传输加密(crypto.transport)

传输加密统一通过 crypto.transport 命名空间提供,供 @h-ai/serv@h-ai/kit@h-ai/api-client 复用同一套协议常量和载荷格式。

常规应用优先让上层封装代接:

  • 服务端:serv.createApp({ transport: { crypto } })kit.createHandle({ crypto: { crypto, transport: true } })
  • 客户端:apiClient.init({ transport: { crypto } })kit.client.create({ transport: { crypto } })

只有在自定义运行时、测试或你确实要自己接 HTTP 协商端点时,才建议直接调用 crypto.transport.createServer() / createClient()

使用流程

  1. await crypto.init(),确保 crypto.asymmetriccrypto.symmetric 已初始化。
  2. 服务端调用 crypto.transport.createServer() 创建 manager;它持有服务端密钥对,并负责保存客户端公钥。多节点部署时可注入共享 keyStore
  3. 服务端提供一个 POST 密钥协商端点:接收 { clientPublicKey },调用 manager.registerClientKey() 注册客户端,再返回 { serverPublicKey: manager.getServerPublicKey(), clientId }
  4. 客户端调用 crypto.transport.createClient({ keyExchangeUrl }) 创建会话;首次 client.init()client.encryptedFetch() 会自动完成这次协商。
  5. 协商完成后,客户端请求会附带 X-Client-Id;若请求有 body,则 body 会被包装成 { encryptedKey, ciphertext, iv },并带上 X-Encrypted: true。无 body 的请求只附带 X-Client-Id,不会额外生成密文 body。
  6. 服务端根据 clientId 找到客户端公钥,先 manager.decryptRequest() 解密请求体,再执行业务逻辑。
  7. 服务端返回 JSON 响应前,用 manager.encryptResponse(clientId, data) 重新加密,并设置 X-Encrypted: true;客户端收到后会自动解密。
  8. 客户端调用 client.destroy() 可清空当前会话;服务端调用 manager.close(),或在模块级调用 await crypto.close(),用于释放资源。

默认 keyStore 是进程内 FIFO 内存实现,超过 maxClients 会淘汰最早注册的客户端。多节点部署时,需要会话粘性(sticky session),或改用共享的 TransportKeyStore 实现来保存客户端公钥。

共享存储可直接复用 @h-ai/crypto 根入口导出的 provider 工厂:

  • createInMemoryKeyStore(maxClients):显式创建默认内存实现,常用于测试或自定义容量
  • createRedisTransportKeyStore({ cache, ttlSeconds? }):复用 @h-ai/cache,通常配合 Redis provider 获取跨节点共享
  • createReldbTransportKeyStore({ reldb, ttlSeconds? }):复用 @h-ai/reldb,自动建表 hai_crypto_transport_client_keys
// 服务端:通常由 serv.createApp({ transport: { crypto } }) 或 kit.createHandle({ crypto }) 内部调用
const server = crypto.transport.createServer({ maxClients: 10000 })
if (!server.success)
  throw new Error(server.error.message)

// 客户端:通常由 apiClient.init({ transport: { crypto } }) 或 kit.client.create({ transport }) 内部调用
const client = crypto.transport.createClient({
  keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange',
})

const response = await client.encryptedFetch('https://api.example.com/api/v1/echo', {
  method: 'POST',
  body: JSON.stringify({ hello: 'world' }),
})
import { cache } from '@h-ai/cache'
import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'

await crypto.init()
await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })

const server = crypto.transport.createServer({
  keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
})
if (!server.success)
  throw new Error(server.error.message)

encryptedFetch() 的语义与原生 fetch 一致:网络失败、密钥协商失败、请求加密失败或响应解密失败时会 reject;业务层应在调用处按 fetch 错误处理策略统一捕获。

协议常量通过 crypto.transport.protocol(或 TRANSPORT_PROTOCOL)访问:X-Client-IdX-Encrypted、默认 /_hai/key-exchange

关闭模块

// 使用完毕后关闭,释放内部状态
await crypto.close()

错误处理

所有操作返回 HaiResult<T>

const result = crypto.asymmetric.encrypt('data', publicKey)
if (!result.success) {
  // result.error.code / result.error.message
}

测试

pnpm --filter @h-ai/crypto test

License

Apache-2.0