📜 ⬆️ ⬇️

Using MS CRYPTO API in Caché

Speaking of databases as sources of knowledge, we always mean that it is not only a large warehouse of diverse, orderly (or not) information, but also a place for its safe storage. Security, as a rule, means the protection of digital data from unauthorized access during transmission via communication channels, but do not forget about the physical protection of data carriers. Nevertheless, I will not argue which armored doors should be placed in your server room and how many guards should be on duty at the checkpoint, but I will tell you about cryptography.

Cryptography, in the broadest sense of the word, is the science of methods for ensuring the confidentiality and authenticity of information. That is, it ensures the impossibility of reading your data by strangers, as well as the impossibility of secretly replacing data with third parties. Over the past four thousand years, scientists, engineers and other figures have developed many ingenious encryption devices and mechanisms, which are now replaced by one computer. Therefore, all the current variety of means of cryptographic protection of information (SKZI) is provided by a variety of algorithms, which are invented by many.

Attention, question! How can a developer, far from cryptographic wisdom, ensure data protection in each specific application? Do I need to invent my own ingenious encryption algorithm, or can I use a SKZI written by someone else, and if so, how? Fortunately, there is no need to reinvent the wheel. You can, for example, use a single software interface - MS CryptoAPI. It describes a wide range of cryptographic functions and algorithms that can be implemented by different ICTD development companies (also called cryptographic providers or CSP - Cryptography Service Provider) in their own way, but using a single API to access them.
To implement cryptographic functions in your application, do the following:

The task I set for myself was to go through each of the listed steps, and, as a result, to get a solution that allows Caché to access the MS Crypto API functions.
Having studied the proposed approach, it is easy to find that it has two huge advantages:

There is also one “small” minus - CryptoAPI works exclusively under Windows.

Suppose that the pros have inspired us, and the minuses did not disappoint, but the task is quite specific - to find an approach to using the functions of CryptoAPI for working with the Caché DBMS. Let, CSP is already installed and configured: how to work with it from Cach? One option is to use the Callout mechanism which is implemented in Caché. About him further and will be discussed.
')
Callout is intended for calling Caché functions from DLLs. In fact, this means that you can create code, for example in C ++, build it into a DLL, and then use it in your solutions. Also, this approach allows you to use when creating a DLL a variety of features of the Windows API, in particular, CryptoAPI.

At the planning stage, I immediately decided to create not only the DLL itself (it will be called ISCAPI), but also a testing and debugging tool (CryptoConsole).

The figure below shows this graphically. There is a base class CacheCommon ( ++), which implements functions that allow you to configure a crypto-provider, initialize the SKZI, get the context, keys, hashing data, create and verify a digital signature, encrypt, logging and much more. All these functions are consistently used in the debugging console and library (the console and DLL are written in C ++). A single form of calls plays a special role, because the DLL in Caché is very problematic to debug, so the main part of the load during development, debugging and testing lies on the console. The code for the entire solution is open, you can find it here .

image

On the server side, Caché interacts with ISCAPI.DLL through the iscapi.Signer class.

Text iscapi.Signer class
///     -  MS CRYPTO API Class iscapi.Signer Extends %RegisteredObject { ///  DLL /// dllPath -     ClassMethod LoadDLL(dllPath As %String) As %Status { s result = $$$OK if (dllPath = "") { w "Please set dllPath equal to path to the ISCAPI.dll" q $$$ERROR($$$GeneralError, "No path to iscapi.dll is provided") } try { d $zf(-3, dllPath) } catch (ex) { s result = ex.AsStatus() } if (result=1) {w "DLL from "_dllPath_" was loaded"} else {w "Cannot load DLL from "_dllPath} q result } ///  DLL ClassMethod UnloadDLL() { d $zf(-3, "") } ///  -. /// provType -   (VipNet=2, CryptoPro=75) /// algId -   (32798) /// containerName -      /// pin -    (  , CSP      ,    ) /// providerName -  - ClassMethod Init(provType = 75, algId = 32798, containerName As %String, pin As %String = "111111", providerName As %String = "") As %Status { s result = $$$OK try { d $zf(-3, "", "Init", provType, algId, containerName, pin, providerName) } catch (ex) { s result = ex.AsStatus() } if (result=1) {w "CSP was successfully initialized"} else {w "Error during CSP initialization"} q result } ///  . /// logFileName -   .     . /// logLevel -   /// 0 -  /// 1 -   /// 2 -   /// logTargets - ,      /// 0 -    /// 1 -    /// 2 -    /// 3 -    ClassMethod InitLogger(logFileName As %String = "c:\iscapi.log", logLevel As %Integer = 2, logTargets As %Integer = 3) As %Status { s result = $$$OK try { d $zf(-3, "", "InitLogger", logFileName, logLevel, logTargets) } catch ex { s result = ex.AsStatus() } if (result=1) {w "Logger was successfully initialized"} else {w "Error during Logger initialization"} q result } ///   . ///         ,       . ClassMethod HashData(dataPortion As %String) As %Status { s result = $$$OK try { d $zf(-3, "", "HashData", dataPortion) } catch ex { s result = ex.AsStatus() } q result } ///  . ClassMethod HashFile(fileName As %String) As %Status { s result = $$$OK try { d $zf(-3, "", "HashFile", fileName) } catch ex { s result = ex.AsStatus() } q result } ///   . ///     ClassMethod GetHashValue() As %String { s result = "" try { s result = $zf(-3, "", "GetHashValue", "") } catch ex { w "GHV exception", ! zw ex s result = "" } w "GHV result is:", result, ! q result } ClassMethod ExportUserKey() As %String { s result = "" try { s result = $zf(-3, "", "ExportUserKey", "") } catch ex { s result = "" } q result } ///   . ///  . ClassMethod SignNewHash(dataPortion As %String) As %String { s result = "" try { s result = $zf(-3, "", "SignNewHash", dataPortion, "") } catch ex { s result = "" } q result } ///    . ClassMethod SignCurrentHash() As %String { s result = "" try { s result = $zf(-3, "", "SignCurrentHash", "") } catch ex { s result = "" } w "Signature recieved: ",result,! q result } ///   . ClassMethod VerifyHash(hash As %String, sign As %String) As %Boolean { s result = 0 try { s result = $zf(-3, "", "VerifyHash", hash, sign, 0) } catch ex { s result = 0 } q result } ClassMethod VerifyHashByKey(hash As %String, sign As %String, pubKey As %String) As %Boolean { s result = 0 try { s result = $zf(-3, "", "VerifyHashByKey", hash, sign, pubKey, 0) } catch ex { s result = 0 } q result } ///      . ///    ClassMethod VerifySignature(dataPortion As %String, sign As %String) As %Boolean { s result = 0 try { s result = $zf(-3, "", "VerifySignature", dataPortion, sign, 0) } catch ex { s result = 0 } q result } ///     DLL ClassMethod ReleaseAll() As %Status { s result = $$$OK try { d $zf(-3, "", "ReleaseAll") d ..UnloadDLL() } catch ex { s result = ex.AsStatus() } q result } ///     HEX /// TODO: rewrite ClassMethod ByteToHex(bString As %String) As %String { s str = "" for i=1:1:$l(bString) { s hex = $zhex($ascii($e(bString, i))) if ($l(hex) = 1) s hex = "0" _ hex s str = str _ hex } q str } ClassMethod HexToString(value As %String) As %String { s str = "" for i=1:2:$l(value) { s hex = $e(value, i, i + 1) s str = str _ $c($zhex(hex)) } q str } ///  DLL ClassMethod PrintProviders() As %Status { s result = $$$OK try { d $zf(-3, "", "PrintProviders") } catch ex { s result = ex.AsStatus() } q result } ///   ClassMethod Test() { s data = "123!" d ..LoadDLL("C:\ISCAPI.dll") w "DLL loaded", ! d ..InitLogger("c:\iscapiL.txt", 2, 1) w "Logger initialized", ! d ..PrintProviders() d ..Init(75, 32798, "CacheCrypt", "", "Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider") w "CSP initialized", ! d ..HashData(data) w "Hash created on: ", data, ! s hash = ..GetHashValue() w "Hash received, hash length=", $l(hash), ! w "Hash to base64:", !, $system.Encryption.Base64Encode(hash), ! w "Hash to HEX:", !, ..ByteToHex(hash), ! w "Hash value:", hash, ! s sign = ..SignCurrentHash() w "Hash signed, sign length=", $l(sign), ! w "Sign to base64:", !, $system.Encryption.Base64Encode(sign), ! w "Sign to HEX:", !, ..ByteToHex(sign), ! s vfy = ..VerifyHash(hash, sign) w "Verifying Hash signature result = ", vfy, ! s vfy = ..VerifySignature(data, sign) w "Verifying Signature by input text result = ", vfy, ! w "Exporting User Key...",! s userKey = ..ExportUserKey() w "Size: ", $l(userKey), ! w "UserKeyBytes: ", ..ByteToHex(userKey), ! s vfy = ..VerifyHashByKey(hash, sign, userKey) w "Verifying Hash signature ByKey result = ", vfy, ! d ..ReleaseAll() } } 



Here the callout mechanism is implemented, which means that you can directly access the CryptoAPI functions that exist in the DLL from the Caché class methods. Access is implemented through a call to the iscapi.Signer class method from the Caché Object Script. Among other things, it is also a cognitive example for novice developers, allowing to grasp the peculiarities of working with external libraries from Caché.

Now I want to say a few words about the functions that have been implemented. The most convenient way is to check them from the console application (CryptoConsole.exe). Immediately after its launch you can see all available commands and the order of their call. To enter parameters for a function, simply list them, separated by a space. Long parameters consisting of several words, as usual, are framed with quotes.
Console commands can be divided into three types:
1. Service teams
A typical example is the help command, which lists all the available commands in the process. The service also includes the showProviders and showProvParams commands that display the data on the crypto-providers installed in the system to the user.
2. Initialization commands
Initialization commands are needed to configure and run a crypto-provider. In general, to initialize its context, you need to specify:

There are additional parameters that can be set to the system, such as encryption algorithm codes, but these five are necessary for the initial initialization of the CSP.
3. Cryptographic commands
When the CSP is initialized, we need to get the context of the crypto-provider (aContext command), and after that we can create hashes, sign data, encrypt - that is, do everything that this package was created for.
Basic commands:

The list of commands is actually much wider than is shown here. And it's not just that the application is constantly under development. When debugging and testing, it is very advantageous to write temporary function scripts that call several related procedures in succession, check the final result, or initialize the CSP with values ​​pre-defined in the code.
As a rule, these functions are rarely documented, but if you look at the application code - they are striking.

If you want to add new functions, first implement them in CacheCommon, test using the console application, then add them to the DLL, and finally, access them through iscapi.Signer. Functions in the dynamic library should be called the same as in the console application. Please note that the DLL code in the "ZFBEGIN ... ZFEND" block necessarily contains an enumeration of all the available procedures.

The set of functions that are currently implemented is small, but I did not set the task to do absolutely everything. Already, you can create a hash of data, sign them and verify the digital signature. At least there is a basis which, which will allow every interested developer to quickly set up a cryptographic provider, initialize its context and start using it. Adding new functions will not take much time, since it is performed by analogy with existing ones.
I want to invite all Caché-developers to use the results of my project, and also to take part in its development.

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


All Articles