代码之家  ›  专栏  ›  技术社区  ›  emre nevayeshirazi

使用ITextSharp和XML签名对Pdf进行签名

  •  0
  • emre nevayeshirazi  · 技术社区  · 8 年前

    我试图使用远程web服务来唱pdf,该服务返回一个XML签名,该签名由 PKCS#1

    deferred asynchronously .

    PKCS#7

    添加空签名字段并获取Pdf可签名字节的代码

    public static string GetBytesToSign(string unsignedPdf, string tempPdf, string signatureFieldName)
    {
        if (File.Exists(tempPdf))
            File.Delete(tempPdf);
    
        using (PdfReader reader = new PdfReader(unsignedPdf))
        {
            using (FileStream os = File.OpenWrite(tempPdf))
            {
                PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
                PdfSignatureAppearance appearance = stamper.SignatureAppearance;
                appearance.SetVisibleSignature(new Rectangle(36, 748, 250, 400), 1, signatureFieldName);
    
                IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    
                MakeSignature.SignExternalContainer(appearance, external, 8192);
    
                byte[] array = SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
    
                return Convert.ToBase64String(array);
            }
        }
    }
    

    打开临时Pdf并嵌入接收到的签名的代码

    public static void EmbedSignature(string tempPdf, string signedPdf, string signatureFieldName, string signature)
    {
        byte[] signedBytes = Convert.FromBase64String(signature);
    
        using (PdfReader reader = new PdfReader(tempPdf))
        {
            using (FileStream os = File.OpenWrite(signedPdf))
            {
                IExternalSignatureContainer external = new MyExternalSignatureContainer(signedBytes);
                MakeSignature.SignDeferred(reader, signatureFieldName, os, external);
            }
        }
    }
    

    <SignatureValue Id="Signature-xxx-SIG">
    RMFbYIigsrjYQEc4PCoHMMg8vwz/hYrCjj0IR+835BZZ/TsTMHZhMVnu2KQZi1UL
    dWMeuhTxagBmjdBSzGiy1OEdH5r0FM77Of4Zz99o/aAhYqr3qpOETGgNn9GHlphL
    5FSPYbNsq2rDHA8FsNdqNdb6qJUZYsfYvuhJaUMstBXeL/dLIT46t7ySCQ7CGjE5
    mpD1AG8M+TVWf4ut5tucZuZV9PMQB3tyoarQD5RoUv872RzB5IorcIhLnf+O6E6o
    EF0HuGitRhN9bbPgdLaUma5MNjKCaeQTpCXp3KXwi8VoQGd5fpUBZbAKtMpKeCts
    RAOk1O4uk/4poic4IGPhDw==
    </SignatureValue>
    <KeyInfo>
    
    <KeyValue>
    <RSAKeyValue>
    
    <Modulus>
    AI5T0zOBBD4i7Cb1v8wDL0+By8i1h2U0HDFQ73/iNBkM9Gd7mUU0i2A9wJTeiktS
    IeBU/JLzqVp7vK857dZlrlT9qiH2cNQufh+q2MpNk7wtPcDACVedVkBUNkIgoXBy
    g4cGmAYoWBsD2zDXiZXukStjUWws+/xCU0hADIelFONr141zRHindfq86QrDTC7q
    bHBtDT7dJckWzaDPHf3Xlej+U/A1x/8kd504VZaFQfAPYBOgGPY918G1HjBtlajR
    nyrl10LuV708IHZtAmKmEfdZOeq//9OGZZLh2nJ86b5Fa6XPFhxzLx/ugBS/8GHt
    iVYeJOlfHXRl5w2k2Vv/ssU=
    </Modulus>
    
    <Exponent>AQAB</Exponent>
    
    </RSAKeyValue>
    </KeyValue>
    
    <X509Data>
    <X509Certificate>
    MIIGpTCCBY2gAwIBAgIRAJvlsG1D+P++OcU8W8D8FnkwDQYJKoZIhvcNAQELBQAw
    bzELMAkGA1UEBhMCVFIxKDAmBgNVBAoMH0VsZWt0cm9uaWsgQmlsZ2kgR3V2ZW5s
    aWdpIEEuUy4xNjA0BgNVBAMMLVRFU1QgVHVya2NlbGwgTW9iaWwgTkVTIEhpem1l
    dCBTYWdsYXlpY2lzaSBTMjAeFw0xNzA4MjUxMjQ3MjFaFw0xODA4MjUxMjQ3MjFa
    MEoxCzAJBgNVBAYTAlRSMREwDwYDVQQKDAhGaXJlIExMVDEUMBIGA1UEBRMLNzY1
    NDM0NTY3NjUxEjAQBgNVBAMMCU1lcnQgSW5hbjCCASIwDQYJKoZIhvcNAQEBBQAD
    ggEPADCCAQoCggEBAI5T0zOBBD4i7Cb1v8wDL0+By8i1h2U0HDFQ73/iNBkM9Gd7
    mUU0i2A9wJTeiktSIeBU/JLzqVp7vK857dZlrlT9qiH2cNQufh+q2MpNk7wtPcDA
    CVedVkBUNkIgoXByg4cGmAYoWBsD2zDXiZXukStjUWws+/xCU0hADIelFONr141z
    RHindfq86QrDTC7qbHBtDT7dJckWzaDPHf3Xlej+U/A1x/8kd504VZaFQfAPYBOg
    GPY918G1HjBtlajRnyrl10LuV708IHZtAmKmEfdZOeq//9OGZZLh2nJ86b5Fa6XP
    FhxzLx/ugBS/8GHtiVYeJOlfHXRl5w2k2Vv/ssUCAwEAAaOCA18wggNbMEIGCCsG
    AQUFBwEBBDYwNDAyBggrBgEFBQcwAYYmaHR0cDovL3Rlc3RvY3NwMi5lLWd1dmVu
    LmNvbS9vY3NwLnh1ZGEwHwYDVR0jBBgwFoAUT9gSazAfQrnZruIq9+dJFZ7E9OUw
    ggFyBgNVHSAEggFpMIIBZTCBsQYGYIYYAwABMIGmMDYGCCsGAQUFBwIBFipodHRw
    Oi8vd3d3LmUtZ3V2ZW4uY29tL2RvY3VtZW50cy9ORVNVRS5wZGYwbAYIKwYBBQUH
    AgIwYBpeQnUgc2VydGlmaWthLCA1MDcwIHNhecSxbMSxIEVsZWt0cm9uaWsgxLBt
    emEgS2FudW51bmEgZ8O2cmUgbml0ZWxpa2xpIGVsZWt0cm9uaWsgc2VydGlmaWth
    ZMSxcjCBrgYJYIYYAwABAQEDMIGgMDcGCCsGAQUFBwIBFitodHRwOi8vd3d3LmUt
    Z3V2ZW4uY29tL2RvY3VtZW50cy9NS05FU0kucGRmMGUGCCsGAQUFBwICMFkaV0J1
    IHNlcnRpZmlrYSwgTUtORVNJIGthcHNhbcSxbmRhIHlhecSxbmxhbm3EscWfIGJp
    ciBuaXRlbGlrbGkgZWxla3Ryb25payBzZXJ0aWZpa2FkxLFyLjBdBgNVHR8EVjBU
    MFKgUKBOhkxodHRwOi8vdGVzdHNpbC5lLWd1dmVuLmNvbS9FbGVrdHJvbmlrQmls
    Z2lHdXZlbmxpZ2lBU01LTkVTSS1TMi9MYXRlc3RDUkwuY3JsMA4GA1UdDwEB/wQE
    AwIGwDCBgwYIKwYBBQUHAQMEdzB1MAgGBgQAjkYBATBpBgtghhgBPQABp04BAQxa
    QnUgc2VydGlmaWthLCA1MDcwIHNheWlsaSBFbGVrdHJvbmlrIEltemEgS2FudW51
    bmEgZ8O2cmUgbml0ZWxpa2xpIGVsZWt0cm9uaWsgc2VydGlmaWthZGlyMFAGA1Ud
    CQRJMEcwFAYIKwYBBQUHCQIxCAQGQW5rYXJhMB0GCCsGAQUFBwkBMREYDzE5Nzgx
    MjMxMjAwMDAwWjAQBggrBgEFBQcJBDEEBAJUUjAYBgNVHREEETAPgQ1maXJlQGZp
    cmUuY29tMB0GA1UdDgQWBBTYUEJX62lhEzkZLSrTdSIdE9n8KzANBgkqhkiG9w0B
    AQsFAAOCAQEAV/MY/Tp7n7eG7/tWJW+XlDgIPQK1nAFgvZHm+DodDJ8Bn7CPWmv8
    EBmcNxx5PYMoG7y54E6+KzVyjdPBu5o0YtWyKlLKnH1St+EtptOmt29VFAODjalb
    Q0An9361DxIDRHbMnQOaJiRTwMlIhED5VDa3OTUhBr7bNlVkp3N5Vs2RuoSdNypR
    fq4D/cCpakVugsFyPk4zhWkGhTS5DlL7c5KYqCnU4ugpC33IRJGB05PSdjICeX7Y
    N0oAdhzF+5QZEwePjgrDb+ROVpXYaGVMWICA8N2tOXuqdDYoGAzj1YemnPqrSn8a
    2iwqbWnFujwep5w5C/TCFVaQWa4NGncMOw==
    </X509Certificate>
    </X509Data>
    
    </KeyInfo>
    

    当我使用返回的 PKCS#1 .

    BlankSignatureContainer ADBE_PKCS7_DETACHED PKCS#1 ? 或者我应该/可以创建 PKCS#7 来自的消息 和用户证书,并使用此格式代替?

    我还可以在请求签名之前检索最终用户证书。

    <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
       <soap:Body>
          <ns1:MSS_ProfileQueryResponse xmlns:ns1="/mobilesignature/validation/soap">
             <MSS_ProfileResp MinorVersion="1" MajorVersion="1" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:ns3="http://www.w3.org/2001/04/xmlenc#" xmlns:ns4="http://www.w3.org/2003/05/soap-envelope" xmlns:ns5="http://uri.etsi.org/TS102204/v1.1.2#">
                <ns5:AP_Info Instant="2017-09-16T04:54:43.260Z" AP_PWD="turkcell123" AP_TransID="_1371012883260" AP_ID="http://turkcell.com.tr"/>
                <ns5:MSSP_Info Instant="2017-09-16T13:33:36.316+02:00">
                   <ns5:MSSP_ID/>
                </ns5:MSSP_Info>
                <ns5:SignatureProfile>
                   <ns5:mssURI>http://MobileSignature/profile2</ns5:mssURI>
                   <ns5:CertIssuerDN> Mobil NES Hizmet Saglayicisi S2</ns5:CertIssuerDN>
                   <ns5:CertSerialNumber>71408272140747005781907792964830346324</ns5:CertSerialNumber>
                   <ns5:CertHash>
                      <ns5:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                      <ns5:DigestValue>e1yKlaPIU95phxxYvrUsmSQDpKqKU60/b+a8BLw1wNM=</ns5:DigestValue>
                   </ns5:CertHash>
                   <ns5:msspUrl>http://localhost</ns5:msspUrl>
                   <ns5:certIssuerDN-DER>MG8xCzAJBgNVBAYTAlRSMSgwJgYDVQQKDB9FbGVrdHJvbmlrIEJpbGdpIEd1dmVubGlnaSBBLlMuMTYwNAYDVQQDDC1URVNUIFR1cmtjZWxsIE1vYmlsIE5FUyBIaXptZXQgU2FnbGF5aWNpc2kgUzI=</ns5:certIssuerDN-DER>
                </ns5:SignatureProfile>
                <ns5:Status>
                   <ns5:StatusCode Value="100"/>
                   <ns5:StatusMessage>REQUEST_OK</ns5:StatusMessage>
                </ns5:Status>
             </MSS_ProfileResp>
          </ns1:MSS_ProfileQueryResponse>
       </soap:Body>
    </soap:Envelope>
    

    public static byte[] GetBytesToSign(string unsignedPdf, string tempPdf, string signatureFieldName, byte[] x509Signature)
    {
        if (File.Exists(tempPdf))
            File.Delete(tempPdf);
    
        var chain = new List<Org.BouncyCastle.X509.X509Certificate>
        {
            Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(new X509Certificate2(x509Signature))
        };
    
        Org.BouncyCastle.X509.X509Certificate certificate = chain.ElementAt(0);
    
        using (PdfReader reader = new PdfReader(unsignedPdf))
        {
            using (FileStream os = File.OpenWrite(tempPdf))
            {
                PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
    
                PdfSignatureAppearance appearance = stamper.SignatureAppearance;
    
                appearance.SetVisibleSignature(new Rectangle(36, 748, 250, 400), 1, signatureFieldName);
    
                appearance.Certificate = chain[0];
    
                IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    
                MakeSignature.SignExternalContainer(appearance, external, 8192);
    
                Stream data = appearance.GetRangeStream();
    
                byte[] hash = DigestAlgorithms.Digest(data, "SHA256");
    
                var signature = new PdfPKCS7(null, chain, "SHA256", false);
    
                byte[] signatureHash = signature.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
    
                _signature = signature;
                _apperance = appearance;
                _hash = hash;
                _signatureHash = signatureHash;
    
                return signatureHash;
            }
        }
    }
    
    public static void EmbedSignature(string tempPdf, string signedPdf, string signatureFieldName, byte[] signedBytes)
    {
        using (PdfReader reader = new PdfReader(tempPdf))
        {
            using (FileStream os = File.OpenWrite(signedPdf))
            {
                _signature.SetExternalDigest(signedBytes, null, "RSA");
    
                byte[] encodedSignature = _signature.GetEncodedPKCS7(_hash, null, null, null, CryptoStandard.CMS);
    
                IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);
    
                MakeSignature.SignDeferred(reader, signatureFieldName, os, external);
            }
        }
    }
    
    1 回复  |  直到 8 年前
        1
  •  1
  •   Community Mohan Dere    6 年前

    (这个答案总结了在评论和编辑问题的过程中工作代码的演变。)

    PDF格式(通常支持的ISO 32000-1:2008)指定了使用裸PKCS#1签名或完整PKCS#7/CMS签名容器的嵌入式签名,特别是第12.8.3节“签名互操作性”

    • 第12.8.3.2节“PKCS#1签名”和
    • 第12.8.3.3节“ISO 32000中使用的PKCS#7签名”。

    事先知道证书

    如果这就是web服务的全部功能,那么就会有一个问题:在嵌入裸PKCS#1签名和构造PKCS#7签名容器时,都需要知道最终实体证书 之前

    (严格来说,非常基本的CMS签名容器不需要这样做,但所有签名配置文件都需要认真对待。)

    “可以访问另一个服务”该服务使应用程序提供商能够请求最终用户证书序列号和证书哈希,可以用于构造要签名的数据

    代码

    OP找到了iText/Java代码,用于实现基于上述签名服务(编辑2)使用嵌入式PKCS#7/CMS签名容器对PDF进行签名的功能。

    然而,要签名的结果哈希值是77字节。web服务不包括32字节的SHA256哈希数据。

    然而,事实证明,这77个字节不是签名属性的散列:

    public static byte[] GetBytesToSign(string unsignedPdf, string tempPdf, string signatureFieldName, byte[] x509Signature)
    {
        [...]
        byte[] signatureHash = signature.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
        [...]
        return signatureHash;
    }
    

    推荐文章