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

带签名哈希的itextsharp签名pdf

葛承嗣
2023-03-14

我正在尝试通过签名服务签署一个pdf文件。这个服务需要发送一个十六进制编码的SHA256摘要,作为回报,我会收到一个十六进制编码的SignatureValue。此外,我还收到了签名证书、中间证书、OCSP响应和TimeStampToken。但是,我在尝试使用SignatureValue对pdf进行签名时已经陷入了困境。

我读过布鲁诺的白皮书,过度浏览互联网,尝试了很多不同的方式,但签名不断出现无效。

首先,准备pdf

PdfReader reader = new PdfReader(src);
FileStream os = new FileStream(dest, FileMode.Create);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Certificate = signingCertificate;
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);

string hashAlgorithm = "SHA-256";
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
PdfSignatureAppearance appearance2 = stamper.SignatureAppearance;
Stream stream = appearance2.GetRangeStream();
byte[] hash = DigestAlgorithms.Digest(stream, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);

哈希字节[]sh并转换为字符串,如下所示

private static String sha256_hash(Byte[] value)
{
    using (SHA256 hash = SHA256.Create())
    {
         return String.Concat(hash.ComputeHash(value).Select(item => item.ToString("x2"))).ToUpper();
    }
}

并发送到签名服务。接收到十六进制编码的signatureValue I然后转换成字节

private static byte[] StringToByteArray(string hex)
{
    return Enumerable.Range(0, hex.Length).Where(x => x % 2 == 0).Select(x => Convert.ToByte(hex.Substring(x, 2), 16)).ToArray();
}
private void CreateSignature(string src, string dest, byte[] sig) 
{
    PdfReader reader = new PdfReader(src); // src is now prepared pdf
    FileStream os = new FileStream(dest, FileMode.Create);
    IExternalSignatureContainer external = new MyExternalSignatureContainer(sig);
    MakeSignature.SignDeferred(reader, "Signature1", os, external);

    reader.Close();
    os.Close();
}
private class MyExternalSignatureContainer : IExternalSignatureContainer
{
    protected byte[] sig;
    public MyExternalSignatureContainer(byte[] sig)
    {
        this.sig = sig;
    }
    public byte[] Sign(Stream s)
    {
        return sig;
    }
    public void ModifySigningDictionary(PdfDictionary signDic) { }
}

多亏了mkl的帮助和Bruno的延迟签名示例,我已经摆脱了无效签名消息。显然我没有收到来自签名服务的完整链,而只是一个中间证书,这导致了无效消息。遗憾的是,签名还是有瑕疵的。

我是这样构建链的:

List<X509Certificate> certificateChain = new List<X509Certificate>
{
     signingCertificate,
     intermediateCertificate
}; 

在MyExternalSignatureContainer的sign方法中,我现在构造并返回签名容器:

public byte[] Sign(Stream s)
{
    string hashAlgorithm = "SHA-256";
    PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);

    byte[] ocspResponse = Convert.FromBase64String("Base64 encoded DER representation of the OCSP response received from signing service");
    byte[] hash = DigestAlgorithms.Digest(s, hashAlgorithm);
    byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocspResponse, null, CryptoStandard.CMS);

    string messageDigest = Sha256_hash(sh);
    // messageDigest sent to signing service
    byte[] signatureAsByte = StringToByteArray("Hex encoded SignatureValue received from signing service");

    sgn.SetExternalDigest(signatureAsByte, null, "RSA");

    ITSAClient tsaClient = new MyITSAClient();

    return sgn.GetEncodedPKCS7(hash, tsaClient, ocspResponse, null, CryptoStandard.CMS); 
}

public class MyITSAClient : ITSAClient
{
    public int GetTokenSizeEstimate()
    {
        return 0;
    }

    public IDigest GetMessageDigest()
    {
        return new Sha256Digest();
    }

    public byte[] GetTimeStampToken(byte[] imprint)
    {
        string hashedImprint = HexEncode(imprint);
        // Hex encoded Imprint sent to signing service

        return Convert.FromBase64String("Base64 encoded DER representation of TimeStampToken received from signing service");
    }
}
  1. “签名者的身份未知,因为它尚未包括在受信任标识列表中,并且无证书或其父证书是受信任标识”
  2. “签名有时间戳,但无法验证时间戳”

进一步的帮助是非常感谢的再次!

共有1个答案

钱运浩
2023-03-14

问题在于,一方面,您开始使用pdfpkcs7实例构建CMS签名容器

PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);

并且对于计算出的文档摘要哈希检索要

byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);

送去签字。

这不能起作用,因为您的签名字节不是直接对文档进行签名,而是对这些签名属性进行签名(因此,间接地,作为文档散列的文档是签名属性之一)。因此,通过忽略正在构建的CMS容器,您丢弃了实际签名的数据...

此外,您使用的子过滤器adbe_pkcs7_detached保证嵌入式签名是一个完整的CMS签名容器,而不是几个裸签名字节,因此格式也是错误的。

您不必将从服务中获得的裸签名字节按原样注入PDF,而是必须将它们设置为pdfpkcs7实例中的外部摘要,您最初是在该实例中开始构造签名容器的:

sgn.SetExternalDigest(sig, null, ENCRYPTION_ALGO);

(encryption_algo必须是签名算法的加密部分,我假设在您的情况下是“RSA”。)

然后可以检索生成的CMS签名容器:

byte[] encodedSig = sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);

现在,这是要使用MyExternalSignatureContainer注入文档的签名容器:

IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSig);
MakeSignature.SignDeferred(reader, "Signature1", os, external);
    null

这一警告是意料之中的,也是正确的!

签名者的身份不明,因为您的签名服务仅使用演示证书,而不是用于生产的证书:

正如您所看到的,证书是由“GlobalSign非公共HVCA演示”颁发的,由于明显的原因,非公共演示颁发者必须不受信任(除非您出于测试目的手动将其添加到信任存储中)。

一方面,就像上面一样,时间戳证书是一个非公开的演示证书(“DSS非公开演示TSA响应器”)。因此,验证器没有理由信任您的时间戳。

另一方面,虽然在您的时间戳代码中有一个实际的错误,您将哈希算法应用了两次!在myitsaclient类中,您有

public byte[] GetTimeStampToken(byte[] imprint)
{
    string hashedImprint = Sha256_hash(imprint);
    // hashedImprint sent to signing service

    return Convert.FromBase64String("Base64 encoded DER representation of TimeStampToken received from signing service");
}

您的GetTimeStampToken实现的Imprint参数已经散列,因此您必须对这些字节进行十六进制编码,并将它们发送给时间戳。但是应用sha256_hash方法,该方法首先进行哈希,然后十六进制编码这个新哈希。

因此,不应用sha256_hash而只是十六进制编码imprint

 类似资料:
  • 我正在尝试使用远程web服务来演唱pdf,该服务返回一个XML签名,该签名由签名和最终用户证书组成。 我需要使用此签名通过IText签名对pdf进行签名,因为web服务。 所有IText示例都使用消息格式,但我不确定应该如何处理XML签名。 打开临时Pdf并嵌入接收到的签名的代码 从web服务返回的XML签名: 当我将返回的签名与上面的代码一起使用时,签名验证失败,出现“错误遇到时BER解码”。

  • 我可以通过外部签名使用itextpdf库对文档进行签名。 但问题是,最终用户不想发送他的文档,因为它可能包含任何敏感数据。因此,我要求最终用户给出文档哈希,以便与外部服务签署哈希,并将签署后的哈希发回。 但是,问题来了,当他们试图使用itextpdf()用给定的签名散列对文档进行签名时,PDF文档被签名了。但在验证签名时,表明签名是无效的。 因此,问题的发生是因为每次使用(itextpdf库)打开

  • 我对iTextSharp有意见。我有一个带有表单字段的文档,并且我已经为签名生成了字段。当第一个人在文件上签字时,它就会正常工作。Adobe Reader显示有效签名。当我让第二个人在文档上签名时,Adobe Reader显示签名1现在是“未知签名”,签名无效。Adobe reader显示: 此签名中包含的格式或信息有错误(支持信息:SigDict/Contents非法数据)

  • 我有一个应用程序生成一个PDF,需要签名。 我们没有用于签署文档的证书,因为它们在HSM中,而我们可以使用证书的唯一方法是使用WebService。 这是我们的代码,首先,我们得到签名外观,并计算散列 在这一点上,我们得到一个已签名的PDF,但签名无效。Adobe称“文档自签署以来已被更改或损坏”。 我已经通过使用外部服务和iText,PDF签名iText pkcs7多签名和是否可能签署一个PDF

  • 我正在尝试用DSS签署PDF文档,我的问题是我无法在服务器a中计算文档的哈希值,然后在服务器B中签署它。 知道服务器A包含PDF文档,我们在服务器B中检索用于签名的证书 我的问题是如何在不需要证书的情况下计算服务器a中文档的哈希值。然后在服务器B上发送签名? 更新: ******散列的准备和计算******** ******散列签名******** ********PDF错误:*********

  • 我正在编写一个服务,其中我用一个空容器预签名pdf文件,从pdf文件中提取一个字节范围的散列,并将其发送到另一个服务,这将允许用户使用移动电话对散列进行签名。我拿回一个证书,我将注入到预签名pdf文件中的签名容器中。 签名本身起作用,数字签名是有效的,但我只需要更改可见签名本身的文本。我认为这是可能的,因为可见签名实际上与证书本身没有任何关系,所以显示来自证书的名称只是一种方便,特别是在多个签名的