写在前面

本文是在解决加密和解密用的不是同一套密钥对时找到的一篇, 最后问题不在byte数组, 是自己工具类中生成密钥对的问题, 但是本文RSA加密中公钥指数和公钥系数的获取(byte[]部分)讲解比较细致, 虽然最后也没用这种方式. 以下是本人采用的方式. 位数确实不对, 但是不影响前台根据系数和指数生成公钥, 也不影响后台解密, 仅仅做一个记录.

//        // 获取公钥系数和公钥指数
// // 获取公钥对象--注意:前端那边需要用到公钥系数和指数
// RSAPublicKey publicKey = RSAUtils.getDefaultPublicKey();
// // 公钥-系数(n)
// request.setAttribute("pkModulus", new String(Hex.encode(publicKey.getModulus().toByteArray())));
// // 公钥-指数(e1)
// request.setAttribute("pkExponent", new String(Hex.encode(publicKey.getPublicExponent().toByteArray())));
// 获取公钥系数和公钥指数
//公钥-系数(n)
RSAPublicKey publicKey = RSAUtils.aPublic;
String pkModulus2 = publicKey.getModulus().toString(16);
request.setAttribute("pkModulus", pkModulus2);
//公钥-指数(e1)
String pkExponent2 = publicKey.getPublicExponent().toString(16);
request.setAttribute("pkExponent", pkExponent2);

BigInteger转为16进制的String方式对比

方式一

String pkModulus2 = publicKey.getModulus().toString(16);生成的公钥系数(256位)

e0ffbc04ee1c099b3e898359a12a3d1a307415ec3daaff86e3d1b61a7d434e5073de79bc7de12324d4643fb93923f007897c35b7bb98c2864b8e4d319a5028935f882fad6ba1df8181478a331cf7d59335a603262bf7ad5aa648869ebd348640ad95f389eb603b6e301ea3e7aff24dc58209c2eef449a2bbe8d6d2159cdf1383

方式二

String pkModulus = new String(Hex.encode(publicKey.getModulus().toByteArray()));生成的公钥系数258位, 区别就是前面多的00

00e0ffbc04ee1c099b3e898359a12a3d1a307415ec3daaff86e3d1b61a7d434e5073de79bc7de12324d4643fb93923f007897c35b7bb98c2864b8e4d319a5028935f882fad6ba1df8181478a331cf7d59335a603262bf7ad5aa648869ebd348640ad95f389eb603b6e301ea3e7aff24dc58209c2eef449a2bbe8d6d2159cdf1383

BigInteger的toString(int radix)方法

/**
* Returns the String representation of this BigInteger in the
* given radix. If the radix is outside the range from {@link
* Character#MIN_RADIX} to {@link Character#MAX_RADIX} inclusive,
* it will default to 10 (as is the case for
* {@code Integer.toString}). The digit-to-character mapping
* provided by {@code Character.forDigit} is used, and a minus
* sign is prepended if appropriate. (This representation is
* compatible with the {@link #BigInteger(String, int) (String,
* int)} constructor.)
*
* @param radix radix of the String representation.
* @return String representation of this BigInteger in the given radix.
* @see Integer#toString
* @see Character#forDigit
* @see #BigInteger(java.lang.String, int)
*/
public String toString(int radix) {
if (signum == 0)
return "0";
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
radix = 10; // If it's small enough, use smallToString.
if (mag.length <= SCHOENHAGE_BASE_CONVERSION_THRESHOLD)
return smallToString(radix); // Otherwise use recursive toString, which requires positive arguments.
// The results will be concatenated into this StringBuilder
StringBuilder sb = new StringBuilder();
if (signum < 0) {
toString(this.negate(), sb, radix, 0);
sb.insert(0, '-');
}
else
toString(this, sb, radix, 0); return sb.toString();
}

jdk1.8中描述

声明

以下内容转自:JavaWeb对RSA的使用

由于公司的网站页面的表单提交是明文的post,虽说是https的页面,但还是有点隐患(https会不会被黑?反正明文逼格是差了点你得承认啊),所以上头吩咐我弄个RSA加密,客户端JS加密,然后服务器JAVA解密

本文主要面向想在javaweb/java应用里面使用RSA的人。

一、RSA是个ShenMeGui:

其实一开始叫我用RSA加密我是拒绝的,因为不可能你叫我用我就用,我得查查他是什么东西对不对。

RSA是目前最有影响力的公钥加密算法,属于非对称加密的,也就是用一个大家都知道的公钥来加密出来的密文,只有拥有私钥的人才能解开,目前听说1024比较安全,2048位那是相当安全,往上就更难破解了。

关于RSA的基本原理,百度百科里面提到“RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。”然后阮一峰先生的 《RSA算法原理(一)》《RSA算法原理(二)》里面就解释的比较清楚,看完你大概就懂了(不愧是大牛吖)。阮先生提到了:

加密公式

me ≡ c (mod n)

解密公式

cd ≡ m (mod n)

n是公共的模数,也就是pq的乘积,e是公钥的指数,d是私钥的指数,下面会说到利用他们可以还原公钥和密钥。

如果你知道是怎么回事的话也就清楚了RSA是怎么回事, 不清楚的话请去看文章,这里主要是说如何去使用RSA而不是自己实现RSA。知道RSA的大概原理将会对我们写代码有帮助,不然你出了问题自己都搞不定,咋办?

二、使用RSA实现加密解密的思路:

说起思路比较简单。其实就是浏览器向服务器拿到公钥,在用户填完信息后,用公钥帮用户加密,然后提交后,服务器再用java解密就可以了…….吗?本来大体思路是这样没错,可是我找到的方法并没有办法直接传输可用的公钥,所以我们要用到上述的公式,先把e和n取出来,利用js把e和n还原成公钥,然后再用他给信息加密后提交,最后就是用服务器上的java解密。

三、具体实施方法:

说了那么多,福利呢干货呢别人写好的代码呢?没错我就知道你想要这个。

一开始我也是大概搞清楚RSA之后开始各种找代码来参考一下,于是在我不懈努力之下,让我找到了这个《用javascript与java进行RSA加密与解密》,总体还是可用的,而且总体思路和我一致,但是用起来问题还是多多的,你们可以先点开链接看看代码。

四、javascript里面的RSA:

首先你需要三个js

BigInt.js  – 用于生成一个大整型;(这是RSA算法的需要)
RSA.js    – RSA的主要算法;
Barrett.js – RSA算法所需要用到的一个支持文件;

像上面提到的,你拥有公钥的n和e就能还原公钥,然后进行加密。关键代码如下:

//setMaxDigits()貌似是生成密文的最大位数,如果不设置或者乱设置的话很可能导致死循环然后浏览器崩溃。
//这个语句必须最先执行,1024位的密钥要传入130,2048的话要传入260
setMaxDigits(130);
//RSAKeyPair是密钥对的对象,用e和n可以生成公钥,第二个参数其实就是d,因为我们只需要公钥当然是传空的。
key&nbsp;=&nbsp;new &nbsp;RSAKeyPair(e,"",n);
//下面是加密方法,比较简单,就是传入公钥和原文,加密成密文。
var result = encryptedString(key, document.getElementById("pwd").value);
 

e和n哪里来?你可以先用着链接里面的来test一下,我认为正常来说应该是java读取了送到给js的,下面会讲到。

五、 java里面的RSA:

那java里面肯定也有RSA的相关类和API能用,我们需要些什么呢?我们需要的是JDK里面的java.security包和一个开源的加解密解决方案BouncyCastle的API。java.security自己import就可以,→bouncyCastle在这里←    要JDK15以上的,下最新的就可以,打不开或者找不到的就百度一下咯~

下面我们说说java里面的代码问题。

先来说生成密钥,利用generateKeyPair()和saveKey(),可以很简单的生成一对密钥,你可以直接存到相应路径,也可以像我一样改一下saveKey()把公钥私钥分开储存,甚至你可以直接保存在session,这样你的网站就永远在用别人猜不到的密钥了(我估计这样服务器工作量略大,还不如定时线程来更新密钥),下面是我修改过的代码

public static KeyPair generateKeyPair() throws Exception {
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider());
final int KEY_SIZE = 1024;// 块加密的大小,你可以改成2048,但是会很慢........
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
KeyPair keyPair = keyPairGen.generateKeyPair();
return keyPair;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}

再说取钥的问题,你可以使用getKeyPair()获取公钥和密钥,大概长这样(我同事帮忙修改了下,返回的object然后自自行强制转换即可)这里注意readObject其实和加密包会有关系。

     FileInputStream fis = new FileInputStream(path);
ObjectInputStream oos = new ObjectInputStream(fis);
Object kp= oos.readObject();
oos.close();
fis.close();
return kp;
}
//使用他来获取密钥,如果你这个路径对应的是公钥或者密钥,也可以直接取出来再强制转换成RSAPublicKey或者RSAPrivateKey
KeyPair kp=(KeyPair)getKey("D:/key.key");

我们读取到公钥后,需要用到RSAPublicKey的getModulus()和getPublicExponent()方法取得公钥的e(Exponent)和n(Modulus)给到前端页面,前端可以用getparameter等方法接收,或者在页面初始化时用ajax请求。

String Modulus=RSAPublicKey.getModulus().toString(16);
String Exponent=RSAPublicKey.getPublicExponent().toString(16);

这里需要toString(16)把他转为16进制,供前端使用。

最后是最重要的解密部分(加密和解密的写法极其相似,有需要的同学可以先看看解密),网上最简单而典型的写法是这样的(raw是密文)

public static byte[] RSAdecrypt(PrivateKey pk, byte[] raw) throws Exception {
Cipher cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
cipher.init(Cipher.DECRYPT_MODE, pk);
return cipher.doFinal(raw);
}

然后我在网上找到demo是这样的

  Cipher cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
  cipher.init(Cipher.DECRYPT_MODE, pk);
  ByteArrayOutputStream bout = null;
  try {
    bout = new ByteArrayOutputStream(64);
    int j = 0;
    int blockSize = cipher.getBlockSize();
    while (raw.length - j * blockSize > 0) {
      bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
      j++;
    }
    return bout.toByteArray();
  } //后面是catch和finally,对bout 的安全处理,就不贴了
}

这样看貌似第二个安全一点,不过我不是很懂为什么要写的这么复杂,测了下运行时间貌似差不多。求各位指导。

这里用到加密算法最核心的类Cipher类,我觉得有必要大概了解一下它,它的加密和解密过程用到的都是doFinal()方法,至于是加密还是解密就取决于init时候的参数Cliper的MODE。然后如果没有我说的BouncyCastle API你就要用 Cipher.getInstance(keyFactory.getAlgorithm())来实例化Clipher了,会麻烦一点。

到这里加密解密就已经完成了~~~然后接下来可能会遇到一些问题,请看↓

六、遇到问题了吧,说好的售后服务呢?

(1)首先再提一下setMaxDigits(),要注意里面的参数,1024位对应130,2048位对应260。

(2)然后是java里面关于解密时参数发送异常的问题:如果完全按照上述方法,或者是网上资料里面的做法,在大量使用的情况下就会出现以下报错。

如果你是用精简版的RSAdecrypt

org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
at org.bouncycastle.crypto.engines.RSACoreEngine.convertInput(Unknown Source)
at org.bouncycastle.crypto.engines.RSABlindedEngine.processBlock(Unknown Source)
at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)

如果你是用复杂版的RSAdecrypt

 at javax.crypto.Cipher.doFinal(Cipher.java:2141)
at com.dimeng.p2p.yylh.util.SecurityHelper.RSAdecrypt(SecurityHelper.java:236)
at com.dimeng.p2p.yylh.util.SecurityHelper.getdecryptStr(SecurityHelper.java:262)
at com.dimeng.p2p.yylh.util.SecurityHelper.main(SecurityHelper.java:342)
Exception in thread "main" java.lang.IllegalArgumentException: Bad arguments
at javax.crypto.Cipher.doFinal(Cipher.java:2141)

原因是网上资料里面的用法是这样的,问题就出在toByteArray()上面

//result是字符串类型的密文
byte[] en_result = new BigInteger(result, 16).toByteArray();
byte[] de_result = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(),en_result);

准确来说是因为js加密的时候会导致byte[]类型密文比指定的长,为什么呢?因为上面提到的三个JS在加密密码时,偶尔会得出正确的密文byte[]多出一byte,里面是0,不信等报错了你自己试试。解决方法如下:

public static byte[] hexStringToBytes(String hexString) {
  if (hexString == null || hexString.equals("")) {
    return null;
  }
  hexString = hexString.toUpperCase();
  int length = hexString.length() / 2;
  char[] hexChars = hexString.toCharArray();
  byte[] d = new byte[length];
  for (int i = 0; i < length; i++) {
    int pos = i * 2;
    d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
  }
  return d;
} /** * Convert char to byte * @param c char * @return byte */
private static byte charToByte(char c) {
  return (byte) "0123456789ABCDEF".indexOf(c);
}
//这样~就对了
byte[] en_result = hexStringToBytes(password_en);

至此出错问题就能解决了

RSA加密公钥系数获取结果多00的更多相关文章

  1. C# 与JAVA 的RSA 加密解密交互,互通,C#使用BouncyCastle来实现私钥加密,公钥解密的方法

    因为C#的RSA加密解密只有公钥加密,私钥解密,没有私钥加密,公钥解密.在网上查了很久也没有很好的实现.BouncyCastle的文档少之又少.很多人可能会说,C#也是可以的,通过Biginteger ...

  2. RSA不对称加密,公钥加密私钥解密,私钥加密公钥解密

    RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作. RSA是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一 ...

  3. RSA加密解密和读取公钥、私钥

    /// <summary>     /// RSA加密解密及RSA签名和验证    /// </summary>     public class RSADE    {    ...

  4. RSA 加密算法 Java 公钥加密私钥解密 和 私钥加密公钥解密 的特点

    package com.smt.cipher.unsymmetry; import org.apache.commons.codec.binary.Base64; import org.apache. ...

  5. 【转】iOS安全之RSA加密/生成公钥、秘钥 pem文件

    在iOS中使用RSA加密解密,需要用到.der和.p12后缀格式的文件,其中.der格式的文件存放的是公钥(Public key)用于加密,.p12格式的文件存放的是私钥(Private key)用于 ...

  6. 基于私钥加密公钥解密的RSA算法C#实现

    RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作. RSA是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一 ...

  7. 银联手机支付(.Net Csharp),3DES加密解密,RSA加密解密,RSA私钥加密公钥解密,.Net RSA 3DES C#

    前段时间做的银联支付,折腾了好久,拼凑的一些代码,有需要的朋友可以参考,本人.Net新手,不保证准确性! 这个银联手机支付没有SDK提供,技术支持也没有.Net的,真心不好搞! RSA加解密,这里有个 ...

  8. RSA加解密 私钥加密公钥解密 私加公解 && C++ 调用openssl库 的代码实例

    前提:秘钥长度=1024 ============================================== 对一片(117字节)明文加密  私加 ===================== ...

  9. RSA 加密 解密 公钥 私钥 签名 加签 验签

    http://blog.csdn.net/21aspnet/article/details/7249401# http://www.ruanyifeng.com/blog/2013/06/rsa_al ...

随机推荐

  1. JavaScript 之 节点操作

    一.文档树结构 DOM 可以将任何 HTML 或 XML 描绘成一个由多层节点构成的结构. 节点分为不同的类型,每种类型分别表示文档中不同的信息.每个节点都拥有各自的特点.数据和方法,另外也与其他节点 ...

  2. setsockopt()函数使用

    closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket BOOL bReuseaddr=TRUE; setsockopt (s,SOL_SOCKET ,SO ...

  3. 数据结构与算法17—B树(B、B+、B*)

    B树 B-树,就是B树,B树的原英文名是B-tree,所以很多翻译为B-树,就会很多人误以为B-树是一种树.B树是另外一种树.其实,B-tree就是B树. B-树的定义 B树(B-tree)是一种树状 ...

  4. Linux命令groupadd

    groupadd [选项] 组 创建一个新的组.Groupadd命令使用命令行中指定的值加上系统默认值创建新的组账户.新组将根据需要输入系统. (1).选项 -f,--force 如果指定的组已经存在 ...

  5. php + h5 实现socket推送技术

    在socket出现之前已经有ajax定时请求.长轮询等方案,但都不能满足需求,socket就应用而生了. socket基本函数socket 总结下常用的socket函数 服务端: socket_cre ...

  6. nmap使用流程

    ip为案列 利用ipconfig 测得本地ip为  192.00.00.100 利用-sn探测一批主机内的存活主机 存活主机ip为    192.000.00.1   192.000.00.100 使 ...

  7. Python中取整的方法floor,ceil,round

    地板函数:math.floor(4.9)=4 天花板函数: math.ceil(4.1)=5 四舍五入: round(4.5)=4 round(4.6)=5

  8. reactNative 获取组件高、宽、位置等信息

    import {findNodeHandle, UIManager} from 'react-native' layout(ref) { const handle = findNodeHandle(r ...

  9. 让iOS 开发更便捷-JSONConverter

    JSONConverter是MAC上iOS开发的辅助小工具,可以快速的把json数据转换生成对应的模型类属性,目前支持Objective-C.Swift以及目前流行的第三方库: SwiftyJSON. ...

  10. linux正则表达式与通配符练习

    ---恢复内容开始--- 1.删除tmp目录下的所有文件 cd  .. cd .. cd tmp rm -fr * 2.把etc目录下以p开头的文件和目录过滤出来 cd /etc/ ls p* (前一 ...