首页
社区
课程
招聘
kcrypt--windows内核加密算法库
2023-8-4 18:23 11952

kcrypt--windows内核加密算法库

2023-8-4 18:23
11952

前言

  • kcrypt是一个用于windows内核和驱动编写的的加密库库中包含了常用的加解密算法
  • 数字摘要包括MD4-MD5 SHA1-SHA512 对称加密包括DES 3DES AES RC4 非对称加密包括 RSA 并且支持IV和不同的加密模式(块加密的CBC,ECB等等)
  • 源代码已开源:github链接 求各位大佬点个小星星
  • 源码较少涉及各种加密算法实现原理,仅是对微软CNG.lib的封装

如何使用

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

Demo

数字摘要

MD5 测试
9592

1
2
3
4
5
6
7
8
unsigned char plainText[2] = { 'a','\0' };
    //MD5 Test
    kcrypt::Md5Creator md5test;
    unsigned char md5Hash[20]{0};
    md5test.crypt(plainText,1, md5Hash, kcrypt::MD5::GetHashLength());
    char md5Str[40]{ 0 };
    kcrypt::hexToStr(md5Str, sizeof (md5Str), md5Hash, kcrypt::MD5::GetHashLength());
    printk("md5 crypt->%s\r\n", md5Str);

SHA1 Test
1920

1
2
3
4
5
6
7
    //SHA1 TEST
kcrypt::SHA1Creator sha1test;
unsigned char sha1Hash[20]{ 0 };
sha1test.crypt(plainText, 1, sha1Hash, kcrypt::SHA1::GetHashLength());
char sha1Str[40]{ 0 };
kcrypt::hexToStr(sha1Str, sizeof sha1Str, sha1Hash, kcrypt::SHA1::GetHashLength());
printk("sha1 crypt->%s\r\n", sha1Str);

对称加密

use default key(0)

DES ecb test

1
2
3
4
5
6
7
8
9
10
11
12
//DES test ecb
    unsigned char desPlainText[8] = { 0 };
    memset(desPlainText, 7, 8);
    kcrypt::DESCreator desTest;
    unsigned char desBuf[20] {0};
    auto result=desTest.encrypt(desPlainText, sizeof desPlainText, desBuf,sizeof desBuf);
    char desStr[20]{ 0 };
    kcrypt::hexToStr(desStr, sizeof desStr, desBuf, 8);
    printk("des ecb str->%s\r\n", desStr);
    //decrypt test
    unsigned char decryptCode[8]{ 0 };
    desTest.decrypt(decryptCode, sizeof decryptCode, desBuf, result);

4434

AES test

1111

非对称加密

RSA test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unsigned char rsaPlainText[1] = { 7 };
    unsigned char rsaBuf[500] = { 0 };
    char rsaStr[40]{ 0 };
    unsigned char rsadecryptCode[1] = { 0 };
    kcrypt::RSACreator rsatest;
    auto [pubkey, pubkeysize] = rsatest.getPubKey();
    auto [prikey, prikeysize] = rsatest.getPriKey();
#pragma  warning(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生成一个特定加密算法的提供者句柄,比如

1
2
3
4
5
6
7
NTSTATUS
WINAPI
BCryptOpenAlgorithmProvider(
    _Out_       BCRYPT_ALG_HANDLE   *phAlgorithm,
    _In_z_      LPCWSTR pszAlgId,
    _In_opt_z_  LPCWSTR pszImplementation,
    _In_        ULONG   dwFlags);

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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 提供程序将使用指定的哈希算法执行 基于哈希的消息身份验证代码 (HMAC) 算法。 此标志仅供哈希算法提供程序使用。
BCRYPT_PROV_DISPATCH 将提供程序加载到非分页内存池中。 如果此标志不存在,则提供程序将加载到分页内存池中。 指定此标志后,在释放所有依赖对象之前,不得关闭返回的句柄。注意 此标志仅在内核模式下受支持,并允许在 Dispatch 级别处理提供程序上的后续操作。 如果提供程序不支持在调度级别调用,则使用此标志打开时,它将返回错误。 Windows Server 2008 和 Windows Vista: 此标志仅受 Microsoft 算法提供程序支持,仅适用于 哈希算法对称密钥加密算法
BCRYPT_HASH_REUSABLE_FLAG 创建可重用哈希对象。 该对象可在调用 BCryptFinishHash 后立即用于新的哈希操作。 有关详细信息,请参阅 使用 CNG 创建哈希Windows Server 2008 R2、Windows 7、Windows Server 2008 和 Windows Vista: 不支持此标志。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 值,请调用 BCryptFinishHash 函数。 为指定的句柄调用 BCryptFinishHash 函数后,无法重复使用该句柄。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#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[i]);
        }
 
    }
 
#pragma warning(default : 4996)
}

对称加密

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

分组加密

其实对称加密使用起来比数字摘要更简短,在使用了使用 BCryptOpenAlgorithmProvider创建相关算法提供者句柄之后,直接调用BCryptGenerateSymmetricKey这个函数就行了;

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

1
2
3
4
5
6
7
8
9
NTSTATUS BCryptGenerateSymmetricKey(
  [in, out]       BCRYPT_ALG_HANDLE hAlgorithm,
  [out]           BCRYPT_KEY_HANDLE *phKey,
  [out, optional] PUCHAR            pbKeyObject,
  [in]            ULONG             cbKeyObject,
  [in]            PUCHAR            pbSecret,
  [in]            ULONG             cbSecret,
  [in]            ULONG             dwFlags
);

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

第三四个参数分别是一个是接受密钥对象的指针后面是密钥对象的大小,这个大小用 BCRYPT_OBJECT_LENGTH 属性获取(调用 BCryptGetProperty 函数);

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

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

一切就绪之后,即可调用

1
2
3
4
5
6
7
8
9
10
11
12
NTSTATUS BCryptEncrypt(
  [in, out]           BCRYPT_KEY_HANDLE hKey,
  [in]                PUCHAR            pbInput,
  [in]                ULONG             cbInput,
  [in, optional]      VOID              *pPaddingInfo,
  [in, out, optional] PUCHAR            pbIV,
  [in]                ULONG             cbIV,
  [out, optional]     PUCHAR            pbOutput,
  [in]                ULONG             cbOutput,
  [out]               ULONG             *pcbResult,
  [in]                ULONG             dwFlags
);

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

解密调用BCryptDecrypt;

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

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

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

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

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

Value 含义
BCRYPT_PAD_NONE 请勿使用任何填充。 不使用 pPaddingInfo 参数。 cbInput 参数中指定的纯文本的大小必须是算法块大小的倍数。
BCRYPT_PAD_OAEP 使用最佳非对称加密填充 (OAEP) 方案。 pPaddingInfo 参数是指向BCRYPT_OAEP_PADDING_INFO结构的指针。
BCRYPT_PAD_PKCS1 数据将填充一个随机数字,以舍入块大小。 不使用 pPaddingInfo 参数。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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"

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

1
2
3
4
5
6
7
if(!NT_SUCCESS(status = BCryptGetProperty(
                                     hAesAlg,
                                     BCRYPT_BLOCK_LENGTH,
                                     (PBYTE)&cbBlockLen,
                                     sizeof(DWORD),
                                     &cbData,
                                     0)))

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
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 to  generate 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[8]{ 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[8] = { 0 };
        return key;
    }
    static PUCHAR GetDefaultIV() {
        // DES block size is 8 bytes.
        static UCHAR iv[8] = { 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[24] = { 0 };
        return key;
    }
    static PUCHAR GetDefaultIV() {
        // 3DES block size is 8 bytes.
        static UCHAR iv[8] = { 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[16] = { 0 };
        return key;
    }
    static UCHAR* GetDefaultIV() {
        // AES block size is 16 bytes.
        static UCHAR iv[16] = { 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得到的结果不同。

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

1
2
3
4
5
6
7
8
9
10
11
12
unsigned char rc4PlainText[1] = { 7 };
unsigned char rc4Buf[1] = { 0 };
char rc4Str[10] = { 0 };
unsigned char rc4decryptCode[1] = { 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);

最终实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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[16] = { 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然后调用

1
2
3
4
5
6
7
status = BCryptImportKeyPair(hAlgo,
            NULL,
            BCRYPT_RSAPUBLIC_BLOB,
            &hKey,
            PublicKey,
            cbPublicKey,
            BCRYPT_NO_KEY_VALIDATION);

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

最终代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
#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)
}

参考


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞5
打赏
分享
最新回复 (8)
雪    币: 6124
活跃值: (4071)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
黑洛 1 2023-8-5 10:32
2
0
牛逼
雪    币: 682
活跃值: (2612)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tmflxw 2023-8-5 14:21
3
0
好人啊
雪    币: 219
活跃值: (161)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Rashomon 2023-8-8 10:21
4
0
好人一生平安
雪    币: 301
活跃值: (98)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ggggggaaaa 2023-8-11 09:39
5
0
学习啦大佬
雪    币: 1174
活跃值: (1177)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cmputer 2023-8-11 11:39
6
0
mark
雪    币: 1022
活跃值: (4050)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
兔先生 2023-8-12 00:50
7
0
非常感谢,赞一个。
雪    币: 4101
活跃值: (2574)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
linghaien 2023-8-12 15:42
8
0

非常感谢,赞一个。

最后于 2023-8-12 15:49 被linghaien编辑 ,原因:
雪    币: 1209
活跃值: (1089)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
风铃i 2023-8-13 00:31
9
0
代码越复杂可能潜在的攻击点越多,一直想要一个库能一个函数实现一个加解密的,从而方便整个函数forceinline然后vmp拉满
游客
登录 | 注册 方可回帖
返回