crypto.uts 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. /**
  2. * AES 加密工具
  3. * 使用 AES-128-CBC 模式,PKCS7/PKCS5 填充
  4. */
  5. // 加密密钥和 IV(与后端保持一致)
  6. const KEY_STR = 'richwaykey000000'
  7. const IV_STR = 'richway-iv000000'
  8. /**
  9. * AES 加密
  10. * @param word 需要加密的字符串
  11. * @returns 加密后的 Base64 字符串
  12. */
  13. export const encryptAES = (word: string): string => {
  14. // #ifdef APP-ANDROID
  15. return encryptAESAndroid(word, KEY_STR, IV_STR)
  16. // #endif
  17. // #ifdef APP-HARMONY
  18. return encryptAESHarmony(word, KEY_STR, IV_STR)
  19. // #endif
  20. // #ifdef APP-IOS
  21. return encryptAESiOS(word, KEY_STR, IV_STR)
  22. // #endif
  23. // #ifdef WEB
  24. return encryptAESWeb(word, KEY_STR, IV_STR)
  25. // #endif
  26. // #ifndef APP-ANDROID || APP-HARMONY || WEB
  27. // 其他平台暂时返回原文
  28. console.warn('当前平台暂不支持 AES 加密,返回原文')
  29. return word
  30. // #endif
  31. }
  32. /**
  33. * AES 解密
  34. * @param word 需要解密的 Base64 字符串
  35. * @returns 解密后的字符串
  36. */
  37. export const decryptAES = (word: string): string => {
  38. // #ifdef APP-ANDROID
  39. console.warn('APP-ANDROID')
  40. return decryptAESAndroid(word, KEY_STR, IV_STR)
  41. // #endif
  42. // #ifdef APP-HARMONY
  43. console.warn('APP-HARMONY')
  44. return decryptAESHarmony(word, KEY_STR, IV_STR)
  45. // #endif
  46. // #ifdef APP-IOS
  47. return decryptAESiOS(word, KEY_STR, IV_STR)
  48. // #endif
  49. // #ifdef WEB
  50. return decryptAESWeb(word, KEY_STR, IV_STR)
  51. // #endif
  52. // #ifndef APP-ANDROID || APP-HARMONY || WEB
  53. // 其他平台暂时返回原文
  54. console.warn('当前平台暂不支持 AES 解密,返回原文')
  55. return word
  56. // #endif
  57. }
  58. // Android 平台实现(使用条件编译)
  59. // #ifdef APP-ANDROID
  60. /**
  61. * Android 平台 AES 加密实现
  62. */
  63. function encryptAESAndroid(plainText: string, keyStr: string, ivStr: string): string {
  64. try {
  65. // 转换密钥和 IV 为字节数组
  66. const keyBytes = new java.lang.String(keyStr).getBytes("UTF-8")
  67. const ivBytes = new java.lang.String(ivStr).getBytes("UTF-8")
  68. // 创建密钥规范和 IV 规范
  69. const secretKeySpec = new javax.crypto.spec.SecretKeySpec(keyBytes, "AES")
  70. const ivParameterSpec = new javax.crypto.spec.IvParameterSpec(ivBytes)
  71. // 创建 Cipher 实例并初始化为加密模式
  72. // 注意:Java 中使用 PKCS5Padding,但在 AES 中它等同于 PKCS7Padding
  73. const cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding")
  74. cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
  75. // 加密数据
  76. const plainBytes = new java.lang.String(plainText).getBytes("UTF-8")
  77. const encryptedBytes = cipher.doFinal(plainBytes)
  78. // 转为 Base64 字符串
  79. const base64Str = android.util.Base64.encodeToString(encryptedBytes, android.util.Base64.NO_WRAP)
  80. return base64Str.toString()
  81. } catch (e) {
  82. console.error('AES 加密失败', e)
  83. return plainText
  84. }
  85. }
  86. /**
  87. * Android 平台 AES 解密实现
  88. */
  89. function decryptAESAndroid(cipherText: string, keyStr: string, ivStr: string): string {
  90. try {
  91. // 转换密钥和 IV 为字节数组
  92. const keyBytes = new java.lang.String(keyStr).getBytes("UTF-8")
  93. const ivBytes = new java.lang.String(ivStr).getBytes("UTF-8")
  94. // 创建密钥规范和 IV 规范
  95. const secretKeySpec = new javax.crypto.spec.SecretKeySpec(keyBytes, "AES")
  96. const ivParameterSpec = new javax.crypto.spec.IvParameterSpec(ivBytes)
  97. // 创建 Cipher 实例并初始化为解密模式
  98. // 注意:Java 中使用 PKCS5Padding,但在 AES 中它等同于 PKCS7Padding
  99. const cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding")
  100. cipher.init(javax.crypto.Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
  101. // 解密数据
  102. const cipherBytes = android.util.Base64.decode(cipherText, android.util.Base64.NO_WRAP)
  103. const decryptedBytes = cipher.doFinal(cipherBytes)
  104. // 转为字符串
  105. const decryptedStr = new java.lang.String(decryptedBytes, "UTF-8")
  106. return decryptedStr.toString()
  107. } catch (e) {
  108. console.error('AES 解密失败', e)
  109. return cipherText
  110. }
  111. }
  112. // #endif
  113. // ============ iOS 平台实现 ============
  114. // #ifdef APP-IOS
  115. /**
  116. * iOS 平台 AES 加密实现
  117. */
  118. function encryptAESiOS(plainText: string, keyStr: string, ivStr: string): string {
  119. try {
  120. // 将字符串转为 Data
  121. const plainData = NSString.stringWithString(plainText).dataUsingEncoding(NSUTF8StringEncoding)
  122. const keyData = NSString.stringWithString(keyStr).dataUsingEncoding(NSUTF8StringEncoding)
  123. const ivData = NSString.stringWithString(ivStr).dataUsingEncoding(NSUTF8StringEncoding)
  124. if (!plainData || !keyData || !ivData) {
  125. console.error('AES 加密失败:数据转换错误')
  126. return plainText
  127. }
  128. // 准备加密参数
  129. const keyBytes = keyData.bytes
  130. const ivBytes = ivData.bytes
  131. const plainBytes = plainData.bytes
  132. const plainLength = plainData.length
  133. // 计算加密后数据大小
  134. const blockSize = 16 // AES 块大小
  135. const bufferSize = Math.floor((plainLength + blockSize) / blockSize) * blockSize
  136. const buffer = new interop.Pointer(bufferSize)
  137. // 执行加密
  138. let encryptedLength = 0
  139. const status = CCCrypt(
  140. 0, // kCCEncrypt
  141. 0, // kCCAlgorithmAES
  142. 0x0001, // kCCOptionPKCS7Padding
  143. keyBytes,
  144. keyData.length,
  145. ivBytes,
  146. plainBytes,
  147. plainLength,
  148. buffer,
  149. bufferSize,
  150. encryptedLength
  151. )
  152. if (status !== 0) { // kCCSuccess
  153. console.error('AES 加密失败,状态码:', status)
  154. return plainText
  155. }
  156. // 转为 Base64
  157. const encryptedData = NSData.dataWithBytesLength(buffer, encryptedLength)
  158. const base64String = encryptedData.base64EncodedStringWithOptions(0)
  159. return base64String.toString()
  160. } catch (e) {
  161. console.error('AES 加密失败(iOS)', e)
  162. return plainText
  163. }
  164. }
  165. /**
  166. * iOS 平台 AES 解密实现
  167. */
  168. function decryptAESiOS(cipherText: string, keyStr: string, ivStr: string): string {
  169. try {
  170. // Base64 字符串转为 Data
  171. const cipherData = NSData.alloc().initWithBase64EncodedStringOptions(cipherText, 0)
  172. const keyData = NSString.stringWithString(keyStr).dataUsingEncoding(NSUTF8StringEncoding)
  173. const ivData = NSString.stringWithString(ivStr).dataUsingEncoding(NSUTF8StringEncoding)
  174. if (!cipherData || !keyData || !ivData) {
  175. console.error('AES 解密失败:数据转换错误')
  176. return cipherText
  177. }
  178. // 准备解密参数
  179. const keyBytes = keyData.bytes
  180. const ivBytes = ivData.bytes
  181. const cipherBytes = cipherData.bytes
  182. const cipherLength = cipherData.length
  183. // 计算解密后数据大小
  184. const bufferSize = cipherLength + 16 // 预留空间
  185. const buffer = new interop.Pointer(bufferSize)
  186. // 执行解密
  187. let decryptedLength = 0
  188. const status = CCCrypt(
  189. 1, // kCCDecrypt
  190. 0, // kCCAlgorithmAES
  191. 0x0001, // kCCOptionPKCS7Padding
  192. keyBytes,
  193. keyData.length,
  194. ivBytes,
  195. cipherBytes,
  196. cipherLength,
  197. buffer,
  198. bufferSize,
  199. decryptedLength
  200. )
  201. if (status !== 0) { // kCCSuccess
  202. console.error('AES 解密失败,状态码:', status)
  203. return cipherText
  204. }
  205. // 转为字符串
  206. const decryptedData = NSData.dataWithBytesLength(buffer, decryptedLength)
  207. const decryptedStr = NSString.alloc().initWithDataEncoding(decryptedData, NSUTF8StringEncoding)
  208. return decryptedStr.toString()
  209. } catch (e) {
  210. console.error('AES 解密失败(iOS)', e)
  211. return cipherText
  212. }
  213. }
  214. // iOS 加密常量定义
  215. declare const CCCrypt: any
  216. declare const NSData: any
  217. declare const NSString: any
  218. declare const NSUTF8StringEncoding: any
  219. declare const interop: any
  220. // 或者更简洁的 iOS 实现版本(使用 CryptoKit)
  221. function encryptAESiOS_simple(plainText: string, keyStr: string, ivStr: string): string {
  222. try {
  223. // 使用 CommonCrypto
  224. const plainData = plainText.toNSData()
  225. const keyData = keyStr.toNSData()
  226. const ivData = ivStr.toNSData()
  227. // 创建加密上下文
  228. const cryptor = NSMutableData.dataWithLength(plainData.length + 16)
  229. let dataOutMoved = 0
  230. const status = CCCrypt(
  231. 0, // kCCEncrypt
  232. 0, // kCCAlgorithmAES128
  233. 1, // kCCOptionPKCS7Padding
  234. keyData.bytes,
  235. keyData.length,
  236. ivData.bytes,
  237. plainData.bytes,
  238. plainData.length,
  239. cryptor.mutableBytes,
  240. cryptor.length,
  241. dataOutMoved
  242. )
  243. if (status === 0) { // kCCSuccess
  244. cryptor.length = dataOutMoved
  245. return cryptor.base64EncodedStringWithOptions(0)
  246. } else {
  247. console.error('加密失败,错误码:', status)
  248. return plainText
  249. }
  250. } catch (e) {
  251. console.error('AES 加密失败(iOS)', e)
  252. return plainText
  253. }
  254. }
  255. function decryptAESiOS_simple(cipherText: string, keyStr: string, ivStr: string): string {
  256. try {
  257. // Base64 解码
  258. const cipherData = NSData.alloc().initWithBase64EncodedStringOptions(cipherText, 0)
  259. const keyData = keyStr.toNSData()
  260. const ivData = ivStr.toNSData()
  261. // 创建解密上下文
  262. const cryptor = NSMutableData.dataWithLength(cipherData.length + 16)
  263. let dataOutMoved = 0
  264. const status = CCCrypt(
  265. 1, // kCCDecrypt
  266. 0, // kCCAlgorithmAES128
  267. 1, // kCCOptionPKCS7Padding
  268. keyData.bytes,
  269. keyData.length,
  270. ivData.bytes,
  271. cipherData.bytes,
  272. cipherData.length,
  273. cryptor.mutableBytes,
  274. cryptor.length,
  275. dataOutMoved
  276. )
  277. if (status === 0) { // kCCSuccess
  278. cryptor.length = dataOutMoved
  279. return NSString.alloc().initWithDataEncoding(cryptor, NSUTF8StringEncoding)
  280. } else {
  281. console.error('解密失败,错误码:', status)
  282. return cipherText
  283. }
  284. } catch (e) {
  285. console.error('AES 解密失败(iOS)', e)
  286. return cipherText
  287. }
  288. }
  289. // 扩展 String 类型以添加 toNSData 方法
  290. declare global {
  291. interface String {
  292. toNSData(): any
  293. }
  294. }
  295. // 为 String 添加 toNSData 方法
  296. String.prototype.toNSData = function(): any {
  297. return NSString.stringWithString(this).dataUsingEncoding(NSUTF8StringEncoding)
  298. }
  299. // #endif
  300. // 鸿蒙平台 AES 加密实现
  301. // #ifdef APP-HARMONY
  302. /**
  303. * 鸿蒙平台 AES 加密实现
  304. * 使用纯 TypeScript 实现 AES-128-CBC
  305. */
  306. function encryptAESHarmony(plainText: string, keyStr: string, ivStr: string): string {
  307. try {
  308. return encryptAESPure(plainText, keyStr, ivStr)
  309. } catch (e) {
  310. console.error('AES 加密失败(鸿蒙)', e)
  311. return plainText
  312. }
  313. }
  314. /**
  315. * 鸿蒙平台 AES 解密实现
  316. */
  317. function decryptAESHarmony(cipherText: string, keyStr: string, ivStr: string): string {
  318. try {
  319. return decryptAESPure(cipherText, keyStr, ivStr)
  320. } catch (e) {
  321. console.error('AES 解密失败(鸿蒙)', e)
  322. return cipherText
  323. }
  324. }
  325. // #endif
  326. // Web 平台 AES 加密实现
  327. // #ifdef WEB
  328. /**
  329. * Web 平台 AES 加密实现
  330. * 使用纯 TypeScript 实现 AES-128-CBC
  331. */
  332. function encryptAESWeb(plainText: string, keyStr: string, ivStr: string): string {
  333. try {
  334. return encryptAESPure(plainText, keyStr, ivStr)
  335. } catch (e) {
  336. console.error('加密失败(Web)', e)
  337. return plainText
  338. }
  339. }
  340. /**
  341. * Web 平台 AES 解密实现
  342. */
  343. function decryptAESWeb(cipherText: string, keyStr: string, ivStr: string): string {
  344. try {
  345. return decryptAESPure(cipherText, keyStr, ivStr)
  346. } catch (e) {
  347. console.error('解密失败(Web)', e)
  348. return cipherText
  349. }
  350. }
  351. // #endif
  352. // 纯 TypeScript 实现的 AES 加密(用于 Web 和鸿蒙平台)
  353. // #ifndef APP-ANDROID
  354. /**
  355. * 纯 TypeScript AES 加密实现(简化版 CryptoJS 逻辑)
  356. */
  357. function encryptAESPure(plainText: string, keyStr: string, ivStr: string): string {
  358. // 将字符串转为字节数组
  359. const plainBytes = stringToBytes(plainText)
  360. const keyBytes = stringToBytes(keyStr)
  361. const ivBytes = stringToBytes(ivStr)
  362. // PKCS7 填充
  363. const paddedBytes = pkcs7Pad(plainBytes)
  364. // AES-CBC 加密
  365. const encryptedBytes = aesCBCEncrypt(paddedBytes, keyBytes, ivBytes)
  366. // 转为 Base64
  367. return bytesToBase64(encryptedBytes)
  368. }
  369. /**
  370. * 纯 TypeScript AES 解密实现
  371. */
  372. function decryptAESPure(cipherText: string, keyStr: string, ivStr: string): string {
  373. // Base64 转字节数组
  374. const cipherBytes = base64ToBytes(cipherText)
  375. const keyBytes = stringToBytes(keyStr)
  376. const ivBytes = stringToBytes(ivStr)
  377. // AES-CBC 解密
  378. const decryptedBytes = aesCBCDecrypt(cipherBytes, keyBytes, ivBytes)
  379. // 去除 PKCS7 填充
  380. const unpaddedBytes = pkcs7Unpad(decryptedBytes)
  381. // 转为字符串
  382. return bytesToString(unpaddedBytes)
  383. }
  384. // ============ 辅助函数 ============
  385. /**
  386. * 字符串转字节数组(UTF-8)
  387. */
  388. function stringToBytes(str: string): number[] {
  389. const bytes: number[] = []
  390. for (let i = 0; i < str.length; i++) {
  391. const charCode = str.charCodeAt(i)
  392. if (charCode < 0x80) {
  393. bytes.push(charCode)
  394. } else if (charCode < 0x800) {
  395. bytes.push(0xc0 | (charCode >> 6))
  396. bytes.push(0x80 | (charCode & 0x3f))
  397. } else if (charCode < 0xd800 || charCode >= 0xe000) {
  398. bytes.push(0xe0 | (charCode >> 12))
  399. bytes.push(0x80 | ((charCode >> 6) & 0x3f))
  400. bytes.push(0x80 | (charCode & 0x3f))
  401. } else {
  402. // UTF-16 代理对
  403. i++
  404. const surrogate = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff))
  405. bytes.push(0xf0 | (surrogate >> 18))
  406. bytes.push(0x80 | ((surrogate >> 12) & 0x3f))
  407. bytes.push(0x80 | ((surrogate >> 6) & 0x3f))
  408. bytes.push(0x80 | (surrogate & 0x3f))
  409. }
  410. }
  411. return bytes
  412. }
  413. /**
  414. * 字节数组转字符串(UTF-8)
  415. */
  416. function bytesToString(bytes: number[]): string {
  417. let str = ''
  418. let i = 0
  419. while (i < bytes.length) {
  420. const byte = bytes[i]
  421. if (byte < 0x80) {
  422. str += String.fromCharCode(byte)
  423. i++
  424. } else if (byte < 0xe0) {
  425. str += String.fromCharCode(((byte & 0x1f) << 6) | (bytes[i + 1] & 0x3f))
  426. i += 2
  427. } else if (byte < 0xf0) {
  428. str += String.fromCharCode(((byte & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f))
  429. i += 3
  430. } else {
  431. const codePoint = ((byte & 0x07) << 18) | ((bytes[i + 1] & 0x3f) << 12) | ((bytes[i + 2] & 0x3f) << 6) | (bytes[i + 3] & 0x3f)
  432. const surrogate = codePoint - 0x10000
  433. str += String.fromCharCode(0xd800 + (surrogate >> 10), 0xdc00 + (surrogate & 0x3ff))
  434. i += 4
  435. }
  436. }
  437. return str
  438. }
  439. /**
  440. * 字节数组转 Base64
  441. */
  442. function bytesToBase64(bytes: number[]): string {
  443. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  444. let base64 = ''
  445. for (let i = 0; i < bytes.length; i += 3) {
  446. const byte1 = bytes[i]
  447. const byte2 = i + 1 < bytes.length ? bytes[i + 1] : 0
  448. const byte3 = i + 2 < bytes.length ? bytes[i + 2] : 0
  449. const enc1 = byte1 >> 2
  450. const enc2 = ((byte1 & 3) << 4) | (byte2 >> 4)
  451. const enc3 = ((byte2 & 15) << 2) | (byte3 >> 6)
  452. const enc4 = byte3 & 63
  453. base64 += chars.charAt(enc1) + chars.charAt(enc2)
  454. base64 += i + 1 < bytes.length ? chars.charAt(enc3) : '='
  455. base64 += i + 2 < bytes.length ? chars.charAt(enc4) : '='
  456. }
  457. return base64
  458. }
  459. /**
  460. * Base64 转字节数组
  461. */
  462. function base64ToBytes(base64: string): number[] {
  463. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  464. const bytes: number[] = []
  465. for (let i = 0; i < base64.length; i += 4) {
  466. const enc1 = chars.indexOf(base64.charAt(i))
  467. const enc2 = chars.indexOf(base64.charAt(i + 1))
  468. const enc3 = chars.indexOf(base64.charAt(i + 2))
  469. const enc4 = chars.indexOf(base64.charAt(i + 3))
  470. const byte1 = (enc1 << 2) | (enc2 >> 4)
  471. const byte2 = ((enc2 & 15) << 4) | (enc3 >> 2)
  472. const byte3 = ((enc3 & 3) << 6) | enc4
  473. bytes.push(byte1)
  474. if (enc3 !== -1) bytes.push(byte2)
  475. if (enc4 !== -1) bytes.push(byte3)
  476. }
  477. return bytes
  478. }
  479. /**
  480. * PKCS7 填充
  481. */
  482. function pkcs7Pad(bytes: number[]): number[] {
  483. const blockSize = 16
  484. const padding = blockSize - (bytes.length % blockSize)
  485. const paddedBytes = bytes.slice()
  486. for (let i = 0; i < padding; i++) {
  487. paddedBytes.push(padding)
  488. }
  489. return paddedBytes
  490. }
  491. /**
  492. * PKCS7 去填充
  493. */
  494. function pkcs7Unpad(bytes: number[]): number[] {
  495. const padding = bytes[bytes.length - 1]
  496. return bytes.slice(0, bytes.length - padding)
  497. }
  498. /**
  499. * AES-CBC 加密
  500. */
  501. function aesCBCEncrypt(plainBytes: number[], keyBytes: number[], ivBytes: number[]): number[] {
  502. const blocks = []
  503. const blockSize = 16
  504. let previousBlock = ivBytes.slice()
  505. // 扩展密钥
  506. const expandedKey = expandKey(keyBytes)
  507. // 分块加密
  508. for (let i = 0; i < plainBytes.length; i += blockSize) {
  509. const block = plainBytes.slice(i, i + blockSize)
  510. // CBC 模式:先与上一个密文块异或
  511. const xorBlock = xorBytes(block, previousBlock)
  512. // AES 加密
  513. const encryptedBlock = aesEncryptBlock(xorBlock, expandedKey)
  514. blocks.push(...encryptedBlock)
  515. previousBlock = encryptedBlock
  516. }
  517. return blocks
  518. }
  519. /**
  520. * AES-CBC 解密
  521. */
  522. function aesCBCDecrypt(cipherBytes: number[], keyBytes: number[], ivBytes: number[]): number[] {
  523. const blocks = []
  524. const blockSize = 16
  525. let previousBlock = ivBytes.slice()
  526. // 扩展密钥
  527. const expandedKey = expandKey(keyBytes)
  528. // 分块解密
  529. for (let i = 0; i < cipherBytes.length; i += blockSize) {
  530. const block = cipherBytes.slice(i, i + blockSize)
  531. // AES 解密
  532. const decryptedBlock = aesDecryptBlock(block, expandedKey)
  533. // CBC 模式:再与上一个密文块异或
  534. const xorBlock = xorBytes(decryptedBlock, previousBlock)
  535. blocks.push(...xorBlock)
  536. previousBlock = block
  537. }
  538. return blocks
  539. }
  540. /**
  541. * 字节数组异或
  542. */
  543. function xorBytes(a: number[], b: number[]): number[] {
  544. const result: number[] = []
  545. for (let i = 0; i < a.length; i++) {
  546. result.push(a[i] ^ b[i])
  547. }
  548. return result
  549. }
  550. /**
  551. * AES 密钥扩展
  552. */
  553. function expandKey(key: number[]): number[][] {
  554. const Nk = 4 // 128-bit key
  555. const Nr = 10 // 10 rounds
  556. const w: number[][] = []
  557. // 初始化前 Nk 个字
  558. for (let i = 0; i < Nk; i++) {
  559. w[i] = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]]
  560. }
  561. // 扩展密钥
  562. for (let i = Nk; i < 4 * (Nr + 1); i++) {
  563. let temp = w[i - 1].slice()
  564. if (i % Nk === 0) {
  565. temp = xorWord(subWord(rotWord(temp)), rcon(i / Nk))
  566. }
  567. w[i] = xorWord(w[i - Nk], temp)
  568. }
  569. return w
  570. }
  571. /**
  572. * AES 加密单个块
  573. */
  574. function aesEncryptBlock(block: number[], expandedKey: number[][]): number[] {
  575. const state = blockToState(block)
  576. const Nr = 10
  577. addRoundKey(state, expandedKey, 0)
  578. for (let round = 1; round < Nr; round++) {
  579. subBytes(state)
  580. shiftRows(state)
  581. mixColumns(state)
  582. addRoundKey(state, expandedKey, round)
  583. }
  584. subBytes(state)
  585. shiftRows(state)
  586. addRoundKey(state, expandedKey, Nr)
  587. return stateToBlock(state)
  588. }
  589. /**
  590. * AES 解密单个块
  591. */
  592. function aesDecryptBlock(block: number[], expandedKey: number[][]): number[] {
  593. const state = blockToState(block)
  594. const Nr = 10
  595. addRoundKey(state, expandedKey, Nr)
  596. for (let round = Nr - 1; round > 0; round--) {
  597. invShiftRows(state)
  598. invSubBytes(state)
  599. addRoundKey(state, expandedKey, round)
  600. invMixColumns(state)
  601. }
  602. invShiftRows(state)
  603. invSubBytes(state)
  604. addRoundKey(state, expandedKey, 0)
  605. return stateToBlock(state)
  606. }
  607. // ============ AES 核心函数 ============
  608. // S-box
  609. const sbox = [
  610. 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
  611. 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
  612. 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
  613. 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
  614. 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
  615. 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
  616. 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
  617. 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
  618. 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
  619. 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
  620. 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
  621. 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
  622. 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
  623. 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
  624. 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
  625. 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
  626. ]
  627. // 逆 S-box
  628. const invSbox = [
  629. 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
  630. 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
  631. 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
  632. 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
  633. 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
  634. 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
  635. 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
  636. 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
  637. 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
  638. 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
  639. 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
  640. 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
  641. 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
  642. 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
  643. 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
  644. 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
  645. ]
  646. // Rcon
  647. const rcon_array = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]
  648. function rcon(i: number): number[] {
  649. return [rcon_array[i], 0, 0, 0]
  650. }
  651. function rotWord(word: number[]): number[] {
  652. return [word[1], word[2], word[3], word[0]]
  653. }
  654. function subWord(word: number[]): number[] {
  655. return word.map(b => sbox[b])
  656. }
  657. function xorWord(a: number[], b: number[]): number[] {
  658. return [a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]]
  659. }
  660. function blockToState(block: number[]): number[][] {
  661. const state: number[][] = []
  662. for (let i = 0; i < 4; i++) {
  663. state[i] = []
  664. for (let j = 0; j < 4; j++) {
  665. state[i][j] = block[i + 4 * j]
  666. }
  667. }
  668. return state
  669. }
  670. function stateToBlock(state: number[][]): number[] {
  671. const block: number[] = []
  672. for (let j = 0; j < 4; j++) {
  673. for (let i = 0; i < 4; i++) {
  674. block.push(state[i][j])
  675. }
  676. }
  677. return block
  678. }
  679. function addRoundKey(state: number[][], expandedKey: number[][], round: number): void {
  680. for (let i = 0; i < 4; i++) {
  681. for (let j = 0; j < 4; j++) {
  682. state[i][j] ^= expandedKey[round * 4 + j][i]
  683. }
  684. }
  685. }
  686. function subBytes(state: number[][]): void {
  687. for (let i = 0; i < 4; i++) {
  688. for (let j = 0; j < 4; j++) {
  689. state[i][j] = sbox[state[i][j]]
  690. }
  691. }
  692. }
  693. function invSubBytes(state: number[][]): void {
  694. for (let i = 0; i < 4; i++) {
  695. for (let j = 0; j < 4; j++) {
  696. state[i][j] = invSbox[state[i][j]]
  697. }
  698. }
  699. }
  700. function shiftRows(state: number[][]): void {
  701. let temp: number
  702. // Row 1: shift left by 1
  703. temp = state[1][0]
  704. state[1][0] = state[1][1]
  705. state[1][1] = state[1][2]
  706. state[1][2] = state[1][3]
  707. state[1][3] = temp
  708. // Row 2: shift left by 2
  709. temp = state[2][0]
  710. state[2][0] = state[2][2]
  711. state[2][2] = temp
  712. temp = state[2][1]
  713. state[2][1] = state[2][3]
  714. state[2][3] = temp
  715. // Row 3: shift left by 3 (or right by 1)
  716. temp = state[3][3]
  717. state[3][3] = state[3][2]
  718. state[3][2] = state[3][1]
  719. state[3][1] = state[3][0]
  720. state[3][0] = temp
  721. }
  722. function invShiftRows(state: number[][]): void {
  723. let temp: number
  724. // Row 1: shift right by 1
  725. temp = state[1][3]
  726. state[1][3] = state[1][2]
  727. state[1][2] = state[1][1]
  728. state[1][1] = state[1][0]
  729. state[1][0] = temp
  730. // Row 2: shift right by 2
  731. temp = state[2][0]
  732. state[2][0] = state[2][2]
  733. state[2][2] = temp
  734. temp = state[2][1]
  735. state[2][1] = state[2][3]
  736. state[2][3] = temp
  737. // Row 3: shift right by 3 (or left by 1)
  738. temp = state[3][0]
  739. state[3][0] = state[3][1]
  740. state[3][1] = state[3][2]
  741. state[3][2] = state[3][3]
  742. state[3][3] = temp
  743. }
  744. function mixColumns(state: number[][]): void {
  745. for (let j = 0; j < 4; j++) {
  746. const s0 = state[0][j]
  747. const s1 = state[1][j]
  748. const s2 = state[2][j]
  749. const s3 = state[3][j]
  750. state[0][j] = gfMul(0x02, s0) ^ gfMul(0x03, s1) ^ s2 ^ s3
  751. state[1][j] = s0 ^ gfMul(0x02, s1) ^ gfMul(0x03, s2) ^ s3
  752. state[2][j] = s0 ^ s1 ^ gfMul(0x02, s2) ^ gfMul(0x03, s3)
  753. state[3][j] = gfMul(0x03, s0) ^ s1 ^ s2 ^ gfMul(0x02, s3)
  754. }
  755. }
  756. function invMixColumns(state: number[][]): void {
  757. for (let j = 0; j < 4; j++) {
  758. const s0 = state[0][j]
  759. const s1 = state[1][j]
  760. const s2 = state[2][j]
  761. const s3 = state[3][j]
  762. state[0][j] = gfMul(0x0e, s0) ^ gfMul(0x0b, s1) ^ gfMul(0x0d, s2) ^ gfMul(0x09, s3)
  763. state[1][j] = gfMul(0x09, s0) ^ gfMul(0x0e, s1) ^ gfMul(0x0b, s2) ^ gfMul(0x0d, s3)
  764. state[2][j] = gfMul(0x0d, s0) ^ gfMul(0x09, s1) ^ gfMul(0x0e, s2) ^ gfMul(0x0b, s3)
  765. state[3][j] = gfMul(0x0b, s0) ^ gfMul(0x0d, s1) ^ gfMul(0x09, s2) ^ gfMul(0x0e, s3)
  766. }
  767. }
  768. /**
  769. * 伽罗瓦域乘法
  770. */
  771. function gfMul(a: number, b: number): number {
  772. let p = 0
  773. for (let i = 0; i < 8; i++) {
  774. if ((b & 1) !== 0) {
  775. p ^= a
  776. }
  777. const hiBitSet = (a & 0x80) !== 0
  778. a = (a << 1) & 0xff
  779. if (hiBitSet) {
  780. a ^= 0x1b // AES 的不可约多项式
  781. }
  782. b >>= 1
  783. }
  784. return p
  785. }
  786. // #endif