├── lib ├── nodeCryptopro.dll └── nodeCryptopro.so ├── package.json ├── src ├── MakeNodeCryptopro ├── nodeCryptopro.h ├── setenv.sh └── nodeCryptopro.c ├── .gitignore ├── examples └── index.js ├── README.md ├── test └── index.js └── index.js /lib/nodeCryptopro.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SolarLabRU/node-cryptopro/HEAD/lib/nodeCryptopro.dll -------------------------------------------------------------------------------- /lib/nodeCryptopro.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SolarLabRU/node-cryptopro/HEAD/lib/nodeCryptopro.so -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-cryptopro", 3 | "version": "0.1.17", 4 | "description": "Node.js package to use Cryptopro.ru functionality", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "mocha" 11 | }, 12 | "author": "solarl.ru", 13 | "license": "ISC", 14 | "dependencies": { 15 | "ffi": "^2.2.0", 16 | "ref": "^1.3.5", 17 | "ref-array": "^1.2.0", 18 | "ref-struct": "^1.1.0" 19 | }, 20 | "devDependencies": { 21 | "chai": "^4.1.2", 22 | "mocha": "^5.0.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MakeNodeCryptopro: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .cpp 2 | 3 | out=nodeCryptopro 4 | 5 | #CPCCC=$(CCC) 6 | CPCCC=$(CXX) 7 | # For FreeBSD you should add -pthread to LDFLAGS. 8 | CFLAGS=-DUNIX -DHAVE_LIMITS_H $(ARCH_FLAGS) $(add_CPPFLAGS) -I$(CSP_INCLUDE) -I$(CSP_INCLUDE)/cpcsp -I$(CSP_INCLUDE)/asn1c/rtsrc -I$(CSP_INCLUDE)/asn1data -DSIZEOF_VOID_P=$(SIZEOF_VOID_P) -fpic 9 | LDFLAGS= $(ARCH_FLAGS) -L$(CSP_LIB) -lssp -lcapi10 -lcapi20 -lrdrsup $(CSP_EXTRA_LIBS) $(add_ldflags) 10 | 11 | OBJS=$(out).o 12 | $(out): $(OBJS) 13 | $(CPCCC) $(OBJS) $(LDFLAGS) -shared -o $@.so $(add_libs) 14 | .cpp.o: 15 | $(CPCCC) $(CFLAGS) -c -fpic -o $@ $< 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | *.cer 61 | *.o 62 | package-lock.json 63 | 64 | .idea/ 65 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const nodeCryptopro = require('node-cryptopro'); 4 | 5 | const senderContainerName = "5973e5bc6-1e43-6206-c603-21fdd08867e"; 6 | const responderCertFilename = "2012_Cert.cer"; 7 | 8 | const responderContainerName = "5973e5bc6-1e43-6206-c603-21fdd08867e"; 9 | const senderCertFilename = "2012_Cert.cer"; 10 | 11 | 12 | const textToEncode = "text message to encode"; 13 | const buffer = Buffer.from(textToEncode); 14 | 15 | 16 | //Encrypt/Decrypt example 17 | const bytesToEncrypt = new Uint8Array(buffer); 18 | console.log("Bytes to encode:" + bytesToEncrypt); 19 | 20 | let encryptionResult = nodeCryptopro.encrypt(bytesToEncrypt, senderContainerName, responderCertFilename); 21 | 22 | console.log("encryptedBytesArray:" + encryptionResult.encryptedBytesArray); 23 | console.log("KeyBlob:" + encryptionResult.sessionKeyBlob); 24 | console.log("IV:" + encryptionResult.IV); 25 | 26 | let decryptedBytes = nodeCryptopro.decrypt( 27 | encryptionResult.encryptedBytesArray, 28 | responderContainerName, 29 | senderCertFilename, 30 | encryptionResult.IV, 31 | encryptionResult.sessionKeyBlob); 32 | 33 | const decryptedMessage = (new Buffer(decryptedBytes)).toString(); 34 | console.log("Decrypted message:" + decryptedMessage); 35 | 36 | 37 | //Signature example: 38 | const bytesArrayToSign = new Uint8Array(buffer); 39 | console.log("Bytes to sign:" + bytesArrayToSign); 40 | 41 | const signature = nodeCryptopro.signHash(senderContainerName, bytesArrayToSign); 42 | console.log("Signature:" + signature); 43 | 44 | const isVerified = nodeCryptopro.verifySignature(bytesArrayToSign, signature, senderCertFilename); 45 | if(isVerified) { 46 | console.log("Verified"); 47 | } else { 48 | console.log("Verification error"); 49 | } 50 | 51 | //CreateHash example: 52 | const hash = nodeCryptopro.createHash(bytesArrayToSign); 53 | console.log("Hash:" + hash); 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nodeCryptopro 2 | Node.js package to use Cryptopro.ru functionality 3 | 4 | ## Установка и настройка пакета 5 | 6 | Установить КриптоПро CSP: https://cryptopro.ru/downloads 7 | 8 | На данный момент используется версия КриптоПро CSP 4.0 R2. 9 | 10 | ### Ubuntu 11 | 12 | npm install node-cryptopro 13 | 14 | ### Windows 15 | 16 | npm install --global --production windows-build-tools 17 | 18 | npm install node-cryptopro 19 | 20 | ## Использование 21 | 22 | ### Ubuntu 23 | 24 | 1) Создание контейнера и генерация пары закрытого/открытого ключа в хранилище: 25 | 26 | /opt/cprocsp/bin/amd64/csptest -keyset -newkeyset -cont '\\.\HDIMAGE\containerName' -provtype 75 -provider "Crypto-Pro GOST R 34.10-2012 KC1 CSP" 27 | 28 | Для просмотра списка контейнеров используется команда: 29 | 30 | /opt/cprocsp/bin/amd64/csptest -keyset -enum_cont -verifycontext -fqcn 31 | 32 | 2) Создание запроса на получение сертификата: 33 | 34 | /opt/cprocsp/bin/amd64/cryptcp -creatrqst -dn "E=requesteremail@mail.ru, C=RU, CN=localhost, SN=company" -nokeygen -both -ku -cont '\\.\HDIMAGE\containerName' containerName.req 35 | 36 | 3) Отправить запрос: 37 | 38 | http://www.cryptopro.ru/certsrv/ 39 | 40 | 4) Получить сертификат 41 | 42 | 5) Установить сертификат: 43 | 44 | /opt/cprocsp/bin/amd64/certmgr -inst -store umy -file containerName.cer -cont '\\.\HDIMAGE\containerName' 45 | 46 | ## Компиляция .so/.dll библиотеки 47 | 48 | ### Ubuntu 49 | 50 | 1) Установить lsb-cprocsp-devel из дистрибутива КриптоПро CSP или КриптоПро OCSP SDK (https://www.cryptopro.ru/products/pki/ocsp/sdk/downloads), например так: 51 | 52 | cd linux-amd64_deb 53 | 54 | sudo dpkg -i lsb-cprocsp-devel_4.0.0-4_all.deb 55 | 56 | 2) Установить переменные окружения: 57 | 58 | eval \`./setenv.sh --64\` 59 | 60 | 3) Скомпилировать: 61 | 62 | make -f MakeNodeCryptopro 63 | 64 | ### Windows 65 | 66 | 1) Установить КриптоПро OCSP SDK (https://www.cryptopro.ru/products/pki/ocsp/sdk/downloads). 67 | 68 | 2) Установить переменные окружения: 69 | 70 | set PATH=%PATH%C:\Program Files (x86)\Crypto Pro\SDK\include 71 | 72 | set INCLUDE=%INCLUDE%C:\Program Files (x86)\Crypto Pro\SDK\include 73 | 74 | set LIBPATH=%LIBPATH%C:\Program Files (x86)\Crypto Pro\SDK\lib\amd64 75 | 76 | set LIBPATH=%LIBPATH%C:\Program Files (x86)\Crypto Pro\SDK\lib 77 | 78 | 3) Скомпилировать: 79 | 80 | cl.exe /D_USRDLL /D_WINDLL nodeCryptopro.c /link /DLL /OUT:nodeCryptopro.dll -------------------------------------------------------------------------------- /src/nodeCryptopro.h: -------------------------------------------------------------------------------- 1 | #ifndef nodeCryptopro_h__ 2 | #define nodeCryptopro_h__ 3 | 4 | #if defined(WIN32) || defined(_WIN32) 5 | # define EXPORT __declspec(dllexport) 6 | #else 7 | # define EXPORT 8 | #endif 9 | 10 | #define GR3411LEN 32//64 11 | 12 | #define MAX_PUBLICKEYBLOB_SIZE 200 13 | 14 | typedef struct CallResult { 15 | int status; 16 | DWORD errorCode; 17 | char *errorMessage; 18 | } CallResult; 19 | 20 | CallResult ResultSuccess() { 21 | CallResult result = {0, 0, ""}; 22 | return result; 23 | } 24 | 25 | CallResult HandleError(const char *s); 26 | 27 | EXPORT CallResult InitParams(const char* sessionKeyExportAlg, const char* mode); 28 | 29 | EXPORT CallResult AcquireContextForContainer(const char* keyContainer); 30 | 31 | EXPORT CallResult GetPublicKeyFromCertificateFile(BYTE *publicKeyBlob, DWORD *publicKeyBlobLength, const char *certificateFileName); 32 | 33 | EXPORT CallResult GetPublicKeyFromCertificate(BYTE *publicKeyBlob, DWORD *publicKeyBlobLength, const char *certificateSubjectKey); 34 | 35 | EXPORT CallResult GetPublicKeyFromCertificateData(BYTE *publicKeyBlob, DWORD *publicKeyBlobLength, BYTE *certificateData, DWORD certificateDataLength); 36 | 37 | EXPORT CallResult GenerateSessionKey( 38 | DWORD* sessionKeyBlobLength, BYTE* sessionKeyBlob, 39 | const char* senderContainerName, 40 | BYTE* responderPublicKeyBlob, int responderPublicKeyBlobLength, 41 | BYTE* IV, DWORD* IVLength 42 | ); 43 | 44 | EXPORT CallResult SignHash( 45 | const char* keyContainer, 46 | BYTE* messageBytesArray, 47 | DWORD messageBytesArrayLength, 48 | BYTE* signatureBytesArray, 49 | DWORD* signatureBytesArrayLength 50 | ); 51 | 52 | EXPORT CallResult VerifySignature( 53 | BYTE* messageBytesArray, DWORD messageBytesArrayLength, 54 | BYTE* signatureByteArray, DWORD signatureBytesArrayLength, 55 | BYTE* publicKeyBlob, int publicKeyBlobLength, 56 | BOOL *verificationResultToReturn 57 | ); 58 | 59 | EXPORT CallResult SignPreparedHash( 60 | const char* keyContainer, 61 | BYTE* hashBytesArray, 62 | DWORD hashBytesArrayLength, 63 | BYTE* signatureBytesArray, 64 | DWORD* signatureBytesArrayLength 65 | ); 66 | 67 | EXPORT CallResult VerifyPreparedHashSignature( 68 | BYTE* hashBytesArray, DWORD hashBytesArrayLength, 69 | BYTE* signatureByteArray, DWORD signatureBytesArrayLength, 70 | BYTE* publicKeyBlob, int publicKeyBlobLength, 71 | BOOL *verificationResultToReturn 72 | ); 73 | 74 | EXPORT CallResult Encrypt( 75 | DWORD* sessionKeyBlobLength, BYTE* sessionKeyBlob, 76 | const char* senderContainerName, 77 | BYTE* responderPublicKeyBlob, 78 | int responderPublicKeyBlobLength, 79 | BYTE* textToEncrypt, 80 | int textToEncryptLength, 81 | BYTE* IV, 82 | DWORD* IVLength 83 | ); 84 | 85 | EXPORT CallResult EncryptWithSessionKey( 86 | BYTE* sessionKeySimpleBlob, DWORD sessionKeySimpleBlobLength, 87 | const char* senderContainerName, 88 | BYTE* responderPublicKeyBlob, int responderPublicKeyBlobLength, 89 | BYTE* textToEncrypt, 90 | int textToEncryptLength, 91 | BYTE* IV, 92 | int IVLength 93 | ); 94 | 95 | EXPORT CallResult RecodeSessionKey( 96 | BYTE* sessionKeySimpleBlob, DWORD sessionKeySimpleBlobLength, 97 | const char* senderContainerName, 98 | BYTE* oldResponderPublicKeyBlob, int oldResponderPublicKeyBlobLength, 99 | BYTE* newResponderPublicKeyBlob, int newResponderPublicKeyBlobLength, 100 | BYTE* IV, int IVLength 101 | ); 102 | 103 | EXPORT CallResult RecodeSessionKeyForNewContainer( 104 | BYTE* sessionKeySimpleBlob, DWORD sessionKeySimpleBlobLength, 105 | const char* oldContainerName, 106 | const char* newContainerName, 107 | BYTE* oldResponderPublicKeyBlob, int oldResponderPublicKeyBlobLength, 108 | BYTE* newResponderPublicKeyBlob, int newResponderPublicKeyBlobLength, 109 | BYTE* IV, int IVLength 110 | ); 111 | 112 | EXPORT CallResult Decrypt( 113 | const char* responderContainerName, 114 | BYTE* senderPublicKeyBlob, 115 | int senderPublicKeyBlobLength, 116 | BYTE* encryptedText, int encryptedTextLength, 117 | BYTE* IV, int IVLength, 118 | BYTE* keySimpleBlob, int keySimpleBlobLength 119 | ); 120 | 121 | EXPORT CallResult CreateHash(BYTE* bytesArrayToHash, DWORD bytesArrayToHashLength, BYTE* hash, DWORD* hashLength); 122 | 123 | #endif // nodeCryptopro_h__ 124 | -------------------------------------------------------------------------------- /src/setenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ARG=32 3 | #case `isainfo -k` in 4 | #i386|sparc) ARG=32;; 5 | #amd64|sparcv9) ARG=64;; 6 | #esac 7 | case `uname -m` in 8 | x86_64|amd64) ARG=64;; 9 | esac 10 | for i in $* 11 | do 12 | case $i in 13 | --64) ARG=64;; 14 | --32) ARG=32;; 15 | --help|--usage) echo "Usage: setenv.sh options" >&2 16 | echo "--32 - configure 32bit environbent" >&2 17 | echo "--64 - configure 64bit environment" >&2 18 | exit 1;; 19 | esac 20 | done 21 | 22 | LIBDIR64=`echo /opt/cprocsp/lib/amd64 |sed -e 's/ia32/amd64/g;s/ppc$/ppc64/g;s/sparc$/sparcv9/g'` 23 | LIBDIR32=`echo /opt/cprocsp/lib/amd64 |sed -e 's/amd64/ia32/g;s/ppc64/ppc/g;s/sparcv9/sparc/g'` 24 | 25 | if test x$ARG = x64;then 26 | MODVERSFILE=/opt/cprocsp/include/Module.symvers.amd64 27 | else 28 | MODVERSFILE=/opt/cprocsp/include/Module.symvers.ia32 29 | fi 30 | 31 | if test x$ARG = x64 32 | then 33 | libdir=$LIBDIR64 34 | SIZEOF_VOID_P=8 35 | else 36 | libdir=$LIBDIR32 37 | SIZEOF_VOID_P=4 38 | fi 39 | 40 | add_CPPFLAGS= 41 | test -f /usr/include/stdint.h && \ 42 | add_CPPFLAGS="${add_CPPFLAGS} -DHAVE_STDINT_H" 43 | test -f /usr/include/sys/inttypes.h && \ 44 | add_CPPFLAGS="${add_CPPFLAGS} -DHAVE_SYS_INTTYPES_H" 45 | 46 | if test -z "$CXX" && test -z "$CCC";then 47 | echo "No compiler specified... trying to guess" >&2 48 | case `uname -s` in 49 | # SunOS) case `isainfo -k` in 50 | SunOS) case "XXX" in 51 | #for stupid lsb checkers "XXX" (instead of isainfo -k) 52 | amd64) COMPARCH=amd64 53 | test $ARG = 64 && CSP_KERNEL_FLAGS="-DUSE_STD_MM -xmodel=kernel";; 54 | sparcv9) COMPARCH=v9 55 | CSP_KERNEL_FLAGS="-DUSE_STD_MM";; 56 | 57 | esac 58 | if test -f /opt/SUNWspro/bin/CC;then 59 | echo "Studio is found">&2 60 | CCC=/opt/SUNWspro/bin/CC 61 | elif type CC >/dev/null;then 62 | echo "CC is in the path" >&2 63 | CCC=CC 64 | elif type g++ >/dev/null;then 65 | echo "g++ is found">&2 66 | CCC=g++ 67 | else 68 | echo "No C++ found">&2 69 | exit 1 70 | fi 71 | test x$ARG = x64 && case $CCC in 72 | *CC) case `$CCC -V 2>&1|head -1|cut -d' ' -f4` in 73 | 5.9) echo "ARCH_FLAGS=\"-m64\";export ARCH_FLAGS;";; 74 | *) echo "ARCH_FLAGS=\"-xarch=$COMPARCH\";export ARCH_FLAGS;";; 75 | esac 76 | test -n "$CSP_KERNEL_FLAGS" && echo "CSP_KERNEL_FLAGS=\"$CSP_KERNEL_FLAGS\";export CSP_KERNEL_FLAGS;" 77 | ;; 78 | *) CCC="$CCC -m64";; 79 | esac 80 | echo "CCC=\"$CCC\";export CCC;" 81 | if test -f /opt/SUNWspro/bin/cc;then 82 | echo "Studio is found">&2 83 | CC=/opt/SUNWspro/bin/cc 84 | elif type cc >/dev/null;then 85 | echo "cc is in the path" >&2 86 | CC=cc 87 | elif type gcc >/dev/null;then 88 | echo "gcc is found">&2 89 | CC=gcc 90 | else 91 | echo "Nothing is found">&2 92 | exit 1 93 | fi 94 | echo "CC=\"$CC\";export CC;" ;; 95 | 96 | AIX) 97 | if test x$ARG = x32;then 98 | echo "CXX=/usr/vacpp/bin/xlC;export CXX;" 99 | echo "CC=\"/usr/vac/bin/cc -qlanglvl=extc89 -qlanglvl=extc99\";export CC;" 100 | else 101 | echo "CXX=\"/usr/vacpp/bin/xlC -q64\";export CXX;" 102 | echo "CC=\"/usr/vac/bin/cc -q64 -qlanglvl=extc89 -qlanglvl=extc99\";export CC;" 103 | echo "OBJECT_MODE=64;export OBJECT_MODE;" 104 | fi 105 | ;; 106 | *) 107 | if type g++ >/dev/null;then 108 | echo "g++ found in the path" >&2 109 | if ( test `uname -m` = x86_64 || test `uname -s` = Darwin ) && test x$ARG = x32 110 | then 111 | echo "CC=\"gcc -m32\";export CC;" 112 | echo "CXX=\"g++ -m32\";export CXX;" 113 | echo "ARCH_FLAGS=\"-m32\";export ARCH_FLAGS;" 114 | else 115 | echo "CC=\"gcc\";export CC;" 116 | echo "CXX=\"g++\";export CXX;" 117 | fi 118 | if test x$ARG = x32 119 | then 120 | echo "LSB_LD=/lib/ld-lsb.so.3;export LSB_LD;" 121 | else 122 | echo "LSB_LD=/lib64/ld-lsb-x86-64.so.3;export LSB_LD;" 123 | fi 124 | else 125 | if type clang++ >/dev/null; then 126 | echo "clang++ found in the path" >&2 127 | echo "CC=\"clang\";export CC;" 128 | echo "CXX=\"clang++\";export CXX;" 129 | else 130 | echo "Nothing is found">&2 131 | exit 1 132 | fi 133 | fi;; 134 | esac 135 | fi 136 | if test -z "$INSTALL" 137 | then 138 | if type install >/dev/null;then 139 | INSTALL=install 140 | else 141 | INSTALL=cp 142 | fi 143 | fi 144 | prefix=/opt/cprocsp 145 | includedir=${prefix}/include 146 | if test -z "$includedir" 147 | then 148 | includedir=/opt/cprocsp/include 149 | fi 150 | 151 | echo "CSP_DIR=/opt/cprocsp;" 152 | echo "CSP_INCLUDE=$includedir;" 153 | echo "add_CPPFLAGS=\"$add_CPPFLAGS\";" 154 | echo "SIZEOF_VOID_P=$SIZEOF_VOID_P;" 155 | echo "CSP_LIB=$libdir;" 156 | echo "INSTALL=$INSTALL;" 157 | echo "MODVERSFILE=$MODVERSFILE;" 158 | case `uname -s` in 159 | SunOS)echo "CSP_EXTRA_LIBS=\"-R$libdir -lsocket -lnsl\";";; 160 | FreeBSD)echo "CSP_EXTRA_LIBS=\"-R$libdir -lpthread\";";; 161 | AIX)echo "CXXFLAGS=\"$CXXFLAGS -qlang=stdc99\";export CXXFLAGS" 162 | echo "add_ldflags=\"-Wl,-brtl\";";; 163 | Darwin) echo "CSP_EXTRA_LIBS=\"-lpthread -framework CoreFoundation\";";; 164 | *) echo "CSP_EXTRA_LIBS=\"-lpthread\";";; 165 | esac 166 | echo "export CSP_DIR CSP_INCLUDE CSP_LIB CSP_EXTRA_LIBS SIZEOF_VOID_P INSTALL add_ldflags add_CPPFLAGS MODVERSFILE;" 167 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | 5 | const nodeCryptopro = require('../index')("CALG_PRO12_EXPORT", "CRYPT_MODE_CNT"); 6 | 7 | 8 | //Имя контейнера с ключами отправителя 9 | const senderContainerName = "55298654e-d073-f75e-9368-0847d712bb2";// "5973e5bc6-1e43-6206-c603-21fdd08867e"; 10 | 11 | //Путь к файлу с сертификатом открытого ключа отправителя 12 | const senderCertFilename = "./55298654e-d073-f75e-9368-0847d712bb2.cer";// "2012_Cert.cer"; 13 | 14 | //Имя контейнера с ключами получателя 15 | const responderContainerName = "5973e5bc6-1e43-6206-c603-21fdd08867e"; 16 | 17 | //Путь к файлу с сертификатом открытого ключа получателя 18 | const responderCertFilename = "./2012_Cert.cer"; 19 | 20 | 21 | describe('Тесты', function () { 22 | 23 | const sourceMessage = "text message for tests"; 24 | const buffer = Buffer.from(sourceMessage); 25 | const sourceMessageBytes = new Uint8Array(buffer); 26 | 27 | // console.log('sourceMessage: ' + buffer.toString('hex')); 28 | 29 | const hashForSourceMessage = new Uint8Array([82,181,47,23,1,228,41,72,41,214,88,194,195,191,190,222,223,73,66,111,196,65,133,235,206,122,89,171,160,130,48,90]); 30 | // console.log("hashForSourceMessage: " + Buffer.from(hashForSourceMessage).toString('hex')); 31 | 32 | const publicKeyBytes = [144,129,142,86,169,62,26,195,207,130,70,122,105,84,35,108,162,39,114,195,205,130,86,214,24,187,179,50,178,170,134,15,82,165,222,213,0,31,89,235,98,208,30,89,111,242,79,159,234,213,149,143,34,11,145,117,195,31,87,82,221,2,83,139]; 33 | // console.log("publicKeyBytesHex: " + Buffer.from(publicKeyBytes).toString('hex')); 34 | 35 | const certificateSubjectKey = "Tokarev2012_3";// 'NewCert2012'; 36 | 37 | let hashSignatureForSourceMessage = ""; 38 | 39 | let signatureForPreparedHash = ""; 40 | 41 | let publicKeyBlob = {}; 42 | 43 | let generatedSessionKey = {}; 44 | 45 | let encryptionResult = {}; 46 | let encryptionResult2 = {}; 47 | 48 | it('Вычисление хеша', async () => { 49 | const hash = nodeCryptopro.createHash(sourceMessageBytes); 50 | 51 | expect(hash).to.deep.equal(hashForSourceMessage); 52 | }); 53 | 54 | it('Загрузка публичного ключа из контейнера', async () => { 55 | publicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificate(certificateSubjectKey); 56 | 57 | expect(publicKeyBlob).to.have.lengthOf(101); 58 | }); 59 | 60 | it('Получение публичного ключа из текста сертификата', async () => { 61 | const certificateData = '30820347308202f6a003020102021312002903709eec80f0c6b0e654000000290370300806062a8503020203307f3123302106092a864886f70d0109011614737570706f72744063727970746f70726f2e7275310b3009060355040613025255310f300d060355040713064d6f73636f7731173015060355040a130e43525950544f2d50524f204c4c433121301f0603550403131843525950544f2d50524f20546573742043656e7465722032301e170d3138303532323135333431315a170d3138303832323135343431315a30563121301f06092a864886f70d0109011612767374726f67616e6f76406d61696c2e7275310b30090603550406130252553112301006035504030c096c6f63616c686f73743110300e06035504040c07636f6d70616e793066301f06082a85030701010101301306072a85030202240006082a850307010102020343000440e21e0ca695409ee93470eb4d3386815b1ac451e105cf778feadc53836ab2749650994b6715ebf381bd64a6763d9ccaac8821241f4cb8e17350d56d4eebd5504da382016d3082016930130603551d25040c300a06082b06010505070304300b0603551d0f0404030204f0301d0603551d0e04160414e22c34e1b508a1378b0d0b6da693a6ac2490da81301f0603551d2304183016801415317cb08d1ade66d7159c4952971724b9017a8330590603551d1f04523050304ea04ca04a8648687474703a2f2f7465737463612e63727970746f70726f2e72752f43657274456e726f6c6c2f43525950544f2d50524f2532305465737425323043656e746572253230322e63726c3081a906082b0601050507010104819c308199306106082b060105050730028655687474703a2f2f7465737463612e63727970746f70726f2e72752f43657274456e726f6c6c2f746573742d63612d323031345f43525950544f2d50524f2532305465737425323043656e746572253230322e637274303406082b060105050730018628687474703a2f2f7465737463612e63727970746f70726f2e72752f6f6373702f6f6373702e737266300806062a85030202030341007d420213f28207f2f05c433a1232dcece4b7c4db4ceb88b4b7a90f104d6fe7d59cbe262390573add35e2edc12c16a63d7d15dbd515019600e162a5cd2cbd7be1'; 62 | const certDataBytes = new Uint8Array( Buffer.from(certificateData, 'hex') ); 63 | 64 | publicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateData(certDataBytes); 65 | 66 | expect(publicKeyBlob).to.have.lengthOf(101); 67 | }); 68 | 69 | it('Загрузка публичного ключа из файла сертификата', async () => { 70 | const certificateFilePath = './55298654e-d073-f75e-9368-0847d712bb2.cer'; 71 | 72 | publicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(certificateFilePath); 73 | 74 | expect(publicKeyBlob).to.have.lengthOf(101); 75 | }); 76 | /* 77 | it('Получение дескриптора контейнера', async () => { 78 | const result = nodeCryptopro.acquireContextForContainer(senderContainerName); 79 | 80 | expect(result).to.equal(true); 81 | }); 82 | 83 | it('Вычисление цифровой подписи хеша', async () => { 84 | hashSignatureForSourceMessage = nodeCryptopro.signHash(senderContainerName, sourceMessageBytes); 85 | 86 | expect(hashSignatureForSourceMessage).to.have.lengthOf(64); 87 | }); 88 | 89 | it('Проверка цифровой подписи хеша', async () => { 90 | const isVerified = nodeCryptopro.verifySignature(sourceMessageBytes, hashSignatureForSourceMessage, publicKeyBlob); 91 | 92 | expect(isVerified).to.equal(true); 93 | }); 94 | 95 | it('Вычисление цифровой подписи предварительно подготовленного хеша', async () => { 96 | signatureForPreparedHash = nodeCryptopro.signPreparedHash(senderContainerName, hashForSourceMessage); 97 | 98 | // console.log("signatureForPreparedHash: " + Buffer.from(signatureForPreparedHash).toString('hex')); 99 | 100 | expect(signatureForPreparedHash).to.have.lengthOf(64); 101 | }); 102 | 103 | it('Проверка цифровой подписи предварительно подготовленного хеша', async () => { 104 | const responderPublicKeyBlob = publicKeyBlob; 105 | 106 | const isVerified = nodeCryptopro.verifyPreparedHashSignature(hashForSourceMessage, signatureForPreparedHash, responderPublicKeyBlob); 107 | 108 | // console.log("responderPublicKeyBlob: " + Buffer.from(responderPublicKeyBlob).toString('hex')); 109 | 110 | expect(isVerified).to.equal(true); 111 | }); 112 | 113 | it('Проверка цифровой подписи предварительно подготовленного хеша в GostCrypto', async () => { 114 | const publicKeyBlobHeader = [6,32,0,0,73,46,0,0,77,65,71,49,0,2,0,0,48,19,6,7,42,133,3,2,2,36,0,6,8,42,133,3,7,1,1,2,2]; 115 | 116 | const responderPublicKeyBlob = publicKeyBlob; 117 | 118 | const pkHex = "953a0a996e33601678ac36f052cd8e98963f5088c080bfaeeea49591ed0ab84012cf75816eb883735e1738678e73a6ad85757cc6b17e39381937b2128c473d21"; 119 | 120 | const pkHexN = "06200000492e00004d41473100020000301306072a85030202230106082a85030701010202abcda6f379d7b7aa2831c989ac49d33e4aa26d9aca4a78d1c06e925d88407d881e883cb14dac56442e1ba01d1267bc60fd140e6e65ea93b86a8e0c7960efb83f"; 121 | 122 | const pkBytes = new Uint8Array( Buffer.from(pkHex, 'hex') ); 123 | const pkBytesN = new Uint8Array( Buffer.from(pkHexN, 'hex') ); 124 | 125 | const hash = new Uint8Array( Buffer.from("52b52f1701e4294829d658c2c3bfbededf49426fc44185ebce7a59aba082305a", "hex") ); 126 | const sign = new Uint8Array( Buffer.from("a9c3dd99a1a32007509fd083d7020e7888eaedfd6a1159caf5d15fac5048e91d57beb27770d8a2891aa297f6d64bcccfa598194c0bd181360913e5de0c1fd784", "hex") ); 127 | 128 | 129 | const isVerified = nodeCryptopro.verifyPreparedHashSignature(hash, sign, pkBytesN); 130 | 131 | expect(isVerified).to.equal(true); 132 | }); 133 | 134 | it('Верификация подписи, созданной с помощью SignPreparedHash, функцией VerifySignature', async () => { 135 | const responderPublicKeyBlob = publicKeyBlob; 136 | 137 | const isVerified = nodeCryptopro.verifySignature(sourceMessageBytes, signatureForPreparedHash, responderPublicKeyBlob); 138 | 139 | expect(isVerified).to.equal(true); 140 | });*/ 141 | /* 142 | it('Шифрование сообщения по алгоритму ГОСТ 28147', async () => { 143 | const responderPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(responderCertFilename); 144 | const senderPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(senderCertFilename); 145 | 146 | encryptionResult = nodeCryptopro.encrypt(sourceMessageBytes, senderContainerName, responderPublicKeyBlob, "CALG_PRO_EXPORT"); 147 | 148 | expect(encryptionResult.encryptedBytesArray).to.have.lengthOf(sourceMessageBytes.length); 149 | expect(encryptionResult.sessionKeyBlob).to.have.lengthOf(73); 150 | expect(encryptionResult.IV).to.have.lengthOf(8); 151 | }); 152 | 153 | it('Дешифрование сообщения по алгоритму ГОСТ 28147', async () => { 154 | const senderPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(senderCertFilename); 155 | 156 | let decryptedBytes = nodeCryptopro.decrypt( 157 | encryptionResult.encryptedBytesArray, 158 | responderContainerName, 159 | senderPublicKeyBlob, 160 | encryptionResult.IV, 161 | encryptionResult.sessionKeyBlob, 162 | "CALG_PRO12_EXPORT"); 163 | 164 | const decryptedMessage = (new Buffer(decryptedBytes)).toString(); 165 | 166 | expect(decryptedMessage).to.equal(sourceMessage); 167 | }); 168 | 169 | it('Шифрование сообщения по алгоритму ГОСТ 28147 на готовом сессионном ключе', async () => { 170 | const responderPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(responderCertFilename); 171 | 172 | generatedSessionKey = nodeCryptopro.generateSessionKey(senderContainerName, responderPublicKeyBlob); 173 | 174 | encryptionResult2 = await nodeCryptopro.encryptWithSessionKey( 175 | sourceMessageBytes, 176 | senderContainerName, 177 | responderPublicKeyBlob, 178 | generatedSessionKey.sessionKeyBlob, 179 | generatedSessionKey.IV 180 | ); 181 | 182 | expect(encryptionResult2.encryptedBytesArray).to.have.lengthOf(sourceMessageBytes.length); 183 | }); 184 | 185 | it('Дешифрование сообщения по алгоритму ГОСТ 28147 на готовом сессионном ключе', async () => { 186 | const senderPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(senderCertFilename); 187 | 188 | let decryptedBytes = nodeCryptopro.decrypt( 189 | encryptionResult2.encryptedBytesArray, 190 | responderContainerName, 191 | senderPublicKeyBlob, 192 | generatedSessionKey.IV, 193 | generatedSessionKey.sessionKeyBlob); 194 | 195 | 196 | const decryptedMessage = (new Buffer(decryptedBytes)).toString(); 197 | 198 | expect(decryptedMessage).to.equal(sourceMessage); 199 | }); 200 | 201 | it('Перекодирование сессионного ключа', async () => { 202 | const oldResponderPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(responderCertFilename); 203 | const newResponderPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(senderCertFilename); 204 | 205 | let generatedSessionKey2 = nodeCryptopro.generateSessionKey(senderContainerName, oldResponderPublicKeyBlob); 206 | 207 | let result = nodeCryptopro.recodeSessionKey( 208 | generatedSessionKey2.sessionKeyBlob, 209 | generatedSessionKey2.IV, 210 | senderContainerName, 211 | oldResponderPublicKeyBlob, 212 | newResponderPublicKeyBlob); 213 | 214 | let result2 = nodeCryptopro.recodeSessionKey( 215 | result.sessionKeyBlob, 216 | result.IV, 217 | senderContainerName, 218 | newResponderPublicKeyBlob, 219 | oldResponderPublicKeyBlob); 220 | 221 | expect(1).to.equal(1); 222 | }); 223 | 224 | it('Перекодирование сессионного ключа со сменой ключевого контейнера', async () => { 225 | const oldResponderPublicKeyBlob = publicKeyBlob; 226 | const newResponderPublicKeyBlob = publicKeyBlob; 227 | const oldSenderContainerName = senderContainerName; 228 | const newSenderContainerName = senderContainerName; 229 | 230 | let generatedSessionKey2 = nodeCryptopro.generateSessionKey(oldSenderContainerName, oldResponderPublicKeyBlob); 231 | 232 | let result = nodeCryptopro.recodeSessionKeyForNewContainer( 233 | generatedSessionKey2.sessionKeyBlob, 234 | generatedSessionKey2.IV, 235 | oldSenderContainerName, 236 | newSenderContainerName, 237 | oldResponderPublicKeyBlob, 238 | newResponderPublicKeyBlob); 239 | 240 | expect(1).to.equal(1); 241 | }); 242 | 243 | 244 | it('Генерация сессионного ключа', async () => { 245 | const sessionKey = nodeCryptopro.generateSessionKey(senderContainerName, publicKeyBlob); 246 | 247 | expect(sessionKey.sessionKeyBlob).to.have.lengthOf(73); 248 | expect(sessionKey.IV).to.have.lengthOf(8); 249 | }); 250 | 251 | 252 | it('Шифрование и дешифрование сообщения с перекодированием сессионного ключа', async () => { 253 | const nodePublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(senderCertFilename); 254 | const nodeContainerName = senderContainerName; 255 | 256 | const clientPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile(responderCertFilename); 257 | const clientContainerName = responderContainerName; 258 | 259 | const generatedSessionKey = nodeCryptopro.generateSessionKey(nodeContainerName, nodePublicKeyBlob); 260 | 261 | const encryptionResult = await nodeCryptopro.encryptWithSessionKey( 262 | sourceMessageBytes, 263 | nodeContainerName, 264 | nodePublicKeyBlob, 265 | generatedSessionKey.sessionKeyBlob, 266 | generatedSessionKey.IV 267 | ); 268 | 269 | let recodedSessionKey = nodeCryptopro.recodeSessionKey( 270 | generatedSessionKey.sessionKeyBlob, 271 | generatedSessionKey.IV, 272 | nodeContainerName, 273 | nodePublicKeyBlob, 274 | clientPublicKeyBlob); 275 | 276 | let decryptedBytes = nodeCryptopro.decrypt( 277 | encryptionResult.encryptedBytesArray, 278 | clientContainerName, 279 | nodePublicKeyBlob, 280 | recodedSessionKey.IV, 281 | recodedSessionKey.sessionKeyBlob); 282 | 283 | 284 | const decryptedMessage = (new Buffer(decryptedBytes)).toString(); 285 | 286 | 287 | expect(decryptedMessage).to.equal(sourceMessage); 288 | }); 289 | 290 | it('Дешифрование сообщения от КриптоПро Browser Plugin', async () => { 291 | const containerName = "5973e5bc6-1e43-6206-c603-21fdd08867e"; 292 | 293 | const KP_CIPHEROID = "312E322E3634332E372E312E322E352E312E3100"; 294 | const pkBlob = new Uint8Array( Buffer.from("0A200000492E00004D41473100020000301306072A85030202240006082A850307010102022E072B2C9D9F94A907BC7FCAFC50341AFFD294313E26388963F11467D50DE165C042F622A6BB39D766C619F8F366857CB9E3F5C429501BD6AEBE547511332612", 'hex') ); 295 | const sessionKey = new Uint8Array( Buffer.from("012000001E660000FD514A371E6600006F82A6A5D9A3CEC7EEA4EE0F196CB50EDDF61D5062A6A61C10F8025D10530C139F473A08F0EF8CB0157EA30C300906072A850302021F01", 'hex') ); 296 | 297 | const iv = new Uint8Array( Buffer.from("A397A8F0473F0ABC", 'hex') ); 298 | const encryptedBytesArray = new Uint8Array( Buffer.from("0231E3C1", 'hex') ); 299 | 300 | let decryptedBytes = nodeCryptopro.decrypt( 301 | encryptedBytesArray, 302 | containerName, 303 | pkBlob, 304 | iv, 305 | sessionKey, 306 | "CALG_PRO12_EXPORT"); 307 | 308 | const decryptedMessage = Buffer.from(decryptedBytes).toString(); 309 | console.log("decryptedMessage: " + decryptedMessage); 310 | 311 | expect(decryptedMessage).to.equal(sourceMessage); 312 | });*/ 313 | /* 314 | it('Дешифрование сообщения от микросервиса dbg.crypto', async () => { 315 | const containerName = "5973e5bc6-1e43-6206-c603-21fdd08867e"; 316 | 317 | const senderPublicKeyBlob = new Uint8Array( Buffer.from("06200000492e00004d41473100020000301306072a85030202240006082a85030701010202e21e0ca695409ee93470eb4d3386815b1ac451e105cf778feadc53836ab2749650994b6715ebf381bd64a6763d9ccaac8821241f4cb8e17350d56d4eebd5504d", 'hex') ); 318 | //nodeCryptopro.GetPublicKeyFromCertificateFile("./55298654e-d073-f75e-9368-0847d712bb2.cer"); 319 | // 320 | const pkBlob = new Uint8Array( Buffer.from("", 'hex') ); 321 | const sessionKey = new Uint8Array( Buffer.from("012000001e660000fd514a371e6600004d9b1b1b6b81b9a7705a97293a4c9c99cb3ace94969fce72760668595674e7429f37f9812a860230b8db2981300b06092a8503070102050101", 'hex') ); 322 | 323 | const iv = new Uint8Array( Buffer.from("b7b37aa3b9606075", 'hex') ); 324 | const encryptedBytesArray = new Uint8Array( Buffer.from("2583ab35", 'hex') ); 325 | 326 | let decryptedBytes = nodeCryptopro.decrypt( 327 | encryptedBytesArray, 328 | containerName, 329 | senderPublicKeyBlob, 330 | iv, 331 | sessionKey); 332 | 333 | const decryptedMessage = (new Buffer(decryptedBytes)).toString(); 334 | console.log("decryptedMessage: " + decryptedMessage); 335 | 336 | expect(decryptedMessage).to.equal("test"); 337 | }); 338 | */ 339 | 340 | /* it('Дешифрование сообщения с Платформы', async () => { 341 | const containerName = "5973e5bc6-1e43-6206-c603-21fdd08867e"; 342 | const pkBlob = new Uint8Array([6,32,0,0,73,46,0,0,77,65,71,49,0,2,0,0,48,19,6,7,42,133,3,2,2,36,0,6,8,42,133,3,7,1,1,2,2,144,129,142,86,169,62,26,195,207,130,70,122,105,84,35,108,162,39,114,195,205,130,86,214,24,187,179,50,178,170,134,15,82,165,222,213,0,31,89,235,98,208,30,89,111,242,79,159,234,213,149,143,34,11,145,117,195,31,87,82,221,2,83,139]); 343 | const sessionKeyBytes = new Uint8Array([1,32,0,0,30,102,0,0,253,81,74,55,30,102,0,0,108,55,242,240,222,51,1,65,194,217,203,30,166,86,252,69,35,116,246,28,78,216,183,39,55,162,77,155,63,221,97,46,163,170,229,17,18,46,247,221,253,137,227,118,48,11,6,9,42,133,3,7,1,2,5,1,1]); 344 | const iv = new Uint8Array([49,188,197,134,61,238,168,89]); 345 | const encryptedBytesArray = new Uint8Array([108,131,157,179,80,86,80,196,96,225,216]); 346 | 347 | const senderPublicKeyBlob = nodeCryptopro.GetPublicKeyFromCertificateFile("./2012_Cert.cer");//("./55298654e-d073-f75e-9368-0847d712bb2.cer"); 348 | console.log("senderPublicKeyBlob (2012_Cert.cer): " + senderPublicKeyBlob); 349 | 350 | const senderPublicKeyBlob2 = nodeCryptopro.GetPublicKeyFromCertificateFile("./55298654e-d073-f75e-9368-0847d712bb2.cer"); 351 | console.log("senderPublicKeyBlob (55298654e-d073-f75e-9368-0847d712bb2): " + senderPublicKeyBlob2); 352 | 353 | const publicKeyBlobFromContainer = nodeCryptopro.GetPublicKeyFromCertificate("vstroganov@mail.ru"); 354 | console.log("publicKeyBlobFromContainer ('Tokarev2012_3'): " + publicKeyBlobFromContainer); 355 | 356 | let decryptedBytes = nodeCryptopro.decrypt( 357 | encryptedBytesArray, 358 | containerName, 359 | senderPublicKeyBlob2, 360 | iv, 361 | sessionKeyBytes); 362 | 363 | const decryptedMessage = (new Buffer(decryptedBytes)).toString(); 364 | console.log("decryptedMessage: " + decryptedMessage); 365 | 366 | expect(decryptedMessage).to.equal(sourceMessage); 367 | });*/ 368 | 369 | }); 370 | 371 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Path = require('path'); 3 | 4 | const pathToNodeCryptopro = Path.resolve(__dirname, '.'); 5 | 6 | let pathToNodeCryptoproLib = ''; 7 | 8 | if(process.platform == 'win32') { 9 | pathToNodeCryptoproLib = pathToNodeCryptopro + '/lib/nodeCryptopro.dll'; 10 | } else { 11 | pathToNodeCryptoproLib = pathToNodeCryptopro + '/lib/nodeCryptopro.so'; 12 | } 13 | 14 | const ffi = require('ffi'); 15 | const ref = require('ref'); 16 | 17 | const G28147_KEYLEN = 32; 18 | const SEANCE_VECTOR_LEN = 8; 19 | const EXPORT_IMIT_SIZE = 4; 20 | 21 | const GOST3411_HASH_LENGTH = 32; 22 | const MAX_PUBLICKEYBLOB_SIZE = 200; 23 | 24 | const MAX_SIGNATURE_LENGTH = 200; 25 | 26 | var ArrayType = require('ref-array'); 27 | var byte = ref.types.byte; 28 | var ByteArray = ArrayType(byte); 29 | 30 | var Struct = require('ref-struct'); 31 | 32 | var CallResult = Struct({ 33 | 'status': 'int', 34 | 'errorCode': 'int', 35 | 'errorMessage': 'string' 36 | }); 37 | 38 | const cryptoLib = ffi.Library(pathToNodeCryptoproLib, { 39 | 'InitParams': [CallResult, ['string', 'string']], 40 | 'AcquireContextForContainer': [CallResult, ['string']], 41 | 'CreateHash': [CallResult, [ref.refType('byte'), 'int', ref.refType('byte'), ref.refType('int')]], 42 | 'Encrypt': [CallResult, [ref.refType('int'), ref.refType('byte'), 'string', ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('byte'), ref.refType('int')]], 43 | 'EncryptWithSessionKey': [CallResult, [ref.refType('byte'), 'int', 'string', ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('byte'), 'int']], 44 | 'Decrypt': [CallResult, ['string', ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('byte'), 'int']], 45 | 'GenerateSessionKey': [CallResult, [ref.refType('int'), ref.refType('byte'), 'string', ref.refType('byte'), 'int', ref.refType('byte'), ref.refType('int')]], 46 | 'SignHash': [CallResult, ['string', ref.refType('byte'), 'int', ref.refType('byte'), ref.refType('int')]], 47 | 'VerifySignature': [CallResult, [ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('bool')]], 48 | 'SignPreparedHash': [CallResult, ['string', ref.refType('byte'), 'int', ref.refType('byte'), ref.refType('int')]], 49 | 'VerifyPreparedHashSignature': [CallResult, [ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('bool')]], 50 | 'GetPublicKeyFromCertificateData': [CallResult, [ref.refType('byte'), ref.refType('int'), ref.refType('byte'), 'int']], 51 | 'GetPublicKeyFromCertificateFile': [CallResult, [ref.refType('byte'), ref.refType('int'), 'string']], 52 | 'GetPublicKeyFromCertificate': [CallResult, [ref.refType('byte'), ref.refType('int'), 'string']], 53 | 'RecodeSessionKey': [CallResult, [ref.refType('byte'), 'int', 'string', ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('byte'), 'int']], 54 | 'RecodeSessionKeyForNewContainer': [CallResult, [ref.refType('byte'), 'int', 'string', 'string', ref.refType('byte'), 'int', ref.refType('byte'), 'int', ref.refType('byte'), 'int']] 55 | }); 56 | 57 | module.exports = (keyExportAlgo = "CALG_PRO_EXPORT", kpMode = "CRYPT_MODE_CFB") => { 58 | let result = cryptoLib.InitParams(keyExportAlgo, kpMode); 59 | 60 | if(result.status) { 61 | throw new Error(result.errorMessage); 62 | } 63 | 64 | return { 65 | 66 | /** 67 | * Получение дескриптора контекста криптографического провайдера для заданного ключевого контейнера. 68 | * 69 | * Получается и сохраняется дескриптор, который затем используется в функции encryptWithSessionKey() 70 | * 71 | * @param {String} containerName Имя ключевого контейнера 72 | * 73 | * @return {Boolean} Результат выполнения операции 74 | */ 75 | acquireContextForContainer: (containerName) => { 76 | let result = cryptoLib.AcquireContextForContainer(containerName); 77 | 78 | if(result.status) { 79 | throw new Error(result.errorMessage); 80 | } else { 81 | return true; 82 | } 83 | }, 84 | /** 85 | * Вычисление хеша по алгоритму ГОСТ Р 34.11-2012 длинной 256 бит 86 | * 87 | * @param {Uint8Array} bytesArrayToHash Исходные данные для хеширования 88 | * 89 | * @return {Uint8Array} Хеш 90 | */ 91 | createHash: (bytesArrayToHash) => { 92 | let hashLength = ref.alloc('int'); 93 | let hash = new Uint8Array(GOST3411_HASH_LENGTH); 94 | 95 | let result = cryptoLib.CreateHash(bytesArrayToHash, bytesArrayToHash.length, hash, hashLength); 96 | 97 | if(result.status) { 98 | throw new Error(result.errorMessage); 99 | } else { 100 | return hash.subarray(0, hashLength.deref()); 101 | } 102 | }, 103 | 104 | /** 105 | * Объект, содержащий результаты шифрования 106 | * 107 | * @typedef {Object} EncryptionResult 108 | * @property {Uint8Array} encryptedBytesArray Зашифрованное сообщение 109 | * @property {Uint8Array} sessionKeyBlob Зашифрованный сессионный ключ в формате SIMPLEBLOB 110 | * @property {Uint8Array} IV Вектор инициализации сессионного ключа 111 | */ 112 | 113 | /** 114 | * Объект, содержащий зашифрованный сессионный ключ 115 | * 116 | * @typedef {Object} EncryptedSessionKey 117 | * @property {Uint8Array} sessionKeyBlob Зашифрованный сессионный ключ в формате SIMPLEBLOB 118 | * @property {Uint8Array} IV Вектор инициализации сессионного ключа 119 | */ 120 | 121 | /** 122 | * Шифрование по алгоритму ГОСТ 28147 123 | * 124 | * Шифрование производится на сессионном ключе. 125 | * Для передачи на сторону получателя сессионный ключ шифруется на ключе согласования по алгоритму CALG_PRO_EXPORT и экспортируется в формате SIMPLEBLOB. 126 | * Ключ согласования получается импортом открытого ключа получателя на закрытом ключе отправителя. 127 | * 128 | * Используется провайдер типа PROV_GOST_2012_256 и ключи алгоритма ГОСТ Р 34.10-2012 длины 256 бит (длина открытого ключа 512 бит). 129 | * 130 | * https://cpdn.cryptopro.ru/content/csp40/html/group___c_s_p_examples_4_0vs3_6.html 131 | * https://cpdn.cryptopro.ru/content/csp40/html/group___pro_c_s_p_key_1gd56b0fb8e9d9c0278e45eb1994c38161.html 132 | * 133 | * @param {Uint8Array} bytesArrayToEncrypt Исходные данные для шифрования 134 | * @param {String} senderContainerName Имя контейнера, содержащего закрытый ключ отправителя 135 | * @param {Uint8Array} responderPublicKey Публичный ключ получателя (PUBLICKEYBLOB) 136 | * 137 | * @return {EncryptionResult} 138 | */ 139 | encrypt: (bytesArrayToEncrypt, senderContainerName, responderPublicKey) => { 140 | let IV = new Uint8Array(SEANCE_VECTOR_LEN); 141 | let IVLength = ref.alloc('int'); 142 | 143 | let sessionKeyBlobLength = ref.alloc('int'); 144 | let sessionKeyBlob = new Uint8Array( MAX_PUBLICKEYBLOB_SIZE ); 145 | 146 | let result = cryptoLib.Encrypt( 147 | sessionKeyBlobLength, 148 | sessionKeyBlob, 149 | senderContainerName, 150 | responderPublicKey, responderPublicKey.length, 151 | bytesArrayToEncrypt, bytesArrayToEncrypt.length, 152 | IV, IVLength 153 | ); 154 | if(result.status) { 155 | throw new Error(result.errorMessage); 156 | } else { 157 | return { 158 | encryptedBytesArray: bytesArrayToEncrypt, 159 | sessionKeyBlob: sessionKeyBlob.subarray(0, sessionKeyBlobLength.deref()), 160 | IV: IV.subarray(0, IVLength.deref()) 161 | }; 162 | } 163 | }, 164 | 165 | /** 166 | * Шифрование по алгоритму ГОСТ 28147 на предвапрительно подготовленном на сессионном ключе. 167 | * 168 | * Сессионный ключ предварительно получен с помощью функции generateSessionKey(). 169 | * 170 | * Используется провайдер типа PROV_GOST_2012_256 и ключи алгоритма ГОСТ Р 34.10-2012 длины 256 бит (длина открытого ключа 512 бит). 171 | * 172 | * https://cpdn.cryptopro.ru/content/csp40/html/group___c_s_p_examples_4_0vs3_6.html 173 | * https://cpdn.cryptopro.ru/content/csp40/html/group___pro_c_s_p_key_1gd56b0fb8e9d9c0278e45eb1994c38161.html 174 | * 175 | * @param {Uint8Array} bytesArrayToEncrypt Исходные данные для шифрования 176 | * @param {String} senderContainerName Имя контейнера, содержащего закрытый ключ отправителя 177 | * @param {Uint8Array} responderPublicKey Публичный ключ получателя (PUBLICKEYBLOB) 178 | * @param {Uint8Array} sessionKeySimpleBlob Зашифрованный сессионный ключ в формате SIMPLEBLOB 179 | * @param {Uint8Array} IV Вектор инициализации сессионного ключа 180 | * 181 | * @return {EncryptionResult} 182 | */ 183 | encryptWithSessionKey: async (bytesArrayToEncrypt, senderContainerName, responderPublicKey, sessionKeySimpleBlob, IV) => { 184 | 185 | let encryptWithSessionKeyAsync = () => { 186 | return new Promise( (resolve, reject) => { 187 | cryptoLib.EncryptWithSessionKey.async( 188 | sessionKeySimpleBlob, 189 | sessionKeySimpleBlob.length, 190 | senderContainerName, 191 | responderPublicKey, responderPublicKey.length, 192 | bytesArrayToEncrypt, bytesArrayToEncrypt.length, 193 | IV, IV.length, 194 | (err, res) => { 195 | if(err) 196 | reject(err); 197 | if(res.status) 198 | throw new Error(res.errorMessage); 199 | resolve({ 200 | encryptedBytesArray: bytesArrayToEncrypt 201 | }); 202 | } 203 | ); 204 | }) 205 | } 206 | 207 | return await encryptWithSessionKeyAsync(); 208 | 209 | /* 210 | //Асинхронный вариант 211 | let result = cryptoLib.EncryptWithSessionKey.async( 212 | sessionKeySimpleBlob, 213 | sessionKeySimpleBlob.length, 214 | senderContainerName, 215 | responderPublicKey, responderPublicKey.length, 216 | bytesArrayToEncrypt, bytesArrayToEncrypt.length, 217 | IV, IV.length 218 | ); 219 | if(result.status) { 220 | throw new Error(result.errorMessage); 221 | } else { 222 | return { 223 | encryptedBytesArray: bytesArrayToEncrypt 224 | }; 225 | }*/ 226 | }, 227 | 228 | /** 229 | * Дешифрование по алгоритму ГОСТ 28147 230 | * 231 | * Сессионный ключ в формате SIMPLEBLOB расшифровывается на ключе согласования по алгоритму CALG_PRO_EXPORT. 232 | * Ключ согласования получается импортом открытого ключа отправителя на закрытом ключе получателя. 233 | * 234 | * @param {Uint8Array} encryptedBytes Массив байтов зашифрованных данных 235 | * @param {String} responderContainerName Имя контейнера, содержащего закрытый ключ получателя 236 | * @param {Uint8Array} senderPublicKey Публичный ключ отправителя (PUBLICKEYBLOB) 237 | * @param {Uint8Array} IV Вектор инициализации сессионного ключа 238 | * @param {Uint8Array} keyBlob Зашифрованный сессионный ключ в формате SIMPLEBLOB 239 | * 240 | * @return {Uint8Array} Массив байтов расшифрованного сообщения 241 | */ 242 | decrypt: (encryptedBytes, responderContainerName, senderPublicKey, IV, keyBlob) => { 243 | let result = cryptoLib.Decrypt( 244 | responderContainerName, 245 | senderPublicKey, senderPublicKey.length, 246 | encryptedBytes, 247 | encryptedBytes.length, 248 | IV, 249 | IV.length, 250 | keyBlob, 251 | keyBlob.length 252 | ); 253 | 254 | if(result.status) { 255 | throw new Error(result.errorMessage); 256 | } else { 257 | return encryptedBytes; 258 | } 259 | }, 260 | /** 261 | * Вычисление хеша сообщения по ГОСТ 34.11-2012 и генерация цифровой подписи 262 | * 263 | * @param {String} keyContainerName Имя контейнера, содержащего закрытый ключ 264 | * @param {Uint8Array} messageBytesArray Массив байтов исходного сообщения 265 | * @return {Uint8Array} Массив байтов цифровой подписи 266 | */ 267 | signHash: (keyContainerName, messageBytesArray) => { 268 | let signatureBytesArrayLength = ref.alloc('int'); 269 | let signatureBytesArray = new Uint8Array( MAX_SIGNATURE_LENGTH ); 270 | 271 | let result = cryptoLib.SignHash( 272 | keyContainerName, 273 | messageBytesArray, 274 | messageBytesArray.length, 275 | signatureBytesArray, 276 | signatureBytesArrayLength 277 | ); 278 | if(result.status) { 279 | throw new Error(result.errorMessage); 280 | } else { 281 | return signatureBytesArray.subarray(0, signatureBytesArrayLength.deref()); 282 | } 283 | }, 284 | /** 285 | * Верификация цифровой подписи хеша сообщения 286 | * 287 | * @param {Uint8Array} messageBytesArray Массив байтов исходного сообщения 288 | * @param {Uint8Array} signatureBytesArray Массив байтов подписи хеша исходного сообщения 289 | * @param {Uint8Array} publicKey Открытый ключ 290 | * @return {Boolean} Результат верификации 291 | */ 292 | verifySignature: (messageBytesArray, signatureBytesArray, publicKey) => { 293 | let verificationResult = ref.alloc('bool'); 294 | let result = cryptoLib.VerifySignature( 295 | messageBytesArray, messageBytesArray.length, 296 | signatureBytesArray, signatureBytesArray.length, 297 | publicKey, publicKey.length, 298 | verificationResult 299 | ); 300 | 301 | if(result.status) { 302 | throw new Error(result.errorMessage); 303 | } else { 304 | return verificationResult.deref(); 305 | } 306 | }, 307 | /** 308 | * Вычисление генерация цифровой подписи для готового хеша сообщения 309 | * 310 | * @param {String} keyContainerName Имя контейнера, содержащего закрытый ключ 311 | * @param {Uint8Array} hashBytesArray Массив байтов предварительно полученного хеша 312 | * @return {Uint8Array} Массив байтов цифровой подписи 313 | */ 314 | signPreparedHash: (keyContainerName, hashBytesArray) => { 315 | let signatureBytesArrayLength = ref.alloc('int'); 316 | let signatureBytesArray = new Uint8Array( MAX_SIGNATURE_LENGTH ); 317 | 318 | let result = cryptoLib.SignPreparedHash( 319 | keyContainerName, 320 | hashBytesArray, 321 | hashBytesArray.length, 322 | signatureBytesArray, 323 | signatureBytesArrayLength 324 | ); 325 | if(result.status) { 326 | throw new Error(result.errorMessage); 327 | } else { 328 | return signatureBytesArray.subarray(0, signatureBytesArrayLength.deref()); 329 | } 330 | }, 331 | /** 332 | * Верификация цифровой подписи хеша сообщения 333 | * 334 | * @param {Uint8Array} hashBytesArray Массив байтов хеша исходного сообщения 335 | * @param {Uint8Array} signatureBytesArray Массив байтов подписи хеша 336 | * @param {Uint8Array} publicKey Открытый ключ 337 | * @return {Boolean} Результат верификации 338 | */ 339 | verifyPreparedHashSignature: (hashBytesArray, signatureBytesArray, publicKey) => { 340 | let verificationResult = ref.alloc('bool'); 341 | 342 | let result = cryptoLib.VerifyPreparedHashSignature( 343 | hashBytesArray, hashBytesArray.length, 344 | signatureBytesArray, signatureBytesArray.length, 345 | publicKey, publicKey.length, 346 | verificationResult 347 | ); 348 | 349 | if(result.status) { 350 | throw new Error(result.errorMessage); 351 | } else { 352 | return verificationResult.deref(); 353 | } 354 | }, 355 | 356 | /** 357 | * Получение публичного ключа из байтов сертификата 358 | * 359 | * @param {String} certificateFilePath Путь к файлу сертификата в формате .cer 360 | * @return {Uint8Array} Массив байтов публичного ключа 361 | */ 362 | GetPublicKeyFromCertificateData: (certificateBytes) => { 363 | let publicKeyBlobLength = ref.alloc('int'); 364 | let publicKeyBlob = new Uint8Array( MAX_PUBLICKEYBLOB_SIZE ); 365 | 366 | let result = cryptoLib.GetPublicKeyFromCertificateData(publicKeyBlob, publicKeyBlobLength, certificateBytes, certificateBytes.length); 367 | 368 | if(result.status) { 369 | throw new Error(result.errorMessage); 370 | } else { 371 | return publicKeyBlob.subarray(0, publicKeyBlobLength.deref()); 372 | } 373 | }, 374 | 375 | /** 376 | * Получение публичного ключа из файла сертификата 377 | * 378 | * @param {String} certificateFilePath Путь к файлу сертификата в формате .cer 379 | * @return {Uint8Array} Массив байтов публичного ключа 380 | */ 381 | GetPublicKeyFromCertificateFile: (certificateFilePath) => { 382 | let publicKeyBlobLength = ref.alloc('int'); 383 | let publicKeyBlob = new Uint8Array( MAX_PUBLICKEYBLOB_SIZE ); 384 | 385 | let result = cryptoLib.GetPublicKeyFromCertificateFile(publicKeyBlob, publicKeyBlobLength, certificateFilePath); 386 | 387 | if(result.status) { 388 | throw new Error(result.errorMessage); 389 | } else { 390 | return publicKeyBlob.subarray(0, publicKeyBlobLength.deref()); 391 | } 392 | }, 393 | 394 | /** 395 | * Получение публичного ключа из ключевого контейнера 396 | * 397 | * @param {String} certificateSubjectKey Ключевое слово для поиска по полю subject ключевого контейнера или PIN контейнера 398 | * @return {Uint8Array} Массив байтов публичного ключа 399 | */ 400 | GetPublicKeyFromCertificate: (certificateSubjectKey) => { 401 | let publicKeyBlobLength = ref.alloc('int'); 402 | let publicKeyBlob = new Uint8Array( MAX_PUBLICKEYBLOB_SIZE ); 403 | 404 | let result = cryptoLib.GetPublicKeyFromCertificate(publicKeyBlob, publicKeyBlobLength, certificateSubjectKey); 405 | 406 | if(result.status) { 407 | throw new Error(result.errorMessage); 408 | } else { 409 | return publicKeyBlob.subarray(0, publicKeyBlobLength.deref()); 410 | } 411 | }, 412 | /** 413 | * Генерация сессионного ключа для шифрования по алгоритму ГОСТ 28147 414 | * 415 | * Сессионный ключ шифруется на ключе согласования по алгоритму CALG_PRO_EXPORT и экспортируется в формате SIMPLEBLOB. 416 | * Ключ согласования получается импортом открытого ключа получателя на закрытом ключе отправителя. 417 | * 418 | * Используется провайдер типа PROV_GOST_2012_256 и ключи алгоритма ГОСТ Р 34.10-2012 длины 256 бит (длина открытого ключа 512 бит). 419 | * 420 | * https://cpdn.cryptopro.ru/content/csp40/html/group___c_s_p_examples_4_0vs3_6.html 421 | * https://cpdn.cryptopro.ru/content/csp40/html/group___pro_c_s_p_key_1gd56b0fb8e9d9c0278e45eb1994c38161.html 422 | * 423 | * @param {String} senderContainerName Имя контейнера, содержащего закрытый ключ отправителя 424 | * @param {Uint8Array} responderPublicKey Публичный ключ получателя (PUBLICKEYBLOB) 425 | * 426 | * @return {EncryptedSessionKey} 427 | */ 428 | generateSessionKey: (senderContainerName, responderPublicKey) => { 429 | let IV = new Uint8Array(SEANCE_VECTOR_LEN); 430 | let IVLength = ref.alloc('int'); 431 | 432 | let sessionKeyBlobLength = ref.alloc('int'); 433 | let sessionKeyBlob = new Uint8Array( MAX_PUBLICKEYBLOB_SIZE ); 434 | 435 | let result = cryptoLib.GenerateSessionKey( 436 | sessionKeyBlobLength, 437 | sessionKeyBlob, 438 | senderContainerName, 439 | responderPublicKey, responderPublicKey.length, 440 | IV, IVLength 441 | ); 442 | if(result.status) { 443 | throw new Error(result.errorMessage); 444 | } else { 445 | return { 446 | sessionKeyBlob: sessionKeyBlob.subarray(0, sessionKeyBlobLength.deref()), 447 | IV: IV.subarray(0, IVLength.deref()) 448 | }; 449 | } 450 | }, 451 | /** 452 | * Перешифрование сессионного ключа 453 | * 454 | * Сессионный ключ шифруется на ключе согласования по алгоритму CALG_PRO_EXPORT и экспортируется в формате SIMPLEBLOB. 455 | * Ключ согласования получается импортом открытого ключа получателя на закрытом ключе отправителя. 456 | * 457 | * Используется провайдер типа PROV_GOST_2012_256 и ключи алгоритма ГОСТ Р 34.10-2012 длины 256 бит (длина открытого ключа 512 бит). 458 | * 459 | * https://cpdn.cryptopro.ru/content/csp40/html/group___c_s_p_examples_4_0vs3_6.html 460 | * https://cpdn.cryptopro.ru/content/csp40/html/group___pro_c_s_p_key_1gd56b0fb8e9d9c0278e45eb1994c38161.html 461 | * 462 | * @param {Uint8Array} sessionKeySimpleBlob Зашифрованный сессионный ключ в формате SIMPLEBLOB 463 | * @param {String} senderContainerName Имя контейнера, содержащего закрытый ключ отправителя 464 | * @param {Uint8Array} oldResponderPublicKeyBlob Публичный ключ получателя (PUBLICKEYBLOB), для которого зашифрован сессионный ключ 465 | * @param {Uint8Array} newResponderPublicKeyBlob Публичный ключ получателя (PUBLICKEYBLOB), для которого будет перешифрован сессионный ключ 466 | * 467 | * @return {EncryptedSessionKey} 468 | */ 469 | recodeSessionKey: (sessionKeySimpleBlob, IV, senderContainerName, oldResponderPublicKeyBlob, newResponderPublicKeyBlob) => { 470 | let result = cryptoLib.RecodeSessionKey( 471 | sessionKeySimpleBlob, sessionKeySimpleBlob.length, 472 | senderContainerName, 473 | oldResponderPublicKeyBlob, oldResponderPublicKeyBlob.length, 474 | newResponderPublicKeyBlob, newResponderPublicKeyBlob.length, 475 | IV, IV.length 476 | ); 477 | 478 | if(result.status) { 479 | throw new Error(result.errorMessage); 480 | } else { 481 | return { 482 | sessionKeyBlob: sessionKeySimpleBlob.subarray(0, sessionKeySimpleBlob.length), 483 | IV: IV.subarray(0, IV.length) 484 | }; 485 | } 486 | }, 487 | /** 488 | * Перешифрование сессионного ключа со сменой ключевого контейнера 489 | * 490 | * Сессионный ключ шифруется на ключе согласования по алгоритму CALG_PRO_EXPORT и экспортируется в формате SIMPLEBLOB. 491 | * Ключ согласования получается импортом открытого ключа получателя на закрытом ключе отправителя. 492 | * 493 | * Используется провайдер типа PROV_GOST_2012_256 и ключи алгоритма ГОСТ Р 34.10-2012 длины 256 бит (длина открытого ключа 512 бит). 494 | * 495 | * https://cpdn.cryptopro.ru/content/csp40/html/group___c_s_p_examples_4_0vs3_6.html 496 | * https://cpdn.cryptopro.ru/content/csp40/html/group___pro_c_s_p_key_1gd56b0fb8e9d9c0278e45eb1994c38161.html 497 | * 498 | * @param {Uint8Array} sessionKeySimpleBlob Зашифрованный сессионный ключ в формате SIMPLEBLOB 499 | * @param {String} oldSenderContainerName Имя контейнера, содержащего закрытый ключ отправителя до перешифрования 500 | * @param {String} newSenderContainerName Имя нового контейнера, содержащего закрытый ключ отправителя 501 | * @param {Uint8Array} oldResponderPublicKeyBlob Публичный ключ получателя (PUBLICKEYBLOB), для которого зашифрован сессионный ключ 502 | * @param {Uint8Array} newResponderPublicKeyBlob Публичный ключ получателя (PUBLICKEYBLOB), для которого будет перешифрован сессионный ключ 503 | * 504 | * @return {EncryptedSessionKey} 505 | */ 506 | recodeSessionKeyForNewContainer: (sessionKeySimpleBlob, IV, oldSenderContainerName, newSenderContainerName, oldResponderPublicKeyBlob, newResponderPublicKeyBlob) => { 507 | let result = cryptoLib.RecodeSessionKeyForNewContainer( 508 | sessionKeySimpleBlob, sessionKeySimpleBlob.length, 509 | oldSenderContainerName, 510 | newSenderContainerName, 511 | oldResponderPublicKeyBlob, oldResponderPublicKeyBlob.length, 512 | newResponderPublicKeyBlob, newResponderPublicKeyBlob.length, 513 | IV, IV.length 514 | ); 515 | 516 | if(result.status) { 517 | throw new Error(result.errorMessage); 518 | } else { 519 | return { 520 | sessionKeyBlob: sessionKeySimpleBlob.subarray(0, sessionKeySimpleBlob.length), 521 | IV: IV.subarray(0, IV.length) 522 | }; 523 | } 524 | } 525 | }; 526 | 527 | }; 528 | -------------------------------------------------------------------------------- /src/nodeCryptopro.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #if defined(WIN32) || defined(_WIN32) 6 | # include 7 | # include 8 | # pragma comment( lib, "Advapi32.lib" ) 9 | # pragma comment(lib,"crypt32.lib") 10 | # define EXPORT __declspec(dllexport) 11 | #else 12 | # include 13 | # include 14 | # include 15 | # define EXPORT 16 | #endif 17 | 18 | #include 19 | 20 | #include "nodeCryptopro.h" 21 | 22 | HCRYPTPROV hContainerProv = 0; // Дескриптор CSP 23 | char* keyContainerName = NULL; 24 | 25 | ALG_ID ke_alg = CALG_PRO_EXPORT; 26 | DWORD kpMode = CRYPT_MODE_CFB; 27 | 28 | EXPORT CallResult InitParams(const char* sessionKeyExportAlg, const char* mode) { 29 | 30 | if( strcmp(sessionKeyExportAlg, "CALG_PRO12_EXPORT") == 0 ) 31 | ke_alg = CALG_PRO12_EXPORT; 32 | 33 | if( strcmp(mode, "CRYPT_MODE_CNT") == 0 ) 34 | kpMode = CRYPT_MODE_CNT; 35 | 36 | return ResultSuccess(); 37 | } 38 | 39 | EXPORT CallResult AcquireContextForContainer( 40 | const char* keyContainer 41 | ) { 42 | 43 | // Получение дескриптора контекста криптографического провайдера 44 | if(!CryptAcquireContext( &hContainerProv, keyContainer, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/ 0)) 45 | return HandleError("Error during CryptAcquireContext"); 46 | 47 | if(keyContainerName) 48 | free(keyContainerName); 49 | 50 | keyContainerName = (char *)malloc(strlen(keyContainer)); 51 | 52 | if(!keyContainerName) 53 | return HandleError("Out of memory"); 54 | 55 | strcpy(keyContainerName, keyContainer); 56 | 57 | return ResultSuccess(); 58 | } 59 | 60 | EXPORT CallResult SignHash( 61 | const char* keyContainer, 62 | BYTE* messageBytesArray, 63 | DWORD messageBytesArrayLength, 64 | BYTE* signatureBytesArray, 65 | DWORD* signatureBytesArrayLength 66 | ) { 67 | HCRYPTPROV hProv = 0; // Дескриптор CSP 68 | HCRYPTHASH hHash = 0; 69 | 70 | BYTE *pbSignature = NULL; 71 | DWORD signatureLength = 0; 72 | 73 | // Получение дескриптора контекста криптографического провайдера 74 | if(!CryptAcquireContext( &hProv, keyContainer, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/ 0)) 75 | return HandleError("Error during CryptAcquireContext"); 76 | 77 | // Создание объекта функции хеширования 78 | if(!CryptCreateHash(hProv, CALG_GR3411_2012_256, /*CALG_GR3411,*/0, 0, &hHash)) 79 | return HandleError("Error during CryptCreateHash"); 80 | 81 | // Вычисление криптографического хеша буфера 82 | if(!CryptHashData(hHash, messageBytesArray, messageBytesArrayLength, 0)) 83 | return HandleError("Error during CryptHashData"); 84 | 85 | // Определение размера подписи и распределение памяти 86 | if(!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, NULL, &signatureLength)) 87 | return HandleError("Error during CryptSignHash"); 88 | 89 | // Распределение памяти под буфер подписи 90 | pbSignature = (BYTE *)malloc(signatureLength); 91 | if(!pbSignature) 92 | return HandleError("Out of memory"); 93 | 94 | // Подпись объекта функции хеширования 95 | if(!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, pbSignature, &signatureLength)) 96 | return HandleError("Error during CryptSignHash"); 97 | 98 | memcpy(signatureBytesArray, pbSignature, signatureLength); 99 | memcpy(signatureBytesArrayLength, &signatureLength, sizeof(signatureLength)); 100 | 101 | free(pbSignature); 102 | // Уничтожение объекта функции хеширования 103 | if(hHash) 104 | CryptDestroyHash(hHash); 105 | if(hProv) 106 | CryptReleaseContext(hProv, 0); 107 | 108 | return ResultSuccess(); 109 | } 110 | 111 | EXPORT CallResult VerifySignature( 112 | BYTE* messageBytesArray, DWORD messageBytesArrayLength, 113 | BYTE* signatureByteArray, DWORD signatureBytesArrayLength, 114 | BYTE* publicKeyBlob, int publicKeyBlobLength, 115 | BOOL *verificationResultToReturn 116 | ) { 117 | HCRYPTPROV hProv = 0; // Дескриптор CSP 118 | HCRYPTHASH hHash = 0; 119 | HCRYPTKEY hPubKey = 0; 120 | 121 | BOOL verificationResult = FALSE; 122 | 123 | if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/CRYPT_VERIFYCONTEXT)) 124 | return HandleError("CryptAcquireContext failed"); 125 | 126 | // Получение откытого ключа отправителя и импортирование его в CSP. Дескриптор открытого ключа возвращается в hPubKey 127 | if(!CryptImportKey(hProv, publicKeyBlob, publicKeyBlobLength, 0, 0, &hPubKey)) 128 | return HandleError("Public key import failed"); 129 | 130 | // Создание объекта функции хеширования 131 | if(!CryptCreateHash(hProv, CALG_GR3411_2012_256, /*CALG_GR3411, */0, 0, &hHash)) 132 | return HandleError("Error during CryptCreateHash"); 133 | 134 | // Вычисление криптографического хеша буфера 135 | if(!CryptHashData(hHash, messageBytesArray, messageBytesArrayLength, 0)) 136 | return HandleError("Error during CryptHashData"); 137 | 138 | // Проверка цифровой подписи 139 | if(CryptVerifySignature(hHash, signatureByteArray, signatureBytesArrayLength, hPubKey, NULL, 0)) 140 | verificationResult = TRUE; 141 | else 142 | verificationResult = FALSE; 143 | 144 | memcpy(verificationResultToReturn, &verificationResult, sizeof(verificationResult)); 145 | 146 | if(hHash) 147 | CryptDestroyHash(hHash); 148 | if(hProv) 149 | CryptReleaseContext(hProv, 0); 150 | 151 | return ResultSuccess(); 152 | } 153 | 154 | EXPORT CallResult SignPreparedHash( 155 | const char* keyContainer, 156 | BYTE* hashBytesArray, 157 | DWORD hashBytesArrayLength, 158 | BYTE* signatureBytesArray, 159 | DWORD* signatureBytesArrayLength 160 | ) { 161 | HCRYPTPROV hProv = 0; // Дескриптор CSP 162 | HCRYPTHASH hHash = 0; 163 | 164 | BYTE *pbSignature = NULL; 165 | DWORD signatureLength = 0; 166 | 167 | // Получение дескриптора контекста криптографического провайдера 168 | if(!CryptAcquireContext( &hProv, keyContainer, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/ 0)) 169 | return HandleError("Error during CryptAcquireContext"); 170 | 171 | // Создание объекта функции хеширования 172 | if(!CryptCreateHash(hProv, CALG_GR3411_2012_256, /*CALG_GR3411,*/0, 0, &hHash)) 173 | return HandleError("Error during CryptCreateHash"); 174 | 175 | if(!CryptSetHashParam(hHash, HP_HASHVAL, hashBytesArray, 0)) 176 | return HandleError("Error during CryptSetHashParam"); 177 | 178 | // Определение размера подписи и распределение памяти 179 | if(!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, NULL, &signatureLength)) 180 | return HandleError("Error during CryptSignHash"); 181 | 182 | // Распределение памяти под буфер подписи 183 | pbSignature = (BYTE *)malloc(signatureLength); 184 | if(!pbSignature) 185 | return HandleError("Out of memory"); 186 | 187 | // Подпись объекта функции хеширования 188 | if(!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, pbSignature, &signatureLength)) 189 | return HandleError("Error during CryptSignHash"); 190 | 191 | memcpy(signatureBytesArray, pbSignature, signatureLength); 192 | memcpy(signatureBytesArrayLength, &signatureLength, sizeof(signatureLength)); 193 | 194 | free(pbSignature); 195 | // Уничтожение объекта функции хеширования 196 | if(hHash) 197 | CryptDestroyHash(hHash); 198 | if(hProv) 199 | CryptReleaseContext(hProv, 0); 200 | 201 | return ResultSuccess(); 202 | } 203 | 204 | EXPORT CallResult VerifyPreparedHashSignature( 205 | BYTE* hashBytesArray, DWORD hashBytesArrayLength, 206 | BYTE* signatureByteArray, DWORD signatureBytesArrayLength, 207 | BYTE* publicKeyBlob, int publicKeyBlobLength, 208 | BOOL *verificationResultToReturn 209 | ) { 210 | HCRYPTPROV hProv = 0; // Дескриптор CSP 211 | HCRYPTHASH hHash = 0; 212 | HCRYPTKEY hPubKey = 0; 213 | 214 | BOOL verificationResult = FALSE; 215 | 216 | if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/CRYPT_VERIFYCONTEXT)) 217 | return HandleError("CryptAcquireContext failed"); 218 | 219 | // Получение откытого ключа отправителя и импортирование его в CSP. Дескриптор открытого ключа возвращается в hPubKey 220 | if(!CryptImportKey(hProv, publicKeyBlob, publicKeyBlobLength, 0, 0, &hPubKey)) 221 | return HandleError("Public key import failed"); 222 | 223 | // Создание объекта функции хеширования 224 | if(!CryptCreateHash(hProv, CALG_GR3411_2012_256, /*CALG_GR3411, */0, 0, &hHash)) 225 | return HandleError("Error during CryptCreateHash"); 226 | 227 | if(!CryptSetHashParam(hHash, HP_HASHVAL, hashBytesArray, 0)) 228 | return HandleError("Error during CryptSetHashParam"); 229 | 230 | // Проверка цифровой подписи 231 | if(CryptVerifySignature(hHash, signatureByteArray, signatureBytesArrayLength, hPubKey, NULL, 0)) 232 | verificationResult = TRUE; 233 | else 234 | verificationResult = FALSE; 235 | 236 | memcpy(verificationResultToReturn, &verificationResult, sizeof(verificationResult)); 237 | 238 | if(hHash) 239 | CryptDestroyHash(hHash); 240 | if(hProv) 241 | CryptReleaseContext(hProv, 0); 242 | 243 | return ResultSuccess(); 244 | } 245 | 246 | EXPORT CallResult Encrypt( 247 | DWORD* sessionKeyBlobLength, BYTE* sessionKeyBlob, 248 | const char* senderContainerName, 249 | BYTE* responderPublicKeyBlob, int responderPublicKeyBlobLength, 250 | BYTE* textToEncrypt, 251 | int textToEncryptLength, 252 | BYTE* IV, 253 | DWORD* IVLength 254 | ) { 255 | HCRYPTPROV hProv = 0; // Дескриптор CSP 256 | HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа 257 | HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа 258 | HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования 259 | 260 | BYTE *pbKeyBlobSimple = NULL; // Указатель на сессионный ключевой BLOB 261 | DWORD dwBlobLenSimple; 262 | 263 | BYTE *pbIV = NULL; // Вектор инициализации сессионного ключа 264 | DWORD dwIV = 0; 265 | 266 | BYTE *pbCIPHER = NULL; // 267 | DWORD dwCIPHER = 0; 268 | 269 | DWORD bufLen = 0; 270 | 271 | // Получение дескриптора контейнера получателя с именем senderContainerName, находящегося в рамках провайдера 272 | if(!CryptAcquireContext(&hProv, senderContainerName, NULL, PROV_GOST_2012_256/*PROV_GOST_2001_DH*/, 0)) 273 | return HandleError("Error during CryptAcquireContext"); 274 | 275 | // Получение дескриптора закрытого ключа отправителя 276 | if(!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey)) 277 | return HandleError("Error during CryptGetUserKey private key"); 278 | 279 | // Получение ключа согласования импортом открытого ключа получателя на закрытом ключе отправителя 280 | if(!CryptImportKey(hProv, responderPublicKeyBlob, responderPublicKeyBlobLength, hKey, 0, &hAgreeKey)) 281 | return HandleError("Error during CryptImportKey public key"); 282 | 283 | // Установление алгоритма ключа согласования 284 | if(!CryptSetKeyParam(hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) 285 | return HandleError("Error during CryptSetKeyParam agree key"); 286 | 287 | // Генерация сессионного ключа 288 | if(!CryptGenKey(hProv, CALG_G28147, CRYPT_EXPORTABLE, &hSessionKey)) 289 | return HandleError("Error during CryptGenKey"); 290 | 291 | if(!CryptSetKeyParam(hSessionKey, KP_MODE, (PBYTE)&kpMode, 0)) 292 | return HandleError("Error during CryptSetKeyParam"); 293 | 294 | //-------------------------------------------------------------------- 295 | // Зашифрование сессионного ключа 296 | //-------------------------------------------------------------------- 297 | 298 | // Определение размера BLOBа сессионного ключа и распределение памяти 299 | if(!CryptExportKey( hSessionKey, hAgreeKey, SIMPLEBLOB, 0, NULL, &dwBlobLenSimple)) 300 | return HandleError("Error computing BLOB length"); 301 | 302 | pbKeyBlobSimple = (BYTE*)malloc(dwBlobLenSimple); 303 | 304 | if(!pbKeyBlobSimple) 305 | return HandleError("Out of memory"); 306 | 307 | // Зашифрование сессионного ключа на ключе Agree, экспорт в pbKeyBlobSimple 308 | if(!CryptExportKey(hSessionKey, hAgreeKey, SIMPLEBLOB, 0, pbKeyBlobSimple, &dwBlobLenSimple)) 309 | return HandleError("Error during CryptExportKey"); 310 | 311 | 312 | // Определение размера вектора инициализации сессионного ключа 313 | if(!CryptGetKeyParam(hSessionKey, KP_IV, NULL, &dwIV, 0)) 314 | return HandleError("Error computing IV length"); 315 | 316 | pbIV = (BYTE*)malloc(dwIV); 317 | if (!pbIV) 318 | return HandleError("Out of memory"); 319 | 320 | // Определение вектора инициализации сессионного ключа 321 | if(!CryptGetKeyParam(hSessionKey, KP_IV, pbIV, &dwIV, 0)) 322 | return HandleError("Error during CryptGetKeyParam"); 323 | 324 | memcpy(IV, pbIV, dwIV); 325 | memcpy(IVLength, &dwIV, sizeof(dwIV)); 326 | 327 | memcpy(sessionKeyBlob, pbKeyBlobSimple, dwBlobLenSimple); 328 | memcpy(sessionKeyBlobLength, &dwBlobLenSimple, sizeof(dwBlobLenSimple)); 329 | 330 | BOOL bFinal = TRUE; 331 | bufLen = textToEncryptLength; 332 | 333 | if(!CryptEncrypt(hSessionKey, 0, bFinal, 0, textToEncrypt, &textToEncryptLength, bufLen)) 334 | return HandleError("Encryption failed"); 335 | 336 | free(pbIV); 337 | free(pbKeyBlobSimple); 338 | if(hAgreeKey) 339 | CryptDestroyKey(hAgreeKey); 340 | if(hSessionKey) 341 | CryptDestroyKey(hSessionKey); 342 | if(hKey) 343 | CryptDestroyKey(hKey); 344 | if(hProv) 345 | CryptReleaseContext(hProv, 0); 346 | 347 | return ResultSuccess(); 348 | } 349 | 350 | EXPORT CallResult Decrypt( 351 | const char* responderContainerName, 352 | BYTE* senderPublicKeyBlob, 353 | int senderPublicKeyBlobLength, 354 | BYTE* encryptedText, int encryptedTextLength, 355 | BYTE* IV, int IVLength, 356 | BYTE* keySimpleBlob, int keySimpleBlobLength 357 | ) { 358 | HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа 359 | HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа 360 | HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования 361 | 362 | CallResult acquireResult; 363 | 364 | if( !keyContainerName || (keyContainerName && (strcmp(keyContainerName, responderContainerName) != 0)) ) { 365 | acquireResult = AcquireContextForContainer(responderContainerName); 366 | if(acquireResult.status != 0) { 367 | return acquireResult; 368 | } 369 | } 370 | 371 | if(!CryptGetUserKey(hContainerProv, AT_KEYEXCHANGE, &hKey)) 372 | return HandleError("Error during CryptGetUserKey private key"); 373 | 374 | if(!CryptImportKey(hContainerProv, senderPublicKeyBlob, senderPublicKeyBlobLength, hKey, 0, &hAgreeKey)) 375 | return HandleError("Error during CryptImportKey public key"); 376 | 377 | if(!CryptSetKeyParam(hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) 378 | return HandleError("Error during CryptSetKeyParam agree key"); 379 | 380 | if(!CryptImportKey(hContainerProv, keySimpleBlob, keySimpleBlobLength, hAgreeKey, 0, &hSessionKey)) 381 | return HandleError("Error during CryptImportKey session key"); 382 | 383 | if(!CryptSetKeyParam(hSessionKey, KP_IV, IV, 0)) 384 | return HandleError("Error during CryptSetKeyParam"); 385 | 386 | if(!CryptSetKeyParam(hSessionKey, KP_MODE, (PBYTE)&kpMode, 0)) 387 | return HandleError("Error during CryptSetKeyParam"); 388 | 389 | BOOL bFinal = TRUE; 390 | 391 | if(!CryptDecrypt(hSessionKey, 0, bFinal, 0, encryptedText, &encryptedTextLength)) 392 | return HandleError("Decryption failed"); 393 | 394 | 395 | if(hAgreeKey) 396 | CryptDestroyKey(hAgreeKey); 397 | if(hSessionKey) 398 | CryptDestroyKey(hSessionKey); 399 | if(hKey) 400 | CryptDestroyKey(hKey); 401 | 402 | return ResultSuccess(); 403 | } 404 | 405 | EXPORT CallResult EncryptWithSessionKey( 406 | BYTE* sessionKeySimpleBlob, DWORD sessionKeySimpleBlobLength, 407 | const char* senderContainerName, 408 | BYTE* responderPublicKeyBlob, int responderPublicKeyBlobLength, 409 | BYTE* textToEncrypt, 410 | int textToEncryptLength, 411 | BYTE* IV, 412 | int IVLength 413 | ) { 414 | HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа 415 | HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа 416 | HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования 417 | 418 | DWORD bufLen = 0; 419 | 420 | CallResult acquireResult; 421 | 422 | if( !keyContainerName || (keyContainerName && (strcmp(keyContainerName, senderContainerName) != 0)) ) { 423 | acquireResult = AcquireContextForContainer(senderContainerName); 424 | if(acquireResult.status != 0) { 425 | return acquireResult; 426 | } 427 | } 428 | 429 | // Получение дескриптора закрытого ключа отправителя 430 | if(!CryptGetUserKey(hContainerProv, AT_KEYEXCHANGE, &hKey)) 431 | return HandleError("Error during CryptGetUserKey private key"); 432 | 433 | // Получение ключа согласования импортом открытого ключа получателя на закрытом ключе отправителя 434 | if(!CryptImportKey(hContainerProv, responderPublicKeyBlob, responderPublicKeyBlobLength, hKey, 0, &hAgreeKey)) 435 | return HandleError("Error during CryptImportKey public key"); 436 | 437 | // Установление алгоритма ключа согласования 438 | if(!CryptSetKeyParam(hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) 439 | return HandleError("Error during CryptSetKeyParam agree key"); 440 | 441 | //Расшифровываем сессионный ключ 442 | if(!CryptImportKey(hContainerProv, sessionKeySimpleBlob, sessionKeySimpleBlobLength, hAgreeKey, 0, &hSessionKey)) 443 | return HandleError("Error during CryptImportKey session key"); 444 | 445 | //Установка режима шифрования 446 | if(!CryptSetKeyParam(hSessionKey, KP_MODE, (PBYTE)&kpMode, 0)) 447 | return HandleError("Error during CryptSetKeyParam"); 448 | 449 | if(!CryptSetKeyParam(hSessionKey, KP_IV, IV, 0)) 450 | return HandleError("Error during CryptSetKeyParam"); 451 | 452 | BOOL bFinal = TRUE; 453 | bufLen = textToEncryptLength; 454 | 455 | if(!CryptEncrypt(hSessionKey, 0, bFinal, 0, textToEncrypt, &textToEncryptLength, bufLen)) 456 | return HandleError("Encryption failed"); 457 | 458 | if(hAgreeKey) 459 | CryptDestroyKey(hAgreeKey); 460 | if(hSessionKey) 461 | CryptDestroyKey(hSessionKey); 462 | if(hKey) 463 | CryptDestroyKey(hKey); 464 | 465 | return ResultSuccess(); 466 | } 467 | 468 | EXPORT CallResult RecodeSessionKey( 469 | BYTE* sessionKeySimpleBlob, DWORD sessionKeySimpleBlobLength, 470 | const char* senderContainerName, 471 | BYTE* oldResponderPublicKeyBlob, int oldResponderPublicKeyBlobLength, 472 | BYTE* newResponderPublicKeyBlob, int newResponderPublicKeyBlobLength, 473 | BYTE* IV, int IVLength 474 | ) { 475 | HCRYPTPROV hProv = 0; // Дескриптор CSP 476 | HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа 477 | 478 | HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа 479 | HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования 480 | HCRYPTKEY hAgreeKey2 = 0; 481 | 482 | BYTE *pbKeyBlobSimple = NULL; // Указатель на сессионный ключевой BLOB 483 | DWORD dwBlobLenSimple; 484 | 485 | 486 | // Получение дескриптора контейнера с именем senderContainerName, находящегося в рамках провайдера 487 | if(!CryptAcquireContext(&hProv, senderContainerName, NULL, PROV_GOST_2012_256/*PROV_GOST_2001_DH*/, 0)) 488 | return HandleError("Error during CryptAcquireContext"); 489 | 490 | // Получение дескриптора закрытого ключа отправителя 491 | if(!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey)) 492 | return HandleError("Error during CryptGetUserKey private key"); 493 | 494 | // Получение ключа согласования импортом открытого ключа получателя на закрытом ключе отправителя 495 | if(!CryptImportKey(hProv, oldResponderPublicKeyBlob, oldResponderPublicKeyBlobLength, hKey, 0, &hAgreeKey)) 496 | return HandleError("Error during CryptImportKey public key"); 497 | if(!CryptImportKey(hProv, newResponderPublicKeyBlob, newResponderPublicKeyBlobLength, hKey, 0, &hAgreeKey2)) 498 | return HandleError("Error during CryptImportKey public key"); 499 | 500 | // Установление алгоритма ключа согласования 501 | if(!CryptSetKeyParam(hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) 502 | return HandleError("Error during CryptSetKeyParam agree key"); 503 | if(!CryptSetKeyParam(hAgreeKey2, KP_ALGID, (LPBYTE)&ke_alg, 0)) 504 | return HandleError("Error during CryptSetKeyParam agree key"); 505 | 506 | //Расшифровываем сессионный ключ 507 | if(!CryptImportKey(hProv, sessionKeySimpleBlob, sessionKeySimpleBlobLength, hAgreeKey, CRYPT_EXPORTABLE, &hSessionKey)) 508 | return HandleError("Error during CryptImportKey session key"); 509 | 510 | if(!CryptSetKeyParam(hSessionKey, KP_IV, IV, 0)) 511 | return HandleError("Error during CryptSetKeyParam"); 512 | 513 | // Определение размера BLOBа сессионного ключа и распределение памяти 514 | if(!CryptExportKey( hSessionKey, hAgreeKey2, SIMPLEBLOB, 0, NULL, &dwBlobLenSimple)) 515 | return HandleError("Error computing BLOB length"); 516 | 517 | pbKeyBlobSimple = (BYTE*)malloc(dwBlobLenSimple); 518 | 519 | if(!pbKeyBlobSimple) 520 | return HandleError("Out of memory"); 521 | 522 | // Зашифрование сессионного ключа на ключе Agree, экспорт в pbKeyBlobSimple 523 | if(!CryptExportKey(hSessionKey, hAgreeKey2, SIMPLEBLOB, 0, pbKeyBlobSimple, &dwBlobLenSimple)) 524 | return HandleError("Error during CryptExportKey"); 525 | 526 | memcpy(sessionKeySimpleBlob, pbKeyBlobSimple, sessionKeySimpleBlobLength); 527 | // memcpy(sessionKeyBlobLength, &dwBlobLenSimple, sizeof(dwBlobLenSimple)); 528 | 529 | free(pbKeyBlobSimple); 530 | if(hAgreeKey) 531 | CryptDestroyKey(hAgreeKey); 532 | if(hAgreeKey2) 533 | CryptDestroyKey(hAgreeKey2); 534 | 535 | if(hSessionKey) 536 | CryptDestroyKey(hSessionKey); 537 | if(hProv) 538 | CryptReleaseContext(hProv, 0); 539 | if(hKey) 540 | CryptDestroyKey(hKey); 541 | 542 | return ResultSuccess(); 543 | } 544 | 545 | EXPORT CallResult RecodeSessionKeyForNewContainer( 546 | BYTE* sessionKeySimpleBlob, DWORD sessionKeySimpleBlobLength, 547 | const char* oldContainerName, 548 | const char* newContainerName, 549 | BYTE* oldResponderPublicKeyBlob, int oldResponderPublicKeyBlobLength, 550 | BYTE* newResponderPublicKeyBlob, int newResponderPublicKeyBlobLength, 551 | BYTE* IV, int IVLength 552 | ) { 553 | HCRYPTPROV hProv = 0; // Дескриптор CSP 554 | HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа 555 | HCRYPTPROV hProvNew = 0; // Дескриптор CSP 556 | HCRYPTKEY hKeyNew = 0; // Дескриптор закрытого ключа 557 | 558 | HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа 559 | HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования 560 | HCRYPTKEY hAgreeKey2 = 0; 561 | 562 | BYTE *pbKeyBlobSimple = NULL; // Указатель на сессионный ключевой BLOB 563 | DWORD dwBlobLenSimple; 564 | 565 | 566 | // Получение дескриптора контейнера с именем senderContainerName, находящегося в рамках провайдера 567 | if(!CryptAcquireContext(&hProv, oldContainerName, NULL, PROV_GOST_2012_256/*PROV_GOST_2001_DH*/, 0)) 568 | return HandleError("Error during CryptAcquireContext"); 569 | if(!CryptAcquireContext(&hProvNew, newContainerName, NULL, PROV_GOST_2012_256/*PROV_GOST_2001_DH*/, 0)) 570 | return HandleError("Error during CryptAcquireContext"); 571 | 572 | 573 | // Получение дескриптора закрытого ключа отправителя 574 | if(!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey)) 575 | return HandleError("Error during CryptGetUserKey private key"); 576 | if(!CryptGetUserKey(hProvNew, AT_KEYEXCHANGE, &hKeyNew)) 577 | return HandleError("Error during CryptGetUserKey private key"); 578 | 579 | // Получение ключа согласования импортом открытого ключа получателя на закрытом ключе отправителя 580 | if(!CryptImportKey(hProv, oldResponderPublicKeyBlob, oldResponderPublicKeyBlobLength, hKey, 0, &hAgreeKey)) 581 | return HandleError("Error during CryptImportKey public key"); 582 | if(!CryptImportKey(hProvNew, newResponderPublicKeyBlob, newResponderPublicKeyBlobLength, hKeyNew, 0, &hAgreeKey2)) 583 | return HandleError("Error during CryptImportKey public key"); 584 | 585 | // Установление алгоритма ключа согласования 586 | if(!CryptSetKeyParam(hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) 587 | return HandleError("Error during CryptSetKeyParam agree key"); 588 | if(!CryptSetKeyParam(hAgreeKey2, KP_ALGID, (LPBYTE)&ke_alg, 0)) 589 | return HandleError("Error during CryptSetKeyParam agree key"); 590 | 591 | //Расшифровываем сессионный ключ 592 | if(!CryptImportKey(hProv, sessionKeySimpleBlob, sessionKeySimpleBlobLength, hAgreeKey, CRYPT_EXPORTABLE, &hSessionKey)) 593 | return HandleError("Error during CryptImportKey session key"); 594 | 595 | if(!CryptSetKeyParam(hSessionKey, KP_IV, IV, 0)) 596 | return HandleError("Error during CryptSetKeyParam"); 597 | 598 | // Определение размера BLOBа сессионного ключа и распределение памяти 599 | if(!CryptExportKey( hSessionKey, hAgreeKey2, SIMPLEBLOB, 0, NULL, &dwBlobLenSimple)) 600 | return HandleError("Error computing BLOB length"); 601 | 602 | pbKeyBlobSimple = (BYTE*)malloc(dwBlobLenSimple); 603 | 604 | if(!pbKeyBlobSimple) 605 | return HandleError("Out of memory"); 606 | 607 | // Зашифрование сессионного ключа на ключе Agree, экспорт в pbKeyBlobSimple 608 | if(!CryptExportKey(hSessionKey, hAgreeKey2, SIMPLEBLOB, 0, pbKeyBlobSimple, &dwBlobLenSimple)) 609 | return HandleError("Error during CryptExportKey"); 610 | 611 | memcpy(sessionKeySimpleBlob, pbKeyBlobSimple, sessionKeySimpleBlobLength); 612 | // memcpy(sessionKeyBlobLength, &dwBlobLenSimple, sizeof(dwBlobLenSimple)); 613 | 614 | free(pbKeyBlobSimple); 615 | if(hAgreeKey) 616 | CryptDestroyKey(hAgreeKey); 617 | if(hAgreeKey2) 618 | CryptDestroyKey(hAgreeKey2); 619 | 620 | if(hSessionKey) 621 | CryptDestroyKey(hSessionKey); 622 | if(hProv) 623 | CryptReleaseContext(hProv, 0); 624 | if(hProvNew) 625 | CryptReleaseContext(hProvNew, 0); 626 | 627 | if(hKey) 628 | CryptDestroyKey(hKey); 629 | if(hKeyNew) 630 | CryptDestroyKey(hKey); 631 | 632 | return ResultSuccess(); 633 | } 634 | 635 | EXPORT CallResult GenerateSessionKey( 636 | DWORD* sessionKeyBlobLength, BYTE* sessionKeyBlob, 637 | const char* senderContainerName, 638 | BYTE* responderPublicKeyBlob, int responderPublicKeyBlobLength, 639 | BYTE* IV, DWORD* IVLength 640 | ) { 641 | HCRYPTPROV hProv = 0; // Дескриптор CSP 642 | HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа 643 | HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа 644 | HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования 645 | 646 | BYTE *pbKeyBlobSimple = NULL; // Указатель на сессионный ключевой BLOB 647 | DWORD dwBlobLenSimple; 648 | 649 | BYTE *pbIV = NULL; // Вектор инициализации сессионного ключа 650 | DWORD dwIV = 0; 651 | 652 | // Получение дескриптора контейнера получателя с именем senderContainerName, находящегося в рамках провайдера 653 | if(!CryptAcquireContext(&hProv, senderContainerName, NULL, PROV_GOST_2012_256/*PROV_GOST_2001_DH*/, 0)) 654 | return HandleError("Error during CryptAcquireContext"); 655 | 656 | // Получение дескриптора закрытого ключа отправителя 657 | if(!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey)) 658 | return HandleError("Error during CryptGetUserKey private key"); 659 | 660 | // Получение ключа согласования импортом открытого ключа получателя на закрытом ключе отправителя 661 | if(!CryptImportKey(hProv, responderPublicKeyBlob, responderPublicKeyBlobLength, hKey, 0, &hAgreeKey)) 662 | return HandleError("Error during CryptImportKey public key"); 663 | 664 | // Установление алгоритма ключа согласования 665 | if(!CryptSetKeyParam(hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) 666 | return HandleError("Error during CryptSetKeyParam agree key"); 667 | 668 | // Генерация сессионного ключа 669 | if(!CryptGenKey(hProv, CALG_G28147, CRYPT_EXPORTABLE, &hSessionKey)) 670 | return HandleError("Error during CryptGenKey"); 671 | 672 | //Установка режима шифрования 673 | if(!CryptSetKeyParam(hSessionKey, KP_MODE, (PBYTE)&kpMode, 0)) 674 | return HandleError("Error during CryptSetKeyParam"); 675 | 676 | //-------------------------------------------------------------------- 677 | // Зашифрование сессионного ключа 678 | //-------------------------------------------------------------------- 679 | 680 | // Определение размера BLOBа сессионного ключа и распределение памяти 681 | if(!CryptExportKey( hSessionKey, hAgreeKey, SIMPLEBLOB, 0, NULL, &dwBlobLenSimple)) 682 | return HandleError("Error computing BLOB length"); 683 | 684 | pbKeyBlobSimple = (BYTE*)malloc(dwBlobLenSimple); 685 | 686 | if(!pbKeyBlobSimple) 687 | return HandleError("Out of memory"); 688 | 689 | // Зашифрование сессионного ключа на ключе Agree, экспорт в pbKeyBlobSimple 690 | if(!CryptExportKey(hSessionKey, hAgreeKey, SIMPLEBLOB, 0, pbKeyBlobSimple, &dwBlobLenSimple)) 691 | return HandleError("Error during CryptExportKey"); 692 | 693 | // Определение размера вектора инициализации сессионного ключа 694 | if(!CryptGetKeyParam(hSessionKey, KP_IV, NULL, &dwIV, 0)) 695 | return HandleError("Error computing IV length"); 696 | 697 | pbIV = (BYTE*)malloc(dwIV); 698 | if (!pbIV) 699 | return HandleError("Out of memory"); 700 | 701 | // Определение вектора инициализации сессионного ключа 702 | if(!CryptGetKeyParam(hSessionKey, KP_IV, pbIV, &dwIV, 0)) 703 | return HandleError("Error during CryptGetKeyParam"); 704 | 705 | memcpy(IV, pbIV, dwIV); 706 | memcpy(IVLength, &dwIV, sizeof(dwIV)); 707 | 708 | memcpy(sessionKeyBlob, pbKeyBlobSimple, dwBlobLenSimple); 709 | memcpy(sessionKeyBlobLength, &dwBlobLenSimple, sizeof(dwBlobLenSimple)); 710 | 711 | free(pbIV); 712 | free(pbKeyBlobSimple); 713 | if(hAgreeKey) 714 | CryptDestroyKey(hAgreeKey); 715 | if(hSessionKey) 716 | CryptDestroyKey(hSessionKey); 717 | if(hKey) 718 | CryptDestroyKey(hKey); 719 | if(hProv) 720 | CryptReleaseContext(hProv, 0); 721 | 722 | return ResultSuccess(); 723 | } 724 | 725 | EXPORT CallResult CreateHash(BYTE* bytesArrayToHash, DWORD bytesArrayToHashLength, BYTE* hash, DWORD* hashLength) { 726 | HCRYPTPROV hProv = 0; // Дескриптор CSP 727 | HCRYPTHASH hHash = 0; 728 | 729 | BYTE rgbHash[GR3411LEN]; 730 | DWORD cbHash = GR3411LEN; 731 | 732 | if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/CRYPT_VERIFYCONTEXT)) { 733 | return HandleError("CryptAcquireContext failed"); 734 | } 735 | 736 | if(!CryptCreateHash(hProv, /*CALG_GR3411*/CALG_GR3411_2012_256, 0, 0, &hHash)) { 737 | CryptReleaseContext(hProv, 0); 738 | return HandleError("CryptCreateHash failed"); 739 | } 740 | 741 | if(!CryptHashData(hHash, bytesArrayToHash, bytesArrayToHashLength, 0)) { 742 | CryptReleaseContext(hProv, 0); 743 | CryptDestroyHash(hHash); 744 | return HandleError("CryptHashData failed"); 745 | } 746 | 747 | if(!CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) { 748 | CryptDestroyHash(hHash); 749 | CryptReleaseContext(hProv, 0); 750 | return HandleError("CryptGetHashParam failed"); 751 | } 752 | 753 | memcpy(hash, rgbHash, cbHash); 754 | memcpy(hashLength, &cbHash, sizeof(cbHash)); 755 | 756 | CryptDestroyHash(hHash); 757 | CryptReleaseContext(hProv, 0); 758 | 759 | return ResultSuccess(); 760 | } 761 | 762 | EXPORT CallResult GetPublicKeyFromCertificateData(BYTE *publicKeyBlob, DWORD *publicKeyBlobLength, BYTE *certificateData, DWORD certificateDataLength) { 763 | 764 | PCCERT_CONTEXT pCertContext = NULL; 765 | HCRYPTKEY hPublicKey; 766 | HCRYPTPROV hProv = 0; // Дескриптор CSP 767 | 768 | BYTE pbKeyBlob[MAX_PUBLICKEYBLOB_SIZE]; 769 | DWORD dwKeyBlobLen = MAX_PUBLICKEYBLOB_SIZE; 770 | 771 | if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/CRYPT_VERIFYCONTEXT)) 772 | return HandleError("CryptAcquireContext failed"); 773 | 774 | pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, certificateData, certificateDataLength); 775 | 776 | if (!pCertContext) 777 | return HandleError( "CertCreateCertificateContext" ); 778 | 779 | // Импортируем открытый ключ 780 | if (!CryptImportPublicKeyInfoEx(hProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->SubjectPublicKeyInfo), 0, 0, NULL, &hPublicKey)) { 781 | CertFreeCertificateContext(pCertContext); 782 | return HandleError( "CryptImportPublicKeyInfoEx" ); 783 | } 784 | 785 | // Экспортируем его в BLOB 786 | if (!CryptExportKey(hPublicKey, 0, PUBLICKEYBLOB, 0, pbKeyBlob, &dwKeyBlobLen)) { 787 | CryptDestroyKey(hPublicKey); 788 | return HandleError( "CryptExportKey" ); 789 | } 790 | 791 | memcpy(publicKeyBlob, pbKeyBlob, dwKeyBlobLen); 792 | memcpy(publicKeyBlobLength, &dwKeyBlobLen, sizeof(dwKeyBlobLen)); 793 | 794 | CertFreeCertificateContext(pCertContext); 795 | CryptDestroyKey(hPublicKey); 796 | CryptReleaseContext(hProv, 0); 797 | 798 | return ResultSuccess(); 799 | } 800 | 801 | 802 | EXPORT CallResult GetPublicKeyFromCertificateFile(BYTE *publicKeyBlob, DWORD *publicKeyBlobLength, const char *certificateFileName) { 803 | FILE *certf = NULL; // Файл, в котором хранится сертификат 804 | 805 | if((certf = fopen(certificateFileName, "rb"))) { 806 | DWORD cbCert = 2000; 807 | BYTE pbCert[2000]; 808 | PCCERT_CONTEXT pCertContext = NULL; 809 | HCRYPTKEY hPublicKey; 810 | HCRYPTPROV hProv = 0; // Дескриптор CSP 811 | 812 | BYTE pbKeyBlob[MAX_PUBLICKEYBLOB_SIZE]; 813 | DWORD dwKeyBlobLen = MAX_PUBLICKEYBLOB_SIZE; 814 | 815 | if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/CRYPT_VERIFYCONTEXT)) 816 | return HandleError("CryptAcquireContext failed"); 817 | 818 | cbCert = (DWORD)fread(pbCert, 1, cbCert, certf); 819 | if(!cbCert) 820 | return HandleError( "Failed to read certificate" ); 821 | 822 | pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbCert, cbCert); 823 | 824 | if (!pCertContext) 825 | return HandleError( "CertCreateCertificateContext" ); 826 | 827 | // Импортируем открытый ключ 828 | if (!CryptImportPublicKeyInfoEx(hProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->SubjectPublicKeyInfo), 0, 0, NULL, &hPublicKey)) { 829 | CertFreeCertificateContext(pCertContext); 830 | return HandleError( "CryptImportPublicKeyInfoEx" ); 831 | } 832 | 833 | // Экспортируем его в BLOB 834 | if (!CryptExportKey(hPublicKey, 0, PUBLICKEYBLOB, 0, pbKeyBlob, &dwKeyBlobLen)) { 835 | CryptDestroyKey(hPublicKey); 836 | return HandleError( "CryptExportKey" ); 837 | } 838 | 839 | memcpy(publicKeyBlob, pbKeyBlob, dwKeyBlobLen); 840 | memcpy(publicKeyBlobLength, &dwKeyBlobLen, sizeof(dwKeyBlobLen)); 841 | 842 | CertFreeCertificateContext(pCertContext); 843 | CryptDestroyKey(hPublicKey); 844 | CryptReleaseContext(hProv, 0); 845 | fclose(certf); 846 | } else { 847 | return HandleError( "Problem opening certificate file" ); 848 | } 849 | 850 | return ResultSuccess(); 851 | } 852 | 853 | CallResult GetPublicKeyFromCertificate(BYTE *publicKeyBlob, DWORD *publicKeyBlobLength, const char *certificateSubjectKey) { 854 | PCCERT_CONTEXT pCertContext = NULL; // Контекст сертификата 855 | HCERTSTORE hStoreHandle = 0; // Дескриптор хранилища сертификатов 856 | HCRYPTKEY hPublicKey = 0; // Дескриптор открытого ключа 857 | HCRYPTPROV hProv = 0; // Дескриптор CSP 858 | 859 | BYTE *pbKeyBlob = NULL; // Указатель на ключевой BLOB 860 | DWORD dwKeyBlobLen; // Длина ключевого BLOBа получателя 861 | 862 | // DWORD dwKeySpecSender; 863 | 864 | // Открытие системного хранилища сертификатов. 865 | hStoreHandle = CertOpenSystemStore(0, "MY"); 866 | 867 | if(!hStoreHandle) { 868 | return HandleError( "Error getting store handle"); 869 | } 870 | 871 | pCertContext = CertFindCertificateInStore(hStoreHandle, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, certificateSubjectKey, NULL); 872 | 873 | if(!pCertContext) { 874 | return HandleError("Error finding certificate"); 875 | } 876 | 877 | //-------------------------------------------------------------------- 878 | // Получение дескриптора CSP, включая доступ к связанному с ним ключевому 879 | // контейнеру для контекста сертификата pCertContext 880 | // if(!CryptAcquireCertificatePrivateKey(pCertContext, 0, NULL, &hProv, &dwKeySpecSender, NULL)) { 881 | // return HandleError("Error during CryptAcquireCertificatePrivateKey"); 882 | // } 883 | 884 | if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_GOST_2012_256, /*PROV_GOST_2001_DH,*/CRYPT_VERIFYCONTEXT)) 885 | return HandleError("CryptAcquireContext failed"); 886 | 887 | //-------------------------------------------------------------------- 888 | // Получение дескриптора открытого ключа 889 | if(!CryptImportPublicKeyInfoEx(hProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->SubjectPublicKeyInfo), 0, 0, NULL, &hPublicKey)) { 890 | return HandleError("Error during CryptImportPublicKeyInfoEx public key"); 891 | } 892 | 893 | //-------------------------------------------------------------------- 894 | // Определение размера BLOBа открытого ключа и распределение памяти. 895 | if(!CryptExportKey(hPublicKey, 0, PUBLICKEYBLOB, 0, NULL, &dwKeyBlobLen)) { 896 | return HandleError("Error computing BLOB length"); 897 | } 898 | 899 | pbKeyBlob = (BYTE*)malloc(dwKeyBlobLen); 900 | 901 | if(!pbKeyBlob) { 902 | return HandleError("Out of memory"); 903 | } 904 | 905 | //-------------------------------------------------------------------- 906 | // Экспортирование открытого ключа получателя в BLOB открытого ключа. 907 | if(!CryptExportKey(hPublicKey, 0, PUBLICKEYBLOB, 0, pbKeyBlob, &dwKeyBlobLen)) { 908 | return HandleError("Error during CryptExportKey"); 909 | } 910 | 911 | memcpy(publicKeyBlob, pbKeyBlob, dwKeyBlobLen); 912 | memcpy(publicKeyBlobLength, &dwKeyBlobLen, sizeof(dwKeyBlobLen)); 913 | 914 | 915 | free(pbKeyBlob); 916 | CertFreeCertificateContext(pCertContext); 917 | CryptDestroyKey(hPublicKey); 918 | CryptReleaseContext(hProv, 0); 919 | CertCloseStore(hStoreHandle, 0); 920 | 921 | return ResultSuccess(); 922 | } 923 | 924 | CallResult HandleError(const char *errorMessage) { 925 | DWORD errorCode = GetLastError(); 926 | if(!errorCode) 927 | errorCode = 1; 928 | 929 | CallResult result = { 930 | errorCode, 931 | errorCode, 932 | errorMessage 933 | }; 934 | 935 | printf("Error number : 0x%x\n", result.errorCode); 936 | printf("Error description: %s\n", result.errorMessage); 937 | 938 | return result; 939 | } 940 | --------------------------------------------------------------------------------