最近,在做接口开发,用户调用对外发布的接口,并参照接口参数规格,即可向服务器数据库插入记录。
实现了接口接本功能,即将接口收到的记录插入到数据库中,这中间涉及到认证和数据完整性(识别并丢弃被第三方截取和修改的数据)的问题,考虑使用RSA算法。RSA是一种算法。 RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。
RSA 加密或签名后的结果是不可读的二进制,使用时经常会转为 BASE64 码再传输。
RSA 加密时,对要加密数据的大小有限制,最大不大于密钥长度。例如在使用 1024 bit 的密钥时(genrsa -out rsa_private_key.pem 1024),最大可以加密 1024/8=128 Bytes 的数据。数据大于 128 Bytes 时,需要对数据进行分组加密(如果数据超限,加解密时会失败,openssl 函数会返回 false),分组加密后的加密串拼接成一个字符串后发送给客户端。如果 Padding 填充方式使用默认的 OPENSSL_PKCS1_PADDING(需要占用 11 个字节用于填充),那么明文长度最多只能就是 128-11=117 Bytes。
接收方解密时也需要分组。将加密后的原始二进制数据(对于经过 BASE64 的数据,需要解码),每 128 Bytes 分为一组,然后再进行解密。解密后的原文字符串拼接起来,就得到原始报文。
下面是我用Java实现的RSA算法,来实现对认证授权码的加密和对信息记录的数字签名,其中,为了便展示认证码密文和信息数字签名,我将每字节差分为两个16进制的字符,以下所有测试数据的长度是理论上的2倍。
在程序中我指定的1024 bit 的密钥,如果数据不超过密钥1024位,密文和签名为1024/=256,转换为16进制字符未512,经过测试生成的公钥长度:444,私钥的长度1700~1800之间,加密后为512。
下面是源程序,分享一下。
package RSA;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import org.apache.commons.collections.set.SynchronizedSet;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class CreateSecretKey {
public static final String KEY_ALGORITHM = "RSA";
private static final String PUBLIC_KEY = "RSAPublicKey";
private static final String PRIVATE_KEY = "RSAPrivateKey";
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
/**
* RSA最大加密明文大小
* */
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5','6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
//获得公钥字符串
public static String getPublicKeyStr(Map<String, Object> keyMap) throws Exception {
//获得map中的公钥对象 转为key对象
Key key = (Key) keyMap.get(PUBLIC_KEY);
//编码返回字符串
return bytesToHex(encryptBASE64(key.getEncoded()).getBytes());
}
//获得私钥字符串
public static String getPrivateKeyStr(Map<String, Object> keyMap) throws Exception {
//获得map中的私钥对象 转为key对象
Key key = (Key) keyMap.get(PRIVATE_KEY);
//编码返回字符串
return bytesToHex(encryptBASE64(key.getEncoded()).getBytes());
}
//获取公钥
public static PublicKey getPublicKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = (new BASE64Decoder()).decodeBuffer(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
//获取私钥
public static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = (new BASE64Decoder()).decodeBuffer(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
//解码返回byte
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
//编码返回字符串
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}
//***************************签名和验证*******************************
public static String sign(byte[] data, String privateKeyStr) throws Exception {
PrivateKey priK = getPrivateKey(new String(hexToBytes(privateKeyStr)));
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initSign(priK);
sig.update(data);
return bytesToHex(sig.sign());
}
public static boolean verify(byte[] data, String sign, String publicKeyStr) throws Exception {
PublicKey pubK = getPublicKey(new String(hexToBytes(publicKeyStr)));
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(pubK);
sig.update(data);
return sig.verify(hexToBytes(sign));
}
//************************加密解密**************************
public static String encrypt(byte[] plainText, String publicKeyStr) throws Exception {
PublicKey publicKey = getPublicKey(new String(hexToBytes(publicKeyStr)));
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = plainText.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
int i = 0;
byte[] cache;
while(inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK){
cache = cipher.doFinal(plainText, offSet, MAX_ENCRYPT_BLOCK);
} else{
cache = cipher.doFinal(plainText, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptText = out.toByteArray();
out.close();
return bytesToHex(encryptText);
}
public static String decrypt(String encryptTextHex, String privateKeyStr) throws Exception {
byte[] encryptText = hexToBytes(encryptTextHex);
PrivateKey privateKey = getPrivateKey(new String(hexToBytes(privateKeyStr)));
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
int inputLen = encryptText.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while(inputLen - offSet > 0){
if(inputLen - offSet > MAX_DECRYPT_BLOCK){
cache = cipher.doFinal(encryptText, offSet, MAX_DECRYPT_BLOCK);
} else{
cache = cipher.doFinal(encryptText, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] plainText = out.toByteArray();
out.close();
return new String(plainText);
}
public static Map<String, Object> initKey() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 将byte[]转换为16进制字符串
*/
public static String bytesToHex(byte[] bytes) {
//一个byte为8位,可用两个十六进制位标识
char[] buf = new char[bytes.length * 2];
int a = 0;
int index = 0;
for(byte b : bytes) { // 使用除与取余进行转换
if(b < 0) {
a = 256 + b;
} else {
a = b;
}
buf[index++] = HEX_CHAR[a / 16];
buf[index++] = HEX_CHAR[a % 16];
}
return new String(buf);
}
/**
* 将16进制字符串转换为byte[]
*
* @param str
* @return
*/
public static byte[] hexToBytes(String str) {
if(str == null || str.trim().equals("")) {
return new byte[0];
}
byte[] bytes = new byte[str.length() / 2];
for(int i = 0; i < str.length() / 2; i++) {
String subStr = str.substring(i * 2, i * 2 + 2);
bytes[i] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
public static void main(String[] args) throws Exception {
Map<String, Object> keyMap;
String cipherText;
String input = "10001eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
try {
keyMap = initKey();
String publicKey = getPublicKeyStr(keyMap);
System.out.println("公钥------------------");
System.out.println(publicKey);
System.out.println("length: " + publicKey.length());
String privateKey = getPrivateKeyStr(keyMap);
System.out.println("私钥------------------");
System.out.println(privateKey);
System.out.println("length: " + privateKey.length());
System.out.println("测试可行性-------------------");
System.out.println("明文=======" + input);
System.out.println("length: " + input.length());
cipherText = encrypt(input.getBytes(), publicKey);
//加密后的东西
System.out.println("密文=======" + cipherText);
System.out.println("length: " + cipherText.length());
//开始解密
String plainText = decrypt(cipherText, privateKey);
System.out.println("解密后明文===== " + new String(plainText));
System.out.println("验证签名-----------");
String str = "被签名的内容sssssssassssssssssssssssssssssssssssssssss";
System.out.println("\n原文:" + str);
String signature = sign(str.getBytes(), privateKey);
System.out.println(signature);
System.out.println(signature.length());
boolean status = verify(str.getBytes(), signature, publicKey);
System.out.println("验证情况:" + status);
} catch (Exception e) {
e.printStackTrace();
}
}
}
编译执行,如下: