📜 ⬆️ ⬇️

Electronic signature in the browser using OpenSSL and SKZI Rutoken EDS

UPDATE. Ready solution for electronic signature in the browser - Ruken Plugin

The need for solutions to help implement an electronic signature in the "browser" is increasing. The main requirements for such solutions are the support of Russian cryptoalgorithms, key security and normal usability. In this topic we will write a browser-based cryptographic java-applet into which OpenSSL GOST is integrated with the Rutoken EDS support module. This applet does not require installation of any client software (except for the java-machine, of course) and allows you to sign files through a browser in the PKCS # 7 format using the hardware implementation of Russian cryptographic standards on the “board” of the USB-token Rutoken EDS. For demonstration, the topic will be given an example of an HTML page using this applet. On the page, you can generate a key inside the token, create a PKCS # 10 application for a certificate for this key, get a test certificate, write it to a token, sign the file.

The solution architecture is shown in the figure:
')
image

Rutoken EDS is a compact USB device, inside of which are protected memory and a microcontroller that implements Russian cryptographic standards. Since the operations are carried out on the “board” of the token, the user key cannot be stolen.

OpenSSL is a cross-platform open source software package that supports most modern cryptographic algorithms, formats like PKCS, CMS, S / MIME, SSL / TLS protocols, etc. Starting with version 1.0.0, OpenSSL provides full-featured support for Russian cryptoalgorithms.

About the plugin that provides support for Rutoken EDS in OpenSSL, you can read right there in Habré
habrahabr.ru/blogs/infosecurity/134725 .

The Signature library is a dynamic library, "add-on" over OpenSSL, which encapsulates OpenSSL calls. Provides a JNI interface for use in Java. We will write this library.

Cryptographic java applet



We do the project as it is done in Eclipse. Add a package Rutoken to the project, and add the OpenSSL class to it, which is responsible for interacting with the Signature native library, and the OpenSSL_Wrapper class, which inherits from the applet.

ATTENTION! It is better to leave the names unchanged, because otherwise you will have to change the names of the Signature library functions of the JNI interface.

Java-applet will store all the necessary libraries inside the JAR-archive as resources, and when loading on the web-page it will unpack them into the% TEMP% folder and use them from there, therefore we add the necessary binaries to the package as resources.

Full list of binaries:

libeay32.dll - OpenSSL library
gost.dll - GOST support module in OpenSSL
pkcs11_gost.dll - Rootoken digital signature support module in OpenSSL
signature.dll - "add-on" over OpenSSL, providing a JNI interface
rtPKCS11ECP.dll - PKCS # 11 library for Rutoken EDS (distributed by vendor)
libp11.dll - "add-on" above the library PKCS # 11
libltdl3.dll - additional library

Collected libraries for the win32 platform can be downloaded at www.rutoken.ru/download/software/forum/openssl-rutoken-win32.zip

Listing 1. OpenSSL class

package Rutoken; import javax.swing.*; public class OpenSSL { static { try { System.load(OpenSSL_Wrapper.temp + "/libeay32.dll"); System.load(OpenSSL_Wrapper.temp + "/signature.dll"); } catch(Exception ex) { JOptionPane.showMessageDialog(null, ex.getMessage()); } } public native static int Init ( String install_path //     ); //     public native static int SaveCertToToken ( String cert, //   PEM String cert_file, //     PEM String slot_cert_id, // SLOT : ID  String label // label ); //      34-10.2001         PKCS#10 public native static String CreateKeyRequest ( String pin, // PIN-  String slot_key_id, // :ID  String paramset, //   String request_file, // ,      String common_name, //    String org, //  String org_unit, //   String city, //  String region, //  String country, //  String email, //   String keyUsages, //   ,  , String extendedKeyUsages //    ,  , ); //   PKCS#7    34.10-2001   PEM public native static String SignFile ( String pin, // PIN-  String slot_key_id, // :ID  String slot_cert_id, // :ID  String file_path, //   ,    int detached //  : 1-, 0- ); } 


Listing 2. OpenSSL_Wrapper class

 package Rutoken; import java.applet.Applet; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.swing.JOptionPane; public class OpenSSL_Wrapper extends Applet { public static String temp=System.getProperty("java.io.tmpdir")+"{rutoken-ecp-1983-8564}"; OpenSSL nativeObj; public void CopyResToFile(String resource, String file) { try { File f=new File(file); InputStream inputStream= getClass().getResourceAsStream(resource); OutputStream out=new FileOutputStream(f); byte buf[]=new byte[1024]; int len; while((len=inputStream.read(buf))>0) out.write(buf,0,len); out.close(); inputStream.close(); } catch (IOException e) {} } public void init() { File f = new File(temp); f.mkdir(); // openssl CopyResToFile("libeay32.dll", temp+"/libeay32.dll"); // openssl plugin CopyResToFile("gost.dll", temp+"/gost.dll"); // openssl plugin    CopyResToFile("pkcs11_gost.dll", temp+"/pkcs11_gost.dll"); //  PKCS#11    CopyResToFile("rtPKCS11ECP.dll", temp+"/rtPKCS11ECP.dll"); // libtool CopyResToFile("libltdl3.dll", temp+"/libltdl3.dll"); // openssl wrapper CopyResToFile("signature.dll", temp+"/signature.dll"); // PKCS#11 wrapper CopyResToFile("libp11.dll", temp+"/libp11.dll"); if(1!=OpenSSL.Init(temp)) { JOptionPane.showMessageDialog(null, "   OpenSSL!"); } } //     public int SaveCertToToken ( String cert, //   PEM String cert_file, //     PEM String slot_cert_id, // SLOT : ID  String label // label ) { return OpenSSL.SaveCertToToken(cert, cert_file, slot_cert_id, label); } //      34-10.2001         PKCS#10 public String CreateKeyRequest ( String pin, // PIN-  String slot_key_id, // :ID  String paramset, //   String request_file, // ,      String common_name, //    String org, //  String org_unit, //   String city, //  String region, //  String country, //  String email, //   String keyUsages, //   ,  , String extendedKeyUsages //    ,  , ) { return OpenSSL.CreateKeyRequest( pin, slot_key_id, paramset, request_file, common_name, org, org_unit, city, region, country, email, keyUsages, extendedKeyUsages); } //   PKCS#7    34.10-2001   PEM public String SignFile( String pin, // PIN-  String slot_key_id, // :ID  String slot_cert_id, // :ID  String file_path, //   ,    int detached //  : 1-, 0- ) { return OpenSSL.SignFile(pin, slot_key_id, slot_cert_id, file_path, detached); } } 


Package Rutoken should be exported to a JAR archive, and then signed with the jarsigner utility.

Signature Library



It implements the following API.

1. Initialization function:

 int init( const char* install_path ); 


2. The function of generating a signature key GOST R 34-10.2001 on Rutoken EDS and creating an application for a certificate in PKCS # 10 format

 char* create_key_request( const char* pin, /* PIN-  */ const char* slot_key_id, /* :ID  */ const char* paramset, /*   */ const char* request_file, /* ,      */ const char* common_name, /*    */ const char* org, /*  */ const char* org_unit, /*   */ const char* city, /*  */ const char* region, /*  */ const char* country, /*  */ const char* email, /* email */ const char* keyUsages, /*   ,  , */ const char* extendedKeyUsages /*    ,  , */ ); 


3. The function of recording the certificate on Rutoken EDS

 int save_pem_cert( const char* cert, /*   PEM */ const char* cert_file, /*    PEM */ const char* slot_cert_id, /* SLOT : ID  */ const char* label /* label */ ); 


4. The function of signing a file in the format PKCS # 7 according to GOST R 34.10-2001:

 char* sign_file( const char* pin, /* PIN-  */ const char* slot_key_id, /* :ID  */ const char* slot_cert_id, /* :ID  */ const char* file_path, /*   ,    */ int detached /*  : 1-, 0- */ ); 


The initialization function has one parameter — the path to the folder into which the applet unpacked the binaries. This is enough for our Signature library to download OpenSSL, a plugin for it, and a PKCS # 11 library for working with Rutoken DS.

To interact with the Java applet, the library also implements the JNI interface.

Listing 3. Signature JNI interface implementation

 #include <windows.h> #include "signature.h" #include <jni.h> #ifdef __cplusplus extern "C" { #endif JNIEXPORT jint JNICALL Java_Rutoken_OpenSSL_Init ( JNIEnv* env, jclass cl, jstring install_path ) { if(!install_path) return 0; return (jint)init((*env).GetStringUTFChars(install_path, false)); } JNIEXPORT jint JNICALL Java_Rutoken_OpenSSL_SaveCertToToken ( JNIEnv* env, jclass cl, jstring cert, //   PEM jstring cert_file, //     PEM jstring slot_cert_id, // SLOT : ID  jstring label ) { char* pCert = NULL; char* pCertFile = NULL; char* pId = NULL; char* pLabel = NULL; if( (!cert && !cert_file) || !slot_cert_id) return 0; if(cert) pCert=(char*)(*env).GetStringUTFChars(cert, false); if(cert_file) pCertFile=(char*)(*env).GetStringUTFChars(cert_file, false); pId=(char*)(*env).GetStringUTFChars(slot_cert_id, false); if(label) pLabel=(char*)(*env).GetStringUTFChars(label, false); return (jint)save_pem_cert( pCert, pCertFile, pId, pLabel); } JNIEXPORT jstring JNICALL Java_Rutoken_OpenSSL_CreateKeyRequest ( JNIEnv* env, jclass cl, jstring pin, // PIN-  jstring slot_key_id, // :ID  jstring paramset, //   jstring request_file, // ,      jstring common_name, //    jstring org, //  jstring org_unit, //   jstring city, //  jstring region, //  jstring country, //  jstring email, //   jstring keyUsages, //   ,  , jstring extendedKeyUsages //    ,  , ) { char* pPin = NULL; char* pSlotKeyId = NULL; char* pParamset = NULL; char* pCommonName = NULL; char* pOrg = NULL; char* pOrgUnit = NULL; char* pCity = NULL; char* pRegion = NULL; char* pCountry = NULL; char* pEmail = NULL; char* pKeyUsages = NULL; char* pExtendedKeyUsages = NULL; char* request = NULL; if(!pin || !slot_key_id || !paramset || !common_name || !email || !keyUsages || !extendedKeyUsages) return NULL; pPin=(char*)(*env).GetStringUTFChars(pin, false); pSlotKeyId=(char*)(*env).GetStringUTFChars(slot_key_id, false); pParamset=(char*)(*env).GetStringUTFChars(paramset, false); pCommonName=(char*)(*env).GetStringUTFChars(common_name, false); pOrg=(char*)(*env).GetStringUTFChars(org, false); pEmail=(char*)(*env).GetStringUTFChars(email, false); pKeyUsages=(char*)(*env).GetStringUTFChars(keyUsages, false); pExtendedKeyUsages=(char*)(*env).GetStringUTFChars(extendedKeyUsages, false); if(org) pOrg=(char*)(*env).GetStringUTFChars(org, false); if(org_unit) pOrgUnit=(char*)(*env).GetStringUTFChars(org_unit, false); if(city) pCity=(char*)(*env).GetStringUTFChars(city, false); if(region) pRegion=(char*)(*env).GetStringUTFChars(region, false); if(country) pCountry=(char*)(*env).GetStringUTFChars(country, false); request=(char*)create_key_request( pPin, pSlotKeyId, pParamset, NULL, pCommonName, pOrg, pOrgUnit, pCity, pRegion, pCountry, pEmail, pKeyUsages, pExtendedKeyUsages); if(request) return (*env).NewStringUTF((const char*)request); else return NULL; } JNIEXPORT jstring JNICALL Java_Rutoken_OpenSSL_SignFile ( JNIEnv* env, jclass cl, jstring pin, // PIN-  jstring slot_key_id, // :ID  jstring slot_cert_id, // :ID  jstring file_path, //   ,    jint detached ) { char* pPin = NULL; char* pKeyID = NULL; char* pCertID = NULL; char* pFilePath = NULL; char* signature = NULL; if(!pin || !slot_key_id || !slot_cert_id || !file_path) return NULL; pPin=(char*)(*env).GetStringUTFChars(pin, false); pKeyID=(char*)(*env).GetStringUTFChars(slot_key_id, false); pCertID=(char*)(*env).GetStringUTFChars(slot_cert_id, false); pFilePath=(char*)(*env).GetStringUTFChars(file_path, false); signature=sign_file( pPin, pKeyID, pCertID, pFilePath, (int)detached); if(signature) return (*env).NewStringUTF((const char*)signature); else return NULL; } #ifdef __cplusplus } #endif 


Listing 4. Signature library implementation

 #include <windows.h> #include <openssl/lhash.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/crypto.h> /* for CRYPTO_* and SSLeay_version */ #include <openssl/rand.h> #include <openssl/md4.h> #include <openssl/des.h> #include <openssl/engine.h> #include <openssl/pkcs12.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #include <openssl/cms.h> #define CMD_LOAD_CERT_CTRL (ENGINE_CMD_BASE+5) #define CMD_SAVE_CERT_CTRL (ENGINE_CMD_BASE+6) #define CMD_LOGOUT (ENGINE_CMD_BASE+8) #define ENGINE_PKCS11_PIN_MESSAGE "PKCS#11 token PIN" char modules_path[MAX_PATH]; /*     engine_pkcs11 */ typedef struct _CERT_PKCS11_INFO { const char* s_slot_cert_id; X509* cert; const char* label; } CERT_PKCS11_INFO; /* callback  PIN- */ int pin_cb(UI *ui, UI_STRING *uis) { char* pPin=NULL; char* pString=NULL; pString=(char*)UI_get0_output_string(uis); if(!pString) return 0; if(!strncmp(pString, ENGINE_PKCS11_PIN_MESSAGE, strlen(ENGINE_PKCS11_PIN_MESSAGE))) { pPin=(char*)UI_get_app_data(ui); if(!pPin) return 0; UI_set_result(ui, uis, pPin); return 1; } else return 0; } /*     */ X509_EXTENSION* create_X509_extension ( char* name, char *value ) { X509_EXTENSION *ex; ex = X509V3_EXT_conf(NULL, NULL, name, value); if (!ex) return NULL; return ex; } ENGINE* engine_gost=NULL; ENGINE* LoadEngine(const char* pin) { ENGINE* engine_pkcs11=NULL; char enginePkcs11[MAX_PATH]; char engineGost[MAX_PATH]; char rtPkcs11ECP[MAX_PATH]; /*   engine GOST */ strcpy(engineGost, modules_path); strcat(engineGost, "\\gost.dll"); engine_gost=ENGINE_by_id("dynamic"); if(!engine_gost) return NULL; if(!ENGINE_ctrl_cmd_string(engine_gost, "SO_PATH", engineGost, 0) || !ENGINE_ctrl_cmd_string(engine_gost, "ID", "gost", 0) || !ENGINE_ctrl_cmd_string(engine_gost, "LOAD", NULL, 0)) return NULL; if(!ENGINE_init(engine_gost)) { ENGINE_free(engine_gost); return NULL; } /*   engine PKCS11 */ strcpy(enginePkcs11, modules_path); strcat(enginePkcs11, "\\pkcs11_gost.dll"); strcpy(rtPkcs11ECP, modules_path); strcat(rtPkcs11ECP, "\\rtPKCS11ECP.dll"); /* WARNING:    */ ENGINE_add(engine_gost); engine_pkcs11=ENGINE_by_id("dynamic"); if(!engine_pkcs11) return NULL; if(!ENGINE_ctrl_cmd_string(engine_pkcs11, "SO_PATH", enginePkcs11, 0) || !ENGINE_ctrl_cmd_string(engine_pkcs11, "ID", "pkcs11_gost", 0) || !ENGINE_ctrl_cmd_string(engine_pkcs11, "LOAD", NULL, 0) || !ENGINE_ctrl_cmd_string(engine_pkcs11, "MODULE_PATH", rtPkcs11ECP, 0)) return NULL; if(pin) { if(!ENGINE_ctrl_cmd_string(engine_pkcs11, "PIN", pin, 0)) return NULL; } if(!ENGINE_init(engine_pkcs11)) { ENGINE_free(engine_pkcs11); return NULL; } if(!ENGINE_set_default(engine_pkcs11, ENGINE_METHOD_ALL)) { ENGINE_free(engine_pkcs11); return NULL; } return engine_pkcs11; } X509_REQ* create_request ( EVP_PKEY* pKey, const char* common_name, /*    */ const char* org, /*  */ const char* org_unit, /*   */ const char* city, /*  */ const char* region, /*  */ const char* country, /*  */ const char* email, /*   */ const char* keyUsages, /*   ,  , */ const char* extendedKeyUsages /*    ,  ; */ ) { X509_REQ* req; X509_NAME* subject; BOOL bGoodEmail=TRUE; subject = X509_NAME_new(); if(common_name && strlen(common_name)>0) { if(!X509_NAME_add_entry_by_NID( subject, NID_commonName, MBSTRING_UTF8, (unsigned char*)common_name, -1, -1, 0)) { X509_NAME_free(subject); return NULL; } } if(org && strlen(org)>0) if(!X509_NAME_add_entry_by_NID( subject, NID_organizationName, MBSTRING_UTF8, (unsigned char*)org, -1, -1, 0)) { X509_NAME_free(subject); return NULL; } if(org_unit && strlen(org_unit)>0) if(!X509_NAME_add_entry_by_NID( subject, NID_organizationalUnitName, MBSTRING_UTF8, (unsigned char*)org_unit, -1, -1, 0)) { X509_NAME_free(subject); return NULL; } if(city && strlen(city)>0) if(!X509_NAME_add_entry_by_NID( subject, NID_localityName, MBSTRING_UTF8, (unsigned char*)city, -1, -1, 0)) { X509_NAME_free(subject); return NULL; } if(region && strlen(region)>0) if(!X509_NAME_add_entry_by_NID( subject, NID_stateOrProvinceName, MBSTRING_UTF8, (unsigned char*)region, -1, -1, 0)) { X509_NAME_free(subject); return NULL; } if(country && strlen(country)>0) if(!X509_NAME_add_entry_by_NID( subject, NID_countryName, MBSTRING_UTF8, (unsigned char*)country, -1, -1, 0)) { X509_NAME_free(subject); return NULL; } if(email && strlen(email)>0) { for (int i=0; i<strlen(email); i++) if (email[i]&0x80) { bGoodEmail=FALSE; break; } if(bGoodEmail) { if(!X509_NAME_add_entry_by_NID( subject, NID_pkcs9_emailAddress, MBSTRING_UTF8, (unsigned char*)email, -1, -1, 0)) { X509_NAME_free(subject); return NULL; } } } req=X509_REQ_new(); if(!req) { X509_NAME_free(subject); return NULL; } /*   */ if(!X509_REQ_set_version(req, 0)) { X509_REQ_free(req); X509_NAME_free(subject); return NULL; } /*  subject */ if(!X509_REQ_set_subject_name(req, subject)) { X509_REQ_free(req); X509_NAME_free(subject); return NULL; } /*    */ if(!X509_REQ_set_pubkey(req, pKey)) { X509_REQ_free(req); X509_NAME_free(subject); return NULL; } /* "digitalSignature,keyEncipherment" */ X509_EXTENSION* keyUsageExt = create_X509_extension("keyUsage", (char*)keyUsages); if(!keyUsageExt) { X509_REQ_free(req); X509_NAME_free(subject); return NULL; } /* "clientAuth,emailProtection" */ X509_EXTENSION* extendedKeyUsageExt = create_X509_extension("extendedKeyUsage", (char*)extendedKeyUsages); if(!extendedKeyUsageExt) { X509_EXTENSION_free(keyUsageExt); X509_REQ_free(req); X509_NAME_free(subject); return NULL; } STACK_OF(X509_EXTENSION)* extension_stack = sk_X509_EXTENSION_new_null(); if(!extension_stack) { X509_EXTENSION_free(extendedKeyUsageExt); X509_EXTENSION_free(keyUsageExt); X509_REQ_free(req); X509_NAME_free(subject); return NULL; } sk_X509_EXTENSION_push(extension_stack, keyUsageExt); sk_X509_EXTENSION_push(extension_stack, extendedKeyUsageExt); if(!X509_REQ_add_extensions(req, extension_stack)) { X509_EXTENSION_free(extendedKeyUsageExt); X509_EXTENSION_free(keyUsageExt); X509_REQ_free(req); X509_NAME_free(subject); return NULL; } if(!X509_REQ_sign(req, pKey, EVP_get_digestbyname("md_gost94"))) { X509_EXTENSION_free(extendedKeyUsageExt); X509_EXTENSION_free(keyUsageExt); X509_REQ_free(req); X509_NAME_free(subject); return NULL; } sk_X509_EXTENSION_pop_free (extension_stack,X509_EXTENSION_free); X509_NAME_free(subject); return req; } ENGINE* engine_pkcs11 = NULL; extern "C" __declspec( dllexport ) int init(const char* install_path) { HMODULE hLibp11 = NULL; HMODULE hLibTdl = NULL; char libp11[MAX_PATH]; char libtdl[MAX_PATH]; strcpy(modules_path, install_path); strcpy(libtdl, install_path); strcat(libtdl, "\\libltdl3.dll"); hLibTdl=LoadLibraryA(libtdl); if(!hLibTdl) return 0; strcpy(libp11, install_path); strcat(libp11, "\\libp11.dll"); hLibp11=LoadLibraryA(libp11); if(!hLibp11) return 0; /*  OpenSSL */ ENGINE_load_builtin_engines(); OPENSSL_add_all_algorithms_noconf(); engine_pkcs11=LoadEngine(NULL); if(!engine_pkcs11) return 0; return 1; } /*     */ extern "C" __declspec( dllexport ) int save_pem_cert ( const char* cert, /*   PEM */ const char* cert_file, /*     PEM */ const char* slot_cert_id, /* SLOT : ID  */ const char* label /* label */ ) { int len = 0; X509* x509 = NULL; BIO* bio_cert = NULL; BIO* bio_der = NULL; CERT_PKCS11_INFO cert_info; /*   */ if(cert) { bio_cert=BIO_new(BIO_s_mem()); if(!bio_cert) return 0; if(!BIO_puts(bio_cert, cert)) return 0; x509=PEM_read_bio_X509(bio_cert, NULL, NULL, NULL); if(!x509) { BIO_free(bio_cert); return 0; } } else if(cert_file) { bio_cert=BIO_new_file(cert_file, "rb"); if(!bio_cert) return 0; x509=PEM_read_bio_X509(bio_cert, NULL, NULL, NULL); if(!x509) { BIO_free(bio_cert); return 0; } } cert_info.s_slot_cert_id=slot_cert_id; cert_info.cert=x509; cert_info.label=label; if(!ENGINE_ctrl(engine_pkcs11, CMD_SAVE_CERT_CTRL, 0, (void*)&cert_info, NULL)) { return 0; } return 1; } /*      34-10.2001         PKCS#10 */ extern "C" __declspec( dllexport ) char* create_key_request( const char* pin, /* PIN-  */ const char* slot_key_id, /* :ID  */ const char* paramset, /*   */ const char* request_file, /* ,      */ const char* common_name, /*    */ const char* org, /*  */ const char* org_unit, /*   */ const char* city, /*  */ const char* region, /*  */ const char* country, /*  */ const char* email, /*   */ const char* keyUsages, /*   ,  , */ const char* extendedKeyUsages /*    ,  , */ ) { BIO* bio_req=NULL; BIO* bio_key=NULL; X509_REQ* pRequest=NULL; BUF_MEM* pbmReq; char* cRequest=NULL; UI_METHOD* uim=NULL; int reason=0; EVP_PKEY* key1=NULL; EVP_PKEY* newkey=NULL; EVP_PKEY_CTX* ctx=NULL; key1=EVP_PKEY_new(); if(!key1) { return NULL; } if(!EVP_PKEY_set_type(key1, 811)) { EVP_PKEY_free(key1); return NULL; } ctx=EVP_PKEY_CTX_new(key1, NULL); if(!ctx) { EVP_PKEY_free(key1); return NULL; } if(!EVP_PKEY_keygen_init(ctx)) { EVP_PKEY_CTX_free(ctx); return NULL; } if(!EVP_PKEY_CTX_ctrl_str(ctx, "paramset", paramset)) { EVP_PKEY_CTX_free(ctx); return NULL; } if(!EVP_PKEY_CTX_ctrl_str(ctx, "slot_key_id", slot_key_id)) { EVP_PKEY_CTX_free(ctx); return NULL; } if(!EVP_PKEY_CTX_ctrl_str(ctx, "pin", pin)) { EVP_PKEY_CTX_free(ctx); return NULL; } if(!EVP_PKEY_keygen(ctx,&newkey)) { EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } pRequest=create_request( newkey, common_name, org, org_unit, city, region, country, email, keyUsages, extendedKeyUsages); if(!pRequest) { EVP_PKEY_free(newkey); EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } bio_req=BIO_new(BIO_s_mem()); if(!bio_req) { X509_REQ_free(pRequest); EVP_PKEY_free(newkey); EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } if(!PEM_write_bio_X509_REQ(bio_req, pRequest)) { BIO_free(bio_req); X509_REQ_free(pRequest); EVP_PKEY_free(newkey); EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } BIO_get_mem_ptr(bio_req, &pbmReq); if(!pbmReq) { BIO_free(bio_req); X509_REQ_free(pRequest); EVP_PKEY_free(newkey); EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } cRequest=(char*)OPENSSL_malloc(pbmReq->length+1); if(!cRequest) { BIO_free(bio_req); X509_REQ_free(pRequest); EVP_PKEY_free(newkey); EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } memset(cRequest, 0, pbmReq->length+1); memcpy(cRequest, pbmReq->data, pbmReq->length); BIO_free(bio_req); bio_req=NULL; if(request_file) { bio_req=BIO_new_file(request_file, "wb"); if(!bio_req) { X509_REQ_free(pRequest); EVP_PKEY_free(newkey); EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } if(!PEM_write_bio_X509_REQ(bio_req, pRequest)) { BIO_free(bio_req); EVP_PKEY_free(newkey); EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } BIO_free(bio_req); } X509_REQ_free(pRequest); EVP_PKEY_free(newkey); EVP_PKEY_CTX_free(ctx); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return cRequest; } /*   PKCS#7    34-10.2001   PEM */ extern "C" __declspec( dllexport ) char* sign_file( const char* pin, /* PIN-  */ const char* slot_key_id, /* :ID  */ const char* slot_cert_id, /* :ID  */ const char* file_path, /*   ,    */ int detached /*  : 1-, 0- */ ) { BIO* bio_cert = NULL; BIO* in = NULL; BIO* out = NULL; BUF_MEM* pbmOut = NULL; EVP_PKEY* pKey = NULL; UI_METHOD* uim = NULL; PKCS7* p7 = NULL; char* pSignature = NULL; int flags = 0; int reason = 0; CERT_PKCS11_INFO cert_info; uim=UI_create_method("RutokenECP"); if(!uim) return NULL; UI_method_set_reader(uim, pin_cb); /*    */ pKey=ENGINE_load_private_key(engine_pkcs11, slot_key_id, uim, (void*)pin); if(!pKey) { /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } memset(&cert_info, 0, sizeof(cert_info)); cert_info.s_slot_cert_id=slot_cert_id; /*     */ if(!ENGINE_ctrl(engine_pkcs11, CMD_LOAD_CERT_CTRL, 0, &cert_info, NULL)) { EVP_PKEY_free(pKey); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } BIO_free(bio_cert); in=BIO_new_file(file_path, "rb"); if(!in) { EVP_PKEY_free(pKey); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } out=BIO_new(BIO_s_mem()); if(!out) { BIO_free(in); EVP_PKEY_free(pKey); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } if(detached) flags=PKCS7_DETACHED|PKCS7_BINARY; else flags=PKCS7_BINARY; /*  pkcs#7 */ p7=PKCS7_sign(cert_info.cert, pKey, NULL, in, flags); if(!p7) { BIO_free(out); BIO_free(in); EVP_PKEY_free(pKey); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } if(!PEM_write_bio_PKCS7(out, p7)) { PKCS7_free(p7); BIO_free(out); BIO_free(in); EVP_PKEY_free(pKey); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } BIO_get_mem_ptr(out, &pbmOut); if(!pbmOut) { PKCS7_free(p7); BIO_free(out); BIO_free(in); EVP_PKEY_free(pKey); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } pSignature=(char*)OPENSSL_malloc(pbmOut->length+1); if(!pSignature) { PKCS7_free(p7); BIO_free(out); BIO_free(in); EVP_PKEY_free(pKey); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return NULL; } memset(pSignature, 0, pbmOut->length+1); memcpy(pSignature, pbmOut->data, pbmOut->length); CRYPTO_cleanup_all_ex_data(); PKCS7_free(p7); BIO_free(out); BIO_free(in); EVP_PKEY_free(pKey); /* logout */ ENGINE_ctrl(engine_pkcs11, CMD_LOGOUT, 0, (void*)slot_key_id, NULL); return pSignature; } 


Those interested can make this library cross-platform (replacing LoadLibrary with an appropriate call), expand this library with the functions they need.

HTML demo page



Listing 5. Sample HTML page where JavaScript uses our applet

 <html><head> <title>-  </title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="keywords" content=""> </head> <body bgcolor="#ffffff"> <applet code="Rutoken.OpenSSL_Wrapper.class" archive="Rutoken.jar" height="0" width="0"> <table> <tbody><tr width="100%" bgcolor="#FFF8DC" valign="top"> <td align="right" height="40" valign="middle" width="10000"><span class="warning">     plugin JAVA.   JAVA   <a href="http://www.java.com/ru/download/windows_xpi.jsp?locale=ru">...</a></span> </td> </tr> </tbody></table> </applet> <center> <script type="text/javascript"> var loaded=0; function createKeyRequest() { var request; var pin; if(!document.getElementById("common_name").value) { alert("!\n  !.") return; } if(!document.getElementById("email").value) { alert("!\n email!.") return; } if(!document.getElementById("pin").value) { alert("!\n PIN!.") return; } pin=document.getElementById("pin").value; if(loaded==0) { var applet = "<applet id='RutokenApplet' style='visibility: hidden' name='RutokenApplet' archive='Rutoken.jar' code='Rutoken.OpenSSL_Wrapper.class' width='0' height='0'></applet>"; var body = document.getElementsByTagName("body")[0]; var div = document.createElement("div"); div.innerHTML = applet; body.appendChild(div); loaded=1; } request = document.RutokenApplet.CreateKeyRequest( pin, document.getElementById("keyId").value, "A", null, document.getElementById("common_name").value, document.getElementById("org").value, document.getElementById("org_unit").value, document.getElementById("city").value, document.getElementById("region").value, document.getElementById("country").value, document.getElementById("email").value, "digitalSignature,keyEncipherment", "clientAuth,emailProtection"); if(!request) { alert("     !"); return; } document.getElementById('request').value=request; } function saveCertToToken() { if (loaded == 0) { var applet = "<applet id='RutokenApplet' style='visibility: hidden' name='RutokenApplet' archive='Rutoken.jar' code='Rutoken.OpenSSL_Wrapper.class' width='0' height='0'></applet>"; var body = document.getElementsByTagName("body")[0]; var div = document.createElement("div"); div.innerHTML = applet; body.appendChild(div); loaded = 1; } if(!document.getElementById('certarea').value) { alert("    !"); return; } if (!document.RutokenApplet.SaveCertToToken(document.getElementById('certarea').value, null, document.getElementById("keyId").value, null)) { alert("     !"); return; } else { alert("!"); } } function signFile() { var signature; if (!document.getElementById("pin").value) { alert("!\n PIN!.") return; } if (!document.getElementById('fileForSign').value) { alert("   !"); return; } if (loaded == 0) { var applet = "<applet id='RutokenApplet' style='visibility: hidden' name='RutokenApplet' archive='Rutoken.jar' code='Rutoken.OpenSSL_Wrapper.class' width='0' height='0'></applet>"; var body = document.getElementsByTagName("body")[0]; var div = document.createElement("div"); div.innerHTML = applet; body.appendChild(div); loaded = 1; } signature=document.RutokenApplet.SignFile( document.getElementById("pin").value, document.getElementById("keyId").value, document.getElementById("keyId").value, document.getElementById('fileForSign').value, 1); if(!signature) { alert("   !"); return; } else { alert("!"); document.getElementById('signature').value = signature; } } </script> <table align="center" border="0" cellpadding="0" cellspacing="0" width="760"> <tbody><tr valign="top"> <td width="8""> </td> <td class="m9" align="center" width="125"> </td> <td><table align="center" width="600"><tbody><tr valign="top"><td width="20"> </td> <td> <!--BEGIN CONTENT--> <table border="0" cellpadding="5" cellspacing="0"> <tbody><tr valign="top"> <td><table bgcolor="" border="0" cellpadding="1" cellspacing="0"><tbody><tr><td> <table border="0" cellpadding="8" cellspacing="0" width="400"> <tbody> <tr> <td valign="top" width="400"> <table border="0" cellpadding="0" cellspacing="0"> <tbody> <tr> <td colspan="2" align="center" height="6"><h3> </h3> </td> </tr> <tr> <td colspan="2" height="10"> </td> </tr> <tr> <td colspan="2" align="center" height="6"><h4>   :</h4> </td> </tr> <tr> <td id="locNameAlign" align="left"><span id="spnNameLabel"><locid id="locNameLabel"><font size="-1">*:</font></locid></span> </td> <td><input id="common_name" maxlength="64" size="38" name="tbCommonName"> </td> </tr> <tr> <td colspan="2" height="18"> </td> </tr> <tr> <td id="locEmailAlign" align="left"><span id="spnEmailLabel"><locid id="locEmailLabel"><font size="-1">Email*:</font></locid></span> </td> <td><input id="email" maxlength="128" size="38" name="tbEmail"> </td> </tr> <tr> <td colspan="2" height="18"> </td> </tr> <tr><td id="locCompanyAlign" align="left"><span id="spnCompanyLabel"><locid id="locOrgLabel"><font size="-1">:</font></locid></span></td> <td><input id="org" maxlength="64" size="38" name="tbOrg"></td> </tr> <tr> <td colspan="2" height="18"> </td> </tr> <tr> <td id="locDepartmentAlign" align="left"><span id="spnDepartmentLabel"><locid id="locOrgUnitLabel"><font size="-1">:</font></locid></span></td> <td><input id="org_unit" maxlength="64" size="38" name="tbOrgUnit"></td></tr> <tr> <td colspan="2" height="18"> </td> </tr> <tr> </tr><tr> <td id="locCityAlign" align="left"><span id="spnCityLabel"><locid id="locLocalityLabel"><font size="-1">:</font></locid></span></td> <td><input id="city" maxlength="128" size="38" name="tbLocality"></td></tr> <tr> <td colspan="2" height="18"> </td> </tr> <tr> </tr><tr> <td id="locStateAlign" align="left"><span id="spnStateLabel"><locid id="locStateLabel"><font size="-1">, :</font></locid></span></td> <td><input id="region" maxlength="128" size="38" name="tbState"></td></tr> <tr> <td colspan="2" height="18"> </td> </tr> <tr> </tr><tr> <td id="locCountryAlign" align="left"><span id="spnCountryLabel"><locid id="locCountryLabel"><font size="-1">/:</font></locid></span></td> <td><input id="country" maxlength="2" size="38" value="RU" name="tbCountry"> </td> </tr> <tr> <td colspan="2" height="18"> </td> </tr> <tr> <td colspan="2" height="8"> </td> </tr> <tr> <td id="locKeyIDAlign" align="left"><span id="spnKeyId"><locid id="locKeyIDLabel"><font size="-1"> ID:</font></locid></span></td> <td><input id="keyId" maxlength="20" size="38" name="tbKeyID"></td> </tr> <tr> <td id="tdPin" align="left"><span id="spnPin"><locid id="locPin"><font size="-1"> PIN:</font></locid></span></td> <td><input id="pin" maxlength="20" size="38" type="password"></td> </tr> <tr> <td colspan="2" height="8"> </td> </tr> <tr> <td height="12"> </td> <td align="right"><input disable="true" value="    " onclick="createKeyRequest();" type="button"> </td> </tr> <tr> <td colspan="2" height="20"> </td> </tr> </tbody> </table> <table> <tbody> <tr> <td>     PKCS#10.     <a href="http://ca.cryptocom.ru"> </a></td> </tr> <tr> <td height="3"> </td> </tr> <tr> <td align="right"><textarea id="request" rows="6" cols="44" name="tbRequest"></textarea> </td> </tr> <tr> <td height="6"> </td> </tr> <tr> <td>        PEM  :</a></td> </tr> <tr> <td height="3"> </td> </tr> <tr> <td align="right"><textarea id="certarea" rows="6" cols="44" name="tbCertArea"></textarea> </td> </tr> <tr> <td align="right"><input disable="true" value="    " onclick="saveCertToToken();" type="button"> </td> </tr> <tr> <td height="6"> </td> </tr> <tr> <td>     :</a></td> </tr> <tr> <td align="left"><input id="fileForSign" maxlength="64" size="50" name="tbFileForSign"></td> </tr> <tr> <td height="3"> </td> </tr> <tr> <td align="right"><input disable="true" value=" " onclick="signFile();" type="button"> </td> </tr> <tr> <td align="right"><textarea id="signature" rows="6" cols="44" name="tbSignatureArea"></textarea> </td> </tr> </tbody> </table> </td></tr></tbody> </table> </td></tr></tbody></table> </td></tr> <!--End top half--> </tbody></table> </tbody></table> </center> </body></html> 

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


All Articles