如何数字签名和签署xml

问题描述 投票:0回答:1

我正在使用asp.net core 2.2开发zatca简化发票,现在我在sign xml中遇到问题。 私钥是使用以下命令从 zatca sdk 生成的 (fatoora -csr -csrconfig csr-config-example.properties)

我已经从发票和私钥的哈希值中生成了签名值,并使用 xpath 将其分配到所需字段, 之后,从 xml 的 SignedProperties 生成签名哈希并将其传递给 的第二个引用,但是当生成发票并在此处检查验证时 (https://sandbox.zatca.gov.sa/TestXML),它会抛出 (错误的 xadesSignedPropertiesDigestValue 和错误的签名值)

// this code if use to generate the signature value public static string SignDataWithECPrivateKey(byte[] data, byte[] privateKey) { ECPrivateKeyParameters keyParams = GetECPrivateKeyParameters(privateKey); ISigner signer = SignerUtilities.GetSigner("SHA256withECDSA"); signer.Init(true, keyParams); // Initialize for signing signer.BlockUpdate(data, 0, data.Length); byte[] signature = signer.GenerateSignature(); return Convert.ToBase64String(signature); } private static ECPrivateKeyParameters GetECPrivateKeyParameters(byte[] privateKey) { try { var privateKeyInfo = PrivateKeyFactory.CreateKey(privateKey); if (privateKeyInfo is ECPrivateKeyParameters ecPrivateKeyParameters) { return ecPrivateKeyParameters; } else { throw new Exception("Invalid EC private key format."); } } catch (Exception ex) { throw new Exception("Error parsing EC private key: " + ex.Message); } } public static string DigitalSignature(string invoiceHash) { byte[] privateKeyBytes = Convert.FromBase64String("MIGNAgEAMBAGByqGSM49AgEGBSuBBAAKBHYwdAIBAQQgH1/NUMqrSQLPiraQBpnHiNMjUOkSy/Re2qJpCHaPwfigBwYFK4EEAAqhRANCAAROmIO4u1eYiWqnVRwHzeKBDe4IOEiYsx8Gviqy1YGPuAMlpjWBEPncU/uFuFaNuNMErtTn8me8MI52QQN9uKoS"); byte[] _invoiceData = Convert.FromBase64String(invoiceHash); return SignDataWithECPrivateKey(_invoiceData, privateKeyBytes); } // end

<---- This code will generate Hash Of invoice sign properties (#xadesSignedProperties) --->

public static string DigestMethod(string inputString) { // Create an instance of the SHA-256 hash algorithm using (SHA256 sha256 = SHA256.Create()) { byte[] inputBytes = Encoding.UTF8.GetBytes(inputString); // Calculate the hash (digest) of the input byte[] hashBytes = sha256.ComputeHash(inputBytes); string hashHex = BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); return Convert.ToBase64String(Encoding.UTF8.GetBytes(hashHex)); } } public static MemoryStream CanonicalizeXml(string transformedXml) { using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(transformedXml))) { //converting xml to standard format //https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.xml.xmldsigc14ntransform?view=dotnet-plat-ext-6.0 var canonicalizer = new XmlDsigC14NTransform(false); canonicalizer.LoadInput(memoryStream); var canonicalizedXml = canonicalizer.GetOutput() as MemoryStream; return canonicalizedXml; } } public static string GenerateSignHash(string inputXml) { var canonicalizedXml = InvoiceHashHelper.CanonicalizeXml(inputXml); return ECDSASigner.DigestMethod(Encoding.UTF8.GetString(canonicalizedXml.ToArray())); } using xpath i fetch following xml /*<xades:SignedProperties Id="xadesSignedProperties"> <xades:SignedSignatureProperties> <xades:SigningTime>2023-10-25T15:28:31</xades:SigningTime> <xades:SigningCertificate> <xades:Cert> <xades:CertDigest> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>Y2U5MzY5MTFiOTA4NTc0YmI2NjExNDFlMzBkNmM2YTljZWMxYjRlZDFmYWE3NjE1NjVlNDQzNjA3ODdkYzZjZQ==</ds:DigestValue> </xades:CertDigest> <xades:IssuerSerial> <ds:X509IssuerName>CN=TSZEINVOICE-SubCA-1, DC=extgazt, DC=gov, DC=local</ds:X509IssuerName> <ds:X509SerialNumber>2475382889481219846080454947234981286678397371</ds:X509SerialNumber> </xades:IssuerSerial> </xades:Cert> </xades:SigningCertificate> </xades:SignedSignatureProperties> </xades:SignedProperties>*/ and pass it **GenerateSignHash** Method //xmlDoc is complete xml template of simplified invoice in which i do changes using xpath // x509certificate pem fromat = "MIID6jCCA5CgAwIBAgITbwAAgbuRbo5tpQ+QjgABAACBuzAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMTEwOTA4MDcyMloXDTI0MTEwODA4MDcyMlowTjELMAkGA1UEBhMCU0ExEzARBgNVBAoTCjM5OTk5OTk5OTkxDDAKBgNVBAsTA1RTVDEcMBoGA1UEAxMTVFNULTM5OTk5OTk5OTkwMDAwMzBWMBAGByqGSM49AgEGBSuBBAAKA0IABGGDDKDmhWAITDv7LXqLX2cmr6+qddUkpcLCvWs5rC2O29W/hS4ajAK4Qdnahym6MaijX75Cg3j4aao7ouYXJ9GjggI5MIICNTCBmgYDVR0RBIGSMIGPpIGMMIGJMTswOQYDVQQEDDIxLVRTVHwyLVRTVHwzLTlmMDkyMjM4LTFkOTctNDcxOC1iNDQxLWNiYzMwMTMyMWIwYTEfMB0GCgmSJomT8ixkAQEMDzM5OTk5OTk5OTkwMDAwMzENMAsGA1UEDAwEMTEwMDEMMAoGA1UEGgwDVFNUMQwwCgYDVQQPDANUU1QwHQYDVR0OBBYEFDuWYlOzWpFN3no1WtyNktQdrA8JMB8GA1UdIwQYMBaAFHZgjPsGoKxnVzWdz5qspyuZNbUvME4GA1UdHwRHMEUwQ6BBoD+GPWh0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL0NlcnRFbnJvbGwvVFNaRUlOVk9JQ0UtU3ViQ0EtMS5jcmwwga0GCCsGAQUFBwEBBIGgMIGdMG4GCCsGAQUFBzABhmJodHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9DZXJ0RW5yb2xsL1RTWkVpbnZvaWNlU0NBMS5leHRnYXp0Lmdvdi5sb2NhbF9UU1pFSU5WT0lDRS1TdWJDQS0xKDEpLmNydDArBggrBgEFBQcwAYYfaHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2Evb2NzcDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMDMCcGCSsGAQQBgjcVCgQaMBgwCgYIKwYBBQUHAwIwCgYIKwYBBQUHAwMwCgYIKoZIzj0EAwIDSAAwRQIgeWUEjxXaW4s8XilH/abzbDJhHHjO3uLaD87YqioA89YCIQDNltfAU98b8FnTD7M8NYIk8cqi7OnPu7h85v5V1Bt3Hg=="; private static void SigninHash(XmlDocument xmlDoc) { byte[] cert = Convert.FromBase64String(GlobalXmlSetting.Cert); var x509 = new X509Certificate2(cert); var certInfo = new ECDSASigner().GetCertificateInfo(x509); //setnodevalue is custom xmlDoc.SetNodeValue(SigningTimeXpath, certInfo.SignTime); xmlDoc.SetNodeValue(CertHashXpath, certInfo.CertHash); xmlDoc.SetNodeValue(CertIssuerXpath, certInfo.CertificateIssuer); xmlDoc.SetNodeValue(CertSerialNumberXpath, certInfo.CertificateSerialNumber); var _xml = xmlDoc.SelectSingleNode(SignedPropXpath).OuterXml; var signHash = ECDSASigner.GenerateSignHash(_xml); var signatureValue = ECDSASigner.DigitalSignature(_currentHash); xmlDoc.SetNodeValue(DigitalSignatureXpath, signatureValue); xmlDoc.SetNodeValue(SignInPropertiesHashXpath, signHash); _qrCode = new TlvConverter().TestTlv(_currentHash, signatureValue); PopulateQRCode(xmlDoc, _qrCode); }

示例 xml :-

<?xml version="1.0" encoding="UTF-8"?> <Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"> <ext:UBLExtensions> <ext:UBLExtension> <ext:ExtensionURI>urn:oasis:names:specification:ubl:dsig:enveloped:xades</ext:ExtensionURI> <ext:ExtensionContent> <sig:UBLDocumentSignatures xmlns:sig="urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2" xmlns:sac="urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2" xmlns:sbc="urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2"> <sac:SignatureInformation> <cbc:ID>urn:oasis:names:specification:ubl:signature:1</cbc:ID> <sbc:ReferencedSignatureID>urn:oasis:names:specification:ubl:signature:Invoice</sbc:ReferencedSignatureID> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="signature"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11" /> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256" /> <ds:Reference Id="invoiceSignedData" URI=""> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"> <ds:XPath>not(//ancestor-or-self::ext:UBLExtensions)</ds:XPath> </ds:Transform> <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"> <ds:XPath>not(//ancestor-or-self::cac:Signature)</ds:XPath> </ds:Transform> <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"> <ds:XPath>not(//ancestor-or-self::cac:AdditionalDocumentReference[cbc:ID='QR'])</ds:XPath> </ds:Transform> <ds:Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11" /> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /> <ds:DigestValue>sZwHJNRoZHatRyJ31IHiwE8og5xfcCOwewQV/FQz09Y=</ds:DigestValue> </ds:Reference> <ds:Reference Type="http://www.w3.org/2000/09/xmldsig#SignatureProperties" URI="#xadesSignedProperties"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /> <ds:DigestValue>ZTE3MjRlYzYxOTI5ZDdjMGNlMGJkMmI2MGJiNTFiNzQzZTgxYTljZmNiZWExMDc0MmVhOTRjZGUwOTAwOTM1NQ==</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>MEUCIBRiUYVKGoAO9LQFuDQUaWZlJbtrZE/yMXLzhWEc02SeAiEAx1DJ/YU8aFyz/+NJTsMPzVB3PQOha9dc4LGxTGIq2gU=</ds:SignatureValue> <ds:KeyInfo> <ds:X509Data> <ds:X509Certificate>MIID6jCCA5CgAwIBAgITbwAAgbuRbo5tpQ+QjgABAACBuzAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMTEwOTA4MDcyMloXDTI0MTEwODA4MDcyMlowTjELMAkGA1UEBhMCU0ExEzARBgNVBAoTCjM5OTk5OTk5OTkxDDAKBgNVBAsTA1RTVDEcMBoGA1UEAxMTVFNULTM5OTk5OTk5OTkwMDAwMzBWMBAGByqGSM49AgEGBSuBBAAKA0IABGGDDKDmhWAITDv7LXqLX2cmr6+qddUkpcLCvWs5rC2O29W/hS4ajAK4Qdnahym6MaijX75Cg3j4aao7ouYXJ9GjggI5MIICNTCBmgYDVR0RBIGSMIGPpIGMMIGJMTswOQYDVQQEDDIxLVRTVHwyLVRTVHwzLTlmMDkyMjM4LTFkOTctNDcxOC1iNDQxLWNiYzMwMTMyMWIwYTEfMB0GCgmSJomT8ixkAQEMDzM5OTk5OTk5OTkwMDAwMzENMAsGA1UEDAwEMTEwMDEMMAoGA1UEGgwDVFNUMQwwCgYDVQQPDANUU1QwHQYDVR0OBBYEFDuWYlOzWpFN3no1WtyNktQdrA8JMB8GA1UdIwQYMBaAFHZgjPsGoKxnVzWdz5qspyuZNbUvME4GA1UdHwRHMEUwQ6BBoD+GPWh0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL0NlcnRFbnJvbGwvVFNaRUlOVk9JQ0UtU3ViQ0EtMS5jcmwwga0GCCsGAQUFBwEBBIGgMIGdMG4GCCsGAQUFBzABhmJodHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9DZXJ0RW5yb2xsL1RTWkVpbnZvaWNlU0NBMS5leHRnYXp0Lmdvdi5sb2NhbF9UU1pFSU5WT0lDRS1TdWJDQS0xKDEpLmNydDArBggrBgEFBQcwAYYfaHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2Evb2NzcDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMDMCcGCSsGAQQBgjcVCgQaMBgwCgYIKwYBBQUHAwIwCgYIKwYBBQUHAwMwCgYIKoZIzj0EAwIDSAAwRQIgeWUEjxXaW4s8XilH/abzbDJhHHjO3uLaD87YqioA89YCIQDNltfAU98b8FnTD7M8NYIk8cqi7OnPu7h85v5V1Bt3Hg==</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> <ds:Object> <xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Target="signature"> <xades:SignedProperties Id="xadesSignedProperties"> <xades:SignedSignatureProperties> <xades:SigningTime>2023-11-01T16:16:17</xades:SigningTime> <xades:SigningCertificate> <xades:Cert> <xades:CertDigest> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /> <ds:DigestValue>Y2U5MzY5MTFiOTA4NTc0YmI2NjExNDFlMzBkNmM2YTljZWMxYjRlZDFmYWE3NjE1NjVlNDQzNjA3ODdkYzZjZQ==</ds:DigestValue> </xades:CertDigest> <xades:IssuerSerial> <ds:X509IssuerName>CN=TSZEINVOICE-SubCA-1, DC=extgazt, DC=gov, DC=local</ds:X509IssuerName> <ds:X509SerialNumber>2475382889481219846080454947234981286678397371</ds:X509SerialNumber> </xades:IssuerSerial> </xades:Cert> </xades:SigningCertificate> </xades:SignedSignatureProperties> </xades:SignedProperties> </xades:QualifyingProperties> </ds:Object> </ds:Signature> </sac:SignatureInformation> </sig:UBLDocumentSignatures> </ext:ExtensionContent> </ext:UBLExtension> </ext:UBLExtensions> <cbc:ProfileID>reporting:1.0</cbc:ProfileID> <cbc:ID>100012</cbc:ID> <cbc:UUID>a7a3abb8-0f26-43bd-9cf9-b028d62fb223</cbc:UUID> <cbc:IssueDate>2022-09-13</cbc:IssueDate> <cbc:IssueTime>14:40:40</cbc:IssueTime> <cbc:InvoiceTypeCode name="0200000">388</cbc:InvoiceTypeCode> <cbc:Note languageID="ar">ABC</cbc:Note> <cbc:DocumentCurrencyCode>SAR</cbc:DocumentCurrencyCode> <cbc:TaxCurrencyCode>SAR</cbc:TaxCurrencyCode> <cac:AdditionalDocumentReference> <cbc:ID>ICV</cbc:ID> <cbc:UUID>10</cbc:UUID> </cac:AdditionalDocumentReference> <cac:AdditionalDocumentReference> <cbc:ID>PIH</cbc:ID> <cac:Attachment> <cbc:EmbeddedDocumentBinaryObject mimeCode="text/plain">NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==</cbc:EmbeddedDocumentBinaryObject> </cac:Attachment> </cac:AdditionalDocumentReference> <cac:AdditionalDocumentReference> <cbc:ID>QR</cbc:ID> <cac:Attachment> <cbc:EmbeddedDocumentBinaryObject mimeCode="text/plain">ARNBY21lIFdpZGdldOKAmXMgTFREAg8zMTExMTExMTExMDExMTMDEzIwMjItMDQtMjUgMTU6MzA6MDAEBzIzMDAuMDAFBjMwMC4wMAYsc1p3SEpOUm9aSGF0UnlKMzFJSGl3RThvZzV4ZmNDT3dld1FWL0ZRejA5WT0HYE1FVUNJQlJpVVlWS0dvQU85TFFGdURRVWFXWmxKYnRyWkUveU1YTHpoV0VjMDJTZUFpRUF4MURKL1lVOGFGeXovK05KVHNNUHpWQjNQUU9oYTlkYzRMR3hUR0lxMmdVPQhYMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYYMMoOaFYAhMO/steotfZyavr6p11SSlwsK9azmsLY7b1b+FLhqMArhB2dqHKboxqKNfvkKDePhpqjui5hcn0QlHMEUCIHllBI8V2luLPF4pR/2m82wyYRx4zt7i2g/O2KoqAPPWAiEAzZbXwFPfG/BZ0w+zPDWCJPHKouzpz7u4fOb+VdQbdx4=</cbc:EmbeddedDocumentBinaryObject> </cac:Attachment> </cac:AdditionalDocumentReference> <cac:Signature> <cbc:ID>urn:oasis:names:specification:ubl:signature:Invoice</cbc:ID> <cbc:SignatureMethod>urn:oasis:names:specification:ubl:dsig:enveloped:xades</cbc:SignatureMethod> </cac:Signature> <cac:AccountingSupplierParty> <cac:Party> <cac:PartyIdentification> <cbc:ID schemeID="CRN">324223432432432</cbc:ID> </cac:PartyIdentification> <cac:PostalAddress> <cbc:StreetName>testA</cbc:StreetName> <cbc:BuildingNumber>3454</cbc:BuildingNumber> <cbc:PlotIdentification>1234</cbc:PlotIdentification> <cbc:CitySubdivisionName>fgffA</cbc:CitySubdivisionName> <cbc:CityName>RiyadhA</cbc:CityName> <cbc:PostalZone>12345</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>SA</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress> <cac:PartyTaxScheme> <cbc:CompanyID>311111111101113</cbc:CompanyID> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:PartyTaxScheme> <cac:PartyLegalEntity> <cbc:RegistrationName>Acme Widget’s LTD</cbc:RegistrationName> </cac:PartyLegalEntity> </cac:Party> </cac:AccountingSupplierParty> <cac:AccountingCustomerParty> <cac:Party> <cac:PostalAddress> <cbc:StreetName>baaounA</cbc:StreetName> <cbc:CitySubdivisionName>fgffA</cbc:CitySubdivisionName> <cac:Country> <cbc:IdentificationCode>SA</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress> <cac:PartyTaxScheme> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:PartyTaxScheme> <cac:PartyLegalEntity> <cbc:RegistrationName>testA</cbc:RegistrationName> </cac:PartyLegalEntity> </cac:Party> </cac:AccountingCustomerParty> <cac:PaymentMeans> <cbc:PaymentMeansCode>10</cbc:PaymentMeansCode> </cac:PaymentMeans> <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>discount</cbc:AllowanceChargeReason> <cbc:Amount currencyID="SAR">0.00</cbc:Amount> <cac:TaxCategory> <cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID> <cbc:Percent>15.00</cbc:Percent> <cac:TaxScheme> <cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID> </cac:TaxScheme> </cac:TaxCategory> </cac:AllowanceCharge> <cac:TaxTotal> <cbc:TaxAmount currencyID="SAR">300.00</cbc:TaxAmount> </cac:TaxTotal> <cac:TaxTotal> <cbc:TaxAmount currencyID="SAR">300.00</cbc:TaxAmount> <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="SAR">2000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="SAR">300.00</cbc:TaxAmount> <cac:TaxCategory> <cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID> <cbc:Percent>15.00</cbc:Percent> <cac:TaxScheme> <cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID> </cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal> </cac:TaxTotal> <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="SAR">2000.00</cbc:LineExtensionAmount> <cbc:TaxExclusiveAmount currencyID="SAR">2000.00</cbc:TaxExclusiveAmount> <cbc:TaxInclusiveAmount currencyID="SAR">2300.00</cbc:TaxInclusiveAmount> <cbc:AllowanceTotalAmount currencyID="SAR">0.00</cbc:AllowanceTotalAmount> <cbc:PrepaidAmount currencyID="SAR">0.00</cbc:PrepaidAmount> <cbc:PayableAmount currencyID="SAR">2300.00</cbc:PayableAmount> </cac:LegalMonetaryTotal> <cac:InvoiceLine> <cbc:ID>2</cbc:ID> <cbc:InvoicedQuantity unitCode="PCE">100</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="SAR">1000.00</cbc:LineExtensionAmount> <cac:TaxTotal> <cbc:TaxAmount currencyID="SAR">150.00</cbc:TaxAmount> <cbc:RoundingAmount currencyID="SAR">1150.00</cbc:RoundingAmount> </cac:TaxTotal> <cac:Item> <cbc:Name>item2</cbc:Name> <cac:ClassifiedTaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>15.00</cbc:Percent> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:ClassifiedTaxCategory> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="SAR">10.00</cbc:PriceAmount> <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>discount</cbc:AllowanceChargeReason> <cbc:Amount currencyID="SAR">34.00</cbc:Amount> </cac:AllowanceCharge> </cac:Price> </cac:InvoiceLine> <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="PCE">100</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="SAR">1000.00</cbc:LineExtensionAmount> <cac:TaxTotal> <cbc:TaxAmount currencyID="SAR">150.00</cbc:TaxAmount> <cbc:RoundingAmount currencyID="SAR">1150.00</cbc:RoundingAmount> </cac:TaxTotal> <cac:Item> <cbc:Name>item1</cbc:Name> <cac:ClassifiedTaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>15.00</cbc:Percent> <cac:TaxScheme> <cbc:ID>VAT</cbc:ID> </cac:TaxScheme> </cac:ClassifiedTaxCategory> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="SAR">10.00</cbc:PriceAmount> <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>discount</cbc:AllowanceChargeReason> <cbc:Amount currencyID="SAR">34.00</cbc:Amount> </cac:AllowanceCharge> </cac:Price> </cac:InvoiceLine></Invoice>
    
c# .net xml cryptography digital-signature
1个回答
0
投票
您似乎想要使用 ECDSA 私钥签署 XML 文档,并生成 ZATCA 简化发票所需的签名和哈希值。您可以使用以下代码来实现此目的:

using System; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Xml; using System.Xml.XPath; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; using Org.BouncyCastle.X509; public class ZatcaInvoiceSigner { private X509Certificate2 certificate; public ZatcaInvoiceSigner(string certificatePem) { certificate = LoadCertificateFromPem(certificatePem); } public void SignXmlDocument(XmlDocument xmlDoc) { // Load the XML document X509Certificate2 certificate = GetCertificate(); // Load your certificate here // Prepare the SignedProperties string signedPropertiesXml = @"<xades:SignedProperties Id=""xadesSignedProperties""> <!-- Include the necessary signed signature properties --> </xades:SignedProperties>"; // Calculate the digest value of the SignedProperties string signedPropertiesDigestValue = CalculateDigestValue(signedPropertiesXml); // Prepare the SignedInfo string signedInfoXml = @"<ds:SignedInfo> <ds:CanonicalizationMethod Algorithm=""http://www.w3.org/2006/12/xml-c14n11"" /> <ds:SignatureMethod Algorithm=""http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"" /> <ds:Reference Id=""invoiceSignedData"" URI=""""> <ds:Transforms> <ds:Transform Algorithm=""http://www.w3.org/TR/1999/REC-xpath-19991116""> <ds:XPath>not(//ancestor-or-self::ext:UBLExtensions)</ds:XPath> </ds:Transform> <ds:Transform Algorithm=""http://www.w3.org/TR/1999/REC-xpath-19991116""> <ds:XPath>not(//ancestor-or-self::cac:Signature)</ds:XPath> </ds:Transform> <ds:Transform Algorithm=""http://www.w3.org/TR/1999/REC-xpath-19991116""> <ds:XPath>not(//ancestor-or-self::cac:AdditionalDocumentReference[cbc:ID='QR'])</ds:XPath> </ds:Transform> <ds:Transform Algorithm=""http://www.w3.org/2006/12/xml-c14n11"" /> </ds:Transforms> <ds:DigestMethod Algorithm=""http://www.w3.org/2001/04/xmlenc#sha256"" /> <ds:DigestValue>sZwHJNRoZHatRyJ31IHiwE8og5xfcCOwewQV/FQz09Y=</ds:DigestValue> </ds:Reference> <ds:Reference Type=""http://www.w3.org/2000/09/xmldsig#SignatureProperties"" URI=""#xadesSignedProperties""> <ds:DigestMethod Algorithm=""http://www.w3.org/2001/04/xmlenc#sha256"" /> <ds:DigestValue>" + signedPropertiesDigestValue + @"</ds:DigestValue> </ds:Reference> </ds:SignedInfo>"; // Calculate the digest value of the SignedInfo string signedInfoDigestValue = CalculateDigestValue(signedInfoXml); // Sign the digest value of SignedInfo string signatureValue = SignDataWithECPrivateKey(Encoding.UTF8.GetBytes(signedInfoDigestValue), certificate.GetRSACngPrivateKey()); // Update the SignatureValue node in the XML xmlDoc.SelectSingleNode("//ds:SignatureValue", xmlDoc.GetNamespaceManager()).InnerText = signatureValue; } private X509Certificate2 LoadCertificateFromPem(string certificatePem) { // Load the certificate from PEM format X509Certificate2 certificate = new X509Certificate2(); using (TextReader textReader = new StringReader(certificatePem)) { PemReader pemReader = new PemReader(textReader); X509Certificate x509Certificate = (X509Certificate)pemReader.ReadObject(); certificate.Import(x509Certificate.GetEncoded()); } return certificate; } private string CalculateDigestValue(string inputXml) { using (SHA256 sha256 = SHA256.Create()) { byte[] inputBytes = Encoding.UTF8.GetBytes(inputXml); byte[] hashBytes = sha256.ComputeHash(inputBytes); string hashHex = BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); return Convert.ToBase64String(Encoding.UTF8.GetBytes(hashHex)); } } private string SignDataWithECPrivateKey(byte[] data, CngKey privateKey) { ECDsaCng ecdsa = new ECDsaCng(privateKey); byte[] signature = ecdsa.SignData(data, HashAlgorithmName.SHA256); ecdsa.Clear(); return Convert.ToBase64String(signature); } private X509Certificate2 GetCertificate() { // Load your certificate from a secure location return certificate; } }
此代码提供了一个 ZatcaInvoiceSigner 类,可以按照 ZATCA 要求对 XML 文档进行签名。将示例 XML、GetCertificate() 方法和其他占位符替换为您的实际 XML 和证书信息。

请注意,您需要安装 Org.BouncyCastle.Crypto 和 Org.BouncyCastle.OpenSsl NuGet 包才能从 PEM 格式加载证书。确保您的证书对于根据 ZATCA 的要求签署发票有效。此外,该代码假定可以使用certificate.GetRSACngPrivateKey() 访问您的证书的私钥。如果您的证书使用不同的密钥存储,您可能需要相应地调整代码。

© www.soinside.com 2019 - 2024. All rights reserved.