一、当做到RSA加密这块时,遇到了问题,我自己加密的自己解密可以,Android端他自己加密自己解密,但是相互加密的都解不开;
然后我就想问题所在:1、是不是私钥公钥没对呢?2、安卓和PHP服务器端 加密方式机密方式对应关系有问题;
二、实现整个思路:1、去支付宝中找 http://pan.baidu.com/s/1hrskICs openssl-RSA 公私钥生成工具中;(注意同时将 dll文件copy到Windows/system32下);
2、生成的公私钥给移动端一份,互相加解密所用此公私钥;
三、实现对接方式:Android 公钥加密--->PHP私钥解密;PHP私钥加密--->Android公钥解密;
然后找到了如下文章:
转至:http://blog.csdn.net/xyxjn/article/details/17225809
最近做手机项目,服务器端使用的是php,客户端分别有android版及ios版,在部分通讯环节需要对内容进行加密,RSA加密演算法是一种非对称加密演算法,能够较好达到要求,不过如果服务器架设https服务,较为麻烦,系统效率也不高,我们只需要在部分重要接口上使用RSA加密解密就行。
首先,准备工作
下载RSA密钥生成工具openssl,点击下载,解压缩至独立的文件夹,进入其中的bin目录,执行以下命令:
- openssl genrsa -out rsa_private_key.pem 1024
- openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem
- openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
第一条命令生成原始 RSA私钥文件 rsa_private_key.pem,第二条命令将原始 RSA私钥转换为 pkcs8格式,第三条生成RSA公钥 rsa_public_key.pem
从上面看出通过私钥能生成对应的公钥,因此我们将私钥private_key.pem用在服务器端,公钥发放给android跟ios等前端
第二步,php服务器端,使用openssl方法来进行加密解密类,代码如下:
- <?php
- /**
- * @author alun (http://alunblog.duapp.com)
- * @version 1.0
- * @created 2013-5-17
- */
- class Rsa
- {
- private static $PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----
- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM9nUm7rPNhSgvsd
- jMuCd5E7IMJB/80A1YY7jYV9fBCKdhVKmqea26QYuw6FW7B00fppEUTSazduSmn9
- Yvhx9UOCcI75b0nq9FWm5O4P+Kp8l31M1pwsJ3cm+DceGOrFsl47vh9idiqj+abI
- lJ4sTmJmDghmbks9YFlZSndQsIBlAgMBAAECgYAasa6vbgF3yi7niScc7l7bR2Pw
- /LOivA+/ZhzR6JO2QUvvc5myJsFMPo6c0Nc7P93iv/EkDX0VNlHHkIBTf79URHXM
- gXwMad4pHAeOiqxk5A9w/szDCBoETngtoqQGJq+QINxwPVvDEO4i224Uj3MKg2fo
- 4SDy3P1GCAAj1ahNoQJBAP4FV9vLWdLOOwOLnBpXt6vru4HT5VIf9fCeBIemuQ4C
- /yRtgU38zXWgZ8AAmS6EjBEUDnN/tWid6UBKfgPDwAkCQQDRBP+Y9wIYIaSxeL7B
- nHhPT25yAJCGK+l6r2qeaHVQr81O9YjusEi8E2M5OxCRolKxC3L7hrLJX8z1oyOV
- dNx9AkBqYGhzpgv+qNiz2mJL8dH8ECMc8lTFeJbw5eu1tw8mHAEnCyisNSMBkGQC
- Vv3PKjjR6hlHKwMYRZDpmIh/IRmpAkEAr1soLGaeZSxkhVetgbUJ4k/bct0yYr4Y
- ZQshwcAVHBpBforT1JwkiVUim3MIFYY/JbVbQ9XfzL4Ir9OsGMkv6QJAPaQnyNY5
- /D0PhXqODOM6jtAHHRfaSi4gve6AZ0iRz6YlB8beJ1ywZaJZWD9Cuw3zy4dDpCOn
- A4tBsIdpMMoT+w==
- -----END PRIVATE KEY-----';
- /**
- *返回对应的私钥
- */
- private static function getPrivateKey(){
- $privKey = self::$PRIVATE_KEY;
- return openssl_pkey_get_private($privKey);
- }
- /**
- * 私钥加密
- */
- public static function privEncrypt($data)
- {
- if(!is_string($data)){
- return null;
- }
- return openssl_private_encrypt($data,$encrypted,self::getPrivateKey())? base64_encode($encrypted) : null;
- }
- /**
- * 私钥解密
- */
- public static function privDecrypt($encrypted)
- {
- if(!is_string($encrypted)){
- return null;
- }
- return (openssl_private_decrypt(base64_decode($encrypted), $decrypted, self::getPrivateKey()))? $decrypted : null;
- }
- }
- ?>
打开private_key.pem,将上面的$PRIVATE_KEY,替换成private_key.pem的内容即可,服务器端我们只需要使用私钥来加密解密。
第三步,android前端,使用java的Cipher类来实现加密解密类,代码如下:
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.InputStream;
- import java.security.KeyFactory;
- import java.security.NoSuchAlgorithmException;
- import java.security.PublicKey;
- import java.security.spec.X509EncodedKeySpec;
- import javax.crypto.Cipher;
- import android.util.Base64;
- /**
- * @author alun (http://alunblog.duapp.com)
- * @version 1.0
- * @created 2013-5-17
- */
- public class Rsa {
- private static final String RSA_PUBLICE =
- "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPZ1Ju6zzYUoL7HYzLgneROyDC" + "\r" +
- "Qf/NANWGO42FfXwQinYVSpqnmtukGLsOhVuwdNH6aRFE0ms3bkpp/WL4cfVDgnCO" + "\r" +
- "+W9J6vRVpuTuD/iqfJd9TNacLCd3Jvg3HhjqxbJeO74fYnYqo/mmyJSeLE5iZg4I" + "\r" +
- "Zm5LPWBZWUp3ULCAZQIDAQAB";
- private static final String ALGORITHM = "RSA";
- /**
- * 得到公钥
- * @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 content
- * @param key
- * @return
- */
- public static String encryptByPublic(String content) {
- try {
- PublicKey pubkey = getPublicKeyFromX509(ALGORITHM, RSA_PUBLICE);
- 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 key 商户私钥
- * @return 解密后的字符串
- */
- public static String decryptByPublic(String content) {
- try {
- PublicKey pubkey = getPublicKeyFromX509(ALGORITHM, RSA_PUBLICE);
- Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
- cipher.init(Cipher.DECRYPT_MODE, pubkey);
- InputStream ins = new ByteArrayInputStream(Base64.decode(content,Base64.DEFAULT));
- ByteArrayOutputStream writer = new ByteArrayOutputStream();
- byte[] buf = new byte[128];
- int bufl;
- while ((bufl = ins.read(buf)) != -1) {
- byte[] block = null;
- if (buf.length == bufl) {
- block = buf;
- } else {
- block = new byte[bufl];
- for (int i = 0; i < bufl; i++) {
- block[i] = buf[i];
- }
- }
- writer.write(cipher.doFinal(block));
- }
- return new String(writer.toByteArray(), "utf-8");
- } catch (Exception e) {
- return null;
- }
- }
- }
需要注意的是,在初始化Cipher对象时,一定要指明使用"RSA/ECB/PKCS1Padding"格式如Cipher.getInstance("RSA/ECB/PKCS1Padding");
打开rsa_public_key.pem文件,将上面代码的RSA_PUBLICE替换成其中内容即可。
第四步,ios前端,iOS上没有直接处理RSA加密的API,网上说的大多数也是处理X.509的证书的方法来实现,不过X.509证书是带签名的,在php端openssl_pkey_get_private方法获取密钥时,第二个参数需要传签名,而android端实现X.509证书加密解密较为不易,在这里我们利用ios兼容c程序的特点,利用openssl的api实现rsa的加密解密,代码如下:
CRSA.h代码:
- #import <Foundation/Foundation.h>
- #include <openssl/rsa.h>
- #include <openssl/pem.h>
- #include <openssl/err.h>
- typedef enum {
- KeyTypePublic,
- KeyTypePrivate
- }KeyType;
- typedef enum {
- RSA_PADDING_TYPE_NONE = RSA_NO_PADDING,
- RSA_PADDING_TYPE_PKCS1 = RSA_PKCS1_PADDING,
- RSA_PADDING_TYPE_SSLV23 = RSA_SSLV23_PADDING
- }RSA_PADDING_TYPE;
- @interface CRSA : NSObject{
- RSA *_rsa;
- }
- + (id)shareInstance;
- - (BOOL)importRSAKeyWithType:(KeyType)type;
- - (int)getBlockSizeWithRSA_PADDING_TYPE:(RSA_PADDING_TYPE)padding_type;
- - (NSString *) encryptByRsa:(NSString*)content withKeyType:(KeyType)keyType;
- - (NSString *) decryptByRsa:(NSString*)content withKeyType:(KeyType)keyType;
- @end
CRSA.m代码
- #import "CRSA.h"
- #define BUFFSIZE 1024
- #import "Base64.h"
- #define PADDING RSA_PADDING_TYPE_PKCS1
- @implementation CRSA
- + (id)shareInstance
- {
- static CRSA *_crsa = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _crsa = [[self alloc] init];
- });
- return _crsa;
- }
- - (BOOL)importRSAKeyWithType:(KeyType)type
- {
- FILEFILE *file;
- NSString *keyName = type == KeyTypePublic ? @"public_key" : @"private_key";
- NSString *keyPath = [[NSBundle mainBundle] pathForResource:keyName ofType:@"pem"];
- file = fopen([keyPath UTF8String], "rb");
- if (NULL != file)
- {
- if (type == KeyTypePublic)
- {
- _rsa = PEM_read_RSA_PUBKEY(file, NULL, NULL, NULL);
- assert(_rsa != NULL);
- }
- else
- {
- _rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);
- assert(_rsa != NULL);
- }
- fclose(file);
- return (_rsa != NULL) ? YES : NO;
- }
- return NO;
- }
- - (NSString *) encryptByRsa:(NSString*)content withKeyType:(KeyType)keyType
- {
- if (![self importRSAKeyWithType:keyType])
- return nil;
- int status;
- int length = [content length];
- unsigned char input[length + 1];
- bzero(input, length + 1);
- int i = 0;
- for (; i < length; i++)
- {
- input[i] = [content characterAtIndex:i];
- }
- NSInteger flen = [self getBlockSizeWithRSA_PADDING_TYPE:PADDING];
- charchar *encData = (char*)malloc(flen);
- bzero(encData, flen);
- switch (keyType) {
- case KeyTypePublic:
- status = RSA_public_encrypt(length, (unsigned char*)input, (unsigned char*)encData, _rsa, PADDING);
- break;
- default:
- status = RSA_private_encrypt(length, (unsigned char*)input, (unsigned char*)encData, _rsa, PADDING);
- break;
- }
- if (status)
- {
- NSData *returnData = [NSData dataWithBytes:encData length:status];
- free(encData);
- encData = NULL;
- NSString *ret = [returnData base64EncodedString];
- return ret;
- }
- free(encData);
- encData = NULL;
- return nil;
- }
- - (NSString *) decryptByRsa:(NSString*)content withKeyType:(KeyType)keyType
- {
- if (![self importRSAKeyWithType:keyType])
- return nil;
- int status;
- NSData *data = [content base64DecodedData];
- int length = [data length];
- NSInteger flen = [self getBlockSizeWithRSA_PADDING_TYPE:PADDING];
- charchar *decData = (char*)malloc(flen);
- bzero(decData, flen);
- switch (keyType) {
- case KeyTypePublic:
- status = RSA_public_decrypt(length, (unsigned char*)[data bytes], (unsigned char*)decData, _rsa, PADDING);
- break;
- default:
- status = RSA_private_decrypt(length, (unsigned char*)[data bytes], (unsigned char*)decData, _rsa, PADDING);
- break;
- }
- if (status)
- {
- NSMutableString *decryptString = [[NSMutableString alloc] initWithBytes:decData length:strlen(decData) encoding:NSASCIIStringEncoding];
- free(decData);
- decData = NULL;
- return decryptString;
- }
- free(decData);
- decData = NULL;
- return nil;
- }
- - (int)getBlockSizeWithRSA_PADDING_TYPE:(RSA_PADDING_TYPE)padding_type
- {
- int len = RSA_size(_rsa);
- if (padding_type == RSA_PADDING_TYPE_PKCS1 || padding_type == RSA_PADDING_TYPE_SSLV23) {
- len -= 11;
- }
- return len;
- }
- @end
以上为 php和安卓的rsa加密解密的方法,再次说明:php私钥加密安卓公钥解迷;安卓公钥加密php私钥解密;
若 php是自己和自己玩具体应用在哪里呢?
如:app的自动登录token的实现,app登录以后 php服务器端自己要通过用户名、密码、时间戳等 通过rsa私钥加密,然后扔给安卓,安卓保存本地,然后下次登录app会自己验证token是否存在,如果存在 自动将token给到php服务器端,服务器端将 token解密后,将用户名密码等 时间戳等是否超时验证后,再 如存在验证通过,则实现了app的自动登录,如果信息不符,则app收到错误返回重定向到app登录界面
注意,app拿着咱们php生成的token 给回到咱们php后,php就需要进行rsa的解密,那么php相当于解自己私钥加密的rsa串, 以上方法就木有了
因为php自己和自己玩耍是:php私钥加密<----->php公钥解密的
所以如下公钥方法才能解密,此时就用到了咱们的公钥:
/**
*返回公钥
*/
private static function getPublicKey(){
$public_key = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC29E5AXyZxeIGbF4j+0NW+XhwE
92D+WGSNt3S3+kmd5lm0QNY5vtRMIRKKrx+Pjb87IeJPiHbLfpWLm2Crdi4DYXTT
3NQCH8uxZEmrNFsjuihKJghKZt0LWmlEVqRRQPvAFMbl25Ud4pEt3Q8q3L1KouEq
m80/bITzdchlFZ82kwIDAQdAB
-----END PUBLIC KEY-----';
return openssl_pkey_get_public($public_key);
}
/**
* 此方法为公钥解密,用于 php私钥加密后,通过公钥解密滴
*/
public function publicDecrypt($encrypted){
return openssl_public_decrypt(base64_decode($encrypted),$decrypted,self::getPublicKey()) ? $decrypted : null; //私钥加密的内容通过公钥可用解密出来;
}
一个实例,在项目中 配合APP实现了加密rsa token 登录
//登录
public function login(){
if(isset($_REQUEST['token']) && !empty($_REQUEST['token'])){
$token = publicDecrypt($_REQUEST['token']); //token由 用户名、密码、时间戳由 | 隔开
$token = explode("|",$token);
//判断上次登录时间 距 当时超过一个月,则需要重新登录
if($_SERVER['REQUEST_TIME']-$token[2]>2592000)die(json_encode(array('status'=>2,'data'=>'token已过期')));
$mobile = $token[0];
$pwd = data_filter($token[1]) ;
}else{
$mobile = data_filter($_REQUEST['mobile']);
$pwd = md5(data_filter($_REQUEST['pwd']));
}
$result = M("Admin")->where("mb='".$mobile."' and pw='".$pwd."'")->find();
//echo json_encode(array('status'=>0,'data'=>$mobile."--".$pwd));exit;
if($result){
//$_SESSION['un'] = array('uId'=>$result['uId'],'m'=>$result['mb'],'nickname'=>$result['nickname']);
$token = privEncrypt($result['mb']."|".$result['pw']."|".$_SERVER['REQUEST_TIME']);
//echo $token."---";exit;
if(isset($_REQUEST['token']) && !empty($_REQUEST['token']))
echo json_encode (array('status'=>1,'data'=>"登录成功",'un'=>array('userId'=>$result['uId'],'mobile'=>$result['mb'],'nickname'=>$result['nickname'],'avatar'=>$result['avatar'])));
else echo json_encode (array('status'=>1,'data'=>"登录成功",'un'=>array('userId'=>$result['uId'],'mobile'=>$result['mb'],'nickname'=>$result['nickname'],'avatar'=>$result['avatar'],'token'=>$token)));
die();
}else{
echo json_encode (array('status'=>0,'data'=>"用户名或密码不正确!")); die();
}
}