رموز التشفير PKCS # 11: عرض وتصدير الشهادات ، والتحقق من صلاحيتها

صورة في التعليقات على المقالة "أداة مساعدة مشتركة للناطقين باللغة الإنجليزية لعرض شهادات x509 مؤهلة باللغة الروسية" كانت هناك رغبة من مستخدم Pas ليس فقط في "تحليل الشهادة" ، ولكن أيضًا لتلقي "سلاسل شهادات الجذر والقيام بالتحقق من PKI ، على الأقل بالنسبة للشهادات ذات الرموز المميزة التي يمكن استخراجها من المفتاح ". تم الحصول على سلسلة من الشهادات في أحد المقالات السابقة. صحيح ، كان الأمر يتعلق بالشهادات المخزنة في الملفات ، لكننا وعدنا بإضافة آليات للعمل مع الشهادات المخزنة على الرموز PKCS # 11. وهذا ما حدث في النهاية.



تتم كتابة الأداة المساعدة للتحليل والعرض في Tcl / Tk ومن أجل إضافة الرموز المميزة لـ PKCS # 11 / البطاقات الذكية إليها ، بالإضافة إلى التحقق من صلاحية الشهادات ، كانت هناك العديد من المهام المطلوبة:

  • تحديد آلية الحصول على شهادات من البطاقة الرمزية / الذكية ؛
  • تحقق من الشهادة مقابل قائمة شهادات CRL الملغاة ؛
  • تحقق من شهادة الصلاحية من خلال آلية OCSP.

الوصول إلى PKCS # 11 الرمز المميز


للوصول إلى الرمز المميز والشهادات المخزنة عليه ، سنستخدم حزمة TclPKCS11 . يتم توزيع الحزمة في كل من الثنائيات وفي أكواد المصدر. ستصبح أكواد المصدر سهلة الاستخدام لاحقًا عندما نضيف دعمًا رمزيًا مع التشفير الروسي إلى الحزمة. هناك طريقتان لتنزيل حزمة TclPKCS11 ، أو استخدام الأمر tcl في النموذج:

load < tclpkcs11> Tclpkcs11 

أو قم بتنزيلها ببساطة مثل الحزمة pki :: pkcs11 ، بعد وضع مكتبة tclpkcs11 وملف pkgIndex.tcl في دليل مناسب لك (في حالتنا ، هذا هو الدليل الفرعي pkcs11 للدليل الحالي) وإضافته إلى مسار auto_path:

 #lappend auto_path [file dirname [info scrypt]] lappend auto_path pkcs11 package require pki package require pki::pkcs11 

نظرًا لأننا مهتمون بالرموز المميزة في المقام الأول مع دعم التشفير الروسي ، فمن حزمة TclPKCS11 ، سنستخدم الوظائف التالية:
 ::pki::pkcs11::loadmodule <filename> -> handle ::pki::pkcs11::unloadmodule <handle> -> true/false ::pki::pkcs11::listslots <handle> -> list: slotId label flags ::pki::pkcs11::listcerts <handle> <slotId> -> list: keylist ::pki::pkcs11::login <handle> <slotId> <password> -> true/false ::pki::pkcs11::logout <handle> <slotId> -> true/false 
قم فوراً بالحجز بأن وظائف تسجيل الدخول والخروج لن يتم النظر فيها هنا. هذا يرجع إلى حقيقة أننا في هذه المقالة سنتعامل فقط مع الشهادات ، وهي كائنات رمزية عامة. للوصول إلى الكائنات العامة ليست هناك حاجة لتسجيل الدخول عبر رمز PIN على الرمز المميز.

تتمثل الوظيفة الأولى :: pki :: pkcs11 :: loadmodule في تحميل مكتبة PKCS # 11 التي تدعم البطاقة المميزة / البطاقة الذكية التي توجد عليها الشهادات. يمكن الحصول على المكتبة إما عن طريق شراء رمز مميز أو تنزيله من الإنترنت أو تم تثبيته مسبقًا على جهاز كمبيوتر. في أي حال ، تحتاج إلى معرفة المكتبة التي تدعم الرمز المميز الخاص بك. ترجع الدالة loadmodule مؤشرًا إلى المكتبة المحملة:

 set filelib "/usr/local/lib64/librtpkcs11ecp_2.0.so" set handle [::pki::pkcs11::loadmodule $filelib] 

وفقًا لذلك ، هناك وظيفة لإلغاء تحميل مكتبة محملة:

 ::pki::pkcs11::unloadmodule $handle 

بعد تحميل المكتبة ولدينا مقبضها ، يمكنك الحصول على قائمة بالفتحات التي تدعمها هذه المكتبة:

 ::pki::pkcs11::listslots $handle {0 {ruToken ECP } {TOKEN_PRESENT RNG LOGIN_REQUIRED USER_PIN_INITIALIZED TOKEN_INITIALIZED REMOVABLE_DEVICE HW_SLOT}} {1 { } {REMOVABLE_DEVICE HW_SLOT}} . . . {14 { } {REMOVABLE_D EVICE HW_SLOT}} 

في هذا المثال ، تحتوي القائمة على 15 عنصر (خمسة عشر من 0 إلى 14). هذا هو عدد الفتحات التي يمكن أن تدعمها عائلة الرموز المميزة RuToken. بدوره ، كل عنصر من عناصر القائمة نفسها هو قائمة من ثلاثة عناصر:

 {{ } { } {   }} 

العنصر الأول في القائمة هو رقم الفتحة. العنصر الثاني من القائمة هو التسمية الموجودة في فتحة الرمز المميز (32 بايت). إذا كانت الفتحة فارغة ، فإن العنصر الثاني يحتوي على 32 مساحة. والعنصر الثالث الأخير من القائمة يحتوي على أعلام. لن ننظر في مجموعة كاملة من الأعلام. ما يهمنا في هذه الأعلام هو وجود علامة TOKEN_PRESENT. تشير هذه العلامة إلى أن الرمز المميز في الفتحة ، ويمكن أن تكون الشهادات التي تهمنا على الرمز المميز. تعتبر العلامات شيءًا مفيدًا جدًا ، فهي تصف حالة الرمز المميز وحالة رموز PIN وما إلى ذلك. استنادًا إلى قيمة العلامات ، تتم إدارة الرموز PKCS # 11:



الآن ، لا شيء يمنعك من كتابة إجراء slots_with_token ، والذي سيعيد قائمة بالفتحات التي تحتوي على ملصقات الرموز المميزة فيها:

 #!/usr/bin/tclsh lappend auto_path pkcs11 package require pki package require pki::pkcs11 #    proc ::slots_with_token {handle} { set slots [pki::pkcs11::listslots $handle] # puts "Slots: $slots" array set listtok [] foreach slotinfo $slots { set slotid [lindex $slotinfo 0] set slotlabel [lindex $slotinfo 1] set slotflags [lindex $slotinfo 2] if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} { set listtok($slotid) $slotlabel } } #     parray listtok return [array get listtok] } set filelib "/usr/local/lib64/librtpkcs11ecp_2.0.so" if {[catch {set handle [::pki::pkcs11::loadmodule $filelib]} res]} { puts "Cannot load library $filelib : $res" exit } #   set listslots {} set listslots [::slots_with_token $handle] #        while {[llength $listslots] == 0} { puts " " after 3000 set listslots [::slots_with_token $handle] } #        foreach {slotid labeltok} $listslots { puts "Number slot: $slotid" puts "Label token: $labeltok" } 

إذا قمت بتنفيذ هذا البرنامج النصي ، بعد حفظه في ملف slots_with_token.tcl ، نتيجة لذلك نحصل على:

 $ ./slots_with_token.tcl listtok(0) = ruToken ECP listtok(1) = RuTokenECP20 Number slot: 0 Label token: RuTokenECP20 Number slot: 1 Label token: ruToken ECP $ 

من بين 15 فتحة متاحة لهذه المكتبة ، هناك مشاركتان فقط ، صفر والأول.
الآن لا شيء يمنع الحصول على قائمة بالشهادات الموجودة على رمز معين:

 set listcerts [::pki::pkcs11::listcerts $handle $slotid] 

يحتوي كل عنصر قائمة على معلومات حول شهادة واحدة. للحصول على معلومات من الشهادة ، تستخدم الدالة :: pki :: pkcs11 :: listcerts الدالة :: pki :: x509 :: parse_cert من حزمة pki. لكن الدالة :: pki :: pkcs11 :: listcerts تكمل هذه القائمة ببيانات ملازمة لبروتوكول PKCS # 11 ، وهي:

  • pkcs11_ عنصر التصنيف (في مصطلحات سمة PKCS # 11 CKA_LABEL) ؛
  • عنصر pkcs11_id (في مصطلحات سمة PKCS # 11 CKA_ID) ؛
  • عنصر pkcs11_handle يحتوي على إشارة إلى مكتبة PKCS # 11 المحملة ؛
  • عنصر pkcs11_slotid يحتوي على رقم الفتحة مع الرمز المميز الذي توجد عليه هذه الشهادة ؛
  • عنصر نوع يحتوي على قيمة pkcs11 للشهادة الموجودة على الرمز المميز.

تذكر أن العناصر المتبقية يتم تحديدها بشكل أساسي بواسطة الدالة pki :: parse_cert.
فيما يلي الإجراء للحصول على قائمة التسميات (listCert) من الشهادات (CKA_LABEL ، pkcs11_label) ومجموعة من المعرفات التي تم تحليلها (:: certs_p11). مفتاح الوصول إلى عنصر صفيف الشهادة هو تسمية الشهادة (CKA_LABEL ، pkcs11_label):

 #  proc listcerttok {handle token_slotlabel token_slotid} { #     set listCer {} #   array set ::arrayCer [] set ::certs_p11 [pki::pkcs11::listcerts $handle $token_slotid] if {[llength $::certs_p11] == 0} { puts {Certificates are not on the token:$tokenslotlabel} return $listCer } foreach certinfo_list $::certs_p11 { unset -nocomplain certinfo array set certinfo $certinfo_list set certinfo(pubkeyinfo) [::pki::x509::parse_cert_pubkeyinfo $certinfo(cert)] set ::arrayCer($certinfo(pkcs11_label)) $certinfo(cert) lappend listCer $certinfo(pkcs11_label) } return $listCer } 

والآن وبعد أن تم تحليل الشهادات ، نعرض بهدوء قائمة التسميات الخاصة بها في combobox:



كيفية تحليل مفاتيح GOST العامة التي بحثناها في المقالة السابقة.

كلمتين حول تصدير الشهادة. يتم تصدير الشهادات في ترميز PEM وترميز DER (أزرار DER ، تنسيق PEM). تحتوي حزمة pki على وظيفة ملائمة pki :: _ encode_pem للتحويل إلى تنسيق PEM:

 set bufpem [::pki::_encode_pem <der-buffer> <Headline> <Lastline>] 

على سبيل المثال:

 set certpem [::pki::encode_pen $cert_der "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"] 

من خلال تحديد تسمية شهادة الصرف الصحي في combobox ، نتمكن من الوصول إلى نص الشهادة:

 #    set nick [.saveCert.labExp.listCert get] #        foreach certinfo_list $::certs_p11 { unset -nocomplain cert_parse array set cert_parse $certinfo_list if {$cert_parse(pkcs11_label) == $nick} { #   set cert_parse(pubkeyinfo) [::pki::x509::parse_cert_pubkeyinfo $cert_parse(cert)] break } } #   file|pkcs11 set ::tekcert "pkcs11" 

تمت مناقشة آلية أخرى لتحليل الشهادة وعرضها سابقًا هنا .

التحقق من صحة الشهادة


عند تحليل الشهادة ، تخزن المتغيرات :: notbefore و :: notafter التاريخ الذي يمكن من خلاله استخدام الشهادة في عمليات التشفير (علامة ، تشفير ، وما إلى ذلك) ، وتاريخ انتهاء صلاحية الشهادة. الإجراء للتحقق من فترة صلاحية الشهادة هو:

 proc cert_valid_date {} { #       #    set startdate $::notbefore #    set enddate $::notafter #      set now [clock seconds] set isvalid 1 set reason "Certificate is valid" if {$startdate > $now} { set isvalid 0 #      set reason "Certificate is not yet valid" } elseif {$now > $enddate} { set isvalid 0 #    set reason "Certificate has expired" } return [list $isvalid $reason] } 

تحتوي القائمة التي تم إرجاعها على عنصرين. يمكن أن يحتوي العنصر الأول على 0 (صفر) أو 1 (واحد). تشير قيمة "1" إلى أن الشهادة صالحة ، وتشير 0 إلى أن الشهادة غير صالحة. يتم الكشف عن سبب الشهادة غير صالحة في العنصر الثاني. يمكن أن يحتوي هذا العنصر على واحدة من ثلاث قيم:

  • الشهادة صالحة (العنصر الأول في القائمة هو 1):
  • الشهادة غير صالحة بعد (لم تنته الشهادة بعد)
  • انتهت صلاحية الشهادة.

يتم تحديد صلاحية الشهادة ليس فقط من خلال فترة صلاحيتها. يجوز تعليق مركز الشهادة أو إنهائه ، سواء بمبادرة خاصة أو بناءً على طلب صاحب الشهادة ، على سبيل المثال ، في حالة فقد الوسيط بالمفتاح الخاص. في هذه الحالة ، يتم تضمين الشهادة بواسطة المرجع المصدق في قائمة شهادات COS / CRL الملغاة التي يتم توزيعها بواسطة المرجع المصدق. عادة ، يتم تضمين نقطة توزيع CRL في الشهادة. يتم التحقق من صلاحية الشهادة من قائمة الشهادات الملغاة.

التحقق من صحة الشهادة من قبل SOS / CRL


الخطوة الأولى هي الحصول على SOS ، ثم تحليلها والتحقق من الشهادة.
توجد قائمة بنقاط إصدار COC / CRL في ملحق الشهادة مع oid 2.5.29.31 (id-ce-cRLDistributionPoints):

 array set extcert $cert_parse(extensions) set ::crlfile "" if {[info exists extcert(2.5.29.31)]} { set ::crlfile [crlpoints [lindex $extcert(2.5.29.31) 1]] } else { puts "cannot load CRL" } 

في الواقع تحميل الملف مع SOS / CRL هو كما يلي:

 set filecrl "" set pointcrl "" foreach pointcrl $::crlfile { set filecrl [readca $pointcrl $dir] if {$filecrl != ""} { set f [file join $dir [file tail $pointcrl]] set fd [open $fw] chan configure $fd -translation binary puts -nonewline $fd $filecrl close $fd set filecrl $f break } # CRL  .     CRL } if {$filecrl == ""} { puts "Cannot load CRL" } 

في الواقع ، يتم استخدام إجراء readca لتحميل COC / CRL:

 proc readca {url dir} { set cer "" #   if { "https://" == [string range $url 0 7]} { #    tls http::register https 443 ::tls::socket } #     if {[catch {set token [http::geturl $url -binary 1] #    set ere [http::status $token] if {$ere == "ok"} { #        set code [http::ncode $token] if {$code == 200} { #      set cer [http::data $token] } elseif {$code == 301 || $code == 302} { #    ,   set newURL [dict get [http::meta $token] Location] #     set cer [readca $newURL $dir] } else { #    set cer "" } } } error]} { #   ,     set cer "" } return $cer } 

يقوم متغير dir بتخزين المسار إلى الدليل الذي سيتم حفظ COS / CRL فيه ، ويحتوي متغير url على قائمة نقاط توزيع CRL التي تم استلامها مسبقًا.

عند تلقي SOS / CRL ، واجهت فجأة حقيقة أنه بالنسبة لبعض الشهادات ، يجب أن يتم تلقي هذه القائمة عبر بروتوكول https (tls) في وضع مجهول. بصراحة ، هذا مثير للدهشة: قائمة CRL هي وثيقة عامة وحمايتها محمية بتوقيع إلكتروني ولدي إمكانية الوصول إليه من خلال https مجهول في رأيي. ولكن لا يوجد شيء يجب القيام به ، يجب عليك توصيل حزمة tls - الحزمة تتطلب tls.

إذا تعذر تنزيل SOS / CRL ، فلن يمكن التحقق من الشهادة إذا لم يتم تحديد نقطة الوصول مع خدمة OCSP في الشهادة. ولكن هذا سوف يناقش في واحدة من المقالات التالية.

لذلك ، هناك شهادة للتحقق ، وهناك قائمة SOS / CRL ، يبقى للتحقق من الشهادة لها. لسوء الحظ ، لا توجد وظائف مقابلة في حزمة pki. لذلك ، كان علي أن أكتب إجراء للتحقق من صحة الشهادة (عدم إبطالها) من قائمة الشهادات الملغاة

validaty_cert_from_crl:
 proc validaty_cert_from_crl {crl sernum issuer} { array set ret [list] if { [string range $crl 0 9 ] == "-----BEGIN" } { array set parsed_crl [::pki::_parse_pem $crl "-----BEGIN X509 CRL-----" "-----END X509 CRL-----"] set crl $parsed_crl(data) } ::asn::asnGetSequence crl crl_seq ::asn::asnGetSequence crl_seq crl_base ::asn::asnPeekByte crl_base peek_tag if {$peek_tag == 0x02} { #   .CRL ::asn::asnGetInteger crl_base ret(version) incr ret(version) } else { set ret(version) 1 } ::asn::asnGetSequence crl_base crl_full ::asn::asnGetObjectIdentifier crl_full ret(signtype) ::::asn::asnGetSequence crl_base crl_issue set ret(issue) [::pki::x509::_dn_to_string $crl_issue] #     /CRL if {$ret(issue) != $issuer } { #/CRL    set ret(error) "Bad Issuer" return [array get ret] } binary scan $crl_issue H* ret(issue_hex) #  ::asn::asnGetUTCTime crl_base ret(publishDate) #   ::asn::asnGetUTCTime crl_base ret(nextDate) #   ::asn::asnPeekByte crl_base peek_tag if {$peek_tag != 0x30} { #    return [array get ret] } ::asn::asnGetSequence crl_base lcert # binary scan $lcert H* ret(lcert) while {$lcert != ""} { ::asn::asnGetSequence lcert lcerti #    ::asn::asnGetBigInteger lcerti ret(sernumrev) set ret(sernumrev) [::math::bignum::tostr $ret(sernumrev)] #      CRL if {$ret(sernumrev) != $sernum} { continue } # .    ::asn::asnGetUTCTime lcerti ret(revokeDate) if {$lcerti != ""} { #   ::asn::asnGetSequence lcerti lcertir ::asn::asnGetSequence lcertir reasone ::asn::asnGetObjectIdentifier reasone ret(reasone) ::asn::asnGetOctetString reasone reasone2 ::asn::asnGetEnumeration reasone2 ret(reasoneData) } break; } return [array get ret] } 

معلمات هذه الوظيفة هي قائمة إبطال الشهادات (crl) ، والرقم التسلسلي للشهادة التي يتم التحقق منها (sernum) والناشر (المُصدر).

يتم تحميل قائمة إبطال الشهادات (crl) على النحو التالي:

 set f [open $filecrl r] chan configure $f -translation binary set crl [read $f] close $f 

يتم أخذ الرقم التسلسلي للشهادة التي تم التحقق منها (sernum) والناشر (المُصدر) من الشهادة التي تم تحليلها وتخزينها في المتغيرات :: sncert و :: issuercert.

جميع الإجراءات يمكن العثور عليها في شفرة المصدر. يمكن العثور على الكود المصدري للأداة المساعدة وتوزيعاتها لنظامي التشغيل Linux و OS X (macOS) و MS Windows هنا.


تحتفظ الأداة المساعدة أيضًا بالقدرة على عرض الشهادات المخزنة في ملف والتحقق منها:



بالمناسبة ، يمكن أيضًا تصدير الشهادات التي تم عرضها من الملفات ، وكذلك الشهادات المخزنة على الرمز المميز. هذا يجعل من السهل تحويل ملفات الشهادة من تنسيق DER إلى PEM والعكس.

الآن لدينا عارض واحد للشهادات المخزنة في الملفات وعلى الرموز PKCS # 11 / البطاقات الذكية.

نعم ، لقد فاتت النقطة: للتحقق من صلاحية الشهادة ، انقر فوق الزر "Extray" وحدد عنصر القائمة "Validaty by CRL" أو اضغط على زر الماوس الأيمن وعندما يكون المؤشر على المعلومات الرئيسية الحقل وحدد أيضًا عنصر القائمة "Validaty by CRL":



تعرض لقطة الشاشة هذه تصفح الشهادات والتحقق منها في رمز سحابة.

في الختام ، نلاحظ ما يلي. في تعليقاته على المقال ، لاحظ المستخدم Pas بشكل صحيح للغاية حول الرموز PKCS # 11 أنهم "أنفسهم يمكنهم حساب كل شيء". نعم ، الرموز هي في الواقع أجهزة تشفير. وفي المقالات التالية ، سنتحدث ليس فقط عن كيفية التحقق من الشهادات باستخدام بروتوكول OCSP ، ولكن أيضًا حول كيفية استخدام آليات التشفير (نحن نتحدث ، بالطبع ، تشفير GOST) من الرموز / الذكاء لحساب التجزئة (GOST R 34-10- 94/2012) ، تشكيل والتحقق من التوقيعات ، الخ

Source: https://habr.com/ru/post/ar443480/


All Articles