HSM技术精讲(3.7):密钥对象与安全属性——密码安全的“四大金刚“
3.7 密钥对象与安全属性密码安全的四大金刚本文内容摘自本人的开源书《HSM技术书 - 从思想实验到安全基石》 在线阅读/下载hsm-bookgitclone https://github.com/Lularible/hsm-book.git⭐ 如果对您有帮助欢迎 Star 支持也欢迎通过 GitHub Issues 交流讨论。密钥对象的分类PKCS#11规范第4.7节定义了密钥对象的基本分类第4.8-4.10节详细定义了三种密钥对象密钥对象分类 CKO_PUBLIC_KEY公钥对象 ├── RSA公钥 ├── DSA公钥 ├── EC公钥ECDSA/ECDH ├── DH公钥 └── 其他公钥 CKO_PRIVATE_KEY私钥对象 ├── RSA私钥 ├── DSA私钥 ├── EC私钥 ├── DH私钥 └── 其他私钥 CKO_SECRET_KEY对称密钥对象 ├── AES密钥 ├── DES/3DES密钥 ├── HMAC密钥 ├── 国密SM4密钥 └── 其他对称密钥每种密钥对象都有专属属性但所有密钥对象共享一组核心安全属性——我们称之为四大金刚。四大安全属性密钥的安全边界PKCS#11定义了四个关键的安全属性控制密钥的导出和读取属性控制什么TRUEFALSECKA_SENSITIVE密钥值是否可读取C_GetAttributeValue无法读取密钥值可以读取密钥值CKA_EXTRACTABLE密钥是否可导出可以通过C_WrapKey导出不能导出CKA_ALWAYS_SENSITIVE是否始终敏感创建时敏感永远保持敏感可以修改CKA_SENSITIVECKA_NEVER_EXTRACTABLE是否从不可导出创建时不可导出永远保持不可导出可以修改CKA_EXTRACTABLE这四个属性的组合定义了密钥的安全边界。CKA_SENSITIVE密钥值读取的闸门定义密钥值是否敏感不可通过C_GetAttributeValue读取。适用范围私钥对象、对称密钥对象公钥对象通常CKA_SENSITIVE FALSE行为/* CKA_SENSITIVE TRUE 时的行为 */CK_ATTRIBUTE getValueAttr{CKA_VALUE,NULL_PTR,0};rvC_GetAttributeValue(hSession,hPrivateKey,getValueAttr,1);// 返回CKR_ATTRIBUTE_SENSITIVE无法读取密钥值/* CKA_SENSITIVE FALSE 时的行为 */CK_ATTRIBUTE getValueAttr{CKA_VALUE,keyBuf,0};rvC_GetAttributeValue(hSession,hPrivateKey,getValueAttr,1);// 成功可以读取密钥值但这是不安全的安全意义TRUE密钥值锁在Token内外部无法读取FALSE密钥值可以读取不安全只适合开发测试典型配置私钥CKA_SENSITIVE TRUE必须对称密钥CKA_SENSITIVE TRUE推荐公钥CKA_SENSITIVE FALSE公钥本意是公开CKA_EXTRACTABLE密钥导出的开关定义密钥是否可以通过C_WrapKey导出包装。行为/* CKA_EXTRACTABLE TRUE 时的行为 */CK_BYTE wrappedKey[256];CK_ULONG wrappedKeyLen256;rvC_WrapKey(hSession,wrapMech,hWrappingKey,hKeyToWrap,wrappedKey,wrappedKeyLen);// 成功密钥被包装导出但导出的是加密数据不是明文密钥/* CKA_EXTRACTABLE FALSE 时的行为 */rvC_WrapKey(hSession,wrapMech,hWrappingKey,hKeyToWrap,wrappedKey,wrappedKeyLen);// 返回CKR_ACTION_PROHIBITED密钥不能导出安全意义TRUE密钥可以包装导出用于备份、迁移FALSE密钥永远不能导出最高安全典型配置最高安全密钥CKA_EXTRACTABLE FALSE需备份密钥CKA_EXTRACTABLE TRUECKA_ALWAYS_SENSITIVE与CKA_NEVER_EXTRACTABLE属性的不可逆锁这两个属性是PKCS#11的安全精髓——一旦设置永远不能改变。CKA_ALWAYS_SENSITIVE如果创建密钥时设置CKA_ALWAYS_SENSITIVE TRUE密钥创建时就CKA_SENSITIVE TRUECKA_SENSITIVE永远不能改为FALSE密钥值永远不能读取/* 创建始终敏感的密钥 */CK_BBOOL bAlwaysSensitiveTRUE;CK_BBOOL bSensitiveTRUE;CK_ATTRIBUTE template[]{{CKA_CLASS,privKeyClass,sizeof(privKeyClass)},{CKA_SENSITIVE,bSensitive,sizeof(bSensitive)},{CKA_ALWAYS_SENSITIVE,bAlwaysSensitive,sizeof(bAlwaysSensitive)},};rvC_CreateObject(hSession,template,3,hKey);/* 之后尝试修改CKA_SENSITIVE */CK_BBOOL bNewSensitiveFALSE;CK_ATTRIBUTE setTemplate[]{{CKA_SENSITIVE,bNewSensitive,sizeof(bNewSensitive)}};rvC_SetAttributeValue(hSession,hKey,setTemplate,1);// 返回CKR_ACTION_PROHIBITED无法修改CKA_NEVER_EXTRACTABLE如果创建密钥时设置CKA_NEVER_EXTRACTABLE TRUE密钥创建时就CKA_EXTRACTABLE FALSECKA_EXTRACTABLE永远不能改为TRUE密钥永远不能导出/* 创建从不可导出的密钥 */CK_BBOOL bNeverExtractableTRUE;CK_BBOOL bExtractableFALSE;CK_ATTRIBUTE template[]{{CKA_CLASS,privKeyClass,sizeof(privKeyClass)},{CKA_EXTRACTABLE,bExtractable,sizeof(bExtractable)},{CKA_NEVER_EXTRACTABLE,bNeverExtractable,sizeof(bNeverExtractable)},};rvC_CreateObject(hSession,template,3,hKey);/* 之后尝试修改CKA_EXTRACTABLE */CK_BBOOL bNewExtractableTRUE;CK_ATTRIBUTE setTemplate[]{{CKA_EXTRACTABLE,bNewExtractable,sizeof(bNewExtractable)}};rvC_SetAttributeValue(hSession,hKey,setTemplate,1);// 返回CKR_ACTION_PROHIBITED无法修改属性的自动联动PKCS#11规范定义了属性的自动联动规则属性联动规则 规则一CKA_ALWAYS_SENSITIVE的联动 ├── 如果创建时CKA_ALWAYS_SENSITIVE TRUE │ └── 必须同时设置CKA_SENSITIVE TRUE │ └── 之后CKA_SENSITIVE永远不能改为FALSE │ └── 如果创建时CKA_ALWAYS_SENSITIVE FALSE └── CKA_SENSITIVE可以自由设置但要注意其他规则 规则二CKA_NEVER_EXTRACTABLE的联动 ├── 如果创建时CKA_NEVER_EXTRACTABLE TRUE │ └── 必须同时设置CKA_EXTRACTABLE FALSE │ └── 之后CKA_EXTRACTABLE永远不能改为TRUE │ └── 如果创建时CKA_NEVER_EXTRACTABLE FALSE └── CKA_EXTRACTABLE可以自由设置但要注意其他规则 规则三C_GenerateKeyPair的自动设置 ├── 使用C_GenerateKeyPair生成密钥对时 │ ├── 公钥CKA_SENSITIVE FALSE, CKA_ALWAYS_SENSITIVE FALSE │ ├── 公钥CKA_EXTRACTABLE TRUE, CKA_NEVER_EXTRACTABLE FALSE │ ├── 私钥CKA_SENSITIVE TRUE, CKA_ALWAYS_SENSITIVE TRUE如果Token支持 │ └── 私钥CKA_EXTRACTABLE FALSE, CKA_NEVER_EXTRACTABLE TRUE如果Token支持 │ └── Token会根据安全策略自动设置私钥的安全属性安全等级的属性组合让我们看不同安全等级的属性组合等级一最高安全金融级别最高安全私钥属性组合 CKA_CLASS CKO_PRIVATE_KEY 私钥对象 CKA_SENSITIVE TRUE 密钥值不可读取 CKA_EXTRACTABLE FALSE 密钥不可导出 CKA_ALWAYS_SENSITIVE TRUE 创建时敏感永远保持 CKA_NEVER_EXTRACTABLE TRUE 创建时不可导出永远保持 CKA_PRIVATE TRUE 需要登录 CKA_TOKEN TRUE 持久存储 CKA_MODIFIABLE FALSE 属性不可修改 CKA_DESTROYABLE FALSE 不可销毁 效果 - 密钥值永远无法读取 - 密钥永远无法导出 - 密钥永远无法销毁 - 属性永远无法修改 密钥永久锁死在Token中等级二高安全企业级别高安全私钥属性组合 CKA_CLASS CKO_PRIVATE_KEY 私钥对象 CKA_SENSITIVE TRUE 密钥值不可读取 CKA_EXTRACTABLE FALSE 密钥不可导出 CKA_ALWAYS_SENSITIVE TRUE 始终敏感 CKA_NEVER_EXTRACTABLE TRUE 从不可导出 CKA_PRIVATE TRUE 需要登录 CKA_TOKEN TRUE 持久存储 CKA_MODIFIABLE FALSE 属性不可修改 CKA_DESTROYABLE TRUE 可以销毁 效果 - 密钥值永远无法读取 - 密钥永远无法导出 - 密钥可以销毁允许密钥轮换 密钥锁死但可以更换等级三中等安全备份级别中等安全私钥属性组合 CKA_CLASS CKO_PRIVATE_KEY 私钥对象 CKA_SENSITIVE TRUE 密钥值不可读取 CKA_EXTRACTABLE TRUE 密钥可以导出用于备份 CKA_ALWAYS_SENSITIVE TRUE 始终敏感 CKA_NEVER_EXTRACTABLE FALSE 不是从不可导出 CKA_PRIVATE TRUE 需要登录 CKA_TOKEN TRUE 持久存储 效果 - 密钥值无法直接读取 - 密钥可以通过C_WrapKey导出加密导出 - 导出的密钥是加密数据需要另一个密钥才能解密 密钥安全但可备份等级四低安全开发级别低安全私钥属性组合 CKA_CLASS CKO_PRIVATE_KEY 私钥对象 CKA_SENSITIVE FALSE 密钥值可以读取 CKA_EXTRACTABLE TRUE 密钥可以导出 CKA_ALWAYS_SENSITIVE FALSE 不是始终敏感 CKA_NEVER_EXTRACTABLE FALSE 不是从不可导出 CKA_PRIVATE FALSE 不需要登录 效果 - 密钥值可以直接读取不安全 - 密钥可以直接导出不安全 密钥完全暴露仅用于开发测试一个类比保险箱钥匙的四个锁让我用一个类比来理解四大安全属性保险箱钥匙的四个锁 保险箱钥匙密钥 │ ├── 锁一透明锁CKA_SENSITIVE │ ├── 上锁TRUE钥匙被遮挡看不到形状 │ └── 开锁FALSE钥匙可见可以复制 │ ├── 锁二出口锁CKA_EXTRACTABLE │ ├── 上锁TRUE钥匙可以取出但要加密包裹 │ └── 开锁FALSE钥匙永远锁在保险箱内 │ ├── 锁三透明永久锁CKA_ALWAYS_SENSITIVE │ ├── 上锁TRUE一旦透明锁上锁永远不能开锁 │ └── 开锁FALSE透明锁可以自由开关 │ └── 锁四出口永久锁CKA_NEVER_EXTRACTABLE ├── 上锁TRUE一旦出口锁上锁永远不能开锁 └── 开锁FALSE出口锁可以自由开关 最高安全钥匙 ├── 透明锁上锁看不到 ├── 出口锁上锁锁在箱内 ├── 透明永久锁上锁永远看不到 └── 出口永久锁上锁永远锁在箱内 钥匙永远锁在箱内只能使用不能取出不能查看实际代码示例生成最高安全密钥对/* 生成最高安全RSA密钥对 */CK_MECHANISM mechanism{CKM_RSA_PKCS_KEY_PAIR_GEN,NULL_PTR,0};CK_OBJECT_HANDLE hPublicKey,hPrivateKey;CK_BBOOL bTrueTRUE;CK_BBOOL bFalseFALSE;CK_ULONG modulusBits2048;CK_BYTE publicExponent[]{0x01,0x00,0x01};/* 65537 *//* 公钥模板 */CK_ATTRIBUTE publicKeyTemplate[]{{CKA_CLASS,pubKeyClass,sizeof(pubKeyClass)},{CKA_KEY_TYPE,rsaKeyType,sizeof(rsaKeyType)},{CKA_TOKEN,bTrue,sizeof(bTrue)},{CKA_PRIVATE,bFalse,sizeof(bFalse)},/* 公开 */{CKA_SENSITIVE,bFalse,sizeof(bFalse)},/* 公钥不敏感 */{CKA_EXTRACTABLE,bTrue,sizeof(bTrue)},/* 公钥可导出 */{CKA_MODULUS_BITS,modulusBits,sizeof(modulusBits)},{CKA_PUBLIC_EXPONENT,publicExponent,sizeof(publicExponent)},};/* 私钥模板最高安全 */CK_ATTRIBUTE privateKeyTemplate[]{{CKA_CLASS,privKeyClass,sizeof(privKeyClass)},{CKA_KEY_TYPE,rsaKeyType,sizeof(rsaKeyType)},{CKA_TOKEN,bTrue,sizeof(bTrue)},{CKA_PRIVATE,bTrue,sizeof(bTrue)},/* 私有 */{CKA_SENSITIVE,bTrue,sizeof(bTrue)},/* 敏感 */{CKA_EXTRACTABLE,bFalse,sizeof(bFalse)},/* 不可导出 */{CKA_ALWAYS_SENSITIVE,bTrue,sizeof(bTrue)},/* 始终敏感 */{CKA_NEVER_EXTRACTABLE,bTrue,sizeof(bTrue)},/* 从不可导出 */{CKA_MODIFIABLE,bFalse,sizeof(bFalse)},/* 属性不可修改 */{CKA_DESTROYABLE,bFalse,sizeof(bFalse)},/* 不可销毁 */};rvC_GenerateKeyPair(hSession,mechanism,publicKeyTemplate,8,privateKeyTemplate,10,hPublicKey,hPrivateKey);if(rvCKR_OK){printf(生成最高安全RSA密钥对成功\n);printf(公钥handle: %lu, 私钥handle: %lu\n,hPublicKey,hPrivateKey);}本篇小结密钥对象有四大安全属性构成了密钥的安全边界四大金刚CKA_SENSITIVE密钥值是否可读取TRUE锁住CKA_EXTRACTABLE密钥是否可导出FALSE锁住CKA_ALWAYS_SENSITIVE始终敏感一旦TRUE永远TRUECKA_NEVER_EXTRACTABLE从不可导出一旦TRUE则CKA_EXTRACTABLE永远保持FALSE不可逆设计CKA_ALWAYS_SENSITIVE和CKA_NEVER_EXTRACTABLE一旦设置永远不能改变这是PKCS#11防止密钥安全等级降级的关键机制安全等级最高安全四个锁全部锁死密钥永久锁在Token内高安全敏感和不可导出锁死但可以销毁密钥轮换中等安全敏感锁死但可以加密导出备份低安全所有锁打开仅用于开发测试下一节我们将看看证书对象——X.509公钥证书如何存储在Token中。【下集预告】密钥存储在Token中证书也需要存储。X.509公钥证书对象存储证书数据和公钥引用。WTLS证书对象用于无线通信的轻量证书。X.509属性证书存储属性声明而非公钥。下一节证书对象详解。