You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用iTextSharp通过SHA1签名PDF失败,USB签名未嵌入求助

PDF数字签名嵌入失败问题排查与修复

我仔细检查了你的代码,发现几个关键问题导致签名无法正确嵌入PDF,即使程序运行无报错。下面是具体的问题分析和修复方案:


1. 证书私钥未正确加载

在selectCert方法中,你创建了带密码的newCert但没有把它赋值给最终返回的cert变量。从系统证书存储中获取的证书通常不包含可直接使用的私钥,必须通过带密码重新加载证书才能获取可用的私钥。这会导致后续签名时使用的私钥无效,生成的签名自然无法被PDF验证。

修复方式:
把var newCert = new X509Certificate2(certificate.RawData, "mypassword");改成certificate = new X509Certificate2(certificate.RawData, "mypassword");,确保返回的证书包含可用的私钥。

2. 待签名哈希计算错误

你直接对appearance.GetRangeStream()计算SHA1哈希,但在调用MakeSignature.SignExternalContainer后,PdfStamper已经完成了临时PDF的生成,此时appearance.GetRangeStream返回的流已经不是正确的待签名范围了。正确的做法是从生成的临时PDF中读取指定的签名字节范围,再计算哈希。

修复方式:
替换原来的哈希计算代码,改为从临时文件中提取正确的待签字节:

// 替换原来的array = SHA1.Create().ComputeHash(appearance.GetRangeStream());
using (var reader = new PdfReader(tempPdf))
{
    var sigDic = reader.AcroFields.GetSignatureDictionary(signatureFieldName);
    var range = sigDic.GetAsArray(PdfName.BYTERANGE);
    using (var fs = new FileStream(tempPdf, FileMode.Open, FileAccess.Read))
    {
        var toSign = new byte[range[2].AsInt()];
        fs.Position = range[1].AsInt();
        fs.Read(toSign, 0, toSign.Length);
        array = SHA1.Create().ComputeHash(toSign);
    }
}

3. 自定义签名容器未配置必要属性

你的MyExternalSignatureContainer类的ModifySigningDictionary方法是空的,这会导致PDF文件无法识别签名的算法和格式,即使嵌入了签名字节,也不会被认定为有效签名。

修复方式:
完善ModifySigningDictionary方法,添加签名所需的关键配置:

public void ModifySigningDictionary(PdfDictionary signDic)
{
    signDic.Put(PdfName.FILTER, PdfName.ADOBE_PPKLITE);
    signDic.Put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
    signDic.Put(PdfName.DIGESTALGORITHM, new PdfName("SHA1"));
}

4. 手动释放资源导致的流异常

你在using语句块内手动调用stamper.Dispose()、stamper.Close()等方法,而using语句本身会自动处理资源释放,手动调用可能导致流提前关闭,破坏临时PDF文件的完整性。

修复方式:
删除所有手动调用的Dispose()和Close()方法,让using自动管理资源生命周期。


完整修复后的代码

private void Form1_Load(object sender, EventArgs e) 
{ 
    string UnsingnedPdf = @"E:\PrintContractAdviceNote.pdf"; 
    string TempPdf = @"E:\PrintContractAdviceNote_TMP.pdf"; 
    string singnedPdf = @"E:\PrintContractAdviceNote_Sign.pdf"; 
    PDFSigning cl = new PDFSigning(); 
    cl.GetBytesToSign(UnsingnedPdf, TempPdf, singnedPdf, "CHUKY1", 3, "BIG BEAR"); 
} 

public static X509Certificate2 selectCert() 
{ 
    X509Certificate2 certificate = null; 
    var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); 
    try 
    { 
        store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); 
        if (store.Certificates.Count == 1) 
        { 
            certificate = store.Certificates[0]; 
        } 
        else if (store.Certificates.Count > 0) 
        { 
            try 
            { 
                var certificates = X509Certificate2UI.SelectFromCollection(store.Certificates, "Digital Certificates", "Select a certificate from the following list:", X509SelectionFlag.SingleSelection); 
                if (certificates.Count == 1) 
                { 
                    certificate = certificates[0]; 
                    // 修复:将带密码的证书赋值给返回变量
                    certificate = new X509Certificate2(certificate.RawData, "mypassword"); 
                } 
                else 
                { 
                    throw new ArgumentException("Please select a certificate to publish PnL to Flash"); 
                } 
            } 
            catch (Exception ex) 
            { 
                Console.WriteLine($"Certificate selection error: {ex.Message}");
            } 
        } 
        else 
        { 
            throw new ArgumentException("There is no certificate available to publish PnL to flash, please contact support."); 
        } 
    } 
    finally 
    { 
        store.Close(); 
    } 
    return certificate; 
} 

public class IpInfo 
{ 
    public string Country { get; set; } 
} 

public byte[] GetBytesToSign(string unsignedPdf, string tempPdf, string signedPdf, string signatureFieldName, Int16 iType, string SignName) 
{ 
    if (File.Exists(tempPdf)) File.Delete(tempPdf); 
    if (File.Exists(signedPdf)) File.Delete(signedPdf); 

    byte[] array = null; 

    // 使用using自动管理资源,无需手动Close/Dispose
    using (var os = File.OpenWrite(tempPdf)) 
    using (var reader = new PdfReader(unsignedPdf)) 
    using (var stamper = PdfStamper.CreateSignature(reader, os, '\0')) 
    { 
        PdfSignatureAppearance appearance = stamper.SignatureAppearance; 
        appearance.Contact = "www.marico.com"; 
        appearance.SignDate = DateTime.Now; 
        appearance.CertificationLevel = PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS; 
        appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION; 
        appearance.Acro6Layers = true; 
        appearance.Layer2Text = $"Signed by:\n {SignName}\nDate :\n {DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()}"; 

        Image img = Image.GetInstance(@"E:\Tài liệu\Marico\HINH ANH\talent.png"); 
        if (iType == 3) 
        { 
            appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(15, 50, 235, 95), reader.NumberOfPages, signatureFieldName); 
        } 
        else 
        { 
            appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(350, 50, 570, 95), reader.NumberOfPages, signatureFieldName); 
        } 
        appearance.SignatureGraphic = img; 

        IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED); 
        MakeSignature.SignExternalContainer(appearance, external, 8192); 
    } 

    // 修复:从临时PDF中读取正确的待签字节范围并计算哈希
    using (var reader = new PdfReader(tempPdf))
    {
        var sigDic = reader.AcroFields.GetSignatureDictionary(signatureFieldName);
        var range = sigDic.GetAsArray(PdfName.BYTERANGE);
        using (var fs = new FileStream(tempPdf, FileMode.Open, FileAccess.Read))
        {
            var toSign = new byte[range[2].AsInt()];
            fs.Position = range[1].AsInt();
            fs.Read(toSign, 0, toSign.Length);
            array = SHA1.Create().ComputeHash(toSign);
        }
    }

    X509Certificate2 cert = selectCert(); 
    if (cert?.PrivateKey == null)
    {
        throw new InvalidOperationException("Failed to retrieve valid private key from selected certificate");
    }
    RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(cert.PrivateKey); 
    formatter.SetHashAlgorithm("SHA1"); 
    byte[] signature = formatter.CreateSignature(array); 

    EmbedSignature(tempPdf, signedPdf, signatureFieldName, signature); 

    return array; 
} 

public static bool IsFileLocked(string pathfile) 
{ 
    try 
    { 
        using (FileStream fs = new FileStream(pathfile, FileMode.Open)) 
        {
            // using自动处理流关闭
        } 
        return false; 
    } 
    catch (IOException) 
    { 
        return true; 
    } 
} 

private static void SetSigPosition(PdfSignatureAppearance sigAppearance, int oldSigCount, string signatureFieldName, int numpage = 1) 
{ 
    float llx = (200 + 20) * (oldSigCount % 5) + 20, lly = (25 + 20) * (oldSigCount / 5) + 5, urx = llx + 200, ury = lly + 45; 
} 

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

private class MyExternalSignatureContainer : IExternalSignatureContainer 
{ 
    private readonly byte[] signedBytes; 

    public MyExternalSignatureContainer(byte[] signedBytes) 
    { 
        this.signedBytes = signedBytes; 
    } 

    public byte[] Sign(Stream data) 
    { 
        return signedBytes; 
    } 

    // 修复:添加签名字典配置
    public void ModifySigningDictionary(PdfDictionary signDic) 
    { 
        signDic.Put(PdfName.FILTER, PdfName.ADOBE_PPKLITE); 
        signDic.Put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED); 
        signDic.Put(PdfName.DIGESTALGORITHM, new PdfName("SHA1")); 
    } 
}

内容的提问来源于stack exchange,提问作者Vinh Lê Quốc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:38:54