Bootstrap

RSA客户端(android/ios)与服务端的通信实现

RSA是什么玩意这里就不再说了,大家可以自己搜索,不说废话,直接上正文

需求

客户端(android/ios)向服务端发送一串已经协议好的公钥加密数据到服务端,服务端使用私钥对公钥进行解码

思路

我们知道spring中我们有Filter HandlerInterceptorAdapter这些关于AOP方面的操作,所以各位童鞋如果不想在业务层面进行解码,可以从这些方面入手解码,具体方式自行操作,本文不做详细介绍,至于客户端就简单多了,直接用公钥进行加密发送到服务端即可。

准备工作:生成RSA私钥,公钥

关于怎么生成RSA的私钥和公钥据笔者所知就两种

1.通过openSSL自行生成,网上已经有很多教程,但是比较麻烦,下面贴出支付宝的官方教程

http://app.alipay.com/market/document.htm?name=saomazhifu#page-23

2.使用支付宝提供工具一键生成,省时省力,具体生成文件这里就不贴出来了,有心的同学可以自行搜索。

密钥说明

**private**.**.pkcs8.pem java后端使用的私钥文件

私钥是用于服务解码用的,不对外暴露,所以请妥善保管好,内容应该如下。

这里写图片描述

这里需要注意,如果服务器使用的是java,那么应当使用带pkcs8字样的私钥文件,而你又是以文件形式进行加载私钥的话,需要把
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
这两行删掉,最终应该如下图一致

这里写图片描述

***private**.pem 其他后端语言使用的私钥文件

***public***.pem 暴露在外的客户端公钥文件

java相关RSA加解密代码,适用java服务端和android端

package com.rsa;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

import javax.crypto.Cipher;

import com.pay.ali.tool.Base64;


/**
 * 
 * @author joncch
 *
 */
public class RSADecrpyt {

    /**
     * 加载私钥
     * @param privateKeyStr
     * @return
     * @throws Exception
     */
    private static RSAPrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        Base64 decoder = new Base64();
        byte[] buffer = decoder.decode(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    }

    /**
     * 解码密文
     * @param rsaBase64Context
     * @return
     */
    public static String getDecryptString(String privateKey,String rsaBase64Context) {
        try {
            RSAPrivateKey key = loadPrivateKey(privateKey);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] encryptData = Base64.decode(rsaBase64Context);
            ByteArrayOutputStream out = new ByteArrayOutputStream(encryptData.length);
            int BLOCK_SIZE = 128;
            for (int i = 0; i < encryptData.length; i += BLOCK_SIZE) {
                int leftBytes = encryptData.length - i;
                int length = (leftBytes <= BLOCK_SIZE) ? leftBytes : BLOCK_SIZE;
                out.write(cipher.doFinal(encryptData, i, length));
            }
            String string = new String(out.toByteArray(), "UTF-8");
            System.out.println("RAS.Decrpyt.Result = "+string);
            System.out.println("RAS.Decrpyt.Length = "+string.length());
            return string;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream(new File("e:/temp.txt"));
        byte[] bs = new byte[fis.available()];
        fis.read(bs);
        System.out.println(RSADecrpyt.getDecryptString("",new String(bs,"utf-8")));
        fis.close();
    }

}

解码代码

package com.rsa;
import java.nio.ByteBuffer;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;

import javax.crypto.Cipher;

import com.pay.ali.tool.Base64;



/**
 * 
 * @author joncch
 *
 */
public class RSAEncrypt {

    /**
     * 载入公钥
     * @param publicKeyStr
     * @return
     * @throws Exception
     */
    private static RSAPublicKey loadPublicKey(String publicKeyStr) throws Exception {
        Base64 decoder = new Base64();
        byte[] buffer = decoder.decode(publicKeyStr);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }

    /**
     * 加密明文
     * @param context 明文
     * @return
     */
    public static byte[] doEncrypt(String key,String context)  {
        try {
            RSAPublicKey publicKey = loadPublicKey(key);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] rawText = context.getBytes("utf-8");
            List<byte[]> bslist = new ArrayList<>();
            int totallength = 0;
            int BLOCK_SIZE = 128 - 11;
            for (int i = 0; i < rawText.length; i += BLOCK_SIZE) {
                int leftBytes = rawText.length - i;
                int length = (leftBytes <= BLOCK_SIZE) ? leftBytes : BLOCK_SIZE;
                byte[] bs = cipher.doFinal(rawText, i, length);
                totallength += bs.length;
                bslist.add(bs);
            }
            ByteBuffer bf = ByteBuffer.allocate(totallength);
            for (int i = 0; i < bslist.size(); i++) {
                bf.put(bslist.get(i));
            }
            return bf.array();
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获得密文base64字串
     * @param context 文明
     * @return
     */
    public static String getBase64Encrypt(String publicKey,String context){
        String string = Base64.encode(doEncrypt(publicKey,context));
        System.out.println("RSA.Encrypt.Result="+string);
        System.out.println("RSA.Encrypt.Lenth="+string.length());
        return string;
    }

    public static void main(String[] args) {
        String s = RSAEncrypt.getBase64Encrypt("","123456");
        System.out.println(s);
    }
}

说明
很多网上教程中对Cipher的初始化都是直接使用RSA
但是因为android和java服务端的标准不太一样会导致解码失败
所以这里需要把值改变为RSA/ECB/PKCS1Padding这样两边的算法就一致了
到此为止,服务端的解码已经完成,其中本文中加载密钥的方式是直接放到一个字符串常量中

Cipher cipher = Cipher.getInstance("RSA");//正常情况
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");//android对服务端后台情况

IOS公钥加密代码

值得注意的是,在java中密钥使用的是pem格式,然而苹果中使用的是der格式,所以需要使用openssl进行转换,又因为苹果自带openssl工具,所以打开终端执行已下步骤即可

这里写图片描述

按提示操作
生成出来的rsa_public_key.der即为IOS端密钥,使用此密钥进行加密

IOS端加密代码调用rsaEncrypt获得密文

- (SecKeyRef) getPublicKey{
    if (_public_key == nil){
        NSString* filePath = [[NSBundle mainBundle] pathForResource:@"rsa_public_key" ofType:@"der"];
        NSData* certificateData = [NSData dataWithContentsOfFile:filePath];
        SecCertificateRef myCertificate =  SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
        if (myCertificate == nil) {
            NSLog(@"无法读取公钥内容");
            return nil;
        }
        SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
        SecTrustRef myTrust;
        OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
        SecTrustResultType trustResult;
        if (status == noErr) {
            status = SecTrustEvaluate(myTrust, &trustResult);
        }else{
            return nil;
        }
        _public_key = SecTrustCopyPublicKey(myTrust);
        CFRelease(myCertificate);
        CFRelease(myPolicy);
        CFRelease(myTrust);
    }
    return _public_key;
}

- (NSData*) rsaEncrypt:(NSData*) data{
    SecKeyRef key = [self getPublicKey];
    if (key == nil) {
        return nil;
    }
    size_t cipherBufferSize = SecKeyGetBlockSize(key);
    uint8_t* cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    size_t blockSize = cipherBufferSize - 11;
    size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
    NSMutableData *encryptedData = [[NSMutableData alloc] init];
    for (int i=0; i < [data length] ; i += blockSize){
         int bufferSize = (int)MIN(blockSize,[data length] - i * blockSize);
         NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
         OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)[buffer bytes],
                                         [buffer length], cipherBuffer, &cipherBufferSize);
         if (status == noErr){
             NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
             [encryptedData appendData:encryptedBytes];
         }else{
             if (cipherBuffer) free(cipherBuffer);
             return nil;
         }
         }
         if (cipherBuffer) free(cipherBuffer);
         return encryptedData;
}
;