@liujitcn/kratos-uni-app-core 0.0.32 → 0.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "type": "git",
8
8
  "url": "git@github.com:liujitcn/kratos-admin.git"
9
9
  },
10
- "version": "0.0.32",
10
+ "version": "0.0.34",
11
11
  "type": "module",
12
12
  "files": [
13
13
  "dist",
@@ -2,10 +2,7 @@ import { defLoginService } from '../api/base/v1/login'
2
2
  import { t } from '../locales'
3
3
  import { PasswordCryptoScene } from '../rpc/base/v1/login'
4
4
  import type { PasswordCrypto } from '../rpc/common/v1/types'
5
-
6
- // #ifdef MP-WEIXIN
7
5
  import * as miniCrypto from 'asmcrypto.js'
8
- // #endif
9
6
 
10
7
  type MiniCrypto = typeof import('asmcrypto.js')
11
8
 
@@ -53,8 +50,12 @@ function getSubtleCrypto() {
53
50
  return cryptoApi
54
51
  }
55
52
 
56
- /** 生成小程序端密码加密所需的随机字节。 */
57
- function getMiniRandomBytes(length: number) {
53
+ /** 生成纯 JavaScript 密码加密所需的随机字节。 */
54
+ function getPortableRandomBytes(length: number) {
55
+ const cryptoApi = globalThis.crypto
56
+ if (cryptoApi?.getRandomValues) {
57
+ return Promise.resolve(cryptoApi.getRandomValues(new Uint8Array(length)).buffer)
58
+ }
58
59
  return new Promise<ArrayBuffer>((resolve, reject) => {
59
60
  if (typeof wx === 'undefined' || typeof wx.getRandomValues !== 'function') {
60
61
  reject(new Error(t('core.crypto.unsupported')))
@@ -119,12 +120,16 @@ function trimDerInteger(value: Uint8Array) {
119
120
  }
120
121
 
121
122
  /** 从 PEM SubjectPublicKeyInfo 中提取 RSA 模数和指数。 */
122
- function parseMiniRsaPublicKey(publicKey: string): [Uint8Array, Uint8Array] {
123
+ function parsePortableRsaPublicKey(publicKey: string): [Uint8Array, Uint8Array] {
123
124
  const base64 = publicKey
124
125
  .replace(/-----BEGIN PUBLIC KEY-----/g, '')
125
126
  .replace(/-----END PUBLIC KEY-----/g, '')
126
127
  .replace(/\s/g, '')
127
- const der = new Uint8Array(wx.base64ToArrayBuffer(base64))
128
+ const der = new Uint8Array(
129
+ typeof wx !== 'undefined' && typeof wx.base64ToArrayBuffer === 'function'
130
+ ? wx.base64ToArrayBuffer(base64)
131
+ : pemToArrayBuffer(publicKey),
132
+ )
128
133
  const subjectPublicKeyInfo = readDerNode(der, 0)
129
134
  const algorithm = readDerNode(subjectPublicKeyInfo.value, 0)
130
135
  const bitString = readDerNode(subjectPublicKeyInfo.value, algorithm.next)
@@ -164,13 +169,17 @@ function generateMgf1Mask(crypto: MiniCrypto, seed: Uint8Array, length: number)
164
169
  return mask
165
170
  }
166
171
 
167
- /** 将字节数组编码为小程序可提交的 Base64。 */
168
- function miniBytesToBase64(value: Uint8Array) {
169
- return wx.arrayBufferToBase64(value.slice().buffer)
172
+ /** 将字节数组编码为当前运行端可提交的 Base64。 */
173
+ function portableBytesToBase64(value: Uint8Array) {
174
+ const buffer = value.slice().buffer
175
+ if (typeof wx !== 'undefined' && typeof wx.arrayBufferToBase64 === 'function') {
176
+ return wx.arrayBufferToBase64(buffer)
177
+ }
178
+ return arrayBufferToBase64(buffer)
170
179
  }
171
180
 
172
- /** 使用纯 JavaScript 加密实现兼容微信小程序运行时的密码加密协议。 */
173
- async function encryptMiniProgramPassword(
181
+ /** 使用纯 JavaScript 实现兼容缺少 WebCrypto 的密码加密协议。 */
182
+ async function encryptPortablePassword(
174
183
  crypto: MiniCrypto,
175
184
  password: string,
176
185
  publicKeyResponse: {
@@ -180,13 +189,13 @@ async function encryptMiniProgramPassword(
180
189
  nonce: string
181
190
  },
182
191
  ): Promise<PasswordCrypto> {
183
- const aesKey = new Uint8Array(await getMiniRandomBytes(32))
184
- const iv = new Uint8Array(await getMiniRandomBytes(12))
192
+ const aesKey = new Uint8Array(await getPortableRandomBytes(32))
193
+ const iv = new Uint8Array(await getPortableRandomBytes(12))
185
194
  const plaintext = crypto.string_to_bytes(password, true)
186
- const [modulus, exponent] = parseMiniRsaPublicKey(publicKeyResponse.public_key)
195
+ const [modulus, exponent] = parsePortableRsaPublicKey(publicKeyResponse.public_key)
187
196
  const hash = new crypto.Sha256()
188
197
  const keySize = Math.ceil(new crypto.BigNumber(modulus).bitLength / 8)
189
- const seed = new Uint8Array(await getMiniRandomBytes(hash.HASH_SIZE))
198
+ const seed = new Uint8Array(await getPortableRandomBytes(hash.HASH_SIZE))
190
199
  const dataBlockLength = keySize - hash.HASH_SIZE - 1
191
200
  const paddingLength = dataBlockLength - aesKey.length - hash.HASH_SIZE - 1
192
201
  if (paddingLength < 0) {
@@ -222,9 +231,9 @@ async function encryptMiniProgramPassword(
222
231
  key_id: publicKeyResponse.key_id,
223
232
  nonce: publicKeyResponse.nonce,
224
233
  algorithm: publicKeyResponse.algorithm,
225
- encrypted_key: miniBytesToBase64(encryptedKey),
226
- iv: miniBytesToBase64(iv),
227
- ciphertext: miniBytesToBase64(ciphertext),
234
+ encrypted_key: portableBytesToBase64(encryptedKey),
235
+ iv: portableBytesToBase64(iv),
236
+ ciphertext: portableBytesToBase64(ciphertext),
228
237
  }
229
238
  }
230
239
 
@@ -240,30 +249,31 @@ export async function encryptPassword(
240
249
 
241
250
  const publicKeyResponse = await defLoginService.PasswordPublicKey({ scene })
242
251
 
243
- // 微信小程序没有 window 和 WebCrypto,使用纯 JavaScript 实现保持与后端协议一致。
244
- // #ifdef MP-WEIXIN
245
- return encryptMiniProgramPassword(miniCrypto, plainPassword, publicKeyResponse)
246
- // #endif
252
+ const cryptoApi = globalThis.crypto
253
+ const isSecureContext = typeof window === 'undefined' || window.isSecureContext
254
+ if (!cryptoApi?.subtle || !isSecureContext) {
255
+ return encryptPortablePassword(miniCrypto, plainPassword, publicKeyResponse)
256
+ }
247
257
 
248
- const cryptoApi = getSubtleCrypto()
249
- const publicKey = await cryptoApi.subtle.importKey(
258
+ const subtleCrypto = getSubtleCrypto()
259
+ const publicKey = await subtleCrypto.subtle.importKey(
250
260
  'spki',
251
261
  pemToArrayBuffer(publicKeyResponse.public_key),
252
262
  { name: 'RSA-OAEP', hash: 'SHA-256' },
253
263
  false,
254
264
  ['encrypt'],
255
265
  )
256
- const aesKey = await cryptoApi.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [
266
+ const aesKey = await subtleCrypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [
257
267
  'encrypt',
258
268
  ])
259
- const rawAesKey = await cryptoApi.subtle.exportKey('raw', aesKey)
260
- const iv = cryptoApi.getRandomValues(new Uint8Array(12))
261
- const ciphertext = await cryptoApi.subtle.encrypt(
269
+ const rawAesKey = await subtleCrypto.subtle.exportKey('raw', aesKey)
270
+ const iv = subtleCrypto.getRandomValues(new Uint8Array(12))
271
+ const ciphertext = await subtleCrypto.subtle.encrypt(
262
272
  { name: 'AES-GCM', iv },
263
273
  aesKey,
264
274
  new TextEncoder().encode(plainPassword),
265
275
  )
266
- const encryptedKey = await cryptoApi.subtle.encrypt({ name: 'RSA-OAEP' }, publicKey, rawAesKey)
276
+ const encryptedKey = await subtleCrypto.subtle.encrypt({ name: 'RSA-OAEP' }, publicKey, rawAesKey)
267
277
 
268
278
  return {
269
279
  key_id: publicKeyResponse.key_id,