说明
签名涉及多种证书公私钥,主要包含根证书、证书吊销列表、由根证书派生的签名证书和时间戳证书等。
根证书是整个信任链起点,泄露根证书私钥会产生严重安全问题,需要保障根证书私钥绝对安全,建议客户使用安全加密、离线保存、多人持有等措施保护根证书私钥不会被泄露、遗失、损毁。
所有与根证书私钥相关的操作建议在离线环境(且一直保持离线状态)中执行,华为不对证书的生成、保存、传输做任何安全保证,也不承担相应后果,一切过程由客户操作、存储和传递[/color]。
本文档参考了 昇腾 - CMS签名
创建签名证书
创建CA证书,创建的根证书私钥请妥善保存
CA证书由根公钥和私钥组件:
- 私钥(
rootca_pri.pem)用于签发签名证书,也可用于吊销签名证书,如果泄漏会引发安全风险,需要证书拥有者妥善保存(如加密+多份安全备份)。 - 公钥(
rootca.der)用于验证签名证书和CRL合法性,其hash值需要写入iBMC的安全区。 - 证书中的组织名称(
O)、证书名称(CN)请变更为你需要的名称
# 生成证书配置文件,其中O一般改为公司名称,CN改你期望的证书名称
cat << EOF > rootca.cnf
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
prompt = no
default_md = sha256
string_mask = utf8only
x509_extensions = extensions
[ req_distinguished_name ]
C = CN
O = Huawei
CN = Huawei EnterpriseIT Root CA
[ extensions ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints = CA:TRUE
keyUsage = cRLSign, keyCertSign
EOF
# 创建根证书私钥
openssl genpkey -algorithm rsa-pss -pkeyopt rsa_keygen_bits:4096 -pkeyopt rsa_keygen_pubexp:65537 -out rootca_pri.pem
# 创建根公钥
openssl req -x509 -new -days 18250 -key rootca_pri.pem -config rootca.cnf -sha256 -out rootca.pem
# 加密存储,密码需要保证适当的长度和复杂度,来源建议是随机生成,例如`dd if=/dev/random bs=1 count=128 2>&1| sha256sum | awk '{print $1}'`命令可以生成64字节的随机字符串
# 测试密码 56ff8ba86d0979b2e6915d156ffcd49b9d7af80a3cceebdd32406e21ebe13594
openssl pkcs12 -export -out rootca.pfx -in rootca.pem -inkey rootca_pri.pem
# 转换成der格式,重要,该文件需要写入环境中,并将其hash写入安全启动支持的安全区域(efuse或其它otp设备)
openssl x509 -in rootca.pem -inform pem -outform der -out rootca.der
# 生成后注意删除临时文件
rm rootca_pri.pem rootca.cnf
执行完上述命令后生成如下三个文件:
- rootca.der:根公钥DER格式证书,需要导入到BMC环境中,安全启动和安全升级功能都依赖此证书验证软件包完整性性,可公开的文件。
- rootca.pfx: 加密存储的根公私钥,后续签发签名证书和吊销列表时使用,如果泄漏会引发安全风险,需要证书拥有者妥善保存(加密+多份安全备份),建议证书和加密密码分开(不同的存储区域 + 不同的管理者)保存。
- rootca.pem:可从rootca.der导出的PEM格式证书。
创建签名证书,该证书由CA根证书签名
签名需要使用私钥,显然我们不能使用根CA的私钥签名。实际上,日常签名活动我们使用的是由CA根证书签发的签名证书。
# 创建签名证书配置,其中O一般改为公司名称,CN改为你期望的证书名称
cat << EOF > signcert.cnf
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
prompt = no
default_md = sha256
string_mask = utf8only
x509_extensions = extensions
[ req_distinguished_name ]
C = CN
O = Huawei
CN = Huawei EnterpriseIT S1
[ extensions ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid, issuer
basicConstraints = CA:FALSE
keyUsage = critical, nonRepudiation, digitalSignature
extendedKeyUsage = codeSigning
EOF
# 生成签名私钥signcert_pri.pem
openssl genrsa -out signcert_pri.pem 4096
# 生成签名请求文件signcert.csr
openssl req -new -config signcert.cnf -out signcert.csr -key signcert_pri.pem
# 从rootca.pfx中导出rootca.pem和rootca_pri.pem,使用后清除
openssl pkcs12 -in rootca.pfx -nocerts -out rootca_pri.pem -nodes
# 生成签名证书,有效期1年,请合理设置,证书到期时及时重新生成并替换
openssl x509 -req -in signcert.csr -CA rootca.pem -CAkey rootca_pri.pem -CAcreateserial -out signcert.pem -days 365 -extensions extensions -extfile signcert.cnf
# 删除rootca_pri.pem
rm rootca_pri.pem
# 可选:生成der格式的签名证书
# openssl x509 -in signcert.pem -inform pem -outform der -out signcert.der
# 将公私钥打包成pem格式的签名证书(签名需要使用此文件),请妥善保存
cat signcert.pem signcert_pri.pem > signer.pem
# 清理环境
rm signcert.csr signcert.pem signcert_pri.pem signcert.cnf rootca.srl
执行完上述命令后生成如下一个文件:
- signer.pem:签名证书,此证书用于hpm包、rootfs、uboot签名,建议参考 《加密证书管理》 章节使用AES-256-CBC加密存储。
吊销证书(发生签名证书泄露时)
当签名证书泄露时,使用泄露证书将带来安全风险,为此我们需要吊销签名证书并生成新证书,吊销证书会生成吊销列表,签名验证会检查签名证书是否已被吊销。
以下脚本将创建crl目录,请妥善保存
# 以下为首次吊销时需要生成的文件
# 创建crl目录
mkdir crl -p
cat << EOF > crl/crl.cnf
[ ca ]
default_ca=CA_default
[ CA_default ]
database=./crl/rootca.crlindex
crlnumber=./crl/rootca.crlnumber
private_key=./rootca_pri.pem
certificate=./rootca.pem
crl_extensions=crl_ext
default_days=365
default_crl_days=365
default_md=default
preserve=no
[ crl_ext ]
authorityKeyIdentifier=keyid
EOF
# 创建一个空的数据库
touch crl/rootca.crlindex
# crl序列号
echo 01 > crl/rootca.crlnumber
# 从rootca.pfx中导出rootca.pem和rootca_pri.pem,使用后清除
openssl pkcs12 -in rootca.pfx -nocerts -out rootca_pri.pem -nodes
# 生成CRL文件
openssl ca -gencrl -keyfile rootca_pri.pem -cert rootca.pem -out rootca.crl.pem -config crl/crl.cnf
# 删除rootca_pri.pem
rm rootca_pri.pem
# 转换成crl文件
openssl crl -in rootca.crl.pem -inform pem -outform der -out rootca.crl
# 删除文件
rm rootca.crl.pem
执行完上述命令后生成如下文件:
- rootca.crl:证书吊销列表,需要通过升级方式更新BMC环境中的crl文件,以正确吊销存在风险的证书,确保这些证书不被用于制作非法的固件。
- crl:吊销配置目录,后续吊销其它文件时使用
新增吊销操作
有必要时可以吊销签名证书,吊销的动作将生成一个新的rootca.crl文件,请将该文件传递给构建系统。证书吊销命令如下:
# 吊销signer.pem文件,需要从rootca.pfx中解出根私钥,使用完成后删除
openssl ca -revoke signer.pem -config crl/crl.cnf
# 生成crl.pem文件
openssl ca -gencrl -out rootca.crl.pem -config crl/crl.cnf
# 生成rootca.crl文件
openssl crl -in rootca.crl.pem -inform pem -outform der -out rootca.crl
# 删除文件
rm rootca.crl.pem
时间戳签名证书
签名证书会因为有效期、被吊销等原因导致失效,验签时需要验证签名时刻签名证书有效即可,为此需要有一个可信的签名时间戳,该时间戳受时间戳签名保护,需要使用时间戳签名证书。
时间戳签名证书生成命令:
cat << EOF > tsa.cnf
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
prompt = no
default_md = sha256
string_mask = utf8only
x509_extensions = extensions
[ req_distinguished_name ]
C = CN
O = Huawei
CN = Huawei EnterpriseIT Timestamp Certificate
[ extensions ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid, issuer
basicConstraints = critical, CA:FALSE
keyUsage = critical, nonRepudiation, digitalSignature
extendedKeyUsage = critical, timeStamping
[ tsa ]
default_tsa = tsa_config1
[ tsa_config1 ]
serial = ./serial
crypto_device = builtin
signer_digest = sha256
default_policy = 1.2.3.4.1
other_policies = 1.2.3.4.5.6, 1.2.3.4.5.7
digests = sha1, sha256, sha384, sha512
ess_cert_id_chain = no
ess_cert_id_alg = sha256
EOF
echo 03 > serial
openssl genrsa -out tsa_pri.pem 4096
openssl req -new -config tsa.cnf -out tsa.csr -key tsa_pri.pem
# 从rootca.pfx中导出rootca.pem和rootca_pri.pem,使用后清除
openssl pkcs12 -in rootca.pfx -nocerts -out rootca_pri.pem -nodes
# 有效期20年,请合理设置
openssl x509 -req -in tsa.csr -CA rootca.pem -CAkey rootca_pri.pem -CAcreateserial -out tsa.pem -days 7300 -extensions extensions -extfile tsa.cnf
# 删除rootca_pri.pem
rm rootca_pri.pem
# 生成签名pem文件
cat tsa.pem tsa_pri.pem > ts_signer.pem
rm tsa.csr tsa.pem tsa_pri.pem serial
执行完上述命令后生成如下文件:
- ts_signer.pem:时间戳签名,用于保护签名时间的完整性,建议参考 《加密证书管理》 章节使用AES-256-CBC加密存储。
加密证书管理
iBMC签名工具支持读取加密的签名证书,过程涉及签名证书加密和解密及加密密钥管理。
生成加密密钥和加密证书
当前支持的加密方式为aes-256-cbc,密钥由厂商自己生成,长度必须为32字节,请妥善保存。以下命令可用于随机生成一个长度为32字节的加密密钥。
# 生成一个随机密码并打印到终端,此密码将用于后续密钥解密, 如: 2ef7680ab4434fca5b58a1f9facaf8dc
dd if=/dev/random bs=1 count=128 2>&1| sha256sum | awk '{print $1}' | cut -c1-32
签名证书加密存储命令示例:
# 加密签名证书,要求输入两次加密密码
openssl aes-256-cbc -salt -pbkdf2 -in signer.pem -out signer.pem.enc
# 验证:解密加密签名证书确保正确
openssl aes-256-cbc -d -pbkdf2 -out signer.pem.out -in signer.pem.enc
# 判断sha256是否一致
sha256sum signer.pem signer.pem.out
# 删除签名证书原文
rm signer.pem signer.pem.out
# 加密时间戳签名证书,要求输入两次加密密码
openssl aes-256-cbc -salt -pbkdf2 -in ts_signer.pem -out ts_signer.pem.enc
# 解密加密签名证书确保正确
openssl aes-256-cbc -d -pbkdf2 -out ts_signer.pem.out -in ts_signer.pem.enc
# 判断sha256是否一致
sha256sum ts_signer.pem ts_signer.pem.out
# 删除签名证书原文
rm ts_signer.pem ts_signer.pem.out
加密密钥管理
生成的加密密钥和密钥加密密码需要妥善保存,建议只有CI环境中使用,不提供给普通开发者,使用方式可以参考 jenkins的安全凭证,如下jenkins file示例演示了使用secret text文本在构建阶段注入:
pipeline {
agent {
// 此处定义 agent 的细节
}
environment {
// signer_secret_text为安全凭证的ID,可自定义。
// SIGN_PASSWORD为签名密码的环境变量,hpm_signer签名工具会读取该密码
SIGN_PASSWORD = credentials('signer_secret_text')
}
stages {
stage('Example stage 1') {
steps {
// 构建代码中可以使用SIGN_SECRET环境变量获取到加密密钥
}
}
}
}
管理实践
签名管理涉及安全和信任,建议定义多个角色参与管理,如:
- 根证书私钥(rootca.pfx):证书持有人和密码持有人分离,多份存储可以更好的防止密钥泄露、损毁、遗失。
- 签名证书(signer.pem.enc和ts_signer.pem.enc):日常开发和构建时使用,需要集成在CI构建镜像中,加密密钥由jenkins的 jenkins的安全凭证存储,并在jenkinsfile中申明使用,并尽量保证安全凭证有效期最小化。
- 吊销列表(rootca.crl):在识别到签名证书可能泄露时需要及时吊销,及时更新CI构建镜像中的rootca.crl文件,随后可通过升级BMC环境生效。
签名配置
开发者生成各项签名证书后可在manifest.yml中配置签名服务,完成配置后可以使用SIGN_PASSWORD=xxxx bmcgo build启动构建,其中xxxx替换为签名密码。
manifest.yml配置示例:
# yaml-language-server: $schema=/usr/share/bmcgo/schema/manifest.schema.json
base:
# 其它已省略
# 签名文件,由构建系统复制到self.config.board_path目录,如需定制
signature:
certificates:
# 根公钥证书,文件需要满足正则表达式要求:^(/[a-zA-Z0-9._-]+)+\\.der$
rootca_der: /home/xuhj/signer/rootca.der
# 签名证书,文件需要满足正则表达式要求:^(/[a-zA-Z0-9._-]+)+\\.pem(.enc)?$
signer_pem: /home/xuhj/signer/signer.pem.enc
# 时间戳签名证书,文件需要满足正则表达式要求:^(/[a-zA-Z0-9._-]+)+\\.pem(.enc)?$
timestamp_signer_pem: /home/xuhj/signer/ts_signer.pem.enc
# 时间戳签名私钥,文件需要满足正则表达式要求:^(/[a-zA-Z0-9._-]+)+\\.cnf$
timestamp_signer_cnf: /home/xuhj/signer/tsa.cnf
# 证书吊销列表:文件需要满足正则表达式要求:^(/[a-zA-Z0-9._-]+)+\\.crl$
rootca_crl: /home/xuhj/signer/rootca.crl
签名和验证
构建工具提供了hpm_signer签名和hpm_verify验签命令,以下示例结合创建的证书演示了如何签名和验签。
# 创建待签名源文件
dd if=/dev/random of=image.filelist bs=1K count=1
# CMS签名,可以使用-p参数传入密码
hpm_signer -s signer.pem.enc -t ts_signer.pem.enc -T tsa.cnf -i image.filelist -o image.cms -p <加密密码>
# CMS签名, 可使用SIGN_PASSWORD存储加密密码
SIGN_PASSWORD=<加密密码> hpm_signer -s signer.pem.enc -t ts_signer.pem.enc -T tsa.cnf -i image.filelist -o image.cms
# 验证签名
hpm_verify -r rootca.pem -C cms.crl.pem -s image.cms -c image.filelist

