关于Android中RSA数字签名的理解及使用

关于RSA数字签名(Android)

近期项目中考虑到数据比较重要,担心与服务器交互过程中被篡改,所以引用了数据签名的技术。

什么是RSA:

RSA是一种非对称的加密算法,RSA的加密解密中有一对密钥(公钥和私钥),通过公钥加密的密文只能通过对应的私钥解密,反之亦然。

我自己的理解:

在数据交互中,必须同时知道密钥对和被加密的字符串(我们暂时先称为密签)的生成规则,才能造出合法的数据。下面假设我们的客户端代码被反编译,获取到了储存在我们本地的密钥对,我们分两种情况进行分析。

客户端请求服务器的数据

加密的密钥得到了,但是没有我们密签的生成规则,造不出合法的请求数据。

服务器响应客户端的数据

通过解密的私钥解密服务器返回的sign值,获取到了服务器响应数据的密签生成规则,但是加密响应数据的加密密钥是保存在服务器的,相对比较安全,所以也造不出合法的响应数据。

所以在这套加密体系中,请求数据的密签和响应数据的密签的生成规则应该是不同的。


思路:

在整个交互中一共有两对密钥,一对用于服务器的加密(公钥A)和客户端的解密(私钥A),另一对用于服务器的解密(私钥B)和客户端的加密(公钥B)。在客户端请求时,将请求数据通过RSA加密后连带要请求的数据一起发送给服务器,服务器收到请求后,首先通过对应的密钥对请求中加密的数据进行解密,然后对比解密后的内容与请求的内容是否一致,若一致,则可以判定数据是合法的,反之则是不合法的。服务器在响应客户端请求时,使用相同的方法。(Ps:请求的数据一般都是键值对,我们通过与服务器协商,使用相同的规则对数据进行拼接,组成一个字符串后进行加密)

大概流程:

这里假设我们的拼接规则是:键=值&键=值&…

Created with Raphaël 2.1.0 客户端 客户端 服务器 服务器 {"name":"张三", "age":"12", "sign":"这里是对数据进行拼接后加密的密文" (这里加密之前应该是:name=张三&age=12)} 服务器收到请求后取出sign的值进行解密, 然后通过拼接字符串的方式将出去sign的其 余参数按照规则拼接,如果拼接出来的值与 sign解密得到的值相同,则可以判断数据是 来自服务器的。 服务器的响应信息中也使用这样的加密方式, 客户端得到信息后,使用相同的方法判断 数据的合法性。

注意:

在加解密过程中,openssl生成的密钥对无法使用。(有大神发现问题希望指出)这里附上一对可用的钥匙。

—–BEGIN PUBLIC KEY—–
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBJq3d4HJscWh44l/Vh431rbKZ
XxdR+gRSgvINmCuHTlgasRsvKiPgtBtRsj/mzpowuxCOg67xkBQvIoXVs1wiJNuQ
Zi+mVXUQCJelLn5ykCLvNqkugMO9nNtre/2VezjP81t5FeOlEnQMo3AFLSeZv2cc
I9V0DD0IsnIlRH/L8wIDAQAB
—–END PUBLIC KEY—–

—–BEGIN PRIVATE KEY—–
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMEmrd3gcmxxaHji
X9WHjfWtsplfF1H6BFKC8g2YK4dOWBqxGy8qI+C0G1GyP+bOmjC7EI6DrvGQFC8i
hdWzXCIk25BmL6ZVdRAIl6UufnKQIu82qS6Aw72c22t7/ZV7OM/zW3kV46USdAyj
cAUtJ5m/Zxwj1XQMPQiyciVEf8vzAgMBAAECgYBaJ7G0BNWj5HN9KTzOME2ExSS+
DfKWovptgQ12Zva6W0kofE5R/3troOW3hlnpY7n40PfzbWe0/SlOrvCFRQjVXbbR
9a+eZ0xynSuMg8m57q8qHZ/l5dV8ECWsEUC1eFHepWpMDygFv8qgLIV2C5Ot03HW
yGXLzoPJ+GD9U+yA8QJBAP2vKm6Js9wYvK/vtUpX7jScuHHFSZmimoyAz3sOIWZ2
XBKspc0XDzLlIAd6BbC5i9JfTqTNJE0Om20NsuOqOp0CQQDC6g25Go56AZvsXPBh
1I5L0Lj673sBx9wqvAUS6/MCeJT9jBUq8FRDi4KVlkWBzhDP5K+AKnADm40fAVi2
R9PPAkBl2pOVyBH/16rClOLcE4vfceEYRNb1KXUV3o3vYVAFvJXhJZJ4Ur7Wd2ox
tktcllLOR1fjET529dtvoKVnhSx9AkEAsOr9dcyTEk8vlkVts37Zght8K9j/j8g3
IVr0SA5+zvlgUxu5gvYNphmVd+2kPYQrYmgynGTos32XhFMWkK11pQJAFOMAzgSE
PggBQohD4jg7vOvZzFO8M4VDee7yaC5xFpJlJBtol1twlIbzv1HjAgHDeqXChStf
rLegWG4sbnXTMA==
—–END PRIVATE KEY—–

加密解密工具类的代码

package com.shsy.test.rsa;

import android.util.Base64;

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * Created by Shsy on 15-11-27.
 */
public class RsaUtils {
    /**
     * 获取公钥
     *
     * @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 key
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = Base64.decode(key, Base64.DEFAULT);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 使用公钥加密
     *
     * @param content
     * @param publicKey
     * @return
     */
    public static String encryptByPublic(String content, String publicKey) {
        try {
            PublicKey pubkey = getPublicKeyFromX509("RSA", publicKey);
            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 privateKey
     * @return
     */
    public static String decryptByPrivate(String content, String privateKey) {
        try {
            PrivateKey prikey = getPrivateKey(privateKey);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, prikey);
            byte[] enBytes = Base64.decode(content, Base64.DEFAULT);
            byte[] deBytes = cipher.doFinal(enBytes);
            return new String(deBytes);
        } catch (Exception e) {
            return null;
        }
    }
}

使用样例

/**将上面的密钥复制到对应的""中*/
/**注意:密钥不包含被"-----BEGIN PUBLIC KEY-----"这样的头尾信息*/
/**加密的公钥*/
pivate final String public_Key="";
/**解密的私钥*/
pivate final String private_Key="";
/**要加密的内容*/
String s = "Test";
/**加密*/
String ss = RsaUtils.encryptByPublic(s,public_Key);
/**解密*/
String sss = RsaUtils.decryptByPrivate(ss,private_Key);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值