通过CryptExportKey( hKey, NULL, PUBLICKEYBLOB,0, NULL, &dwBlobLen)

函数导出的公钥信息如下:
06 02 00 00 00 A4 00 00 52 53
41 31 00 04 00 00 01 00 01 00
CD 85 DA 77 E8 CE 2D 25 84 C5
6F 4F E8 0C BA 30 12 1B 4E 73
49 5D 37 9D 9A BD 21 41 F6 36
C3 84 76 EA 95 9F AD D5 7A E1
27 94 32 6C DA 2E F9 E1 4E 4B
D3 4A AE 1A F9 C2 19 1E 66 DE
59 CB D1 D6 BF 4F 2B 68 E1 35
FD 8E 0D 2E 26 3A 29 09 10 FB
56 73 00 CB 22 9E AE 4C 4B 0E
2E 20 F0 1E 5C 48 47 D7 A2 2A
C3 B2 B2 A8 90 BF AF 10 77 31
7F 0F 62 A2 F5 5B 48 1A B1 A9
51 C6 2F B6 1C 72 17 C5
 
 
其中红色部分是key BLOBHeader
紫色部分表示公钥magic
黄色部分表示公钥模长
浅绿色表示公钥指数
蓝色部分表示公钥的模
 
 
 
接下来主要分析BLOBHeader部分
PUBLICKEYSTRUC 结构,也即BLOBHEADER结构,表明了一个key的算法及BLOB类型
结构体如下:
typedef struct _PUBLICKEYSTRUC{
    BYTE     bType;
    BYTE     bVersion;
    WORD  reserved;
    ALG_ID aiKeyAlg;
}BLOBHEARER,
PUBLICKEYSTURC;
 
 
结构成员解释如下
(1) bType------(1个字节)表示 key的BLOB类型
            列表如下:
 
(2)bVersion------(2个字节)表示BLOB格式的版本信息
            目前该值为CUR_BLOB_VERSION(0x02)
 
(3)reserved------保留将来用,目前设置为0
 
(4)aiKeyAlg------(4个字节)ALG_ID结构,包含该key的算法信息
            注:并不是所有的算法标志都是有效的BLOB类型。例如,一个RC4的密钥,它是交换密钥,并不能被PUBLICKEYBLOB导出
 
 
 
公钥结构体如下:
typedef struct _RSAPUBKEY{
    DWORD    magic;
    DWORD    bitlen;
    DWORD    pubexp;
}RSAPUBKEY;
 
 
结构体成员如下
(1)magic--------Set to RSA1 (0x31415352) for public keys and to RSA2 (0x32415352) for private keys.
(2)bitlen--------公钥模长(比特长度)
(3)pubexp------公钥指数
 
 
再后边就是公钥的模了,长度为公钥的模长。
由于csp的数据排列和其他不用,因此需要将获取到的后边的模作一次颠倒处理。既将最后一个字节和第一个字节对调,依次类推。
 
 
 
 
 
通过函数

CryptExportKey( hKey, NULL, PRIVATEKEYBLOB,0, pbKeyBlob, &dwBlobLen)
导出的密钥对信息如下
注:在产生该密钥对时,需要
CryptGenKey(hCryptProv, AT_KEYEXCHANGE,CRYPT_EXPORTABLE,&hKey)
这个CRYPT_EXPORTABLE必须要有,否则该密钥对会因为权限问题导出失败
 
07 02 00 00 00 A4 00 00 52 53
41 32 00 04 00 00 01 00 01 00
CD DD 41 C4 99 BC E2 0E BC 4C
09 CC 7A D9 62 7F 29 24 25 66
E5 4F 56 42 13 30 96 9E 1B 42
1B 99 BF 24 66 B8 CB DD 4B E7
02 2A 67 1E C5 54 0A 83 88 F8
80 5C 0A E9 FA 19 24 41 4F 9F
B4 A7 1A 52 36 7D D1 44 1A 17
DF F1 12 73 EF 12 61 51 5E 13
00 ED 50 A6 85 98 98 17 03 E6
CF 5B 51 BE 22 CC 3E 39 1F 94
58 EC 84 07 91 69 C3 EB 5B F3
0B D6 26 F5 3E AB 58 C1 55 8A
A7 43 89 E0 87 1D 9C 8B 95 09
D5 9E 80 70 21 FA 84 82 F0 C4
DC 75 F7 BB 78 E5 02 F2 B6 90
8E B6 65 DD DD A0 F5 52 34 8A
F4 70 41 C7 36 33 84 4C 17 42
43 71 1E D5 B3 C8 44 44 45 EE
6B AC 0D 27 5C A8 5C 18 3A 3E
3F C0 59 25 D3 6C 1C A7 36 05
EE 4B E5 84 5A 7C 72 AA 15 10
E1 04 A5 41 D0 70 A9 34 48 44
51 90 34 AA 30 B4 BF EA 08 75
19 5F AF B4 C7 64 84 10 47 C3
F7 74 0B 56 BF BA 51 75 C8 E6
61 81 3F 40 E8 B9 79 8A BA 34
17 71 ED B8 C9 8E 0A AC A4 AC
39 6B 88 50 C8 A8 8D 89 4F ED
E2 C8 49 00 CA 3D 0B 0C A9 B8
63 68 58 9E 9C 69 48 21 28 9D
83 91 06 01 70 62 E7 C9 98 64
3B C7 38 7E AE B7 55 61 9B 77
01 C7 6D D8 34 07 D0 5A 10 EA
28 13 47 12 66 CB A2 5A B8 48
44 F4 B0 E1 39 BB 74 EB 36 67
83 73 34 91 CA 6A 03 C4 3E C2
66 8C FC 04 48 C5 2A 5A C5 40
AF 90 CB 20 A4 AF 37 BA 12 63
B5 E0 24 93 75 FF C4 8A F8 43
E3 7E EB 84 E3 4C A0 2F AC 5D
73 F7 53 7D 60 82 75 DF 2C 3C
CF CA C5 95 06 2F 8C 2E 5C 2F
7C 27 83 29 B6 F6 34 09 AA 5C
F1 9E 8C A4 10 95 3B EA 10 26
51 31 97 08 6F DD 85 82 E1 94
12 16 21 C9 08 B3 21 33 F1 9A
FB 8F 2F 4B A4 B7 1D D3 76 9A
17 E1 60 89 6F D8 B4 64 26 47
4A 5C 62 77 77 06 B9 B5 C3 52
D5 1E 75 6A 6F BA 3E 4E 05 A6
C3 0C C5 3F 5E B5 F5 EB AB 15
94 1D AB 11 A1 A3 9B 58 67 C9
8B 17 07 22 26 A1 8C 8D 3E 7D
DA AA AC 9B 0A 58 B6 A1 97 38
6C D6 DF 73 1F 4E 74 C2 E0 FE
34 E2 06 A0 DA E5 D7 73 F3 C4
82 68 B2 88 1A AE 7C 0E DF 75
A8 2B F7 C5 6C 63
 
 
分析如下:
私钥BLOB
BLOBHEADER blobheader;
RSAPUBKEY rsapubkey;
BYTE modulus[rsapubkey.bitlen/8];
BYTE prime1[rsapubkey.bitlen/16];
BYTE prime2[rsapubkey.bitlen/16];
BYTE exponent1[rsapubkey.bitlen/16];
BYTE exponent2[rsapubkey.bitlen/16];
BYTE coefficient[rsapubkey.bitlen/16];
BYTE privateExponent[rsapubkey.bitlen/8];

 
 
 
 
 
//BLOBHEADER
0702000000A40000
//密钥magic
52534132
//公钥模长
00040000
//公钥指数
01000100
//公钥n 也即公钥的模 即 prime1*prime2
CDDD41C499BCE20EBC4C09CC7AD9627F29242566E54F56421330969E1B421B99BF2466B8CBDD4BE7022A671EC5540A8388F8805C0AE9FA1924414F9FB4A71A52367DD1441A17DFF11273EF1261515E1300ED50A68598981703E6CF5B51BE22CC3E391F9458EC84079169C3EB5BF30BD626F53EAB58C1558AA74389E0871D9C8B
//私钥p 也即prime1
9509D59E807021FA8482F0C4DC75F7BB78E502F2B6908EB665DDDDA0F552348AF47041C73633844C174243711ED5B3C8444445EE6BAC0D275CA85C183A3E3FC0
//私钥q 也即prime2
5925D36C1CA73605EE4BE5845A7C72AA1510E104A541D070A9344844519034AA30B4BFEA0875195FAFB4C764841047C3F7740B56BFBA5175C8E661813F40E8B9
//私钥exponent1 私钥指数1    也即d mod (p - 1)
798ABA341771EDB8C98E0AACA4AC396B8850C8A88D894FEDE2C84900CA3D0B0CA9B86368589E9C694821289D839106017062E7C998643BC7387EAEB755619B77
//私钥exponent2  私钥指数2  也即d mod (q - 1)
01C76DD83407D05A10EA2813471266CBA25AB84844F4B0E139BB74EB366783733491CA6A03C43EC2668CFC0448C52A5AC540AF90CB20A4AF37BA1263B5E02493
//私钥coef  私钥系数   也即(inverse of q)mod p
75FFC48AF843E37EEB84E34CA02FAC5D73F7537D608275DF2C3CCFCAC595062F8C2E5C2F7C278329B6F63409AA5CF19E8CA410953BEA1026513197086FDD8582
//私钥exp                也即d
E194121621C908B32133F19AFB8F2F4BA4B71DD3769A17E160896FD8B46426474A5C62777706B9B5C352D51E756A6FBA3E4E05A6C30CC53F5EB5F5EBAB15941DAB11A1A39B5867C98B17072226A18C8D3E7DDAAAAC9B0A58B6A197386CD6DF731F4E74C2E0FE34E206A0DAE5D773F3C48268B2881AAE7C0EDF75A82BF7C56C63
 #include <Windows.h>
#include <WinCrypt.h>
#include <stdio.h>
#include <tchar.h> #pragma comment(lib, "crypt32.lib") #define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING|X509_ASN_ENCODING) void HandleError(char * str)
{
printf("%s[%x]\n",str,GetLastError());
system("pause");
exit();
} int main(int argc, char* argv[])
{
HCRYPTPROV hCryptProv = NULL; //CSP句柄
LPCTSTR pszContainerName = TEXT("MyKeyContainer"); //CSP密钥容器句柄
HCRYPTKEY hKey = NULL;
BYTE* pbKeyBlob;
DWORD dwBlobLen; if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
))
{
printf("获取到");
_tprintf(TEXT("[%s]"), pszContainerName);
printf("的密钥容器\n");
}
else
{
//发生错误,如果是密钥容器不存在,则创建新的密钥容器
if (GetLastError() == NTE_BAD_KEYSET)
{
if (CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))
{
printf("新的密钥容器已创建\n");
}
else
{
HandleError("无法创建密钥容器");
}
}
else
{
HandleError("无法获取CSP句柄");
}
} //获取一个加解密key句柄
if (CryptGetUserKey(hCryptProv, AT_KEYEXCHANGE, &hKey))
{
printf("获取到加解密key句柄\n");
}
else
{
if (GetLastError() == NTE_NO_KEY)
{
//因为没有密钥对, 创建一个
printf("密钥对不存在,创建一个密钥对\n");
if (CryptGenKey(hCryptProv, AT_KEYEXCHANGE,CRYPT_EXPORTABLE,&hKey))
{
printf("创建签名验签密钥对成功\n");
}
else
{
HandleError("创建密钥对失败");
}
}
else
{
HandleError("获取密钥对错误,签名验签密钥对不可用");
}
} //PUBLICKEYBLOB
//PRIVATEKEYBLOB
if(!(CryptExportKey( hKey, NULL, PUBLICKEYBLOB,, NULL, &dwBlobLen)))
{
HandleError("导出公钥信息失败");
} pbKeyBlob = (BYTE*)malloc(dwBlobLen); if(!(CryptExportKey( hKey, NULL, PUBLICKEYBLOB,, pbKeyBlob, &dwBlobLen)))
{
HandleError("导出公钥信息失败2;");
} printf("公钥BLOB信息如下:\n");
for(int i=;i<dwBlobLen;i++)
{
if (i % == )
{
printf("\n");
}
printf("%02X ", pbKeyBlob[i]);
}
printf("\n"); BLOBHEADER blobHeader;
RSAPUBKEY rsaPubkey;
PBYTE p = NULL;
p = pbKeyBlob;
//分析得到的BLOB
memcpy(&blobHeader.bType, p, );
p += ;
memcpy(&blobHeader.bVersion, p, );
p += ;
memcpy(&blobHeader.reserved, p, );
p += ;
memcpy(&blobHeader.aiKeyAlg, p, );
p += ; memcpy(&rsaPubkey.magic, p, );
p += ;
memcpy(&rsaPubkey.bitlen, p, );
p += ;
memcpy(&rsaPubkey.pubexp, p, );
p += ;
PBYTE reaMod = (PBYTE)LocalAlloc(LPTR, rsaPubkey.bitlen/);
memcpy(reaMod, p, rsaPubkey.bitlen/); //需要转换一下
BYTE bTemp;
for (int i=;i< rsaPubkey.bitlen//; i++)
{
bTemp = reaMod[i];
reaMod[i] = reaMod[rsaPubkey.bitlen/ - i - ];
reaMod[rsaPubkey.bitlen/- i - ] = bTemp;
} printf("BLOBHEADER.bType=[%02x]\n", blobHeader.bType);
printf("BLOBHEADER.bVersion=[%02x]\n", blobHeader.bVersion);
printf("BLOBHEADER.reserved=[%d]\n", blobHeader.reserved);
printf("BLOBHEADER.aiKeyAlg=[%d]\n\n", blobHeader.aiKeyAlg);
printf("rsaPubkey.magic=[");
char* q = (char *)&rsaPubkey.magic;
for (int i = ;i<;i++)
{
printf("%c",*q++);
} printf("]\nrsaPubkey.bitlen=[%d]\n", rsaPubkey.bitlen);
printf("rsaPubkey.pubexp=[%d]\n", rsaPubkey.pubexp);
printf("RSA MOD:\n");
for(int i=;i<rsaPubkey.bitlen/;i++)
{
if (i % == )
{
printf("\n");
}
printf("%02X ", reaMod[i]);
}
printf("\n"); //释放
LocalFree(reaMod); free(pbKeyBlob);
if (hKey)
{
CryptDestroyKey(hKey);
} if(hCryptProv)
{
CryptReleaseContext(hCryptProv, );
} if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_DELETEKEYSET))
{
printf("删除容器成功\n");
}
else
{
HandleError("删除容器失败");
}
system("pause");
return ;
}

结果如下:

导出私钥的代码跟导出公钥类似,修改CryptExportKey函数的第3个参数为PRIVATEKEYBLOB 即可。

CSP学习之导出密钥BLOB 解析的更多相关文章

  1. CSP学习之CryptoAPI初识

    Crypto API目的就是提供开发者在windows下使用PKI的编程接口. Crypto 提供了很多的加解密相关函数,如编码.解码.加密解密,哈希,数字证书.证书管理证书存储等.       有关 ...

  2. Batik - 将svg转换成其他格式图片或PDF - [导出服务器配置] 导出服务器原理解析

    导出服务器原理解析 Highcharts图表导出(或下载)本质上是将SVG代码转换为不同文件格式的过程,用到的工具是batik,所以所谓导出服务器,只不过是调用batik,将SVG代码转换并下载.下图 ...

  3. Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition

    Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition Property animation系统还提供了对ViewGroup中的View改变 ...

  4. Android Animation学习(四) ApiDemos解析:多属性动画

    Android Animation学习(四) ApiDemos解析:多属性动画 如果想同时改变多个属性,根据前面所学的,比较显而易见的一种思路是构造多个对象Animator , ( Animator可 ...

  5. Android Animation学习(三) ApiDemos解析:XML动画文件的使用

    Android Animation学习(三) ApiDemos解析:XML动画文件的使用 可以用XML文件来定义Animation. 文件必须有一个唯一的根节点: <set>, <o ...

  6. Android Animation学习(二) ApiDemos解析:基本Animators使用

    Android Animation学习(二) ApiDemos解析:基本Animatiors使用 Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.O ...

  7. Android Animation学习(二) ApiDemos解析:基本Animatiors使用

    Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.ObjectAnimator.AnimatorSet ApiDemos中Animation部分是单独 ...

  8. Mybatis源码学习之parsing包(解析器)(二)

    简述 大家都知道mybatis中,无论是配置文件mybatis-config.xml,还是SQL语句,都是写在XML文件中的,那么mybatis是如何解析这些XML文件呢?这就是本文将要学习的就是,m ...

  9. 简单学习一下ibd数据文件解析

    来源:原创投稿 作者:花家舍 简介:数据库技术爱好者. GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 简单学习一下数据文件解析 这是尝试使用Golang语言简单解析My ...

随机推荐

  1. 基于 Django 的手机管理系统

    前段时间和小组一起完成数据库作业,觉得收获挺多的,分享到博客来. 一.概述 打算通过设计数据库,然后结合 Python 框架Django,实现在网页上对数据库的增删改查(本例以手机的管理为例,不考虑订 ...

  2. mysql双机互相备份

    互备/***************************************master服务器**************************************/vi my.cnf[ ...

  3. php 镜像richarvey/nginx-php-fpm的ngnix配置

    1.遇到错误option error_page 405 =200 $request_uri; location ~ ^/(json)/ { # root /data/website/xxxx.com/ ...

  4. ssh 操作

    1.登录 ssh 用户名@远程地址 2.拷贝:scp -r 本地文件夹 远程文件夹(user@ip:目录) 3.从ssh中临时退出: a. ~  (control+z) b.查看后台任务:jobs c ...

  5. CBoard 看板参数管理

    看板设计采用简单Row+Column布局模式,每行总长度为12,每列对应一个图表,行高度可以调节,列高度集成行高 左边栏看板分类中,我的看板为当前用户创建的看板,普通看板分类通过分类管理维护,保存看板 ...

  6. Java 实现手机发送短信验证码

    Java 实现手机发送短信验证码 采用引入第三方工具的方式,网上查了半天,发现简单的实现方式便是注册一个中国网建的账号,新建账号的时候会附带赠几条免费短信,彩信 ,之后想要在使用就得花钱了.简单的操作 ...

  7. 最长上升子序列(LIS)的n*log(n)求法

    方法: 对于某个序列,设一个数组,将序列第一个数放入,然后再一个一个判断序列下一位,如果大于当前数组的末尾元素,则加入数组,否则利用二分法找到第一个大于等于当前数的元素并替换,最后这个数组的长度len ...

  8. C++_基础5-内存模型

    C++为在内存中存储数据提供了多种选择: 可以选择数据保留在内存中的时间长度(存储持续性): 程序的哪一部分可以访问数据(作用域和链接): 可以使用new来动态地分配内存:定位new运算符提供了这种技 ...

  9. DP设状态 : 状压与线

    [NOIP2017]宝藏(状压) [AHOI2009]中国象棋(状压) [BZOJ1814] URAL1519 Formula 1(插头\(DP\)模板) 新链接 : Luogu5056 , dark ...

  10. 面向对象中的@classonlymethod 与 @classmethod的区别

    如果要使用classonlymethod ,则需要先定义好一个classonlymethod 类. 首先我们需要明白无论是classonlymethod还是classmethod,本质都是一个类,而c ...