Skip to content

Commit 24bb4e2

Browse files
committed
增加非对称加密RSA工具类
1 parent 77b89a0 commit 24bb4e2

File tree

4 files changed

+459
-6
lines changed

4 files changed

+459
-6
lines changed

app/src/main/java/com/fantasy/blogdemo/crypto/AsymmetricEncryptActivity.java

+116-2
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,34 @@
33
import android.content.Context;
44
import android.content.Intent;
55
import android.os.Bundle;
6+
import android.text.TextUtils;
7+
import android.util.Log;
68
import android.view.View;
9+
import android.widget.EditText;
10+
import android.widget.RadioGroup;
711
import android.widget.TextView;
812

13+
import com.fantasy.blogdemo.Constant;
914
import com.fantasy.blogdemo.R;
1015
import com.fantasy.blogdemo.base.BaseActivity;
16+
import com.fantasy.blogdemo.crypto.utils.RSAUtils;
1117

1218
/**
1319
* 非对称加密
1420
* <pre>
1521
* author : Fantasy
16-
* version : 1.0, 2019-08-20
17-
* since : 1.0, 2019-08-20
22+
* version : 1.0, 2019-08-25
23+
* since : 1.0, 2019-08-25
1824
* </pre>
1925
*/
2026
public class AsymmetricEncryptActivity extends BaseActivity implements View.OnClickListener {
27+
private EditText mEtPublicKey;
28+
private EditText mEtPrivateKey;
29+
private EditText mEtData;
30+
private EditText mEtResult;
31+
private int mOutputModeCheckedId;
32+
private String mTransformation;
33+
2134
/**
2235
* 打开“非对称加密”模块
2336
*
@@ -32,11 +45,39 @@ protected void onCreate(Bundle savedInstanceState) {
3245
super.onCreate(savedInstanceState);
3346
setContentView(R.layout.activity_crypto_asymmetric);
3447
bindEvent();
48+
initData();
3549
}
3650

3751
private void bindEvent() {
3852
((TextView) findViewById(R.id.tv_title_bar_title)).setText(R.string.crypto_title_asymmetric);
3953
findViewById(R.id.iv_title_bar_back).setOnClickListener(this);
54+
mEtPublicKey = findViewById(R.id.et_crypto_public_key);
55+
mEtPrivateKey = findViewById(R.id.et_crypto_private_key);
56+
mEtData = findViewById(R.id.et_crypto_data);
57+
mEtResult = findViewById(R.id.et_crypto_result);
58+
findViewById(R.id.btn_crypto_rsa_encrypt).setOnClickListener(this);
59+
findViewById(R.id.btn_crypto_rsa_decrypt).setOnClickListener(this);
60+
((RadioGroup) findViewById(R.id.rg_crypto_output_mode)).setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
61+
@Override
62+
public void onCheckedChanged(RadioGroup group, int checkedId) {
63+
mOutputModeCheckedId = checkedId;
64+
}
65+
});
66+
}
67+
68+
private void initData() {
69+
mOutputModeCheckedId = R.id.rb_crypto_base64;
70+
mTransformation = "RSA/None/PKCS1Padding";
71+
72+
//String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYHGvdORdwsK5i+s9rKaMPL1O5eDK2XwNHRUWaxmGB/cxLxeinJrrqdAN+mME7XtGN9bklnOR3MUBQLVnWIn/IU0pnIJY9DpPTVc7x+1zFb8UUq1N0BBo/NpUG5olxuQULuAAHZOg28pnP/Pcb5XVEvpNKL0HaWjN8pu/Dzf8gZwIDAQAB";
73+
//String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJgca905F3CwrmL6z2spow8vU7l4MrZfA0dFRZrGYYH9zEvF6Kcmuup0A36YwTte0Y31uSWc5HcxQFAtWdYif8hTSmcglj0Ok9NVzvH7XMVvxRSrU3QEGj82lQbmiXG5BQu4AAdk6Dbymc/89xvldUS+k0ovQdpaM3ym78PN/yBnAgMBAAECgYAFdX+pgNMGiFC53KZ1AhmIAfrPPTEUunQzqpjE5Tm6oJEkZwXiedFbeK5nbLQCnXSH07nBT9AjNvFH71i6BqLvT1l3/ezPq9pmRPriHfWQQ3/J3ASf1O9F9CkYbq/s/qqkXEFcl8PdYQV0xU/kS4jZPP+60Lv3sPkLg2DpkhM+AQJBANTl+/v6sBqqQSS0Anl5nE15Ck3XGBcq0nvATHfFkJYtG9rrXz3ZoRATLxF1iJYwGSAtirhev9W7qFayjci0ztcCQQC25/kkFbeMEWT6/kyV8wcPIog1mKy8RVB9+2l6C8AzbWBPZYtLlB7uaGSJeZBTEGfvRYzpFm5xO0JqwCfDddjxAkBmxtgM3wqg9MwaAeSn6/Nu2x4EUfBJTtzp7P19XJzeQsyNtM73ttYwQnKYhRr5FiMrC5FKTENj1QIBSJV17QNlAkAL5cUAAuWgl9UQuo/yxQ81fdKMYfUCfiPBPiRbSv5imf/Eyl8oOGdWrLW1d5HaxVttZgHHe60NcoRce0la3oSRAkAe8OqLsm9ryXNvBtZxSG+1JUvePVxpRSlJdZIAUKxN6XQE0S9aEe/IkNDBgVeiUEtop76R2NkkGtGTwzbzl0gm";
74+
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhSzPPnFn41iaz+t4tI4kbaXNuNFOsI8hFeCYtlwPFKRbETHbBS10bMvUbOWLFtRgZV3L924GQ9orbomEmJ1nWyaSO8iBbZAyiWUP5PJJh/b9kHj1MMwG712bGfYYPdjkRprNpzU9w4UBzUMKKUoHU4c/Gbb4XeBK9LNTPWQL4YwIDAQAB";
75+
String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOFLM8+cWfjWJrP63i0jiRtpc240U6wjyEV4Ji2XA8UpFsRMdsFLXRsy9Rs5YsW1GBlXcv3bgZD2ituiYSYnWdbJpI7yIFtkDKJZQ/k8kmH9v2QePUwzAbvXZsZ9hg92ORGms2nNT3DhQHNQwopSgdThz8Ztvhd4Er0s1M9ZAvhjAgMBAAECgYEAxwNLTUXsJGfn4Gzm/jC52MEZ+mu2zgT90IAGGZeg+PUG63gwHyeXo4MsCVRz7/m8xAX/ykew+IEQwFt8Pdvc+rrs5yml4gOBPfhpau5QaI75xNjnyH7UA3mbRCZeyZrvuKqtY/f8pCgzy3EBWnRpkcsqeE6bsOQrD45mltr+0QECQQDynvhKEh+hD5xBpF/DIP8Fp6fizexHdA6+aZT/gLaFA4XgZ9HEDDBhvNdadyYUNOLWhkxRHv6CkT5azfLXsJEhAkEA7begtbBCDXDf1+DRh3j2S8zcv6+utYgcpjvxZqjbPi6UIWXLxI80PIwQ0uouHCUMjikBA6VX9vTbw9TZ/IelAwJBAKI3W7baiz86mrTg3A4w/5GeWQexuurDVCBHo5F5U493nYk+oOe9ZpPSmQIpa9JS0d+xB1GtsWlHBzPbQySnL0ECQA/btCjqvT1QTl6EbPXwp92eqQtQmQMbNW4RiaUjlpyrVs5zkAho1T9EyMqJPNI71n6VVa/8k8WxyAdkZ7ZlBikCQEkNe1+sAKnh+AFGCJ+6WAq1J2RuIgcA6bVL3ip7F2NHdE+N+tR9JqWw3JNCweWmAlzKIGs6eKSVD5egzKaLXss=";
76+
mEtPublicKey.setText(publicKey);
77+
mEtPublicKey.setSelection(mEtPublicKey.length());
78+
mEtPrivateKey.setText(privateKey);
79+
mEtPrivateKey.setSelection(mEtPrivateKey.length());
80+
mEtData.requestFocus();
4081
}
4182

4283
@Override
@@ -45,8 +86,81 @@ public void onClick(View v) {
4586
case R.id.iv_title_bar_back:
4687
finish();
4788
break;
89+
case R.id.btn_crypto_rsa_encrypt: // RSA加密(公钥)
90+
String key = mEtPublicKey.getText().toString();
91+
String data = mEtData.getText().toString();
92+
String result;
93+
if (checkPublicKey() && checkData()) {
94+
if (mOutputModeCheckedId == R.id.rb_crypto_base64) {
95+
result = RSAUtils.encryptBase64(data, key, 1024, mTransformation);
96+
} else {
97+
result = RSAUtils.encryptHex(data, key, 1024, mTransformation);
98+
}
99+
mEtResult.setText(result);
100+
Log.d(Constant.TAG, "publicKey : " + key + "\ndata : " + data + "\nresult : " + result);
101+
}
102+
break;
103+
case R.id.btn_crypto_rsa_decrypt: // RSA解密(私钥)
104+
key = mEtPrivateKey.getText().toString();
105+
data = mEtData.getText().toString();
106+
if (checkPrivateKey() && checkData()) {
107+
if (mOutputModeCheckedId == R.id.rb_crypto_base64) {
108+
result = RSAUtils.decryptBase64(data, key, 1024, mTransformation);
109+
} else {
110+
result = RSAUtils.decryptHex(data, key, 1024, mTransformation);
111+
}
112+
mEtResult.setText(result);
113+
Log.d(Constant.TAG, "privateKey : " + key + "\ndata : " + data + "\nresult : " + result);
114+
}
115+
break;
48116
default:
49117
break;
50118
}
51119
}
120+
121+
/**
122+
* 检查输入的公钥
123+
*
124+
* @return 如果符合输入规则,则返回true;不符合则返回false
125+
*/
126+
private boolean checkPublicKey() {
127+
if (TextUtils.isEmpty(mEtPublicKey.getText().toString())) {
128+
mEtPublicKey.requestFocus();
129+
mEtResult.setText("");
130+
showToast(R.string.crypto_public_key_hint);
131+
return false;
132+
}
133+
return true;
134+
}
135+
136+
/**
137+
* 检查输入的私钥
138+
*
139+
* @return 如果符合输入规则,则返回true;不符合则返回false
140+
*/
141+
private boolean checkPrivateKey() {
142+
if (TextUtils.isEmpty(mEtPrivateKey.getText().toString())) {
143+
mEtPrivateKey.requestFocus();
144+
mEtResult.setText("");
145+
showToast(R.string.crypto_private_key_hint);
146+
return false;
147+
}
148+
return true;
149+
}
150+
151+
/**
152+
* 检查输入的待加密或待解密的数据
153+
*
154+
* @return 如果符合输入规则,则返回true;不符合则返回false
155+
*/
156+
private boolean checkData() {
157+
if (TextUtils.isEmpty(mEtData.getText().toString())) {
158+
mEtData.requestFocus();
159+
mEtResult.setText("");
160+
showToast(R.string.crypto_data_hint);
161+
return false;
162+
}
163+
return true;
164+
}
165+
52166
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package com.fantasy.blogdemo.crypto.utils;
2+
3+
import android.util.Base64;
4+
5+
import com.fantasy.blogdemo.utils.ConvertUtils;
6+
7+
import java.security.Key;
8+
import java.security.KeyFactory;
9+
import java.security.spec.PKCS8EncodedKeySpec;
10+
import java.security.spec.X509EncodedKeySpec;
11+
12+
import javax.crypto.Cipher;
13+
14+
/**
15+
* RSA加解密工具类
16+
* <pre>
17+
* author : Fantasy
18+
* version : 1.0, 2019-08-25
19+
* since : 1.0, 2019-08-25
20+
* </pre>
21+
*/
22+
public class RSAUtils {
23+
private static final String CHARSET = "UTF-8";
24+
25+
/**
26+
* 加密,输出Base64字符串密文
27+
*
28+
* @param data 明文
29+
* @param publicKey 公钥
30+
* @param keySize 公钥大小,举例:1024, 2048...
31+
* @param transformation 类型,格式为:加密算法/加密模式/填充方式,举例:<i>RSA/None/PKCS1Padding</i>。<br/>
32+
* 相关取值可以查看下列两个文档:
33+
* <ul>
34+
* <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
35+
* 中的 javax.crypto.Cipher</li>
36+
* <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
37+
* Standard Algorithm Name Documentation</a></li>
38+
* </ul>
39+
* @return 密文
40+
*/
41+
public static String encryptBase64(String data, String publicKey, int keySize, String transformation) {
42+
try {
43+
return Base64.encodeToString(handle(data.getBytes(CHARSET), Base64.decode(publicKey, Base64.NO_WRAP),
44+
keySize, transformation, true), Base64.NO_WRAP);
45+
} catch (Exception e) {
46+
e.printStackTrace();
47+
}
48+
return null;
49+
}
50+
51+
/**
52+
* 解密,密文为Base64字符串
53+
*
54+
* @param data 密文
55+
* @param privateKey 私钥
56+
* @param keySize 私钥大小,举例:1024, 2048...
57+
* @param transformation 类型,格式为:加密算法/加密模式/填充方式,举例:<i>RSA/None/PKCS1Padding</i>。<br/>
58+
* 相关取值可以查看下列两个文档:
59+
* <ul>
60+
* <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
61+
* 中的 javax.crypto.Cipher</li>
62+
* <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
63+
* Standard Algorithm Name Documentation</a></li>
64+
* </ul>
65+
* @return 明文
66+
*/
67+
public static String decryptBase64(String data, String privateKey, int keySize, String transformation) {
68+
try {
69+
return new String(handle(Base64.decode(data, Base64.NO_WRAP), Base64.decode(privateKey, Base64.NO_WRAP),
70+
keySize, transformation, false), CHARSET);
71+
} catch (Exception e) {
72+
e.printStackTrace();
73+
}
74+
return null;
75+
}
76+
77+
/**
78+
* 加密,输出十六进制字符串密文
79+
*
80+
* @param data 明文
81+
* @param publicKey 公钥
82+
* @param keySize 公钥大小,举例:1024, 2048...
83+
* @param transformation 类型,格式为:加密算法/加密模式/填充方式,举例:<i>RSA/None/PKCS1Padding</i>。<br/>
84+
* 相关取值可以查看下列两个文档:
85+
* <ul>
86+
* <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
87+
* 中的 javax.crypto.Cipher</li>
88+
* <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
89+
* Standard Algorithm Name Documentation</a></li>
90+
* </ul>
91+
* @return 密文
92+
*/
93+
public static String encryptHex(String data, String publicKey, int keySize, String transformation) {
94+
try {
95+
return ConvertUtils.bytesToHexString(handle(data.getBytes(CHARSET), Base64.decode(publicKey, Base64.NO_WRAP),
96+
keySize, transformation, true));
97+
} catch (Exception e) {
98+
e.printStackTrace();
99+
}
100+
return null;
101+
}
102+
103+
/**
104+
* 解密,密文为十六进制字符串
105+
*
106+
* @param data 密文
107+
* @param privateKey 私钥
108+
* @param keySize 私钥大小,举例:1024, 2048...
109+
* @param transformation 类型,格式为:加密算法/加密模式/填充方式,举例:<i>RSA/None/PKCS1Padding</i>。<br/>
110+
* 相关取值可以查看下列两个文档:
111+
* <ul>
112+
* <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
113+
* 中的 javax.crypto.Cipher</li>
114+
* <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
115+
* Standard Algorithm Name Documentation</a></li>
116+
* </ul>
117+
* @return 明文
118+
*/
119+
public static String decryptHex(String data, String privateKey, int keySize, String transformation) {
120+
try {
121+
return new String(handle(ConvertUtils.hexStringToBytes(data), Base64.decode(privateKey, Base64.NO_WRAP),
122+
keySize, transformation, false), CHARSET);
123+
} catch (Exception e) {
124+
e.printStackTrace();
125+
}
126+
return null;
127+
}
128+
129+
/**
130+
* 处理数据,加密或解密
131+
*
132+
* @param data 数据
133+
* @param key 密钥
134+
* @param keySize 密钥大小,举例:1024, 2048...
135+
* @param transformation 类型,格式为:加密算法/加密模式/填充方式,举例:<i>RSA/None/PKCS1Padding</i>。<br/>
136+
* 相关取值可以查看下列两个文档:
137+
* <ul>
138+
* <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
139+
* 中的 javax.crypto.Cipher</li>
140+
* <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
141+
* Standard Algorithm Name Documentation</a></li>
142+
* </ul>
143+
* @param isEncrypt 如果是加密,则为true;如果为解密,则为false
144+
* @return 加密后或解密后的字节数组
145+
* @throws Exception 异常
146+
*/
147+
private static byte[] handle(byte[] data, byte[] key, int keySize, String transformation,
148+
boolean isEncrypt) throws Exception {
149+
Key rsaKey;
150+
if (isEncrypt) {
151+
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);
152+
rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);
153+
} else {
154+
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);
155+
rsaKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
156+
}
157+
Cipher cipher = Cipher.getInstance(transformation);
158+
cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, rsaKey);
159+
int len = data.length;
160+
int maxLen = keySize / 8;
161+
if (isEncrypt) {
162+
String lowerTrans = transformation.toLowerCase();
163+
if (lowerTrans.endsWith("pkcs1padding")) {
164+
maxLen -= 11;
165+
}
166+
}
167+
int count = len / maxLen;
168+
if (count > 0) {
169+
byte[] ret = new byte[0];
170+
byte[] buff = new byte[maxLen];
171+
int index = 0;
172+
for (int i = 0; i < count; i++) {
173+
System.arraycopy(data, index, buff, 0, maxLen);
174+
ret = joins(ret, cipher.doFinal(buff));
175+
index += maxLen;
176+
}
177+
if (index != len) {
178+
int restLen = len - index;
179+
buff = new byte[restLen];
180+
System.arraycopy(data, index, buff, 0, restLen);
181+
ret = joins(ret, cipher.doFinal(buff));
182+
}
183+
return ret;
184+
} else {
185+
return cipher.doFinal(data);
186+
}
187+
}
188+
189+
/**
190+
* 合并两个字节数组
191+
*
192+
* @param prefix 前一个字节数组
193+
* @param suffix 后一个字节数组
194+
* @return 字节数组
195+
*/
196+
private static byte[] joins(byte[] prefix, byte[] suffix) {
197+
byte[] ret = new byte[prefix.length + suffix.length];
198+
System.arraycopy(prefix, 0, ret, 0, prefix.length);
199+
System.arraycopy(suffix, 0, ret, prefix.length, suffix.length);
200+
return ret;
201+
}
202+
203+
}

0 commit comments

Comments
 (0)