oxygen1a1 发表于 2023-8-4 18:06

kcrypt--windows内核加密算法库

本帖最后由 oxygen1a1 于 2023-8-4 18:08 编辑

# 介绍

- **kcrypt是一个用于windows内核和驱动编写的的加密库库中包含了常用的加解密算法**

- 数字摘要包括MD4-MD5 SHA1-SHA512 对称加密包括DES 3DES AES RC4 非对称加密包括 RSA 并且支持IV和不同的加密模式(块加密的CBC,ECB等等)
- 源代码已开源 (https://github.com/Oxygen1a1/kcrypt)

# 如何使用

- 把三个hpp文件引入,然后#include <Hasher.hpp> #include <SymCipher.hpp> #include <ASymCipher.hpp> 接着必须在连接器选项->引入->添加cng.lib这个库

```cpp
#include <Hasher.hpp>
#include <SymCipher.hpp>
#include <ASymCipher.hpp>
```
# Demo演示
## 数字摘要

```cpp
unsigned char plainText = { 'a','\0' };
      //MD5 Test
      kcrypt::Md5Creator md5test;
      unsigned char md5Hash{0};
      md5test.crypt(plainText,1, md5Hash, kcrypt::MD5::GetHashLength());
      char md5Str{ 0 };
      kcrypt::hexToStr(md5Str, sizeof (md5Str), md5Hash, kcrypt::MD5::GetHashLength());
      printk("md5 crypt->%s\r\n", md5Str);
```



```cpp
      //SHA1 TEST
      kcrypt::SHA1Creator sha1test;
      unsigned char sha1Hash{ 0 };
      sha1test.crypt(plainText, 1, sha1Hash, kcrypt::SHA1::GetHashLength());
      char sha1Str{ 0 };
      kcrypt::hexToStr(sha1Str, sizeof sha1Str, sha1Hash, kcrypt::SHA1::GetHashLength());
      printk("sha1 crypt->%s\r\n", sha1Str);
```



## 对称加密

use default key(0)

**DES ecb test**

```cpp
//DES test ecb
      unsigned char desPlainText = { 0 };
      memset(desPlainText, 7, 8);
      kcrypt::DESCreator desTest;
      unsigned char desBuf {0};
      auto result=desTest.encrypt(desPlainText, sizeof desPlainText, desBuf,sizeof desBuf);
      char desStr{ 0 };
      kcrypt::hexToStr(desStr, sizeof desStr, desBuf, 8);
      printk("des ecb str->%s\r\n", desStr);
      //decrypt test
      unsigned char decryptCode{ 0 };
      desTest.decrypt(decryptCode, sizeof decryptCode, desBuf, result);
```



**AES test**



## 非对称加密

RSA test

```cpp
unsigned char rsaPlainText = { 7 };
      unsigned char rsaBuf = { 0 };
      char rsaStr{ 0 };
      unsigned char rsadecryptCode = { 0 };
      kcrypt::RSACreator rsatest;
      auto = rsatest.getPubKey();
      auto = rsatest.getPriKey();
#pragmawarning(disable :4996)
      auto pubKeyStr = (char*)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'tmp');
      auto priKeyStr = (char*)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'tmp');
      memset(pubKeyStr, 0, PAGE_SIZE);
      memset(priKeyStr, 0, PAGE_SIZE);
      kcrypt::hexToStr(priKeyStr, PAGE_SIZE, prikey, prikeysize);
      kcrypt::hexToStr(pubKeyStr, PAGE_SIZE, pubkey, pubkeysize);
      printk("pub key ->%s\r\n pri key ->%s\r\n", pubKeyStr,priKeyStr);
      // You can leave arg5 and arg6 blank,
      // as this will use the default public key for encryption
      // and the private key for decryption.
      result=rsatest.encrypt(rsaPlainText, sizeof rsaPlainText, rsaBuf, sizeof rsaBuf);
      kcrypt::hexToStr(rsaStr, sizeof rsaStr, rsaBuf, result);
      printk("rsa str->%s\r\n", rsaStr);
      //rsatest.decrypt(rsadecryptCode, sizeof rsadecryptCode, rsaBuf, result);
      //and you can fill any pubkey or private key to encrypt and decrypt
      result = rsatest.encrypt(rsaPlainText, sizeof rsaPlainText,
                rsaBuf, sizeof rsaBuf, pubkey, pubkeysize);
      rsatest.decrypt(rsadecryptCode, sizeof rsadecryptCode, rsaBuf, result);
      ExFreePool(pubKeyStr);
```
# 实现原理
这个其实更多地用在文件系统的透明加密上面

我们知道,加密算法主要分为三种

- 数字摘要(MD5 SHAX)
- 对称加密(DES 3DES AES)
- 非对称加密(RSA)

数字摘要确定数据完整性,对称加密用于数据传输的时候加密,非对称加密用处主要是用于数字签名,证书认证等确认身份的;

在windows内核都实现了相关的函数进行加解密,库是`cng.lib`,而头文件是`bcrypt.h`

## 数字摘要

这里可以简单地封装个hasher类来进行简单调用这个库,因为使用cng.lib这个库方法是很固定的。

都是先`BCryptOpenAlgorithmProvider`生成一个**特定加密算法的提供者句柄**,比如

```cpp
NTSTATUS
WINAPI
BCryptOpenAlgorithmProvider(
    _Out_       BCRYPT_ALG_HANDLE   *phAlgorithm,
    _In_z_      LPCWSTR pszAlgId,
    _In_opt_z_LPCWSTR pszImplementation,
    _In_      ULONG   dwFlags);
```

返回的就是算法句柄,但是要`BCryptCloseAlgorithmProvider `来进行清理他;

这个pszAlgId其实就是加密算法的名字

```cpp
#define BCRYPT_RSA_ALGORITHM                  L"RSA"
#define BCRYPT_RSA_SIGN_ALGORITHM               L"RSA_SIGN"
#define BCRYPT_DH_ALGORITHM                     L"DH"
#define BCRYPT_DSA_ALGORITHM                  L"DSA"
#define BCRYPT_RC2_ALGORITHM                  L"RC2"
#define BCRYPT_RC4_ALGORITHM                  L"RC4"
#define BCRYPT_AES_ALGORITHM                  L"AES"
#define BCRYPT_DES_ALGORITHM                  L"DES"
#define BCRYPT_DESX_ALGORITHM                   L"DESX"
#define BCRYPT_3DES_ALGORITHM                   L"3DES"
#define BCRYPT_3DES_112_ALGORITHM               L"3DES_112"
#define BCRYPT_MD2_ALGORITHM                  L"MD2"
#define BCRYPT_MD4_ALGORITHM                  L"MD4"
#define BCRYPT_MD5_ALGORITHM                  L"MD5"
#define BCRYPT_SHA1_ALGORITHM                   L"SHA1"
#define BCRYPT_SHA256_ALGORITHM               L"SHA256"
#define BCRYPT_SHA384_ALGORITHM               L"SHA384"
```

windows已经预定义了,第三个第四个参数都是可选的,第三个是算法提供程序,第四个是flags

| alue                            | 含义                                                         |
| :------------------------------ | :----------------------------------------------------------- |
| **BCRYPT_ALG_HANDLE_HMAC_FLAG** | 提供程序将使用指定的哈希算法执行 [基于哈希的消息身份验证代码](https://learn.microsoft.com/zh-cn/windows/desktop/SecGloss/h-gly) (HMAC) 算法。 此标志仅供哈希算法提供程序使用。 |
| **BCRYPT_PROV_DISPATCH**      | 将提供程序加载到非分页内存池中。 如果此标志不存在,则提供程序将加载到分页内存池中。 指定此标志后,在释放所有依赖对象之前,不得关闭返回的句柄。**注意** 此标志仅在内核模式下受支持,并允许在 Dispatch 级别处理提供程序上的后续操作。 如果提供程序不支持在调度级别调用,则使用此标志打开时,它将返回错误。 **Windows Server 2008 和 Windows Vista:** 此标志仅受 Microsoft 算法提供程序支持,仅适用于 [哈希算法](https://learn.microsoft.com/zh-cn/windows/desktop/SecGloss/h-gly) 和 [对称密钥](https://learn.microsoft.com/zh-cn/windows/desktop/SecGloss/s-gly)[加密算法](https://learn.microsoft.com/zh-cn/windows/desktop/SecGloss/c-gly)。 |
| **BCRYPT_HASH_REUSABLE_FLAG**   | 创建可重用哈希对象。 该对象可在调用 (https://learn.microsoft.com/zh-cn/windows/desktop/api/bcrypt/nf-bcrypt-bcryptfinishhash) 后立即用于新的哈希操作。 有关详细信息,请参阅 [使用 CNG 创建哈希](https://learn.microsoft.com/zh-cn/windows/desktop/SecCNG/creating-a-hash-with-cng)。**Windows Server 2008 R2、Windows 7、Windows Server 2008 和 Windows Vista:** 不支持此标志。 |

获取完这个句柄之后,我们要获取这个提供者的相关信息,分配一个**加密对象**,注意一定是非分页内存

```cpp
status = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hashObjectSize, sizeof(ULONG), &resultSize, 0);
                if (!NT_SUCCESS(status))
                {
                        DbgPrint("BCryptGetProperty failed with %x\n", status);
                        goto Cleanup;
                }

                hashObject = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, hashObjectSize, 'md5h');
                if (hashObject == nullptr)
                {
                        DbgPrint("Memory allocation failed\n");
                        status = STATUS_INSUFFICIENT_RESOURCES;
                        goto Cleanup;
                }

                status = BCryptCreateHash(hAlgorithm, &hHash, hashObject, hashObjectSize, nullptr, 0, 0);
                if (!NT_SUCCESS(status))
                {
                        DbgPrint("BCryptCreateHash failed with %x\n", status);
                        goto Cleanup;
                }

                status = BCryptHashData(hHash, data, dataLength, 0);
                if (!NT_SUCCESS(status))
                {
                        DbgPrint("BCryptHashData failed with %x\n", status);
                        goto Cleanup;
                }

                status = BCryptFinishHash(hHash, hash, hashLength, 0);
                if (!NT_SUCCESS(status))
                {
                        DbgPrint("BCryptFinishHash failed with %x\n", status);
                        goto Cleanup;
                }
```

`BCryptGetProperty`获取MD5加密算法加密对象的大小,申请块非分页内存,然后使用`BCryptCreateHash`创建一个加密对象,一定要注意,假如是对称加密,生成密钥就要用`BCryptGenerateSymmetricKey`了;不同类型的加密算法所使用生成对象的函数是不一样的;

创建成功之后,直接使用`BCryptHashData`,参数填刚才获得的`hHash`即可。若要将多个缓冲区合并到哈希或 MAC 中,可以多次调用此函数,每次传递不同的缓冲区。 若要获取哈希或 MAC 值,请调用 (https://learn.microsoft.com/zh-cn/windows/desktop/api/bcrypt/nf-bcrypt-bcryptfinishhash) 函数。 为指定的句柄调用 **BCryptFinishHash** 函数后,无法重复使用该句柄。

这里封装个类+模板来更方便地优雅地使用hash;这里只支持MD4-MD5,SHA1-SHA256这些常用的数字摘要;

如果支持更多,可以自己添加相关的类,然后填入模板;

```cpp
#pragma once
#include <fltKernel.h>
#include <bcrypt.h>
#include <ntstrsafe.h>
namespace kcrypt {

      //由于这个项目用了crt库 不能用内核的了 只能这样
      using fnsprintf = int(*)(char* _DstBuf,const char* _Format, ...);
#pragma warning(disable : 4996)
#define hashprintk(...)do{DbgPrintEx(77,0,__VA_ARGS__);}while(0);

      template <typename Algorithm>
      class Hasher {
      public:
                Hasher();
                ~Hasher();
                bool crypt(PUCHAR data, ULONG size, PUCHAR hash, ULONG hash_len);
                Hasher(const Hasher&) = delete;
                Hasher(Hasher&&) = delete;
                Hasher& operator=(Hasher&) = delete;
      private:
                BCRYPT_ALG_HANDLE hAlgorithm_;//算法提供者句柄
                BCRYPT_HASH_HANDLE hHash_;//hash对象句柄
                unsigned long hashObjSize_;
                void* hashObj_;//哈希对象
      };

      template <typename Algorithm>
      Hasher<Algorithm>::Hasher() {

                auto status = STATUS_UNSUCCESSFUL;
                do {

                        status = BCryptOpenAlgorithmProvider(&hAlgorithm_, Algorithm::GetAlgorithmName(), nullptr, 0);
                        if (!NT_SUCCESS(status)) break;

                        unsigned long resultSize = 0;
                        //询问属性
                        status = BCryptGetProperty(hAlgorithm_, BCRYPT_OBJECT_LENGTH,
                              (PUCHAR)&hashObjSize_, sizeof hashObjSize_, &resultSize, 0);
                        if (!NT_SUCCESS(status)) {
                              break;
                        }

                        hashObj_ = ExAllocatePoolWithTag(NonPagedPool, hashObjSize_, 'hash');
                        if (hashObj_ == nullptr) break;
                        //创建hash对象
                        status = BCryptCreateHash(hAlgorithm_, &hHash_,(PUCHAR)hashObj_, hashObjSize_, 0, 0, 0);
                        if (!NT_SUCCESS(status)) {

                              break;
                        }


                        return;//成功
                } while (0);

                //走到这是不成功
                if (hashObj_) {
                        ExFreePool(hashObj_);
                }
                if (hAlgorithm_) {
                        BCryptCloseAlgorithmProvider(hAlgorithm_,0);
                }
                if (hHash_) {
                        BCryptDestroyHash(hHash_);
                }
               
                hashprintk("failed to ctor!\r\n");

      }
      template <typename Algorithm>
      Hasher<Algorithm>::~Hasher() {

                if (hashObj_) {
                        ExFreePool(hashObj_);
                        hashObj_ = 0;
                }
                if (hAlgorithm_) {
                        BCryptCloseAlgorithmProvider(hAlgorithm_,0);
                        hAlgorithm_ = 0;
                }
                if (hHash_) {
                        BCryptDestroyHash(hHash_);
                        hHash_ = 0;
                }
      }
      template <typename Algorithm>
      bool Hasher<Algorithm>::crypt(PUCHAR data, ULONG size, PUCHAR hash, ULONG hash_len) {
                if (hHash_ && hashObj_ && hAlgorithm_) {

                        //先验证hash的长度是否正确
                        //BCryptFinsihHash 很邪门,必须长度等同于hash值长度
                        if (Algorithm::GetHashLength() > hash_len)return false;
                        auto status = STATUS_UNSUCCESSFUL;

                        status = BCryptHashData(hHash_, data, size, 0);
                        if (!NT_SUCCESS(status)) return false;

                        status = BCryptFinishHash(hHash_, hash, Algorithm::GetHashLength(), 0);
                        if (!NT_SUCCESS(status)) return false;
                        
                        return true;
                }
               
                return false;
      }

      class SHA1 {
      public:
                static constexpr PCWSTR GetAlgorithmName() {
                        return BCRYPT_SHA1_ALGORITHM;
                }
                static constexpr ULONG GetHashLength() {
                        return 20;// SHA-1的哈希值长度为160位,即20字节
                }
      };

      class SHA256 {
      public:
                static constexpr PCWSTR GetAlgorithmName() {
                        return BCRYPT_SHA256_ALGORITHM;
                }
                static constexpr ULONG GetHashLength() {
                        return 32;// SHA-256的哈希值长度为256位,即32字节
                }
      };

      class SHA384 {
      public:
                static constexpr PCWSTR GetAlgorithmName() {
                        return BCRYPT_SHA384_ALGORITHM;
                }
                static constexpr ULONG GetHashLength() {
                        return 48;// SHA-384的哈希值长度为384位,即48字节
                }
      };

      class SHA512 {
      public:
                static constexpr PCWSTR GetAlgorithmName() {
                        return BCRYPT_SHA512_ALGORITHM;
                }
                static constexpr ULONG GetHashLength() {
                        return 64;// SHA-512的哈希值长度为512位,即64字节
                }
      };

      class MD4 {
      public:
                static constexpr PCWSTR GetAlgorithmName() {
                        return BCRYPT_MD4_ALGORITHM;
                }
                static constexpr ULONG GetHashLength() {
                        return 16;// MD4的哈希值长度为128位,即16字节
                }
      };

      class MD5 {
      public:
                static constexpr PCWSTR GetAlgorithmName() {

                        return BCRYPT_MD5_ALGORITHM;
                }
                static constexpr ULONG GetHashLength() {
                        return 16;
                }
      };

      using Md4Creator = Hasher<MD4>;
      using Md5Creator = Hasher<MD5>;
      using SHA1Creator = Hasher<SHA1>;
      using SHA256Creator = Hasher<SHA256>;
      using SHA384Creator = Hasher<SHA384>;
      using SHA512Creator = Hasher<SHA512>;

      //16进制转换成str
      void hexToStr(char* hexStr, ULONG len, PUCHAR hexArry, ULONG hexLen) {
                if (len < hexLen * 2) return;
                UNICODE_STRING uFuncName{ 0 };
                RtlInitUnicodeString(&uFuncName, L"sprintf");
                auto ___sprintf = (fnsprintf)MmGetSystemRoutineAddress(&uFuncName);
                for (unsigned int i = 0; i < hexLen; i++) {
                        //只能从ntoskrnl.exe导出了 因为被stdio这个坑填了
                        ___sprintf(hexStr + i * 2,"%02x", hexArry);
                }

      }

#pragma warning(default : 4996)
}

```

## 对称加密

常见的对称加密主要是DES 3DES AES这些,而对称加密有`密钥`这个概念,因此就得生成密钥了;

### 分组加密

其实对称加密使用起来比数字摘要更简短,在使用了使用 (https://learn.microsoft.com/zh-cn/windows/desktop/api/bcrypt/nf-bcrypt-bcryptopenalgorithmprovider)创建相关算法提供者句柄之后,直接调用`BCryptGenerateSymmetricKey`这个函数就行了;

里面可以指定密钥,也可以不填,如果不填会由windows默认生成;

```cpp
NTSTATUS BCryptGenerateSymmetricKey(
       BCRYPT_ALG_HANDLE hAlgorithm,
         BCRYPT_KEY_HANDLE *phKey,
PUCHAR            pbKeyObject,
            ULONG             cbKeyObject,
            PUCHAR            pbSecret,
            ULONG             cbSecret,
            ULONG             dwFlags
);
```

`phKey`这个就是传出的加密句柄了,后续`BCryptEncrypt`时候需要用到,不需要用次句柄就得调用`BCryptDestoryKey`销毁了;

第三四个参数分别是一个是接受密钥对象的指针后面是密钥对象的大小,这个大小用 **BCRYPT_OBJECT_LENGTH** 属性获取(调用 (https://learn.microsoft.com/zh-cn/windows/desktop/api/bcrypt/nf-bcrypt-bcryptgetproperty) 函数);

第三个第四个均可以填0,这个意思是则此函数会**分配并释放密钥对象的内存。**

第五个第六个参数就是密钥

- [**用于非对称提供商的BCryptGenerateKeyPair**](https://learn.microsoft.com/en-us/windows/desktop/api/Bcrypt/nf-bcrypt-bcryptgeneratekeypair)或[**BCryptImportKeyPair**](https://learn.microsoft.com/en-us/windows/desktop/api/Bcrypt/nf-bcrypt-bcryptimportkeypair)。
- 对称提供程序的[**BCryptGenerateSymmetricKey**](https://learn.microsoft.com/en-us/windows/desktop/api/Bcrypt/nf-bcrypt-bcryptgeneratesymmetrickey)或[**BCryptImportKey 。**](https://learn.microsoft.com/en-us/windows/desktop/api/Bcrypt/nf-bcrypt-bcryptimportkey)

一切就绪之后,即可调用

```cpp
NTSTATUS BCryptEncrypt(
         BCRYPT_KEY_HANDLE hKey,
                PUCHAR            pbInput,
                ULONG             cbInput,
      VOID            *pPaddingInfo,
PUCHAR            pbIV,
                ULONG             cbIV,
   PUCHAR            pbOutput,
                ULONG             cbOutput,
               ULONG             *pcbResult,
                ULONG             dwFlags
);
```

这个函数进行加密,这个函数非对称加密也是通用的,对称加密使用这个函数很多填0即可;

解密调用`BCryptDecrypt`;

**IV主要是给分组密码用的**,这个主要是为了**防止块加密对于相同的块加密的数据是相同情况的弊端**;

还需要注意的是,每次调用EnCrypt这个IV都会被更改,但是解密的时候和加密的时候必须用到的IV是相同的,而且一般IV大小和块的大小是相同的;因为可以考虑在类中写入`get`,`set`函数设置IV,来方便加解密

而flags可以填的是:如果密钥是对称密钥,则此值可以为零或以下值。

| Value                  | 含义                                                         |
| :----------------------- | :----------------------------------------------------------- |
| **BCRYPT_BLOCK_PADDING** | 允许加密算法将数据填充到下一个块大小。 如果未指定此标志, *则 cbInput* 参数中指定的纯文本的大小必须是算法块大小的倍数。可以通过调用 (https://learn.microsoft.com/zh-cn/windows/desktop/api/bcrypt/nf-bcrypt-bcryptgetproperty) 函数来获取该密钥 **的BCRYPT_BLOCK_LENGTH** 属性来获取块大小。 这将为算法提供块的大小。此标志不得与经过身份验证的加密模式一起使用, (AES-CCM 和 AES-GCM) 。 |

如果密钥是非对称密钥,则此值可以是下列值之一。

| Value                | 含义                                                         |
| :------------------- | :----------------------------------------------------------- |
| **BCRYPT_PAD_NONE**| 请勿使用任何填充。 不使用 *pPaddingInfo* 参数。 *cbInput* 参数中指定的纯文本的大小必须是算法块大小的倍数。 |
| **BCRYPT_PAD_OAEP**| 使用最佳非对称加密填充 (OAEP) 方案。 *pPaddingInfo* 参数是指向(https://learn.microsoft.com/zh-cn/windows/desktop/api/bcrypt/ns-bcrypt-bcrypt_oaep_padding_info)结构的指针。 |
| **BCRYPT_PAD_PKCS1** | 数据将填充一个随机数字,以舍入块大小。 不使用 *pPaddingInfo* 参数。 |

众所周知AES/DES有多种加密方式,ECB(电子密码本模式)、CBC(密码分组链接模式)、CFB(密码反馈模式;ECB不太安全,CBC啥的更安全,这样加密两段相同明文就不会密文一样了;

至于如何设置一个对称加密的加密模式,使用`BCryptSetProperty`即可;

```cpp
if(!NT_SUCCESS(status = BCryptSetProperty(
                              hAesAlg,
                              BCRYPT_CHAINING_MODE,
                              (PBYTE)BCRYPT_CHAIN_MODE_CBC,
                              sizeof(BCRYPT_CHAIN_MODE_CBC),
                              0)))
   // Property Strings
#define BCRYPT_CHAIN_MODE_NA      L"ChainingModeN/A"
#define BCRYPT_CHAIN_MODE_CBC       L"ChainingModeCBC"
#define BCRYPT_CHAIN_MODE_ECB       L"ChainingModeECB"
#define BCRYPT_CHAIN_MODE_CFB       L"ChainingModeCFB"
#define BCRYPT_CHAIN_MODE_CCM       L"ChainingModeCCM"
#define BCRYPT_CHAIN_MODE_GCM       L"ChainingModeGCM"
```

此外,对于对称加密有块的概念,所以可以询问块的大小,一般来说明文必须是按照块对齐的;

```cpp
   if(!NT_SUCCESS(status = BCryptGetProperty(
                                        hAesAlg,
                                        BCRYPT_BLOCK_LENGTH,
                                        (PBYTE)&cbBlockLen,
                                        sizeof(DWORD),
                                        &cbData,
                                        0)))
```

下面与数字摘要那个类似,封装一个类进行实现对称加密的加解密;

```cpp
      enum class Mode {
                ecb,
                cbc,
                cfb,
                ccm,
                gcm
      };

      template<typename Algorithm>
      class BlockCipher {
      public:
                //密钥 IV随机向量 还有加密模式
                BlockCipher(PUCHAR key=nullptr, Mode mod = Mode::ecb,PUCHAR iv=nullptr);
                ~BlockCipher();
                ULONG encrypt(PUCHAR data,ULONG dataSize,PUCHAR cryptData,ULONG cryptSize);
                ULONG decrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize);
                PUCHAR constexpr getcuriv() { return _iv; }
                PUCHAR constexpr getlastiv(){return _preIv}
                PUCHAR constexpr getkey() { return _key; }
      private:
                PUCHAR _key;//密钥
                BCRYPT_ALG_HANDLE _hAlg;//算法提供句柄
                BCRYPT_KEY_HANDLE _hKey;//对称加密的密钥对象句柄 必须用这个加解密
                PUCHAR _keyObj;
                Mode _mod;
                PUCHAR _iv;
                PUCHAR _preIv;
                ULONG _ivSize;
      };

      template<typename Algorithm>
      BlockCipher<Algorithm>::BlockCipher(PUCHAR key, Mode mod,PUCHAR iv):_key(key),_mod(mod),_iv(iv) {

                do {

                        //创建提供者
                        auto status = BCryptOpenAlgorithmProvider(&_hAlg,
                              Algorithm::GetAlgorithmName(), 0, 0);
                        if (!NT_SUCCESS(status)) break;


                        //询问密钥对象大小
                        ULONG keyObjSize = 0,result=0;
                        status = BCryptGetProperty(_hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObjSize, sizeof ULONG, &result, 0);
                        if (!NT_SUCCESS(status)) break;
                        _keyObj = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,keyObjSize, 'sym');
                        if (_keyObj == nullptr) break;


                        //设置IV随机向量 用于加密 如果是ECB方式则不需要
                        if (_key == nullptr) _key = Algorithm::GetDefaultKey();
                        else {
                              //需要申请
                              _key = (PUCHAR)ExAllocatePoolWithTag(PagedPool, Algorithm::GetBlockSize(), 'sym');
                              if (_key == nullptr) break;
                              memcpy(_key, key, Algorithm::GetBlockSize());
                        }

                        if (mod != Mode::ecb) {
                              //询问获取_ivSize
                              if (!NT_SUCCESS(BCryptGetProperty(
                                        _hAlg,
                                        BCRYPT_BLOCK_LENGTH,
                                        (PUCHAR)&_ivSize,
                                        sizeof(ULONG),
                                        &result,
                                        0))) break;
                              //如果不是ECB加密 需要随机向量
                              if (_iv == nullptr) {
                                        iv = Algorithm::GetDefaultIV();
                              }
                              
                              //申请内存 防止传入的是局部变量
                              _iv = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, _ivSize, 'sym');
                              _preIv= (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, _ivSize, 'sym');
                              if (_iv == nullptr || !_preIv) break;
                              memcpy(_iv, iv, _ivSize);
                              memset(_preIv, 0, _ivSize);
                              
                        }else { _iv = nullptr, _ivSize = 0,_preIv=nullptr; }
                        //设置加密方式
                        switch (_mod)
                        {
                        case kcrypt::Mode::ecb:
                              status = BCryptSetProperty(_hAlg, BCRYPT_CHAINING_MODE,
                                        (PUCHAR)BCRYPT_CHAIN_MODE_ECB,
                                        sizeof(BCRYPT_CHAIN_MODE_ECB),
                                        0);
                              break;
                        case kcrypt::Mode::cbc:
                              status = BCryptSetProperty(_hAlg, BCRYPT_CHAINING_MODE,
                                        (PUCHAR)BCRYPT_CHAIN_MODE_CBC,
                                        sizeof(BCRYPT_CHAIN_MODE_CBC),
                                        0);
                              break;
                        case kcrypt::Mode::cfb:
                              status = BCryptSetProperty(_hAlg, BCRYPT_CHAINING_MODE,
                                        (PUCHAR)BCRYPT_CHAIN_MODE_CFB,
                                        sizeof(BCRYPT_CHAIN_MODE_CFB),
                                        0);
                              break;
                        case kcrypt::Mode::ccm:
                              status = BCryptSetProperty(_hAlg, BCRYPT_CHAINING_MODE,
                                        (PUCHAR)BCRYPT_CHAIN_MODE_CCM,
                                        sizeof(BCRYPT_CHAIN_MODE_CCM),
                                        0);
                              break;
                        case kcrypt::Mode::gcm:
                              status = BCryptSetProperty(_hAlg, BCRYPT_CHAINING_MODE,
                                        (PUCHAR)BCRYPT_CHAIN_MODE_GCM,
                                        sizeof(BCRYPT_CHAIN_MODE_GCM),
                                        0);
                              break;
                        default:
                              status = STATUS_INVALID_PARAMETER;
                              break;
                        }

                        if (!NT_SUCCESS(status)) break;

                        //生成密钥句柄 用于后续加密
                        status = BCryptGenerateSymmetricKey(_hAlg, &_hKey, _keyObj,keyObjSize, _key, Algorithm::GetBlockSize(),0);
                        if (!NT_SUCCESS(status)) {
                              symprintk("failed togenerate key!\r\n");
                              break;
                        }

                        return;
                } while (0);

                //对称加密可以省略密钥对象 由windows自己管理
                if (_hAlg) {

                        BCryptCloseAlgorithmProvider(_hAlg,0);
                        _hAlg = 0;
                }
                if (_hKey) {

                        BCryptDestroyKey(_hKey);
                        _hKey = 0;
                }
                if (_keyObj) {
                        ExFreePool(_keyObj);
                        _keyObj = 0;
                }
                if (_iv != nullptr) {
                        ExFreePool(_iv);
                        _iv = 0;
                }
                if (_preIv) {
                        ExFreePool(_preIv);
                        _preIv = 0;
                }
                if (_key != Algorithm::GetDefaultKey() && key) {
                        ExFreePool(_key);
                        _key = nullptr;
                }
                symprintk("failed to ctor!\r\n");
               
      }

      template <typename Algorithm>
      BlockCipher<Algorithm>::~BlockCipher() {
                if (_keyObj) {
                        ExFreePool(_keyObj);
                        _keyObj = 0;
                }
                //对称加密可以省略密钥对象 由windows自己管理
                if (_hAlg) {

                        BCryptCloseAlgorithmProvider(_hAlg,0);
                        _hAlg = 0;
                }
                if (_hKey) {

                        BCryptDestroyKey(_hKey);
                        _hKey = 0;
                }
                if (_iv != nullptr) {
                        ExFreePool(_iv);
                        _iv = 0;
                }
                if (_preIv) {
                        ExFreePool(_preIv);
                        _preIv = 0;
                }
                if (_key != Algorithm::GetDefaultKey() && _key) {
                        ExFreePool(_key);
                        _key = nullptr;
                }
      }

      template <typename Algorithm>
      ULONG BlockCipher<Algorithm>::encrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize) {
                if (!_hKey || !_hAlg ||!_keyObj) {
                        return 0;
                }
                ULONG result = 0;
                NTSTATUS status = STATUS_UNSUCCESSFUL;
                unsigned char saveIv{ 0 };
                if(_iv!=nullptr)
                        memcpy(saveIv, _iv, 8);
                //自动填充到一个块
                if (!NT_SUCCESS(status=BCryptEncrypt(_hKey, data, dataSize, 0, _iv,_ivSize,
                        cryptData, cryptSize, &result, 0))) {

                        symprintk("fialed to encrypt,errcode->08%x\r\n", status);
                        return 0;
                }
                else {
                        if (_iv != nullptr)
                              memcpy(_preIv, saveIv, 8);
                        return result;
                }
      }

      template<typename Algorithm>
      ULONG BlockCipher<Algorithm>::decrypt(PUCHAR data, ULONG dataSize,PUCHAR cryptData, ULONG cryptSize) {

                //解密
                if (!_hKey || !_hAlg || !_keyObj) {
                        return 0;
                }
                ULONG result = 0;
                NTSTATUS status = STATUS_UNSUCCESSFUL;
                if (!NT_SUCCESS(status = BCryptDecrypt(_hKey,cryptData,cryptSize,0,_preIv
                        ,_ivSize,data,dataSize,&result,0))) {

                        symprintk("failed to decrypt,errcode->08%x\r\n", status);
                        return 0;
                }
                else return result;

      }

      class DES {

      public:
                static constexpr LPCWSTR GetAlgorithmName() {
                        return BCRYPT_DES_ALGORITHM;
                }
                static constexpr ULONG GetBlockSize() {
               
                        return 8;
                }
                static PUCHAR GetDefaultKey(){
                        //DES的密钥是7字节
                        static UCHAR key = { 0 };
                        return key;
                }
                static PUCHAR GetDefaultIV() {
                        // DES block size is 8 bytes.
                        static UCHAR iv = { 0 };
                        return iv;
                }
      };

      class TripleDES {
      public:
                static constexpr PCWSTR GetAlgorithmName() {
                        return BCRYPT_3DES_ALGORITHM;
                }
                static constexpr ULONG GetBlockSize() {
                        return 24;
                }
                static UCHAR* GetDefaultKey() {
                        // 3DES supports key sizes of 21 bytes.
                        static UCHAR key = { 0 };
                        return key;
                }
                static PUCHAR GetDefaultIV() {
                        // 3DES block size is 8 bytes.
                        static UCHAR iv = { 0 };
                        return iv;
                }
      };

      class AES {
      public:
                static constexpr PCWSTR GetAlgorithmName() {
                        return BCRYPT_AES_ALGORITHM;
                }
                static constexpr ULONG GetBlockSize() {
                        return 16;
                }
                static UCHAR* GetDefaultKey() {
                        // AES supports key sizes of 16, 24, or 32 bytes.
                        static UCHAR key = { 0 };
                        return key;
                }
                static UCHAR* GetDefaultIV() {
                        // AES block size is 16 bytes.
                        static UCHAR iv = { 0 };
                        return iv;
                }
      };
      
      using DESCreator = BlockCipher<DES>;
      using TripleDESCreator = BlockCipher<TripleDES>;
      using AESCreator = BlockCipher<AES>;
```

上面还支持了自定义IV,自定义密钥和自定义加密方式(ECB CBC CFB等);

### 流加密

这里用RC4做例子,流加密和块加密最大的不同就是**流加密对于明文长度是不固定的;**

流加密的加密原理是根据密钥(伪随机)生成一个密钥流,然后依次把伪密钥流和明文进行加密运算

举个例子,假设你的密钥流是`1234567890`,你第一次调用`encrypt`使用了前三个数字`123`,那么第二次调用`encrypt`就会使用下三个数字`456`,依此类推。这就是导致连续两次调用`encrypt`得到的结果不同。

同时,由于密钥流的不同,解密的时候需要重新创建一个对象,而且密钥必须是相同的;这样保证初始化的伪随机的密钥流是相同的;从而可以完成解密

```cpp
      unsigned char rc4PlainText = { 7 };
      unsigned char rc4Buf = { 0 };
      char rc4Str = { 0 };
      unsigned char rc4decryptCode = { 0 };
      kcrypt::RC4Creator rc4test;
      result=rc4test.encrypt(rc4PlainText, sizeof rc4PlainText, rc4Buf, sizeof rc4Buf);
      kcrypt::hexToStr(rc4Str, sizeof rc4Str, rc4Buf, result);
      printk("rc4 str->%s\r\n", rc4Str);
      //RC4 is a stream cipher,so we need to create a new rc4Creator instance
      //so that we can confirm that stream key is same as we encrypt
      kcrypt::RC4Creator rc4Decrypt;
      rc4Decrypt.decrypt(rc4decryptCode, sizeof rc4decryptCode, rc4Buf, result);
```

最终实现如下

```cpp
template<typename Algorithm>
      class StreamCipher {
      public:
                StreamCipher(PUCHAR key=nullptr);
                ~StreamCipher();
                ULONG encrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize);
                ULONG decrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize);

      private:
                PUCHAR _key;
                BCRYPT_ALG_HANDLE _hAlg;
                BCRYPT_KEY_HANDLE _hKey;
                PUCHAR _keyObj;

      };

      template<typename Algorithm>
      StreamCipher<Algorithm>::StreamCipher(PUCHAR key):_key(key) {

                do {

                        NTSTATUS status = 0;
                        //process key
                        if (_key == nullptr) key = Algorithm::GetDefaultKey();
                        _key = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, Algorithm::GetKeySize(), 'sym');
                        if (_key == nullptr) {
                              symprintk("failed to create key space errcode->%08x\r\n", status);
                              break;
                        }
                        memcpy(_key, key, Algorithm::GetKeySize());
                        //open provider
                        if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&_hAlg,
                              Algorithm::GetAlgorithmName(), 0, 0))) {

                              symprintk("failed to create algorithm provider! errcode->%08x\r\n", status);
                              break;
                        }
                        ULONG objSize = 0,result=0;
                        //get property
                        if (!NT_SUCCESS(status = BCryptGetProperty(_hAlg, BCRYPT_OBJECT_LENGTH,
                              (PUCHAR)&objSize, sizeof ULONG, &result, 0))) {
                              symprintk("failed to get object size! errcode->%08x\r\n", status);
                              break;
                        }

                        _keyObj = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, objSize, 'sym');
                        if (_keyObj == nullptr) {
                              symprintk("failed to create errcode->%08x\r\n", status);
                              break;
                        }
                        
                        //generate key handle for encrypt or decrypt
                        if (!NT_SUCCESS(status = BCryptGenerateSymmetricKey(_hAlg, &_hKey,
                              _keyObj, objSize, _key,
                              Algorithm::GetKeySize(), 0
                        ))) {
                              symprintk("failed to generate key! errcode->%08x\r\n",status);
                              break;
                        }
                        return;
                } while (0);
               
                if (_keyObj) {

                        ExFreePool(_keyObj);
                        _key = 0;
                }
                if (_hAlg) {
                        BCryptCloseAlgorithmProvider(_hAlg, 0);
                        _hAlg = 0;
                }
                if (_hKey) {
                        BCryptDestroyKey(_hKey);
                        _hKey = 0;
                }
                if (_key) {
                        ExFreePool(_key);
                        _key = 0;
                }
               
      }
      template<typename Algorithm>
      StreamCipher<Algorithm>::~StreamCipher() {

                if (_keyObj) {

                        ExFreePool(_keyObj);
                        _key = 0;
                }
                if (_hAlg) {
                        BCryptCloseAlgorithmProvider(_hAlg, 0);
                        _hAlg = 0;
                }
                if (_hKey) {
                        BCryptDestroyKey(_hKey);
                        _hKey = 0;
                }
                if (_key) {
                        ExFreePool(_key);
                        _key = 0;
                }

      }
      template<typename Algorithm>
      ULONG StreamCipher<Algorithm>::encrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize) {
                if (!_hKey) return 0;
               
                NTSTATUS status = 0;
                ULONG result = 0;
                if (!NT_SUCCESS(status = BCryptEncrypt(_hKey, data, dataSize,
                        0, 0, 0, cryptData, cryptSize, &result, 0))) {
                        symprintk("failed to encrypt! errorcode->08%X\r\n",status);
                        return 0;
                }
                else return result;

      }
      template<typename Algorithm>
      ULONG StreamCipher<Algorithm>::decrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize) {
                if (!_hKey) return 0;

                NTSTATUS status = 0;
                ULONG result = 0;
                if (!NT_SUCCESS(status = BCryptDecrypt(_hKey, cryptData, cryptSize,
                        0, 0, 0, data, dataSize, &result, 0))) {
                        symprintk("failed to decrypt! errorcode->08%X\r\n", status);
                        return 0;
                }
                else return result;
                        
      }
      class RC4 {
      public:
                static constexpr LPCWSTR GetAlgorithmName() {
                        return BCRYPT_RC4_ALGORITHM;
                }
                static constexpr ULONG GetKeySize() {
                        return 16; // 128-bit key.
                }
                static PUCHAR GetDefaultKey() {
                        static unsigned char key = { 0 };
                        return key;
                }
      };
      
      using RC4Creator = StreamCipher<RC4>;
```

## 非对称加密

我们知道,非对称加密(也叫)公钥加密,也分为很多种;但是作用不同

- RSA

既可以数字签名,也可以用于加密数据

- DSA

仅用于数字签名

- 圆锥曲线加密

也是一种非对称加密,效率高

windows的cng.sys也提供了上述算法,同时,由于非对称加密有私钥和公钥的概念;

因此,加解密是需要调用`BCryptExportKey`来获取私钥,公钥长度和真实数值的;此外,因为公钥加密的特殊性,只有密钥的位数是固定的,密钥本身应当是随机生成的;

目前cng.sys所支持的非对称加密公用这些;

**分别是DH(密钥交换协议,不是用于加密或签名的,而是用于在不安全的网络环境中创建一个只有两方知道的共享密钥,具体算法不知道,猜测可能是像大数定理那样质数分解)**

**RSA,DSA,基于椭圆曲线的DSA,基于椭圆取消的DH;**

> RSA就很熟悉了,它本质上是用n=pq,pq都是质数,然后计算;
>
> 选取e(1<e<φ(n)),e 与φ(n)互素,计算d,使得ed=1(modφ(n))
>
> 公钥为(n,e),私钥为(n,d)
>
> 加密:c=m^e^mod n
>
> 解密:e=m^d^mod n

| Algorithm identifier            | Meaning                                                      |
| :------------------------------ | :----------------------------------------------------------- |
| **BCRYPT_DH_ALGORITHM**         | The key size must be greater than or equal to 512 bits, less than or equal to 4096 bits, and must be a multiple of 64. |
| **BCRYPT_DSA_ALGORITHM**      | Prior to Windows 8, the key size must be greater than or equal to 512 bits, less than or equal to 1024 bits, and must be a multiple of 64.Beginning with Windows 8, the key size must be greater than or equal to 512 bits, less than or equal to 3072 bits, and must be a multiple of 64. Processing for key sizes less than or equal to 1024 bits adheres to FIPS 186-2. Processing for key sizes greater than 1024 and less than or equal to 3072 adheres to FIPS 186-3. |
| **BCRYPT_ECDH_P256_ALGORITHM**| The key size must be 256 bits.                               |
| **BCRYPT_ECDH_P384_ALGORITHM**| The key size must be 384 bits.                               |
| **BCRYPT_ECDH_P521_ALGORITHM**| The key size must be 521 bits.                               |
| **BCRYPT_ECDSA_P256_ALGORITHM** | The key size must be 256 bits.                               |
| **BCRYPT_ECDSA_P384_ALGORITHM** | The key size must be 384 bits.                               |
| **BCRYPT_ECDSA_P521_ALGORITHM** | The key size must be 521 bits.                               |
| **BCRYPT_RSA_ALGORITHM**      | The key size must be greater than or equal to 512 bits, less than or equal to 16384 bits, and must be a multiple of 64. |

这里只使用RSA作为加解密的简单演示

注意,首先`BCryptGenerateKeyPair`调用这个是生成一对公钥和私钥;此外,还应当提供一个接口,RSA加密大部分是只有公钥没有私钥,解密也是只用到私钥;

可以在类中直接简单地使用加解密函数,但是无法自定义公钥和私钥那么也没有啥意义了;所以最好写两个构造函数,分别是只有私钥和只有公钥的情况;分别对应加解密;

而这样的还是先`BCryptOpenAlgorithmProvider`然后调用

```cpp
status = BCryptImportKeyPair(hAlgo,
            NULL,
            BCRYPT_RSAPUBLIC_BLOB,
            &hKey,
            PublicKey,
            cbPublicKey,
            BCRYPT_NO_KEY_VALIDATION);

```

来把公钥变成一个**密钥句柄**,只有这样才能调用`BCryptEncypt`;私钥用于解密也是同理;

最终代码如下

```cpp
#pragma once
#include <fltkernel.h>
#include <bcrypt.h>

namespace kcrypt {
#pragma warning(disable :4996)
#define asymprink(...)do{DbgPrintEx(77,0,__VA_ARGS__);}while(0)
      struct keyInfo {
                PUCHAR key;
                ULONG keySize;
      };

      
      template<typename Algorithm>
      class ASymCipher {
               
      public:
               
                ASymCipher(ULONG keySize=512);
                ~ASymCipher();
                ULONG encrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize,PUCHAR pubKey=nullptr,ULONG pubSize=0);
                ULONG decrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize,PUCHAR priKey=nullptr,ULONG priSize=0);
                keyInfo constexpr getPriKey() { return { _priKey,_priSize}; }
                keyInfo constexpr getPubKey() { return { _pubKey ,_pubSize}; }


                ASymCipher& operator=(ASymCipher&) = delete;
                ASymCipher& operator=(ASymCipher&&) = delete;
                ASymCipher(ASymCipher&) = delete;
      private:
                BCRYPT_ALG_HANDLE _hAlg;//create when open alg provider
                BCRYPT_KEY_HANDLE _hKey;//use for encrypt and decrypt
                PUCHAR _priKey;
                PUCHAR _pubKey;
                ULONG _pubSize;
                ULONG _priSize;
      private:
                bool rsaCheck(ULONG cbKeySize, ULONG cbData, BOOLEAN bEncrypt);
               
      };

      
      template<typename Algorithm>
      inline ASymCipher<Algorithm>::ASymCipher(ULONG keySize)
      {
                do {
                        //check key size correct
                        if (keySize != 512 && keySize != 1024 && keySize != 2048 && keySize != 4096) {
                              asymprink("key len error!\r\n");
                              break;
                        }
                        

                        NTSTATUS status = 0;
                        if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&_hAlg,
                              Algorithm::GetAlgorithmName(), 0, 0))) {
                              asymprink("failed to open alg provider! errcode->%08x\r\n",status);
                              break;
                        }

                        //generate key pair and must call BCryptFinalizeKeyPair
                        if (!NT_SUCCESS(status = BCryptGenerateKeyPair(_hAlg, &_hKey, keySize, 0))) {
                              asymprink("failed to generate key pair! errcode->08%x\r\n", status);
                              break;
                        }

                        //if call BCryptFinalizeKeyPair we can not call BCryptSetProperty anymore
                        if (!NT_SUCCESS(status = BCryptFinalizeKeyPair(_hKey, 0))) {
                              asymprink("failed to finalize key pair! errcode->%08x\r\n", status);
                              break;
                        }

                        //get public key and private key size
                        if (!NT_SUCCESS(status = BCryptExportKey(_hKey, 0, BCRYPT_RSAPUBLIC_BLOB, 0, 0, &_pubSize, 0))) {

                              asymprink("failed to get pub key size! errcode->%08x\r\n", status);
                              break;
                        }
                        if (!NT_SUCCESS(status = BCryptExportKey(_hKey, 0, BCRYPT_RSAPRIVATE_BLOB, 0, 0, &_priSize, 0))) {

                              asymprink("failed to get private key size! errcode->%08x\r\n", status);
                              break;
                        }
                        _pubKey = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, _pubSize, 'asym');
                        _priKey= (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, _priSize, 'asym');
                        if (!_priKey || !_pubKey) {
                              asymprink("failed to alloc mem for key! errcode->%08x\r\n", status);
                              break;
                        }
                        //get public and private key
                        if (!NT_SUCCESS(status = BCryptExportKey(_hKey, 0, BCRYPT_RSAPUBLIC_BLOB, _pubKey, _pubSize, &_pubSize, 0))) {

                              asymprink("failed to get pub key! errcode->%08x\r\n", status);
                              break;
                        }
                        if (!NT_SUCCESS(status = BCryptExportKey(_hKey, 0, BCRYPT_RSAPRIVATE_BLOB, _priKey, _priSize, &_priSize, 0))) {

                              asymprink("failed to get private key! errcode->%08x\r\n", status);
                              break;
                        }

                        return;

                } while (0);

                //fault or err
                if (_hKey) {

                        BCryptDestroyKey(_hKey);
                        _hKey = 0;
                }
                if (_hAlg) {
                        BCryptCloseAlgorithmProvider(_hAlg,0);
                        _hAlg = 0;
                }
                if (_priKey) {
                        ExFreePool(_priKey);
                        _priKey = 0;
                }
                if (_pubKey) {
                        ExFreePool(_pubKey);
                        _pubKey = 0;

                }
      }

      template<typename Algorithm>
      inline ASymCipher<Algorithm>::~ASymCipher()
      {
                if (_hKey) {

                        BCryptDestroyKey(_hKey);
                        _hKey = 0;
                }
                if (_hAlg) {
                        BCryptCloseAlgorithmProvider(_hAlg,0);
                        _hAlg = 0;
                }
                if (_priKey) {
                        ExFreePool(_priKey);
                        _priKey = 0;
                }
                if (_pubKey) {
                        ExFreePool(_pubKey);
                        _pubKey = 0;

                }

      }

      //if arg5 isn't nullptr means that we need to import a new key handle to encrypt
      template<typename Algorithm>
      inline ULONG ASymCipher<Algorithm>::encrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize, PUCHAR pubKey,ULONG pubSize)
      {
                if (!_hKey) return 0;
                if (!rsaCheck(pubSize ? pubSize :_pubSize, dataSize, true)) {
                        asymprink("key size or data size err!\r\n");
                        return 0;
                }
                ULONG result=0;
                NTSTATUS status = 0;
                if (pubKey == nullptr) {
                        //using pkcs1 padding and defualt public key
                        if (!NT_SUCCESS(status = BCryptEncrypt(_hKey, data, dataSize, 0, 0, 0, cryptData,
                              cryptSize, &result, BCRYPT_PAD_PKCS1))) {
                              asymprink("failed to encrypt! errcode->%x\r\n", status);
                              return 0;
                        }
                        else return result;

                }
                else {
                        //we need open new hAlg and create new key handle(public key)
                        BCRYPT_ALG_HANDLE hAlg = nullptr;
                        BCRYPT_KEY_HANDLE hKey = nullptr;
                        do {
                              if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hAlg,
                                        Algorithm::GetAlgorithmName(), 0, 0))) {
                                        asymprink("failed to open provider errcode->%08x\r\n", status);
                                        break;
                              }

                              __debugbreak();
                              //import key handle
                              if (!NT_SUCCESS(status = BCryptImportKeyPair(hAlg
                                        , nullptr, BCRYPT_RSAPUBLIC_BLOB
                                        , &hKey, pubKey, pubSize
                                        , BCRYPT_NO_KEY_VALIDATION))) {
                                        asymprink("failed to get key handle errcode->%x\r\n", status);
                                        break;
                              }
                              
                              //using pkcs1 padding and defualt public key
                              if (!NT_SUCCESS(status = BCryptEncrypt(hKey, data, dataSize, 0, 0, 0, cryptData,
                                        cryptSize, &result, BCRYPT_PAD_PKCS1))) {
                                        asymprink("failed to encrypt! errcode->%x\r\n", status);
                                        result = 0;
                                        break;
                              }
                              else break;
                              
                        } while (0);
                        
                        if (hKey) {
                              BCryptDestroyKey(hKey);
                        }
                        if (hAlg) {
                              BCryptCloseAlgorithmProvider(hAlg, 0);
                        }
                        return result;

                }

      }

      //if arg5 isn't nullptr means that we need to import a new key handle to decrypt
      template<typename Algorithm>
      inline ULONG ASymCipher<Algorithm>::decrypt(PUCHAR data, ULONG dataSize, PUCHAR cryptData, ULONG cryptSize, PUCHAR priKey,ULONG priSize)
      {
                if (!_hKey) return 0;
                if (!rsaCheck(priSize ? priSize :_priSize, dataSize, false)) {
                        asymprink("key size or data size err!\r\n");
                        return 0;
                }
                ULONG result = 0;
                NTSTATUS status = 0;
                //using pkcs1 padding
                if (priKey == nullptr) {
                        if (!NT_SUCCESS(status = BCryptDecrypt(_hKey, cryptData, cryptSize, 0, 0, 0, data,
                              dataSize, &result, BCRYPT_PAD_PKCS1))) {
                              asymprink("failed to decrypt! errcode->%x\r\n", status);
                              return 0;
                        }
                        else return result;

                }
                else {

                        //we need open new hAlg and create new key handle(public key)
                        BCRYPT_ALG_HANDLE hAlg = nullptr;
                        BCRYPT_KEY_HANDLE hKey = nullptr;
                        do {
                              if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hAlg,
                                        Algorithm::GetAlgorithmName(), 0, 0))) {
                                        asymprink("failed to open provider errcode->%08x\r\n", status);
                                        break;
                              }

                              //import key handle
                              if (!NT_SUCCESS(status = BCryptImportKeyPair(hAlg
                                        , nullptr, BCRYPT_RSAPRIVATE_BLOB
                                        , &hKey, priKey, priSize
                                        , BCRYPT_NO_KEY_VALIDATION))) {
                                        asymprink("failed to get key handle errcode->%08x\r\n", status);
                                        break;
                              }

                              //using pkcs1 padding and defualt public key
                              if (!NT_SUCCESS(status = BCryptDecrypt(hKey, cryptData, cryptSize, 0, 0, 0, data,
                                        dataSize, &result, BCRYPT_PAD_PKCS1))) {
                                        asymprink("failed to decrypt! errcode->%x\r\n", status);
                                        result = 0;
                                        break;
                              }
                              else break;

                        } while (0);

                        if (hKey) {
                              BCryptDestroyKey(hKey);
                        }
                        if (hAlg) {
                              BCryptCloseAlgorithmProvider(hAlg, 0);
                        }
                        return result;

                }

      }


      /*
      the length of the content that can be encrypted
      depends on the bis size of the key.
      For 512bit key:
      Public key length: 91, Private key: 155
      For 1024bit:
      Public key: 155, Private key: 283
      For 2048bit:
      Public key: 283, Private key: 539
      For 4096bit:
      Public key: 539, Private key: 1051
      */
      template<typename Algorithm>
      inline bool ASymCipher<Algorithm>::rsaCheck(ULONG cbKeySize, ULONG cbData, BOOLEAN bEncrypt)
      {
                if (bEncrypt)
                {
                        switch (cbKeySize)
                        {
                        case 91: // 512bit
                              if (cbData > 64)
                                        return FALSE;
                              break;
                        case 155: // 1024bit
                              if (cbData > 128)
                                        return FALSE;
                              break;
                        case 283: // 2048bit
                              if (cbData > 256)
                                        return FALSE;
                              break;
                        case 539: // 4096bit
                              if (cbData > 512)
                                        return FALSE;
                              break;
                        default:
                              return FALSE;
                              break;
                        }
                        /*if (cbKeySize - cbData > 27)
                              return TRUE;
                        else
                              return FALSE;*/
                        return TRUE;
                }
                else
                {
                        switch (cbKeySize)
                        {
                        case 155: // 512bit
                        case 283: // 1024bit
                        case 539: // 2048bit
                        case 1051: // 4096bit
                              return TRUE;
                              break;
                        default:
                              return FALSE;
                              break;
                        }
                }
      }


      
      class RSA {
      public:
                static constexpr LPCWSTR GetAlgorithmName() {
                        return BCRYPT_RSA_ALGORITHM;
                }

      };

      class DSA {
      public:
                static constexpr LPCWSTR GetAlgorithmName() {
                        return BCRYPT_DSA_ALGORITHM;
                }

      };

      using RSACreator = ASymCipher<RSA>;
#pragma warning(default :4996)
}
```
# 参考
https://learn.microsoft.com/
https://zh.wikipedia.org/wiki/%E5%88%86%E7%BB%84%E5%AF%86%E7%A0%81%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F
chatgpt

dyc66666 发表于 2023-8-4 21:11

很秀,但看不懂

zhurilinyu 发表于 2023-8-4 21:38

好详细,谢谢分享

laustar 发表于 2023-8-4 23:01

学习一下。

ZYLB2023 发表于 2023-8-4 23:07

努力加油

qqcs6 发表于 2023-8-5 00:25

感谢分享!

xiaobinwang 发表于 2023-8-5 01:52

感谢分享!!

youxiaxy 发表于 2023-8-5 08:45

写的好详细。太感谢了

zico123 发表于 2023-8-7 11:16

好东西学习了   感谢分享

tl;dr 发表于 2023-8-7 18:39

页: [1] 2
查看完整版本: kcrypt--windows内核加密算法库