android AES加密

来源:互联网 发布:linux用户和组管理 编辑:程序博客网 时间:2024/05/17 06:25
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://xuxiong3316.blog.51cto.com/8126857/1343251

今天花了一天的时间总是把AES加密这块做完了。总结下以备不时之需:网上版本太多了,还是自己写一些:

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
/***
 * 关于加解密JAVA一般使用的JCE,关于C++可以实现AES加解密的开源项目就多的数不胜数的。
 理论上上算法一样,对称密钥一样就能够互相识别了。
 相信很多人开始想法都同我一样,起初我JAVA用JCE,C++使用openssl。
 结果发现加密出的密文完全不相同。
 出现问题就要解决
 了解了一下JCE:
 JCE中AES支持五中模式:CBC,CFB,ECB,OFB,PCBC;支持三种填充:NoPadding,PKCS5Padding,ISO10126Padding。
 不支持SSL3Padding。不支持“NONE”模式。
 好原来有模式和填充一说。
 在OPENSSL中直接有一个CBC加解密函数。填充没找到,试过后发现C++加密出的内容比JAVA的要长出一截,前面的内容是完全一样的。
 这应该就出现在填充上。
 本来以为找到问题关键就应该很容易解决的。结果发现OPENSSL的填充是固定实现的,而我所需要解密的java端代码不能改动。
 一条路走不通咱就换条路。最后发现有一个开源项目Botan什么都有而且同JCE十分相似并且满足我要求垮平台,好就是它了。
 附:
 算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度
 AES/CBC/NoPadding 16 不支持
 AES/CBC/PKCS5Padding 32 16
 AES/CBC/ISO10126Padding 32 16
 AES/CFB/NoPadding 16 原始数据长度
 AES/CFB/PKCS5Padding 32 16
 AES/CFB/ISO10126Padding 32 16
 AES/ECB/NoPadding 16 不支持
 AES/ECB/PKCS5Padding 32 16
 AES/ECB/ISO10126Padding 32 16
 AES/OFB/NoPadding 16 原始数据长度
 AES/OFB/PKCS5Padding 32 16
 AES/OFB/ISO10126Padding 32 16
 AES/PCBC/NoPadding 16 不支持
 AES/PCBC/PKCS5Padding 32 16
 AES/PCBC/ISO10126Padding 32 16
 可以看到,在原始数据长度为16的整数倍时,
 假如原始数据长度等于16*n,
 则使用NoPadding时加密后数据长度等于16*n
 ,其它情况下加密数据长度等于16*(n+1)。
 在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16]
 ,除了NoPadding填充之外的任何方式,加密数据长度都等于16*(n+1)
 ;NoPadding填充情况下,CBC、ECB和PCBC三种模式是不支持的,CFB、OFB两种模式下则加密数据长度等于原始数据长度。
 */
package com.example.ex_crypto;
import java.io.UnsupportedEncodingException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
 * AES加密解密类
 *
 * @author xuxiong
 */
public class AESHelper {
    /** 算法/模式/填充 **/
    private static final String CipherMode = "AES/ECB/NoPadding";
    /**
     * 创建密钥
     *
     * @param password
     *            例如:"0123456701234567" 128位 16*8 所有密钥长度不能超过16字符中文占两个。192 24;
     *            256 32
     * @return SecretKeySpec 实例
     */
    private static SecretKeySpec createKey(String password) {
        byte[] data = null;
        if (password == null) {
            password = "";
        }
        StringBuffer sb = new StringBuffer(16);
        sb.append(password);
        while (sb.length() < 16) {
            sb.append("0");
        }
        if (sb.length() > 16) {
            sb.setLength(16);
        }
        try {
            data = sb.toString().getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return new SecretKeySpec(data, "AES");
    }
    /**
     * 加密字节数据
     *
     * @param content
     *            需要加密的字节数组
     * @param password
     *            密钥 128 <16个字节 192 <24,256 <32个字节
     * @return 加密完后的字节数组
     */
    public static byte[] encrypt(byte[] content, String password) {
        try {
            SecretKeySpec key = createKey(password);
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 加密(结果为16进制字符串)
     *
     * @param content
     *            要加密的字符串
     * @param password
     *            密钥
     * @return 加密后的16进制字符串
     */
    public static String encrypt(String content, String password) {
        byte[] data = null;
        try {
            data = content.getBytes("UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        data = encrypt(data, password);
        String result = byte2hex(data);
        return result;
    }
    /** 解密字节数组 **/
    public static byte[] decrypt(byte[] content, String password) {
        try {
            SecretKeySpec key = createKey(password);
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /** 解密16进制的字符串为字符串 **/
    public static String decrypt(String content, String password) {
        byte[] data = null;
        try {
            data = hex2byte(content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        data = decrypt(data, password);
        if (data == null)
            return null;
        String result = null;
        try {
            result = new String(data, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 字节数组转成16进制字符串
     *
     * @param b
     * @return 16进制字符串
     */
    public static String byte2hex(byte[] b) { // 一个字节的数,
        StringBuffer sb = new StringBuffer(b.length * 2);
        String tmp = "";
        for (int n = 0; n < b.length; n++) {
            // 整数转成十六进制表示
            tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
            if (tmp.length() == 1) {
                sb.append("0");
            }
            sb.append(tmp);
        }
        return sb.toString().toUpperCase(); // 转成大写
    }
    /**
     * 将hex字符串转换成字节数组 *
     *
     * @param 16进制的字符串
     * @return 字节数组
     */
    private static byte[] hex2byte(String inputString) {
        if (inputString == null || inputString.length() < 2) {
            return new byte[0];
        }
        inputString = inputString.toLowerCase();
        int l = inputString.length() / 2;
        byte[] result = new byte[l];
        for (int i = 0; i < l; ++i) {
            String tmp = inputString.substring(2 * i, 2 * i + 2);
            result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
        }
        return result;
    }
}

注意:创建密钥时要注意你用的是那种128,还是192还是256位 他们对应的密钥长度分别是16/24/32一个中文字符占用2个字节,今天就没有注意到这个,输出来的字符与实际的不一致,后来才发现。

最后调用方法就OK了。

1
2
3
4
//需要加密的字符  密钥
String encrypt = AESHelper.encrypt("0123456701234567", "0123456701234567");
// 加密完后的字符 密钥
String decrypt = AESHelper.decrypt(encrypt, "0123456701234567");

这样就可以了

153951224.png

153954694.png

好了这就样了。


本文出自 “android随记” 博客,请务必保留此出处http://xuxiong3316.blog.51cto.com/8126857/1343251

0 0
原创粉丝点击