RSA加解密你会了吗?

来源:互联网 发布:优化关键词论文 编辑:程序博客网 时间:2024/04/28 10:04

一、当做到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目录,执行以下命令:
[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. openssl genrsa -out rsa_private_key.pem 1024  
  2.    
  3. openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem  
  4.    
  5. 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方法来进行加密解密类,代码如下:

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?php  
  2. /** 
  3.  * @author alun (http://alunblog.duapp.com) 
  4.  * @version 1.0 
  5.  * @created 2013-5-17 
  6.  */  
  7.    
  8. class Rsa  
  9. {  
  10. private static $PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----  
  11. MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM9nUm7rPNhSgvsd  
  12. jMuCd5E7IMJB/80A1YY7jYV9fBCKdhVKmqea26QYuw6FW7B00fppEUTSazduSmn9  
  13. Yvhx9UOCcI75b0nq9FWm5O4P+Kp8l31M1pwsJ3cm+DceGOrFsl47vh9idiqj+abI  
  14. lJ4sTmJmDghmbks9YFlZSndQsIBlAgMBAAECgYAasa6vbgF3yi7niScc7l7bR2Pw  
  15. /LOivA+/ZhzR6JO2QUvvc5myJsFMPo6c0Nc7P93iv/EkDX0VNlHHkIBTf79URHXM  
  16. gXwMad4pHAeOiqxk5A9w/szDCBoETngtoqQGJq+QINxwPVvDEO4i224Uj3MKg2fo  
  17. 4SDy3P1GCAAj1ahNoQJBAP4FV9vLWdLOOwOLnBpXt6vru4HT5VIf9fCeBIemuQ4C  
  18. /yRtgU38zXWgZ8AAmS6EjBEUDnN/tWid6UBKfgPDwAkCQQDRBP+Y9wIYIaSxeL7B  
  19. nHhPT25yAJCGK+l6r2qeaHVQr81O9YjusEi8E2M5OxCRolKxC3L7hrLJX8z1oyOV  
  20. dNx9AkBqYGhzpgv+qNiz2mJL8dH8ECMc8lTFeJbw5eu1tw8mHAEnCyisNSMBkGQC  
  21. Vv3PKjjR6hlHKwMYRZDpmIh/IRmpAkEAr1soLGaeZSxkhVetgbUJ4k/bct0yYr4Y  
  22. ZQshwcAVHBpBforT1JwkiVUim3MIFYY/JbVbQ9XfzL4Ir9OsGMkv6QJAPaQnyNY5  
  23. /D0PhXqODOM6jtAHHRfaSi4gve6AZ0iRz6YlB8beJ1ywZaJZWD9Cuw3zy4dDpCOn  
  24. A4tBsIdpMMoT+w==  
  25. -----END PRIVATE KEY-----';  
  26.     /** 
  27.     *返回对应的私钥 
  28.     */  
  29.     private static function getPrivateKey(){  
  30.       
  31.         $privKey = self::$PRIVATE_KEY;  
  32.            
  33.         return openssl_pkey_get_private($privKey);        
  34.     }  
  35.    
  36.     /** 
  37.      * 私钥加密 
  38.      */  
  39.     public static function privEncrypt($data)  
  40.     {  
  41.         if(!is_string($data)){  
  42.                 return null;  
  43.         }             
  44.         return openssl_private_encrypt($data,$encrypted,self::getPrivateKey())? base64_encode($encrypted) : null;  
  45.     }  
  46.       
  47.       
  48.     /** 
  49.      * 私钥解密 
  50.      */  
  51.     public static function privDecrypt($encrypted)  
  52.     {  
  53.         if(!is_string($encrypted)){  
  54.                 return null;  
  55.         }  
  56.         return (openssl_private_decrypt(base64_decode($encrypted), $decrypted, self::getPrivateKey()))? $decrypted : null;  
  57.     }  
  58. }  
  59.    
  60. ?>  

打开private_key.pem,将上面的$PRIVATE_KEY,替换成private_key.pem的内容即可,服务器端我们只需要使用私钥来加密解密。

第三步,android前端,使用java的Cipher类来实现加密解密类,代码如下:

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. import java.io.ByteArrayInputStream;  
  2. import java.io.ByteArrayOutputStream;  
  3. import java.io.InputStream;  
  4. import java.security.KeyFactory;  
  5. import java.security.NoSuchAlgorithmException;  
  6. import java.security.PublicKey;  
  7. import java.security.spec.X509EncodedKeySpec;  
  8. import javax.crypto.Cipher;  
  9.    
  10. import android.util.Base64;  
  11.    
  12. /** 
  13.  * @author alun (http://alunblog.duapp.com) 
  14.  * @version 1.0 
  15.  * @created 2013-5-17 
  16.  */  
  17. public class Rsa {  
  18.     private static final String RSA_PUBLICE =  
  19.             "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPZ1Ju6zzYUoL7HYzLgneROyDC" + "\r" +  
  20.             "Qf/NANWGO42FfXwQinYVSpqnmtukGLsOhVuwdNH6aRFE0ms3bkpp/WL4cfVDgnCO" + "\r" +  
  21.             "+W9J6vRVpuTuD/iqfJd9TNacLCd3Jvg3HhjqxbJeO74fYnYqo/mmyJSeLE5iZg4I" + "\r" +  
  22.             "Zm5LPWBZWUp3ULCAZQIDAQAB";  
  23.     private static final String ALGORITHM = "RSA";  
  24.    
  25.     /** 
  26.      * 得到公钥 
  27.      * @param algorithm 
  28.      * @param bysKey 
  29.      * @return 
  30.      */  
  31.     private static PublicKey getPublicKeyFromX509(String algorithm,  
  32.             String bysKey) throws NoSuchAlgorithmException, Exception {  
  33.         byte[] decodedKey = Base64.decode(bysKey,Base64.DEFAULT);  
  34.         X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodedKey);  
  35.    
  36.         KeyFactory keyFactory = KeyFactory.getInstance(algorithm);  
  37.         return keyFactory.generatePublic(x509);  
  38.     }  
  39.    
  40.     /** 
  41.      * 使用公钥加密 
  42.      * @param content 
  43.      * @param key 
  44.      * @return 
  45.      */  
  46.     public static String encryptByPublic(String content) {  
  47.         try {  
  48.             PublicKey pubkey = getPublicKeyFromX509(ALGORITHM, RSA_PUBLICE);  
  49.    
  50.             Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");  
  51.             cipher.init(Cipher.ENCRYPT_MODE, pubkey);  
  52.    
  53.             byte plaintext[] = content.getBytes("UTF-8");  
  54.             byte[] output = cipher.doFinal(plaintext);  
  55.    
  56.             String s = new String(Base64.encode(output,Base64.DEFAULT));  
  57.    
  58.             return s;  
  59.    
  60.         } catch (Exception e) {  
  61.             return null;  
  62.         }  
  63.     }  
  64.    
  65.     /** 
  66.     * 使用公钥解密 
  67.     * @param content 密文 
  68.     * @param key 商户私钥 
  69.     * @return 解密后的字符串 
  70.     */  
  71.     public static String decryptByPublic(String content) {  
  72.         try {  
  73.             PublicKey pubkey = getPublicKeyFromX509(ALGORITHM, RSA_PUBLICE);  
  74.             Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");  
  75.             cipher.init(Cipher.DECRYPT_MODE, pubkey);  
  76.             InputStream ins = new ByteArrayInputStream(Base64.decode(content,Base64.DEFAULT));  
  77.             ByteArrayOutputStream writer = new ByteArrayOutputStream();  
  78.             byte[] buf = new byte[128];  
  79.             int bufl;  
  80.             while ((bufl = ins.read(buf)) != -1) {  
  81.                 byte[] block = null;  
  82.                 if (buf.length == bufl) {  
  83.                 block = buf;  
  84.                 } else {  
  85.                 block = new byte[bufl];  
  86.                 for (int i = 0; i < bufl; i++) {  
  87.                     block[i] = buf[i];  
  88.                 }  
  89.                 }  
  90.                 writer.write(cipher.doFinal(block));  
  91.             }  
  92.             return new String(writer.toByteArray(), "utf-8");  
  93.         } catch (Exception e) {  
  94.             return null;  
  95.         }  
  96.     }  
  97.    
  98. }  

需要注意的是,在初始化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代码:

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #import <Foundation/Foundation.h>  
  2. #include <openssl/rsa.h>  
  3. #include <openssl/pem.h>  
  4. #include <openssl/err.h>  
  5.    
  6. typedef enum {  
  7.     KeyTypePublic,  
  8.     KeyTypePrivate  
  9. }KeyType;  
  10.    
  11. typedef enum {  
  12.     RSA_PADDING_TYPE_NONE       = RSA_NO_PADDING,  
  13.     RSA_PADDING_TYPE_PKCS1      = RSA_PKCS1_PADDING,  
  14.     RSA_PADDING_TYPE_SSLV23     = RSA_SSLV23_PADDING  
  15. }RSA_PADDING_TYPE;  
  16.    
  17. @interface CRSA : NSObject{  
  18.     RSA *_rsa;  
  19. }  
  20. + (id)shareInstance;  
  21. - (BOOL)importRSAKeyWithType:(KeyType)type;  
  22. - (int)getBlockSizeWithRSA_PADDING_TYPE:(RSA_PADDING_TYPE)padding_type;  
  23. - (NSString *) encryptByRsa:(NSString*)content withKeyType:(KeyType)keyType;  
  24. - (NSString *) decryptByRsa:(NSString*)content withKeyType:(KeyType)keyType;  
  25. @end  

CRSA.m代码

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #import "CRSA.h"  
  2.    
  3. #define BUFFSIZE  1024  
  4. #import "Base64.h"  
  5.    
  6. #define PADDING RSA_PADDING_TYPE_PKCS1  
  7. @implementation CRSA  
  8.    
  9. + (id)shareInstance  
  10. {  
  11.     static CRSA *_crsa = nil;  
  12.     static dispatch_once_t onceToken;  
  13.     dispatch_once(&onceToken, ^{  
  14.         _crsa = [[self alloc] init];  
  15.     });  
  16.     return _crsa;  
  17. }  
  18. - (BOOL)importRSAKeyWithType:(KeyType)type  
  19. {  
  20.     FILEFILE *file;  
  21.     NSString *keyName = type == KeyTypePublic ? @"public_key" : @"private_key";  
  22.     NSString *keyPath = [[NSBundle mainBundle] pathForResource:keyName ofType:@"pem"];  
  23.        
  24.     file = fopen([keyPath UTF8String], "rb");  
  25.        
  26.     if (NULL != file)  
  27.     {  
  28.         if (type == KeyTypePublic)  
  29.         {  
  30.             _rsa = PEM_read_RSA_PUBKEY(file, NULLNULLNULL);  
  31.             assert(_rsa != NULL);  
  32.         }  
  33.         else  
  34.         {  
  35.             _rsa = PEM_read_RSAPrivateKey(file, NULLNULLNULL);  
  36.             assert(_rsa != NULL);  
  37.         }  
  38.            
  39.         fclose(file);  
  40.            
  41.         return (_rsa != NULL) ? YES : NO;  
  42.     }  
  43.        
  44.     return NO;  
  45. }  
  46.    
  47. - (NSString *) encryptByRsa:(NSString*)content withKeyType:(KeyType)keyType  
  48. {  
  49.     if (![self importRSAKeyWithType:keyType])  
  50.          return nil;  
  51.        
  52.     int status;  
  53.     int length  = [content length];  
  54.     unsigned char input[length + 1];  
  55.     bzero(input, length + 1);  
  56.     int i = 0;  
  57.     for (; i < length; i++)  
  58.     {  
  59.         input[i] = [content characterAtIndex:i];  
  60.     }  
  61.        
  62.     NSInteger  flen = [self getBlockSizeWithRSA_PADDING_TYPE:PADDING];  
  63.        
  64.     charchar *encData = (char*)malloc(flen);  
  65.     bzero(encData, flen);  
  66.        
  67.     switch (keyType) {  
  68.         case KeyTypePublic:  
  69.             status = RSA_public_encrypt(length, (unsigned char*)input, (unsigned char*)encData, _rsa, PADDING);  
  70.             break;  
  71.                
  72.         default:  
  73.             status = RSA_private_encrypt(length, (unsigned char*)input, (unsigned char*)encData, _rsa, PADDING);  
  74.             break;  
  75.     }  
  76.        
  77.     if (status)  
  78.     {  
  79.         NSData *returnData = [NSData dataWithBytes:encData length:status];  
  80.         free(encData);  
  81.         encData = NULL;  
  82.            
  83.         NSString *ret = [returnData base64EncodedString];  
  84.         return ret;  
  85.     }  
  86.        
  87.     free(encData);  
  88.     encData = NULL;  
  89.        
  90.     return nil;  
  91. }  
  92.    
  93. - (NSString *) decryptByRsa:(NSString*)content withKeyType:(KeyType)keyType  
  94. {  
  95.     if (![self importRSAKeyWithType:keyType])  
  96.         return nil;  
  97.        
  98.     int status;  
  99.    
  100.     NSData *data = [content base64DecodedData];  
  101.     int length = [data length];  
  102.        
  103.     NSInteger flen = [self getBlockSizeWithRSA_PADDING_TYPE:PADDING];  
  104.     charchar *decData = (char*)malloc(flen);  
  105.     bzero(decData, flen);  
  106.        
  107.     switch (keyType) {  
  108.         case KeyTypePublic:  
  109.             status = RSA_public_decrypt(length, (unsigned char*)[data bytes], (unsigned char*)decData, _rsa, PADDING);  
  110.             break;  
  111.                
  112.         default:  
  113.             status = RSA_private_decrypt(length, (unsigned char*)[data bytes], (unsigned char*)decData, _rsa, PADDING);  
  114.             break;  
  115.     }  
  116.        
  117.     if (status)  
  118.     {  
  119.         NSMutableString *decryptString = [[NSMutableString alloc] initWithBytes:decData length:strlen(decData) encoding:NSASCIIStringEncoding];  
  120.         free(decData);  
  121.         decData = NULL;  
  122.            
  123.         return decryptString;  
  124.     }  
  125.        
  126.     free(decData);  
  127.     decData = NULL;  
  128.        
  129.     return nil;  
  130. }  
  131.    
  132. - (int)getBlockSizeWithRSA_PADDING_TYPE:(RSA_PADDING_TYPE)padding_type  
  133. {  
  134.     int len = RSA_size(_rsa);  
  135.        
  136.     if (padding_type == RSA_PADDING_TYPE_PKCS1 || padding_type == RSA_PADDING_TYPE_SSLV23) {  
  137.         len -= 11;  
  138.     }  
  139.        
  140.     return len;  
  141. }  
  142. @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();
         }
    }


0 0
原创粉丝点击