ブラウザプラグインとopensslを使用して、WEBインターフェイスを備えたシステムに電子署名を埋め込む



数年前、当社はWebベースのシステムに電子署名を埋め込むように設計されたRutoken Plug-in製品を発売しました 。 製品を実際のプロジェクトに統合することで得られた経験に基づいて、開発者はロシア側の暗号化アルゴリズムをサポートするopensslを使用してサーバー側を実装することを好むことが多いことに注意したいと思います。

この記事では、プラグインを使用するための以下のシナリオに基づいて、このような統合の典型的なスキームについて説明します。



これらのシナリオには、クライアントとサーバーの相互作用、JavaScriptでのクライアントスクリプトの記述、および対応するopensslサーバー呼び出しが含まれます。

カットの下の詳細。


一般的な操作


デバイス操作


接続されたデバイスを検索する


クライアントシナリオはすべて、コンピューターに接続されているRootoken USBデバイスの検索から始まります。 この記事では、Rutoken EDSのデバイスに重点を置いています。
var devices = Array(); try { devices = plugin.enumerateDevices(); } catch (error) { console.log(error); } 


これは、接続されたデバイスの識別子のリストを返します。 識別子は、デバイスが接続されているスロット番号に関連付けられた番号です。 再度列挙すると、この番号は同じデバイスで異なる場合があります。

Rutoken Plug-inは、コンピューターに接続されているすべてのUSBデバイス(Rutoken EDS、Rutoken PINPad、Rutoken WEB)を定義します。 したがって、次のステップはデバイスのタイプを判別することです。

デバイス情報を取得する


デバイスのタイプを判別するには、TOKEN_INFO_DEVICE_TYPEパラメーターを指定してgetDeviceInfo関数を使用します。 この定数の値は、プラグインオブジェクトに含まれています。
 var type; try { type = plugin.getDeviceInfo(deviceId, plugin.TOKEN_INFO_DEVICE_TYPE); } catch (error) { console.log(error); } switch (type) { case plugin.TOKEN_TYPE_UNKNOWN: message = " "; break; case plugin.TOKEN_TYPE_RUTOKEN_ECP: message = " "; break; case plugin.TOKEN_TYPE_RUTOKEN_WEB: message = " Web"; break; case plugin.TOKEN_TYPE_RUTOKEN_PINPAD_2: message = " PINPad"; break; } 


また、 getDeviceInfo関数を使用すると、 次のものを取得できます。


PINを変更


デバイスのPINコードを変更する例:
 var options = {}; try { plugin.changePin(deviceId, "12345678", "12345671", options); } catch (error) { console.log(error); } 


ここで、最初のパラメーターは古いPINで、2番目は新しいPINです。

証明書を使用する


1.トークンには、3つのカテゴリの証明書を保存できます。



2.デバイスに保存されている証明書を読み取るには、デバイスでの承認は必要ありません。

デバイスからユーザー証明書を読み取る例:
 var certs = Array(); try { certs = plugin.enumerateCertificates(deviceId, plugin.CERT_CATEGORY_USER); } catch (error) { console.log(error); } 


3.証明書はPEM形式でエクスポートできます。
 var certpem; try { certpem = plugin.getCertificate(deviceId, certId); } catch (error) { console.log(error); } 


次のような行が得られます:
 -----BEGIN CERTIFICATE----- MIIBmjCCAUegAwIBAgIBATAKBgYqhQMCAgMFADBUMQswCQYDVQQGEwJSVTEPMA0G A1UEBxMGTW9zY293MSIwIAYDVQQKFBlPT08gIkdhcmFudC1QYXJrLVRlbGVjb20i MRAwDgYDVQQDEwdUZXN0IENBMB4XDTE0MTIyMjE2NTEyNVoXDTE1MTIyMjE2NTEy NVowEDEOMAwGA1UEAxMFZmZmZmYwYzAcBgYqhQMCAhMwEgYHKoUDAgIjAQYHKoUD AgIeAQNDAARADKA/O1Zw50PzMpcNkWnW39mAJcTehAhkQ2Vg7bHkIwIdf7zPe2Px HyAr6lH+stqdACK6sFYmkZ58cBjzL0WBwaNEMEIwJQYDVR0lBB4wHAYIKwYBBQUH AwIGCCsGAQUFBwMEBgYpAQEBAQIwCwYDVR0PBAQDAgKkMAwGA1UdEwEB/wQCMAAw CgYGKoUDAgIDBQADQQD5TY55KbwADGKJRK+bwCGZw24sdIyayIX5dn9hrKkNrZsW detWY3KJFylSulykS/dfJ871IT+8dXPU5A7WqG4+ -----END CERTIFICATE----- 


4. parseCertificateを呼び出して証明書を解析し、DNサブジェクト、DN発行者、拡張機能、公開キー値、署名、シリアル番号、有効期限などを取得できます。

5.証明書をデバイスに書き込むことができます。

ユーザー証明書としてデバイスに証明書を書き込む例:
 var certpem = "-----BEGIN CERTIFICATE----- MIIBmjCCAUegAwIBAgIBATAKBgYqhQMCAgMFADBUMQswCQYDVQQGEwJSVTEPMA0G A1UEBxMGTW9zY293MSIwIAYDVQQKFBlPT08gIkdhcmFudC1QYXJrLVRlbGVjb20i MRAwDgYDVQQDEwdUZXN0IENBMB4XDTE0MTIyMjE2NTEyNVoXDTE1MTIyMjE2NTEy NVowEDEOMAwGA1UEAxMFZmZmZmYwYzAcBgYqhQMCAhMwEgYHKoUDAgIjAQYHKoUD AgIeAQNDAARADKA/O1Zw50PzMpcNkWnW39mAJcTehAhkQ2Vg7bHkIwIdf7zPe2Px HyAr6lH+stqdACK6sFYmkZ58cBjzL0WBwaNEMEIwJQYDVR0lBB4wHAYIKwYBBQUH AwIGCCsGAQUFBwMEBgYpAQEBAQIwCwYDVR0PBAQDAgKkMAwGA1UdEwEB/wQCMAAw CgYGKoUDAgIDBQADQQD5TY55KbwADGKJRK+bwCGZw24sdIyayIX5dn9hrKkNrZsW detWY3KJFylSulykS/dfJ871IT+8dXPU5A7WqG4+ -----END CERTIFICATE-----"; try { plugin.importCertificate(deviceId, certpem, plugin.CERT_CATEGORY_USER); } catch (error) { console.log(error); } 


6. deleteCertificate関数を呼び出すことにより 、トークンから証明書を削除できます。

キーペアGOST R 34.10-2001を使用する


1.デバイスに保存されているキーペアデクリプターを受信するには、PINコードを入力する必要があります。 キーは回復できないため、秘密キーの意味そのものを取得できないことを理解しておく必要があります。

 var keys = Array(); try { plugin.login(deviceId, "12345678"); keys = plugin.enumerateKeys(deviceId, null); } catch (error) { console.log(error); } 


2.キーペアを生成するには、PINコードが必要です。 キーを生成するとき、パラメーターはセットから選択できます。


キーペアGOST R 34.10-2001を生成する例:
 var options = {}; var keyId; try { keyId = plugin.generateKeyPair(deviceId, "A", null, options); } catch (error) { console.log(error); } 


3. deleteKeyPair関数を使用して、 キーペアをトークンから削除できます。

Opensslの設定


Opensslは、バージョン1.0以降のロシアの暗号化アルゴリズムをサポートしています。 それらを使用するには、opensslがエンジンgostをロードする必要があります。 ほとんどのopensslディストリビューションにはこのライブラリがあります。 エンジンをロードするために、openssl構成ファイルに書き込むことができます。

 [openssl_def] engines = engine_section [engine_section] gost = gost_section [gost_section] engine_id = gost default_algorithms = ALL 


openssl構成ファイルが標準の場所にない場合、そのファイルへのパスはOPENSSL_CONF環境変数を介して設定できます。

エンジンgostをロードする別のオプションは、opensslユーティリティのコマンドラインオプションで渡すことです。

エンジンgostが標準の場所にない場合は、環境変数OPENSSL_ENGINESを使用して、opensslが検索するディレクトリへのパスを設定できます。

opensslユーティリティの呼び出しが成功したかどうかの情報を取得するには、エラーを明確にする可能性があるため、stdoutとstderrorを解析する必要があります。 記事の最後に、このユーティリティを使用するPHPスクリプトへのリンクがあります。

それでは、完成したユーザースクリプトの実装に移りましょう。

ポータルへの登録



証明書は、システムへの登録時に発行されます





クライアントスクリプトでの呼び出しのシーケンスは次のとおりです。



次に、要求がサーバーに送信され、サーバーはそれに基づいて証明書を発行します。
これを行うには、1.0のopensslバージョンをサーバーにインストールして正しく構成し、CA機能を展開する必要があります。

1. CA免除の生成:
 openssl genpkey -engine gost -algorithm GOST2001 -pkeyopt paramset:A -out ca.key 

その後、秘密鍵がca.keyファイルに作成されます

2.自己署名CA証明書の作成:
 openssl req -engine gost -x509 -new -key ca.key -out ca.crt 

ca.crtファイルに発行者に関する必要な情報を入力すると、CA証明書が作成されます。

クライアントから受信したリクエストはuser.csrファイルに保存され、それに基づいて証明書を発行します(リクエストのデータを変更することなく):
 openssl ca -engine gost -keyfile ca.key -cert ca.crt -in user.csr -out user.crt -outform PEM -batch 


その後、PEM形式のユーザー証明書がuser.crtファイルに作成されます。 クライアントに送信する必要があります。
クライアントでの呼び出しのさらなるシーケンス:



証明書は既に外部CAによって発行されたトークン上にあります


キーペアは、Rootoken EDSのrtPKCS11ECPライブラリと互換性のある形式で作成する必要があります。



クライアントでの呼び出しのシーケンス:


署名はbase64形式で取得されます。 サーバーでopensslを使用してチェックする場合、署名をヘッダーでフレーム化してPEMにする必要があります。 同様の署名は次のようになります。

 -----BEGIN CMS----- MIIDUQYJKoZIhvcNAQcCoIIDQjCCAz4CAQExDDAKBgYqhQMCAgkFADCBygYJKoZI hvcNAQcBoIG8BIG5PCFQSU5QQURGSUxFIFVURjg+PFY+0JLRi9C/0L7Qu9C90LjR gtGMINCw0YPRgtC10L3RgtC40YTQuNC60LDRhtC40Y4/PCE+c2VydmVyLXJhbmRv bS1kYXRhZTI6ZGE6MmM6MDU6MGI6MzY6MjU6MzQ6YzM6NDk6Nzk6Mzk6YmI6MmY6 YzU6Mzc6ZGI6MzA6MTQ6NDQ6ODM6NjY6Njk6NmI6OWY6YTU6MDk6MzQ6YmY6YzQ6 NzY6YzmgggGeMIIBmjCCAUegAwIBAgIBATAKBgYqhQMCAgMFADBUMQswCQYDVQQG EwJSVTEPMA0GA1UEBxMGTW9zY293MSIwIAYDVQQKFBlPT08gIkdhcmFudC1QYXJr LVRlbGVjb20iMRAwDgYDVQQDEwdUZXN0IENBMB4XDTE0MTIyMjE2NTEyNVoXDTE1 MTIyMjE2NTEyNVowEDEOMAwGA1UEAxMFZmZmZmYwYzAcBgYqhQMCAhMwEgYHKoUD AgIjAQYHKoUDAgIeAQNDAARADKA/O1Zw50PzMpcNkWnW39mAJcTehAhkQ2Vg7bHk IwIdf7zPe2PxHyAr6lH+stqdACK6sFYmkZ58cBjzL0WBwaNEMEIwJQYDVR0lBB4w HAYIKwYBBQUHAwIGCCsGAQUFBwMEBgYpAQEBAQIwCwYDVR0PBAQDAgKkMAwGA1Ud EwEB/wQCMAAwCgYGKoUDAgIDBQADQQD5TY55KbwADGKJRK+bwCGZw24sdIyayIX5 dn9hrKkNrZsWdetWY3KJFylSulykS/dfJ871IT+8dXPU5A7WqG4+MYG7MIG4AgEB MFkwVDELMAkGA1UEBhMCUlUxDzANBgNVBAcTBk1vc2NvdzEiMCAGA1UEChQZT09P ICJHYXJhbnQtUGFyay1UZWxlY29tIjEQMA4GA1UEAxMHVGVzdCBDQQIBATAKBgYq hQMCAgkFADAKBgYqhQMCAhMFAARAco5PumEfUYVcLMb1cnzETNOuWC8Goda8pdUL W5ASK+tztCwM7wpXgAy+Y6/sLtClO9sh8dKnAaEY2Yavg3altQ== -----END CMS----- 


サーバー上の署名の検証:
 openssl cms -engine gost -verify -in sign.cms -inform PEM -CAfile ca.crt -out data.file -certsout user.crt 


ここで、sign.cmsは署名が配置されているファイル、ca.crtはルート証明書を含むファイル、そのうちの1つは整列されている必要があり、data.fileは署名されたデータが保存されるファイル、user.crtはファイルですユーザー証明書が保存されます。 data.fileからデータを抽出し、最後の32文字を切断し、saltを比較する必要があります。

サーバー上の証明書から情報を取得する必要がある場合は、次のように解析できます。

テキストビューで証明書の内容を表示します。
 openssl x509 -in cert.pem -noout -text 


証明書のシリアル番号を表示:
 openssl x509 -in cert.pem -noout -serial 


件名DNを表示:
 openssl x509 -in cert.pem -noout -subject 


発行者DNを表示:
 openssl x509 -in cert.pem -noout -issuer 


件名の住所を表示:
 openssl x509 -in cert.pem -noout -email 


証明書の開始時間を表示:
 openssl x509 -in cert.pem -noout -startdate 


証明書の有効期限を表示:
 openssl x509 -in cert.pem -noout -enddate 


ポータルでの強力な認証



Rutokenプラグインで使用される一般的な認証スキームは次のとおりです。

このスキームの実装は、「登録、外部CAによって発行された証明書が既に利用可能」と根本的に違いはありません。

CMS形式のデータやファイルの電子署名





サーバーでの署名の検証については上記で説明しています。

CMS形式のデータおよび/またはファイルの暗号化/復号化



サーバーのクライアントデータ暗号化



クライアントとサーバー間のデータ交換の機密性を確保するために、プラグインはデータの暗号化/復号化を提供します。 データはCMS形式で暗号化されます。 CMS形式でデータを暗号化するには、「宛先」の公開鍵の証明書が必要です。 同時に、秘密鍵の所有者のみがそのようなメッセージを解読できます。 サーバーのデータを暗号化する場合、サーバー証明書をRootoken EDSに保存することをお勧めします。 この証明書は、ポータルでのユーザー登録中にデバイスに書き込むことができます。 これを行うには、 importCertificate関数を使用して、 CERT_CATEGORY_OTHERをカテゴリパラメーターとして渡します。 cmsEncrypt関数を使用するには、 getCertificate関数を使用して、ハンドルで証明書の本文を取得する必要があります。 この場合、記述子は一意で変更されず、サーバー証明書をインポートするときにサーバー上のユーザーアカウントに保存できます。 GOST 28147-89に従ってハードウェア暗号化を使用するには、useHardwareEncryptionオプションをtrueに設定する必要があります。 それ以外の場合は、GOST 28147-89の迅速なソフトウェア実装が使用されます。

呼び出しのシーケンスを図に示します。



クライアントデータの暗号化:
 try { var recipientCert = plugin.getCertificate(deviceId, certRecId); } catch (error) { console.log(error); } var options = {}; options.useHardwareEncryption = true; var cms; try { cms = plugin.cmsEncrypt(deviceId, certSenderId, recipientCert, data, options); } catch (error) { console.log(error); } 


サーバー上のデータの復号化、復号化の前に、メッセージはPEMヘッダー「----- BEGIN PKCS7 -----」および「----- END PKCS7 -----」でフレーム化する必要があります。
 openssl smime -engine gost -decrypt -in message.cms -inform PEM -recip recipient.crt -inkey recipient.key 

recipient.crt-メッセージが暗号化された人の証明書、recipient.key-メッセージが暗号化された人のキー。

クライアント上のサーバーから受信したデータの復号化



サーバーから受信したデータを復号化するには、 cmsDecrypt関数を使用します。 サーバーはその証明書を使用してクライアントを暗号化するため、証明書内の公開鍵に対応するクライアント秘密鍵記述子をkeyIdとして渡す必要があります。 この記述子は一意で不変であるため、サーバーのユーザーアカウントに格納できます。 さらに、 getKeyByCertificate関数を呼び出すことにより、ユーザーキー記述子を明示的に取得できます。

クライアントのサーバーデータ暗号化:
 openssl smime -encrypt -engine gost -gost89 -binary -outform PEM -in data.file -out message.enc user.crt 


クライアント上のデータの復号化:
 var data; var options = {}; try { data = plugin.cmsDecrypt(deviceId, keyId, cms, options); } catch (error) { console.log(error); } 


便利なリンク


これらのリンクは、Rutokenプラグインとopensslに基づいたデジタル署名をサポートする情報システムの開発者にとって有用です。

Rootoken Demosystemプラグイン
キーの生成、リクエストの生成、証明書の管理、証明書リクエストのテンプレートの生成のためのWEBサービス
Rutokenプラグインのドキュメント
ロシアの暗号化アルゴリズムでのopensslユーティリティの使用に関するドキュメント
opensslユーティリティを使用したPHPスクリプトの例

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


All Articles