Intel eHSM(基于 SGX Enclave 的硬件安全模块密钥管理系统)是一个融合了硬件和软件的解决方案,主要提供云环境中的密钥管理服务。该系统利用 Intel SGX(Software Guard Extensions,软件防护扩展)技术,实现了近似于传统硬件安全模块的加密功能保护级别,包括密钥生成和管理。用户可以在私有云或者像阿里巴巴云这样的公有云上部署这一解决方案,有效地管理和控制加密密钥的使用,确保数据的安全性和完整性。此外,eHSM-KMS解决方案的推出,为需要密钥管理系统的客户提供了一种成本效益高且可扩展的选择。
本文将介绍如何在已搭建好的ehsm服务器上调用ehsm提供的api接口,如果还未搭建好ehsm服务器,请参考搭建intel ehsm-kms单节点
一、环境准备
因为intel sgx已经集成了一个名为ehsm的python包专门提供api的调用功能,所以我们也不需要自己从零开始写post语句,本文ehsm的安装教程参考intel ehsm官方提供的ehsm包的方法。(推荐手动安装)
安装完成后导入命令如下
from ehsm import Client
from ehsm.api.enums import KeySpec, Origin, KeyUsage, PaddingMode,DigestMode,MessageType
二 、api使用介绍
2.1 api接口概览
Cryptographic Functionalities APIs | 描述 |
---|---|
CreateKey | 用于创建新的加密密钥。 |
Encrypt | 使用加密密钥加密明文数据。 |
Decrypt | 将密文解密回明文。 |
AsymmetricEncrypt | 使用非对称密钥加密数据。 |
AsymmetricDecrypt | 解密使用非对称密钥加密的数据。 |
Sign | 使用私钥生成数字签名。 |
Verify | 使用公钥验证数字签名。 |
GenerateDataKey | 生成数据加密密钥。 |
GenerateDataKeyWithoutPlaintext | 生成数据密钥但不返回密钥明文。 |
ExportDataKey | 导出加密的数据密钥。 |
GetPublicKey | 从密钥对中检索公钥。 |
GetParametersForImport | 获取导入密钥材料所需的参数。 |
ImportKeyMaterial | 导入密钥材料以用于加密密钥。 |
Key Management APIs | 描述 |
---|---|
GetVersion | 检索密钥管理服务的版本信息。 |
Enroll | 为密钥管理服务注册用户或系统。 |
ListKey | 列出所有加密密钥。 |
DeleteKey | 删除指定的加密密钥。 |
DeleteALLKey | 删除所有加密密钥。 |
EnableKey | 启用加密密钥以供使用。 |
DisableKey | 禁用加密密钥以阻止其使用。 |
Remote Attestation APIs | 描述 |
---|---|
GenerateQuote | 生成用于远程认证的报价。 |
VerifyQuote | 验证报价的真实性。 |
UploadQuotePolicy | 上传与报价验证相关的策略。 |
GetQuotePolicy | 检索与报价验证相关的策略。 |
下文将重点介绍Cryptographic Functionalities APIs 部分;Key Management APIs很简单不做过多介绍;Remote Attestation APIs这部分由于作者还未成功搭建pccs服务器,因此暂时不做讲解,后续若有涉及会继续更新;Secret Manager APIs,这一部分用来管理包括加密秘钥在内的API 密钥、SSH 密钥、令牌、密码或证书等,但目前作者的工作不涉及这个,如果想深入了解的话可参考eHSM REST API Reference
2.2 Cryptographic Functionalities APIs 使用演示
在测试之前,我们先需要确保ehsm服务器确实可以,这里可以直接在终端验证。
curl --insecure https://60.205.130.184:9002/ehsm?Action=Enroll
这里返回了apikey和appid,是用户身份信息认证的参数,后续的所有api调用都是基于这两个参数的,因此需要提前定义好。
url = "https://60.205.130.184:9002/ehsm"
appid= "ae264c5a-ad26-4349-afc1-b6dfb35baa7f"
apikey = "hF4Rd4LTdzrnpxEsddinZn2kutdi7dw8"
#这里的客户端相当一个对象,后续api都是由它调用
client = Client(base_url=url, appid = appid,apikey = apikey, allow_insecure=True)
CreateKey
Keyspec:秘钥规格,里面的参数包括一系列对称/非对称的加密方法
origin:指定秘钥的源头是ehsm提供还是用户提供,这里目前只支持ehsm自己提供
keyusage:秘钥用途,这里包括EH_KEYUSAGE_ENCRYPT_DECRYPT和EH_KEYUSAGE_SIGN_VERIFY两个参数,用于加解密,签名验证两个用途
# 创建CMK对称秘钥,对称CMK负责加密DataKey和少量数据,非对称CMK负责签名/验证或者非对称加解密数据
result = client.create_key(
KeySpec.EH_AES_GCM_128, Origin.EH_INTERNAL_KEY, KeyUsage.EH_KEYUSAGE_ENCRYPT_DECRYPT
)
print("result:",result)
其它类型的秘钥创建命令如下,返回结果同上,不再额外截图
# 创建CMK非对称秘钥,负责签名/验证或者非对称加解密数据
result = client.create_key(
KeySpec.EH_RSA_2048, Origin.EH_INTERNAL_KEY, KeyUsage.EH_KEYUSAGE_ENCRYPT_DECRYPT
)
print("result:",result)
# 创建非对称的负责签名/验证的CMK
result = client.create_key(
KeySpec.EH_RSA_2048, Origin.EH_INTERNAL_KEY, KeyUsage.EH_KEYUSAGE_SIGN_VERIFY
)
print("result:",result)
# 创建外部秘钥的id
result = client.create_key(
KeySpec.EH_RSA_2048, Origin.EH_EXTERNAL_KEY , KeyUsage.EH_KEYUSAGE_ENCRYPT_DECRYPT
)
print("result:",result)
Encrypt & Decrypt
需要注意,这里keyid对应的密钥类型一定要是对称密钥,而KeyUsage要选加解密的。
# 给定base64集合的字符串明文加密为密文
aad = "Y2hhbGxlbmdl" #base64字符集
keyid = keyid #create_key获取到的cmk的id即keyid
plaintext = "cGxhaW50ZXh0"
result = client.encrypt(
aad,keyid,plaintext
)
print("result:",result)
log_message(f"result:{result}")
# 给定base64集合的字符串密文解密为明文
aad = "Y2hhbGxlbmdl" #base64字符集
keyid = keyid #create_key获取到的cmk的id即keyid
ciphertext = "B79m7WUTDq9Gt08uHDI10CBReC2Ab8fJX3fse2rba9q6h5h7kA=="
result = client.decrypt(
aad,keyid,ciphertext
)
print("result:",result)
log_message(f"result:{result}")
AsymmetricEncrypt & AsymmetricDecrypt
注:①这里的秘钥在创建时候,必须要用非对称加密方法的KeySpec,而KeyUsage要选加解密的。
②关于非对称加解密的公私钥问题,猜测是因为秘钥本身存储在ehsm中了,因此ehsm服务器只需要一个id即可知道对应的公私钥对。
# 非对称加密AsymmetricEncrypt
asymmetric_keyid = asymmetric_keyid
plaintext = "cGxhaW50ZXh0"
padding_mode = PaddingMode.EH_RSA_PKCS1
result = client.asymm_encrypt(
asymmetric_keyid,plaintext,padding_mode
)
print("result:",result)
log_message(f"result:{result}")
# 非对称解密AsymmetricDecrypt
asymmetric_keyid = asymmetric_keyid
ciphertext='aEAJxabZ6J5DI/wLkMv1cGCfj1IAkHwX2syF9siqmU+uHybqcL7k1HWe+mTxontuluKk/UhqeAu+I2+GhOSruh/NVThpRiD8p72wgpJfwZD0hXHqBIu7K4S+kfqO5HROADJEsEeydS0O+EUavn02vFtSdoEtVGxCdbYeDxMyzxlQEn7YzwbK0LtKgoeYHdpfogQ9erpUQc8L300WptcxmGb3dhBmVy6RYsc0cgrJ++BJy4jD1AHDci8PDCrV2ytJopC8WZjqESBjxBtQdREcLiX+6Xl2CrSle+MycPpot1bFzjTs1vzrOSHHQXoTa1pHJlECTmmyx6Y8RkQfdUDouA=='
padding_mode = PaddingMode.EH_RSA_PKCS1
result = client.asymm_decrypt(
asymmetric_keyid,ciphertext,padding_mode
)
print("result:",result)
log_message(f"result:{result}")
Sign & Verify
注:①这里的秘钥在创建时候,必须要用非对称加密方法的KeySpec,而KeyUsage要选验证和签名的。
②关于sign的digest_mode: 两个参数 EH_RAW对原始数据生成摘要,EH_DIGES对用户自己提供的摘要生成摘要
#签名函数测试
sign_keyid = sign_keyid
padding_mode = PaddingMode.EH_RSA_PKCS1
digest_mode = DigestMode.EH_SHA_256
message_type =MessageType.EH_RAW
message = "ZGlnZXN0"
result = client.sign(
sign_keyid,padding_mode,digest_mode,message_type,message
)
print("result:",result)
log_message(f"result:{result}")
#验证函数测试
sign_keyid = sign_keyid
padding_mode = PaddingMode.EH_RSA_PKCS1
digest_mode = DigestMode.EH_SHA_256
message_type =MessageType.EH_RAW
message = "ZGlnZXN0"
signature = 'eU+YL3FcZMaFMnwGg/j9GfOFLZiRwlXvbGrzpIqeqPYvE4hSn0o9FqdY+cSMN32X7O5Lkf1fa3lfORKVi0ZHUSii+SpzxCDsQPbTuFbRXzbdoaqjiKykR1TUUId5ghXxWaacjgDQjyDIvayRC5/ACUa/rmEIzNJOO8ZHzh0XCU/nB2OnUm9XZsq0v0xw6XOpZNpTtAGGQr5uWdcqPNcsC5IcXXOV/711BFuS3g3NYAfgXIxezvHiiEMHeMWtDEMkWwbdPMZ+teYHV6Vf8fIEdyFSPhK4kf/bHathMbyCr5+yRwHh7Lt3ezy4irCwZN7QV4WrPSr2WkgfciD/SlFyrQ=='
result = client.verify(
sign_keyid,padding_mode,digest_mode,message_type,message,signature
)
print("result:",result)
log_message(f"result:{result}")
GenerateDataKey & GenerateDataKeyWithoutPlaintext & ExportDataKey
注:这里的明文秘钥是以base64格式保存的,将其转换为16进制时就会发现与指定长度相符合。
#创建数据秘钥Generatedatakey
#这里数据秘钥生成需要提供对应的CMK(对称密钥),ehsm返回用户数据秘钥的明密文,
#用户用明文加密完数据后需要立即清除明文秘钥,密文秘钥则需要保存好,
#再次需要解密数据时,需要用对应的cmkid和密钥密文向ehsm申请解密密钥得到明文秘钥。
aad ="Y2hhbGxlbmdl"
keyid = keyid
keylen = 16
result = client.generate_data_key(
aad,keyid,keylen
)
print("result:",result)
print("keyid:",keyid)
log_message(f"result:{result}")
#创建数据秘钥Generatedatakeywithoutplaintext,不返回秘钥明文
#这里的安全性主要体现在生成密钥时不立即暴露,和一些政策合规性的要求,但是密钥存储安全性是一样的
aad ="Y2hhbGxlbmdl"
keyid = keyid
keylen = 16
result = client.generate_data_key_without_plaintext(
aad,keyid,keylen
)
print("result:",result)
print("keyid:",keyid)
log_message(f"result:{result}")
# ExportDataKey使用用户提供原本的CMK来解密EDataKey,然后用用户提供的公钥来加密数据加密密钥,导出数据密钥(密文)将返回给调用者。
# 这里调试的时候报错了,提示olddatakey_base不存在,可能是版本更新了,解决办法是在crypto里修改了提交的数据包,把old_data_key该为了olddatakey_base
aad = "Y2hhbGxlbmdl"
keyid = keyid
old_data_key = 'RwLH+XV+JB4olEtaXDv2OgnmsZK5IgcShP7TWlA3PrMPHZese+I8rSngKeM='
ukeyid = asymmetric_keyid
padding_mode = PaddingMode.EH_RSA_PKCS1 #"EH_PAD_RSA_PKCS1"
result = client.export_data_key(
aad,keyid,old_data_key,ukeyid,padding_mode
)
print("result:",result)
log_message(f"result:{result}")
GetPublicKey
# GetPublicKey
asymmetric_keyid = asymmetric_keyid
result = client.get_public_key(
asymmetric_keyid
)
print("result:",result)
log_message(f"result:{result}")
GetParametersForImport & ImportKeyMaterial
GetParametersForImport 用于提供导入用户自己的秘钥即key material的相关文件,如公钥和token。
ImportKeyMaterial 这个函数是将自己的秘钥导入到指定的外部CMKid的参数,但是我这里目前的调试是失败的,原因大概是出在对秘钥材料的加密上了,这里不确定是否要用这个api,所以暂时搁置
2.3 Key Management APIs 代码示例
from ehsm import Client
from ehsm.api.enums import KeySpec, Origin, KeyUsage, PaddingMode,DigestMode,MessageType
url = "https://60.205.130.184:9002/ehsm"
appid= "ae264c5a-ad26-4349-afc1-b6dfb35baa7f"
apikey = "hF4Rd4LTdzrnpxEsddinZn2kutdi7dw8"
client = Client(base_url=url, appid = appid,apikey = apikey, allow_insecure=True)
# 创建CMK对称秘钥,对称CMK负责加密DataKey和少量数据,非对称CMK负责签名/验证或者非对称加解密数据
result = client.create_key(
KeySpec.EH_AES_GCM_128, Origin.EH_INTERNAL_KEY, KeyUsage.EH_KEYUSAGE_ENCRYPT_DECRYPT
)
print("result:",result)
log_message(f"result:{result}")
# #DeleteKey 删除指定秘钥id的秘钥
keyid = '3c5fdbea-034f-4d7f-8a57-3a866b73347f'
result = client.delete_key(
keyid
);
print("result:",result)
log_message(f"result:{result}")
# DeleteALLKey
# 删除当前账户的所有密钥,请不要轻易尝试
##### !!! result = !!! client.delete_all_key();
# print("result:",result)
# log_message(f"result:{result}")
# EnableKey 激活被禁用的密钥
keyid = '0191752d-dc72-469c-8516-180d28c59d22'
result = client.enable_key(
keyid
);
print("result:",result)
log_message(f"result:{result}")
# DisableKey 激活被禁用的密钥
keyid = '0191752d-dc72-469c-8516-180d28c59d22'
result = client.disable_key(
keyid
);
print("result:",result)
log_message(f"result:{result}")
#ListKey 列出所有秘钥的列表
# keyid:唯一标识符; alias:密钥的别名 ; keyspec:密钥的类型和用途; keystate:密钥的状态码,1激活/0禁用; creation_data&expire_time:创建和过期的时间节点
result = client.list_key();
print("result:",result)
log_message(f"result:{result}")