RSA公钥只返回一串base64字符 怎么使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RSA公钥只返回一串base64字符 怎么使用相关的知识,希望对你有一定的参考价值。

参考技术A

正常的情况下RSA公钥是不会只返回一串base64编码的。我们从原理上说起:

RSA密钥对有很多个参数,d /dp/dq/Expoent/InverseQ/Modulus/p/q,原理上我们是知道他是由两个大素数p和q的乘积,正常的情况下含有私钥的文件会反这些参数全部给出,而如果只给出公钥时,事实上是给定了n(Modules)和e(Exponent)两个参数,这两个参数可以用来加密,我们也都知道,如何将这两个参数有效地传递给另一方呢?或者说我们把这两个参数如何给其他人呢,这个就是我们常说的公钥文件,当然有什么pfx等等,但这里我们如果简要地传递给第二方时,我们会经常使用一个对象RSAParameters类,这个对象就是这几个参数,如果我需要传递给你时,有效的方式就是把RSAParameters序列化,然后把序列化的内容给你就可以了。

在C#中有两种常用的序列化形式,一种就是byte序列化,一种就是xml序列化(一般使用JSON序列化的时候不多,因为javascript对实现RSA原生支技较为复杂)。

byte序列化不适合在网络上传输,所以一般都使用XML序列化,xml适合网上传输,他会把二进制自动序列化为base64编码。而事实上RSAParameters类和几个参数全是byte类型的(方便存储超大数字),所以会序列化成如下形式:

<RSAKeyValue>
   <Modulus>…</Modulus>
   <Exponent>…</Exponent>
   <P>…</P>
   <Q>…</Q>
   <DP>…</DP>
   <DQ>…</DQ>
   <InverseQ>…</InverseQ>
   <D>…</D>
</RSAKeyValue>

很明显这是一个序列化后的xml形式,其中几个参数都是被自动序列化成base64编码,如果是公钥的话,其中<Modulus>和<Exponent>两个参数有值,其他的在序列化过程中如果没有值不序列化,所以你可以最终看到只有这两个参数的一个结构。

PS:序列化与反序列化由RSACryptoServiceProvider类的ToXmlString(bool)执行,其中参数表示是否含有私钥,如果是则全部参数都会存在,否则只含有公钥。拿到这个文件时,可以直接使用FromXmlString(string)进行导入即可使用。

但事实上,RSACryptoServiceProvidder提供的不仅有ToXmlString()方法,还存在有ExportCspBlob()方法和ExportRSAParameter()方法。导出RSAParameter方法不用说了,基本上用于本地版本,就是导出一个RSAParameter结构,而ExportCspBlob方法与ToXmlString()有一定的比较性,它导出的是一个二进制的形式,也就是二进制序列化后的数值,由于其是二进制形的数值。但两种形式的序列化虽然都是序列化RSAParameter结构,但并没有让你直接使用XmlSerializer类进行序列化,而由于RSACryptoServiceProvider中的方法已经实现了,当然反序列化也是同理。

但由我们在使用.net自动生成公私钥时,其Exponent参数永远是65537(ABAQ),其实对于.net自动生成的公私钥对算是一个测试的公私钥对,所以它保证了Exponent为固定的一个值。所以很多人不太明白这个道理,以为真正使用的公私有对的e都一定被选定为该值,所以有一类人为了“节省”带宽,自做主张在传递公钥时只传递了一个Modulus的值!换句话来说,这些非商用的情况下的可行的,而且在商用的情况下也有一定的可行性。这里你可能只看到对方传递的是一个base64编码——这种情况显然不是规范的,所以尽量不使用或少使用,如果某企业有自己的证书或提供自己的公钥时,这种情况显然会导致你重新修改程序。但这也是你见到全是base64编码的原因。(他只传递了modulus的值 ,而不是RSAParameter序列化结构)。

另外一类的程序员,喜欢造轮子,或者是他们喜欢上了ExportCspBlob()和ImportCspBlob()两个方法,不管是本地还是网络传输,可怜他们只会用一对的方法,总是不知道ToXmlString()和FromXmlString()这一对方法是用来干什么的。所以他们爱好就是动不动用ExportCspBlob()方法导出公钥或私钥对。如果是本地程序这当然没有问题,但如果是远程的话,也使用这个,但导出的一堆byte他们没办法通过网络(我指的html之类的通讯,不是CS模式下)传输,当然有,遇到我们没有传递二进制时设计成编码就可以了。所以指出了一堆二进制之后,把这个二进制进行base64编码来传递——这种情况我见到的最多!如果你看到这种的一堆base64时,需要先手式把base64编码转换成二进制,然后使用ImportCspBlob()方法再导入进去。这种情况看起来没有大问题,但实际上是绕了一个圈圈,感情就会使用ExportCspBlob()方法,为什么不直接考虑使用ToXmlString()方法呢?所以我看到很多情况下base64位编码基本上都属于这种情况!

事实上,我的建议是,如果是本地存储的情况下,比如需要存储成一个文件,这时可以使用ExportCspBlob()方法导到成byte数组,然后可以直接将该数组写入到dat文件中,以方便其他时间的使用;如果是网络传输的情况下,因涉及到网络协议的问题,二进制流传输还行,但如果是文本流传输时使用ToXmlString()方法导出,可以直接让其他程序使用。虽然在微软上没有这么规定,但如果你这么使用却是一个很恰当的使用场景(二进制序列化时,由于参数的值本身就是二进制,所以它序列化时其他最为简单,那些个二进制的值不需要任何的编码处理,所以性能会高一些,而xml序列化则涉及到二进制的编码,所以性能上没有直接二进制高,但如果你先导出成二进制,再base64编码,就让人难以想通了,完全属于对.net类库不熟悉而造成的!很多人可能会说,其他语言中导到的二进制——如果基于这种考虑,理由似乎说得过去,但至少常见的语言中还都是支持xml形式的,包括javascript新版语言原生支持RSA,它只支持xml形式和JSON形式,并不支持二进制形式。所以先导出二进制再编码base64形式纯属多余)。

PS:这一点更为重要,不少人把RSA的公私钥对导出后直接保存成文件,事实上我也这么说了,但是在windows中,尽量避免这样做,除非你要对机器进行迁移时或者其他原因需要,否则不要大摇大摆地把公私钥对存储成一个文件,如果被别有用心的人拿了去,你自己想想后果是什么样的!windows本身提供了一种叫密钥容器的机制,可以存储DSA/RSA/DES/TDES等非对称或对称密钥的安全存储。应当使用这种安全存储的机制,而不是把公钥密文件大摇大摆在丢在某个目录下。关于密钥容器的问题,你可以查阅MSDN上,可以直接使用的。类似的情况还有,某些情况下我们从CA(认证中心)获得一个证书时,也是把证书导入到容器中,然后设置导出密码,销毁原证书文件。如果在迁移时,可以使用密钥导出私钥证书,然后再到其他机器上安装。很多人不太理解这样做的目的,其实密钥管理还涉及到一个人员管理的问题,这样证书只会经过某个特定的人进行处理,其他任何人都无法拿到该证书,但他们可以使用,所以这样做是十分安全的。

从 base64 RSA 公钥生成 SecKeyRef

【中文标题】从 base64 RSA 公钥生成 SecKeyRef【英文标题】:Generate SecKeyRef from base64 RSA public key 【发布时间】:2016-05-24 13:36:33 【问题描述】:

我查看了 *** 上的其他答案,但没有找到我想要的。 我有一个由其他系统生成的 Base64 格式的公共 RSA 密钥。 它采用 x509 格式,加密强度为 512 位。

我需要创建一个 SecKeyRef 以便使用加密一些数据并将其发送回主页。 (出于所有目的,公钥是从服务器端发送的)

在这种情况下,我在网上找不到任何可以帮助我的东西,所有其他示例和答案都绕过了这个问题。我无法创建 PEM 或 DER 文件或使用任何其他“技巧”,Base64 -> SecKeyRef 仅此而已。

我知道这很难,所以感谢大家的帮助。

p.s 这是针对框架而非应用程序的。

【问题讨论】:

【参考方案1】:

我发现这个很棒的 GitHub 项目对我有很大帮助!

https://github.com/ideawu/Objective-C-RSA

我希望这会有所帮助!

【讨论】:

这不是这个问题的合适答案。

以上是关于RSA公钥只返回一串base64字符 怎么使用的主要内容,如果未能解决你的问题,请参考以下文章

从 base64 RSA 公钥生成 SecKeyRef

Java前端Rsa公钥加密,后端Rsa私钥解密(目前还不支持中文加密解密,其他都行)

Java中的RSA加密/解密

vue 上传图片时 base64 怎么传到java后台

rsa解密乱码,重启应用后正常

java RSA 生成公钥私钥