
Sintaks transport ASN.1 mendefinisikan cara unik untuk mengonversi nilai variabel tipe yang valid ke dalam urutan byte untuk transmisi melalui jaringan. Dalam ASN.1, itu disebut Aturan Pengkodean Dasar (BER). Aturan bersifat rekursif, jadi pengodean objek komposit adalah rantai urutan komponen objek yang dikodekan. Protokol ASN.1 menggambarkan struktur data dalam bahasa yang
sederhana dan mudah dimengerti .
Setiap nilai yang ditransmisikan - baik tipe dasar maupun turunan - terdiri dari tiga bidang:
- pengidentifikasi;
- panjang bidang data (dalam byte);
- bidang data.
Jika Anda selalu menentukan panjang bidang data (saya anggap ini praktik yang baik), maka bendera akhir bidang data tidak digunakan.
Ada banyak kompiler berbeda untuk ASN.1, baik yang berbayar maupun gratis, untuk bahasa pemrograman yang berbeda, tetapi kami ingin memiliki sesuatu yang sangat sederhana.
Sebagian besar pengembang perangkat lunak menemukan
kompleks standar
ASN.1 . Saya juga berpikir begitu sampai saat ini. Bekerja di bidang PKI / PKI / kriptografi hampir setiap hari Anda berurusan dengan struktur ASN1 dalam bentuk sertifikat X509, permintaan sertifikat, daftar sertifikat yang dicabut. Dan daftarnya berlanjut. Jadi, ketika mengerjakan utilitas untuk membuat permintaan sertifikat dalam format PKCS # 10 dengan menghasilkan pasangan kunci pada token / kartu pintar PKCS, saya secara alami harus membentuk, khususnya, struktur asn1 kunci publik untuk menulisnya ke permintaan sertifikat :
C-Sequence C-Sequence (<>) Object Identifier (<>) <oid public key> C-Sequence (<>) Object Identifier (<>) <oid > Object Identifier (<>) <oid > Bit String (<>) < >
Karena kami menggunakan token PKCS # 11 dengan dukungan untuk kriptografi Rusia sebagai alat perlindungan informasi kriptografi, bahan sumber untuk struktur ini diperoleh dari token sesuai dengan templat berikut:
CK_BYTE gostr3410par[12]; CK_BYTE gostr3411par[12]; CK_ULONG gostr3410par_len; CK_ULONG gostr3411par_len; CK_BYTE pubkey[128]; CK_ULONG pubkeu_len; CK_KEY_TYPE key_type; CK_ATTRIBUTE templ_pk[] = { . . . {CKA_GOSTR3410PARAMS, gostr3410par, sizeof(gostr3410par)}, {CKA_GOSTR3411PARAMS, gostr3411par, sizeof(gostr3410par)}, {CKA_VALUE, pubkey, sizeof(pubkey)}, {CKA_KEY_TYPE, &key_type, sizeof(key_type)} }
Langsung dari struktur ini, nilai atribut CKA_VALUE yang berisi nilai kunci publik dan nilai atribut CKA_GOSTR3410PARAMS dan CKA_GOSTR3411PARAMS yang berisi oid dari parameter tanda tangan dan parameter hash akan digunakan untuk mengisi asn1-publickeyinfo.
Atribut CKA_KEY_TYPE, yang dapat mengambil nilai CKK_GOSTR3410 dan CKK_GOSTR3410_512 (dalam kondisi ketika algoritma tanda tangan GOST R 34.10-2001 terus beroperasi) secara ambigu mendefinisikan algoritma pasangan kunci. Jika nilai atribut CKA_KEY_TYPE sama dengan CKK_GOSTR3410_512, maka, tentu saja, itu menunjuk secara unik ke algoritma GOST R 34.10-2012 dengan panjang kunci 512 bit (oid = 1.2.643.7.1.1.1.1.2). Tetapi jika itu sama dengan CKK_GOSTR3410, maka ada ambiguitas tentang jenis kunci yang dimiliki oleh kunci ini: GOST R 34.10-2001 atau masih GOST R 34.10-2012 dengan panjang kunci 256 bit. Atribut CKA_GOSTR3411PARAMS membantu menyelesaikan ambiguitas ini.
Segera, kami mencatat bahwa parameter CKA_GOSTR3410PARAMS dan CKA_GOSTR3411PARAMS pada token sesuai dengan rekomendasi TK-26 disimpan dalam bentuk pengenal objek yang dikodekan oleh oid, misalnya:
\ x06 \ x06 \ x2a \ x85 \ x03 \ x02 \ x02 \ x13, di mana byte nol menentukan jenis urutan (0x06 adalah pengidentifikasi objek, lihat tabel di bawah), byte kedua menunjukkan panjang (dalam kasus umum, panjangnya mungkin memerlukan beberapa byte, tetapi lebih lanjut tentang itu di bawah ini) bidang data tempat oid disimpan dalam bentuk biner.
Jika parameter ini berisi oid dari algoritma hash GOST R 34.10-2012 dengan panjang 256 bit (oid = 1.2.643.7.1.1.2.2, dalam bentuk biner "\ x2a \ x 85 \ x 85 \ x 03 \ x 07 \ x 07 \ x 01 \ x 01 \ x 02 \ x02 "), maka jenis kunci harus ditetapkan sebagai GOST R 34.10-2012 dengan panjang kunci 256 bit. Kalau tidak, itu adalah kunci dari GOST R 34.10-2001. Algoritma untuk menentukan jenis kunci mungkin terlihat seperti ini:
. . . for (curr_attr_idx = 0; curr_attr_idx < (sizeof(templ_pk)/sizeof(templ_pk[0])); curr_attr_idx++){ curr_attr = &templ_pk[curr_attr_idx]; if (!curr_attr->pValue) { continue; } swith (curr_attr->type) { . . . case CKA_VALUE: pubkey_len = curr_attr->ulValueLen; break; case CKA_GOSTR3410PARAMS: gostr3410par_len = curr_attr->ulValueLen; break; case CKA_GOSTR3410PARAMS: gostr3411par_len = curr_attr->ulValueLen; break; case CKA_KEY_TYPE: ulattr = curr_attr->pValue; if (*ulattr == CKK_GOSTR3410) { if (!memmem(gostr3411par), gostr3411par_len,"\x06\x08\x2a\x85\x03\x07", 6)) { strcpy(oid_key_type, "1.2.643.2.2.19"); memcpy(oid_key_type_asn1("\x06\x06\x2a\x85\x03\x02\x02\x13", 8); } else { strcpy(oid_key_type, ("1 2 643 7 1 1 1 1"); memcpy(oid_key_type_asn1 ("\x06\x08\x2a\x85\x03\x07\x01\x01\x01\x01", 10); } } else if (*ulattr == CKK_GOSTR3410_512) { strcpy(oid_key_type, ("1 2 643 7 1 1 1 2"); memcpy(oid_key_type_asn1 ("\x06\x08\x2a\x85\x03\x07\x01\x01\x01\x02", 10); } else { fprintf(stderr, "tclpkcs11_perform_pki_keypair CKK_GOSTR ERROR\n"); return (-1) } break; . . . } } . . .
Sekarang kita memiliki semua sumber data untuk membuat struktur kunci publik asn1.
Ingat bahwa setiap elemen struktur asn1 terdiri dari tiga bidang:
- pengidentifikasi;
- panjang bidang data (dalam byte);
- bidang data.
Berikut ini
adalah tabel pengkodean untuk beberapa jenis pengidentifikasi yang digunakan dalam PKI / PKI:
Ketik Nama | Deskripsi singkat | Ketik representasi dalam pengkodean DER |
---|
URUTAN | Digunakan untuk menggambarkan struktur data yang terdiri dari berbagai jenis. | 30 |
INTEGER | Integer | 02 |
IDENTIFIER OBYEK | Urutan bilangan bulat. | 06 |
UTCTime | Jenis sementara, berisi 2 digit untuk menentukan tahun | 17 |
Waktu umum | Jenis waktu yang diperpanjang, berisi 4 digit untuk menunjukkan tahun. | 18 |
SET | Menjelaskan struktur data dari berbagai jenis. | 31 |
UTF8String | Menjelaskan data string. | 0C |
Tidak | Sebenarnya NULL | 05 |
BIT STRING | Ketik untuk menyimpan urutan bit. | 03 |
STRING OCTET | Ketik untuk menyimpan urutan byte | 04 |
Ketika bekerja dengan struktur ASN1, kejutan terbesar untuk yang belum tahu disebabkan oleh metode pengkodean panjang bidang data, terutama ketika itu terbentuk, dan jika kita memperhitungkan arsitektur komputer (littleendien, bigendien). Ini adalah keseluruhan
ilmu . Jadi, dalam proses membahas algoritma untuk membentuk bidang ini, muncul ide untuk menggunakan fungsi sprintf, yang dengan sendirinya akan mempertimbangkan arsitektur, dan bagaimana jumlah byte untuk menyimpan panjang ditentukan dapat dilihat oleh kode fungsi, yang menyiapkan buffer dengan pengidentifikasi tipe data dan panjang data:
unsigned char *wrap_id_with_length(unsigned char type,
Fungsi mengembalikan pointer ke buffer dengan struktur asn1, dialokasikan dengan mempertimbangkan panjang data. Tetap menyalin data ini ke dalam buffer yang diterima dengan offset dengan panjang header. Panjang tajuk dikembalikan melalui parameter lenasn.
Untuk memeriksa bagaimana fungsi ini bekerja, kami akan menulis sebuah utilitas sederhana:
#include <stdio.h> #include <stdlib.h> #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) int main (int argc, char *argv[]) { unsigned char *hdrasn; unsigned char type; unsigned long length; unsigned long lenasn; if (argc != 3) { fprintf (stderr, "Usage: wrap_id_with_length <id> <length>\n"); exit(-1); } type = atoi(argv[1]); length = atol(argv[2]); fprintf (stderr, "<id=%02x> <length=%lu>\n", type, length); if (length == 0) { fprintf (stderr, "Bad length=%s\nUsage: wrap_id_with_length <id> <length>\n", argv[2]); exit(-1); } hdrasn = wrap_id_with_length(type, length, &lenasn); fprintf (stderr, "Length asn1-buffer=%lu, LEN_HEADER=%lu, LEN_DATA=%lu\n", lenasn, lenasn - length, length); }
Simpan bersama-sama dengan fungsi wrap_id_with_length dalam file wrap_id_with_length.c.
Kami menyiarkan:
$cc –o wrap_id_with_length wrap_id_with_length.c $
Jalankan program yang dihasilkan dengan berbagai sumber data. Tipe data ditentukan oleh desimal:
bash-4.3$ ./wrap_id_with_length 06 8 <id=06> <length=8> ASN1 - :0608 Length asn1-buffer=10, LEN_HEADER=2, LEN_DATA=8 bash-4.3$ ./wrap_id_with_length 06 127 <id=06> <length=127> ASN1 - :067f Length asn1-buffer=129, LEN_HEADER=2, LEN_DATA=127 bash-4.3$ ./wrap_id_with_length 48 128 <id=30> <length=128> ASN1 - :308180 Length asn1-buffer=131, LEN_HEADER=3, LEN_DATA=128 bash-4.3$ ./wrap_id_with_length 48 4097 <id=30> <length=4097> ASN1 - :30821001 Length asn1-buffer=4101, LEN_HEADER=4, LEN_DATA=4097 bash-4.3$
Anda dapat memeriksa kebenaran pembentukan header menggunakan kalkulator apa pun:

Kita semua siap untuk membentuk struktur ASN1. Tapi pertama-tama, buat perubahan kecil pada fungsi wrap_id_with_length dan panggil saja
wrap_for_asn1: unsigned char *wrap_for_asn1(unsigned char type, unsigned char *prefix, unsigned long prefix_len, unsigned char *wrap, unsigned long wrap_len, unsigned long *lenasn){ unsigned long length; int buflen = 0; unsigned char *buf; char *format; const char buf_for_len[100]; const char *s; char f0[] = "%02x%02x"; char f1[] = "%02x81%02x"; char f2[] = "%02x82%04x"; char f3[] = "%02x83%06x"; char f4[] = "%02x84%08x"; length = prefix_len + wrap_len; buflen += ( length <= 0x80 ? 1: length <= 0xff ? 2: length <= 0xffff ? 3: length <= 0xffffff ? 4: 5); buf = malloc(length + buflen); switch (buflen - 1) { case 0: format = f0; break; case 1: format = f1; break; case 2: format = f2; break; case 3: format = f3; break; case 4: format = f4; break; }
Seperti yang Anda lihat, perubahannya minimal. Sebagai parameter input, data itu sendiri ditambahkan, yang di dalam fungsi dikemas ke dalam struktur ASN1. Selain itu, dua buffer dapat dimasukkan ke input sekaligus. Bagi kami, ini mudah.
Sebelum menyajikan kasus uji, kami memberikan kode tiga fungsi lagi. Fungsi
oid2buffer pertama mengubah oid dari bentuk desimal bertitik ke penyandian DER. Kami membutuhkan fungsi ini untuk mengonversi, khususnya, oids dari pasangan kunci (lihat di atas).
Teks fungsi ada di sini:static char * oid2buffer (char * oid_str, unsigned long * len) {
char * curstr;
char * curstr1;
char * nextstr;
firstval int unsigned;
unsvaled int secondval;
val int unsigned;
unsigned char buf [5];
int count;
unsigned char oid_hex [100];
char * res;
int i;
if (oid_str == NULL) {
* len = 0;
mengembalikan NULL;
}
* len = 0;
curstr = strdup ((const char *) oid_str);
curstr1 = curstr;
nextstr = strchr (curstr, '.');
if (nextstr == NULL) {
* len = 0;
mengembalikan NULL;
}
* nextstr = '\ 0';
firstval = atoi (curstr);
curstr = nextstr + 1;
nextstr = strchr (curstr, '.');
if (nextstr) {
* nextstr = '\ 0';
}
secondval = atoi (curstr);
if (firstval> 2) {
* len = 0;
mengembalikan NULL;
}
if (secondval> 39) {
* len = 0;
mengembalikan NULL;
}
oid_hex [0] = (unsigned char) ((firstval * 40) + secondval);
i = 1;
while (nextstr) {
curstr = nextstr + 1;
nextstr = strchr (curstr, '.');
if (nextstr) {
* nextstr = '\ 0';
}
memset (buf, 0, sizeof (buf));
val = atoi (curstr);
hitung = 0;
if (curstr [0]! = '0')
while (val) {
buf [count] = (val & 0x7f);
val = val >> 7;
hitung ++;
}
selain itu {
buf [count] = (val & 0x7f);
val = val >> 7;
hitung ++;
}
while (count--) {
if (count) {
oid_hex [i] = buf [count] | 0x80;
} lain {
oid_hex [i] = buf [count];
}
i ++;
}
}
res = (char *) malloc (i);
if (res) {
memcpy (res, oid_hex, i);
* len = i;
}
gratis (curstr1);
pengembalian res;
}
Dua fungsi lainnya memungkinkan buffer biner dikonversi menjadi heksadesimal (buffer2hex) dan sebaliknya (hex2buffer).
Fungsi-fungsi ini ada di sini:char statis *
buffer2hex (const unsigned char * src, size_t len)
{
int i;
char * dest;
char * res;
dest = (char *) malloc (len * 2 + 1);
res = dest;
jika (dest)
{
untuk (i = 0; i <len; i ++, dest + = 2)
sprintf (dest, "% 02X", src [i]);
}
pengembalian res;
}
kekosongan statis *
hex2buffer (const char * string, size_t * r_length)
{
const char * s;
buffer * char unsigned;
panjang size_t;
buffer = malloc (strlen (string) / 2 + 1);
panjang = 0;
untuk (s = string; * s; s + = 2)
{
if (! hexdigitp (s) ||! hexdigitp (s + 1)) {
fprintf (stderr, "digit hex tidak valid dalam \"% s \ "\ n", string);
}
((unsigned char *) buffer) [length ++] = xtoi_2 (s);
}
* r_length = panjang;
mengembalikan buffer;
}
Fungsi-fungsi ini sangat nyaman untuk debugging, dan kemungkinan besar banyak yang memilikinya.
Dan sekarang kita kembali ke solusi tugas, memperoleh struktur publik kunci asn1. Kami akan menulis sebuah utilitas yang akan menghasilkan dan menyimpan struktur kunci publik asn1 dalam file ASN1_PIBINFO.der.
Utilitas ini ada di sini: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdint.h> #include <string.h> #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) /* oid2buffer*/ /* buffer2hex hex2buffer*/ /* wrap_for_asn1*/ int main() { int fd; unsigned char *asn, *asn1, *asn2, *asn3, *pubkeyalgo; unsigned char* pubkey_bin; // char gost3410par[] = "\x06\x7\x2a\x85\x03\x02\x02\x23\x01"; unsigned long gost3410par_len = sizeof(gost3410par) - 1; char gost3411par[] = "\x06\x8\x2a\x85\x03\x07\x01\x01\x02\x02"; unsigned long gost3411par_len = sizeof(gost3411par) - 1; unsigned char pubkey_hex[] = "9af03570ed0c54cd4953f11ab19e551022cd48603326c1b9b630b1cff74e5a160ba1718166cc22bf70f82bdc957d924c501b9332491cb3a36ce45770f05487b5"; char pubkey_oid_2001[] = "1.2.643.2.2.19"; char pubkey_oid_2012_256[] = "1.2.643.7.1.1.1.1"; char pubkey_oid_2012_512[] = "1.2.643.7.1.1.1.2"; unsigned long pubkey_len, pubkey_len_full, len10, len11, len12, lenalgo; unsigned char *pkalgo; unsigned long pkalgo_len; uint16_t x = 1; /* 0x0001 */ printf("%s\n", *((uint8_t *) &x) == 0 ? "big-endian" : "little-endian"); ////pubkeyinfo // if (!memmem(gost3411par, 8, "\x2a\x85\x03\x07", 4)) { // 34.11-94, 34.10-2001 - 1.2.643.2.2.19 pubkeyalgo = (unsigned char *)oid2buffer(pubkey_oid_2001, &lenalgo); } else if (!memcmp(gost3411par, "\x2a\x85\x03\x07\x01\x01\x02\x02", 8)){ // 34.11-2012-256, 34.10-2012-256 - 1.2.643.7.1.1.1.1 pubkeyalgo = (unsigned char *)oid2buffer(pubkey_oid_2012_256, &lenalgo); } else { // 34.11-2012-512, 34.10-2012-512 - 1.2.643.7.1.1.1.2 pubkeyalgo = (unsigned char *)oid2buffer(pubkey_oid_2012_512, &lenalgo); } pubkey_bin =(unsigned char*)hex2buffer((const char *)pubkey_hex, &pubkey_len); // asn1 = wrap_for_asn1_bin('\x04', (unsigned char *)"", 0, pubkey_bin, pubkey_len, &pubkey_len); asn = wrap_for_asn1_bin('\x03', (unsigned char *)"\x00", 1, asn1, pubkey_len, &pubkey_len_full); fprintf(stderr, "PUBLIC_VALUE=%s\n", buffer2hex(asn, pubkey_len_full)); free(asn1); // asn3 = wrap_for_asn1_bin('\x30', (unsigned char*)gost3410par, gost3410par_len, (unsigned char *)gost3411par, gost3411par_len, &len12); fprintf(stderr, "\nPARAMS len12=%lu, FULL=%s\n", len12, buffer2hex(asn3, len12)); // pkalgo = wrap_for_asn1_bin('\x06', (unsigned char *)"", 0, pubkeyalgo, lenalgo, &pkalgo_len); // asn2 = wrap_for_asn1_bin('\x30', pkalgo, pkalgo_len, asn3, len12, &len11); fprintf(stderr, "PubKEY=%s\n", buffer2hex(asn3, len11)); asn1 = wrap_for_asn1_bin('\x30', asn2, len11, asn, pubkey_len_full, &len10); free(asn2); free(asn3); fprintf(stderr, "\n%s\n", buffer2hex(asn1, len10)); fd = open ("ASN1_PUBINFO.der", O_TRUNC|O_RDWR|O_CREAT,S_IRWXO); write(fd, asn1, len10); close(fd); free(asn1); chmod("ASN1_PUBINFO.der", 0666); }
Untuk memverifikasi hasilnya, kami akan menggunakan utilitas derdump dan pp dari paket NSS.
Utilitas pertama akan menunjukkan kepada kita struktur asn1 dari kunci publik:
$ derdump -i ASN1_PUBINFO.der C-Sequence (102) C-Sequence (31) Object Identifier (8) 1 2 643 7 1 1 1 2 (GOST R 34.10-2012 Key 512) C-Sequence (19) Object Identifier (7) 1 2 643 2 2 35 1 Object Identifier (8) 1 2 643 7 1 1 2 2 (GOST R 34.11-2012 256) Bit String (67) 00 04 40 9a f0 35 70 ed 0c 54 cd 49 53 f1 1a b1 9e 55 10 22 cd 48 60 33 26 c1 b9 b6 30 b1 cf f7 4e 5a 16 0b a1 71 81 66 cc 22 bf 70 f8 2b dc 95 7d 92 4c 50 1b 93 32 49 1c b3 a3 6c e4 57 70 f0 54 87 b5 $
Yang kedua akan menampilkan konten kunci:
$ pp -t pk -i ASN1_PUBINFO.der Public Key: Subject Public Key Info: Public Key Algorithm: GOST R 34.10-2012 512 Public Key: PublicValue: 9a:f0:35:70:ed:0c:54:cd:49:53:f1:1a:b1:9e:55:10: 22:cd:48:60:33:26:c1:b9:b6:30:b1:cf:f7:4e:5a:16: 0b:a1:71:81:66:cc:22:bf:70:f8:2b:dc:95:7d:92:4c: 50:1b:93:32:49:1c:b3:a3:6c:e4:57:70:f0:54:87:b5 GOSTR3410Params: OID.1.2.643.2.2.35.1 GOSTR3411Params: GOST R 34.11-2012 256 $
Mereka yang ingin dapat mengecek, misalnya, dengan utilitas openssl, lebih disukai dengan mesin
GOST yang terhubung :
$ /usr/local/lirssl_csp_64/bin/lirssl_static asn1parse -inform DER -in ASN1_PUBINFO.der 0:d=0 hl=2 l= 102 cons: SEQUENCE 2:d=1 hl=2 l= 31 cons: SEQUENCE 4:d=2 hl=2 l= 8 prim: OBJECT :GOST R 34.10-2012 with 512 bit modulus 14:d=2 hl=2 l= 19 cons: SEQUENCE 16:d=3 hl=2 l= 7 prim: OBJECT :id-GostR3410-2001-CryptoPro-A-ParamSet 25:d=3 hl=2 l= 8 prim: OBJECT :GOST R 34.11-2012 with 256 bit hash 35:d=1 hl=2 l= 67 prim: BIT STRING $
Seperti yang Anda lihat, struktur ASN1 yang dihasilkan berhasil diuji di mana-mana.
Algoritme dan utilitas yang diusulkan untuk membentuk struktur ASN1 tidak memerlukan penggunaan kompiler ASN1 dan pustaka ekstensi (openssl yang sama) dan ternyata sangat nyaman digunakan. Kami akan memanggil mereka di artikel berikutnya, ketika keinginan
Pas akan terpenuhi dan utilitas grafis akan disajikan yang tidak hanya "parsing sertifikat" dan memverifikasi validitasnya, tetapi juga menghasilkan pasangan kunci pada token PKCS # 11, menghasilkan dan menandatangani permintaan untuk sertifikat yang memenuhi syarat. Dengan permintaan ini, Anda dapat dengan aman pergi ke CA untuk mendapatkan sertifikat. Menjelang pertanyaan, saya segera mencatat bahwa dalam kasus terakhir, token harus disertifikasi sebagai sistem perlindungan informasi kriptografis dalam sistem sertifikasi FSB Rusia.