关于RSA数字签名(Android)
近期项目中考虑到数据比较重要,担心与服务器交互过程中被篡改,所以引用了数据签名的技术。
什么是RSA:
RSA是一种非对称的加密算法,RSA的加密解密中有一对密钥(公钥和私钥),通过公钥加密的密文只能通过对应的私钥解密,反之亦然。
我自己的理解:
在数据交互中,必须同时知道密钥对和被加密的字符串(我们暂时先称为密签)的生成规则,才能造出合法的数据。下面假设我们的客户端代码被反编译,获取到了储存在我们本地的密钥对,我们分两种情况进行分析。
客户端请求服务器的数据
加密的密钥得到了,但是没有我们密签的生成规则,造不出合法的请求数据。
服务器响应客户端的数据
通过解密的私钥解密服务器返回的sign值,获取到了服务器响应数据的密签生成规则,但是加密响应数据的加密密钥是保存在服务器的,相对比较安全,所以也造不出合法的响应数据。
所以在这套加密体系中,请求数据的密签和响应数据的密签的生成规则应该是不同的。
思路:
在整个交互中一共有两对密钥,一对用于服务器的加密(公钥A)和客户端的解密(私钥A),另一对用于服务器的解密(私钥B)和客户端的加密(公钥B)。在客户端请求时,将请求数据通过RSA加密后连带要请求的数据一起发送给服务器,服务器收到请求后,首先通过对应的密钥对请求中加密的数据进行解密,然后对比解密后的内容与请求的内容是否一致,若一致,则可以判定数据是合法的,反之则是不合法的。服务器在响应客户端请求时,使用相同的方法。(Ps:请求的数据一般都是键值对,我们通过与服务器协商,使用相同的规则对数据进行拼接,组成一个字符串后进行加密)
大概流程:
这里假设我们的拼接规则是:键=值&键=值&…
注意:
在加解密过程中,openssl生成的密钥对无法使用。(有大神发现问题希望指出)这里附上一对可用的钥匙。
—–BEGIN PUBLIC KEY—–
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBJq3d4HJscWh44l/Vh431rbKZ
XxdR+gRSgvINmCuHTlgasRsvKiPgtBtRsj/mzpowuxCOg67xkBQvIoXVs1wiJNuQ
Zi+mVXUQCJelLn5ykCLvNqkugMO9nNtre/2VezjP81t5FeOlEnQMo3AFLSeZv2cc
I9V0DD0IsnIlRH/L8wIDAQAB
—–END PUBLIC KEY—–—–BEGIN PRIVATE KEY—–
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMEmrd3gcmxxaHji
X9WHjfWtsplfF1H6BFKC8g2YK4dOWBqxGy8qI+C0G1GyP+bOmjC7EI6DrvGQFC8i
hdWzXCIk25BmL6ZVdRAIl6UufnKQIu82qS6Aw72c22t7/ZV7OM/zW3kV46USdAyj
cAUtJ5m/Zxwj1XQMPQiyciVEf8vzAgMBAAECgYBaJ7G0BNWj5HN9KTzOME2ExSS+
DfKWovptgQ12Zva6W0kofE5R/3troOW3hlnpY7n40PfzbWe0/SlOrvCFRQjVXbbR
9a+eZ0xynSuMg8m57q8qHZ/l5dV8ECWsEUC1eFHepWpMDygFv8qgLIV2C5Ot03HW
yGXLzoPJ+GD9U+yA8QJBAP2vKm6Js9wYvK/vtUpX7jScuHHFSZmimoyAz3sOIWZ2
XBKspc0XDzLlIAd6BbC5i9JfTqTNJE0Om20NsuOqOp0CQQDC6g25Go56AZvsXPBh
1I5L0Lj673sBx9wqvAUS6/MCeJT9jBUq8FRDi4KVlkWBzhDP5K+AKnADm40fAVi2
R9PPAkBl2pOVyBH/16rClOLcE4vfceEYRNb1KXUV3o3vYVAFvJXhJZJ4Ur7Wd2ox
tktcllLOR1fjET529dtvoKVnhSx9AkEAsOr9dcyTEk8vlkVts37Zght8K9j/j8g3
IVr0SA5+zvlgUxu5gvYNphmVd+2kPYQrYmgynGTos32XhFMWkK11pQJAFOMAzgSE
PggBQohD4jg7vOvZzFO8M4VDee7yaC5xFpJlJBtol1twlIbzv1HjAgHDeqXChStf
rLegWG4sbnXTMA==
—–END PRIVATE KEY—–
加密解密工具类的代码
package com.shsy.test.rsa;
import android.util.Base64;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
/**
* Created by Shsy on 15-11-27.
*/
public class RsaUtils {
/**
* 获取公钥
*
* @param algorithm
* @param bysKey
* @return
*/
private static PublicKey getPublicKeyFromX509(String algorithm,
String bysKey) throws NoSuchAlgorithmException, Exception {
byte[] decodedKey = Base64.decode(bysKey, Base64.DEFAULT);
X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
return keyFactory.generatePublic(x509);
}
/**
* 获取私钥
*
* @param key
* @return
* @throws Exception
*/
public static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = Base64.decode(key, Base64.DEFAULT);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
/**
* 使用公钥加密
*
* @param content
* @param publicKey
* @return
*/
public static String encryptByPublic(String content, String publicKey) {
try {
PublicKey pubkey = getPublicKeyFromX509("RSA", publicKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubkey);
byte plaintext[] = content.getBytes("UTF-8");
byte[] output = cipher.doFinal(plaintext);
String s = new String(Base64.encode(output, Base64.DEFAULT));
return s;
} catch (Exception e) {
return null;
}
}
/**
* 通过私钥解密
*
* @param content
* @param privateKey
* @return
*/
public static String decryptByPrivate(String content, String privateKey) {
try {
PrivateKey prikey = getPrivateKey(privateKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, prikey);
byte[] enBytes = Base64.decode(content, Base64.DEFAULT);
byte[] deBytes = cipher.doFinal(enBytes);
return new String(deBytes);
} catch (Exception e) {
return null;
}
}
}
使用样例
/**将上面的密钥复制到对应的""中*/
/**注意:密钥不包含被"-----BEGIN PUBLIC KEY-----"这样的头尾信息*/
/**加密的公钥*/
pivate final String public_Key="";
/**解密的私钥*/
pivate final String private_Key="";
/**要加密的内容*/
String s = "Test";
/**加密*/
String ss = RsaUtils.encryptByPublic(s,public_Key);
/**解密*/
String sss = RsaUtils.decryptByPrivate(ss,private_Key);