Files
jumpserver/apps/static/js/session-encrypt.js
2026-03-31 11:17:54 +08:00

170 lines
5.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
function fillKey(key) {
const keyLength = 16
if (key.length > keyLength) {
key = key.slice(0, keyLength)
}
const filledKey = Buffer.alloc(keyLength)
const keys = Buffer.from(key)
for (let i = 0; i < keys.length; i++) {
filledKey[i] = keys[i]
}
return filledKey
}
function aesEncrypt(text, originKey) {
const key = CryptoJS.enc.Utf8.parse(fillKey(originKey));
return CryptoJS.AES.encrypt(text, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
}).toString();
}
function rsaEncrypt(text, pubKey) {
if (!text) {
return text
}
const jsEncrypt = new JSEncrypt();
jsEncrypt.setPublicKey(pubKey);
return jsEncrypt.encrypt(text);
}
function rsaDecrypt(cipher, pkey) {
const jsEncrypt = new JSEncrypt();
jsEncrypt.setPrivateKey(pkey);
return jsEncrypt.decrypt(cipher)
}
window.rsaEncrypt = rsaEncrypt
window.rsaDecrypt = rsaDecrypt
function hexToBytes(hex) {
if (!hex) return new Uint8Array([])
hex = hex.toString().trim().toLowerCase()
if (hex.startsWith('0x')) {
hex = hex.slice(2)
}
// 确保是偶数长度
const len = Math.floor(hex.length / 2)
const bytes = new Uint8Array(len)
for (let i = 0; i < len; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16)
}
return bytes
}
function bytesToBase64(bytes) {
// Uint8Array -> base64标准 base64
let binary = ''
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i])
}
return btoa(binary)
}
function rsaEncryptPassword(password, rsaPublicKey) {
const aesKey = (Math.random() + 1).toString(36).substring(2)
// public key base64 存储的
const keyCipher = rsaEncrypt(aesKey, rsaPublicKey)
const passwordCipher = aesEncrypt(password, aesKey)
return `${keyCipher}:${passwordCipher}`
}
function ensureSm2PublicKey(sm2PublicKey) {
// sm2.min.js doEncrypt 需要能被 decodePointHex 解析的公钥
// 通常为非压缩点 hex格式 `04||x||y`总长度 130
// 但后端生成/下发的公钥有时是 `x||y`长度 128这里做归一化补齐 `04` 前缀
if (typeof sm2PublicKey === 'string') {
sm2PublicKey = sm2PublicKey.replaceAll('"', '').trim()
if (sm2PublicKey.startsWith('0x')) {
sm2PublicKey = sm2PublicKey.slice(2)
}
// 后端下发的 SM2 公钥常见是 x||y128 hexsm-crypto 需要 04||x||y130 hex
if (sm2PublicKey.length === 128 && !sm2PublicKey.startsWith('04')) {
sm2PublicKey = '04' + sm2PublicKey
}
}
return sm2PublicKey
}
function gmEncryptPassword(password, sm2PublicKey) {
sm2PublicKey = ensureSm2PublicKey(sm2PublicKey)
// 只适配前端不改后端
// 直接生成 16 字符 key后端 padding_key 会保持原样不再补齐
const sm4KeyRaw = randomString(16)
const sm4KeyHex = Buffer.from(sm4KeyRaw).toString('hex')
let keyCipher = ''
try {
// 与后端 gmssl.sm2.CryptSM2 默认 decrypt mode 对齐
// gmssl 解析的格式是 C1C2C3mode=0前端这里输出也用 mode=0
keyCipher = sm2.doEncrypt(sm4KeyRaw, sm2PublicKey, 0)
} catch (e) {
console.error('gmEncryptPassword sm2.doEncrypt failed:', e)
// 避免前端崩溃失败时返回明文由后端按原值流程处理至少可继续登录/看报错
return password
}
const passwordCipher = sm4.encrypt(password, sm4KeyHex)
// sm2/sm4 默认输出是 hex但后端 gm.py/session.py 需要 base64
// - sm2_decrypt: base64.b64decode
// - sm4 decrypt: base64.urlsafe_b64decode
const keyCipherB64 = bytesToBase64(hexToBytes(keyCipher))
const passwordCipherB64 = bytesToBase64(hexToBytes(passwordCipher))
return `${keyCipherB64}:${passwordCipherB64}`
}
function encryptPassword(password) {
if (!password) {
console.log('password is empty')
return ''
}
let publicKeyText = getCookie('jms_public_key')
if (!publicKeyText) {
console.log('publicKeyText is empty')
return password
}
publicKeyText = publicKeyText.replaceAll('"', '')
publicKeyText = atob(publicKeyText)
let cipher = ''
let jmsGMSSL = getCookie('jms_gm_ssl')
if (publicKeyText.includes('PUBLIC KEY')) {
jmsGMSSL = '0'
}
if (jmsGMSSL === '1') {
cipher = gmEncryptPassword(password, publicKeyText)
} else {
cipher = rsaEncryptPassword(password, publicKeyText)
}
return cipher
}
function randomString(length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
function testEncrypt() {
const radio = []
const len2 = []
for (let i = 1; i < 4096; i++) {
const password = randomString(i)
const cipher = encryptPassword(password)
len2.push([password.length, cipher.length])
radio.push(cipher.length / password.length)
}
return radio
}
window.encryptPassword = encryptPassword