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

作者&投稿:宿魏 (若有异议请与网页底部的电邮联系)
Base64编码的字符串使用的RSA公钥从C java问题,怎么解决~

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

// 将 s 进行 BASE64 编码
public static String getBASE64(String s) {
if (s == null) return null;
return (new sun.misc.BASE64Encoder()).encode( s.getBytes() );
}

// 将 BASE64 编码的字符串 s 进行解码
public static String getFromBASE64(String s) {
if (s == null) return null;
BASE64Decoder decoder = new BASE64Decoder();
try {
byte[] b = decoder.decodeBuffer(s);
return new String(b);
} catch (Exception e) {
return null;
}
}

Exception in thread "main" javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
at sun.security.rsa.RSAPadding.unpad(Unknown Source)
at com.sun.crypto.provider.RSACipher.a(DashoA12275)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA12275)
at javax.crypto.Cipher.doFinal(DashoA12275)
at CipherTest.rsaEncrypt(CipherTest.java:33)
at CipherTest.main(CipherTest.java:40)



RSA是一种对称加密算法,但加密时出现这个错误的原因,是因为使用了私钥进行加密.

正常应该使用公钥进行加密,然后通过私钥进行解密.

代码:

KeyPair keypair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
String str = "hello world";
Cipher cipher = Cipher.getInstance("RSA");

cipher.init(Cipher.PUBLIC_KEY, keypair.getPublic());
byte[] buffer = cipher.doFinal(str.getBytes());

cipher.init(Cipher.PRIVATE_KEY, keypair.getPrivate());
System.out.println(new String(cipher.doFinal(buffer)));

正常的情况下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(认证中心)获得一个证书时,也是把证书导入到容器中,然后设置导出密码,销毁原证书文件。如果在迁移时,可以使用密钥导出私钥证书,然后再到其他机器上安装。很多人不太理解这样做的目的,其实密钥管理还涉及到一个人员管理的问题,这样证书只会经过某个特定的人进行处理,其他任何人都无法拿到该证书,但他们可以使用,所以这样做是十分安全的。




RSA、Diffie-Hellman和中间人攻击
DH算法的过程可以简单解释如下:通信双方AB,各自生成一对DH密钥(Pa,Sa)和(Pb,Sb)(P代表公钥,S代表私钥)。双方交换各自的公钥P,于是A持有Sa、Pb,B持有Sb、Pa。通过某种计算,Sa、Pb可以生成会话密钥K,Sb、Pa也可以生成相同的K。DH算法本身不包含身份认证机制,所以中间人攻击是其明显的问题。

像诚信币这样基于区块链的数字货币中,私钥,公钥,地址到底是怎么回事...
私钥是怎么来的?很简单:钱包根据密码学原理帮用户准备好的。虽然这三者都在钱包里,但在日常交易中,除了纸钱包外,人们通常并不用操心私钥是什么,更不用管理公钥,只要懂得使用地址转账就可以了。地址是一串很长的字符串,比如笔者个人的手机钱包里的一个诚信币地址:DBy6aRpBi8SSQi1AgXjZXCSA23tt...

HTTPS--握手,证书及秘钥协商
5.6.7、客户端收到证书后,首先验证证书合法性,获取OS内部受信任的CA证书,使用OS公钥对证书摘要进行解密出hash值,再使用相同的hash算法对证书内容进行hash计算,比较两个hash值是否一致,一致则认为证书合法; 8、客户端验证证书合法后,client生成对称秘钥对,并使用公钥A将秘钥对加密,发送给服务器,之后的交互数据,则使用...

网络工程师面试题
但密钥的维护需要双方的协商,容易被人窃取;非对称型加密算法使用公钥和私钥,双方维护对方的公钥(一对),并且各自维护自己的私钥,在加密过程中,通常使用对端公钥进行加密,对端接受后使用其私钥进行解密,加密性良好,而且不易被窃取,但加密速度慢.

IPSec是什么?它是否是一种新的加密形式?
IPSec的公钥加密用于身份认证和密钥交换。公钥加密,也被称为"不对称加密法",即加解密过程需要两把不同的密钥,一把用来产生数字签名和加密数据,另一把用来验证数字签名和对数据进行解密。 使用公钥加密法,每个用户拥有一个密钥对,其中私钥仅为其个人所知,公钥则可分发给任意需要与之进行加密通信的人。例如:A想要发送...

雷州市13096968359: RSA公钥只返回一串base64字符 怎么使用 -
慈福德依: 正常的情况下RSA公钥是不会只返回一串base64编码的.我们从原理上说起:RSA密钥对有很多个参数,d /dp/dq/Expoent/InverseQ/Modulus/p/q,原理上我们是知道他是由两个大素数p和q的乘积,正常的情况下含有私钥的文件会反这些参数全...

雷州市13096968359: 一个字符串用RSA公钥加密为byte[],在用base64 加密成String,在用base64 解密为String -
慈福德依: 俺有类似的函数,核心加密是用des 原型:int WINAPI icePub_encryptText3(char *strInput, char *strOutputHexstring, char *strKey) 输入:strInput 待加密文本数据串 strKey 密钥,任意长度 输出:strOutputHexstring 加密后base64串 返回码: 原型...

雷州市13096968359: Base64编码的字符串使用的RSA公钥从C java问题,怎么解决 -
慈福德依: import sun.misc.BASE64Encoder; import sun.misc.BASE64Decoder; // 将 s 进行 BASE64 编码 public static String getBASE64(String s) { if (s == null) return null; return (new sun.misc.BASE64Encoder()).encode( s.getBytes() ); } // 将 BASE64 ...

雷州市13096968359: Java 第三方公钥 RSA加密求助 -
慈福德依: 下面是RSA加密代码./*** RSA算法,实现数据的加密解密.* @author ShaoJiang**/ public class RSAUtil {private static Cipher cipher;static{try {cipher = Cipher.getInstance("RSA");} catch (NoSuchAlgorithmException e) {e....

雷州市13096968359: Java通过RSA算法获取公私钥对 将公钥提供出去 如何获取字符串的公钥 -
慈福德依: 直接将公匙BYTE数组转换为16进制的串啊 private static char hexTable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; public static String toHexString(byte bytes[]) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { ...

雷州市13096968359: rsa解密算法 -
慈福德依: 我刚刚复习完关于rsa的算法知识,告诉你吧: RSA公钥密码系统: 1.密钥对的产生:随机产生两个大的素数:p,q 计算n=p*q 2.随机产生加密密钥e:选择一个随机的e使Gcd(e,(p-1)*(q-1))= 1就是选择一个随机的e,使e和 (p-1)*(q-1)互素....

雷州市13096968359: RSA的公钥和私钥到底哪个才是用来加密和哪个用来解密 -
慈福德依: 我们来回顾一下RSA的加密算法.我们从公钥加密算法和签名算法的定义出发,用比较规范的语言来描述这一算法.RSA公钥加密体制包含如下3个算法:KeyGen(密钥生成算法),Encrypt(加密算法)以及Decrypt(解密算法).(PK, SK)\...

雷州市13096968359: RSA 是什么意思?? -
慈福德依: RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作. RSA是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一.RSA的安全性依赖...

雷州市13096968359: 什么是RSA非对称加密? -
慈福德依: 非对称密钥——RSA算法RSA算法是最流行的公钥密码算法,使用长度可以变化的密钥.RSA是第一个既能用于数据加密也能用于数字签名的算法.RSA算法原理如下:1.随机选择两个大质数p和q,p不等于q,计算N=pq; 2.选择一个大于1小...

雷州市13096968359: ios开发rsa加密怎么生成秘钥 -
慈福德依: 1、加密解密的第一步是生成公钥、私钥对,私钥加密的内容能通过公钥解密(反过来亦可以) 下载开源RSA密钥生成工具openssl(通常Linux系统都自带该程序),解压缩至独立的文件夹,进入其中的bin目录,执行以下命令:代码如下:...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网