数字摘要
MD5
Message Digest Algorithm MD5(消息摘要算法第五版),用于确保信息的完整一致。
MD5算法具有以下特点:
1. 压缩性:任意长度的数据,算出的MD5值长度都是固定的(128bit,32位十六进制)。
2. 容易计算:从原数据计算出MD5值很容易。
3. 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
4. 强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
MD5作用:
确定数据未被修改,保证文件(数据)的唯一性
使用场景:
用户的密码一般都是需要进行md5加密。
注意:
MD5是不可逆的,而且1KB的文件和1TB的文件的MD5值的长度是一样的。
SHA1
SHA1:160bit,40位十六进制
SHA1和MD5的算法不一样,但是作用是一样的,都是用来保证文件的唯一性。
在Android中,androidsupport v4包的版本冲突,就是根据SHA1值来进行判断的。
类似QQ、360的秒传功能,其实也是根据文件的SHA1来判断是否网盘已经有此正在发送的文件,若有,则无需用户再次上传。
加盐
加盐就是在密码的前面加入一些特殊字符,如:%^&&%^& 。
场景:
用户注册,密码111111 -> 对密码进行MD5 -> 保存密码的MD5值到数据库
风险:
假如数据库泄漏,现在网络上有很多MD5在线解析平台(其原理是将MD5值与一个庞大的数据库进行比对,最终得到原值),破坏者可以轻松得知用户的密码,用户信息遭到泄漏。
解决方案:
用户注册,密码111111 ->对密码加盐(&% + 111111 + ~%)->对加盐后的密码进行MD5 -> 保存MD5值到数据库
加盐实际上就是在原值的基础上随机的添加一些佐料,密码经过加盐后再存储到数据库中,破坏者哪怕得到了数据库里的MD5值,也无法还原出正确的用户密码,保证了用户信息的安全性。
加密解密
Android中常见的加密解密
对称加密算法
对称加密算法指的是加密和解密操作只有一把密钥,如果密钥暴露,文件就会被暴露。
常见的对称加密算法有:
l DES:Data Encryption Standard(数据加密标准)
l AES:Advanced Encryption Standard(高级加密标准),AES是在DES的基础上发展而来的
对称加密算法的特点:
l 加密速度比较快,可以加密比较大的文件
l 密码可以自己指定
非对称加密算法
非对称的加密算法有两把钥匙(密钥对),分别为公钥和私钥,公钥可以给别人,私钥需要自己保存。
常见加密:
RSA
加密解密方式:
一方用来加密,则另一方就用来解密,成对使用
l 公钥加密 -> 私钥解密
l 私钥加密 -> 公钥解密
数字签名:
私钥签名-->公钥校验,其目的在于确定数据来源的不可否认性,确定数据的所属关系
非对称加密算法的特点:
l 加密速度比慢一些,但是安全系数比较高
l 秘钥对需要程序生成,不能我们自己定义
加密解密实例
DES
工具类:
package com.billy.androidutils.utils.encrypt.des; import java.io.IOException; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import Decoder.BASE64Decoder; import Decoder.BASE64Encoder; public class Des { private final static String DES = "DES"; public static void main(String[] args) throws Exception { String data = "强大的黑马51期"; String key = "wang!@#$%"; System.err.println(encrypt(data, key)); // System.err.println(decrypt(encrypt(data, key), key)); } /** * Description 根据键值进行加密 * @param data * @param key 加密键byte数组 * @return * @throws Exception */ public static String encrypt(String data, String key) throws Exception { byte[] bt = encrypt(data.getBytes(), key.getBytes()); String strs = new BASE64Encoder().encode(bt); return strs; } /** * Description 根据键值进行解密 * @param data * @param key 加密键byte数组 * @return * @throws IOException * @throws Exception */ public static String decrypt(String data, String key) throws IOException, Exception { if (data == null) return null; BASE64Decoder decoder = new BASE64Decoder(); byte[] buf = decoder.decodeBuffer(data); byte[] bt = decrypt(buf, key.getBytes()); return new String(bt); } /** * Description 根据键值进行加密 * @param data * @param key 加密键byte数组 * @return * @throws Exception */ private static byte[] encrypt(byte[] data, byte[] key) throws Exception { // 生成一个可信任的随机数源 SecureRandom sr = new SecureRandom(); // 从原始密钥数据创建DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); SecretKey securekey = keyFactory.generateSecret(dks); // Cipher对象实际完成加密操作 Cipher cipher = Cipher.getInstance(DES); // 用密钥初始化Cipher对象 cipher.init(Cipher.ENCRYPT_MODE, securekey, sr); return cipher.doFinal(data); } /** * Description 根据键值进行解密 * @param data * @param key 加密键byte数组 * @return * @throws Exception */ private static byte[] decrypt(byte[] data, byte[] key) throws Exception { // 生成一个可信任的随机数源 SecureRandom sr = new SecureRandom(); // 从原始密钥数据创建DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); SecretKey securekey = keyFactory.generateSecret(dks); // Cipher对象实际完成解密操作 Cipher cipher = Cipher.getInstance(DES); // 用密钥初始化Cipher对象 cipher.init(Cipher.DECRYPT_MODE, securekey, sr); return cipher.doFinal(data); } }
加密:
/** 要被加密的数据 */
private String data = "需要加密的数据";
/** 秘钥 */
private String key = "%^&*()^&*(^&";
/**DES加密的结果 */
private String mDesEncryptResult;
……
//加密
mDesEncryptResult=Des.encrypt(data, key);
……
解密:
//解密
mDesDecryptResult = Des.decrypt(mDesEncryptResult, key);
AES
工具类:
package com.billy.androidutils.utils.encrypt.aes; import android.annotation.SuppressLint; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * AES加密 */ public class Aes { private final static String HEX = "0123456789ABCDEF"; public static String encrypt(String data, String password) throws Exception { byte[] rawKey = getRawKey(password.getBytes()); byte[] result = encrypt(rawKey, data.getBytes()); return toHex(result); } public static String decrypt(String encrypted, String password) throws Exception { byte[] rawKey = getRawKey(password.getBytes()); byte[] enc = toByte(encrypted); byte[] result = decrypt(rawKey, enc); return new String(result); } @SuppressLint("TrulyRandom") private static byte[] getRawKey(byte[] password) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance("AES"); SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); sr.setSeed(password); kgen.init(128, sr); // 192 and 256 bits may not be available SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); return raw; } private static byte[] encrypt(byte[] raw, byte[] data) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] encrypted = cipher.doFinal(data); return encrypted; } private static byte[] decrypt(byte[] raw, byte[] data) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] decrypted = cipher.doFinal(data); return decrypted; } private 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; } private 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)); } }
加密:
/** 要被加密的数据 */ …… // 加密 …… |
解密:
// 解密 |
RSA
在进行RSA加密解密之前,先要获取RSA的密钥对。不同于AES和DES,AES和DES的秘钥只有一个,而且可以自己指定,但是RSA的密钥对是代码生成的,不能由我们来定义。
包含:
KeyGen:密钥生成算法,PK,SK=KeyGen()
Encrypt:加密算法,X=Encrypt(PK,M)
Decrypt:解密算法,M=Decrypt(SK,X)
Sign:签名算法,sign= Sign(SK,M)。
Verify:验证算法,b= Verify(PK,sign,M)
注:PK:公钥。SK:密钥。M:明文。X:密文。sign:明文的签名
用途:
a:公钥加密,私钥解密。用于传输数据的加密。
b: 私钥签名,公钥验签。用于传输数据的完整性校验。
优点:加密解密使用不同的密钥
缺点:加密解密速度慢,资源消耗大
工具类:
package com.billy.androidutils.utils.encrypt.rsa; import java.io.ByteArrayOutputStream; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; import javax.crypto.Cipher; import Decoder.BASE64Decoder; import Decoder.BASE64Encoder; public class RSACrypt { /** * 文件读取缓冲区大小 */ private static final int CACHE_SIZE = 1024; /** * <p> * BASE64字符串解码为二进制数据 * </p> * * @param base64 * @return * @throws Exception */ public static byte[] decode(String base64) throws Exception { return new BASE64Decoder().decodeBuffer(base64); } /** * <p> * 二进制数据编码为BASE64字符串 * </p> * * @param bytes * @return * @throws Exception */ public static String encode(byte[] bytes) throws Exception { return new BASE64Encoder().encode(bytes); } /** * 加密算法RSA */ // public static final String KEY_ALGORITHM = "RSA"; public static final String KEY_ALGORITHM = "RSA"; /** * 签名算法 */ public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; /** * 获取公钥的key */ private static final String PUBLIC_KEY = "RSAPublicKey"; /** * 获取私钥的key */ private static final String PRIVATE_KEY = "RSAPrivateKey"; /** * RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 117; /** * RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 128; /** * <p> * 生成密钥对(公钥和私钥) * </p> * * @return * @throws Exception */ public static Map<String, Object> genKeyPair() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGen.initialize(1024); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * <p> * 生成密钥对(公钥和私钥) * </p> * * @return * @throws Exception */ public static Map<String, Object> genKeyPair(String seed) throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGen.initialize(1024); // SecureRandom secrand = new SecureRandom(); // secrand.setSeed(seed.getBytes()); // 初始化随机产生器 // keyPairGen.initialize(1024, secrand); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * <p> * 用私钥对信息生成数字签名 * </p> * * @param data * 已加密数据 * @param privateKey * 私钥(BASE64编码) * * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { byte[] keyBytes = decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(privateK); signature.update(data); return encode(signature.sign()); } /** * <p> * 校验数字签名 * </p> * * @param data * 已加密数据 * @param publicKey * 公钥(BASE64编码) * @param sign * 数字签名 * * @return * @throws Exception * */ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { byte[] keyBytes = decode(publicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PublicKey publicK = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(publicK); signature.update(data); return signature.verify(decode(sign)); } /** * <P> * 私钥解密 * </p> * * @param encryptedData * 已加密数据 * @param privateKey * 私钥(BASE64编码) * @return * @throws Exception */ public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception { byte[] keyBytes = decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); // Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING"); cipher.init(Cipher.DECRYPT_MODE, privateK); int inputLen = encryptedData.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; } /** * <p> * 公钥解密 * </p> * * @param encryptedData * 已加密数据 * @param publicKey * 公钥(BASE64编码) * @return * @throws Exception */ public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception { byte[] keyBytes = decode(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicK = keyFactory.generatePublic(x509KeySpec); // Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING"); cipher.init(Cipher.DECRYPT_MODE, publicK); int inputLen = encryptedData.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; } /** * <p> * 公钥加密 * </p> * * @param data * 源数据 * @param publicKey * 公钥(BASE64编码) * @return * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { byte[] keyBytes = decode(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicK = keyFactory.generatePublic(x509KeySpec); // 对数据加密 // Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING"); cipher.init(Cipher.ENCRYPT_MODE, publicK); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段加密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } /** * <p> * 私钥加密 * </p> * * @param data * 源数据 * @param privateKey * 私钥(BASE64编码) * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception { byte[] keyBytes = decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); // Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING"); cipher.init(Cipher.ENCRYPT_MODE, privateK); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段加密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } /** * <p> * 获取私钥 * </p> * * @param keyMap * 密钥对 * @return * @throws Exception */ public static String getPrivateKey(Map<String, Object> keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return encode(key.getEncoded()); } /** * <p> * 获取公钥 * </p> * * @param keyMap * 密钥对 * @return * @throws Exception */ public static String getPublicKey(Map<String, Object> keyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return encode(key.getEncoded()); } }
生成RSA密钥对:
/** RSA加密的公钥 */ …… /** |
加密:
/** RSA私钥加密的结果 */ …… // 加密 |
解密:
// 解密 |
RSA公钥互换
公钥互换指的是两个人或者两个机构互相交换公钥,换句话就是说两个密钥对的持有者相互的置换公钥。
场景:
A机构有私钥“A private key”和公钥“A public key”;
B机构有私钥“B private key”和“B public key”;
A机构和B机构互相置换了公钥;
置换后A机构持有:“A private key”、“A public key”、“B public key”;
置换后B机构持有:“B private key”、“B public key”、“A public key”;
当A机构要向B机构发送数据的时候,可以用“A private key”或者“B public key”对数据进行加密,B机构就可以用”A public key”或者“B private key”对加密的数据进行解密了。
公钥互换的作用:
1. 加密解密
2. 签名校验
数字签名
数字签名的作用就是确定数据来源的不可否认性,确定数据的所属关系(其实就是确定privatekey在哪里)。
注意:
数字签名只能用私钥进行加密,用公钥进行校验
原理:
数字签名必须用私钥来做加密,那么校验的时候就只能用公钥。
而私钥是由个人自己保存的,是不能告诉外界的,能告诉给外界的只有自己的公钥。
因此,若一个加密的数据能够被一个公钥校验,那么就可以唯一的确定这个加密数据的私钥来自哪里。
public class Test { |