C#- Как подписать XML-документ сертификата без атрибута URI в теге <Reference>?
Можно ли подписать XML-документ без передачи атрибута URI тегу?
Я был в состоянии подписать весь XML документ типа с использованием KeyInfoX509Data сертификата и формат XAdES с помощью C#. Подпись действительна, возвращает истину . Проблема в том, что брокер, ответственный за проверку подписи, не поддерживает пустые атрибуты, такие как:
<ds:Reference URI=""> // Here is the problem
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>rDCBSbPc1QFATbQeP+77oskLJ3Tw6aog61bqGtyglXs=</ds:DigestValue>
</ds:Reference>
Я решил удалить атрибут URI перед отправкой документа брокеру, но без него возвращает false . Подпись недействительна без URI.
Я попытался удалить атрибут URI перед вызовом
SignedXml.ComputeSignature()
, но у меня есть следующее исключение:
An XmlDocument context is required for enveloped transforms.
Это означает, что атрибут URI является обязательным при использовании
XmlDsigEnvelopedSignatureTransform
класс для генерации подписи.
Затем я попытался установить для тега атрибут Id:
<Invoice Id="id-dsig-123456"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../oasis/maindoc/UBL-Invoice-2.1.xsd"
xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
Это означает, что подпись по-прежнему будет проверять весь документ путем поиска атрибута Id в теге:
<ds:Reference URI="#id-dsig-123456">
Подпись создается, и
SignedXml.CheckSignature()
возвращает истину, но брокер не разрешает использование каких-либо атрибутов
<Invoice>
тег и аннулирует подпись.
Мне нужно создать такую подпись
<ds:Signature Id="id-a086e4d3-ee02-458c-8429-eafe54eb6f6b"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference> ***// Without URI***
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>rDCBSbPc1QFATbQeP+77oskLJ3Tw6aog61bqGtyglXs=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#xades-id-a086e4d3-ee02-458c-8429-eafe54eb6f6b"
Type="http://uri.etsi.org/01903#SignedProperties">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>xBW/yL+uQJa3KrCXjdEPlFEJns7ZnM2pHs0Y5nLNvMM=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>XDTgRB3qyzLPMBPzfYeuwGSOz5JN52cdstIMHo3IXc8jGWp5JWFbOTD7Nj7QHZB0z5mULXgL7/eWfR5KkrT12aRHWPHocCbLfBgAi1xjNYGW1aNNxhnn4fSZSg4KrWS9oMoxkEKM2IrbJ++PgIM4+89MZA4kEtyih4WKkENnlE+w4RDnstjirA5viLZQEBDegPtGqS9ybrVej5QkF5/fy8HpZKsEl4oWPIWSrTQk7G3d4oDo/d2AU8XQgvcht+LkttJF4PJRq3AiyqwlJjoNSbt1R7NWYEJp+IulWEU0pdvW7LcVjrLO8yIkoHTWtyhbP20Zi3zLHxP9uPpMD37tzQ==</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>Certificate raw content...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
<ds:Object>
<xades:QualifyingProperties Target="#id-a086e4d3-ee02-458c-8429-eafe54eb6f6b"
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#">
<xades:SignedProperties Id="xades-id-a086e4d3-ee02-458c-8429-eafe54eb6f6b">
<xades:SignedSignatureProperties>
<xades:SigningTime>2021-06-05T02:52:49Z</xades:SigningTime>
<xades:SigningCertificate>
<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>LgjnhFUgsN4oTt4wjtzF+/7GYHEkTLVijMpjlMjfZ2E=</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>Certificate data....</ds:X509IssuerName>
<ds:X509SerialNumber>Serial number...</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>
</xades:SigningCertificate>
</xades:SignedSignatureProperties>
</xades:SignedProperties>
</xades:QualifyingProperties>
</ds:Object>
</ds:Signature>
Для создания подписи я использую следующий код. Класс наследуется от и используется для установки "
ds:
"при использовании префикса
SignedXml
методы:
private PrefixedSignedXml GeneratePrefixedSignedXml(XmlDocument xmlDocument, X509Certificate2 certificate)
{
Log.Information("[Begin] - GeneratePrefixedSignedXml");
Sha256SignatureDescription.Register();
var keyInfo = new KeyInfo();
var cspParams = new CspParameters(24) { KeyContainerName = PrefixedSignedXml.XmlKeyContainerName };
var rsaKey = new RSACryptoServiceProvider(cspParams) { PersistKeyInCsp = false };
keyInfo.AddClause(new KeyInfoX509Data(certificate));
var signedXml = new PrefixedSignedXml(xmlDocument) { SigningKey = rsaKey };
signedXml.KeyInfo = keyInfo;
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
signedXml.SignedInfo.SignatureMethod = PrefixedSignedXml.XmlRsaSignatureMethod;
var signatureReference = new Reference
{
Uri = "", // This needs to be null
DigestMethod = PrefixedSignedXml.XmlRsaDigestMethod,
};
signatureReference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(signatureReference);
Log.Information("[End] - GeneratePrefixedSignedXml");
return signedXml;
}
Это
PrefixedSignedXml
:
public class PrefixedSignedXml : SignedXml
{
public const string XmlDsigSignatureProperties = "http://uri.etsi.org/01903#SignedProperties";
public const string XadesProofOfApproval = "http://uri.etsi.org/01903/v1.2.2#ProofOfApproval";
public const string XadesNamespaceUrl = "http://uri.etsi.org/01903/v1.3.2#";
public const string XmlRsaSignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
public const string XmlRsaDigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
public const string DsaSignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
public const string XmlKeyContainerName = "XML_DSIG_RSA_KEY";
private const string CryptographicXmlLoadKeyFailedException = "Cryptography_Xml_LoadKeyFailed";
private const string CryptographicXmlCreatedKeyFailedException = "Cryptography_Xml_CreatedKeyFailed";
private const string CryptographicXmlSignatureDescriptionNotCreatedException = "Cryptography_Xml_SignatureDescriptionNotCreated";
private const string CryptographicExceptionXmlCreateHashAlgorithmFailed = "Cryptography_Xml_CreateHashAlgorithmFailed";
private const string MethodInfoName = "BuildDigestedReferences";
public XmlElement PropertiesNode { get; set; }
private readonly List<DataObject> _dataObjects = new List<DataObject>();
public PrefixedSignedXml(XmlDocument document)
: base(document) { }
public override XmlElement GetIdElement(XmlDocument document, string idValue)
{
if (string.IsNullOrEmpty(idValue))
return null;
var xmlElement = base.GetIdElement(document, idValue);
if (xmlElement == null)
{
XmlNamespaceManager nsManager = new XmlNamespaceManager(document.NameTable);
nsManager.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
xmlElement = document.SelectSingleNode("//*[@Id=\"" + idValue + "\"]", nsManager) as XmlElement;
}
if (xmlElement != null)
return xmlElement;
if (_dataObjects.Count == 0)
return null;
foreach (var dataObject in _dataObjects)
{
var nodeWithSameId = XmlHelper.FindNodeWithAttributeValueIn(dataObject.Data, "Id", idValue);
if (nodeWithSameId != null)
return nodeWithSameId;
}
return null;
}
public new void AddObject(DataObject dataObject)
{
base.AddObject(dataObject);
_dataObjects.Add(dataObject);
}
public void ComputeSignature(string prefix)
{
this.BuildDigestedReferences();
var signingKey = this.SigningKey;
if (signingKey == null)
throw new CryptographicException(CryptographicXmlLoadKeyFailedException);
if (this.SignedInfo.SignatureMethod == null)
{
if ((signingKey is DSA))
this.SignedInfo.SignatureMethod = DsaSignatureMethod;
if (!(signingKey is RSA))
throw new CryptographicException(CryptographicXmlCreatedKeyFailedException);
if (this.SignedInfo.SignatureMethod == null)
this.SignedInfo.SignatureMethod = XmlRsaSignatureMethod;
}
if (!(CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) is SignatureDescription description))
throw new CryptographicException(CryptographicXmlSignatureDescriptionNotCreatedException);
var hash = description.CreateDigest();
if (hash == null)
throw new CryptographicException(CryptographicExceptionXmlCreateHashAlgorithmFailed);
this.GetC14NDigest(hash, prefix);
this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
}
public XmlElement GetXml(string prefix)
{
var xmlElement = this.GetXml();
XmlHelper.SetPrefix(prefix, xmlElement);
return xmlElement;
}
private void BuildDigestedReferences()
{
var type = typeof(SignedXml);
var methodInfo = type.GetMethod(MethodInfoName, BindingFlags.NonPublic | BindingFlags.Instance);
methodInfo.Invoke(this, new object[] { });
}
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
var canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
var document = new XmlDocument
{
PreserveWhitespace = true
};
document.AppendChild(document.ImportNode(this.SignedInfo.GetXml(), true));
XmlHelper.SetPrefix(prefix, document.DocumentElement);
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
}
И это метод, используемый для добавления атрибутов XAdEs:
private void AddXadesProperties(XmlDocument document, PrefixedSignedXml xadesSignedXml, X509Certificate2 signingCertificate)
{
Log.Information("[Begin] - AddXadesProperties");
var parametersSignature = new Reference
{
Uri = $"#{SignaturePropertiesId}{SignatureId}",
Type = PrefixedSignedXml.XmlDsigSignatureProperties,
DigestMethod = PrefixedSignedXml.XmlRsaDigestMethod
};
parametersSignature.AddTransform(new XmlDsigExcC14NTransform());
xadesSignedXml.AddReference(parametersSignature);
// <Object>
var objectNode = document.CreateElement(XmlDsPrefix, "Object", SignedXml.XmlDsigNamespaceUrl);
// <Object><QualifyingProperties>
var qualifyingPropertiesNode = document.CreateElement(XadesPrefix, "QualifyingProperties", PrefixedSignedXml.XadesNamespaceUrl);
qualifyingPropertiesNode.SetAttribute("Target", $"#{SignatureId}");
objectNode.AppendChild(qualifyingPropertiesNode);
// <Object><QualifyingProperties><SignedProperties>
var signedPropertiesNode = document.CreateElement(XadesPrefix, "SignedProperties", PrefixedSignedXml.XadesNamespaceUrl);
signedPropertiesNode.SetAttribute("Id", $"{SignaturePropertiesId}{SignatureId}");
qualifyingPropertiesNode.AppendChild(signedPropertiesNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties>
var signedSignaturePropertiesNode = document.CreateElement(XadesPrefix, "SignedSignatureProperties", PrefixedSignedXml.XadesNamespaceUrl);
signedPropertiesNode.AppendChild(signedSignaturePropertiesNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties></SigningTime>
var signingTime = document.CreateElement(XadesPrefix, "SigningTime", PrefixedSignedXml.XadesNamespaceUrl);
signingTime.InnerText = $"{DateTime.UtcNow:s}Z";
signedSignaturePropertiesNode.AppendChild(signingTime);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate>
var signingCertificateNode = document.CreateElement(XadesPrefix, "SigningCertificate", PrefixedSignedXml.XadesNamespaceUrl);
signedSignaturePropertiesNode.AppendChild(signingCertificateNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert>
var certNode = document.CreateElement(XadesPrefix, "Cert", PrefixedSignedXml.XadesNamespaceUrl);
signingCertificateNode.AppendChild(certNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest>
var certDigestNode = document.CreateElement(XadesPrefix, "CertDigest", PrefixedSignedXml.XadesNamespaceUrl);
certNode.AppendChild(certDigestNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest></DigestMethod>
var digestMethod = document.CreateElement(XmlDsPrefix, "DigestMethod", SignedXml.XmlDsigNamespaceUrl);
var digestMethodAlgorithmAtribute = document.CreateAttribute("Algorithm");
digestMethodAlgorithmAtribute.InnerText = PrefixedSignedXml.XmlRsaDigestMethod;
digestMethod.Attributes.Append(digestMethodAlgorithmAtribute);
certDigestNode.AppendChild(digestMethod);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest></DigestMethod>
var digestValue = document.CreateElement(XmlDsPrefix, "DigestValue", SignedXml.XmlDsigNamespaceUrl);
digestValue.InnerText = GenerateCertificateHash256Value(signingCertificate);
certDigestNode.AppendChild(digestValue);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial>
var issuerSerialNode = document.CreateElement(XadesPrefix, "IssuerSerial", PrefixedSignedXml.XadesNamespaceUrl);
certNode.AppendChild(issuerSerialNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial></X509IssuerName>
var x509IssuerName = document.CreateElement(XmlDsPrefix, "X509IssuerName", SignedXml.XmlDsigNamespaceUrl);
x509IssuerName.InnerText = signingCertificate.IssuerName.Name;
issuerSerialNode.AppendChild(x509IssuerName);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial></X509SerialNumber>
var x509SerialNumber = document.CreateElement(XmlDsPrefix, "X509SerialNumber", SignedXml.XmlDsigNamespaceUrl);
x509SerialNumber.InnerText = ToDecimalSerialNumberString(signingCertificate.SerialNumber);
issuerSerialNode.AppendChild(x509SerialNumber);
var dataObject = new DataObject { Data = qualifyingPropertiesNode.SelectNodes(".") };
xadesSignedXml.AddObject(dataObject);
Log.Information("[End] - AddXadesProperties");
}
После этого
ComputeSignature()
генерирует дайджесты и исходную подпись, затем я передаю
<ds:SignedInfo>
тег на другой сервер, который генерирует окончательный
<ds:SignatureValue>
. Наконец, я прохожу
<SignatureValue>
к
<ds:Signature>
сам, добавить к
XmlDocument
и отправляем брокеру.
Кто-нибудь знает, можно ли подписать XML-документ без передачи атрибута URI в
<Reference>
тег?
Проект представляет собой ClassLibrary, использующий .NetFramework 4.6.
Я столкнулся с этой проблемой какое-то время, любая помощь будет благодарна.
Заранее спасибо.
2 ответа
Я получил ссылку без атрибута URI, но в моем случае полученный подписанный xml все еще можно было проверить на другой стороне. Возможно мой код поможет.
Создайте класс преобразования, унаследованный от XmlDsigExcC14NTransform.
public class MyXmlDsigExcC14NTransform : XmlDsigExcC14NTransform { public override void LoadInput(object obj) { if(obj == null) obj = this.Context.OwnerDocument; base.LoadInput(obj); } }
Добавьте это преобразование в эталонный экземпляр:
var documentReference = new Reference(); documentReference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1"; documentReference.AddTransform(new MyXmlDsigExcC14NTransform()); references.Add(documentReference);
После того, как я подписал xml, я получил в подписи необходимый тег Reference.
<ds:Reference>
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>DigestValue</ds:DigestValue>
</ds:Reference>
Надеюсь, это поможет
Оказывается, все это время я был прав. В
*<Reference URI="#id-value">*
В этом случае необходим тег, и брокер был обновлен для поддержки этого формата.
Спасибо jdweng за поддержку.