当前位置: 首页 > 知识库问答 >
问题:

iText7:如何在分离的PKCS7中验证签名?

高嘉树
2023-03-14

我正在编写一个接收PKCS7数据(从签名的PDF文档中提取)的服务,并需要对其进行验证。

我使用iText7 PdfPKCS7进行验证,但签名验证总是失败。我可以从PKCS7读取所有其他信息(证书、时间戳等,我也用OpenSSL验证了这一点)。只有签名显示为无效。

下面是测试案例:

public static void main(String[] args) throws IOException, GeneralSecurityException,
    NoSuchFieldException, IllegalArgumentException, IllegalAccessException {

    Logger logger = Logger.getLogger(PKCS7Test.class.getName());

    BouncyCastleProvider provider = new BouncyCastleProvider();
    Security.addProvider(provider);

    String path ="/tmp/signed.pdf";

    PdfDocument pdf = new PdfDocument(new PdfReader(path));
    SignatureUtil signatureUtil = new SignatureUtil(pdf);
    List<String> names = signatureUtil.getSignatureNames();
    String outerRevisionName = names.get(names.size()-1);

    PdfPKCS7 pkcs7In = signatureUtil.verifySignature(outerRevisionName);

    boolean isValidSignature = pkcs7In.verify();
    logger.log(Level.INFO, "pkcs7In signature is " + ((isValidSignature)?"":"not ") + "valid");

    // get hash of original document       
    Field digestAttrField = PdfPKCS7.class.getDeclaredField("digestAttr");
    digestAttrField.setAccessible(true);
    byte[] originalDigest = (byte[]) digestAttrField.get(pkcs7In);

    // get pkcs7 structure of original signature                
    PdfDictionary dict = signatureUtil.getSignatureDictionary(outerRevisionName);        
    PdfString contents = dict.getAsString(PdfName.Contents);
    byte [] originalBytes = contents.getValueBytes();
    String originalPkcs7 = Base64.getEncoder().encodeToString(originalBytes);

    // now reverse process and import PKCS7 data back into object
    byte[] pkcs7Bytes = Base64.getDecoder().decode(originalPkcs7);
    PdfPKCS7 pkcs7Out = new PdfPKCS7(pkcs7Bytes, PdfName.Adbe_pkcs7_detached, provider.getName());

    isValidSignature = pkcs7Out.verify();
    logger.log(Level.INFO, "pkcs7Out signature is " + ((isValidSignature)?"":"not ") + "valid");

    // get hash of original document from imported signature     
    digestAttrField = PdfPKCS7.class.getDeclaredField("digestAttr");
    digestAttrField.setAccessible(true);
    byte [] importedDigest = (byte[]) digestAttrField.get(pkcs7Out);

    logger.log(Level.INFO, "Hash values are " + ((Arrays.areEqual(originalDigest, importedDigest))?"":"not ") + "equal");

输出总是:

pkcs7In signature is valid
pkcs7Out signature is not valid
Hash values are equal

我想我在进口方面做错了什么,但就是找不到什么...

顺便说一句,/tmp/signed。pdf验证其他pdf工具(Adobe DC、PdfOnline等)中的OK(签名和内容)

编辑:我试图用BouncyCastle验证签名,但也失败了...

    CMSSignedData signature = new CMSSignedData(pkcs7Bytes);        

    Store                   certStore = signature.getCertificates();
    SignerInformationStore  signers = signature.getSignerInfos();
    Collection              c = signers.getSigners();
    Iterator                it = c.iterator();

    while (it.hasNext())
    {
        SignerInformation   signer = (SignerInformation)it.next();
        Collection          certCollection = certStore.getMatches(signer.getSID());

        Iterator              certIt = certCollection.iterator();
        X509CertificateHolder cert = (X509CertificateHolder)certIt.next();

       try {
            signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
        }
        catch (Exception ex) {
            logger.log(Level.INFO, "Failed to verify with: " + cert.getSubject().toString() + ": " + ex.getLocalizedMessage());
            byte [] contentDigest = signer.getContentDigest();
            bi = new BigInteger(1, contentDigest);
            String hash = String.format("%0" + (contentDigest.length << 1) + "x", bi);
            logger.log(Level.INFO, "Bouncycastle Hash from pkcs7Out is {0}", hash);
        }
    }

结果是验证失败:...消息摘要属性值与计算值不匹配,哈希值明显不同于真实值...

编辑2:通过向PdfPKCS7添加单独的verify(byte[]msgDigestBytes)方法来解决问题:

public boolean verify(final byte[] msgDigestBytes) throws GeneralSecurityException {
    if (verified)
        return verifyResult;
    if (isTsp) {
        TimeStampTokenInfo info = timeStampToken.getTimeStampInfo();
        MessageImprint imprint = info.toASN1Structure().getMessageImprint();
        byte[] md = msgDigestBytes; // was: messageDigest.digest();
        byte[] imphashed = imprint.getHashedMessage();
        verifyResult = Arrays.equals(md, imphashed);
    } else {
        if (sigAttr != null || sigAttrDer != null) {
            // was: final byte[] msgDigestBytes = messageDigest.digest();
            boolean verifyRSAdata = true;
            // Stefan Santesson fixed a bug, keeping the code backward compatible
            boolean encContDigestCompare = false;
            if (rsaData != null) {
                verifyRSAdata = Arrays.equals(msgDigestBytes, rsaData);
                encContDigest.update(rsaData);
                encContDigestCompare = Arrays.equals(encContDigest.digest(), digestAttr);
            }
            boolean absentEncContDigestCompare = Arrays.equals(msgDigestBytes, digestAttr);
            boolean concludingDigestCompare = absentEncContDigestCompare || encContDigestCompare;
            boolean sigVerify = verifySigAttributes(sigAttr) || verifySigAttributes(sigAttrDer);
            verifyResult = concludingDigestCompare && sigVerify && verifyRSAdata;
        } else {
            if (rsaData != null)
                sig.update(msgDigestBytes); // was: sig.update(messageDigest.digest());
            verifyResult = sig.verify(digest);
        }
    }
    verified = true;
    return verifyResult;
}

为此,我需要来自可信来源的文档的原始签名哈希,在我的情况下,我有。这是否仍然验证哈希上的签名是正确的?

共有1个答案

微生景胜
2023-03-14

PdfPKCS7 pkcs7Out = new PdfPKCS7(pkcs7Bytes, PdfName.Adbe_pkcs7_detached, provider.getName());

isValidSignature = pkcs7Out.verify();

您无法期望签名的正确验证结果:此PdfPKCS7仅知道CMS签名容器、签名子过滤器和安全提供程序以提供算法实现。因此,它没有关于签名实际要签名的PDF的信息。因此,这段代码无法验证所讨论的签名,特别是它是否正确地对其声称的签名数据进行了签名!

如果您想使用该PdfPKCS7对象验证签名,您必须完成初始化,以便它确实具有来自PDF的所需信息。

要查看所需内容,请查看SignatureUtil方法verifySignature

PdfPKCS7 pk = null;
if (sub.equals(PdfName.Adbe_x509_rsa_sha1)) {
    PdfString cert = signature.getPdfObject().getAsString(PdfName.Cert);
    if (cert == null)
        cert = signature.getPdfObject().getAsArray(PdfName.Cert).getAsString(0);
    pk = new PdfPKCS7(PdfEncodings.convertToBytes(contents.getValue(), null), cert.getValueBytes(), provider);
}
else
    pk = new PdfPKCS7(PdfEncodings.convertToBytes(contents.getValue(), null), sub, provider);
updateByteRange(pk, signature);
PdfString date = signature.getDate();
if (date != null)
    pk.setSignDate(PdfDate.decode(date.toString()));
String signName = signature.getName();
pk.setSignName(signName);
String reason = signature.getReason();
if (reason != null)
    pk.setReason(reason);
String location = signature.getLocation();
if (location != null)
    pk.setLocation(location);

因此,你必须

  • 更新签名数据的摘要,如SignatureUtil。updateByteRangeDos;这是将实际签名数据通知PdfPKCS7对象以允许实际验证的步骤;和
 类似资料:
  • 情况是我必须检查一个数字签名: 字符串1“A1005056807CE11EE2B4CE0025305725CFRCN=KED,OU=I0020266601,OU=SAPWebAS,O=SAPTrustCommunity,C=DE20130611102236”通过PKCS#7签名并通过HTTP-URL发送给我。 我在BASE64中获得签名的内容(在代码字符串sc中查找)。 现在我必须检查,如果Str

  • 我想使用BouncyCastle解析和验证OpenPGP分离签名。签名如下所示: 下面是我如何尝试在Kotlin中创建CMSSignedData: 我应该如何使用BouncyCastle验证这种签名?

  • 是否可以验证带有p7s分离签名的文件?我正试图使用Openssl实现这一点,但我得到了一条关于Openssl的默认消息和 这是我的命令: 是否可以使用openssl进行文件验证和p7s签名? --编辑。。。 只是想让你知道。我有一个p7s文件和一个pdf文件。我想知道如何验证这一点。

  • > 文件,将ZIP文件的内容描述为XML(文件); 包含传输文档内容的文件(例如,文件); 具有分离数字签名内容的文件(文件-分离数字签名); > 方法(请参阅下面的此方法)当前未使用文件的分离签名。我确实试过了,但没有成功。如何正确验证文件对应文件的分离签名? 在方法(请参阅下面的方法)中,如何验证从文件中提取的证书与从Base64格式的输入字符串中提取的证书的一致性? 代码行-->Certif

  • 我需要将一个使用iText5进行PDF签名验证/创建的Java程序移植到iText7。 旧代码显然不能按原样工作,因为iText的大部分内容都经过了重组。 我找到的所有关于如何做到这一点的例子和教程都是针对iText5的。(非常好的)白皮书也是如此。它们依赖于通过方法返回的列表,在该方法上执行所有与签名相关的操作。 在iText7中,不再具有该方法。 有人知道iText7的例子/文档吗?

  • 问题内容: 我有一个字符串,一个签名和一个公共密钥,我想验证字符串上的签名。密钥如下所示: 我已经阅读了一段时间的pycrypto文档,但是我不知道如何使用这种密钥制作RSAobj。如果您了解PHP,我将尝试执行以下操作: 另外,如果我对任何术语感到困惑,请告诉我。 问题答案: 标记之间的数据是包含PKCS#1 RSAPublicKey的PKCS#8 PublicKeyInfo的ASN.1 DER