PDF itext pkcs#7 国密签名验证

2 篇文章 0 订阅

环境需求

  • bouncycastle provider 1.60以上
  • itext7-core 7.1.6
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.1.6</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.64</version>
</dependency>

Quick Start

在新的 bouncycastle 库中已经完全支持了 国密算法,而在itext中对国密算法的支持不太友好。

我们通过下面代码来验证PDF中的PKCS#7类型的数字签名

Security.addProvider(new BouncyCastleProvider());

String file = "C:/Users/Administrator/Documents/sm2pkcs7sig.pdf";
PdfReader reader = new PdfReader(file);
// 创建签名工具
PdfDocument pdfDocument = new PdfDocument(reader);
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
// 获取所有的签名域,域名称列表
List<String> fieldNames = signatureUtil.getSignatureNames();
for (String fieldName : fieldNames) {
    // 获取签名值的PKCS#7数据
    PdfPKCS7 pkcs7 = signatureUtil.readSignatureData(fieldName, "BC");
    // 数字签名验证是否有效
    boolean isValidate = pkcs7.verifySignatureIntegrityAndAuthenticity();
    System.out.println("Is signature validate: " + isValidate);
}

C:/Users/Administrator/Documents/sm2pkcs7sig.pdf 是一个采用了SM3withSM2签名的PDF文件

通过上述方法我们在验证类似于SHA1withRSASHA256withRSA的时候是OK,但是在验证国密算法时会抛出下面异常:

Caused by: com.itextpdf.kernel.PdfException: Unknown PdfException.
	...
Caused by: java.security.NoSuchAlgorithmException: no such algorithm: 1.2.156.10197.1.401with1.2.156.10197.1.501 for provider BC
	 ...

1.2.156.10197.1.401with1.2.156.10197.1.501 实际上就是 SM3withSM2的OID

实际上 bouncycastle 库中是支持SM3withSM2的OID的算法的,造成这个问题的原因是出在iText上的。

查看 iText7源码 可以知道解析签名算法的方式是从pkcs#7对象中取出:摘要算法、加密算法的OID,然后通过JCE算法提供者,获取对应的签名算法,用于之后的验证

private Signature initSignature(PublicKey key) throws ...
    String digestAlgorithm = getDigestAlgorithm();
    if (PdfName.Adbe_x509_rsa_sha1.equals(getFilterSubtype()))
        digestAlgorithm = "SHA1withRSA";
    Signature signature = SignUtils.getSignatureHelper(digestAlgorithm, provider);
    signature.initVerify(key);
    return signature;
}
public String getDigestAlgorithm() {
    return getHashAlgorithm() + "with" + getEncryptionAlgorithm();
}
public String getHashAlgorithm() {
    return DigestAlgorithms.getDigest(digestAlgorithmOid);
}
public String getEncryptionAlgorithm() {
    String encryptAlgo = EncryptionAlgorithms.getAlgorithm(digestEncryptionAlgorithmOid);
    if (encryptAlgo == null)
        encryptAlgo = digestEncryptionAlgorithmOid;
    return encryptAlgo;
}

出现之所以getDigestAlgorithm 返还了1.2.156.10197.1.401with1.2.156.10197.1.501 而不是 SM3withSM2这样的名字。

原因是:itext中EncryptionAlgorithmsDigestAlgorithms 类的映射关系中没有国密算法的标识符

那么为了简单的解决问题我们直接通过反射加入然后测试是否能够解决

public static void main(String[] args) throws Exception {
    Security.addProvider(new BouncyCastleProvider());
    Field digestNamesField = DigestAlgorithms.class.getDeclaredField("digestNames");
    digestNamesField.setAccessible(true);
    HashMap<String, String> digestNames = (HashMap<String,String>)digestNamesField.get(null);
    digestNames.put("1.2.156.10197.1.401", "SM3");
    
    Field algorithmNamesField = EncryptionAlgorithms.class.getDeclaredField("algorithmNames");
    algorithmNamesField.setAccessible(true);
    HashMap<String, String> algorithmNames = (HashMap<String,String>)algorithmNamesField.get(null);
    algorithmNames.put("1.2.156.10197.1.501", "SM2");
    String file = "C:/Users/Administrator/Documents/sm2pkcs7sig.pdf";
    PdfReader reader = new PdfReader(file);
    
    // 创建签名工具
    PdfDocument pdfDocument = new PdfDocument(reader);
    SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
    // 获取所有的签名域,域名称列表
    List<String> fieldNames = signatureUtil.getSignatureNames();
    for (String fieldName : fieldNames) {
        // 获取签名值的PKCS#7数据
        PdfPKCS7 pkcs7 = signatureUtil.readSignatureData(fieldName, "BC");
        // 数字签名验证是否有效
        boolean isValidate = pkcs7.verifySignatureIntegrityAndAuthenticity();
        System.out.println("Is signature validate: " + isValidate);
    }
}

运行结果

Is signature validate: true

确实解决了该问题,更好的做法是继承PdfPKCS7然后做一些内部修改,不过我准备向itext7 库搞一个 Pull req 直接在对应的Map中加入算法标识符就行了。

参考

[1]. GMSSL . http://gmssl.org/docs/oid.html

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值