您的位置:首页 > 移动开发 > Android开发

android、ios与服务器端php使用rsa加密解密通讯

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


首先,准备工作


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


[objc] view
plaincopy





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

[objc] view
plaincopy





<?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类来实现加密解密类,代码如下:

[objc] view
plaincopy





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代码:

[objc] view
plaincopy





#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代码

[objc] view
plaincopy





#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: