Android网络传输中必用的两个加密算法

#欢迎来到风的博客

###MD5 和 RSA
MD5和RSA是网络传输中最常用的两个算法,了解这两个算法原理后就能大致知道加密是怎么一回事了。但这两种算法使用环境有差异,刚好互补


MD5加密

######首先MD5是不可逆的,只能加密而不能解密。比如明文是yanzi1225627,得到MD5加密后的字符串是:14F2AE15259E2C276A095E7394DA0CA9 但不能由后面一大串倒推出yanzi1225627.因此可以用来存储用户输入的密码在服务器上。现在下载文件校验文件是否中途被篡改也是用的它,原理参见:http://blog.csdn.net/forgotaboutgirl/article/details/7258109 无论在Android上还是pc上用java实现MD5都比较容易,因为java已经把它做到了java.security.MessageDigest里。下面是一个MD5Util.java类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package org.md5.util;
import java.security.MessageDigest;
public class MD5Util {
public final static String getMD5String(String s) {
char hexDigits[] = { '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
try {
byte[] btInput = s.getBytes();
//获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
//使用指定的字节更新摘要
mdInst.update(btInput);
//获得密文
byte[] md = mdInst.digest();
//把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

通过下面两行代码调用:

1
2
3
//MD5加密测试
String srcString = "yanzi1225627";
System.out.println("MD5加密后 = " + MD5Util.getMD5String(srcString));

##RSA 加密
RSA是可逆的,一个字符串可以经rsa加密后,经加密后的字符串传到对端如服务器上,再进行解密即可。前提是服务器知道解密的私钥,当然这个私钥最好不要再网络传输。
RSA算法描述中需要以下几个变量:

  • 1:p和q 是不相等的,足够大的两个质数。 p和q是保密的
  • 2:n = p*q n是公开的
  • 3:f(n) = (p-1)*(q-1)
  • 4:e 是和f(n)互质的质数
  • 5:计算参数d
  • 6:经过上面5步计算得到公钥KU=(e,n) 私钥KR=(d,n)
    下面两篇文章对此有清晰的描述:
    http://wenku.baidu.com/view/e53fbe36a32d7375a417801b.html
    http://bank.hexun.com/2009-06-24/118958531.html
    ####下面是java实现RSAUtil.java类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    import javax.crypto.Cipher;
    import java.security.*;
    import java.security.spec.RSAPublicKeySpec;
    import java.security.spec.RSAPrivateKeySpec;
    import java.security.spec.InvalidKeySpecException;
    import java.security.interfaces.RSAPrivateKey;
    import java.security.interfaces.RSAPublicKey;
    import java.io.*;
    import java.math.BigInteger;
    /**
    * RSA 工具类。提供加密,解密,生成密钥对等方法。
    * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
    * RSA加密原理概述
    * RSA的安全性依赖于大数的分解,公钥和私钥都是两个大素数(大于100的十进制位)的函数。
    * 据猜测,从一个密钥和密文推断出明文的难度等同于分解两个大素数的积
    * ===================================================================
    * (该算法的安全性未得到理论的证明)
    * ===================================================================
    * 密钥的产生:
    * 1.选择两个大素数 p,q ,计算 n=p*q;
    * 2.随机选择加密密钥 e ,要求 e 和 (p-1)*(q-1)互质
    * 3.利用 Euclid 算法计算解密密钥 d , 使其满足 e*d = 1(mod(p-1)*(q-1)) (其中 n,d 也要互质)
    * 4:至此得出公钥为 (n,e) 私钥为 (n,d)
    * ===========================================================
    * 加解密方法:
    * 1.首先将要加密的信息 m(二进制表示) 分成等长的数据块 m1,m2,...,mi 块长 s(尽可能大) ,其中 2^s<n
    * 2:对应的密文是: ci = mi^e(mod n)
    * 3:解密时作如下计算: mi = ci^d(mod n)
    * ===========================================================
    * RSA速度
    * 由于进行的都是大数计算,使得RSA最快的情况也比DES慢上100倍,无论 是软件还是硬件实现。
    * 速度一直是RSA的缺陷。一般来说只用于少量数据 加密。
    *文件名:RSAUtil.java<br>
    *@author 董利伟<br>
    *版本:<br>
    *描述:<br>
    *创建时间:2008-9-23 下午09:58:16<br>
    *文件描述:<br>
    *修改者:<br>
    *修改日期:<br>
    *修改描述:<br>
    */
    public class RSAUtil {
    //密钥对
    private KeyPair keyPair = null;
    /**
    * 初始化密钥对
    */
    public RSAUtil(){
    try {
    this.keyPair = this.generateKeyPair();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    /**
    * 生成密钥对
    * @return KeyPair
    * @throws Exception
    */
    private KeyPair generateKeyPair() throws Exception {
    try {
    KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
    //这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低
    final int KEY_SIZE = 1024;
    keyPairGen.initialize(KEY_SIZE, new SecureRandom());
    KeyPair keyPair = keyPairGen.genKeyPair();
    return keyPair;
    } catch (Exception e) {
    throw new Exception(e.getMessage());
    }
    }
    /**
    * 生成公钥
    * @param modulus
    * @param publicExponent
    * @return RSAPublicKey
    * @throws Exception
    */
    private RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) throws Exception {
    KeyFactory keyFac = null;
    try {
    keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
    } catch (NoSuchAlgorithmException ex) {
    throw new Exception(ex.getMessage());
    }
    RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent));
    try {
    return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
    } catch (InvalidKeySpecException ex) {
    throw new Exception(ex.getMessage());
    }
    }
    /**
    * 生成私钥
    * @param modulus
    * @param privateExponent
    * @return RSAPrivateKey
    * @throws Exception
    */
    private RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) throws Exception {
    KeyFactory keyFac = null;
    try {
    keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
    } catch (NoSuchAlgorithmException ex) {
    throw new Exception(ex.getMessage());
    }
    RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent));
    try {
    return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
    } catch (InvalidKeySpecException ex) {
    throw new Exception(ex.getMessage());
    }
    }
    /**
    * 加密
    * @param key 加密的密钥
    * @param data 待加密的明文数据
    * @return 加密后的数据
    * @throws Exception
    */
    public byte[] encrypt(Key key, byte[] data) throws Exception {
    try {
    Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
    cipher.init(Cipher.ENCRYPT_MODE, key);
    //获得加密块大小,如:加密前数据为128个byte,而key_size=1024 加密块大小为127 byte,加密后为128个byte;
    //因此共有2个加密块,第一个127 byte第二个为1个byte
    int blockSize = cipher.getBlockSize();
    int outputSize = cipher.getOutputSize(data.length);//获得加密块加密后块大小
    int leavedSize = data.length % blockSize;
    int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
    byte[] raw = new byte[outputSize * blocksSize];
    int i = 0;
    while (data.length - i * blockSize > 0) {
    if (data.length - i * blockSize > blockSize)
    cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
    else
    cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
    //这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中
    //,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
    i++;
    }
    return raw;
    } catch (Exception e) {
    throw new Exception(e.getMessage());
    }
    }
    /**
    * 解密
    * @param key 解密的密钥
    * @param raw 已经加密的数据
    * @return 解密后的明文
    * @throws Exception
    */
    public byte[] decrypt(Key key, byte[] raw) throws Exception {
    try {
    Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
    cipher.init(cipher.DECRYPT_MODE, key);
    int blockSize = cipher.getBlockSize();
    ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
    int j = 0;
    while (raw.length - j * blockSize > 0) {
    bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
    j++;
    }
    return bout.toByteArray();
    } catch (Exception e) {
    throw new Exception(e.getMessage());
    }
    }
    /**
    * 返回公钥
    * @return
    * @throws Exception
    */
    public RSAPublicKey getRSAPublicKey() throws Exception{
    //获取公钥
    RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic();
    //获取公钥系数(字节数组形式)
    byte[] pubModBytes = pubKey.getModulus().toByteArray();
    //返回公钥公用指数(字节数组形式)
    byte[] pubPubExpBytes = pubKey.getPublicExponent().toByteArray();
    //生成公钥
    RSAPublicKey recoveryPubKey = this.generateRSAPublicKey(pubModBytes,pubPubExpBytes);
    return recoveryPubKey;
    }
    /**
    * 获取私钥
    * @return
    * @throws Exception
    */
    public RSAPrivateKey getRSAPrivateKey() throws Exception{
    //获取私钥
    RSAPrivateKey priKey = (RSAPrivateKey) keyPair.getPrivate();
    //返回私钥系数(字节数组形式)
    byte[] priModBytes = priKey.getModulus().toByteArray();
    //返回私钥专用指数(字节数组形式)
    byte[] priPriExpBytes = priKey.getPrivateExponent().toByteArray();
    //生成私钥
    RSAPrivateKey recoveryPriKey = this.generateRSAPrivateKey(priModBytes,priPriExpBytes);
    return recoveryPriKey;
    }
    }

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
RSAUtil rsa = new RSAUtil();
String str = "yanzi1225627";
RSAPublicKey pubKey = rsa.getRSAPublicKey();
RSAPrivateKey priKey = rsa.getRSAPrivateKey();
byte[] enRsaBytes = rsa.encrypt(pubKey,str.getBytes());
String enRsaStr = new String(enRsaBytes, "UTF-8");
System.out.println("加密后==" + enRsaStr);
System.out.println("解密后==" + new String(rsa.decrypt(priKey, rsa.encrypt(pubKey,str.getBytes()))));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

下面是执行结果:
加密后==s?ko?1@lo????BJ?iE???1Ux?Kx&??=??nO? ?l?>?????2r?y??8v- \A??`????r?t3?-3y?hjL?M??Se?Z???????~?”??e??XZ?苜?
解密后==yanzi1225627
上面代码需要用到一个包rsa.jar,下载链接及上面的测试代码我已打包,下载链接见下:
http://download.csdn.net/detail/yanzi1225627/7382263

原文引自http://blog.csdn.net/yanzi1225627/article/details/26508035