لنفرض أن شركة ناشئة تبني منصة لإصدار بطاقات مسبقة الدفع بالشراكة مع بنك راع، والمنصة هي التي تستقبل طلبات التفويض من الشبكة وتقرر فيها. وفي أول مراجعة للتصميم مع فريق أمن المعلومات في البنك، عرض أحد المطورين فكرة تبدو بسيطة: يصل الرقم السري مشفراً في الحقل 52، فيفكه سيرفر التطبيق بمفتاح محفوظ في مدير الأسرار، ويقارنه بالرقم المحفوظ للعميل، ثم يقرر. فأوقف مسؤول الأمن العرض عند هذه الشريحة وقال جملة واحدة: «الرقم السري لا يظهر واضحاً في أي سيرفر عندكم، ولا لحظة واحدة، ولا في الذاكرة». ثم أعطى الفريق قائمة: وحدة أمن عتاد HSM للدفع، ومفاتيح تدخل بمكونات يحملها أكثر من شخص، ورمز تحقق لكل مفتاح، وكتل مفاتيح TR-31 للتبادل مع الشريك، وخطة للانتقال إلى صيغة الكتلة 4 وAES.
والفريق لم يكن يعرف نصف هذه المصطلحات. ولهذا قرر قبل أي عرض أسعار أن يبني بيئة تجريبية يفهم فيها كل بند بيده: SoftHSM2 كوحدة تشفير برمجية تتكلم معيار PKCS#11 مثل وحدات العتاد، وكتل الرقم السري يحسبها بنفسه ويقارنها بمثال منشور من شبكة بطاقات، وكتلة مفتاح TR-31 يحاول أن يعبث بها. وقد ذكرنا في مقال الصراف الآلي أن لوحة EPP تشفر الرقم داخلها، وفي مقال الشريحة EMV أن مفتاح المصدر الرئيسي يعيش في HSM البنك، وهذا المقال يفتح ذلك الصندوق. وسوف نناقش ما يلي:
- لماذا يحتاج الرقم السري معاملة خاصة، وما وحدة أمن العتاد HSM، والفرق بين HSM عام وHSM للدفع.
- صيغ كتلة الرقم السري PIN Block في ISO 9564، وتجربة تعيد مثال Mastercard المنشور حتى آخر خانة داخل SoftHSM.
- مكونات المفتاح ورمز التحقق KCV وخصائص المفتاح في PKCS#11، ومشكلة وجدناها في الإعداد الافتراضي.
- ترجمة الرقم السري بين المفاتيح، ولماذا لا تكفي أوامر PKCS#11 العامة لها.
- هرم المفاتيح وDUKPT، والصيغة 4 والانتقال إلى AES، وكتل المفاتيح TR-31.
- حالة موثقة، وكيف يبدو هذا في مؤسسة حقيقية، والأخطاء الشائعة، وقائمة تحقق.
لماذا لا يعامل الرقم السري ككلمة مرور؟
في مقال التشفير للمطورين رأينا أن كلمة المرور تحفظ بهاش بطيء مع ملح، ولا تشفر. فلماذا لا نفعل الشيء نفسه مع الرقم السري؟ المشكلة في الطول: الرقم السري أربعة أرقام إلى ستة غالباً، أي عشرة آلاف احتمال إلى مليون فقط، ولا يوجد هاش بطيء يجعل تجربة عشرة آلاف احتمال صعبة. ويشرح الملحق المعلوماتي من PCI SSC عن تطبيق الصيغة 4 (سبتمبر 2021) الخطر بوضوح: لو شفر الرقم وحده، لاستطاع المهاجم أن يبني جدولاً بالأرقام المشفرة تحت مفتاح لا يعرفه، يكفيه أن يرى الرسائل المشفرة وأن يجرب أرقاماً مختلفة، ثم يقرأ أي رقم مستقبلي من الجدول مباشرة، ولو حصل على عدة رسائل يعرف أرقامها لامتلك ما يكفي لمهاجمة المفتاح نفسه.
ولهذا لا يشفر الرقم وحده أبداً، بل يوضع أولاً في كتلة PIN Block تخلطه بشيء آخر، غالباً رقم الحساب. ويقتبس الملحق نفسه من ISO 9564-1 القاعدة التي تحكم ذلك: يجب أن يضمن التشفير ألا يعطي الرقم نفسه تحت المفتاح نفسه قيمة مشفرة واحدة يمكن توقعها حين يرتبط بحسابات مختلفة. أي أن عميلين رقمهما السري 1234 يجب أن تختلف كتلتاهما المشفرتان، وإلا فإن من يعرف رقم أحدهما يعرف رقم الآخر.
والقاعدة الثانية أهم للمهندس: أين يظهر الرقم واضحاً؟ والجواب في المعيار: داخل جهاز تشفير آمن Secure Cryptographic Device فقط، واختصاراً SCD. ويذكر الملحق أن ISO 9564-1 يطلب أن يحدث إنشاء الكتلة وفكها داخل SCD، وأن معيار أمن الرقم السري من PCI يطلب أن تحدث كل ترجمة للرقم، من واضح إلى مشفر، ومن مشفر بمفتاح إلى مشفر بآخر، ومن مشفر إلى واضح، داخل SCD. وهذا بالضبط ما قاله مسؤول الأمن في القصة، والصورة التالية تبين أين تحدث هذه الترجمات في عملية واحدة:

ما وحدة أمن العتاد HSM؟
وحدة أمن العتاد Hardware Security Module جهاز مخصص يحفظ المفاتيح وينفذ العمليات بها داخله، فالتطبيق يرسل إليه بيانات وأمراً، «شفر هذه الكتلة بالمفتاح الفلاني»، ويعود إليه الناتج، والمفتاح نفسه لا يخرج. والجهاز مصمم ليقاوم العبث: إذا فتح غلافه أو تغيرت حرارته أو جهده بشكل غير طبيعي، يمسح مفاتيحه. وهذا ما يجعله جهاز تشفير آمناً بالمعنى الذي تقصده المعايير، لا مجرد سيرفر عليه برنامج تشفير.

وقد يتساءل البعض: إذا كان مدير الأسرار في السحابة يحفظ المفتاح بأمان، فلماذا HSM؟ والإجابة أن مدير الأسرار يعطي التطبيق المفتاح ليستعمله، فيصبح المفتاح في ذاكرة التطبيق، ويصبح كل من يصل إلى ذاكرة السيرفر قادراً على قراءته. أما الـ HSM فلا يعطي المفتاح لأحد، بل يعطي نتيجة استعماله. وقد شرحنا هذا الفرق بين KMS وHSM وتشفير الظرف في مقال التشفير للمطورين، والجديد هنا أن الدفع يحتاج نوعاً خاصاً من HSM.
HSM عام أم HSM للدفع؟
هناك نوعان من وحدات أمن العتاد بحسب الأوامر التي تفهمها. النوع العام يتكلم واجهة معيارية مثل PKCS#11 التي تنشرها OASIS، وأوامرها عامة: شفر، فك، وقع، تحقق، اشتق مفتاحاً، لف مفتاحاً بمفتاح آخر. ويستخدم لشهادات TLS ومفاتيح التوقيع وتشفير قواعد البيانات. أما HSM الدفع فيضيف أوامر خاصة بالبطاقات تعمل كوحدة واحدة داخل الجهاز: ترجم الكتلة من مفتاح إلى آخر، تحقق من الرقم السري مقابل قيمة محفوظة، ولد رمز التحقق CVV، تحقق من رمز الشريحة ARQC وولد ARPC، اشتق مفتاح DUKPT لهذه العملية. والفرق ليس في الراحة، بل في الأمان، كما سنرى بالتجربة: الأمر الواحد «ترجم» لا يظهر الرقم أبداً، أما «فك ثم شفر» فيظهره بين الخطوتين.
وفي المختبر استخدمنا SoftHSM2، وهو مشروع مفتوح المصدر يحفظ المفاتيح في ملفات ويتيحها عبر PKCS#11، فهو HSM عام برمجي للتطوير والاختبار، لا جهاز آمن ولا HSM دفع. واخترناه لأن التطبيق الذي يكلمه هو نفسه الكود الذي سيكلم HSM عاماً حقيقياً، ولأن حدوده بالضبط هي ما يحتاج الفريق أن يراه قبل أن يشتري.
صيغ كتلة الرقم السري في ISO 9564
يذكر ملحق PCI خمس صيغ للكتلة بحسب ISO 9564، والرقم الأول في الكتلة هو رقم الصيغة:
| الصيغة | كيف تبنى | أين تستخدم |
|---|---|---|
| 0 | حقل الرقم XOR حقل الحساب، 64 بت | الأشهر في الأجهزة والشبكات مع 3DES |
| 1 | حقل الرقم مع حقل للعملية، دون الحساب | حين لا يتوفر رقم الحساب |
| 2 | حقل الرقم مع حشو | للبطاقة بالشريحة دون اتصال فقط، ولا تستخدم للتحقق بالاتصال |
| 3 | مثل الصيغة 0 مع حشو مختلف | بديل للصيغة 0 مع 3DES |
| 4 | حقلان من 128 بت للرقم والحساب، وتشفيران | الصيغة الوحيدة التي تدعم AES |
والصيغة 0 هي التي سيراها الفريق في أغلب الأنظمة اليوم، وبناؤها كما يلي: حقل الرقم يبدأ بالرقم 0، ثم طول الرقم، ثم الأرقام، ثم F حتى ست عشرة خانة. وحقل الحساب أربعة أصفار ثم آخر اثني عشر رقماً من رقم البطاقة دون رقم التحقق الأخير. ثم XOR بين الحقلين، فتكون الكتلة الواضحة، ثم تشفر بمفتاح 3DES. ولماذا دون رقم التحقق؟ لأنه محسوب من باقي الأرقام فلا يضيف شيئاً، ولماذا آخر اثني عشر؟ لأنها الجزء الذي يميز الحساب، أما الأرقام الأولى فتعرف البنك المصدر، وهي متشابهة بين بطاقات البنك الواحد.
تجهيز التجربة: SoftHSM2 وPKCS#11 في Container
بنينا Docker Image صغيراً من python:3.13.17-slim فيه حزمتا softhsm2 2.6.1 وopensc 0.26.1 من Debian، والأخيرة لأداة pkcs11-tool التي تقرأ ما في الـ HSM من سطر الأوامر، ومكتبتا python-pkcs11 0.10.1 لنكلم الـ HSM من Python، وpsec 1.3.0، وهي مكتبة بترخيص MIT لتشفير المدفوعات من صاحب مكتبتي pyiso8583 وpyemv، نستخدمها كتنفيذ مستقل نقارن به حساباتنا:
FROM python:3.13.17-slim
RUN apt-get update && apt-get install -y --no-install-recommends softhsm2 opensc && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir --root-user-action=ignore python-pkcs11==0.10.1 psec==1.3.0
WORKDIR /lab
COPY *.py *.sh ./وحجم الـ Image الناتج 244 ميغابايت. ثم سكربت يهيئ «بطاقة» Token في SoftHSM باسم، ويشغل التجربة، ثم يعرض ما بقي فيها:
#!/bin/sh
# Create a SoftHSM token for the lab, run the Python lab, then list what is stored in the token.
# The PINs are random for each run and live only in this shell.
set -e
export SO_PIN=$(python -c "import secrets; print(secrets.randbelow(10**8))")
export USER_PIN=$(python -c "import secrets; print(secrets.randbelow(10**8))")
softhsm2-util --init-token --free --label "arabroot-lab" --so-pin "$SO_PIN" --pin "$USER_PIN"
python -W ignore hsm_lab.py
echo "== objects in the token (pkcs11-tool)"
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --token-label arabroot-lab --login --pin "$USER_PIN" --list-objectsفي السكربت أعلاه لاحظ التالي:
- رمزان للدخول: رمز مسؤول الأمن SO PIN الذي يدير الـ Token، ورمز المستخدم User PIN الذي يستعمل المفاتيح. وفصل الدورين مبدأ في كل HSM: من يدير الجهاز ليس بالضرورة من يستعمل مفاتيحه.
- الرمزان عشوائيان في كل تشغيل، ولا يكتبان في أي ملف، ويعيشان في متغيرات البيئة لهذه الصدفة فقط. فحتى في بيئة اختبار لا يوجد رمز ثابت في المستودع يتعود عليه الفريق ثم ينسخه إلى الإنتاج.
- نختار الـ Token باسمه لا برقم الخانة Slot، والسبب ظهر في المخرج:
The token has been initialized and is reassigned to slot 790712836، فرقم الخانة يتغير بعد التهيئة وفي كل تشغيل، والكود الذي يكتب رقم خانة ثابتاً يعمل اليوم ويتعطل غداً.
docker build -t sfa-hsm-lab:1 .
docker run --rm --name sfa-hsm sfa-hsm-lab:1 ./init.shوأول ما يفعله السكربت أن يحاول الدخول برمز خاطئ، فيعيد الـ HSM الخطأ PinIncorrect. وهذا تفصيل صغير، لكنه أول سطر في سجل أمني جيد: كل محاولة دخول فاشلة إلى HSM حدث يستحق تنبيهاً، لأن التطبيق الصحيح يعرف رمزه.
تجربة: الصيغة 0 بمثال Mastercard المنشور
أردنا أن نقارن حساباتنا بشيء لم نكتبه نحن. ووجدنا في دليل Mastercard لتكوين كتلة الرقم وتشفيرها مثالاً كاملاً: الرقم 7580، ورقم البطاقة 5141576351968605، والكتلة الواضحة بالصيغة 0، ومفتاح مثال ZPK بقيمة 7C933777139EAA6F2940664B299DFFC2، والكتلة المشفرة بـ 3DES. فبنينا الكتلة بثلاث طرق: يدوياً بالدالة التالية، وبمكتبة psec، ثم قارنا بالقيمة المنشورة:
def iso0_by_hand(pin, pan):
pin_field = bytes.fromhex(f"0{len(pin)}{pin}".ljust(16, "F"))
pan_field = bytes.fromhex("0000" + pan[-13:-1]) # 12 rightmost digits without the check digit
return pin_field, pan_field, xor(pin_field, pan_field)== 1. format 0 by hand, with psec, and the Mastercard worked example
PIN field : 047580FFFFFFFFFF
PAN field : 0000157635196860
by hand : 04759589CAE6979F
psec : 04759589CAE6979F
published : 04759589CAE6979Fوالقيم الثلاث متطابقة، والصورة التالية ترسم الطريق من الحقلين إلى الكتلة المشفرة التي سنحسبها داخل الـ HSM بعد قليل:

وحين قرأنا الصفحة بدقة وجدنا ما وجدناه في عينة الشريحة في المقال السابق: جدول XOR التوضيحي في الصفحة نفسها يذكر في عنوانه رقم بطاقة، ويستخدم في سطوره رقماً آخر، والنتيجة فيه لا تطابق أياً منهما. أما المثال المكتوب بالكود، وهو الذي استخدمناه، فمتسق، وقد تحققنا منه بثلاث طرق. والدرس نفسه يتكرر: عينات التوثيق للشرح، والتحقق يكون بالحساب لا بالنسخ.
مكونات المفتاح ورمز التحقق KCV
كيف يدخل مفتاح ZPK إلى الـ HSM أصلاً؟ لو كتبه شخص واحد على لوحة المفاتيح، لعرفه هذا الشخص، ولأصبح المفتاح كله معلقاً بأمانة شخص واحد. ولهذا تدخل المفاتيح الرئيسية بمبدأ المعرفة المجزأة Split Knowledge والتحكم المزدوج Dual Control: يقسم المفتاح إلى مكونات Components، يحمل كل أمين Custodian واحداً منها، ويدخله في جلسة منفصلة، والـ HSM يجمعها بـ XOR داخله. ولا يعرف أي أمين المفتاح، ولا يكفي أي مكون وحده لمعرفة شيء عنه.
لكن كيف يعرف الأمين أنه أدخل مكونه صحيحاً دون أن يقرأه أحد؟ هنا يأتي رمز التحقق Key Check Value، واختصاراً KCV: تشفير كتلة أصفار بالمفتاح، وأخذ أول ثلاثة بايتات من الناتج. فالرمز يتغير بتغير أي خانة في المفتاح، ولا يكشف المفتاح نفسه. وفي المختبر قسمنا مفتاح Mastercard المنشور إلى ثلاثة مكونات، اثنان عشوائيان في كل تشغيل والثالث يكمل المفتاح، وأدخلنا كلاً منها لنحسب رمزه، ثم الثلاثة مجموعة:
c1, c2 = secrets.token_bytes(16), secrets.token_bytes(16)
c3 = xor(xor(MC_ZPK, c1), c2) # three custodians, each holds one part
for n, c in enumerate((c1, c2, c3), 1):
k = import_des3(s, f"component-{n}", c)
print(f"component {n} KCV:", kcv(k))
k.destroy()
zpk = import_des3(s, "ZPK-MC-EXAMPLE", xor(xor(c1, c2), c3))== 2. key components, check values, and the key inside the HSM
component 1 KCV: 57FBBC
component 2 KCV: D3E753
component 3 KCV: 0B05F5
combined ZPK KCV : 726897
read the key value: AttributeSensitive
SENSITIVE True | EXTRACTABLE Falseفي المخرج أعلاه لاحظ التالي:
- رموز المكونات تتغير في كل تشغيل لأن المكونات عشوائية، أما رمز المفتاح المجموع فكان
726897في كل التشغيلات. وهذا هو ما يفعله الأمناء في مراسم المفاتيح Key Ceremony: يقارن كل منهم رمز مكونه بالرمز المكتوب في ظرفه، ثم يقارنون رمز المفتاح النهائي بالرمز الذي أرسله الشريك، فإذا تطابق عرفوا أن المفتاح عند الطرفين واحد دون أن يراه أحد. - محاولة قراءة قيمة المفتاح أعادت
AttributeSensitive: المفتاح موسوم بأنه حساسSENSITIVEوغير قابل للتصديرEXTRACTABLE False، فالـ HSM يرفض إخراجه حتى لمن دخل برمز المستخدم الصحيح.
ولاحظ أن الاستيراد في المختبر نفسه مختصر: المكونات مرت في ذاكرة Python لأننا ولدناها هناك. وفي الواقع يدخل كل أمين مكونه مباشرة في الـ HSM من لوحة متصلة به أو بطاقة ذكية، أو يصل المفتاح من الشريك ملفوفاً بمفتاح آخر، فلا يمر المكون الواضح بأي سيرفر.
تشفير الكتلة داخل الـ HSM
بعد أن صار المفتاح داخل SoftHSM، طلبنا منه أن يشفر الكتلة الواضحة بآلية DES3_ECB:
== 3. encrypt the PIN block inside the HSM
HSM DES3-ECB : 0B8315C81719E9CA
published : 0B8315C81719E9CAالناتج يطابق القيمة المنشورة في دليل Mastercard. ولماذا ECB هنا مع أننا قلنا في مقال التشفير للمطورين إن ECB يظهر الشكل من خلال التشفير؟ لأن الكتلة هنا كتلة واحدة من ثمانية بايتات بالضبط، فلا يوجد «شكل» يتكرر بين كتل، والحماية من التشابه تأتي من خلط الرقم بالحساب قبل التشفير، لا من وضع التشفير. لكن هذا يجعل الصيغة 0 حتمية: الرقم نفسه مع الحساب نفسه والمفتاح نفسه يعطي الكتلة نفسها دائماً، وسنرى بعد قليل لماذا غيرت الصيغة 4 ذلك.
ماذا لو فك المصدر الكتلة بحساب آخر؟
جربنا أن نفك الكتلة المشفرة، ثم نطبق XOR بحقل حساب مختلف، كأن الكتلة وصلت مع رقم بطاقة غير التي بنيت لها:
== 4. what if the issuer decodes with a different PAN?
right PAN -> 047580FFFFFFFFFF | wrong PAN -> 047595DFAFB0F2CAبالحساب الصحيح عاد حقل الرقم كما بني: الصيغة 0، والطول 4، والرقم 7580، ثم F كاملة. وبالحساب الخاطئ جاء حقل يبدأ صدفة بـ 0475 لأن الحسابين يشتركان في بعض الخانات الأولى، لكن الحشو بعده ليس F، فيعرف الـ HSM أن الكتلة لا تنتمي لهذا الحساب ويرفضها. وهذا ما يسمى ربط الرقم بالحساب PAN Binding: كتلة مسروقة من عملية لا يمكن نقلها إلى بطاقة أخرى.
الترجمة بين المفاتيح: أين تفشل أوامر PKCS#11 العامة؟
الآن نصل إلى ما يفعله HSM المستحوذ في كل عملية: يستقبل الكتلة مشفرة بمفتاح الجهاز، ويخرجها مشفرة بمفتاح المنطقة مع الشبكة. وفي PKCS#11 العامة لا يوجد أمر «ترجم كتلة رقم سري»، فالطريقة الوحيدة أن تفك بالمفتاح الأول ثم تشفر بالثاني، وهذا ما فعلناه بمفتاح ثان ولده الـ HSM داخله:
clear_in_host = zpk.decrypt(enc, mechanism=Mechanism.DES3_ECB)
out = zpk2.encrypt(clear_in_host, mechanism=Mechanism.DES3_ECB)== 5. translate from ZPK to a second key with plain PKCS#11
under ZPK-ACQ: 9C131D519404CD2F | clear block that passed through host memory: 04759589CAE6979Fالترجمة نجحت، والكتلة الآن تحت المفتاح الثاني. لكن انظر إلى نهاية السطر: الكتلة الواضحة 04759589CAE6979F مرت في ذاكرة برنامج Python بين الأمرين، ومنها يستخرج أي أحد الرقم 7580 بـ XOR واحد مع حقل الحساب. وهذه بالضبط فكرة المطور في أول القصة، لكن بخطوة أخرى: لم يعد المفتاح في التطبيق، لكن الرقم صار فيه. وهذا ما يمنعه معيار أمن الرقم السري من PCI، كما يذكر الملحق: كل ترجمة للرقم داخل SCD.
وHSM الدفع يحل هذا بأمر واحد: «ترجم هذه الكتلة من المفتاح أ بالصيغة 0 إلى المفتاح ب بالصيغة 0 لهذا الحساب»، فيفك ويعيد التشفير داخله، ولا يخرج إلا الكتلة الجديدة، ومعها يفحص أن الصيغة صحيحة والحشو سليم. ويمكن أن يرفض ترجمات معينة بحسب جدول القيود في الملحق، فالترجمة من الصيغة 0 إلى 1 مثلاً غير مسموح بها، لأن الصيغة 1 لا تحمل الحساب، فالترجمة إليها تفك ربط الرقم بالحساب. وكان هذا أهم سطر في تقرير الفريق بعد المختبر: الـ HSM العام يكفي لحفظ المفاتيح والتوقيع، لكنه لا يكفي لمعالجة الرقم السري، ولا بد من HSM دفع أو خدمة تشفير مدفوعات معتمدة.

المشكلة التي وجدناها: المفتاح يصلح لكل شيء
في التشغيل الأول عرضت أداة pkcs11-tool مفاتيحنا بهذا الشكل:
Secret Key Object; DES3
label: ZPK-MC-EXAMPLE
Usage: encrypt, decrypt, sign, verify, wrap, unwrap
Access: sensitiveأي أن مفتاح الرقم السري، الذي لا يجب أن يفعل شيئاً إلا تشفير كتل الرقم وفكها، يستطيع أيضاً أن يوقع رسائل ويتحقق منها، وأن يلف مفاتيح أخرى ويفكها. والسبب أننا لم نحدد هذه الخصائص عند الإنشاء، فأخذت قيمها الافتراضية. وهذا خطر حقيقي لا نظري: المفتاح الذي يستطيع فك لف مفتاح آخر قد يستعمل لإدخال مفتاح يعرفه المهاجم، والمفتاح الذي يوقع ويشفر قد تستغل نتيجة أحد الاستخدامين لمهاجمة الآخر. والمبدأ في إدارة المفاتيح أن لكل مفتاح غرضاً واحداً. فأضفنا قاموساً يعطل الاستخدامات الأخرى في كل مفتاح ننشئه أو ندخله:
# A PIN key encrypts and decrypts PIN blocks and nothing else (the token default also allows sign, verify, wrap, unwrap)
NO_OTHER_USE = {Attribute.SIGN: False, Attribute.VERIFY: False, Attribute.WRAP: False, Attribute.UNWRAP: False}فأصبح العرض بعد التعديل كما يلي:
Secret Key Object; DES3
label: ZPK-ACQ
Usage: encrypt, decrypt
Access: sensitive, always sensitive, never extractable, local
Secret Key Object; DES3
label: ZPK-MC-EXAMPLE
Usage: encrypt, decrypt
Access: sensitiveولاحظ الفرق في سطر Access بين المفتاحين، وهو درس ثان لم نقصده. فالمفتاح ZPK-ACQ ولده الـ HSM داخله، فيعرف عنه أنه «حساس دائماً، ولم يكن قابلاً للتصدير أبداً، ومحلي»، أي أن أحداً لم يره واضحاً قط. أما ZPK-MC-EXAMPLE فأدخلناه من الخارج، فلا يستطيع الـ HSM أن يشهد بذلك، ويكتفي بـ «حساس الآن». والمدقق الذي يراجع مفاتيح مؤسسة يسأل هذا السؤال بالضبط: أي المفاتيح لم تخرج قط، وأيها دخل من الخارج، وكيف دخل؟ وفي HSM الدفع تحفظ هذه المعلومة وغيرها مع كل مفتاح، ومنها ما سنراه في كتلة TR-31.
هرم المفاتيح: مفتاح يحمي مفتاحاً
إذا كان لكل جهاز صراف ونقطة بيع مفتاح، ولكل شريك مفتاح، فأين تحفظ كل هذه المفاتيح؟ الـ HSM لا يحفظها كلها داخله غالباً، بل يحفظ مفتاحاً رئيسياً واحداً يسمى عند الشركات المصنعة LMK أو MFK، وكل مفتاح آخر يحفظ في قاعدة بيانات التطبيق مشفراً بهذا المفتاح الرئيسي. فإذا احتاج التطبيق أن يترجم كتلة، أرسل إلى الـ HSM الكتلة والمفتاحين المشفرين، ففكهما الـ HSM داخله، واستعملهما، ونسيهما. والصورة التالية تبين الهرم المبسط:

- مفتاح المنطقة الرئيسي ZMK: يتبادله البنك مع كل شريك مرة واحدة بمراسم مكونات، ثم تلف به المفاتيح العاملة التي تتغير دورياً، فلا يحتاج الطرفان مراسم جديدة في كل تغيير.
- مفتاح المنطقة للرقم السري ZPK: يحمي الكتل في الطريق بين المستحوذ والشبكة، وهو ما استخدمناه في المختبر.
- مفاتيح الأجهزة TPK وTAK: الأول للرقم السري في الجهاز، والثاني لرمز MAC على رسائله كما رأينا في مقال الصراف، ويحمل الجهاز كلاً منهما لغرضه.
- مفتاح الاشتقاق الأساسي BDK لنظام DUKPT، اختصاراً لـ Derived Unique Key Per Transaction: بدل أن يحمل كل جهاز مفتاحاً ثابتاً، يحمل مفتاحاً أولياً مشتقاً من BDK ورقم الجهاز، ويشتق منه مفتاحاً جديداً لكل عملية، ثم ينسى المفتاح السابق. فلو استخرج أحد المفتاح الحالي من جهاز مسروق، لا يستطيع أن يفك عمليات سابقة. والجهاز يرسل مع كل كتلة رقماً تسلسلياً KSN يعرف منه المضيف كيف يشتق المفتاح نفسه. وقد رأينا مثالاً لذلك في عينة الطلب التي قرأناها في مقال الشريحة EMV من دليل Cybersource، ففيها كتلة رقم مشفرة
B2042C7F4175632Fبالصيغة 0، ومعها رقم تسلسلي23288800990000000006في حقلencryptedKeySerialNumber.
الصيغة 4 والانتقال إلى AES
كل ما سبق مبني على 3DES، وهي خوارزمية من السبعينات بحجم كتلة 64 بت. ويذكر ملحق PCI أن NIST نشر AES في 2001، وسحب معيار DES في 2005، وأن 3DES بثلاثة مفاتيح كان موصوفاً بأنه «مهمل» Deprecated حتى نهاية 2023 و«غير مسموح» Disallowed بعدها، بحسب NIST SP 800-131A. ورأينا أثر ذلك في مقال الشريحة حين حذرت مكتبة cryptography من 3DES بمفتاحين. فلماذا لا ينتقل الجميع إلى AES فوراً؟ لأن كتلة الصيغة 0 ثمانية بايتات، وكتلة AES ستة عشر، فلا تتسع الصيغة القديمة لـ AES أصلاً، ولأن كل جهاز وكل HSM وكل رسالة في الطريق يجب أن يدعم الجديد.
والصيغة 4 هي الحل الذي أضافه ISO 9564، وبحسب الملحق فهي الصيغة الوحيدة التي تدعم AES. وبناؤها كما في الملحق: حقل رقم من 128 بت يبدأ بالرقم 4 ثم الطول ثم الأرقام ثم حشو بـ A حتى الخانة السادسة عشرة، ثم 64 بت عشوائية، وحقل حساب من 128 بت يبدأ بخانة تقول كم يزيد طول الحساب على 12، ثم الأرقام، ثم أصفار. ثم يشفر حقل الرقم، ويجمع الناتج بـ XOR مع حقل الحساب، ويشفر مرة ثانية. ولم نجد متجهات اختبار رسمية منشورة مجاناً لهذه الصيغة، فالمعيار نفسه مدفوع، فتحققنا من تنفيذنا بطريقة أخرى: بنينا الكتلة بأنفسنا وشفرناها داخل SoftHSM بمفتاح AES اختباري، ثم أعطيناها لمكتبة psec، وهي تنفيذ مستقل، لتفكها:
== 6. format 0 is deterministic, format 4 is not
format 0 twice: 0B8315C81719E9CA 0B8315C81719E9CA
format 4: PIN field 447580AAAAAAAAAAB200EDCE2AE58F07 -> 9C3BF735911AED0C47CC162743199C9C | psec reads PIN 7580
format 4: PIN field 447580AAAAAAAAAA4167BEB906AEE85D -> 3D58192F630881E4459C5F0D83DF1B3B | psec reads PIN 7580
PAN field: 45141576351968605000000000000000
في المخرج أعلاه لاحظ التالي:
- الصيغة 0 أعطت الكتلة نفسها مرتين، أما الصيغة 4 فأعطت كتلتين مختلفتين تماماً للرقم والحساب والمفتاح نفسها، والسبب الخانات العشوائية الستة عشر في آخر حقل الرقم.
- psec قرأت الرقم 7580 من الكتلتين، أي أن ترتيب الحقول والتشفيرين في كودنا يطابق تنفيذاً مستقلاً.
- حقل الحساب يبدأ بالرقم 4، لأن رقم البطاقة 16 رقماً، أي 12 زائد 4، ثم الأرقام كاملة دون حذف رقم التحقق، بخلاف الصيغة 0.
ولماذا تهم الكتلة المختلفة في كل مرة؟ يشرح الملحق ذلك بإعادة الاستعمال البناءة Constructive Reuse: أن يعيد أحد إرسال كتلة مشفرة رآها من قبل فتقبل، دون أن يعرف المفتاح. ففي الصيغة 0 لا يستطيع المستقبل أن يفرق بين كتلة جديدة وكتلة معادة، أما في الصيغة 4 فالكتلة المكررة لا تحدث إلا صدفة، فإذا كان عند المستقبل ضابط يكشف التكرار ويرفضه، أغلق هذا الطريق. وأما المواعيد: فيذكر الملحق أن إصدار معيار أمن الرقم السري 3.1 في مارس 2021 علق مواعيد بدء دعم الصيغة 4 التي كانت في الإصدار 3.0، وأن المجلس يعيد تقييمها، وأن أجهزة القبول المعتمدة على POI الإصدار 5 وما بعده مطلوب منها دعم الصيغة 4. فالسؤال الذي كتبه الفريق لمورد الـ HSM والأجهزة: هل تدعمون الصيغة 4 وAES اليوم، وما خطتكم إن لم تدعموه؟ ويمكن قراءة معيار PCI نفسه من صفحة معيار أمن الرقم السري لدى المجلس.
كتل المفاتيح TR-31: المفتاح ومعه قواعد استخدامه
رأينا في مشكلة المختبر أن مفتاح الرقم السري قد يحمل استخدامات لا يحتاجها. والمشكلة أكبر حين ينتقل المفتاح بين مؤسستين: إذا أرسل البنك إلى الشريك مفتاح ZPK ملفوفاً بمفتاح ZMK بالطريقة القديمة، فالذي يصل ستة عشر بايتاً مشفرة فقط، ولا شيء فيها يقول إن هذا مفتاح رقم سري لا مفتاح بيانات. فإذا استطاع أحد أن يدخل هذه البايتات إلى HSM على أنها مفتاح بيانات، فقد يستطيع أن يستعمله لفك ما لا يجب أن يفك. وكتلة المفتاح Key Block تحل هذا: رأس يصف المفتاح، ثم المفتاح مشفراً، ثم رمز MAC يحمي الاثنين معاً. وأشهر صيغها TR-31، ويذكر ملحق PCI ضمن خطط الانتقال ترحيل كتل المفاتيح الخاصة إلى ISO 20038 أو X9 TR-31.
ولففنا في المختبر مفتاح Mastercard المنشور بمكتبة psec بمفتاح حماية اختباري، برأس من مثال المكتبة، ثم فككناه، ثم غيرنا في الرأس حرفين:
kb = psec.tr31.wrap(kbpk, "B0096P0TE00N0000", MC_ZPK)
print(kb)
h, k = psec.tr31.unwrap(kbpk, kb)
tampered = kb[:5] + "D0" + kb[7:] # change usage P0 (PIN key) to D0 (data key)== 7. a TR-31 key block for sending the ZPK to a partner (psec, test KBPK)
B0096P0TE00N0000FE02FBC1C67759C24B92ED4057D77BFD897B8DAC0C8C613E8AC1FDDA0ECCF7B85342BC625F27BD3C
header: B P0 T E N | key ok: True
usage changed to D0: Key block MAC doesn't match generated MAC.
والرأس B0096P0TE00N0000 يقرأ هكذا: B إصدار الكتلة، و0096 طول الكتلة كلها بالحروف، وP0 الاستخدام وهو مفتاح تشفير رقم سري، وT الخوارزمية 3DES، وE وضع الاستخدام وهو التشفير فقط، و00 رقم إصدار المفتاح، وN قابلية التصدير وهي غير قابل، ثم بقية الرأس. وحين غيرنا P0 إلى D0 رفضت المكتبة الكتلة لأن رمز MAC لم يعد يطابق، فلا يمكن تغيير قواعد الاستخدام دون مفتاح الحماية. والدرس العملي للفريق أن يطلب من الشريك تبادل المفاتيح بكتل TR-31 أو ما يعادلها، وأن يرفض استقبال مفاتيح «عارية» بعد اليوم إن استطاع.
كيف يتحقق المصدر من الرقم دون أن يحفظه؟
بقي سؤال من قصة المطور: إذا كان الـ HSM لا يخرج الرقم، فبماذا يقارن المصدر؟ والجواب أن المصدر لا يحفظ الرقم السري أصلاً، بل يحفظ قيمة مشتقة منه بمفتاح سري داخل الـ HSM، مثل قيمة التحقق من الرقم PIN Verification Value أو فرق Offset بين رقم العميل ورقم طبيعي يولده الـ HSM من رقم البطاقة. وعند العملية يرسل التطبيق إلى الـ HSM الكتلة المشفرة والقيمة المحفوظة، فيفك الـ HSM الكتلة داخله، ويحسب القيمة من الرقم الذي فيها، ويقارن، ويعيد نعم أو لا. ولا تخرج من الـ HSM إلا كلمة واحدة. وهذا يجعل قاعدة بيانات المصدر نفسها بلا أرقام سرية، فلو سرقت لما كشفت رقماً واحداً دون مفتاح الـ HSM. وتفاصيل هذه الطرق تختلف بين الشبكات وأنظمة البطاقات، لكن المبدأ واحد، وهو ما كان على منصة الشركة الناشئة أن تبنيه بدل الفك في سيرفر التطبيق.
تغيير المفاتيح: متى ولماذا وكيف دون توقف؟
المفتاح الذي يعمل سنوات دون تغيير يتراكم تحته كل شيء: كل كتلة شفرت به، وكل رسالة حملت رمزه، فإذا انكشف يوماً انكشف معه كل ما مر تحته. ولهذا يعطى كل مفتاح مدة تشفير Cryptoperiod، وهي المدة التي يسمح فيها باستعماله قبل أن يستبدل. والمدة نفسها قرار مخاطر يحدده البنك بحسب نوع المفتاح وكمية البيانات تحته ومعايير الشبكة، فمفتاح المنطقة الذي تمر تحته ملايين الكتل يوماً يختلف عن مفتاح رئيسي لا يستعمل إلا للف مفاتيح أخرى.
والسؤال العملي للفريق: كيف تغير مفتاح ZPK مع الشبكة دون أن تتوقف العمليات لحظة؟ والطريقة المعتادة أن يرسل أحد الطرفين المفتاح الجديد ملفوفاً بمفتاح ZMK في رسالة إدارة شبكة، من فئة الرسائل 8 التي رأيناها في مقال الصراف، ومعه رمز KCV، فيدخله الطرف الآخر ويتحقق من الرمز ويؤكد، ثم يبدأ الطرفان استعماله من لحظة متفق عليها، مع بقاء المفتاح القديم مقبولاً فترة قصيرة للرسائل التي في الطريق. وهذا هو سبب وجود هرم المفاتيح أصلاً: المراسم الثقيلة بالمكونات والأمناء تحدث مرة واحدة لمفتاح ZMK، والتغيير المتكرر للمفاتيح العاملة يحدث آلياً تحته. أما مفاتيح الأجهزة فـ DUKPT يجعل تغييرها في كل عملية دون أي رسالة، وهذه أهم ميزة له في شبكة من آلاف الأجهزة.
كيف تختبر كود التشفير دون مفاتيح حقيقية؟
كل خطأ رأيناه في هذا المقال من النوع الذي لا يظهر في الإنتاج إلا كرفض غامض من الطرف الآخر: خانة في غير مكانها في حقل الحساب، أو حشو بـ F بدل A، أو ترتيب تشفيرين معكوس في الصيغة 4. ولا يقول لك الشريك «حقل الحساب خاطئ»، بل يرفض الرقم السري كأن العميل أخطأ فيه، فيتصل العميل بالدعم مقتنعاً أن رقمه صحيح، وهو صحيح فعلاً. ولهذا رتب الفريق الاختبار في ثلاث طبقات تعمل كلها في خط CI دون أي مفتاح حقيقي:
- متجهات منشورة: مثال Mastercard للصيغة 0 كما فعلنا، ومثال pyemv للرمز ARQC من مقال الشريحة، وأي مثال يعطيه المستحوذ أو الشبكة في وثائق الاعتماد، كل منها اختبار يقارن بالقيمة المنشورة حرفاً بحرف.
- تنفيذ مستقل: حيث لا يوجد متجه منشور، كالصيغة 4، يبنى الشيء بكودك ويفك بمكتبة أخرى، فإذا اتفق تنفيذان مستقلان قل احتمال أن يكون الخطأ في الفهم نفسه.
- SoftHSM في Container مؤقت: يهيأ برموز عشوائية في كل تشغيل كما في المختبر، فيختبر الكود الذي يكلم PKCS#11 فعلاً، بما فيه خصائص المفاتيح واستخداماتها، ثم يحذف مع الـ Container.
وتبقى طبقة رابعة لا يغني عنها شيء: اختبار التوافق في بيئة اختبار الشبكة أو المستحوذ، ببطاقات اختبار ومفاتيح اختبار يرسلها الشريك، قبل أن يمر أي رقم سري حقيقي.
حالة موثقة: حين انكشف تشفير المعالج
والبيان لا يشرح تفاصيل كيف تم تجاوز التشفير، فلا نخمن. لكن الدروس للمدافع واضحة من النتيجة نفسها: البيانات التي سمحت بصنع بطاقات واستعمال أرقام سرية كانت في متناول من دخل الشبكة، وحدود الحسابات كانت تغير من داخل النظام دون ضابط يوقفها، والسحب المنسق من آلاف الصرافات خلال ساعات نمط يمكن رصده. ولهذا تفصل المعايير بين ثلاثة أشياء لا تجتمع في سيرفر واحد: الرقم السري الذي لا يخرج من الـ HSM، والمفاتيح التي لا تخرج إلا ملفوفة، وصلاحيات تغيير الحدود التي تحتاج أكثر من شخص وتسجيلاً لا يمحى، مع مراقبة لنمط السحب كما ذكرنا في مقال الصراف.
كيف يبدو هذا في مؤسسة حقيقية؟
بعد المختبر عرض الفريق على البنك الراعي خطة مختلفة تماماً عن الشريحة الأولى، وهي تصلح نموذجاً لأي جهة تدخل معالجة البطاقات:
- HSM دفع، لا HSM عام: جهاز معتمد أو خدمة سحابية معتمدة لتشفير المدفوعات، يملك أوامر الترجمة والتحقق، والتطبيق يرسل إليه الكتل والمفاتيح الملفوفة فقط. وSoftHSM يبقى في بيئة التطوير والاختبار الآلي، بمفاتيح اختبار، لأن الكود الذي يكلم PKCS#11 فيه يكلم أي HSM عام آخر.
- مراسم المفاتيح مكتوبة: من الأمناء، ومن يحفظ كل مكون وأين، وكيف يتحقق كل منهم برمز KCV، ومن يحضر، وأين يسجل كل ذلك. وكل مراسم تتكرر بالإجراء نفسه عند تغيير مفتاح أو جهاز.
- كل مفتاح بغرض واحد ومدة: الاستخدام محدد عند الإنشاء كما فعلنا بعد المشكلة، وكل مفتاح له مدة تشفير Cryptoperiod يتغير بعدها، ومفاتيح الأجهزة بـ DUKPT حيث يمكن.
- التبادل بكتل مفاتيح: TR-31 أو ما يعادلها مع كل شريك، ورفض المفاتيح العارية.
- خطة الصيغة 4 وAES: جرد بالأجهزة والـ HSM والرسائل التي تدعمها، كما يقترح ملحق PCI، والصيغة 3 بينهما حيث يلزم.
- لا رقم سري ولا كتلة واضحة في أي سجل: والكتلة المشفرة نفسها لا تسجل كاملة، لأنها مع رقم البطاقة كافية لإعادة الإرسال في الصيغة 0.
- المراقبة: كل دخول فاشل إلى الـ HSM، وكل خطأ ترجمة أو صيغة، وكل تغيير في حدود الحسابات، حدث أمني بتنبيه.
وكان أكثر ما فاجأ الفريق أن معظم العمل ليس كوداً. فالكود الذي يكلم الـ HSM في المختبر بضع عشرات من الأسطر، أما الإجراءات حول المفاتيح، من يحملها، ومتى تتغير، وكيف تثبت أنك فعلت ذلك أمام مدقق، فهي التي تأخذ الأسابيع. وهذا ما يعنيه معيار أمن الرقم السري حين يطلب أن يحمى الرقم في كل مراحل حياته، من الإدخال إلى التحقق، كما يقتبس الملحق من ISO 9564.
أخطاء شائعة Common Mistakes
| الخطأ | لماذا يضر | البديل |
|---|---|---|
| فك الكتلة في سيرفر التطبيق | الرقم في الذاكرة والسجلات وتفريغ الذاكرة | التحقق داخل HSM الدفع |
| الترجمة بفك ثم تشفير عبر PKCS#11 | الكتلة الواضحة تمر في ذاكرة التطبيق، كما رأينا | أمر الترجمة في HSM الدفع |
| إنشاء المفاتيح بالاستخدامات الافتراضية | مفتاح الرقم يوقع ويلف ويفك اللف | تحديد الاستخدام عند الإنشاء |
| إدخال المفتاح من شخص واحد | المفتاح كله معلق بأمانة شخص | مكونات وتحكم مزدوج وKCV |
| كتابة رقم الخانة Slot في الكود | الرقم يتغير بعد التهيئة | اختيار الـ Token باسمه |
| رموز الـ HSM ثابتة في المستودع | تنتقل من الاختبار إلى الإنتاج | رموز من مدير أسرار، وعشوائية في الاختبار |
| تبادل المفاتيح دون كتلة | لا شيء يمنع استعمال المفتاح لغير غرضه | TR-31 أو ما يعادلها |
| تأجيل الصيغة 4 حتى يطلبها أحد | أجهزة وHSM لا تدعمها حين يحين الوقت | جرد وخطة الآن |
| الثقة بعينات التوثيق دون حساب | بعضها غير متسق، كما في جدول XOR | التحقق بالحساب وبتنفيذ مستقل |
قائمة تحقق قبل معالجة أي رقم سري
- الرقم السري والكتلة الواضحة لا يظهران خارج جهاز تشفير آمن في أي مرحلة، ولا في الذاكرة.
- التحقق والترجمة بأوامر HSM دفع أو خدمة تشفير مدفوعات معتمدة، لا بأوامر عامة.
- المفاتيح الرئيسية تدخل بمكونات وتحكم مزدوج، ولكل مكون ومفتاح رمز KCV موثق.
- كل مفتاح بغرض واحد محدد عند إنشائه، وغير قابل للتصدير، وله مدة تشفير.
- تبادل المفاتيح مع الشركاء بكتل TR-31 أو ما يعادلها.
- مفاتيح الأجهزة بـ DUKPT أو مفاتيح فريدة لكل جهاز، لا مفتاحاً مشتركاً.
- جرد بالأجهزة والـ HSM والرسائل التي تدعم الصيغة 4 وAES، وخطة للانتقال.
- لا كتل ولا أرقام في السجلات، وكل خطأ دخول أو ترجمة إلى الـ HSM ينبه.
- بيئة التطوير تستخدم SoftHSM بمفاتيح اختبار ورموز عشوائية، واختبارات آلية بمتجهات معروفة.
الخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- الرقم السري قصير جداً على الهاش، فيوضع في كتلة تربطه بالحساب ثم يشفر، ولا يظهر واضحاً إلا داخل جهاز تشفير آمن، وكل ترجمة له تحدث داخله.
- أعدنا مثال Mastercard المنشور بالصيغة 0 يدوياً وبمكتبة psec وداخل SoftHSM: الكتلة الواضحة
04759589CAE6979Fوالمشفرة0B8315C81719E9CAحتى آخر خانة. - المفتاح يدخل بمكونات ويتحقق منه برمز KCV دون أن يراه أحد، وفي المختبر أعطت مكونات عشوائية مختلفة المفتاح نفسه برمز
726897في كل تشغيل. - أوامر PKCS#11 العامة تترجم الكتلة بفك ثم تشفير، فتمر الكتلة الواضحة في ذاكرة التطبيق، ولهذا يحتاج الرقم السري HSM دفع بأمر ترجمة داخلي.
- الإعداد الافتراضي أعطى مفتاح الرقم استخدامات التوقيع واللف، والمفتاح المولد داخل الـ HSM وحده يحمل شهادة أنه لم يخرج قط.
- الصيغة 4 الوحيدة التي تدعم AES، وتعطي كتلة مختلفة في كل مرة فيكشف التكرار، وكتلة TR-31 تربط المفتاح بقواعد استخدامه ورفضت تغيير P0 إلى D0.
وفي المقال التالي من هذه المرحلة بإذن الله نتابع الكتلة المشفرة في طريقها بين البنوك: المحول المالي Payment Switch الذي يقرر إلى أي مصدر يذهب كل طلب. وبقية مقالات قسم التقنية المالية والمدفوعات:
دليلكيف يعمل الصراف الآلي ATM من الداخل؟نفتح الصراف: الموزع والأدراج ولوحة الرقم السري ومعيار XFS ورسائل المضيف، ونحاكي سحباً يتعطل فيه الموزع وكيف يصحح العكس الخصم.
دليلالشريحة EMV والدفع اللاتلامسي: ماذا يحدث في ثانيتين؟نفك بيانات الشريحة في الحقل 55 من عينة منشورة، ونعيد حساب رمز ARQC منشور خطوة خطوة، ونرى كيف يقرر الجهاز الاتصال بالبنك أو الموافقة دونه.
دليلالمحول المالي Payment Switch: توجيه رسائل البطاقات مع jPOSنبني محولاً بـ jPOS يوجه طلبات مستحوذ إلى مصدرين، ونعيد قصة رفض ظهر معه حجز، ونرى ماذا يحدث حين يتأخر المصدر أو يتوقف أو يتكرر رقم التتبع.
دليلرسائل البنوك: ISO 8583 للبطاقات وISO 20022 للتحويلاتنفكك رسالة تفويض بطاقة بايتاً بايتاً، ونبني أمر تحويل بأسماء عربية ونتحقق منه بالمخطط الرسمي، ونرى ما يتعطل في كل معيار.
دليلأمن بيانات البطاقات PCI DSS والترميز Tokenizationنطاق PCI DSS v4.0.1 وما يمنع حفظه من بيانات البطاقة، وكيف يعمل الترميز، مع مختبر لفحص السجلات وخطر التجزئة دون مفتاح.
دليلالتشفير للمطورين: ماذا تستخدم ولماذا لا تخترع خوارزميتكأي هاش تستخدم لكلمات المرور، ومتى تحتاج التشفير المتماثل أو غير المتماثل، وأين تحفظ المفاتيح بأمان في تطبيقك.سجل التحديثات
- أكتوبر 2026: كتابة المقال وتشغيل المختبر يوم 27 أكتوبر 2026 في Container من python:3.13.17-slim مع softhsm2 2.6.1 وopensc 0.26.1 وpython-pkcs11 0.10.1 وpsec 1.3.0، بمثال Mastercard المنشور ومفاتيح اختبار فقط، ومراجعة ملحق PCI SSC عن الصيغة 4 وبيان وزارة العدل الأمريكية في التاريخ نفسه.