我正在测试iText 7.1.2.0库,以便在C#项目中使用数字证书或智能卡(X509Certificate2)签署pdf文件。但是当我尝试创建IExternalSignature时,我遇到了这个错误。
根据找到的留档(这里,这里和这里),实现这个过程的方法是使用BouncyCastle库,它允许从数字证书中提取主键,但是,它给我一个错误,我找不到另一种方法来做到这一点。在留档(这里)中,它们是从. pfx文件创建的,但对于这种情况,我需要直接从读卡器中的证书中获取主键。在以前版本的iText中,它允许使用以下命令进行创建:
IExternalSignature externalSignature = new X509Certificate2Signature(Certificate, "SHA-1");
但在版本7中,它不再可用,在文档中我看不到如何实现它。
有人使用了iText 7,并且能够使用知道创建IExternalSignature的正确方法的X509证书2进行签名?
这是我正在使用的代码:
public void SignPDF(string source, string target, X509Certificate2 certificate, string reason, string location, bool addVisibleSign, bool addTimeStamp, string strTSA, int qtySigns, int pageNumber)
{
try
{
Org.BouncyCastle.X509.X509Certificate vert = Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(certificate);
X509CertificateParser objCP = new X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] objChain = new Org.BouncyCastle.X509.X509Certificate[] { objCP.ReadCertificate(certificate.RawData) };
IList<ICrlClient> crlList = new List<ICrlClient>();
crlList.Add(new CrlClientOnline(objChain));
PdfReader objReader = new PdfReader(source);
PdfSigner objStamper = new PdfSigner(objReader, new FileStream(target, FileMode.Create), false);
ITSAClient tsaClient = null;
IOcspClient ocspClient = null;
if (addTimeStamp)
{
OCSPVerifier ocspVerifier = new OCSPVerifier(null, null);
ocspClient = new OcspClientBouncyCastle(ocspVerifier);
tsaClient = new TSAClientBouncyCastle(strTSA);
}
PdfSignatureAppearance signatureAppearance = objStamper.GetSignatureAppearance();
signatureAppearance.SetReason(reason);
signatureAppearance.SetLocation(location);
signatureAppearance.SetPageNumber(pageNumber);
signatureAppearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION);
if (addVisibleSign && qtySigns == 1)
signatureAppearance.SetPageRect(new iText.Kernel.Geom.Rectangle(36, 20, 144, 53)).SetPageNumber(pageNumber);
else if (addVisibleSign && qtySigns == 2)
signatureAppearance.SetPageRect(new iText.Kernel.Geom.Rectangle(160, 20, 268, 53)).SetPageNumber(pageNumber);
else if (addVisibleSign && qtySigns == 3)
signatureAppearance.SetPageRect(new iText.Kernel.Geom.Rectangle(284, 20, 392, 53)).SetPageNumber(pageNumber);
else if (addVisibleSign && qtySigns == 4)
signatureAppearance.SetPageRect(new iText.Kernel.Geom.Rectangle(408, 20, 516, 53)).SetPageNumber(pageNumber);
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(certificate.PrivateKey).Private;
IExternalSignature externalSignature = new PrivateKeySignature(pk, "SHA-1");
objStamper.SignDetached(externalSignature, objChain, crlList, ocspClient, tsaClient, 0, PdfSigner.CryptoStandard.CMS);
if (objReader != null)
{
objReader.Close();
}
}
catch (Exception ex)
{
result.error = true;
result.errorMessage += "Error: " + ex.Message;
}
}
谢谢!
证书。根据我的经验,在处理非RSA密钥对时,PrivateKey不可靠。例如,具有ECDsa密钥对的证书在调用
时将抛出
,不知道为什么(其实这里有原因,也可能有用)。所以我实现了一个(小janky)解决方案来检索私钥,并用它创建一个NotSupportedException(“不支持证书密钥算法”)
。PrivateKeyIExternalSignature
。由于代码不知道证书具有什么类型的密钥,而且我也懒得处理其他属性来查找,因此我编写了一个循环,尝试进行通用、RSA、DSA和ECDsa密钥提取。泛型也适用于RSA,但由于X509Certificate2
有一个GetRSAPrivateKey()
方法,我为它做了一个切换案例以防万一。
#nullable enable
private (IExternalSignature? signature, IDisposable? key) ExtractPrivateKey(X509Certificate2 certificate)
{
IExternalSignature? signature = null;
bool foundPk = false;
int attempt = 0;
IDisposable? key = null;
while (foundPk is false)
{
try
{
switch (attempt)
{
case 0:
AsymmetricAlgorithm? pk = certificate.PrivateKey; //throws exception when certificate has an ECDsa key
signature = new PrivateKeySignature(DotNetUtilities.GetKeyPair(pk).Private, _digestAlgorithm);
key = pk;
break;
case 1:
RSA rsa = certificate.GetRSAPrivateKey() ?? throw new NotSupportedException("RSA private key is null on the certificate.");
signature = new PrivateKeySignature(DotNetUtilities.GetRsaKeyPair(rsa).Private, _digestAlgorithm);
key = rsa;
break;
case 2:
DSA dsa = certificate.GetDSAPrivateKey() ?? throw new NotSupportedException("Dsa private key is null on the certificate.");
signature = new PrivateKeySignature(DotNetUtilities.GetDsaKeyPair(dsa).Private, _digestAlgorithm);
key = dsa;
break;
case 3:
ECDsa ecdsa = certificate.GetECDsaPrivateKey() ?? throw new NotSupportedException("ECDsa private key is null on the certificate.");
signature = new EcdsaSignature(ecdsa, _digestAlgorithm);
key = ecdsa;
break;
}
foundPk = true;
}
catch (Exception e)
{
_logger.LogDebug(e, $"Private key extraction ran into an exception. Attempt (zero-based): {attempt}");
}
attempt++;
}
if (signature == null)
{
throw new NotSupportedException(
$"The private key of the certificate could not be retrieved for signing. {JsonConvert.SerializeObject(certificate.SubjectName)}");
}
return (signature, key);
}
正如您所见,iText 7有一个方便的实用程序类,其中包含许多从Java版本移植的util函数,但由于某些原因,这些函数只有很多,而不是全部。它不支持ECDsa密钥对,所以我必须实现我自己的版本。为了完整起见,该课程包含在下面。
#nullable enable
using System;
using System.Security.Cryptography;
using iText.Signatures;
public class EcdsaSignature : IExternalSignature
{
private readonly string _encryptionAlgorithm;
private readonly string _hashAlgorithm;
private readonly ECDsa _pk;
private const string EcdsaEncryptionAlgorithm = "ECDSA";
public EcdsaSignature(ECDsa? pk, string hashAlgorithm)
{
_pk = pk ?? throw new ArgumentNullException(nameof(pk), "ECDSA private key cannot be null.");
_hashAlgorithm = DigestAlgorithms.GetDigest(DigestAlgorithms.GetAllowedDigest(hashAlgorithm));
_encryptionAlgorithm = EcdsaEncryptionAlgorithm;
}
/// <summary>
/// <inheritDoc />
/// </summary>
public virtual string GetEncryptionAlgorithm()
{
return _encryptionAlgorithm;
}
/// <summary>
/// <inheritDoc />
/// </summary>
public virtual string GetHashAlgorithm()
{
return _hashAlgorithm;
}
/// <summary>
/// <inheritDoc />
/// </summary>
/// <remarks>
/// <see href="https://stackoverflow.com/a/67255440/6389395"> RFC 5480 format.</see>
/// </remarks>
public virtual byte[] Sign(byte[] message)
{
return _pk.SignData(message, new HashAlgorithmName(_hashAlgorithm), DSASignatureFormat.Rfc3279DerSequence);
}
}
还要注意,
ExtractPrivateKey()
方法也会将密钥作为IDisposable
返回,这样签名完成后就可以处理它了。我本可以在EcdsaSignature
中创建一个公共属性,但iText自己的IExternalSignature
s也不公开PK,所以我也不应该公开PK。
我不相信那个类被移植到iText 7——它只是一个包装类。
在本例中,您可以看到如何创建自定义IExternalSignatureContainer
请注意,可以在此处找到iText 5 X509Certificate2Signature的源代码
所以像这样:
public class X509Certificate2Signature: IExternalSignature {
private String hashAlgorithm;
private String encryptionAlgorithm;
private X509Certificate2 certificate;
public X509Certificate2Signature(X509Certificate2 certificate, String hashAlgorithm) {
if (!certificate.HasPrivateKey)
throw new ArgumentException("No private key.");
this.certificate = certificate;
this.hashAlgorithm = DigestAlgorithms.GetDigest(DigestAlgorithms.GetAllowedDigest(hashAlgorithm));
if (certificate.PrivateKey is RSACryptoServiceProvider)
encryptionAlgorithm = "RSA";
else if (certificate.PrivateKey is DSACryptoServiceProvider)
encryptionAlgorithm = "DSA";
else
throw new ArgumentException("Unknown encryption algorithm " + certificate.PrivateKey);
}
public virtual byte[] Sign(byte[] message) {
if (certificate.PrivateKey is RSACryptoServiceProvider) {
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider) certificate.PrivateKey;
return rsa.SignData(message, hashAlgorithm);
}
else {
DSACryptoServiceProvider dsa = (DSACryptoServiceProvider) certificate.PrivateKey;
return dsa.SignData(message);
}
}
public virtual String GetHashAlgorithm() {
return hashAlgorithm;
}
public virtual String GetEncryptionAlgorithm() {
return encryptionAlgorithm;
}
}
将在iText 7中复制该类的函数。在我的第一个链接中显示了如何使用该类,不过您很可能会使用signdefered()
方法,而不是signdefered()
方法。
我想用Java语言创建一个X509证书,然后从中提取公钥。 我在网上搜索了一下,发现了很多代码示例,但都有错误(未知变量或未知类型),或者有很多警告说:“方法...来自类型...是不建议使用的”等。 例如,以下代码不起作用的原因: 谁能告诉我如何使用纯Java或Bouncy Castle创建证书,然后从中获取公钥? 谢谢大家。
过去,我一直在通过导出带有密码的PFX证书来制作安全的TcpListener,但我想知道是否可以跳过这一步。 我没有使用商业SSL证书,并且有一个根CA,用于颁发服务器证书。在C#中托管TcpListener时,这些服务器证书需要额外的步骤(我想是因为没有使用CSR)。。。但是如果我有私钥和OpenSSL生成/使用的证书呢。 这很好,但是我必须发出openssl命令,从证书和私钥生成pfx文件,然
问题内容: 是否可以在不使用Bouncy Castle 类的情况下用Java代码合理地创建X509证书? 问题答案: 签名证书的能力不是标准Java库或扩展的一部分。 您自己需要做的许多代码都是核心的一部分。有一些类可以编码和解码X.500名称,X.509证书扩展,各种算法的公钥,当然还有用于实际执行数字签名的类。 自己实施这项工作并非易事,但绝对是可行的-我第一次制作可用于证书签名的原型时,可能
问题内容: 我想用PyCrypto在python中加密一些数据。 但是使用时出现错误: 密钥是使用以下命令生成的: 代码是: 问题答案: PyCrypto不支持X.509证书。您必须首先使用以下命令提取公钥: 然后,您可以在上使用。 如果您不想或不能使用openssl,则可以获取PEM X.509证书,并使用纯Python进行认证,如下所示:
我已经使用Bouncy Castle创建了X509证书,但我不能与或一起使用,因为它们(当然)使用的是.NET版本。虽然Bouncy Castle中有一个转换器,它接受一个BC X509并返回一个.NET X509。问题似乎是AuthenticateasServer/AuthenticateasClient需要包含私钥的证书。至少当我尝试转换并使用新证书时,在尝试使用SSLStream进行连接时会
问题内容: 我试图分几个步骤处理X509证书,并遇到了两个问题。我是JCE的新手,所以我还没有完全了解最新信息。 我们希望能够基于不同的编码(PEM,DER和PCKS7)解析几个不同的X509证书。我已经使用FireFox 从https://belgium.be以PEM和PCKS7格式导出了相同的证书(证书包括链)。我已经省略了几行不需要的问题 只要我使用FileInputStream而不是PCK