带有签名哈希的itextsharp签名pdf [英] itextsharp signing pdf with signed hash

查看:392
本文介绍了带有签名哈希的itextsharp签名pdf的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过签名服务来签名pdf.此服务需要发送一个十六进制编码的SHA256摘要,并且作为回报,我收到一个十六进制编码的signatureValue.除此之外,我还收到签名证书,中间证书,OCSP响应和TimeStampToken.但是,我已经很难尝试使用signatureValue来对pdf进行签名.

I'm trying to sign a pdf through a signing service. This service requires to send a hex encoded SHA256 digest and in return I receive a hex encoded signatureValue. Besides that I also receive a signing certificate, intermediate certificate, OCSP response, and TimeStampToken. However, I already get stuck trying to sign the pdf with the signatureValue.

我已经阅读了布鲁诺的白皮书,过多地浏览了Internet,并尝试了许多不同的方法,但是签名不断显示为无效.

I have read Bruno's white paper, browsed the internet excessively, and tried many different ways, but the signature keeps coming up as invalid.

我最近的尝试:

首先,准备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);

hash byte [] sh并按​​如下所示转换为字符串

Hash byte[] sh and convert to string as follows

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转换为字节

and send to signing service. The received hex encoded signatureValue I then convert to bytes

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) { }
}

我做错了什么?非常感谢您的帮助.谢谢!

What am I doing wrong? Help is very much appreciated. Thanks!

修改:当前状态

感谢mkl的帮助,并按照Bruno递延的签名示例进行操作,我已经越过了无效的签名消息.显然,我没有从签名服务中收到完整的链,而只是一个中间证书,这导致了无效消息.不幸的是,签名仍然存在缺陷.

Thanks to help from mkl and following Bruno's deferred signing example I've gotten past the invalid signature message. Apparently I don't receive a full chain from the signing service, but just an intermediate certificate, which caused the invalid message. Unfortunately, the signature still has flaws.

我这样构建链:

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

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

In the sign method of MyExternalSignatureContainer I now construct and return the signature container:

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. "The signer's identity is unknown because it has not been included in the list of trusted identities and none or its parent certificates are trusted identities"
  2. "The signature is timestamped, but the timestamp could not be verified"

再次感谢您的进一步帮助!

Further help is very much appreciated again!

推荐答案

我在做什么错了?"

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

"What am I doing wrong?"

The problem is that on one hand you start constructing a CMS signature container using a PdfPKCS7 instance

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

并为计算出的文档摘要hash检索要签名的属性

and for the calculated document digest hash retrieve the signed attributes to be

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

发送给他们进行签名.

到目前为止一切都很好.

So far so good.

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

But then you ignore the CMS container you started constructing but instead inject the naked signature bytes you got from your service into the PDF.

这不能工作,因为您的签名字节不会直接对文档进行签名,而是会对这些已签名的属性进行签名(因此,由于文档哈希是已签名的属性之一,因此会间接地对文档进行签名).因此,通过忽略正在构建的CMS容器,您删除了实际签名的数据...

This cannot work as your signature bytes don't sign the document directly but instead they sign these signed attributes (and, therefore, indirectly the document as the document hash is one of the signed attributes). Thus, by ignoring the CMS container under construction you dropped the actually signed data...

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

Furthermore, the subfilter ADBE_PKCS7_DETACHED you use promises that the embedded signature is a full CMS signature container, not a few naked signature bytes, so the format also is wrong.

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

Instead of injecting the naked signature bytes you got from your service into the PDF as is, you have to set them as external digest in the PdfPKCS7 instance in which you originally started constructing the signature container:

sgn.SetExternalDigest(sig, null, ENCRYPTION_ALGO);

(ENCRYPTION_ALGO必须是签名算法的加密部分,在您的情况下,我认为是"RSA".)

(ENCRYPTION_ALGO must be the encryption part of the signature algorithm, I assume in your case "RSA".)

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

and then you can retrieve the generated CMS signature container:

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

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

Now this is the signature container to inject into the document using MyExternalSignatureContainer:

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

其余问题

已更正您的代码Adobe Reader仍会警告您的签名:

Remaining issues

Having corrected your code Adobe Reader still warns about your signatures:

  1. 签名者的身份未知,因为它没有包含在受信任身份列表中,并且没有一个或其父证书是受信任身份"

此警告是可以预期并正确的!

This warning is to be expected and correct!

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

The signer's identity is unknown because your signature service uses merely a demo certificate, not a certificate for production use:

如您所见,证书是由"GlobalSign非公开HVCA演示"和非公开演示颁发者出于明显原因发出的,必须不受信任(除非您手动将它们添加到您的信任库中以进行测试).

As you see the certificate is issued by "GlobalSign Non-Public HVCA Demo", and non-public demo issuers for obvious reasons must not be trusted (unless you manually add them to your trust store for testing purposes).

  1. 签名带有时间戳,但无法验证时间戳"

Adob​​e不批准您的时间戳有两个原因:

There are two reasons why Adobe does not approve of your timestamp:

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

On one hand, just like above, the timestamp certificate is a non-public, demo certificate ("DSS Non-Public Demo TSA Responder"). Thus, there is no reason for the verifier to trust your timestamp.

但是,另一方面,您的时间戳记代码中存在实际错误,您需要两次应用哈希算法!在您的MyITSAClient课堂上,

On the other hand, though, there is an actual error in your timestamp'ing code, you apply the hashing algorithm twice! In your MyITSAClient class you have

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方法,该方法首先进行哈希处理,然后对此新哈希进行十六进制编码.

The imprint parameter of your GetTimeStampToken implementation is already hashed, so you have to hex encode these bytes and send them for timestamp'ing. But you apply your method Sha256_hash which first hashes and then hex encodes this new hash.

因此,不是应用Sha256_hash而是仅对imprint进行十六进制编码!

Thus, instead of applying Sha256_hash merely hex encode the imprint!

这篇关于带有签名哈希的itextsharp签名pdf的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆