Android 中 RSA介面加密以及解密
1.RSA簡介
RSA是一種非對稱加密演算法,加密和解密使用不同的金鑰。通訊雙方各握有一對金鑰(稱為公鑰和私鑰)中的一把,己方金鑰加密的資料,只有對方金鑰能夠解密。RSA基於一個數論事實:將兩個大素數相乘十分容易,但想要對其乘積進行因式分解卻極其困 難,因此可以將乘積公開作為加密金鑰,即公鑰,而兩個大素數組合成私鑰。公鑰是可提供給任何人使用,私鑰則為自己所有,供解密之用。
一般在Android中使用RSA加密都是用來介面資料加密和資料的解密 , 我們可以在請求介面時把傳送到後臺的資料進行加密, 然後後臺根據你加密的祕鑰來進行解密 。而後臺返回重要資料的介面也是這樣相反的 後臺進行資料加密 我們根據後臺提供的祕鑰進行解密,這樣可以達到一定程度上的資料安全性,即使抓包得到的資料也是不可見的。
下面演示一組本地的資料加密
先上效果圖
我們可以看到上面的加密狀態 採用對稱加密方法 公鑰加密和私鑰解密 私鑰加密和公鑰解密的方式 來對資料進行加密
2.Android中進行加密示例
1.此工具類為加密解密工具類 裡面包含公鑰加密和私鑰解密 私鑰加密和公鑰解密2種形式 可直接進行使用
public class RSAUtils { public static final String RSA = "RSA";// 非對稱加密金鑰演算法 public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//加密填充方式 public static final int DEFAULT_KEY_SIZE = 2048;//祕鑰預設長度 public static final byte[] DEFAULT_SPLIT = "#PART#".getBytes(); // 當要加密的內容超過bufferSize,則採用partSplit進行分塊加密 public static final int DEFAULT_BUFFERSIZE = (DEFAULT_KEY_SIZE / 8) - 11;// 當前祕鑰支援加密的最大位元組數 /** * 隨機生成RSA金鑰對 * * @param keyLength 金鑰長度,範圍:512~2048 * 一般1024 * @return */ public static KeyPair generateRSAKeyPair(int keyLength) { try { KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA); kpg.initialize(keyLength); return kpg.genKeyPair(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } } /** * 用公鑰對字串進行加密 * * @param data 原文 */ public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception { // 得到公鑰 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey); KeyFactory kf = KeyFactory.getInstance(RSA); PublicKey keyPublic = kf.generatePublic(keySpec); // 加密資料 Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING); cp.init(Cipher.ENCRYPT_MODE, keyPublic); return cp.doFinal(data); } /** * 私鑰加密 * * @param data 待加密資料 * @param privateKey 金鑰 * @return byte[] 加密資料 */ public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception { // 得到私鑰 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey); KeyFactory kf = KeyFactory.getInstance(RSA); PrivateKey keyPrivate = kf.generatePrivate(keySpec); // 資料加密 Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING); cipher.init(Cipher.ENCRYPT_MODE, keyPrivate); return cipher.doFinal(data); } /** * 公鑰解密 * * @param data 待解密資料 * @param publicKey 金鑰 * @return byte[] 解密資料 */ public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) throws Exception { // 得到公鑰 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey); KeyFactory kf = KeyFactory.getInstance(RSA); PublicKey keyPublic = kf.generatePublic(keySpec); // 資料解密 Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING); cipher.init(Cipher.DECRYPT_MODE, keyPublic); return cipher.doFinal(data); } /** * 使用私鑰進行解密 */ public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception { // 得到私鑰 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey); KeyFactory kf = KeyFactory.getInstance(RSA); PrivateKey keyPrivate = kf.generatePrivate(keySpec); // 解密資料 Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING); cp.init(Cipher.DECRYPT_MODE, keyPrivate); byte[] arr = cp.doFinal(encrypted); return arr; } /** * 用公鑰對字串進行分段加密 * */ public static byte[] encryptByPublicKeyForSpilt(byte[] data, byte[] publicKey) throws Exception { int dataLen = data.length; if (dataLen <= DEFAULT_BUFFERSIZE) { return encryptByPublicKey(data, publicKey); } List<Byte> allBytes = new ArrayList<Byte>(2048); int bufIndex = 0; int subDataLoop = 0; byte[] buf = new byte[DEFAULT_BUFFERSIZE]; for (int i = 0; i < dataLen; i++) { buf[bufIndex] = data[i]; if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) { subDataLoop++; if (subDataLoop != 1) { for (byte b : DEFAULT_SPLIT) { allBytes.add(b); } } byte[] encryptBytes = encryptByPublicKey(buf, publicKey); for (byte b : encryptBytes) { allBytes.add(b); } bufIndex = 0; if (i == dataLen - 1) { buf = null; } else { buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)]; } } } byte[] bytes = new byte[allBytes.size()]; { int i = 0; for (Byte b : allBytes) { bytes[i++] = b.byteValue(); } } return bytes; } /** * 分段加密 * * @param data 要加密的原始資料 * @param privateKey 祕鑰 */ public static byte[] encryptByPrivateKeyForSpilt(byte[] data, byte[] privateKey) throws Exception { int dataLen = data.length; if (dataLen <= DEFAULT_BUFFERSIZE) { return encryptByPrivateKey(data, privateKey); } List<Byte> allBytes = new ArrayList<Byte>(2048); int bufIndex = 0; int subDataLoop = 0; byte[] buf = new byte[DEFAULT_BUFFERSIZE]; for (int i = 0; i < dataLen; i++) { buf[bufIndex] = data[i]; if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) { subDataLoop++; if (subDataLoop != 1) { for (byte b : DEFAULT_SPLIT) { allBytes.add(b); } } byte[] encryptBytes = encryptByPrivateKey(buf, privateKey); for (byte b : encryptBytes) { allBytes.add(b); } bufIndex = 0; if (i == dataLen - 1) { buf = null; } else { buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)]; } } } byte[] bytes = new byte[allBytes.size()]; { int i = 0; for (Byte b : allBytes) { bytes[i++] = b.byteValue(); } } return bytes; } /** * 公鑰分段解密 * * @param encrypted 待解密資料 * @param publicKey 金鑰 */ public static byte[] decryptByPublicKeyForSpilt(byte[] encrypted, byte[] publicKey) throws Exception { int splitLen = DEFAULT_SPLIT.length; if (splitLen <= 0) { return decryptByPublicKey(encrypted, publicKey); } int dataLen = encrypted.length; List<Byte> allBytes = new ArrayList<Byte>(1024); int latestStartIndex = 0; for (int i = 0; i < dataLen; i++) { byte bt = encrypted[i]; boolean isMatchSplit = false; if (i == dataLen - 1) { // 到data的最後了 byte[] part = new byte[dataLen - latestStartIndex]; System.arraycopy(encrypted, latestStartIndex, part, 0, part.length); byte[] decryptPart = decryptByPublicKey(part, publicKey); for (byte b : decryptPart) { allBytes.add(b); } latestStartIndex = i + splitLen; i = latestStartIndex - 1; } else if (bt == DEFAULT_SPLIT[0]) { // 這個是以split[0]開頭 if (splitLen > 1) { if (i + splitLen < dataLen) { // 沒有超出data的範圍 for (int j = 1; j < splitLen; j++) { if (DEFAULT_SPLIT[j] != encrypted[i + j]) { break; } if (j == splitLen - 1) { // 驗證到split的最後一位,都沒有break,則表明已經確認是split段 isMatchSplit = true; } } } } else { // split只有一位,則已經匹配了 isMatchSplit = true; } } if (isMatchSplit) { byte[] part = new byte[i - latestStartIndex]; System.arraycopy(encrypted, latestStartIndex, part, 0, part.length); byte[] decryptPart = decryptByPublicKey(part, publicKey); for (byte b : decryptPart) { allBytes.add(b); } latestStartIndex = i + splitLen; i = latestStartIndex - 1; } } byte[] bytes = new byte[allBytes.size()]; { int i = 0; for (Byte b : allBytes) { bytes[i++] = b.byteValue(); } } return bytes; } /** * 使用私鑰分段解密 * */ public static byte[] decryptByPrivateKeyForSpilt(byte[] encrypted, byte[] privateKey) throws Exception { int splitLen = DEFAULT_SPLIT.length; if (splitLen <= 0) { return decryptByPrivateKey(encrypted, privateKey); } int dataLen = encrypted.length; List<Byte> allBytes = new ArrayList<Byte>(1024); int latestStartIndex = 0; for (int i = 0; i < dataLen; i++) { byte bt = encrypted[i]; boolean isMatchSplit = false; if (i == dataLen - 1) { // 到data的最後了 byte[] part = new byte[dataLen - latestStartIndex]; System.arraycopy(encrypted, latestStartIndex, part, 0, part.length); byte[] decryptPart = decryptByPrivateKey(part, privateKey); for (byte b : decryptPart) { allBytes.add(b); } latestStartIndex = i + splitLen; i = latestStartIndex - 1; } else if (bt == DEFAULT_SPLIT[0]) { // 這個是以split[0]開頭 if (splitLen > 1) { if (i + splitLen < dataLen) { // 沒有超出data的範圍 for (int j = 1; j < splitLen; j++) { if (DEFAULT_SPLIT[j] != encrypted[i + j]) { break; } if (j == splitLen - 1) { // 驗證到split的最後一位,都沒有break,則表明已經確認是split段 isMatchSplit = true; } } } } else { // split只有一位,則已經匹配了 isMatchSplit = true; } } if (isMatchSplit) { byte[] part = new byte[i - latestStartIndex]; System.arraycopy(encrypted, latestStartIndex, part, 0, part.length); byte[] decryptPart = decryptByPrivateKey(part, privateKey); for (byte b : decryptPart) { allBytes.add(b); } latestStartIndex = i + splitLen; i = latestStartIndex - 1; } } byte[] bytes = new byte[allBytes.size()]; { int i = 0; for (Byte b : allBytes) { bytes[i++] = b.byteValue(); } } return bytes; } }
本地進行字串加密測試 首先獲取生成祕鑰的方法 獲取公鑰和私鑰
text = "測試加密 RSA";
keyPair = RSAUtils.generateRSAKeyPair(RSAUtils.DEFAULT_KEY_SIZE);
// 公鑰
publicKey = (RSAPublicKey) keyPair.getPublic();
// 私鑰
privateKey = (RSAPrivateKey) keyPair.getPrivate();
公鑰加密 獲取加密後的資料
//公鑰加密 byte[] encryptBytes = new byte[0]; try { encryptBytes = RSAUtils.encryptByPublicKeyForSpilt(text.getBytes(), publicKey.getEncoded()); } catch (Exception e) { e.printStackTrace(); } encryStr = Base64.encode(encryptBytes); Log.d("RSA", "加密後的資料:" + encryStr);
利用公鑰加密後對稱的私鑰進行解密 結果請看效果圖
//私鑰解密
byte[] decryptBytes = new byte[0];
try {
decryptBytes = RSAUtils.decryptByPrivateKeyForSpilt(Base64.decode(encryStr), privateKey.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
String decryStr = new String(decryptBytes);
Log.d("RSA", "解密後的資料:" + decryStr);
私鑰加密 獲取加密後的資料
//私鑰加密
byte[] encryptBytes2 = new byte[0];
try {
encryptBytes2 = RSAUtils.encryptByPrivateKeyForSpilt(text.getBytes(), privateKey.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
encryStr = Base64.encode(encryptBytes2);
Log.d("RSA", "加密後的資料:" + encryStr);
利用私鑰加密後對稱的公鑰進行解密 結果請看效果圖
//公鑰解密
byte[] decryptBytes2 = new byte[0];
try {
decryptBytes2 = RSAUtils.decryptByPublicKeyForSpilt(Base64.decode(encryStr), publicKey.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
decryStr = new String(decryptBytes2);
Log.d("RSA", "解密後的資料:" + decryStr);
其中用到Base64工具類最好不用系統的 我們可以自己進行自定義實現
public class Base64 {
private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.toCharArray();
public static String encode(byte[] data) {
int start = 0;
int len = data.length;
StringBuffer buf = new StringBuffer(data.length * 3 / 2);
int end = len - 3;
int i = start;
int n = 0;
while (i <= end) {
int d = ((((int) data[i]) & 0x0ff) << 16)
| ((((int) data[i + 1]) & 0x0ff) << 8)
| (((int) data[i + 2]) & 0x0ff);
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append(legalChars[(d >> 6) & 63]);
buf.append(legalChars[d & 63]);
i += 3;
if (n++ >= 14) {
n = 0;
buf.append(" ");
}
}
if (i == start + len - 2) {
int d = ((((int) data[i]) & 0x0ff) << 16)
| ((((int) data[i + 1]) & 255) << 8);
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append(legalChars[(d >> 6) & 63]);
buf.append("=");
} else if (i == start + len - 1) {
int d = (((int) data[i]) & 0x0ff) << 16;
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append("==");
}
return buf.toString();
}
private static int decode(char c) {
if (c >= 'A' && c <= 'Z')
return ((int) c) - 65;
else if (c >= 'a' && c <= 'z')
return ((int) c) - 97 + 26;
else if (c >= '0' && c <= '9')
return ((int) c) - 48 + 26 + 26;
else
switch (c) {
case '+':
return 62;
case '/':
return 63;
case '=':
return 0;
default:
throw new RuntimeException("unexpected code: " + c);
}
}
/**
* Decodes the given Base64 encoded String to a new byte array. The byte
* array holding the decoded data is returned.
*/
public static byte[] decode(String s) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
decode(s, bos);
} catch (IOException e) {
throw new RuntimeException();
}
byte[] decodedBytes = bos.toByteArray();
try {
bos.close();
bos = null;
} catch (IOException ex) {
System.err.println("Error while decoding BASE64: " + ex.toString());
}
return decodedBytes;
}
private static void decode(String s, OutputStream os) throws IOException {
int i = 0;
int len = s.length();
while (true) {
while (i < len && s.charAt(i) <= ' ')
i++;
if (i == len)
break;
int tri = (decode(s.charAt(i)) << 18)
+ (decode(s.charAt(i + 1)) << 12)
+ (decode(s.charAt(i + 2)) << 6)
+ (decode(s.charAt(i + 3)));
os.write((tri >> 16) & 255);
if (s.charAt(i + 2) == '=')
break;
os.write((tri >> 8) & 255);
if (s.charAt(i + 3) == '=')
break;
os.write(tri & 255);
i += 4;
}
}
}
這樣我們就簡單完成了 公鑰加密私鑰解密 和私鑰加密 公鑰解密 兩種加密方式 本文只是個簡單的本地加密示例 具體和後臺除錯可進行部分參考
採用RSA進行加密,遇到了坑,記錄一下
1.base64工具類Android自帶工具類是不包含部分方法的 需要自定義 可參考本文Base64工具類
2.加密填充方式不能跟後臺一樣 需要替換 這點比較坑
/**
* Android 加密填充方式
*/
public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";
/**
* java 後端的加密演算法RSA
*/
public static final String KEY_ALGORITHM = "RSA";