原文地址:https://blog.csdn.net/whb20081815/article/details/73997645
一:什么是AES加密
AES高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
AES 是一个迭代的、
对称密钥分组
的密码,
AES算法加密强度大,执行效率高,使用简单,实际开发中建议选择AES 算法。
-
二:AES加密安卓中的实现安卓中AES加密方案如下:安卓里面提供了2个API:SecretKeySpec和Cipher明文和密文,由Ciper提供加密和解密的函数
加密的具体过程:
String filecontent = CXAESUtil.encrypt(AESKEY, inputString);//加密后的
/** * 加密 * * @param key * 密钥 * @param src * 加密文本 * @return * @throws Exception */ public static String encrypt(String key, String src) throws Exception { // /src = Base64.encodeToString(src.getBytes(), Base64.DEFAULT); byte[] rawKey = toMakekey(key, keyLenght, defaultV).getBytes();// key.getBytes(); byte[] result = encrypt(rawKey, src.getBytes("utf-8")); // result = Base64.encode(result, Base64.DEFAULT); return toHex(result); }
/** * 真正的加密过程 * 1.通过密钥得到一个密钥专用的对象SecretKeySpec * 2.Cipher 加密算法,加密模式和填充方式三部分或指定加密算 (可以只用写算法然后用默认的其他方式)Cipher.getInstance("AES"); * @param key * @param src * @return * @throws Exception */ private static byte[] encrypt(byte[] key, byte[] src) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] encrypted = cipher.doFinal(src); return encrypted; }
/** * 二进制转字符,转成了16进制 * 0123456789abcdefg * @param buf * @return */ public static String toHex(byte[] buf) { if (buf == null) return ""; StringBuffer result = new StringBuffer(2 * buf.length); for (int i = 0; i < buf.length; i++) { appendHex(result, buf[i]); } return result.toString(); } private static void appendHex(StringBuffer sb, byte b) { sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f)); }
注意的问题:先前一直失败,其重点是对 “填充模式” 的应用,我最终使用了AES/CFB/NoPadding,当不满16字节时,加密后数据长度不变。
Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
- 算法/模式/填充 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两种模式下则加密数据长度等于原始数据长度。
三:AES的算法分析AES的加密算法解析:
根据使用的密码长度,AES最常见的有3种方案,用以适应不同的场景要求,分别是AES-128、AES-192和AES-256。本文主要对AES-128进行介绍,另外两种的思路基本一样,只是轮数会适当增加。
算法中16字节的明文、密文和轮密钥都以一个4x4的矩阵表示。AES加密有很多轮的重复和变换。大致步骤如下:1、密钥扩展(KeyExpansion),2、初始轮(Initial Round),3、重复轮(Rounds),每一轮又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey,4、最终轮(Final Round),最终轮没有MixColumns。
- Rijnadel(State, CipherKey) {
- //初始化
- KeyExpansion( CipherKey, ExpandedKey );//生成子密钥
- AddRoundKey( State, ExpandedKey );//与子密钥位与
- // 前Nr-1轮
- for(i =1; i < Nr; i++) {
- ByteSub(State);// S-盒
- ShiftRow(State);// 行被移位
- MixColumn(State);// 列被混叠
- AddRoundKey (State, ExpandedKey ); //与子密钥位与
- }
- //最后一轮
- ByteSub(State);
- ShiftRow(State);
- AddRoundKey (State, ExpandedKey );
- }
从图上可以看出AES加解密包括10轮,前面9轮包含
S盒变换、行移位、列混淆、轮密钥加4个阶段
,最后一轮则少了列混淆这个阶段。
解密过程:
/** * 解密 * * @param key * 密钥 * @param encrypted * 待揭秘文本 * @return * @throws Exception */ public static String decrypt(String key, String encrypted) throws Exception { byte[] rawKey = toMakekey(key, keyLenght, defaultV).getBytes();// key.getBytes(); byte[] enc = toByte(encrypted); // enc = Base64.decode(enc, Base64.DEFAULT); byte[] result = decrypt(rawKey, enc); // /result = Base64.decode(result, Base64.DEFAULT); return new String(result, "utf-8"); }
/** * 真正的解密过程 * * @param key * @param encrypted * @return * @throws Exception */ private static byte[] decrypt(byte[] key, byte[] encrypted) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; }
加密总结:
2如果先把一个文件转换成字节数组,然后再加密,最后生成文件,这样很大机率很会产生OOM,所以这里利用了FileChannel,一次读取一定的字节数,而后再进行加密解密,最后再通过Channel生成新文件
3.一个加密的密钥和加密的字符串,然后通过AES算法密码把字节数组加密,然后转换成了16进制,得到数字和字母
4.加密和解密都一样,就是在设置AES模式(加密和解密)不一样,还有一个就是对内容的处理,字节和16进制的相互转换
5.
同样的代码,Android和java的运行结果是不同的,
这应该是2端的编码不同导致的。
四:为什么采用对称加密的方式?
非对称加密一般不会单独拿来使用,他并不是为了取代对称加密而出现的,非对称加密速度比对称加密慢很多,
极端情况下会慢1000 倍,所以一般不会用来加密大量数据,通常我们经常会将对称加密和非对称加密两种技术联合起来使用,例如用非对称加密来给称加密里的秘钥进行加密(即秘钥交换)。
五:AES加密在安卓中的运用
- 做一个管理密码的app,我们在不同的网站里使用不同账号密码,很难记住,想做个app 统一管理,但是账号密码保存在手机里,一旦丢失了容易造成安全隐患,所以需要一种加密算法,将账号密码信息加密起来保管,这时候如果使用对称加密算法,将数据进行加密,秘钥我们自己记在心里,只需要记住一个密码。需要的时候可以还原信息。
- android 里需要把一些敏感数据保存到SharedPrefrence 里的时候,也可以使用对称加密,这样可以在需要的时候还原。
- 请求网络接口的时候,我们需要上传一些敏感数据,同样也可以使用对称加密,服务端使用同样的算法就可以解密。或者服务端需要给客户端传递数据,同样也可以先加密,然后客户端使用同样算法解密。
六;安卓中用的到加密
1.Https编程 :应该是使用带安全的网络协议处理。除非你本地需要加密
2.数据签名:混淆代码和防二次打包的APK加密技术
3.对称加密:可以先将数据通过某种加密方式加密发送到服务器端,然后服务器端再解密 ,项目中除了登陆,支付等接口采用rsa非对称加密,之外的采用aes对称加密
4.非对称加密====支付宝
final EditText editText=(EditText)findViewById(R.id.edit_query); final TextView tv_result=(TextView)findViewById(R.id.tv_result); final TextView tv_before=(TextView)findViewById(R.id.tv_before); Button btn_entry=(Button)findViewById(R.id.btn_encry); btn_entry.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { String inputString=editText.getText().toString(); Log.i(TAG,"输入的内容="+inputString); String filecontent = CXAESUtil.encrypt(AESKEY, inputString);//加密后的 tv_before.setText("加密后的内容:"+filecontent); Log.i(TAG,"加密后的内容="+filecontent); String decryptString = CXAESUtil.decrypt(AESKEY, filecontent);//加密后的 Log.i(TAG,"解密后的内容="+decryptString); tv_result.setText("解密后的内容:"+decryptString); } catch (Exception e) { e.printStackTrace(); } } }); Button btn_reset=(Button)findViewById(R.id.btn_reset); btn_reset.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { editText.getText().clear(); } });
package password.aes.peng.cx.com.aesdemo; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class CXAESUtil { private final static String HEX = "0123456789ABCDEF"; private static final int keyLenght = 16; private static final String defaultV = "0"; /** * 加密 * * @param key * 密钥 * @param src * 加密文本 * @return * @throws Exception */ public static String encrypt(String key, String src) throws Exception { // /src = Base64.encodeToString(src.getBytes(), Base64.DEFAULT); byte[] rawKey = toMakekey(key, keyLenght, defaultV).getBytes();// key.getBytes(); byte[] result = encrypt(rawKey, src.getBytes("utf-8")); // result = Base64.encode(result, Base64.DEFAULT); return toHex(result); } /** * 加密 * * @param key * 密钥 * @param src * 加密文本 * @return * @throws Exception */ public static String encrypt2Java(String key, String src) throws Exception { // /src = Base64.encodeToString(src.getBytes(), Base64.DEFAULT); byte[] rawKey = toMakekey(key, keyLenght, defaultV).getBytes();// key.getBytes(); byte[] result = encrypt2Java(rawKey, src.getBytes("utf-8")); // result = Base64.encode(result, Base64.DEFAULT); return toHex(result); } /** * 解密 * * @param key * 密钥 * @param encrypted * 待揭秘文本 * @return * @throws Exception */ public static String decrypt(String key, String encrypted) throws Exception { byte[] rawKey = toMakekey(key, keyLenght, defaultV).getBytes();// key.getBytes(); byte[] enc = toByte(encrypted); // enc = Base64.decode(enc, Base64.DEFAULT); byte[] result = decrypt(rawKey, enc); // /result = Base64.decode(result, Base64.DEFAULT); return new String(result, "utf-8"); } /** * 密钥key ,默认补的数字,补全16位数,以保证安全补全至少16位长度,android和ios对接通过 * @param str * @param strLength * @param val * @return */ private static String toMakekey(String str, int strLength, String val) { int strLen = str.length(); if (strLen < strLength) { while (strLen < strLength) { StringBuffer buffer = new StringBuffer(); buffer.append(str).append(val); str = buffer.toString(); strLen = str.length(); } } return str; } /** * 真正的加密过程 * 1.通过密钥得到一个密钥专用的对象SecretKeySpec * 2.Cipher 加密算法,加密模式和填充方式三部分或指定加密算 (可以只用写算法然后用默认的其他方式)Cipher.getInstance("AES"); * @param key * @param src * @return * @throws Exception */ private static byte[] encrypt(byte[] key, byte[] src) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] encrypted = cipher.doFinal(src); return encrypted; } /** * 真正的加密过程 * * @param key * @param src * @return * @throws Exception */ private static byte[] encrypt2Java(byte[] key, byte[] src) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] encrypted = cipher.doFinal(src); return encrypted; } /** * 真正的解密过程 * * @param key * @param encrypted * @return * @throws Exception */ private static byte[] decrypt(byte[] key, byte[] encrypted) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; } public static String toHex(String txt) { return toHex(txt.getBytes()); } public static String fromHex(String hex) { return new String(toByte(hex)); } /** * 把16进制转化为字节数组 * @param hexString * @return */ public static byte[] toByte(String hexString) { int len = hexString.length() / 2; byte[] result = new byte[len]; for (int i = 0; i < len; i++) result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue(); return result; } /** * 二进制转字符,转成了16进制 * 0123456789abcdefg * @param buf * @return */ public static String toHex(byte[] buf) { if (buf == null) return ""; StringBuffer result = new StringBuffer(2 * buf.length); for (int i = 0; i < buf.length; i++) { appendHex(result, buf[i]); } return result.toString(); } private static void appendHex(StringBuffer sb, byte b) { sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f)); } /** * 初始化 AES Cipher * @param sKey * @param cipherMode * @return */ public static Cipher initAESCipher(String sKey, int cipherMode) { // 创建Key gen // KeyGenerator keyGenerator = null; Cipher cipher = null; try { /* * keyGenerator = KeyGenerator.getInstance("AES"); * keyGenerator.init(128, new SecureRandom(sKey.getBytes())); * SecretKey secretKey = keyGenerator.generateKey(); byte[] * codeFormat = secretKey.getEncoded(); SecretKeySpec key = new * SecretKeySpec(codeFormat, "AES"); cipher = * Cipher.getInstance("AES"); //初始化 cipher.init(cipherMode, key); */ byte[] rawKey = toMakekey(sKey, keyLenght, defaultV).getBytes(); SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES"); cipher = Cipher.getInstance("AES"); cipher.init(cipherMode, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); // To change body of catch statement use File | // Settings | File Templates. } catch (NoSuchPaddingException e) { e.printStackTrace(); // To change body of catch statement use File | // Settings | File Templates. } catch (InvalidKeyException e) { e.printStackTrace(); // To change body of catch statement use File | // Settings | File Templates. } catch (InvalidAlgorithmParameterException e) { // TODO Auto-generated catch block e.printStackTrace(); } return cipher; } /** * 对文件进行AES加密 * @param sourceFile * @param fileType * @param sKey * @return */ public static File encryptFile(File sourceFile, String toFile, String dir, String sKey) { // 新建临时加密文件 File encrypfile = null; InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = new FileInputStream(sourceFile); encrypfile = new File(dir + toFile); outputStream = new FileOutputStream(encrypfile); Cipher cipher = initAESCipher(sKey, Cipher.ENCRYPT_MODE); // 以加密流写入文件 CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); byte[] cache = new byte[1024]; int nRead = 0; while ((nRead = cipherInputStream.read(cache)) != -1) { outputStream.write(cache, 0, nRead); outputStream.flush(); } cipherInputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); // To change body of catch statement use File | // Settings | File Templates. } catch (IOException e) { e.printStackTrace(); // To change body of catch statement use File | // Settings | File Templates. } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); // To change body of catch statement use // File | Settings | File Templates. } try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); // To change body of catch statement use // File | Settings | File Templates. } } return encrypfile; } /** * AES方式解密文件 * @param sourceFile * @return */ public static File decryptFile(File sourceFile, String toFile, String dir, String sKey) { File decryptFile = null; InputStream inputStream = null; OutputStream outputStream = null; try { decryptFile = new File(dir + toFile); Cipher cipher = initAESCipher(sKey, Cipher.DECRYPT_MODE); inputStream = new FileInputStream(sourceFile); outputStream = new FileOutputStream(decryptFile); CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher); byte[] buffer = new byte[1024]; int r; while ((r = inputStream.read(buffer)) >= 0) { cipherOutputStream.write(buffer, 0, r); } cipherOutputStream.close(); } catch (IOException e) { e.printStackTrace(); // To change body of catch statement use File | // Settings | File Templates. } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); // To change body of catch statement use // File | Settings | File Templates. } try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); // To change body of catch statement use // File | Settings | File Templates. } } return decryptFile; } }
DEMO地址:不知道为什么上传不了。