encrypt.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import CryptoJS from 'crypto-js'
  2. import { JSEncrypt } from 'jsencrypt'
  3. /**
  4. * API 加解密工具类
  5. * 支持 AES 和 RSA 加密算法
  6. */
  7. // 从环境变量获取配置
  8. const API_ENCRYPT_ENABLE = import.meta.env.VITE_APP_API_ENCRYPT_ENABLE === 'true'
  9. const API_ENCRYPT_HEADER = import.meta.env.VITE_APP_API_ENCRYPT_HEADER || 'X-Api-Encrypt'
  10. const API_ENCRYPT_ALGORITHM = import.meta.env.VITE_APP_API_ENCRYPT_ALGORITHM || 'AES'
  11. const API_ENCRYPT_REQUEST_KEY = import.meta.env.VITE_APP_API_ENCRYPT_REQUEST_KEY || '' // AES密钥 或 RSA公钥
  12. const API_ENCRYPT_RESPONSE_KEY = import.meta.env.VITE_APP_API_ENCRYPT_RESPONSE_KEY || '' // AES密钥 或 RSA私钥
  13. /**
  14. * AES 加密工具类
  15. */
  16. export class AES {
  17. /**
  18. * AES 加密
  19. * @param data 要加密的数据
  20. * @param key 加密密钥
  21. * @returns 加密后的字符串
  22. */
  23. static encrypt(data: string, key: string): string {
  24. try {
  25. if (!key) {
  26. throw new Error('AES 加密密钥不能为空')
  27. }
  28. if (key.length !== 32) {
  29. throw new Error(`AES 加密密钥长度必须为 32 位,当前长度: ${key.length}`)
  30. }
  31. const keyUtf8 = CryptoJS.enc.Utf8.parse(key)
  32. const encrypted = CryptoJS.AES.encrypt(data, keyUtf8, {
  33. mode: CryptoJS.mode.ECB,
  34. padding: CryptoJS.pad.Pkcs7,
  35. })
  36. return encrypted.toString()
  37. } catch (error) {
  38. console.error('AES 加密失败:', error)
  39. throw error
  40. }
  41. }
  42. /**
  43. * AES 解密
  44. * @param encryptedData 加密的数据
  45. * @param key 解密密钥
  46. * @returns 解密后的字符串
  47. */
  48. static decrypt(encryptedData: string, key: string): string {
  49. try {
  50. if (!key) {
  51. throw new Error('AES 解密密钥不能为空')
  52. }
  53. if (key.length !== 32) {
  54. throw new Error(`AES 解密密钥长度必须为 32 位,当前长度: ${key.length}`)
  55. }
  56. if (!encryptedData) {
  57. throw new Error('AES 解密数据不能为空')
  58. }
  59. const keyUtf8 = CryptoJS.enc.Utf8.parse(key)
  60. const decrypted = CryptoJS.AES.decrypt(encryptedData, keyUtf8, {
  61. mode: CryptoJS.mode.ECB,
  62. padding: CryptoJS.pad.Pkcs7,
  63. })
  64. const result = decrypted.toString(CryptoJS.enc.Utf8)
  65. if (!result) {
  66. throw new Error('AES 解密结果为空,可能是密钥错误或数据损坏')
  67. }
  68. return result
  69. } catch (error) {
  70. console.error('AES 解密失败:', error)
  71. throw error
  72. }
  73. }
  74. }
  75. /**
  76. * RSA 加密工具类
  77. */
  78. export class RSA {
  79. /**
  80. * RSA 加密
  81. * @param data 要加密的数据
  82. * @param publicKey 公钥(必需)
  83. * @returns 加密后的字符串
  84. */
  85. static encrypt(data: string, publicKey: string): string | false {
  86. try {
  87. if (!publicKey) {
  88. throw new Error('RSA 公钥不能为空')
  89. }
  90. const encryptor = new JSEncrypt()
  91. encryptor.setPublicKey(publicKey)
  92. const result = encryptor.encrypt(data)
  93. if (result === false) {
  94. throw new Error('RSA 加密失败,可能是公钥格式错误或数据过长')
  95. }
  96. return result
  97. } catch (error) {
  98. console.error('RSA 加密失败:', error)
  99. throw error
  100. }
  101. }
  102. /**
  103. * RSA 解密
  104. * @param encryptedData 加密的数据
  105. * @param privateKey 私钥(必需)
  106. * @returns 解密后的字符串
  107. */
  108. static decrypt(encryptedData: string, privateKey: string): string | false {
  109. try {
  110. if (!privateKey) {
  111. throw new Error('RSA 私钥不能为空')
  112. }
  113. if (!encryptedData) {
  114. throw new Error('RSA 解密数据不能为空')
  115. }
  116. const encryptor = new JSEncrypt()
  117. encryptor.setPrivateKey(privateKey)
  118. const result = encryptor.decrypt(encryptedData)
  119. if (result === false) {
  120. throw new Error('RSA 解密失败,可能是私钥错误或数据损坏')
  121. }
  122. return result
  123. } catch (error) {
  124. console.error('RSA 解密失败:', error)
  125. throw error
  126. }
  127. }
  128. }
  129. /**
  130. * API 加解密主类
  131. */
  132. export class ApiEncrypt {
  133. /**
  134. * 获取加密头名称
  135. */
  136. static getEncryptHeader(): string {
  137. return API_ENCRYPT_HEADER
  138. }
  139. /**
  140. * 加密请求数据
  141. * @param data 要加密的数据
  142. * @returns 加密后的数据
  143. */
  144. static encryptRequest(data: any): string {
  145. if (!API_ENCRYPT_ENABLE) {
  146. return data
  147. }
  148. try {
  149. const jsonData = typeof data === 'string' ? data : JSON.stringify(data)
  150. if (API_ENCRYPT_ALGORITHM.toUpperCase() === 'AES') {
  151. if (!API_ENCRYPT_REQUEST_KEY) {
  152. throw new Error('AES 请求加密密钥未配置')
  153. }
  154. return AES.encrypt(jsonData, API_ENCRYPT_REQUEST_KEY)
  155. } else if (API_ENCRYPT_ALGORITHM.toUpperCase() === 'RSA') {
  156. if (!API_ENCRYPT_REQUEST_KEY) {
  157. throw new Error('RSA 公钥未配置')
  158. }
  159. const result = RSA.encrypt(jsonData, API_ENCRYPT_REQUEST_KEY)
  160. if (result === false) {
  161. throw new Error('RSA 加密失败')
  162. }
  163. return result
  164. } else {
  165. throw new Error(`不支持的加密算法: ${API_ENCRYPT_ALGORITHM}`)
  166. }
  167. } catch (error) {
  168. console.error('请求数据加密失败:', error)
  169. throw error
  170. }
  171. }
  172. /**
  173. * 解密响应数据
  174. * @param encryptedData 加密的响应数据
  175. * @returns 解密后的数据
  176. */
  177. static decryptResponse(encryptedData: string): any {
  178. if (!API_ENCRYPT_ENABLE) {
  179. return encryptedData
  180. }
  181. try {
  182. let decryptedData: string | false = ''
  183. if (API_ENCRYPT_ALGORITHM.toUpperCase() === 'AES') {
  184. if (!API_ENCRYPT_RESPONSE_KEY) {
  185. throw new Error('AES 响应解密密钥未配置')
  186. }
  187. decryptedData = AES.decrypt(encryptedData, API_ENCRYPT_RESPONSE_KEY)
  188. } else if (API_ENCRYPT_ALGORITHM.toUpperCase() === 'RSA') {
  189. if (!API_ENCRYPT_RESPONSE_KEY) {
  190. throw new Error('RSA 私钥未配置')
  191. }
  192. decryptedData = RSA.decrypt(encryptedData, API_ENCRYPT_RESPONSE_KEY)
  193. if (decryptedData === false) {
  194. throw new Error('RSA 解密失败')
  195. }
  196. } else {
  197. throw new Error(`不支持的解密算法: ${API_ENCRYPT_ALGORITHM}`)
  198. }
  199. if (!decryptedData) {
  200. throw new Error('解密结果为空')
  201. }
  202. // 尝试解析为 JSON,如果失败则返回原字符串
  203. try {
  204. return JSON.parse(decryptedData)
  205. } catch {
  206. return decryptedData
  207. }
  208. } catch (error) {
  209. console.error('响应数据解密失败:', error)
  210. throw error
  211. }
  212. }
  213. }