android/ios与通过php+rsa加密解密与服务器通讯

作者:简简单单 2015-06-21

RSA加密演算法是一种非对称加密演算法,能够较好达到要求,不过如果服务器架设https服务,较为麻烦,系统效率也不高,我们只需要在部分重要接口上使用RSA加密解密就行。

首先,准备工作

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方法来进行加密解密类,代码如下:

/**
 * @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
#include
#include
#include
 
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
{
    FILE *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];
     
    char *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];
    char *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

其中openssl api包,我们可以在第一步RSA密钥生成工具openssl的include文件夹中得到


php openssl rsa 加密解密应用

1、安装openssl

yum -y install openssl-devel

2、安装php openssl
到php源码包的目录 ext/openssl 下执行:

/data/apps/php/bin/phpize

./configure --with-openssl --with-php-config=/data/apps/php/bin/php-config
make
make install

(可能需要mv config0.m4 config.m4)
把生成的 openssl.so 文件添加到php.ini中

extension = /xxx/openssl.so;

可以用以下命令查看安装的模块:

php -m |grep openssl

3、生成公钥和私钥
生成私钥:

openssl genrsa 1024 > private.key

(注意,1024是密钥的长度,如果密钥较长,相应加密后的密文也会较长)

生成公钥:

openssl rsa -in private.key -pubout > public.key

4、利用php加密和解密文本,代码如下:


class mycrypt {

    public $pubkey;
    public $privkey;

    function __construct() {
                $this->pubkey = file_get_contents('/home/openssl/public.key');
                $this->privkey = file_get_contents('/home/openssl/private.key');
    }

    public function encrypt($data) {
        if (openssl_public_encrypt($data, $encrypted, $this->pubkey))
            $data = base64_encode($encrypted);
        else
            throw new Exception('Unable to encrypt data. Perhaps it is bigger than the key size?');

        return $data;
    }

    public function decrypt($data) {
        if (openssl_private_decrypt(base64_decode($data), $decrypted, $this->privkey))
            $data = $decrypted;
        else
            $data = '';

        return $data;
    }

}

$rsa = new mycrypt();
echo $rsa -> encrypt('abc');

//echo $rsa -> decrypt('W+ducpssNJlyp2XYE08wwokHfT0bm87yBz9vviZbfjAGsy/U9Ns9FIed684lWjYyyofi/1YWrU0Mp8vLOYi8l6CfklBY=');


PHP通过OpenSSL生成证书、密钥并且加密解密数据

关于PHP生成证书密钥的资料真是好少啊,查了半天,最终还是在官方文档找到了相关资料,又根据自己的理解,整理成了以下代码,分成两部分:生成证书密钥、加密解密数据。直接复制下来做成两个文件运行就好啦。已经写了详细的注释,相信PHP程序员都能看得懂。
generate.php

$dn = array(  
    "countryName" => 'XX', //所在国家名称  
    "stateOrProvinceName" => 'State', //所在省份名称  
    "localityName" => 'SomewhereCity', //所在城市名称  
    "organizationName" => 'MySelf',   //注册人姓名  
    "organizationalUnitName" => 'Whatever', //组织名称  
    "commonName" => 'mySelf', //公共名称  
    "emailAddress" => 'user@domain.com' //邮箱  
 );  
$privkeypass = '111111'; //私钥密码  
$numberofdays = 365;     //有效时长  
$cerpath = "./test.cer"; //生成证书路径  
$pfxpath = "./test.pfx"; //密钥文件路径  
 
//生成证书  
$privkey = openssl_pkey_new();  
$csr = openssl_csr_new($dn, $privkey);  
$sscert = openssl_csr_sign($csr, null, $privkey, $numberofdays);  
openssl_x509_export($sscert, $csrkey); //导出证书$csrkey  
openssl_pkcs12_export($sscert, $privatekey, $privkey, $privkeypass); //导出密钥$privatekey  

//生成证书文件  
$fp = fopen($cerpath, "w");  
fwrite($fp, $csrkey);  
fclose($fp);  

//生成密钥文件  
$fp = fopen($pfxpath, "w");  
fwrite($fp, $privatekey);  
fclose($fp);  
?>

crypt.php

$privkeypass = '111111'; //私钥密码  
$pfxpath = "./test.pfx"; //密钥文件路径  
$priv_key = file_get_contents($pfxpath); //获取密钥文件内容  
$data = "test"; //加密数据测试test  
   
//私钥加密  
openssl_pkcs12_read($priv_key, $certs, $privkeypass); //读取公钥、私钥  
$prikeyid = $certs['pkey']; //私钥  
openssl_sign($data, $signMsg, $prikeyid,OPENSSL_ALGO_SHA1); //注册生成加密信息  
$signMsg = base64_encode($signMsg); //base64转码加密信息  
   
   
//公钥解密  
$unsignMsg=base64_decode($signMsg);//base64解码加密信息  
openssl_pkcs12_read($priv_key, $certs, $privkeypass); //读取公钥、私钥  
$pubkeyid = $certs['cert']; //公钥  
$res = openssl_verify($data, $unsignMsg, $pubkeyid); //验证  
echo $res; //输出验证结果,1:验证成功,0:验证失败  
?>

相关文章

精彩推荐