如何构造PKCS 7签名(一)

PKCS#7定义了加密消息的语法标准,也就是加密数据、数字信封、数字签名这些密码运算结果的数据格式标准。基于这一标准,使得不同密码体系之间交换数据成为可能。PKCS#7作为RSA安全体系的一部分,被广泛支持和使用,如CryptoAPI、OpenSSL、PDF加密签名等。但在某些情况下,如Java自带的加密库并不支持PKCS#7,或者使用PKCS#7不支持的国密算法时,就可能需要我们自己实现PKCS#7标准。

今天先结合代码讲解一下如何创建使用最多的PKCS#7数字签名。说明的是,提供的代码示例只能是示例,因为完整的代码是比较复杂的,不可能全部提供。主要是给大家起到参考和启发的作用。学习此部分内容,需要一些ASN.1基础。

PKCS#7采用ASN.1语义描述,因此数字签名也需按照其通用语法标准封装成ContentInfo类型,其定义如下。

ContentInfo::= SEQUENCE {
contentTypeContentType,
content [0] EXPLICIT ANY DEFINED BYcontentType OPTIONAL }
ContentType::= OBJECT IDENTIFIER

示例代码如下:

CInnerObject<CDerValue,IDerValue> sig; // CDerValue是自DER编码处理类。这里

hr=sig.CreateInstance(); //创建一个实例,用于生成符合ASN.1

_handle_result2(); //标准的数据。下同

hr=sig->put_Tag(TAG_SEQUENCE); // ContentInfo的类型是SEQUENCE

_handle_result2();

CInnerObject<CDerValue,IDerValue> ctype;

hr=ctype.CreateInstance();

_handle_result2();

BSTR oid =CharToWchar(szOID_PKCS_7_SIGNED); //标识: “1.2.840.113549.1.7.2”

hr=ctype->put_ObjectIdentifier(oid); // contentType是OBJECT IDENTIFIER类型, //把PKCS#7数字签名的标识赋值给它。

_handle_result2();

hr=sig->AddItem(ctype,&datasize);

_handle_result2();

CInnerObject<CDerValue,IDerValue> cdata; //PKCS7 signedData数据

hr=cdata.CreateInstance();

_handle_result2();

hr=cdata->put_Tag(TAG_OPT); // OPTIONAL类型, 且content标识为0, //所以TAG值设置为TAG_OPT

_handle_result2();

hr=sig->AddItem(cdata,&datasize);

_handle_result2();

数字签名类型为SignedData ,定义如下。

SignedData::= SEQUENCE {
version Version,
digestAlgorithmsDigestAlgorithmIdentifiers,
contentInfoContentInfo,
certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
crls[1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerInfos SignerInfos
}
首先生成SEQUENCE类型的SignedData,并填充到上面ContentInfo里的content里

CInnerObject<CDerValue,IDerValue> sdata;

hr=sdata.CreateInstance();

_handle_result2();

hr=sdata->put_Tag(TAG_SEQUENCE);

_handle_result2();

hr=cdata->AddItem(sdata,&datasize);

_handle_result2();

接下来就按照标准拼装数据,形成一个标准的PKCS#7签名。

  1. version

整数类型,指PKCS#7语法的版本,目前取值为1

CInnerObject<CDerValue,IDerValue> version; //版本号

hr=version.CreateInstance();

_handle_result2();

eseals::CBigIntegerPtr ver; //这里用了一个自定义的大整数类处理

hr=ver.Create(1); //设置为1

hr=version->SetInteger(ver);

_handle_result2();

hr=sdata->AddItem(version,&datasize);

_handle_result2();

  1. digestAlgorithms

digestAlgorithms是消息摘要算法标识符的集合,用来标识每个签名者使用的消息摘要算法,ASN.1定义如下。

DigestAlgorithmIdentifiers:=Set of DigestAlgorithmIdentifier
Set of DigestAlgorithmIdentifier
DigestAlgorithmIdentifier::= AlgorithmIdentifier
AlgorithmIdentifier::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}

示例代码,使用SM3算法:

CInnerObject<CDerValue,IDerValue> algorithms; //摘要算法集合

hr=algorithms.CreateInstance();

_handle_result2();

hr=algorithms->put_Tag(TAG_SET); //集合类型,这里只有一个元素, //即单用户签名

_handle_result2();

CInnerObject<CDerValue,IDerValue> algsdata;

hr=algsdata.CreateInstance();

_handle_result2();

hr=algsdata->put_Tag(TAG_SEQUENCE); // AlgorithmIdentifier为SEQUENCE类型 _handle_result2();

hr=algorithms->AddItem(algsdata,&datasize);

_handle_result2();

CInnerObject<CDerValue,IDerValue> algcdata;

hr=algcdata.CreateInstance();

_handle_result2();

//这里用SM3算法标识"1.2.156.10197.1.401",同时没有其他参数。

hr=algcdata->put_ObjectIdentifier(CharToWchar(szOID_SM3_Hash_Algorithm));

_handle_result2();

hr=algsdata->AddItem(algcdata,&datasize);

_handle_result2();

hr=sdata->AddItem(algorithms,&datasize);

_handle_result2();

3.contentInfo

contentInfo是被签名的原文内容。这里又是通用的ContentInfo类型,就不再贴类型定义了。如果contentInfo里的content元素不存在时,即签名中不包括原文内容。这种签名与正文分离的模式,要求验证签名时原文需另外提供,这也是实际应用中常用的方式。示例如下:

CInnerObject<CDerValue,IDerValue> content; //原文

hr=content.CreateInstance();

_handle_result2();

hr=content->put_Tag(TAG_SEQUENCE);

_handle_result2();

CInnerObject<CDerValue,IDerValue> ctncdata;

hr=ctncdata.CreateInstance();

_handle_result2();

//原文类型为数据内容(PKCS7 DATA),同时签名与正文分离,只储存原文类型

hr=ctncdata->put_ObjectIdentifier(CharToWchar(szOID_PKCS_7_DATA));

_handle_result2();

hr=content->AddItem(ctncdata,&datasize);

_handle_result2();

hr=sdata->AddItem(content,&datasize);

_handle_result2();

  1. certificates

certificates是签名使用的证书集合,它是可选的,定义如下。

ExtendedCertificatesAndCertificates:=Set of ExtendedCertificateOrCertificate
Set of ExtendedCertificateOrCertificate
ExtendedCertificateOrCertificate::= CHOICE {
certificate Certificate, – X.509
extendedCertificate[0] IMPLICIT ExtendedCertificate
}

从定义可以看出,certificates的每一个元素是X.509证书或PKCS#6扩展证书。示例里使用的是X509证书,这里通过调用CryptoAPI的CertCreateCertificateContext方法可以直接生成证书的ASN1编码内容,并赋值给certificate元素,而不需要再根据Certificate类型的定义一个个元素拼装了。示例代码如下。

CInnerObject<CDerValue,IDerValue> certs; //签名所使用的证书

hr=certs.CreateInstance();

_handle_result2();

hr=certs->put_Tag(TAG_OPT); //

_handle_result2();

hr=sdata->AddItem(certs,&datasize);

_handle_result2();

CMemoryLocator ml; // CMemoryLocator是用来处理数据的模板类

hr = ml.Base64Decode(CertStr); // CertStr是签名证书的BASE64值

CCertContext pCertContext = ::CertCreateCertificateContext(X509_ASN_ENCODING, ml, ml.GetSize()); //得到ASN1编码的证书内容

::memcpy(ml.GetBuffer(),pCertContext->pbCertEncoded,pCertContext->cbCertEncoded);

CComPtr pStm;

hr=ml.get_IStream(&pStm); //得到证书ASN1编码的字节流

_handle_result2();

CInnerObject<CDerValue,IDerValue> certsdata;

hr=certsdata.CreateInstance();

_handle_result2();

hr=certsdata->Load(pStm); //加载字节流,得到DER编码数据

_handle_result2();

hr=certs->AddItem(certsdata,&datasize);

_handle_result2();

  1. crls

crls是证书吊销列表(CRL)的集合,它也是可选的,其定义如下。所谓证书吊销列表就是由CA发布的、在它所发放证书中已经被吊销的证书的列表名单。通过CRL对比验证,我们可以确定证书是否被吊销。

CertificateRevocationLists:=Set of CertificateRevocationList
Set of CertificateRevocationList
CertificateRevocationList::= CertificateList
CertificateList::= SEQUENCE {
crlToSignCRLToSign,
algorithmIdentifierAlgorithmIdentifier,
signatureValueBIT STRING
}

在实际使用中,加入CRL验证是一件比较复杂的事情,故此处略去。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
PKCS#1 签名转换为 PKCS#7 签名可以按照以下步骤进行操作: 1. 首先,确保你已经正确引入了Bouncy Castle库。你可以在项目中添加BC库的JAR文件或者使用构建工具(如Maven)来添加依赖。 2. 导入所需的类: ```java import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.cms.CMSTypedData; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.PrivateKey; import java.security.Security; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; ``` 3. 添加BC作为安全提供者: ```java Security.addProvider(new BouncyCastleProvider()); ``` 4. 准备待签名的数据和私钥、证书: ```java byte[] dataToSign = <待签名的数据>; PrivateKey privateKey = <私钥>; X509Certificate certificate = <证书>; ``` 5. 创建 CMSSignedDataGenerator 对象并配置签名器: ```java CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); JcaSignerInfoGeneratorBuilder signerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder( new BcDigestCalculatorProvider()) .setDirectSignature(true); generator.addSignerInfoGenerator( signerInfoGeneratorBuilder.build( new JcaContentSignerBuilder("SHA256withRSA") .setProvider("BC") .build(privateKey), certificate)); ``` 6. 使用 CMSSignedDataGenerator 对象进行签名: ```java CMSTypedData cmsData = new CMSProcessableByteArray(dataToSign); CMSSignedData signedData = generator.generate(cmsData, true); byte[] pkcs7Signature = signedData.getEncoded(); ``` 通过以上步骤,你可以将 PKCS#1 格式的签名转换为 PKCS#7 格式的签名。在代码示例中,我们使用 Bouncy Castle 提供的 CMSSignedDataGenerator 类来进行签名转换。首先,我们配置签名器的构建器,然后使用私钥和证书进行签名。最后,我们将签名结果编码为字节数组形式的 PKCS#7 签名。 请注意,以上代码仅为示例,具体实现可能会根据你的需求而有所不同。同时,确保你有正确的私钥和证书,并了解 PKCS#1 和 PKCS#7 的区别。 希望这可以帮到你!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_45303938

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值