PHP ANDROID IOS RSA

未结帖
0 838
ajian carter 2017-08-11
悬赏:5飞吻

http://alunblog.duapp.com/?p=50

点击打开链接



最近做手机项目,服务器端使用的是PHP,客户端分别有Android版及iOS版,在部分通讯环节需要对内容进行加密,RSA加密演算法是一种非对称加密演算法,能够较好达到要求,不过如果服务器架设https服务,较为麻烦,系统效率也不高,我们只需要在部分重要接口上使用RSA加密解密就行。

首先,准备工作

下载RSA密钥生成工具openssl,点击下载,解压缩至独立的文件夹,进入其中的bin目录,执行以下命令:

[objc] view plain copy

  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

  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

  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

  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

  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  



其中openssl api包,我们可以在第一步RSA密钥生成工具openssl的include文件夹中得到,完整代码包请点击这里下载


热忱回答0


最近热帖

近期热议

  1. javascript——prototype与__proto 9
  2. Mysql 中出现的Data truncated for column 3
  3. 在nginx中使用x-sendfile的解决方案 3
  4. 高版本jQuery面插件实现Ajax上传图片 1
  5. Thinkphp Socket.class.php 类的使用 1
  6. 使用ionic3创建第一个App 0
  7. ios-oc html5 0
  8. nginx.conf 0
  9. 基于ionic3.4.0的项目搭建 0
  10. php 缩略图 0