我正在开发一个需要使用 TPM(可信平台模块)的项目,并且需要验证 TPM 生成的 ECC 签名。验证需要在软件中完成,即在 TPM 之外,我们希望使用 OpenSSL (libcrypto) 来实现此目的。因此,TPM 的 ECC 公钥必须从 TPM 特定的
TPMT_PUBLIC
数据结构(其中由 TPM 提供)转换为 EVP_PKEY
格式可以与 OpenSSL (libcrypto) API 一起使用进行签名验证。
TPM 以以下结构提供 ECC 公钥,该结构不是由我定义,而是由 TPM 软件堆栈 (TSS) 定义,我必须使用它:
我从 TPM 获得的通用公钥结构(仅显示相关字段):
/* Definition of TPMT_PUBLIC Structure */
typedef struct TPMT_PUBLIC TPMT_PUBLIC;
struct TPMT_PUBLIC {
/* ... */
TPMU_PUBLIC_ID unique; /* For an asymmetric key this would be the public key. */
};
上面“唯一”字段中的键类型具体结构:
/* Definition of TPMU_PUBLIC_ID Union <INOUT S> */
typedef union TPMU_PUBLIC_ID TPMU_PUBLIC_ID;
union TPMU_PUBLIC_ID {
TPM2B_DIGEST keyedHash;
TPM2B_DIGEST sym;
TPM2B_PUBLIC_KEY_RSA rsa;
TPMS_ECC_POINT ecc; /* <-- This is what is used for ECC key !!! */
TPMS_DERIVE derive;
};
TPM 提供的实际 ECC 公钥,如上面的“ecc”字段中所示:
/* Definition of ECC TPMS_ECC_POINT Structure */
typedef struct TPMS_ECC_POINT TPMS_ECC_POINT;
struct TPMS_ECC_POINT {
TPM2B_ECC_PARAMETER x; /* X coordinate */
TPM2B_ECC_PARAMETER y; /* Y coordinate */
};
x 和 y 坐标存储为:
/* Definition of ECC TPM2B_ECC_PARAMETER Structure */
typedef struct TPM2B_ECC_PARAMETER TPM2B_ECC_PARAMETER;
struct TPM2B_ECC_PARAMETER {
UINT16 size;
BYTE buffer[128];
};
更多详细信息(不是我的代码)可以在这里找到:
https://github.com/tpm2-software/tpm2-tss/blob/master/include/tss2/tss2_tpm2_types.h
因此,我们实际上从 TPM 中获取了 ECC 公钥的“x”和“y”坐标,每个坐标都以“原始”字节数组的形式提供。这不是 OpenSSL 可以直接使用的某种格式,例如 PEM 编码的公钥!那么我如何将其转换为 OpenSSL 能够处理的 EVP_PKEY
实例,例如通过
EVP_PKEY_verify()
功能?不幸的是,我没有能够找到如何从“原始”值(x和y坐标)构造
EVP_PKEY
格式的ECC密钥的示例。无论是在 OpenSSL 文档中还是其他地方......(我已经根据“原始”RSA 模数和指数值构造了一个 RSA 密钥作为
EVP_PKEY
,所以我认为应该可以对 ECC 密钥做类似的事情)
谢谢您的建议!
o2i_ECPublicKey
可用于将八位字节流(即字节序列)转换为
EC_KEY
类型的 EC 公钥。ec.h头文件包含以下内容:
/** Decodes a ec public key from a octet string.
* \param key a pointer to a EC_KEY object which should be used
* \param in memory buffer with the encoded public key
* \param len length of the encoded public key
* \return EC_KEY object with decoded public key or NULL if an error
* occurred.
*/
EC_KEY *o2i_ECPublicKey(EC_KEY **key, const unsigned char **in, long len);
第一个参数需要初始化,例如使用
EC_KEY_new_by_curve_name
。输入缓冲区预计是一个值为 4 的单字节,后跟该点的 X 坐标,然后是该点的 Y 坐标。
加载EC_KEY
后,您可以使用
EVP_PKEY_new
创建新的 EVP_PKEY
,然后使用 EVP_PKEY_set1_EC_KEY
将 EC_KEY
加载到 EC_KEY
中。