一、简介

1、AES(Advanced Encryption Standard,高级加密标准)

  • 功能

AES 是一种对称加密算法,用于对数据进行加密和解密(加密和解密使用相同密钥)。它是目前全球广泛采用的标准加密算法,替代了安全性不足的 DES 算法。

  • 核心特点

  1. 分组加密:AES 处理固定长度的 “数据块”(分组大小),标准分组大小为 128 位(16 字节),密钥长度可选择 128 位、192 位或 256 位(密钥越长,安全性越高,但加密速度略慢)。

  2. 安全性高:至今未被发现有效的攻击方法,能抵抗绝大多数常见密码分析。

  3. 效率高:适合硬件和软件实现,在各种设备(从嵌入式系统到大型服务器)上都能高效运行。

  • 作用

AES 是加密的 “核心引擎”,定义了如何对单个 128 位数据块进行混淆、置换和迭代运算,生成加密后的密文块。

2、ECB(Electronic Codebook,电子密码本模式)

  • 功能

ECB 是 AES 等分组加密算法的工作模式,定义了如何处理超过单个分组长度的数据(即多块数据的加密方式)。

  • 核心特点

    1. 独立加密:将明文分成多个 128 位的块,每个块独立用 AES 加密,互不影响。

    2. 确定性:相同的明文块会生成相同的密文块(因为加密方式完全独立)。

  • 缺点(安全性问题)

    1. 由于相同明文块加密后结果相同,攻击者可通过分析密文块的重复模式,推测明文结构(例如图片加密后,轮廓可能依然可见)。

    2. 不提供完整性校验,无法检测数据是否被篡改。

  • 适用场景

仅适用于加密极短的数据(如单个密钥),不适合加密长文本、文件等,实际应用中很少使用(推荐使用 CBC、GCM 等更安全的模式)。

概念

层面

核心作用

示例场景

AES

加密算法

定义单个分组的加密逻辑(核心运算)

对 16 字节明文块进行加密

ECB

工作模式

定义多块数据的加密顺序和关联方式

将 32 字节明文分成两个块,分别用 AES 加密

3、加密方法

/// 加密方法:AES-ECB模式(零填充)
/// @param plaintext 明文(字符串)
/// @param key 密钥(16/24/32字节字符串,对应128/192/256位)
/// @return 加密后十六进制字符串
/// w ##class(NTSC.Test.Test1).Encrypt("<REQ><DEPT_ID>-1</DEPT_ID><HOS_ID>12622800439051277W</HOS_ID></REQ>","zmd_scan_pay_+++")
ClassMethod Encrypt(plaintext As %String, key As %String) As %String [ Language = objectscript ]
{
    // 检查密钥长度
    Set keyLen = $Length(key)
    If (keyLen '= 16) && (keyLen '= 24) && (keyLen '= 32) {
        Return "密钥长度必须为16、24或32字节"
    }
    // 明文转UTF-8二进制
    Set plainBin = $ZCONVERT(plaintext, "O", "UTF8")
    // 零填充(拆分循环为多行,避免紧凑语法)
    Set padLen = (16 - ($Length(plainBin) # 16)) # 16
    If padLen > 0 {
        Set i = 1
        For {
            If i > padLen { Quit }
            Set plainBin = plainBin _ $Char(0)
            Set i = i + 1
        }
    }
    // AES-ECB加密
    Set keyBits = keyLen * 8
    Set cipherBin = ##class(%SYSTEM.Encryption).AESEncode(plainBin, key)
    q:(cipherBin="") "加密失败:未生成密文"
    // 二进制转十六进制(拆分循环为多行)
    Set hexStr = ""
    Set cipherLen = $Length(cipherBin)
    Set i = 1
    For {
        If i > cipherLen { Quit }
        Set byte = $Ascii($Extract(cipherBin, i))
        If byte < 16 {
            Set hex = "0" _ $ZHEX(byte)
        } Else {
            Set hex = $ZHEX(byte)
        }
        Set hexStr = hexStr _ hex
        Set i = i + 1
    }
    q hexStr
}

运行效果:

4、对应解密方法

/// 解密方法:AES-ECB模式(零填充)
/// @param cipherHex 加密后的十六进制字符串
/// @param key 密钥(同加密密钥)
/// @return 解密后的明文(去除末尾零填充)
/// w ##class(NTSC.Test.Test1).Decrypt("8291FFCFBFE05D267C3BB6D5E92A6AE10B1364D30E7744FCC3C7F914F676A8D84F84A34831DC8AC4FD002945E7AFC7B4EB33EE24CB407D42E799366AF9BE70F624D7EA9743724D6875944EAC9E362E4C","zmd_scan_pay_++/")
ClassMethod Decrypt(cipherHex As %String, key As %String) As %String [ Language = objectscript ]
{
    // 检查密钥长度
    Set keyLen = $Length(key)
    If (keyLen '= 16) && (keyLen '= 24) && (keyLen '= 32) q "密钥长度必须为16、24或32字节"

    // 检查密文长度为偶数
    If ($Length(cipherHex) # 2) q "密文格式错误:长度必须为偶数"

    // 手动十六进制转二进制
    Set cipherBin = "", i = 1, hexMap = "0123456789ABCDEF"
    For {
        If i > $Length(cipherHex) Quit
        Set c1 = $Extract(cipherHex, i)
        Set p1 = $Locate(hexMap, $ZCONVERT(c1, "U"))
        If p1 = 0 Return "密文错误:非法字符 '"_c1_"'"
        Set val = (p1 - 1) * 16
        Set c2 = $Extract(cipherHex, i + 1)
        Set p2 = $Locate(hexMap, $ZCONVERT(c2, "U"))
        If p2 = 0 Return "密文错误:非法字符 '"_c2_"'"
        Set val = val + (p2 - 1)
        // 拼接二进制字符
        Set cipherBin = cipherBin _ $Char(val)
        Set i = i + 2
    }
    // 解密并处理填充
    Set plainBin = ##class(%SYSTEM.Encryption).AESDecode(cipherBin, key)
	If plainBin = "" q "解密失败"
	// 去除尾部零填充
	Set i = $Length(plainBin)
	// 替换不兼容的 For 循环写法
	For {
    	If i < 1 {
       	 Quit
    	}
    	If $Ascii($Extract(plainBin, i)) '= 0 {
       	 Quit
   	 }
    	Set i = i - 1
	}
	i plainBin'["REQ" q "解密失败!key不合法"
	Set str = $ZCONVERT($Extract(plainBin, 1, i), "I", "UTF8")
	q str
}

运行效果:

5,、无填充补零方式解密

/// 解密方法:AES-ECB模式(PKCS#7填充)
/// 用于解析密文 JYNO1FsGBWO2ULrhbDcZ8iQpecoWuWl5P23VR+GwyA++/ciomaRogQJt6QDYk+D9bagIjtPh4ajAZmXCVmH6tufw3SNMp6LK1lGgVLiZjxc=
/// @param cipherBase64 加密后的Base64字符串
/// @param key 密钥(2fMVbe6Nvtyy6CsV)
/// @return 解密后的明文XML
/// w ##class(NTSC.Test.Test1).DecryptAES("JYNO1FsGBWO2ULrhbDcZ8iQpecoWuWl5P23VR+GwyA++/ciomaRogQJt6QDYk+D9bagIjtPh4ajAZmXCVmH6tufw3SNMp6LK1lGgVLiZjxc=","2fMVbe6Nvtyy6CsV")
ClassMethod DecryptAES(cipherBase64 As %String, key As %String) As %String [ Language = objectscript ]
{
    // 检查密钥长度(AES-128需要16字节密钥)
    Set keyLen = $Length(key)
    If keyLen '= 16 d  q "密钥长度错误,需要16字节"
    // 1. Base64解码密文
    Set cipherBin = ##class(%SYSTEM.Encryption).Base64Decode(cipherBase64)
    If cipherBin = "" d  q "Base64解码失败,密文格式错误"
    // 2. 使用AES-ECB模式解密(IRIS默认使用PKCS#7填充)
    Set plainBin = ##class(%SYSTEM.Encryption).AESDecode(cipherBin, key)
    If plainBin = "" d  q "AES解密失败,密钥或密文错误"
    // 3. 将二进制转换为UTF-8字符串
    Set plainText = $ZCONVERT(plainBin, "I", "UTF8")
    If plainText = "" d  q "字符串编码转换失败"
    q plainText
}