1算凿、對稱加密和非對稱加密
對稱加密指的就是加密和解密使用同一個秘鑰。對稱加密只有一個秘鑰犁功,作為私鑰氓轰。
常見的對稱加密算法:DES,AES浸卦,3DES等等署鸡。
非對稱加密指的是加密和解密使用不同的秘鑰,一把作為公開的公鑰镐躲,另一把作為私鑰储玫。公鑰加密的信息,只有私鑰才能解密萤皂。私鑰加密的信息撒穷,只有公鑰才能解密。
常見的非對稱加密算法:RSA裆熙,ECC
對稱加密和非對稱加密不能說誰好誰不好端礼,主要是要看應(yīng)用場景,如果可以用對稱加密解決的問題入录,那么就沒有必要用非對稱進(jìn)行加密蛤奥。因?yàn)榉菍ΨQ加密的開銷一般比較大,例如RSA 1024的安全性,與AES128的安全性是相當(dāng)?shù)摹?/p>
2僚稿、對稱加密算法AES
對稱算法以DES和AES為代表性凡桥,其底層原理也有相似之處,都有一個S盒子(可不可以叫做黑盒子)蚀同,然后通過交換和替換等復(fù)雜變換缅刽,達(dá)到加密的效果。對稱加密算法有個非常重要的特性蠢络,就是加密和解密可以互逆衰猛。總之呢刹孔,算法還是挺復(fù)雜的啡省,不過,作為程序員的我們,可以不去關(guān)心這些卦睹,我們只要知道畦戒,我們可以用他們來做加密。加密的安全性取決于密鑰的長度结序,密鑰越長兢交,越安全如密鑰長為128的AES,我們稱之為AES128,密鑰為256的AES笼痹,我們稱之為AES256配喳。當(dāng)前計(jì)算機(jī)的計(jì)算能力下,128的AES基本是安全的凳干,256完全可以放心了晴裹。
好了,用java代碼來實(shí)現(xiàn)AES加解密吧救赐,這個才是我們關(guān)心的涧团。
加密方法:
public static final String VIPARA = "20179TELIGRR1234";//初始化向量 16位
private static final String DEFAULT_ENCODING = "utf-8";//編碼方式
/**
* AES加密
* @param content 待加密的內(nèi)容
* @param encryptKey 加密密鑰
* @return 加密后的byte[]
* @throws Exception
*/
public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes(DEFAULT_ENCODING));
SecretKeySpec keySpec = new SecretKeySpec(encryptKey.getBytes(DEFAULT_ENCODING),"AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,keySpec,zeroIv);
return cipher.doFinal(content.getBytes(DEFAULT_ENCODING));
}
這個加密方法傳入?yún)?shù)是文本內(nèi)容以及密鑰,注意密鑰長度要為16byte(與上述的初始化向量是一樣的)经磅。
解密方法類似:
/**
* AES解密
* @param encryptBytes 待解密的byte[]
* @param decryptKey 解密密鑰
* @return 解密后的String
* @throws Exception
*/
public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes(DEFAULT_ENCODING));
SecretKeySpec keySpec = new SecretKeySpec(decryptKey.getBytes(DEFAULT_ENCODING),"AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,keySpec,zeroIv);
byte[] decryptBytes = cipher.doFinal(encryptBytes);
return new String(decryptBytes);
}
上訴加密和解密方法泌绣,還是有挺多與密碼學(xué)相關(guān)的知識的,比如初始化向量预厌,以及加密的CBC模式阿迈,不過其實(shí),作為我們應(yīng)用層的轧叽,可以不去關(guān)心這些底層的實(shí)現(xiàn)苗沧,使用就可以完成我們的目標(biāo)了。不過炭晒,使用舊了待逞,自然而且,我們會好奇网严,它到底怎么實(shí)現(xiàn)的识樱?它為什么就是安全的?到時震束,我們就可以深入去學(xué)習(xí)了怜庸,希望大家這種好奇來的越早越好。
有了上訴的加密和解密方法驴一,我們就可以來進(jìn)行加解密了休雌,示例如下:
byte[] data1 = aesEncryptToBytes("DW1234567fddddddddddfffffffffff8","aaaaaaaaaaaaaaaa");
String data1Str = bytesToHexString(data1);
System.out.println("密文:"+data1Str);
String result1 = aesDecryptByBytes(hexStringToBytes(data1Str),"aaaaaaaaaaaaaaaa");
System.out.println("明文:"+result1);
這上面應(yīng)用到兩個函數(shù)bytesToHexString與hexStringToBytes灶壶,這個其實(shí)不是加解密的環(huán)節(jié)肝断,而是編解碼的環(huán)節(jié)了,它們的作用就是把字節(jié)轉(zhuǎn)成16進(jìn)制數(shù),以及它的逆操作胸懈,具體函數(shù)如下:
/**
* 字節(jié)數(shù)組轉(zhuǎn)十六進(jìn)制數(shù)
*
* @param b
* @return
*/
public static String bytesToHexString(byte[] b) {
if(b == null || b.length <= 0){
return null;
}
StringBuilder sb = new StringBuilder(b.length * 2);
for (int i = 0; i < b.length; i++) {
sb.append(HEXCHAR[(b[i] & 0xf0) >>> 4]);
sb.append(HEXCHAR[b[i] & 0x0f]);
}
return sb.toString();
}
/**
* 16進(jìn)制串字符轉(zhuǎn)字節(jié)
* 和 bytesToHexString 互逆
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString){
if(hexString == null || hexString.length() <= 0){
return null;
}
byte[] data = new byte[hexString.length()/2];
for(int i=0;i< hexString.length();){
char high = hexString.charAt(i);
i++;
char low = hexString.charAt(i);
data[i/2] = hexToByte(high,low);
i++;
}
return data;
}
3担扑、非對稱加密算法 RSA
非對稱加密算法都有一個數(shù)學(xué)依據(jù)的,比如RSA的數(shù)學(xué)依據(jù)總結(jié)起來可以說:兩個質(zhì)數(shù)相乘得到一個合數(shù)是很容易的趣钱,而兩個大質(zhì)數(shù)相乘得到的大數(shù)難以被因式分解涌献。
對于非對稱加密,你首先先要一對密鑰首有,一個公鑰燕垃,用來發(fā)布出去的,一個私鑰井联,只能自己擁有的卜壕。基于java的RSA密鑰生成方法如下:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);// 秘鑰長度為1024烙常,可以改成2048等
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();//公鑰
PrivateKey privateKey = keyPair.getPrivate();//私鑰
現(xiàn)在公鑰和私鑰都有了轴捎,首先我們需要把它們保存起來,可以直接保存為文件蚕脏,或者生成字符串等等侦副,下面我把他們保存為文件
byte[] publicKeyByte = publicKey.getEncoded();
byte[] privateKeyByte = privateKey.getEncoded();
BufferedOutputStream pukout = new BufferedOutputStream(
new FileOutputStream("src/com/lh/test/publicKey.key"));
BufferedOutputStream prkout = new BufferedOutputStream(
new FileOutputStream("src/com/lh/test/privateKey.key"));
pukout.write(publicKeyByte);
prkout.write(privateKeyByte);
pukout.flush();
prkout.flush();
保存的文件讀出如下:
BufferedInputStream pukIn = new BufferedInputStream(
new FileInputStream("src/com/lh/test/publicKey.key"));
BufferedInputStream prkIn = new BufferedInputStream(
new FileInputStream("src/com/lh/test/privateKey.key"));
byte[] puKeyByte = new byte[1024];
byte[] prKeyByte = new byte[1024];
pukIn.read(puKeyByte, 0, 1024);
prkIn.read(prKeyByte, 0, 1024);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
puKeyByte);// 公鑰
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
prKeyByte);// 私鑰
PrivateKey privateKey = keyFactory
.generatePrivate(pkcs8EncodedKeySpec);
現(xiàn)在我們通過文件讀取(或其它途徑)拿到了公鑰和私鑰驼鞭,我們就可以用來使用了秦驯,如果公鑰加密,那么需要私鑰進(jìn)行解密挣棕,反之亦然汇竭。
公鑰加密:
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] resultbytes = cipher.doFinal(plainBytes);
//plainBytes 要加密的字節(jié)數(shù)組
私鑰解密:
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] deBytes = cipher.doFinal(ciperBytes);
//ciperBytes要解密的字節(jié)數(shù)組
有了byte[],就可以根據(jù)上面的hexStringToBytes,bytesToHexString方法穴张,進(jìn)行字節(jié)數(shù)組與字符串的轉(zhuǎn)換细燎,便于傳輸。
4皂甘、安全哈希函數(shù)MD5
實(shí)際開發(fā)中玻驻,經(jīng)常可以聽到我們密碼字段是用MD5加密的偿枕,其實(shí)這種說法是不對的璧瞬,MD5(安全哈希函數(shù))只是一串?dāng)?shù)據(jù)指紋,只能用來做數(shù)據(jù)驗(yàn)證渐夸。即嗤锉,只能單向認(rèn)證,只能從明文單向計(jì)算出MD5值墓塌,而不能從MD5值計(jì)算出明文瘟忱。
安全哈希函數(shù)常用于驗(yàn)證信息完整性以及驗(yàn)證信息等奥额,比如協(xié)議的完整性校驗(yàn)、驗(yàn)證密碼访诱。
安全哈希函數(shù)有MD5/SHA1/SHA2/SHA3,現(xiàn)在推薦使用SHA2/SHA3
Java實(shí)現(xiàn)MD5如下
private static final String md5Key = "MD5";
private static final String defaultEncoding = "utf-8";
/**
* 以字符串為輸入輸出的Md5
* @param dataStr
* @return
*/
public static String md5Encrypt(String dataStr){
try {
MessageDigest md5 = MessageDigest.getInstance(md5Key);
byte [] data = md5.digest(dataStr.getBytes(defaultEncoding));
return bytesToHexString(data);
} catch (Exception e) {
return null;
}
}