هذه الوثيقة موجَّهة للمطوّرين ومهندسي التكامل الذين يبنون أو يدقّقون توليد الفاتورة الإلكترونية وفق متطلبات هيئة الزكاة والضريبة والجمارك (ZATCA). نتناول هنا المواصفة التقنية للتوقيع الرقمي على مستوى الخوارزمية والحقل: أي معاملات التوقيع التي تفرضها الهيئة بالضبط، وبنية عناصر ds:SignedInfo وds:Reference وds:Transforms، وخصائص XAdES الموقَّعة، والقيم البايتية لمعرّفات الخوارزميات.
قبل المتابعة، ميّز بين ثلاث وثائق مترابطة لا متطابقة. الوثيقة المفاهيمية ما هو التوقيع الرقمي ولماذا تطلبه الهيئة تشرح فكرة التشفير غير المتماثل ودالة الهاش نظريًا. ووثيقة المواصفة التقنية للختم التشفيري تشرح مكان عنصر التوقيع داخل ملف XML وكيف يُغلَّف ضمن UBLExtensions. أما هذه الوثيقة فتغطّي طبقة مختلفة: معاملات الخوارزمية ذاتها وخصائص التوقيع الموقَّعة، أي ماذا تُوقّع وبأي منحنى وأي دالة هاش وأي معرّف تقنين، بايتًا ببايت.
نطاق هذه الوثيقة: المواصفة لا الفكرة ولا الموضع
المعمارية التي تفرضها الهيئة للفاتورة الإلكترونية مبنية على معيار UBL 2.1 للفاتورة، ومعيار XML Digital Signature (اختصارًا XMLDSig) للتوقيع، وملف تعريف XAdES لإضافة الخصائص الموقَّعة. التوقيع الذي نتحدث عنه هنا هو توقيع المنشأة على الفاتورة، ويُسمّى في وثائق الهيئة الختم التشفيري للفاتورة. لكن السؤال الذي تجيب عنه هذه الوثيقة ليس أين يقع هذا التوقيع، بل بأي خوارزمية يُنتَج وبأي معاملات.
الفرق عملي وليس أكاديميًا. لو نسخت بنية XML الصحيحة من وثيقة الموضع لكنك وقّعت بمنحنى خاطئ أو دالة هاش غير مطابقة، سيرفض محرّك التحقق لدى الهيئة الفاتورة رغم أن شكلها سليم. المواصفة هنا تحسم تلك المعاملات.
نفترض أنك تتعامل مع شهادة الختم التشفيري الخاصة بالمنشأة. تفاصيل إصدار تلك الشهادة وتسجيلها تجدها في معرّف الختم التشفيري للامتثال (CSID). باختصار: الشهادة تحمل المفتاح العام للمنشأة، والمفتاح الخاص المقابل هو ما يُجري عملية التوقيع التي نصفها أدناه.
لماذا نفصل المواصفة عن المفهوم والموضع
قد يبدو الفصل بين ثلاث وثائق ترفًا توثيقيًا، لكنه يعكس ثلاث مسؤوليات مختلفة في فريق التطوير. مهندس الأمان يهتم بالمفهوم: لماذا التوقيع الرقمي يضمن سلامة الفاتورة وعدم إنكار مُصدِرها. ومهندس التكامل يهتم بالموضع: أين يضع عنصر التوقيع داخل بنية UBL حتى يقبله المخطط (schema). أما مطوّر التشفير فيهتم بالمواصفة: بأي خوارزمية ومنحنى ومعرّف يُنتَج التوقيع فعلًا حتى يجتاز التحقق.
الخلط بين هذه الطبقات هو مصدر معظم حالات الفشل. فريق ينسخ بنية XML صحيحة من مرجع، ويضعها في الموضع الصحيح، لكنه يوقّع بمعاملات خاطئة، فيحصل على فاتورة سليمة الشكل مرفوضة المضمون. هذه الوثيقة تعزل الطبقة الأخيرة وتحسمها، فلا يبقى مجال للاجتهاد في المعاملات التي يقرأها محرّك الهيئة حرفيًا.
نبدأ من الخوارزمية، لأنها أساس كل ما يليها. كل معرّف خوارزمية، وكل عنصر بصمة، وكل خاصية موقَّعة، إنما يخدم في النهاية عملية توقيع واحدة بمعاملات محدّدة. فهم تلك المعاملات أولًا يجعل بقية البنية مفهومة لا محفوظة.
الخوارزمية المعتمدة: ECDSA على المنحنى secp256k1
تفرض الهيئة خوارزمية توقيع رقمي قائمة على التشفير بالمنحنيات الإهليلجية. الخوارزمية هي ECDSA (اختصار Elliptic Curve Digital Signature Algorithm)، ودالة الهاش المصاحبة هي SHA-256. المنحنى الإهليلجي المعتمد هو secp256k1، وهو المنحنى نفسه المستخدم في تطبيقات تشفيرية واسعة الانتشار.
هذا الاختيار ليس تفصيلًا عابرًا. زوج المفاتيح في شهادة المنشأة يجب أن يكون مولَّدًا على المنحنى secp256k1 تحديدًا. لو ولّدت الشهادة بزوج مفاتيح RSA أو بمنحنى إهليلجي مختلف مثل secp256r1، فلن تتوافق الشهادة مع متطلبات الفوترة الإلكترونية، وسيفشل التحقق.
طول التوقيع الناتج عن ECDSA على هذا المنحنى أقصر بكثير من توقيع RSA بمستوى أمان مكافئ، وهذا أحد أسباب اعتماد المنحنيات الإهليلجية. لكن من منظور التطبيق، ما يهمّك هو ضبط مكتبة التشفير لديك على هذه المعاملات الثلاثة معًا: ECDSA للتوقيع، SHA-256 للهاش، وsecp256k1 للمنحنى.
يفيد هنا توضيح الفرق بين خوارزمية التوقيع والمنحنى ودالة الهاش، لأن المطوّرين كثيرًا ما يدمجونها خطأً في معامل واحد. ECDSA هي بروتوكول التوقيع الذي يحدّد كيف يُحوَّل المفتاح الخاص وبصمة الرسالة إلى توقيع رياضي. والمنحنى secp256k1 هو الفضاء الرياضي الذي تجري فوقه عمليات ECDSA، ويحدّد طول المفتاح وقوته. ودالة الهاش SHA-256 تنتج البصمة التي تُوقَّع. تغيير أي من الثلاثة يغيّر الناتج كليًا، ولذلك تثبّتها الهيئة جميعًا لا أحدها.
عمليًا، عند توليد زوج المفاتيح للشهادة، تختار أداة التوليد المنحنى. وعند بناء عنصر التوقيع، تختار المكتبة خوارزمية التوقيع والهاش عبر معرّف SignatureMethod. لو ضبطت الأول ونسيت الثاني، أو العكس، تنشأ فجوة بين المنحنى المستخدم في الشهادة وبين الخوارزمية المعلنة في XML، فيرفض المحرّك التحقق. الضبط المتّسق للطرفين شرط أساسي لنجاح التكامل.
خوارزمية التوقيع: ECDSA
المنحنى: secp256k1
دالة التجزئة: SHA-256
التوحيد القياسي: C14N 1.1
معرّفات الخوارزميات كما تظهر في XML
كل خوارزمية في معيار XMLDSig تُعرَّف عبر معرّف موحّد (URI) يُكتب صراحةً في الـ XML. هذه المعرّفات ليست اختيارية ولا قابلة للاجتهاد. محرّك التحقق يقرأها حرفيًا ليعرف بأي خوارزمية يتحقق. القيم المطلوبة:
المعرّف الأول يربط التوقيع بخوارزمية ECDSA مع SHA-256. المعرّف الثاني يحدّد دالة الهاش المستخدمة في حساب البصمة. المعرّف الثالث يحدّد طريقة التقنين، وهو الموضوع الأخطر في كل المواصفة كما نوضّح لاحقًا.
الفرق بين التوقيع والبصمة في هذه المواصفة
يخلط كثير من المطوّرين بين دالة الهاش وخوارزمية التوقيع، رغم أنهما يؤديان دورين مختلفين تمامًا في هذه المواصفة. دالة الهاش SHA-256 تنتج بصمة ثابتة الطول من أي مدخل مهما كبر، وهي عملية باتجاه واحد لا يمكن عكسها، ولا تستخدم مفتاحًا. أما خوارزمية التوقيع ECDSA فتستخدم المفتاح الخاص للمنشأة لإنتاج قيمة لا يقدر عليها إلا من يملك ذلك المفتاح، ويمكن التحقق منها بالمفتاح العام المقابل.
الترتيب في المواصفة دقيق: تُحسب بصمة SHA-256 أولًا للفاتورة وللخصائص الموقَّعة، ثم تُجمَّع هاتان البصمتان داخل ds:SignedInfo، ثم يُوقَّع ds:SignedInfo نفسه بـECDSA. أي أن التوقيع لا يطال محتوى الفاتورة الضخم مباشرة، بل يطال عنصرًا صغيرًا يحوي بصماته. هذا التصميم معياري في XMLDSig، وغرضه الكفاءة: توقيع عنصر صغير أسرع من توقيع مستند كامل، مع الحفاظ على الأمان لأن أي تغيير في المحتوى يغيّر بصمته فيغيّر ds:SignedInfo فيكسر التوقيع.
لهذا تظهر SHA-256 في موضعين منفصلين: داخل DigestMethod لكل ds:Reference (لحساب البصمات)، وداخل معرّف SignatureMethod المركّب ecdsa-sha256 (لأن ECDSA نفسها تجزّئ ds:SignedInfo المقنَّن قبل توقيعه). الدالة واحدة لكن الدور مختلف في كل موضع. الوعي بهذا التمييز يفسّر لماذا نرى SHA-256 أربع مرات أو أكثر في فاتورة واحدة دون تكرار خاطئ.
نوع التوقيع: enveloped مع مرجعين
توقيع الفاتورة في هذه المواصفة من نوع التوقيع المُغلَّف (enveloped signature): عنصر التوقيع يعيش داخل المستند الذي يوقّعه، لا منفصلًا عنه. ولأنه كذلك، نشأت معضلة الدوران التي حلّتها التحويلات: التوقيع جزء من الفاتورة، لكنه لا يستطيع أن يوقّع نفسه، فوجب استبعاد منطقته من حساب البصمة عبر ds:Transforms.
تصميم المرجعين (ds:Reference مرتين) يفصل ما يُحمى. المرجع الأول يحمي محتوى الفاتورة الفعلي: الأطراف، البنود، الضريبة، الإجماليات. والمرجع الثاني يحمي بيانات التوقيع نفسها: وقته وشهادته. لو اكتفينا بمرجع واحد على الفاتورة، لأمكن لمهاجم تغيير وقت التوقيع أو الإشارة إلى شهادة أخرى دون كسر التوقيع. فصل المرجعين يسدّ هذه الثغرة، ويجعل بيانات XAdES محمية بالقوة نفسها التي تحمي بها الفاتورة.
هذا الفهم يفسّر ترتيب الخطوات عند التحقق: المحرّك لا يكتفي بالتأكد من توقيع ds:SignedInfo، بل يعيد حساب بصمتي المرجعين ويقارنهما بالقيم المخزّنة. توقيع صحيح على ds:SignedInfo يحمل بصمتين مزيّفتين يظل مرفوضًا، لأن المقارنة في الخطوتين الثالثة والرابعة من تسلسل التحقق ستكشف التزييف.
بنية ds:SignedInfo: ما الذي يُوقَّع فعلًا
عنصر ds:SignedInfo هو قلب التوقيع. القاعدة التي يخطئ فيها كثيرون: التوقيع الرقمي لا يُطبَّق على الفاتورة مباشرة، بل يُطبَّق على عنصر ds:SignedInfo بعد تقنينه. وهذا العنصر بدوره يحتوي على بصمات (digests) لما تريد حمايته. أي أن التوقيع يحمي البصمات، والبصمات تحمي المحتوى.
يتكوّن ds:SignedInfo من ثلاثة أجزاء بالترتيب: طريقة التقنين، ثم طريقة التوقيع، ثم عنصر أو أكثر من ds:Reference. إليك بنيته المطلوبة:
لاحظ وجود عنصرَي ds:Reference. الأول يشير إلى الفاتورة كلها (قيمة URI فارغة تعني الجذر) بعد استبعاد ما لا يجب أن يدخل في الحساب. والثاني يشير إلى الخصائص الموقَّعة عبر URI="#xadesSignedProperties". كلاهما يحمل بصمة SHA-256 مخزّنة في ds:DigestValue بترميز Base64.
ترتيب العناصر إلزامي
ترتيب العناصر داخل ds:SignedInfo ليس حرًا. يجب أن تأتي CanonicalizationMethod أولًا، ثم SignatureMethod، ثم عناصر ds:Reference. أي تبديل في الترتيب يغيّر التمثيل المقنَّن للعنصر، وبالتالي يغيّر البصمة، فيفشل التحقق. هذه نقطة يخطئ فيها من يبني الـ XML يدويًا بدل توليده آليًا.
ds:Transforms: التحويلات قبل حساب البصمة
قبل حساب بصمة الفاتورة في ds:Reference الأول، تمرّ الفاتورة عبر سلسلة تحويلات (ds:Transforms). الغرض من هذه التحويلات استبعاد ثلاثة أجزاء يجب ألا تدخل في حساب البصمة، لأنها إما تحتوي التوقيع نفسه أو تُحسب لاحقًا.
التحويلات الثلاثة الأولى من نوع XPath، ووظيفتها الاستبعاد عبر تعبير not(...):
- استبعاد
UBLExtensions: هذا الجزء يحتوي عنصر التوقيع ذاته. لو دخل في حساب البصمة لاستحال التوقيع، لأنك ستحتاج البصمة لتوقّع، وتحتاج التوقيع لتحسب البصمة. - استبعاد
cac:Signature: عنصر مرجعي للتوقيع داخل جسم الفاتورة، يُستبعد للسبب نفسه. - استبعاد مرجع رمز
QR: عنصرAdditionalDocumentReferenceالذي معرّفهQRيُحسب بعد التوقيع، لأن رمز الاستجابة السريعة يتضمّن قيمة التوقيع نفسها.
التحويل الرابع والأخير من نوع التقنين (c14n11)، ويأتي بعد الاستبعادات الثلاثة ليحوّل الناتج إلى صورته المقنَّنة قبل حساب البصمة.
استبعاد UBLExtensions
استبعاد عنصر التوقيع
استبعاد مرجع QR
التوحيد القياسي C14N ← التجزئة
التقنين (Canonicalization) ولماذا هو أخطر خطوة
التقنين هو تحويل مستند XML إلى صورة بايتية موحّدة لا لبس فيها. السبب أن المستند الواحد يمكن كتابته بطرق متعددة متكافئة منطقيًا لكنها مختلفة بايتيًا: مسافة بادئة هنا، سطر فارغ هناك، ترتيب مختلف للسمات (attributes)، أو طريقة مختلفة لإعلان مساحات الأسماء (namespaces). كل اختلاف بايتي ينتج بصمة SHA-256 مختلفة، فيفشل التحقق.
الهيئة تفرض خوارزمية تقنين محدّدة هي Canonical XML 1.1، ومعرّفها http://www.w3.org/2006/12/xml-c14n11. هذه الخوارزمية تحسم كل مصادر الاختلاف: ترتّب السمات أبجديًا، توحّد ترميز المسافات، تنشر إعلانات مساحات الأسماء بطريقة ثابتة، وتزيل الفروق غير الدلالية.
القاعدة الحاسمة: التوقيع يُطبَّق على الصورة المقنَّنة من ds:SignedInfo، لا على نصّه كما كتبته. لذلك أي معالجة لاحقة على الـ XML بعد التوقيع، مهما بدت بريئة، قد تكسر التوقيع إن غيّرت الصورة البايتية. من الأخطاء الشائعة إعادة تنسيق الـ XML أو إضافة مسافات بادئة للقراءة بعد التوقيع، فينهار التحقق.
لماذا التقنين قبل التوقيع وقبل البصمة معًا
التقنين يحدث في موضعين: مرة على الفاتورة قبل حساب بصمتها (التحويل الرابع في ds:Transforms)، ومرة على ds:SignedInfo قبل التوقيع عليه (عبر CanonicalizationMethod). الموضعان يستخدمان الخوارزمية نفسها c14n11. الفهم الصحيح لهذه الازدواجية هو ما يفرّق بين تطبيق يمرّ من التحقق وآخر يفشل بلا سبب ظاهر.
خصائص XAdES الموقَّعة (SignedSignatureProperties)
معيار XMLDSig وحده لا يكفي لمتطلبات الهيئة. تُضاف فوقه طبقة XAdES (اختصار XML Advanced Electronic Signatures) التي تربط بالتوقيع بيانات إضافية موقَّعة، أهمّها وقت التوقيع وبصمة شهادة الموقّع. هذه البيانات ليست خارج التوقيع، بل داخله: فالـ ds:Reference الثاني في ds:SignedInfo يحسب بصمتها، فتصبح محمية بالتوقيع نفسها.
العنصر الجوهري هو xades:SignedSignatureProperties، ويحوي حقلين إلزاميين:
الحقل الأول SigningTime هو طابع زمني بصيغة ISO 8601 بالتوقيت العالمي المنسّق (UTC)، يثبت لحظة التوقيع. والحقل الثاني SigningCertificate يحمل بصمة SHA-256 لشهادة المنشأة (لا الشهادة كاملة)، مع اسم الجهة المُصدِرة والرقم التسلسلي. هكذا يثبت التوقيع أنه أُنتج بهذه الشهادة بعينها، فلا يمكن استبدالها لاحقًا دون كسر التوقيع.
قيمة Id="xadesSignedProperties" ليست عشوائية. هي بالضبط الهدف الذي يشير إليه ds:Reference الثاني عبر URI="#xadesSignedProperties". أي خطأ إملائي في هذا المعرّف يقطع الربط بين الإشارة والهدف، فتفشل البصمة.
قيمة التوقيع ds:SignatureValue والمفتاح العام ds:KeyInfo
بعد تقنين ds:SignedInfo وتوقيعه بالمفتاح الخاص للمنشأة عبر ECDSA مع SHA-256، تُخزَّن النتيجة في عنصر ds:SignatureValue بترميز Base64. هذه هي قيمة التوقيع الفعلية: ناتج خوارزمية ECDSA المكوَّن من زوج القيمتين (r) و(s) بعد ترميزه.
عنصر ds:KeyInfo يحمل الشهادة العامة للمنشأة بصيغة X.509. هذه الشهادة تحتوي المفتاح العام الذي يستخدمه محرّك الهيئة للتحقق من قيمة التوقيع. ولأن خصائص XAdES سبق أن خزّنت بصمة هذه الشهادة داخل المنطقة الموقَّعة، فإن أي محاولة لاستبدال الشهادة في ds:KeyInfo ستتعارض مع البصمة المحفوظة، فينكشف التلاعب.
الترميز: Base64 وصيغة X.509 وترتيب البايتات
تظهر في المواصفة قيم ثنائية لا يمكن وضعها كما هي داخل نص XML: قيمة التوقيع، والبصمات، والشهادة. الحل المعتمد هو ترميز Base64، الذي يحوّل البيانات الثنائية إلى محارف نصية آمنة داخل XML. كل قيمة ds:DigestValue وقيمة ds:SignatureValue والشهادة في ds:X509Certificate تُكتب بهذا الترميز.
الشهادة العامة تُخزَّن بصيغة X.509، وهي الصيغة المعيارية للشهادات الرقمية، مرمّزة بـBase64 داخل ds:X509Certificate. تتضمّن الشهادة المفتاح العام للمنشأة، ومعلومات الجهة المُصدِرة، والرقم التسلسلي، وصلاحية الشهادة. ولأن خصائص XAdES خزّنت بصمة هذه الشهادة في منطقة موقَّعة، فإن المفتاح العام المستخدم في التحقق مثبَّت ولا يقبل الاستبدال.
نقطة دقيقة في ترميز ناتج ECDSA: الخوارزمية تنتج قيمتين عدديتين (r) و(s)، ويجب تجميعهما بصيغة متّسقة قبل ترميزهما بـBase64. اختلاف صيغة التجميع بين مكتبتك ومحرّك التحقق يفشل المطابقة رغم صحة التوقيع رياضيًا. لذلك من المهم استخدام مكتبة تطبّق المواصفة بالصيغة المتوقَّعة، لا أن تجمّع القيمتين بطريقتك الخاصة.
يضاف إلى ذلك ضبط مساحات الأسماء (namespaces). البادئات ds: وxades: وext: وcac: وcbc: ليست تجميلية، بل تربط كل عنصر بمساحة اسمه الصحيحة. التقنين c14n11 ينشر هذه الإعلانات بطريقة ثابتة، لكن سوء إعلانها قبل التقنين قد ينتج صورة مقنَّنة مختلفة عمّا يتوقّعه المحرّك. الضبط الصحيح لمساحات الأسماء جزء لا يتجزّأ من المواصفة وإن بدا تفصيلًا نحويًا.
تسلسل التحقق عند الهيئة خطوة بخطوة
لفهم لماذا تُرتَّب العناصر بهذه الدقة، تتبّع ما يفعله محرّك التحقق لدى الهيئة عند استقبال الفاتورة:
- يقرأ
ds:KeyInfoليستخرج شهادة المنشأة ومفتاحها العام. - يقنّن
ds:SignedInfoبخوارزميةc14n11، ثم يتحقق من قيمةds:SignatureValueباستخدام المفتاح العام وECDSAمعSHA-256. لو فشل هنا، فالتوقيع غير صالح أو العنصر عُدِّل بعد التوقيع. - يعيد حساب بصمة الفاتورة بعد تطبيق التحويلات الأربع، ويقارنها بقيمة
ds:DigestValueفي المرجع الأول. لو اختلفت، فالفاتورة عُدِّلت. - يعيد حساب بصمة
xades:SignedPropertiesويقارنها بقيمةds:DigestValueفي المرجع الثاني. لو اختلفت، فالخصائص عُدِّلت. - يطابق بصمة الشهادة المخزّنة في
xades:CertDigestمع الشهادة الواردة فيds:KeyInfo. لو اختلفت، فالشهادة استُبدِلت.
أي إخفاق في أي خطوة يرفض الفاتورة. لذلك سلامة الشكل وحدها لا تكفي: لا بد من تطابق الخوارزميات والمعرّفات والتقنين بايتًا ببايت.
قراءة الشهادة من KeyInfo
التحقق من SignatureValue
إعادة حساب تجزئة الفاتورة
مطابقة تجزئة الخصائص الموقّعة
الأخطاء الشائعة التي تفشل التحقق
من واقع تكامل الفوترة الإلكترونية، تتكرّر أخطاء بعينها رغم أن الـ XML يبدو سليمًا:
- منحنى خاطئ: توليد زوج المفاتيح على
secp256r1بدلsecp256k1. الشهادة تبدو صالحة لكنها لا تطابق المواصفة. - معرّف خوارزمية غير مطابق: كتابة
rsa-sha256أو معرّف هاش مختلف فيSignatureMethod. المحرّك يقرأ المعرّف حرفيًا. - إعادة تنسيق بعد التوقيع: إضافة مسافات بادئة أو إعادة ترتيب السمات على الـ XML بعد التوقيع. الصورة البايتية تتغيّر فتنهار البصمة.
- خلل في الاستبعادات: نسيان استبعاد
UBLExtensionsأو مرجعQRضمنds:Transforms، فتدخل أجزاء يجب ألا تدخل في حساب البصمة. - معرّف خصائص غير متطابق: اختلاف بين قيمة
IdفيSignedSignaturePropertiesوبينURIفيds:Referenceالثاني.
القاعدة العملية: لا تحرّر الـ XML الموقَّع يدويًا أبدًا، ولا تطبّق عليه أي تحويل بعد التوقيع. ولّده آليًا بمكتبة تطبّق المواصفة كاملة، ثم لا تلمسه.
توصيات عملية لفريق التكامل
إذا كنت تبني التكامل بنفسك، فابدأ بأداة التحقق التي توفّرها الهيئة قبل الإرسال الفعلي. هذه الأداة تطبّق تسلسل التحقق نفسه الذي وصفناه، وترجع رسائل خطأ تشير إلى الخطوة التي أخفقت. رسالة فشل عند التحقق من ds:SignatureValue تعني خطأً في الخوارزمية أو في الصورة المقنَّنة. ورسالة فشل في مطابقة بصمة الفاتورة تعني خللًا في التحويلات أو تعديلًا بعد التوقيع.
اختبر أولًا فاتورة بسيطة بأقل عدد من البنود قبل الانتقال إلى الحالات المعقّدة، ليسهل عزل سبب الفشل. ثبّت بيئة التوليد بحيث لا تتدخّل أي طبقة وسيطة (مثل خوادم وكيلة أو محرّكات تنسيق) في الـ XML بعد توقيعه. وسجّل الصورة المقنَّنة من ds:SignedInfo أثناء التطوير لمقارنتها بما يحسبه المحرّك عند الفشل، فهذا غالبًا أسرع طريق لاكتشاف اختلاف بايتي خفي.
راقب أيضًا صلاحية شهادة الختم التشفيري. شهادة منتهية أو غير مسجّلة لدى الهيئة تنتج توقيعًا صحيحًا رياضيًا لكنه مرفوض عند التحقق من سلسلة الثقة. إدارة دورة حياة الشهادة، من الإصدار إلى التجديد، جزء من نجاح التكامل لا يقلّ أهمية عن صحة الخوارزمية.
الخيار الأبسط لمعظم المنشآت هو ألا تبني هذه الطبقة بنفسها أصلًا، بل تستخدم نظامًا يتولّاها. عندها ينحصر دورك في تسجيل الشهادة لدى الهيئة، وتترك توليد XML والتوقيع والتقنين وإدارة الشهادة للنظام.
دع قيود يتولّى التوقيع الرقمي بدلًا عنك
يولّد قيود فواتيرك بصيغة UBL ويوقّعها بخوارزمية ECDSA المعتمدة ويدير شهادة الختم التشفيري تلقائيًا، فتتوافق مع الهيئة دون أن تكتب سطر XML واحدًا.
دور قيود في التوقيع الرقمي
كل ما وصفناه أعلاه يجري آليًا داخل قيود. عند إصدار الفاتورة، يولّد قيود ملف XML وفق معيار UBL 2.1، ويبني عنصر ds:Signature بكامل بنيته، ويطبّق التحويلات والتقنين، ويوقّع ds:SignedInfo بخوارزمية ECDSA مع SHA-256 على المنحنى secp256k1، ويضيف خصائص XAdES الموقَّعة. كما يدير قيود شهادة الختم التشفيري للامتثال (CSID) تلقائيًا، ويخزّن سلسلة بصمات الفواتير لأغراض التحقق.
ما يبقى على المنشأة هو تسجيل شهادتها لدى الهيئة، وهي خطوة يرشدك قيود خلالها. قيود لا يتولّى التسجيل نيابةً عنك، ولا تحتاج أنت إلى صياغة الـ XML يدويًا. اقرأ تفاصيل الشهادة في معرّف الختم التشفيري للامتثال (CSID)، وتفاصيل موضع التوقيع داخل الملف في المواصفة التقنية للختم التشفيري.
الأسئلة الشائعة
ما الفرق بين هذه الوثيقة ووثيقة المواصفة التقنية للختم التشفيري؟
وثيقة الختم التشفيري تشرح أين يعيش عنصر التوقيع داخل ملف XML وكيف يُغلَّف في UBLExtensions. هذه الوثيقة تشرح بأي خوارزمية ومعاملات يُنتَج التوقيع: ECDSA، secp256k1، SHA-256، وخصائص XAdES الموقَّعة.
لماذا secp256k1 تحديدًا وليس secp256r1؟
لأن الهيئة تفرض هذا المنحنى في مواصفتها. زوج مفاتيح الشهادة يجب أن يُولَّد عليه، وأي منحنى آخر ينتج شهادة غير متوافقة يرفضها التحقق.
هل يمكنني تنسيق ملف XML الموقَّع لتسهيل قراءته؟
لا. أي إعادة تنسيق بعد التوقيع تغيّر الصورة البايتية المقنَّنة، فتنهار البصمة ويفشل التحقق. ولّد الملف آليًا ولا تحرّره بعد التوقيع.
ماذا يحدث لو دخلت UBLExtensions في حساب البصمة؟
يستحيل إنتاج التوقيع، لأنك ستحتاج البصمة لتوقّع، وتحتاج التوقيع الموجود داخل UBLExtensions لتحسب البصمة. لذلك تستبعدها التحويلات في ds:Transforms.
أين تُخزَّن قيمة التوقيع الفعلية؟
في عنصر ds:SignatureValue بترميز Base64، وهي ناتج خوارزمية ECDSA المطبَّقة على ds:SignedInfo بعد تقنينه.
هل يولّد قيود هذا التوقيع تلقائيًا؟
نعم. يبني قيود عنصر ds:Signature كاملًا ويوقّعه بالمعاملات المعتمدة ويدير شهادة CSID تلقائيًا، فلا تحتاج إلى كتابة XML يدويًا.
للصورة الكاملة عن الفوترة الإلكترونية ومتطلباتها، راجع صفحة الفاتورة الإلكترونية من قيود.