RSACryptoServiceProvider 提供的 rsa 加密,其本身可以泄露私钥,望讨论

jmcooler 2015-05-11 10:19:07
做了个小测试,发现 RSACryptoServiceProvider,及其内核 Microsoft Cryptography API,完全可以轻松泄露私钥:
代码如下:
CspParameters cspParam = new CspParameters( 1, null, "x-user" );
RSACryptoServiceProvider ras = new RSACryptoServiceProvider( cspParam );

bool bset = ras.PersistKeyInCsp;
bset = ras.PublicOnly;
string key1 = ras.KeyExchangeAlgorithm;
key1 = ras.SignatureAlgorithm;
byte[] blob = ras.ExportCspBlob( true );

StreamWriter blbWriter = new StreamWriter( "e:\\blob.txt" );

for( int mm = 0; mm < blob.Length; mm++ )
{
blbWriter.Write( "{0:x2} ", blob[mm] );
if( (mm + 1) % 20 == 0 )
blbWriter.WriteLine();
}
blbWriter.Dispose();


如果你将这个程序运行两次,你会发现,不仅私钥可以被输出,而且两次的私钥内容完全一样。
因为公约和私钥对,永久驻留在 Windows 系统中,就算你重启 windows,它俩的数据保持不变。
换句话说,上述名字为 “x-user” 的密钥容器中的密钥永远不会改变,而且还可以被输出。

那就完蛋了,只要你用过 RSACryptoServiceProvider 来进行过非对称加密,人家拿到你密钥容器的名字,就可以把你的私钥输出来。
而拿到你密钥容器的名字易如反掌,只要反编译你的 C# 代码,或者查找程序集中的常量字符串,就知道了

问题的根源不在于 .NET 的 RSACryptoServiceProvider 类,而是在于 Windows 提供的 Microsoft Cryptography API,它本身就是永久保存密钥对,并可以输出私钥。
我又用 VC 调用这个 API 做了个测试,密钥容器名字仍使用 x-user,发现输出的密钥对,完全同上述一样。

各位想象一下,这有多危险
...全文
458 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
jmcooler 2015-05-12
  • 打赏
  • 举报
回复
我这个帖子,至少帮助大家,了解了加密的基本概念。 同时也提醒了大家,不要乱用诸如 RSACryptoServiceProvider() 的加密类,要不然,不知不觉,你自己就已经泄密了
jmcooler 2015-05-12
  • 打赏
  • 举报
回复
CSDN 有牛人啊,sp1234 就是一位,是版主吧,谢谢了哈。 大家,能不能再给些思路和提示啊。
jmcooler 2015-05-12
  • 打赏
  • 举报
回复
为何国内打开 csdn 总是打不开,我用代理才能打开这个论坛。刷新和提交一次慢如蜗牛。 我是现学现用的,可能还有更好的方案,都快想破脑子了。看起来上面的方案啰嗦了点,肯定还有更好的办法。 我还在查资料,还在努力想,不能就这么定案。 我这个程序,主要是想从服务器下载机密的文件和核心算法。
秋的红果实 2015-05-12
  • 打赏
  • 举报
回复
看来得好好学习了,我也想参与讨论 我的加密方式很弱:数据传输只用了https,没有在传输前用程序加密;服务器端用了sql自身带的对称加密
jmcooler 2015-05-12
  • 打赏
  • 举报
回复
典型地,SSL 就是采用了这种基本的加密思路,最终协商出一个会话密钥。只不过,它还需要协商这个会话密钥的类型,是用 DES、3DES、RC2,还是 RC4。
泡泡龙 2015-05-12
  • 打赏
  • 举报
回复
学习一下,很有意义
jmcooler 2015-05-12
  • 打赏
  • 举报
回复
第一步:客户端创建一个临时密钥容器,而服务器端可以创建永久的密钥容器。它们分别各自在自己的容器中,创建一个交换密钥对和一个签名密钥对。 第二步:客户端将交换公钥和签名公钥从容器中取出,并传递给服务器。服务器也将自己的两个公钥传给客户端。这就是简单的密钥交换。更复杂的多方密钥交换,需要参考密钥交换协议(如 DH 和 IEK)。 从第三步开始,有两种做法,一种是每次通信都产生一个临时的会话(对称)密钥,或者商议一个后续通信的不变的对称密钥。 这里,我们以前者为例: 第三步:客户端在容器中创建一个对称密钥【会话密钥】,加密要发送的认证信息。并创建一个 Hash 实例,对该会话密钥或者认证信息,进行摘要,然后用【签名私钥】对这个 hash 摘要进行签名,最后用服务器的【交换公钥】加密这个会话密钥。最终,认证信息,会话密钥和签名数据,三部分被发往服务器。 第四步:服务器,用自己的【交换私钥】解开会话密钥,并用客户端【签名公钥】进行签名核验,然后用会话密钥解开认证信息 第五步:服务器和客户端一样的做法,产生一个会话密钥,或就用客户端的会话密钥,加密通信数据,用自己的【签名私钥】对会话密钥或通信数据的 hash 摘要进行签名,并以客户端的【交换公钥】加密会话密钥。 我说了,从第三步开始,既然客户端已经拟定了一个会话密钥,那么第五步认证完毕之后,双方都可以仅用这个会话密钥加密后续的通信数据,而无需交换密钥和签名密钥等的参与,包括 hash 和签名,都可以销毁了。
jmcooler 2015-05-12
  • 打赏
  • 举报
回复
这个问题,我已经有明确答案了。 首先声明,我也是刚研究这个,同时在 .NET 和 C++ 中使用,并且 .NET 和 C++ 的加密解密要互通,也就是说,C++模块加密,服务器方的 .NET C# 能顺利地解密,反之一样。 最重要的一点,要求客户端在与服务器的整个通信过程中,都要求对双方的数据加密。 感谢 sp1234 的下面一句话,点醒了我: CSP本来就是服务器端保存私钥用的,你的程序本来就是服务器端程序。客户端程序根本不是你那种编程方法,它只收到公钥、只能加密而不能解密。 让我又整整研究了一晚上,把 MSDN 的 CryptoAPI,或者称为 cryptographic service provider (CSP) API 细细地读了一遍。 并参考 SSL 握手之密钥协商过程,得到如下的理解,并提出最终的加密方案: CSP API 实际上使用恰当,是不会泄露和输出私钥的。关键在于下面一个函数:
BOOL WINAPI CryptAcquireContext(
  _Out_ HCRYPTPROV *phProv,
  _In_  LPCTSTR    pszContainer,
  _In_  LPCTSTR    pszProvider,
  _In_  DWORD      dwProvType,
  _In_  DWORD      dwFlags
);
这是使用 CSP 时要调用的第一个函数,它以指定的“密钥服务提供者”,创建一个带指定名字的密钥容器。 一个密钥容器中,只能存放上面表中一行上的 4 种密钥算法,貌似每种还只能有一个实例。交换算法和签名算法的实例肯定只能各自有 1 个,至于能否创建多个对称加密算法和Hash算法的实例,我还没测试。按道理说,估计应该是可以的。 函数中,当给 pszContainer 传递一个容器名字,而 dwFlags 为 0 时,这将会创建一个永久存储在Windows 当前用户的密钥存储区的。它一旦被创建,任何进程都可以用同样的容器名字获得它,并输出其中的密钥。 但是当给 pszContainer 传 NULL 值,而 dwFlags 为 CRYPT_VERIFYCONTEXT 时,这将创建一个临时的密钥容器,其它进程不能获得它,它也不被存储在任何系统位置。
  • 打赏
  • 举报
回复
它只收到公钥、只能加密而不能解密 --> 它只收到公钥、因而只能用公钥加解密
jmcooler 2015-05-11
  • 打赏
  • 举报
回复
换话句话说,如果只是单向上传,那倒好办。登录,就是单向的嘛。 可要是,双方都有重要数据要传输,那是不是说,客户端 CSP 就用不了了?
jmcooler 2015-05-11
  • 打赏
  • 举报
回复
赞同 sp1234 的说法,不过我那程序的第一行写死的是一个密钥容器的名字,而非登录用户名。 对于类似于 QQ 的登录应用,那当然客户端只能加密,而不能解密。也许我举 QQ 的例子不恰当。 但考虑另外一种应用场景,假定,服务器有一些核心数据,他们绝不可泄露。 服务器怎么把他们传给我? 我能想到的就是,它用我的公钥加密后,发给我,我用私钥来解密。
jmcooler 2015-05-11
  • 打赏
  • 举报
回复
于是 CSP API 内核将上述 1、2、3、4 这一组由某个 CSP 提供者提供,如下表:


你可以基于一个 CSP,创建一个密钥容器,并存放上述 4 种密钥
  • 打赏
  • 举报
回复
引用 4 楼 jmcooler 的回复:
公钥,都是会公布出来的,或者被放入证书中,XML 中,以交换给对方。 私钥,是绝对不可泄露的,然而微软的 CSP API 居然能输出私钥,还永久不变
CSP本来就是服务器端保存私钥用的,你的程序本来就是服务器端程序。客户端程序根本不是你那种编程方法,它只收到公钥、只能加密而不能解密。 RSA是不对称的操作。你只是看到了RSA编程的三分之一的例子就以为看懂了RSA了。
  • 打赏
  • 举报
回复
程序第一行是你自己写的。明明是你自己把第一行代码给写死了,这不赖人家。 比如说,每一个人都学过如何写一个简单的面向服务器程序的“登录”功能代码,它会让用户输入“账号、密码”,然后判断能否登录。 现在,你把“一个”帐号密码在程序中写死了(而不是让用户输入),而不是让用户输入了,你来说.net类库不保密了、c#程序不保密了,这怪谁?
jmcooler 2015-05-11
  • 打赏
  • 举报
回复
扫个盲,让大家明白非对称加密。 假定你是 QQ 服务器,我是 QQ 客户端,我现在输入用户名和密码,登录服务器。 用户名和密码,不能进行明文发送,这是显然的,就需要加密: 一种简单的办法,就是服务器和客户端,采用相同的写死的密钥,对通信数据进行加密,这被称为对称加密。显然,只要人家在 QQ 客户端中,找到这个写死的密钥,就能将输入的密码破解出来。 第二种,是采用非对称加密,即我有一个密钥对,公钥和私钥。同样,你也有一个密钥对。 我俩先将自己的公钥互相交换,私钥自己留存,并且不可泄露。 我要给你发信息,就用你的公钥加密。你收到信息后,就用你的私钥解密。反之亦然。 总结来说,就是: 用对方的公钥加密,对方用私钥进行解密 非对称加密,本身可以概括为,针对一个密钥对,用公钥加密的数据,能用私钥解密。而也可以用私钥加密,而用公钥解密。 因此,上述通信中,我就是用你的公钥加密,而你用你的私钥解密。 公钥加密—>私钥解密,通常就是用来加密通信数据的。 私钥加密—>公钥解密,则是用来进行数字签名的。它只能证明信息绝对是我发的,绝非他人冒充的。 还有一个特点,对称加密速度快,通常适用于大量的数据需要加密。而非对称加密速度慢,只适合少量数据的加密。 于是,通信过程中,若干种加密办法会同时使用,以解决加密,信息被修改,被冒充,速度要快的各种目的。 于是就有了一种加密模式: 用对称密钥,来加密大量通信数据,用 Hash 对数据进行摘要,以防止被修改,用数字签名以防止被冒充。然后以非对称加密,对你刚刚用到的那个对称密钥进行加密(因为该对称密钥的数据不过 10 多字节) 因此一次通信中,将用到: 1、一个对称加密密钥,用于对通信数据加密,它通常被称为会话密钥,每次不同。 2、一个非对称加密密钥对(公钥和私钥),用于对 1 进行加密,相对不变化,或周期性变化 3、一个非对称加密密钥对(公钥和私钥),用于对 1 进行签名,相对不变化,或周期性变化 4、一个 hash 算法,用于对通信数据,或者对 1 进行摘要 公钥,都是会公布出来的,或者被放入证书中,XML 中,以交换给对方。 私钥,是绝对不可泄露的,然而微软的 CSP API 居然能输出私钥,还永久不变
Dobzhansky 2015-05-11
  • 打赏
  • 举报
回复
RSACryptoServiceProvider 底下的是各种 CSP, 你找个u盾读读看
tcmakebest 2015-05-11
  • 打赏
  • 举报
回复
楼主是不是多虑了, 如果你的电脑都被别人控制了,知道不知道密钥容器的名字又能如何.
  • 打赏
  • 举报
回复
说实在的,加密解密这块从来不是我的菜……

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧