添加项目
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,30 @@
|
||||
apply plugin: 'com.android.library'
|
||||
android {
|
||||
compileSdkVersion rootProject.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.minSdkVersion
|
||||
targetSdkVersion rootProject.targetSdkVersion
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
// buildTypes {
|
||||
// release {
|
||||
// minifyEnabled false
|
||||
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
// }
|
||||
// }
|
||||
}
|
||||
task makeJar(type: Copy) {
|
||||
delete 'build/libs/rsalibrary.jar' //删除已经存在的jar包
|
||||
from('build/intermediates/bundles/debug/')//从该目录下加载要打包的文件
|
||||
into('build/libs/')//jar包的保存目录
|
||||
include('classes.jar')//设置过滤,只打包classes文件
|
||||
rename('classes.jar', 'rsalibrary.jar')//重命名,mylibrary.jar 根据自己的需求设置
|
||||
}
|
||||
makeJar.dependsOn(build)
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Users\liqy\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.baileren.rsalibrary;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumentation test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest {
|
||||
@Test public void useAppContext() throws Exception {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.baileren.rsalibrary.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
|
||||
package="com.baileren.rsalibrary"
|
||||
>
|
||||
|
||||
<application android:allowBackup="true"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,222 @@
|
||||
package com.baileren.rsalibrary;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* Author :nanfeifei
|
||||
* DATA : 2017-12-05
|
||||
* DES :
|
||||
* Android端的加密思路需要4步:
|
||||
* 1.生成AES密钥;
|
||||
* 2.使用RSA公钥加密刚刚生成的AES密钥;
|
||||
* 3.再使用第1步生成的AES密钥,通过AES加密需要提交给服务端的数据;
|
||||
* 4.将第2与第3生成的内容传给服务端。
|
||||
*
|
||||
* JAVA服务端的解密思路只需3步:
|
||||
* 1.获取到客户端传过来的AES密钥密文和内容密文;
|
||||
* 2.使用RSA私钥解密从客户端拿到的AES密钥密文;
|
||||
* 3.再使用第2步解密出来的明文密钥,通过AES解密内容的密文。
|
||||
*/
|
||||
|
||||
public class AESCipherStrategy {
|
||||
// /** 算法/模式/填充 **/ECB模式可改为CBC或者更安全,
|
||||
private static final String CipherMode = "AES/ECB/ISO10126Padding";
|
||||
// private static final String CipherMode = "AES";
|
||||
|
||||
/**
|
||||
* 生成一个AES密钥对象
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static SecretKeySpec generateKey() {
|
||||
try {
|
||||
KeyGenerator kgen = KeyGenerator.getInstance("AES");
|
||||
kgen.init(128, new SecureRandom()); //256的长度更佳
|
||||
SecretKey secretKey = kgen.generateKey();
|
||||
byte[] enCodeFormat = secretKey.getEncoded();
|
||||
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
|
||||
return key;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个AES密钥字符串
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String generateKeyString() {
|
||||
return byte2hex(generateKey().getEncoded());
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密字节数据
|
||||
*
|
||||
* @param content
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static byte[] encrypt(byte[] content, byte[] key) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(CipherMode);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
|
||||
byte[] result = cipher.doFinal(content);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过byte[]类型的密钥加密String
|
||||
*
|
||||
* @param content
|
||||
* @param key
|
||||
* @return 16进制密文字符串
|
||||
*/
|
||||
public static String encrypt(String content, byte[] key) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(CipherMode);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
|
||||
byte[] data = cipher.doFinal(content.getBytes("UTF-8"));
|
||||
String result = byte2hex(data);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过String类型的密钥加密String
|
||||
*
|
||||
* @param content
|
||||
* @param key
|
||||
* @return 16进制密文字符串
|
||||
*/
|
||||
public static String encrypt(String content, String key) {
|
||||
byte[] data = null;
|
||||
try {
|
||||
data = content.getBytes("UTF-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
data = encrypt(data, key.getBytes());
|
||||
String result = Base64.encode(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过byte[]类型的密钥解密byte[]
|
||||
*
|
||||
* @param content
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static byte[] decrypt(byte[] content, byte[] key) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(CipherMode);
|
||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
|
||||
byte[] result = cipher.doFinal(content);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过String类型的密钥 解密String类型的密文
|
||||
*
|
||||
* @param content
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String decrypt(String content, String key) {
|
||||
byte[] data = null;
|
||||
try {
|
||||
data = Base64.decode(content);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
data = decrypt(data,key.getBytes());
|
||||
if (data == null)
|
||||
return null;
|
||||
String result = null;
|
||||
try {
|
||||
result = new String(data, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过byte[]类型的密钥 解密String类型的密文
|
||||
*
|
||||
* @param content
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String decrypt(String content, byte[] key) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(CipherMode);
|
||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
|
||||
byte[] data = cipher.doFinal(hex2byte(content));
|
||||
return new String(data, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转成16进制字符串
|
||||
*
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public static String byte2hex(byte[] b) { // 一个字节的数,
|
||||
StringBuffer sb = new StringBuffer(b.length * 2);
|
||||
String tmp = "";
|
||||
for (int n = 0; n < b.length; n++) {
|
||||
// 整数转成十六进制表示
|
||||
tmp = (Integer.toHexString(b[n] & 0XFF));
|
||||
if (tmp.length() == 1) {
|
||||
sb.append("0");
|
||||
}
|
||||
sb.append(tmp);
|
||||
}
|
||||
return sb.toString().toUpperCase(); // 转成大写
|
||||
}
|
||||
|
||||
/**
|
||||
* 将hex字符串转换成字节数组
|
||||
*
|
||||
* @param inputString
|
||||
* @return
|
||||
*/
|
||||
private static byte[] hex2byte(String inputString) {
|
||||
if (inputString == null || inputString.length() < 2) {
|
||||
return new byte[0];
|
||||
}
|
||||
inputString = inputString.toLowerCase();
|
||||
int l = inputString.length() / 2;
|
||||
byte[] result = new byte[l];
|
||||
for (int i = 0; i < l; ++i) {
|
||||
String tmp = inputString.substring(2 * i, 2 * i + 2);
|
||||
result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package com.baileren.rsalibrary;
|
||||
|
||||
final class Base64 {
|
||||
|
||||
private static final int BASELENGTH = 128;
|
||||
private static final int LOOKUPLENGTH = 64;
|
||||
private static final int TWENTYFOURBITGROUP = 24;
|
||||
private static final int EIGHTBIT = 8;
|
||||
private static final int SIXTEENBIT = 16;
|
||||
private static final int FOURBYTE = 4;
|
||||
private static final int SIGN = -128;
|
||||
private static char PAD = '=';
|
||||
private static byte[] base64Alphabet = new byte[BASELENGTH];
|
||||
private static char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < BASELENGTH; ++i) {
|
||||
base64Alphabet[i] = -1;
|
||||
}
|
||||
for (int i = 'Z'; i >= 'A'; i--) {
|
||||
base64Alphabet[i] = (byte) (i - 'A');
|
||||
}
|
||||
for (int i = 'z'; i >= 'a'; i--) {
|
||||
base64Alphabet[i] = (byte) (i - 'a' + 26);
|
||||
}
|
||||
|
||||
for (int i = '9'; i >= '0'; i--) {
|
||||
base64Alphabet[i] = (byte) (i - '0' + 52);
|
||||
}
|
||||
|
||||
base64Alphabet['+'] = 62;
|
||||
base64Alphabet['/'] = 63;
|
||||
|
||||
for (int i = 0; i <= 25; i++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('A' + i);
|
||||
}
|
||||
|
||||
for (int i = 26, j = 0; i <= 51; i++, j++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('a' + j);
|
||||
}
|
||||
|
||||
for (int i = 52, j = 0; i <= 61; i++, j++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('0' + j);
|
||||
}
|
||||
lookUpBase64Alphabet[62] = (char) '+';
|
||||
lookUpBase64Alphabet[63] = (char) '/';
|
||||
}
|
||||
|
||||
private static boolean isWhiteSpace(char octect) {
|
||||
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
|
||||
}
|
||||
|
||||
private static boolean isPad(char octect) {
|
||||
return (octect == PAD);
|
||||
}
|
||||
|
||||
private static boolean isData(char octect) {
|
||||
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes hex octects into Base64
|
||||
*
|
||||
* @param binaryData Array containing binaryData
|
||||
* @return Encoded Base64 array
|
||||
*/
|
||||
static String encode(byte[] binaryData) {
|
||||
|
||||
if (binaryData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int lengthDataBits = binaryData.length * EIGHTBIT;
|
||||
if (lengthDataBits == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
|
||||
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
|
||||
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
|
||||
char encodedData[] = null;
|
||||
|
||||
encodedData = new char[numberQuartet * 4];
|
||||
|
||||
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
|
||||
|
||||
int encodedIndex = 0;
|
||||
int dataIndex = 0;
|
||||
|
||||
for (int i = 0; i < numberTriplets; i++) {
|
||||
b1 = binaryData[dataIndex++];
|
||||
b2 = binaryData[dataIndex++];
|
||||
b3 = binaryData[dataIndex++];
|
||||
|
||||
l = (byte) (b2 & 0x0f);
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
|
||||
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
|
||||
}
|
||||
|
||||
// form integral number of 6-bit groups
|
||||
if (fewerThan24bits == EIGHTBIT) {
|
||||
b1 = binaryData[dataIndex];
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
} else if (fewerThan24bits == SIXTEENBIT) {
|
||||
b1 = binaryData[dataIndex];
|
||||
b2 = binaryData[dataIndex + 1];
|
||||
l = (byte) (b2 & 0x0f);
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
}
|
||||
return new String(encodedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Base64 data into octects
|
||||
*
|
||||
* @param encoded string containing Base64 data
|
||||
* @return Array containind decoded data.
|
||||
*/
|
||||
static byte[] decode(String encoded) {
|
||||
|
||||
if (encoded == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
char[] base64Data = encoded.toCharArray();
|
||||
// remove white spaces
|
||||
int len = removeWhiteSpace(base64Data);
|
||||
|
||||
if (len % FOURBYTE != 0) {
|
||||
return null;// should be divisible by four
|
||||
}
|
||||
|
||||
int numberQuadruple = (len / FOURBYTE);
|
||||
|
||||
if (numberQuadruple == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
byte decodedData[] = null;
|
||||
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
|
||||
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
|
||||
|
||||
int i = 0;
|
||||
int encodedIndex = 0;
|
||||
int dataIndex = 0;
|
||||
decodedData = new byte[(numberQuadruple) * 3];
|
||||
|
||||
for (; i < numberQuadruple - 1; i++) {
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++]))
|
||||
|| !isData((d2 = base64Data[dataIndex++]))
|
||||
|| !isData((d3 = base64Data[dataIndex++]))
|
||||
|| !isData((d4 = base64Data[dataIndex++]))) {
|
||||
return null;
|
||||
}// if found "no data" just return null
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||
}
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
|
||||
return null;// if found "no data" just return null
|
||||
}
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
|
||||
d3 = base64Data[dataIndex++];
|
||||
d4 = base64Data[dataIndex++];
|
||||
if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
|
||||
if (isPad(d3) && isPad(d4)) {
|
||||
if ((b2 & 0xf) != 0)// last 4 bits should be zero
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] tmp = new byte[i * 3 + 1];
|
||||
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
|
||||
return tmp;
|
||||
} else if (!isPad(d3) && isPad(d4)) {
|
||||
b3 = base64Alphabet[d3];
|
||||
if ((b3 & 0x3) != 0)// last 2 bits should be zero
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] tmp = new byte[i * 3 + 2];
|
||||
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
return tmp;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else { // No PAD e.g 3cQl
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||
}
|
||||
|
||||
return decodedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove WhiteSpace from MIME containing encoded Base64 data.
|
||||
*
|
||||
* @param data the byte array of base64 data (with WS)
|
||||
* @return the new length
|
||||
*/
|
||||
private static int removeWhiteSpace(char[] data) {
|
||||
if (data == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// count characters that's not whitespace
|
||||
int newSize = 0;
|
||||
int len = data.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!isWhiteSpace(data[i])) {
|
||||
data[newSize++] = data[i];
|
||||
}
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2015 Rocko (http://rocko.xyz) <rocko.zxp@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baileren.rsalibrary;
|
||||
|
||||
|
||||
/**
|
||||
* 加解密处理的抽象接口
|
||||
*/
|
||||
public abstract class CipherStrategy {
|
||||
|
||||
public final static String CHARSET = "UTF-8";
|
||||
|
||||
/**
|
||||
* 将加密内容的 Base64 编码转换为二进制内容
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public byte[] decodeConvert(String str) {
|
||||
return Base64.decode(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对加密后的二进制结果转换为 Base64 编码
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public String encodeConvert(byte[] bytes) {
|
||||
return Base64.encode(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对字符串进行加密
|
||||
*
|
||||
* @param content 需要加密的字符串
|
||||
* @return
|
||||
*/
|
||||
public abstract String encrypt(String content);
|
||||
|
||||
/**
|
||||
* 对字符串进行解密
|
||||
*
|
||||
* @param encryptContent 加密内容的 Base64 编码
|
||||
* @return
|
||||
*/
|
||||
public abstract String decrypt(String encryptContent);
|
||||
/**
|
||||
* 对字符串进行解密
|
||||
*
|
||||
* @param encryptContent 加密内容的 Base64 编码
|
||||
* @return
|
||||
*/
|
||||
public abstract byte[] decryptByte(String encryptContent);
|
||||
/**
|
||||
* 对字符串进行解密
|
||||
*
|
||||
* @param encryptContent 加密内容的 Base64 编码
|
||||
* @return
|
||||
*/
|
||||
public abstract String decryptStr(String encryptContent, String charsetName);
|
||||
// 文件、流等
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2015 Rocko (http://rocko.xyz) <rocko.zxp@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baileren.rsalibrary;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
public class RSACipherStrategy extends CipherStrategy {
|
||||
|
||||
private PublicKey mPublicKey;
|
||||
private PrivateKey mPrivateKey;
|
||||
|
||||
public void initPublicKey(String publicKeyContentStr) {
|
||||
try {
|
||||
mPublicKey = RSAUtils.loadPublicKey(publicKeyContentStr);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void initPublicKey(InputStream publicKeyIs) {
|
||||
try {
|
||||
mPublicKey = RSAUtils.loadPublicKey(publicKeyIs);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void initPrivateKey(String privateKeyContentStr) {
|
||||
try {
|
||||
mPrivateKey = RSAUtils.loadPrivateKey(privateKeyContentStr);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void initPrivateKey(InputStream privateIs) {
|
||||
try {
|
||||
mPrivateKey = RSAUtils.loadPrivateKey(privateIs);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String content) {
|
||||
if (mPublicKey == null) {
|
||||
throw new NullPointerException("PublicKey is null, please init it first");
|
||||
}
|
||||
byte[] encryptByte = RSAUtils.encryptData(content.getBytes(), mPublicKey);
|
||||
|
||||
return encodeConvert(encryptByte);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String encryptContent) {
|
||||
if (mPrivateKey == null) {
|
||||
throw new NullPointerException("PrivateKey is null, please init it first");
|
||||
}
|
||||
String string = "";
|
||||
byte[] encryptByte = decodeConvert(encryptContent);
|
||||
byte[] decryptByte = RSAUtils.decryptData(encryptByte, mPrivateKey);
|
||||
try {
|
||||
string = new String(decryptByte,"utf-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override public String decryptStr(String encryptContent, String charsetName) {
|
||||
if (mPrivateKey == null) {
|
||||
throw new NullPointerException("PrivateKey is null, please init it first");
|
||||
}
|
||||
String string = "";
|
||||
byte[] encryptByte = decodeConvert(encryptContent);
|
||||
byte[] decryptByte = RSAUtils.decryptData(encryptByte, mPrivateKey);
|
||||
try {
|
||||
string = new String(decryptByte,charsetName);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return string;
|
||||
}
|
||||
@Override public byte[] decryptByte(String encryptContent) {
|
||||
if (mPrivateKey == null) {
|
||||
throw new NullPointerException("PrivateKey is null, please init it first");
|
||||
}
|
||||
byte[] encryptByte = decodeConvert(encryptContent);
|
||||
byte[] decryptByte = RSAUtils.decryptData(encryptByte, mPrivateKey);
|
||||
return decryptByte;
|
||||
}
|
||||
/**
|
||||
* 加密
|
||||
*/
|
||||
public String encrypt(String publicKey, String sourceContent) {
|
||||
// rsa 公钥加密
|
||||
initPublicKey(publicKey);
|
||||
return encrypt(sourceContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*/
|
||||
public String decrypt(String privateKey, String sourceContent) {
|
||||
// rsa 私钥解密
|
||||
initPrivateKey(privateKey);
|
||||
|
||||
return decrypt(sourceContent);
|
||||
}
|
||||
/**
|
||||
* 解密
|
||||
*/
|
||||
public byte[] decryptByte(String privateKey, String sourceContent) {
|
||||
// rsa 私钥解密
|
||||
initPrivateKey(privateKey);
|
||||
|
||||
return decryptByte(sourceContent);
|
||||
}
|
||||
/**
|
||||
* 解密
|
||||
*/
|
||||
public String decrypt(String privateKey, String sourceContent, String charsetName) {
|
||||
// rsa 私钥解密
|
||||
initPrivateKey(privateKey);
|
||||
|
||||
return decryptStr(sourceContent, charsetName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright 2015 Rocko (http://rocko.xyz) <rocko.zxp@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baileren.rsalibrary;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
/**
|
||||
* @author nanfeifei
|
||||
* @date 2017年8月22日 下午1:44:23
|
||||
*/
|
||||
public final class RSAUtils
|
||||
{
|
||||
private final static String KEY_PAIR = "RSA";
|
||||
private final static String CIPHER = "RSA/ECB/PKCS1Padding";
|
||||
|
||||
/**
|
||||
* 随机生成RSA密钥对(默认密钥长度为1024)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static KeyPair generateRSAKeyPair()
|
||||
{
|
||||
return generateRSAKeyPair(1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成RSA密钥对
|
||||
*
|
||||
* @param keyLength
|
||||
* 密钥长度,范围:512~2048<br>
|
||||
* 一般1024
|
||||
* @return
|
||||
*/
|
||||
public static KeyPair generateRSAKeyPair(int keyLength)
|
||||
{
|
||||
try
|
||||
{
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_PAIR);
|
||||
kpg.initialize(keyLength);
|
||||
return kpg.genKeyPair();
|
||||
} catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用公钥加密 <br>
|
||||
* 每次加密的字节数,不能超过密钥的长度值减去11
|
||||
*
|
||||
* @param data
|
||||
* 需加密数据的byte数据
|
||||
* @param publicKey
|
||||
* 公钥
|
||||
* @return 加密后的byte型数据
|
||||
*/
|
||||
public static byte[] encryptData(byte[] data, PublicKey publicKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
Cipher cipher = Cipher.getInstance(CIPHER);
|
||||
// 编码前设定编码方式及密钥
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
// 传入编码数据并返回编码结果
|
||||
return cipher.doFinal(data);
|
||||
} catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用私钥解密
|
||||
*
|
||||
* @param encryptedData
|
||||
* 经过encryptedData()加密返回的byte数据
|
||||
* @param privateKey
|
||||
* 私钥
|
||||
* @return
|
||||
*/
|
||||
public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
Cipher cipher = Cipher.getInstance(CIPHER);
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
return cipher.doFinal(encryptedData);
|
||||
} catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过公钥byte[](publicKey.getEncoded())将公钥还原,适用于RSA算法
|
||||
*
|
||||
* @param keyBytes
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeySpecException
|
||||
*/
|
||||
public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException,
|
||||
InvalidKeySpecException
|
||||
{
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR);
|
||||
PublicKey publicKey = keyFactory.generatePublic(keySpec);
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过私钥byte[]将公钥还原,适用于RSA算法
|
||||
*
|
||||
* @param keyBytes
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeySpecException
|
||||
*/
|
||||
public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException,
|
||||
InvalidKeySpecException
|
||||
{
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR);
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用N、e值还原公钥
|
||||
*
|
||||
* @param modulus
|
||||
* @param publicExponent
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeySpecException
|
||||
*/
|
||||
public static PublicKey getPublicKey(String modulus, String publicExponent)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException
|
||||
{
|
||||
BigInteger bigIntModulus = new BigInteger(modulus);
|
||||
BigInteger bigIntPrivateExponent = new BigInteger(publicExponent);
|
||||
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR);
|
||||
PublicKey publicKey = keyFactory.generatePublic(keySpec);
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用N、d值还原私钥
|
||||
*
|
||||
* @param modulus
|
||||
* @param privateExponent
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeySpecException
|
||||
*/
|
||||
public static PrivateKey getPrivateKey(String modulus, String privateExponent)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException
|
||||
{
|
||||
BigInteger bigIntModulus = new BigInteger(modulus);
|
||||
BigInteger bigIntPrivateExponent = new BigInteger(privateExponent);
|
||||
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR);
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中加载公钥
|
||||
*
|
||||
* @param publicKeyStr
|
||||
* 公钥数据字符串
|
||||
* @throws Exception
|
||||
* 加载公钥时产生的异常
|
||||
*/
|
||||
public static PublicKey loadPublicKey(String publicKeyStr) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] buffer = Base64.decode(publicKeyStr);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR);
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
|
||||
return keyFactory.generatePublic(keySpec);
|
||||
} catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
throw new Exception("无此算法");
|
||||
} catch (InvalidKeySpecException e)
|
||||
{
|
||||
throw new Exception("公钥非法");
|
||||
} catch (NullPointerException e)
|
||||
{
|
||||
throw new Exception("公钥数据为空");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中加载私钥<br>
|
||||
* 加载时使用的是PKCS8EncodedKeySpec(PKCS#8编码的Key指令)。
|
||||
*
|
||||
* @param privateKeyStr
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] buffer = Base64.decode(privateKeyStr);
|
||||
// X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR);
|
||||
return keyFactory.generatePrivate(keySpec);
|
||||
} catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
throw new Exception("无此算法");
|
||||
} catch (InvalidKeySpecException e)
|
||||
{
|
||||
throw new Exception("私钥非法");
|
||||
} catch (NullPointerException e)
|
||||
{
|
||||
throw new Exception("私钥数据为空");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中输入流中加载公钥
|
||||
*
|
||||
* @param in
|
||||
* 公钥输入流
|
||||
* @throws Exception
|
||||
* 加载公钥时产生的异常
|
||||
*/
|
||||
public static PublicKey loadPublicKey(InputStream in) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
return loadPublicKey(readKey(in));
|
||||
} catch (IOException e)
|
||||
{
|
||||
throw new Exception("公钥数据流读取错误");
|
||||
} catch (NullPointerException e)
|
||||
{
|
||||
throw new Exception("公钥输入流为空");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中加载私钥
|
||||
*
|
||||
* @param
|
||||
* @return 是否成功
|
||||
* @throws Exception
|
||||
*/
|
||||
public static PrivateKey loadPrivateKey(InputStream in) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
return loadPrivateKey(readKey(in));
|
||||
} catch (IOException e)
|
||||
{
|
||||
throw new Exception("私钥数据读取错误");
|
||||
} catch (NullPointerException e)
|
||||
{
|
||||
throw new Exception("私钥输入流为空");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取密钥信息
|
||||
* --------------------
|
||||
* CONTENT
|
||||
* --------------------
|
||||
* @param in
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private static String readKey(InputStream in) throws IOException
|
||||
{
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(in));
|
||||
String readLine = null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while ((readLine = br.readLine()) != null)
|
||||
{
|
||||
if (readLine.charAt(0) == '-')
|
||||
{
|
||||
continue;
|
||||
} else
|
||||
{
|
||||
sb.append(readLine);
|
||||
sb.append('\r');
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">RSALibrary</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baileren.rsalibrary;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
RSACipherStrategy A = new RSACipherStrategy();
|
||||
String aa = A.encrypt(
|
||||
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmZfR/bA9X3vp86y1aEpvwzXJYKRRF1fLau2+05/ZtaITLpV8bhkmSf3neSy/Q9gAdvG75Fr73E+GWE+K5b0BpvIS1jDGo319+PpZR39SaZTKZ27XFXrosmJTZutN79t819HS1VseleunHAFgMVufE9U5jP6LGzl/wbkSy01GhzwIDAQAB",
|
||||
"Aa123456");
|
||||
System.out.print(aa);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user