惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Spread Privacy
Spread Privacy
L
LangChain Blog
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google DeepMind News
Google DeepMind News
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
人人都是产品经理
人人都是产品经理
H
Hackread – Cybersecurity News, Data Breaches, AI and More
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Engineering at Meta
Engineering at Meta
P
Privacy International News Feed
I
Intezer
NISL@THU
NISL@THU
Jina AI
Jina AI
G
GRAHAM CLULEY
C
CERT Recently Published Vulnerability Notes
S
Schneier on Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cisco Talos Blog
Cisco Talos Blog
Scott Helme
Scott Helme
MyScale Blog
MyScale Blog
IT之家
IT之家
Security Latest
Security Latest
C
Cisco Blogs
Cyberwarzone
Cyberwarzone
aimingoo的专栏
aimingoo的专栏
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
Recorded Future
Recorded Future
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
月光博客
月光博客
A
Arctic Wolf
云风的 BLOG
云风的 BLOG
N
Netflix TechBlog - Medium
K
Kaspersky official blog
S
Securelist
M
MIT News - Artificial intelligence
T
Threat Research - Cisco Blogs
P
Palo Alto Networks Blog
Simon Willison's Weblog
Simon Willison's Weblog
Know Your Adversary
Know Your Adversary
WordPress大学
WordPress大学
Project Zero
Project Zero
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
News and Events Feed by Topic
AWS News Blog
AWS News Blog
T
The Exploit Database - CXSecurity.com
T
The Blog of Author Tim Ferriss

博客园 - 兵

C#异常处理的方式 Firefox中Iframe的blur与focus事件问题 .NET中几种基本的线程同步方法 为什么说WinForm的控件只能在主线程中创建和调用 C#与Native C++互相访问 PHP5中的引用 Windows版的PDO太衰了 Framework里某些类型成员方法的一点疑惑 发现一个FireFox的问题 C#中COM操作(二)---接口查询 C#中COM操作(一)---实例化 非弹出式的模态对话框的背景遮罩 DOTNET事件拾遗 CSS开发辅助工具:Internet Explorer Developer Toolbar 另外一种高效地判断奇数和偶数的方法 如何利用客户端缓存对网站进行优化? 一个多表查询引出的问题(2) 类的成员初始化顺序 一个多表查询引出的问题(1)
CryptoAPI与.NET数字签名类的交互问题
· 2009-06-18 · via 博客园 - 兵

      最近做了一个Web项目,服务端采用ASP.NET。浏览器中需要用到证书登录,为了使以后能够读取USBKey证书和密钥,所以使用了ActiveX技述。这中间遇到了好多的问题,不过最后都一个一个解决了。

      首先是从客户机里导出证书和密钥的问题,基本的CryptoAPI提供了两个函数可以导出,一个是CryptSaveStore,另外一个是PFXExportCertStore函数。网上关于这两个函数导出证书的代码都有很多了,我也不想再在这里写。不过这两个函数都有一些问题,用起来我都不太满意。第一个函数也就是CryptSaveStore导出的证书怎么也不能用Windows向导导入到证书管理器中,这个问题折腾了我好久,最终没能解决只得放弃;第二个函数也就是PFXExportCertStore,这个函数虽说能导出证书可是只能导出PFX格式的,也就是可能包括了私钥(如果原本有的话)在内的证书,而且还得给个导出密码,重新导入的时候还得再次输入相同的密码。我想要做的是只导出证书文件本身,不导出私钥,对它用私钥签名,任何拿到证书的人都可以用证书上带的公钥进行验证证书的合法性,而且检验的方式越简单越好。所以上面的两种方式都不行。又查资料又Coding和Debug,熬了两个晚上终于发现了一个传说中的函数CryptUIWizExport ,这个函数在MSDN里面也有介绍,只不过不在CryptoAPI系列中。它的强大之处是可以调用证书导出向导导出证书,功能和直接使用证书管理器一模一样,可以导出各种格式的证书,而且还支持使用参数方式,也就是说可以无窗口界面。在我仔细看了该函数的用法后直接晕死。原来一开始的时候就不知在哪里见到过这个函数,只是看到名字里带个UI以为是会出现交互界面的,而我的导出证书的过程是在提交表单的时候后台自动完成并签名的,所以不能有交互界面的。没想到这个还可以没有交互的方式使用。只不过有一点小小的不足是导出的只能导成文件,不能导入到内存中,不过这也没什么了。只需要生成到临时文件,再读到内存中就可以了。附上一段代码。

 1function TX509Certificate.ExportCert: TBytes;
 2var
 3  ExportInfo:CRYPTUI_WIZ_EXPORT_INFO ;
 4  ContextInfo: CRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO;
 5  TempPath: array [0..MAX_PATH] of WideChar;
 6  TempFile: array [0..MAX_PATH] of WideChar;
 7  FS: TFileStream;
 8begin
 9  ZeroMemory(@ExportInfo, sizeof(CRYPTUI_WIZ_EXPORT_INFO));
10  ZeroMemory(@ContextInfo, sizeof(CRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO));
11
12  if (GetTempPath(MAX_PATH, @TempPath) = 0or
13    (GetTempFileName(@TempPath, 'tmp'0, @TempFile) = 0then
14  begin
15    raise Exception.Create('创建临时文件失败');
16  end;
17
18  ExportInfo.dwSize               := sizeof(CRYPTUI_WIZ_EXPORT_INFO);
19  ExportInfo.pwszExportFileName   := @TempFile;
20  ExportInfo.dwSubjectChoice      := CRYPTUI_WIZ_EXPORT_CERT_CONTEXT;
21  ExportInfo.Union.pCertContext   := Self.m_pCertContext;
22
23  ContextInfo.dwSize              := sizeof(CRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO);
24    ContextInfo.dwExportFormat      := CRYPTUI_WIZ_EXPORT_FORMAT_DER;
25    ContextInfo.fExportChain        := FALSE;
26    ContextInfo.fExportPrivateKeys  := FALSE;
27    //ContextInfo.pwszPassword        := nil;
28    //ContextInfo.fStrongEncryption  = TRUE;
29
30
31  try
32    if not CryptUIWizExport(
33      CRYPTUI_WIZ_NO_UI or CRYPTUI_WIZ_IGNORE_NO_UI_FLAG_FOR_CSPS,
34      0nil,
35      @ExportInfo,
36      @ContextInfo) then raise Exception.Create('导出客户端证书失败');
37
38    FS := TFileStream.Create(string(@TempFile), fmOpenRead);
39    SetLength(Result, FS.Size);
40    FS.Read(Result[0], FS.Size);
41  finally
42    DeleteFile(string(@TempFile));
43    if Assigned(FS) then
44      FreeAndNil(FS);
45  end;
46end;

       证书导出来了就该进行签名了。这里主要用到了CryptAcquireCertificatePrivateKey,CryptCreateHash,CryptSignHash这三个函数,都比较简单,也没什么好说的,MSDN上都写的很清楚了。

      原本以为为完成签名和签名验证是件很容易的事情,可是真正做的时候确遇到了新的问题。 CryptoAPI导出证书的签名不能通过.NET的签名验证。在.NET里面采用同样的证书和私钥签完名后的值也与CryptoAPI得到的结果不一样,难道说这两种操作方式不能互相兼容?

      带着这个问题我在网上查了大量的资料。中文的这方面的资料就很少,感觉好像是很少有人遇到过这样的问题似的,不像那些证书怎么从证书管理器中读取出来类的问题满天飞。看来这种平台交互的加密签名还是很少人做呀。又是熬了两个晚上的时间(这个时候总是觉得时间过得这么快,不够用),后来在偶然发现一个国外的BBS上有个老外也提到了相同的问题。下面有很多人跟帖,大都是说方法不正确呀,密钥不配对呀什么的。有一个人的回帖比较有意思,说的是这两种平台用的签名数据的字节顺序不一致,一个是Little-Endian,一个是Big-Endian。一开始我也没有当回事,以为就是随便胡说的。后台第N次查看MSDN的时候发现这么一段话:

同 Microsoft Cryptographic API (CAPI) 相互操作

与非托管 CAPI 中的 RSA 实现不同的是,RSACryptoServiceProvider 类会在加密之后、解密之前颠倒被加密数组的字节顺序。默认情况下,CAPI CryptDecrypt 函数无法解密由 RSACryptoServiceProvider 类加密的数据,RSACryptoServiceProvider 类无法解密由 CAPI CryptEncrypt 方法加密的数据。

如果在 API 之间互相操作时没有对颠倒的顺序进行补偿,RSACryptoServiceProvider 类会引发 CryptographicException

要同 CAPI 相互操作,必须在加密数据与其他 API 相互操作之前,手动颠倒加密字节的顺序。通过调用 Array..::.Reverse 方法可轻松颠倒托管字节数组的顺序。

       于是我在调用签名验证前把签名后的数据用Array.Reverse反转了一下,结果这回就通过验证了。这个发现让我大跌眼镜,这么个小问题折腾了我两个晚上。