今天我来说下如何使用RSA方式进行加密解密
一、生成公钥和私钥;
使用OpenSSL就可以,一般Linux和mac有自带的;windows的可自行安装;
通过如下命令生成;
注:
RSA非对称加密内容长度有限制,1024位key的最多只能加密127位数据,如果加密字符串过长请使用2048
momodeMBP:~ momo$ openssl genrsa -out rsa_private_key.pem 1024(去掉1024默认生成的是2048位)
Generating RSA private key, 1024 bit long modulus
.....++++++
............................++++++
e is 65537 (0x10001)
momodeMBP:~ momo$ openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem
momodeMBP:~ momo$ openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
writing RSA key
momodeMBP:~ momo$
第一条命令生成原始 RSA私钥文件 rsa_private_key.pem;
第二条命令将原始 RSA私钥转换为 pkcs8格式;
第三条生成RSA公钥 rsa_public_key.pem;
从上面看出通过私钥能生成对应的公钥,因此我们将私钥private_key.pem用在服务器端,rsa_public_key.pem给客户端ios、Android、外部合作方;
这里我们来说下服务器端的处理方式:
<?php
$private_key=file_get_contents('private_key.pem'); //读取私钥
$public_key=file_get_contents('rsa_public_key.pem'); //读取公钥
$pi_key = openssl_pkey_get_private($private_key);//这个函数可用来判断私钥是否是可用的,可用返回资源id Resource id
$pu_key = openssl_pkey_get_public($public_key);//这个函数可用来判断公钥是否是可用的
$data = 'method=medicool.user.detail&nonce_str=607673¶meters={"test":"2458"}&partnerid=test';//原始数据
echo "private key encrypt:\n";
openssl_private_encrypt($data,$encrypted,$pi_key);//私钥加密
$encrypted = base64_encode($encrypted);//加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
$encrypted=urlencode($encrypted);
echo $encrypted,"\n"; //输出私钥加密后的字符串数据
echo "public key decrypt:\n";
openssl_public_decrypt(base64_decode(urldecode($encrypted)),$decrypted,$pu_key);//私钥加密的内容通过公钥可用解密出来
echo $decrypted,"\n"; //通过公钥解密后的字符串数据
echo "---------------------------------------\n";
echo "public key encrypt:\n";
openssl_public_encrypt($data,$encrypted,$pu_key);//公钥加密
$encrypted = (base64_encode($encrypted));
$encrypted=urlencode($encrypted);
echo $encrypted,"\n";//通过公钥加密后的字符串数据
echo "private key decrypt:\n";
openssl_private_decrypt(base64_decode(urldecode($encrypted)),$decrypted,$pi_key);//私钥解密
echo $decrypted,"\n"; //通过私钥解密后的字符串数据
?>
下面是ios的对接demo:
//获得筛选标签
-(void)requesYikuData{
//随机生成6位数
int num = (arc4random() % 1000000);
NSString * randomNumber = [NSString stringWithFormat:@"%.6d", num];
NSLog(@"%@", randomNumber);
//遵循约定,生成签名
//JSON转换之后有回车空格以及中文转义问题 导致签名无法验证
//文字转义放在客户端执行
NSString * searchName ="头孢";//搜索名称
int page = 1;//页码
NSString *string =[NSString stringWithFormat:@"{\"cpage\":%d,\"keywords\":\"%@\"}",page,searchName];
//3.未加密签名组成字符串
NSString * str1 = [NSString stringWithFormat:@"method=medicool.drug.search&nonce_str=%@¶meters=%@&partnerid=test",randomNumber,string];
//4;对3字符串根据公钥RSA加密;
NSString *rsaStr = [RSAEncryptor encryptString:[str1 lowercaseString] publicKey:YIKUPUBLICKEY];
NSLog(@"RSA加密后字符串%@",rsaStr);
//5;对4结果进行base64_encode
NSData * encodeData = [rsaStr dataUsingEncoding:NSUTF8StringEncoding];
// encodeData = [encodeData base64EncodedDataWithOptions:0];
NSString *base64Str = [[NSString alloc] initWithData:encodeData encoding:NSUTF8StringEncoding];
//6;对5结果进行urlencode;
NSString * signStr = [RSAEncryptor encodeString:base64Str];
NSLog(@"urlencode后字符串%@",signStr);
//请求的url
NSString * urStr = @"http://extratest.cn/Apidrug/medisearch";
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes =[NSSet setWithObjects:@"text/html",@"text/plain",@"text/json",@"application/json",nil];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSDictionary* URLParameters = @{
@"cpage":@1,
@"keywords":@"头孢",
@"method":@"medicool.drug.search",
@"nonce_str":randomNumber,
@"partnerid":@"test",
@"sign":signStr,
};
NSMutableURLRequest* request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:urStr parameters:URLParameters error:NULL];
AFHTTPRequestOperation *operation = [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"HTTP Response Status Code: %ld", [operation.response statusCode]);
NSLog(@"HTTP Response Body: %@", responseObject);
NSDictionary *tempDictQueryDiamond = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil];
NSLog(@"%@",tempDictQueryDiamond);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"HTTP Request failed: %@", error);
}];
[manager.operationQueue addOperation:operation];
}
Android对接demo:
主项目
package medicool.com.medicool;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
import java.util.HashMap;
public class MainActivity extends AppCompatActivity {
private final String url = "http://extratest.meditool.cn/Apidrug/medisearch";
private final String search_method = "medicool.drug.search";
private SearchView searchView;
private TextView textView;
private int cpage = 1;
private String sign;
//POST数据
HashMap<String, String> args_jsondata = new HashMap<>();
//签名原数据
HashMap<String, Object> args_sign = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* method 请求方法名
nonce_str 随机字符串
partnerid 合作方指定字符串
sign 签名
cpage 请求页码
keywords 搜索关键词
*/
args_jsondata.put("method", search_method);
args_jsondata.put("nonce_str", "123456");
args_jsondata.put("partnerid", Sign.PARTNERID);
textView = (TextView) findViewById(R.id.textView);
searchView = (SearchView) findViewById(R.id.searchView);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
if ("".equals(query)) {
return false;
}
//这里注意下手动排序,即 医库合作约定
// 1;将业务请求参数名按照字母升序存入数组
args_sign.put("cpage", cpage);
args_sign.put("keywords", query.trim());
//对args_sign签名
try {
sign = Sign.getSign(MainActivity.this, search_method, "123456", args_sign);
} catch (Exception e) {
e.printStackTrace();
}
//参数补全
args_jsondata.put("sign", sign);
args_jsondata.put("cpage", cpage + "");
args_jsondata.put("keywords", query.trim());
new Thread(new Runnable() {
@Override
public void run() {
final String result = HttpUtils.getData(url, args_jsondata);
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(result);
}
});
}
}).start();
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
}
}
Sign类:
package medicool.com.medicool;
import android.content.Context;
import android.net.Uri;
import android.util.Base64;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class Sign {
//固定
public static final String PARTNERID = "medicoolgandan2017";
public static String getSign(Context context, String method, String nonce_str, HashMap<String, Object> parameters) throws Exception {
String sign;
JSONObject jsonObject = new JSONObject();
String j_parameters = "";
//对parameters进行json_encode处理
try {
if (parameters != null && !parameters.isEmpty()) {
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
jsonObject.put(entry.getKey(), entry.getValue());
}
j_parameters = jsonObject.toString();
}
} catch (JSONException e) {
e.printStackTrace();
}
//拼接参数method=$method&nonce_str=$nonce_str¶meters=$parameters&partnerid=$partnerid
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("method=").append(method + "&").append("nonce_str=").append(nonce_str + "&").append("parameters=").append(j_parameters + "&").append("partnerid=" + PARTNERID);
//RSA加密
byte[] bytes = RsaUtils.encryptData(stringBuffer.toString().getBytes(), context);
//base64_encodec
sign = Base64.encodeToString(bytes, Base64.DEFAULT);
//urlencode
sign = Uri.encode(sign);
return sign;
}
}
RsaUtils类:
package medicool.com.medicool;
import android.content.Context;
import android.util.Base64;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
public class RsaUtils {
private static String RSA = "RSA";
private static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";
public static byte[] encryptData(byte[] data, Context context) {
byte[] resultBytes = null;
try {
PublicKey publicKey = loadPublicKey(context);
Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
resultBytes = cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return resultBytes;
}
private static PublicKey loadPublicKey(Context context) throws Exception {
try {
PublicKey publicKey;
byte[] keyBytes = Base64.decode(readFile(context, "rsa_public_key.pem"), Base64.DEFAULT);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
} catch (Exception e) {
throw new Exception("密钥数据读取错误");
}
}
private static String readFile(Context context, String file) {
int len;
byte[] buf;
String grammar = "";
try {
InputStream in = context.getAssets().open(file);
len = in.available();
buf = new byte[len];
in.read(buf, 0, len);
grammar = new String(buf, "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
return grammar;
}
}
HttpUtils类:
package medicool.com.medicool;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class HttpUtils {
public static String getData(String url, HashMap<String, String> params) {
HttpPost post = new HttpPost(url);
HttpClient httpclient = new DefaultHttpClient();
httpclient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30000);
httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 30000);
ArrayList<NameValuePair> paramPairs = new ArrayList<>();
if (params != null && !params.isEmpty()) {
for (Map.Entry<String, String> entry : params.entrySet()) {
paramPairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
}
try {
post.setEntity(new UrlEncodedFormEntity(paramPairs, "UTF-8"));
} catch (UnsupportedEncodingException e) {
return "";
}
try {
HttpResponse response;
response = httpclient.execute(post);
String inf;
if (response.getStatusLine().getStatusCode() == 200) {// 判断响应状态码
inf = EntityUtils.toString(response.getEntity());
return inf;
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}