一、简介:
RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它。RSA是第一个比较完善的公开密钥算法,它既能用于加密,也能用于数字签名。这个算法经受住了多年深入的密码分析,虽然密码分析者既不能证明也不能否定RSA的安全性,但这恰恰说明该算法有一定的可信性,目前它已经成为最流行的公开密钥算法。
二、RSA的公钥、私钥的组成,以及加密、解密的公式可见于下表
三、使用方式:
① 假设A、B机器进行通信,已A机器为主;
② A首先需要用自己的私钥为发送请求数据签名,并将公钥一同发送给B;
③ B收到数据后,需要用A发送的公钥进行验证,已确保收到的数据是未经篡改的;
④ B验签通过后,处理逻辑,并把处理结果返回,返回数据需要用A发送的公钥进行加密(公钥加密后,只能用配对的私钥解密);
⑤ A收到B返回的数据,使用私钥解密,至此,一次数据交互完成。
四、代码示例:
第一步获取私钥,为签名做准备。
/*** 读取私钥 返回PrivateKey
*@parampath 包含私钥的证书路径
*@parampassword 私钥证书密码
*@return返回私钥PrivateKey
*@throwsKeyStoreException
*@throwsNoSuchAlgorithmException
*@throwsCertificateException
*@throwsIOException
*@throwsUnrecoverableKeyException*/
private staticPrivateKey getPrivateKey(String path,String password)throwsKeyStoreException, NoSuchAlgorithmException, CertificateException,
IOException, UnrecoverableKeyException {
KeyStore ks= KeyStore.getInstance("PKCS12");
FileInputStream fis= newFileInputStream(path);char[] nPassword = null;if ((password == null) || password.trim().equals("")) {
nPassword= null;
}else{
nPassword=password.toCharArray();
}
ks.load(fis, nPassword);
fis.close();
Enumeration en =ks.aliases();
String keyAlias= null;if(en.hasMoreElements()) {
keyAlias=(String) en.nextElement();
}return(PrivateKey) ks.getKey(keyAlias, nPassword);
}
签名示例 通过第一步得到的私钥,进行签名操作,具体请看以下代码:
/*** 私钥签名: 签名方法如下:BASE64(RSA(MD5(src),privatekey)),其中src为需要签名的字符串,
privatekey是商户的CFCA证书私钥。
*@paramplainText 待签名字符串
*@parampath 签名私钥路径
*@parampassword 签名私钥密码
*@return返回签名后的字符串
*@throwsException*/
public staticString sign(String plainText,String path,String password)throwsException {/** MD5加密*/MessageDigest md5= MessageDigest.getInstance("MD5");
md5.update(plainText.getBytes("utf-8"));byte[] digestBytes =md5.digest();/** 用私钥进行签名 RSA
* Cipher负责完成加密或解密工作,基于RSA*/Cipher cipher= Cipher.getInstance("RSA/ECB/PKCS1Padding");//ENCRYPT_MODE表示为加密模式
cipher.init(Cipher.ENCRYPT_MODE, getPrivateKey(path, password));//加密
byte[] rsaBytes =cipher.doFinal(digestBytes);//Base64编码
return Base64.byteArrayToBase64(rsaBytes);
B收到数据后,需要使用A提供的公钥信息进行验签,此处使用公钥的N、E进行验签
首先通过公钥N、E得到公钥PublicKey,如下:
/*** 根据公钥n、e生成公钥
*@parammodulus 公钥n串
*@parampublicExponent 公钥e串
*@return返回公钥PublicKey
*@throwsException*/
public staticPublicKey getPublickKey(String modulus, String publicExponent)throwsException {
KeySpec publicKeySpec= newRSAPublicKeySpec(new BigInteger(modulus, 16), new BigInteger(publicExponent, 16));
KeyFactory factory= KeyFactory.getInstance("RSA");
PublicKey publicKey=factory.generatePublic(publicKeySpec);returnpublicKey;
}
得到公钥PublicKey后,再去验证签名,代码如下:
/*** 用公钥证书进行验签
*@parammessage 签名之前的原文
*@paramcipherText 签名
*@parampubKeyn 公钥n串
*@parampubKeye 公钥e串
*@returnboolean 验签成功为true,失败为false
*@throwsException*/
public static booleanverify(String message, String cipherText,String pubKeyn,
String pubKeye)throwsException {
Cipher c4= Cipher.getInstance("RSA/ECB/PKCS1Padding");//根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示解密模式
c4.init(Cipher.DECRYPT_MODE, getPublickKey(pubKeyn,pubKeye));//解密
byte[] desDecTextBytes =c4.doFinal(Base64.base64ToByteArray(cipherText));//得到前置对原文进行的MD5
String md5Digest1 =Base64.byteArrayToBase64(desDecTextBytes);
MessageDigest md5= MessageDigest.getInstance("MD5");
md5.update(message.getBytes("utf-8"));byte[] digestBytes =md5.digest();//得到商户对原文进行的MD5
String md5Digest2 =Base64.byteArrayToBase64(digestBytes);//验证签名
if(md5Digest1.equals(md5Digest2)) {return true;
}else{return false;
}
}
至此,签名验签已经完毕
提供一个从.cer文件读取公钥的方法:
/*** 读取公钥cer
*@parampath .cer文件的路径 如:c:/abc.cer
*@returnbase64后的公钥串
*@throwsIOException
*@throwsCertificateException*/
public static String getPublicKey(String path) throwsIOException,
CertificateException{
InputStream inStream= newFileInputStream(path);
ByteArrayOutputStream out= newByteArrayOutputStream();intch;
String res= "";while ((ch = inStream.read()) != -1) {
out.write(ch);
}byte[] result =out.toByteArray();
res=Base64.byteArrayToBase64(result);returnres;
}