I'm trying to sign XML with XAdES with certificate stored on Smart Card.
First of all, it is worth to mention, that when I'm signing the same XML document with software added to this smart card, operation goes without any problem.
Here is code:
public class XadesWrapper
{
private X509Certificate2 _certificate;
private X509Chain _chain;
private XmlDocument _envelopedSignatureXmlDocument;
private XadesSignedXml _xadesSignedXml;
private int _docDataObjectCounter;
public byte[] _signedDoc;
public XadesWrapper(XmlDocument xmlDoc, X509Certificate2 xCert)
{
try
{
_envelopedSignatureXmlDocument = xmlDoc;
_certificate = xCert; //certificate installed from smart card
AddReference();
CheckCertificate();
AddKeyInfo();
AddObjectInfo();
AddSignatureToDocument();
}
catch (Exception e)
{
throw e;
}
}
private void AddReference()
{
Reference reference = new Reference();
_docDataObjectCounter = 1;
_xadesSignedXml = new XadesSignedXml(_envelopedSignatureXmlDocument);
_xadesSignedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
reference.Id = "r-id-" + _docDataObjectCounter;
reference.Uri = "";
reference.Type = "";
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
_xadesSignedXml.AddReference(reference);
}
private void CheckCertificate()
{
if (_certificate == null)
return;
_chain = new X509Chain();
_chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
_chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
_chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30);
_chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
if (_chain.Build(_certificate) == true)
AddKeyInfo();
else
{
_certificate = null;
_chain = null;
throw new ArgumentException("Chain is not valid.");
}
}
private void AddKeyInfo()
{
RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)_certificate.PrivateKey;
_xadesSignedXml.SigningKey = rsaKey;
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(_certificate));
keyInfo.AddClause(new RSAKeyValue(rsaKey));
this._xadesSignedXml.KeyInfo = keyInfo;
}
private void AddObjectInfo()
{
_xadesSignedXml.Signature.Id = "id-" + _certificate.Thumbprint.ToLower();
XadesObject xadesObject = new XadesObject();
xadesObject.QualifyingProperties.Target = "#" + _xadesSignedXml.Signature.Id;
xadesObject.QualifyingProperties.SignedProperties.Id = "xades-" + _xadesSignedXml.Signature.Id;
Cert cert = new Cert();
cert.IssuerSerial.X509IssuerName = this._certificate.IssuerName.Name;
cert.IssuerSerial.X509SerialNumber = BigInteger.Parse(this._certificate.SerialNumber, NumberStyles.HexNumber).ToString();
cert.CertDigest.DigestMethod.Algorithm = "http://www.w3.org/2001/04/xmlenc#sha256";
cert.CertDigest.DigestValue = new SHA256CryptoServiceProvider().ComputeHash(_certificate.RawData);
xadesObject.QualifyingProperties.SignedProperties.SignedSignatureProperties.SigningCertificate.CertCollection.Add(cert);
xadesObject.QualifyingProperties.SignedProperties.SignedSignatureProperties.SigningTime = DateTime.Now;
DataObjectFormat newDataObjectFormat = new DataObjectFormat();
newDataObjectFormat.Description = "";
newDataObjectFormat.MimeType = "text/xml";
newDataObjectFormat.ObjectReferenceAttribute = "#r-id-" + _docDataObjectCounter;
xadesObject.QualifyingProperties.SignedProperties.SignedDataObjectProperties.DataObjectFormatCollection.Add(newDataObjectFormat);
_xadesSignedXml.AddXadesObject(xadesObject);
}
private void AddSignatureToDocument()
{
_xadesSignedXml.ComputeSignature();
_xadesSignedXml.SignatureValueId = "value-" + _xadesSignedXml.Signature.Id;
_envelopedSignatureXmlDocument.DocumentElement.AppendChild(_envelopedSignatureXmlDocument.ImportNode(_xadesSignedXml.GetXml(), true));
_signedDoc = Encoding.UTF8.GetBytes(_envelopedSignatureXmlDocument.OuterXml);
}
}
Everything goes well till execution reaches line _xadesSignedXml.ComputeSignature() in AddSignatureToDocument() method.
System is asking for smart card PIN QSCD , and then ComputeSignature() throws a CryptographicException() with HRESULT -2146435068 (0x80100004) and message "One or more of the supplied parameters could not be properly interpreted."
Here is call stack:
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash)
at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash)
at System.Security.Cryptography.RSAPKCS1SignatureFormatter.CreateSignature(Byte[] rgbHash)
at System.Security.Cryptography.AsymmetricSignatureFormatter.CreateSignature(HashAlgorithm hash)
at Microsoft.Xades.XadesSignedXml.ComputeSignature()
So I've found the solution: xadesSignedXml element had
xadesSignedXml.SignedInfo.SignatureMethod
property null, while thexadesSignedXml.SigningKey.SignatureAlgorithm
property was automatically set to "http://www.w3.org/2000/09/xmldsig#rsa-sha1".While
xadesSignedXml.ComputeSignature()
is being executed, andxadesSignedXml.SignedInfo.SignatureMethod
is null, then value ofxadesSignedXml.SigningKey.SignatureAlgorithm
is assigned to it.The problem occurred because the signing was performed with SHA-1, which is no longer supported by Microsoft.
Problem was solved by adding
xadesSignedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
inAddReference()
method.