带签名哈希的itextsharp签名pdf

带签名哈希的itextsharp签名pdf,pdf,hash,itext,signature,Pdf,Hash,Itext,Signature,我正在尝试通过签名服务签署pdf。这个服务需要发送一个十六进制编码的SHA256摘要,作为回报,我会收到一个十六进制编码的signatureValue。除此之外,我还收到签名证书、中间证书、OCSP响应和时间戳令牌。然而,我已经在尝试用signatureValue为pdf签名时遇到了麻烦 我读过布鲁诺的白皮书,过度浏览了互联网,尝试了许多不同的方法,但签名一直被认为是无效的 我的最新尝试: 首先,准备pdf PdfReader reader = new PdfReader(src); FileS

我正在尝试通过签名服务签署pdf。这个服务需要发送一个十六进制编码的SHA256摘要,作为回报,我会收到一个十六进制编码的signatureValue。除此之外,我还收到签名证书、中间证书、OCSP响应和时间戳令牌。然而,我已经在尝试用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
}; 
仍然会收到以下消息:

  • “签名者的身份未知,因为它未包含在受信任的身份列表中,且无或其父身份 证书是受信任的身份”
  • 签名有时间戳,但无法验证时间戳
  • 再次感谢您的进一步帮助

    “我做错了什么?” 问题是,一方面,您开始使用
    PdfPKCS7
    实例构建CMS签名容器

    PdfPKCS7 sgn=新的PdfPKCS7(null、chain、hashAlgorithm、false);
    
    对于计算的文档摘要
    hash
    检索要删除的签名属性

    byte[]sh=sgn.getAuthenticatedAttributeBytes(哈希、null、null、CryptoStandard.CMS);
    
    发送它们以供签署

    到目前为止还不错

    但随后您忽略了您开始构建的CMS容器,而是将您从服务中获得的裸签名字节注入到PDF中

    这无法工作,因为签名字节不直接对文档进行签名,而是对这些已签名属性进行签名(因此,间接地将文档作为已签名属性之一)。因此,通过忽略正在构造的CMS容器,您删除了实际签名的数据

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

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

    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");
        }
    }
    
    sgn.SetExternalDigest(sig,null,加密算法);
    
    ENCRYPTION\u ALGO
    必须是签名算法的加密部分,我假设在您的案例中
    “RSA”

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

    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");
        }
    }
    
    byte[]encodedSig=sgn.GetEncodedPKCS7(散列、null、null、null、CryptoStandard.CMS);
    
    现在,这是要使用MyExternalSignatureContainer将签名容器注入文档的签名容器:

    IExternalSignatureContainer external=新的MyExternalSignatureContainer(encodedSig);
    MakeSignature.SignDeferred(读卡器,“Signature1”,操作系统,外部);
    
    遗留问题 更正代码后,Adobe Reader仍会对您的签名发出警告:

  • “签名者的身份未知,因为它未包含在受信任的身份列表中,并且没有或其父证书是受信任的身份”
  • 此警告是预期的,并且是正确的

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

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

  • 签名有时间戳,但无法验证时间戳
  • Adobe不批准您的时间戳有两个原因:

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

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

    public字节[]GetTimeStampToken(字节[]压印)
    {
    字符串hashedImprint=Sha256_散列(压印);
    //hashedImprint已发送到签名服务
    返回Convert.FromBase64String(“从签名服务接收的TimeStampToken的Base64编码DER表示”);
    }
    
    您的
    GetTimeStampToken
    实现的
    imprint
    参数已经过哈希,因此您必须对这些字节进行十六进制编码并发送它们以进行时间戳。但是您可以应用您的方法
    Sha256_hash
    ,它首先对这个新的hash进行散列,然后对其进行十六进制编码


    因此,不应用Sha256_散列而只对压印进行十六进制编码

    “我做错了什么?”-请先说出哪里出了错。最好共享一个由您的代码签名的示例pdf进行分析。Adobe表示签名无效。也看不到证书的详细信息。这里是示例pdf的链接:感谢您的关注。感谢您的明确回答。不幸的是,我还没有完全掌握它。我应该准备带有bla的pdf