开源之夏-20
今天就可以把测试框架对齐GM/T 0018 2023了!
一些零散的疑问:
Session
为什么所有的操作都要在Session内完成?
所有操作都要在Session内完成,原因如下:
资源隔离与安全
Session(会话)为每个用户/进程分配独立的资源和上下文,防止不同用户/进程间的数据、密钥、状态相互干扰或泄露。状态管理
Session保存操作过程中的临时状态(如密钥句柄、Hash上下文、协商参数等),保证多步操作的连续性和正确性。并发支持
多个Session可并发存在,支持多用户/多线程安全地同时操作同一台设备。权限控制
Session可绑定访问权限(如私钥访问、密钥句柄),便于实现细粒度的安全策略和权限回收。资源释放
Session关闭时,相关的临时密钥、句柄、缓冲区等资源会自动释放,防止资源泄漏和安全隐患。
总结:
Session机制是密码设备/库实现安全隔离、状态管理、并发支持和资源回收的基础,所有操作都必须在Session内完成,才能保证安全性和正确性。
HMAC和可鉴别加密
HMAC和“可鉴别的对称加密”(如GCM/CCM模式)都能实现“认证+完整性”,但原理和应用略有不同:
- 工作原理
- HMAC:先用对称密钥对数据做Hash,生成认证码(MAC),单独用于认证和完整性校验,不加密数据本身。
- 可鉴别对称加密(如SM4-GCM/CCM):加密数据的同时,自动生成认证标签(Tag),既保证机密性,又保证认证和完整性。
- 关系
- 可鉴别加密=加密+认证(通常内部实现会用类似HMAC/MAC的机制生成Tag)。
- HMAC只做认证,不加密数据;可鉴别加密同时完成加密和认证。
- 应用场景
- HMAC:用于消息认证、API鉴权、协议认证等,只关心数据是否被篡改、是否来自合法方。
- 可鉴别加密:用于需要同时保证数据机密性和认证性的场景,如安全通信、加密存储等。
- 组合方式
- 有些协议会“先加密后HMAC”,但现代推荐直接用GCM/CCM等可鉴别加密模式,一步完成。
总结:
- HMAC只做认证,不能加密数据。
- 可鉴别对称加密同时加密和认证,安全性更高,使用更方便。
- 现代安全通信优先推荐可鉴别加密模式(如GCM/CCM),无需单独HMAC。
能返回密钥句柄的函数
后边对称加密的接口会用到密钥句柄,所以需要对这几个接口熟悉
后边测试的时候调用这几个接口,生成密钥句柄。
SDF_GenerateKeyWithIPK_RSA
SDF_GenerateKeyWithEPK_RSA
SDF_ImportKeyWithISK_RSA
SDF_GenerateKeyWithIPK_ECC
SDF_GenerateKeyWithEPK_ECC
SDF_ImportKeyWithISK_ECC
SDF_GenerateKeyWithKEK
SDF_ImportKeyWithKEK
SDF_GenerateKeyWithECC(密钥协商)
SDF_GenerateAgreementDataAndKeyWithECC(密钥协商)
KEK(密钥加密密钥)
通常是持久密钥,有设备内部生成并长期安全存储。
专门用于加密/解密会话密钥,实现密钥分级和安全传递。只能在设备内部用于密钥封装和解封装。
- KEK在设备内部生成、写入并保存,不能导出明文。
- KEK的生命周期长,通常随设备初始化或密钥管理流程生成,只有设备内部能直接使用。
- 业务系统或外部应用无法直接获取KEK明文,只能通过索引(如KEKIndex)引用。
IPK/EPK/ISK<=>KEK
问题在于:已经有了像GenerateKeyWithIPK,EPK,ISK这样的过程
还要KEK吗?
KEK(密钥加密密钥)与IPK/EPK/ISK(内部/外部公钥)各有不同的应用场景和安全需求,不是互相替代,而是互补:
1. EPK/ISK(公钥加密/封装会话密钥)
- 用于跨系统、跨设备安全传递会话密钥。
- 适合“非对称”场景:如A系统用B的公钥加密会话密钥,B用私钥解封装。
- 典型场景:分布式系统、远程密钥分发、密钥协商。
- 安全性高,但运算速度慢(非对称加密)。
2. KEK(对称密钥加密/封装会话密钥)
- KEK用于同一设备或同一安全域内的密钥封装、迁移、备份
- KEK属于“对称加密”,速度快,适合高频密钥操作。
- 运算速度快,适合高频密钥操作。适合“性能优先、安全可控”的场景。
- 典型场景:设备内密钥备份、密钥迁移、密钥生命周期管理。
3.IPK(内部公钥)
- IPK用于同一台设备内的不同业务进程/模块之间安全传递会话密钥,防止密钥在内存或进程间被窃取。
- 但本质上,IPK依然是“非对称加密”,即用公钥加密、私钥解密,安全性高,但运算速度慢。
- 适合“安全优先、性能次要”的内部隔离场景,或有严格安全隔离需求的多租户设备。
总结:
KEK不是用来替代IPK/EPK/ISK,而是为设备内部高效安全地管理和传递密钥提供补充。
IPK/EPK/ISK适合跨设备/跨域,KEK适合设备内部或同域高效密钥封装,两者共同构成完整的密钥管理体系。
关于SM2签名算法中的Z值
Z值(又称ZA)是SM2签名算法中的一个“用户身份绑定杂凑值”,用于防止公钥替换攻击和实现签名与用户身份的绑定。
有一条业务流程:
- 公钥:用ExportSignPublicKey_ECC
- 摘要:用SDF_HashInit/Update/Final (自动SM2预处理)
- 签名:用InternalSing_ECC
- 验签:用ExternalVerify_ECC
自动完成SM2预处理和SM3摘要计算,依赖于SDF标准的SDF_HashInit接口的特殊用法:
SM2签名预处理要求:先计算Z值(与公钥、用户ID等相关),再拼接Z||M(M为原始消息),最后对Z||M做SM3杂凑,得到摘要。
这个摘要才是后续签名/验签的输入。
SDF标准规定:
- 当调用SDF_HashInit时,如果传入了ECC公钥和用户ID参数,设备/库会自动完成Z值计算和拼接。
- 后续只需用SDF_HashUpdate输入原始消息,设备/库会自动拼接Z||M并做SM3。
- SDF_HashFinal输出的就是“SM2预处理+SM3”后的摘要。SDF_HashFinal输出的就是“SM2预处理+SM3”后的摘要。
Z值的组成
Z值 = SM3(用户ID || 公钥参数 || 椭圆曲线参数)
包含用户ID(如身份证号、用户名等)、SM2公钥(x, y)、椭圆曲线参数(a, b, G, n, p)等。
这样可以确保签名不仅和消息相关,还和用户身份、公钥绑定。
作用
- 防止攻击者替换公钥后伪造签名(即“公钥替换攻击”)。
- 使签名结果与用户身份唯一绑定,提升安全性。
在SM2签名流程中的位置
签名前,先计算Z值。
签名摘要 = SM3(Z || M),M为原始消息。
验签时同样先计算Z值,再拼接消息做SM3。
密钥协商
SDF_GenerateKeyWithECC(密钥协商)
密钥协商完成后,协商句柄被销毁。返回会话密钥句柄。
总流程:GenerateKeyWithECC
由发起方调用的。
在SM2密钥协商流程中,发起方先调用 GenerateAgreementDataWithECC
生成自己的协商参数,响应方调用 GenerateAgreementDataAndKeyWithECC
生成自己的协商参数和会话密钥。
然后,发起方收到响应方的协商参数后,调用 GenerateKeyWithECC
,结合双方参数计算出会话密钥。
SDF_GenerateAgreementDataWithECC
SponsorID/SponsorIDLength:协商参数
ISKIndex/KeyBits:发送方长期公钥的索引/长度
pSponsorPublicKey:接口会自动填充为发起方的长期ECC公钥(由ISKIndex指定)。
pSponsorTmpPublicKey:接口会自动生成临时密钥对,并填充临时公钥。
&phAgreementHandle:协商句柄
SDF_GenerateAgreementDataAndKeyWithECC
- 接收发送方的协商参数,发送接收方的协商参数,长期公钥,临时公钥
- 接收会话密钥句柄
这里即使用主公钥(长期密钥)又使用临时公钥,为什么?
需要“发起方主公钥”和“临时公钥”是因为它们在密钥协商中各自承担不同的安全角色,二者缺一不可:
1. 主公钥(长期公钥)的作用
- 用于身份认证和密钥协商的基础信任锚。
- 只有主公钥是经过CA/设备认证、长期绑定用户身份的,能防止中间人伪造身份。
- 协商双方通过主公钥确认对方身份,保证协商不是和攻击者进行。
2. 临时公钥的作用
- 用于提升前向安全性(forward secrecy)。
- 每次会话动态生成,协商出的会话密钥与长期密钥无关,即使长期密钥泄露,历史会话密钥也不会被恢复。
- 防止重放和密钥复用攻击。
3. 为什么要同时用?
- 只用主公钥:有身份认证,但没有前向安全性,长期密钥一旦泄露,所有历史会话都不安全。
- 只用临时公钥:有前向安全性,但无法认证对方身份,容易被中间人攻击。
- 两者结合:既能认证身份,又有前向安全性,是现代密钥协商协议(如SM2密钥协商、ECDHE等)的标准做法。
结论:
主公钥保证身份可信,临时公钥保证每次会话密钥独立且安全。两者结合,才能实现既安全又可靠的密钥协商。
临时公钥不是替代主公钥,而是补充主公钥的安全短板。
柔性数组
检查ECCCipher结构时,标准里给的是 C[]
发现GmSSL有这样的处理:
1 |
|
C标准允许结构体最后一个成员是C[1](或C[],C99后),是为了实现“柔性数组成员”(flexible array member),用于支持变长结构体。
原理如下:
- 在C89/C90时代,没有柔性数组,常用C[1]作为结构体最后一个成员,配合malloc分配更大空间,实现变长数据区。
- 这样定义后,结构体大小只包含C[1]的1字节,但你可以分配更大空间,把多余部分当作C数组的后续元素来用。
- C99标准正式引入了C[](柔性数组成员),允许结构体最后一个成员是未指定长度的数组,更加规范和安全。
用途:
- 主要用于协议、密码学等需要变长数据的场景。
- 方便通过结构体指针访问变长数据区。
总结:C[1]是历史兼容写法,C99后推荐用C[]。它们都用于支持结构体变长数据的内存布局。
历史写法示例(C89/C90):
1 |
|
分配更大空间的方法:
1 |
|
说明:
- data[1] 只是占位,实际分配时多分配 (datalen-1) 个字节。
- 这样结构体末尾的 data 区就能容纳 datalen 字节的内容。
- 访问时直接用 p->data[i],i=0~datalen-1。
C99后推荐用 data[],分配方式类似。