Java加密解密全解
1 sha加密:
安全雜湊演算法(Secure Hash Algorithm)主要適用於數字簽名標準(Digital Signature Standard DSS)裡面定義的數字簽名演算法(Digital Signature Algorithm DSA)。對於長度小於2^64位的訊息,SHA1會產生一個160位的訊息摘要。該演算法經過加密專家多年來的發展和改進已日益完善,並被廣泛使用。該演算法的思想是接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文,也可以簡單的理解為取一串輸入碼(稱為預對映或資訊),並把它們轉化為長度較短、位數固定的輸出序列即雜湊值(也稱為資訊摘要或資訊認證程式碼)的過程。雜湊函式值可以說是對明文的一種“
雜湊演算法
雜湊是資訊的提煉,通常其長度要比資訊小得多,且為一個固定長度。加密性強的雜湊一定是不可逆的,這就意味著通過雜湊結果,無法推出任何部分的原始資訊。任何輸入資訊的變化,哪怕僅一位,都將導致雜湊結果的明顯變化,這稱之為雪崩效應。雜湊還應該是防衝突的,即找不出具有相同雜湊結果的兩條資訊。具有這些特性的雜湊結果就可以用於驗證資訊是否被修改。原理
SHA-1與MD5的比較
因為二者均由MD4匯出,SHA-1和MD5彼此很相似。相應的,他們的強度和其他特性也是相似,但還有以下幾點不同: l 對強行攻擊的安全性:最顯著和最重要的區別是SHA-1摘要比MD5摘要長32 位。使用強行技術,產生任何一個報文使其摘要等於給定報摘要的難度對MD5是2^128數量級的操作,而對SHA-1則是2^160數量級的操作。這樣,SHA-1對強行攻擊有更大的強度。 l 對密碼分析的安全性:由於MD5的設計,易受密碼分析的攻擊,SHA-1顯得不易受這樣的攻擊。 l 速度:在相同的硬體上,SHA-1的執行速度比MD5慢。JAVA 已經實現了 SHA-256 和 SHA-512 兩種 Hash 演算法
利用 java.security.MessageDigest 呼叫已經整合的 Hash 演算法
建立 Encrypt 物件,並呼叫 SHA256 或者 SHA512 並傳入要加密的文字資訊,分別得到 SHA-256 或 SHA-512 兩種被加密的 hash 串。
若要改為 MD5 演算法,修改傳入引數 strType 為 "MD5" 即可得到 MD5 加密功能。
/**
* @file Encrypt.java
* @date 2016年8月5日
* @version 3.4.1
*
* Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
*/
package encrypt;
/**
*
*
* @author chengjian.he
* @version 3.4, 2016年8月5日 上午10:05:37
* @since
*/
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Encrypt
{
/**
* 傳入文字內容,返回 SHA-256 串
*
* @param strText
* @return
*/
public String SHA256(final String strText)
{
return SHA(strText, "SHA-256");
}
/**
* 傳入文字內容,返回 SHA-512 串
*
* @param strText
* @return
*/
public String SHA512(final String strText)
{
return SHA(strText, "SHA-512");
}
/**
* 字串 SHA 加密
*
* @param strSourceText
* @return
*/
private String SHA(final String strText, final String strType)
{
// 返回值
String strResult = null;
// 是否是有效字串
if (strText != null && strText.length() > 0)
{
try
{
// SHA 加密開始
// 建立加密物件 並傳入加密型別
MessageDigest messageDigest = MessageDigest.getInstance(strType);
// 傳入要加密的字串
messageDigest.update(strText.getBytes());
// 得到 byte 型別結果
byte byteBuffer[] = messageDigest.digest();
// 將 byte 轉換為 string
StringBuffer strHexString = new StringBuffer();
// 遍歷 byte buffer
for (int i = 0; i < byteBuffer.length; i++)
{
String hex = Integer.toHexString(0xff & byteBuffer[i]);
if (hex.length() == 1)
{
strHexString.append('0');
}
strHexString.append(hex);
}
// 得到返回結果
strResult = strHexString.toString();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
}
return strResult;
}
public static void main(String args[]){
Encrypt ey = new Encrypt();
System.out.println(ey.SHA("ILoveYou", "MD5"));//62accaf23ac9a73c0b28765b7dfaf75a
}
}
2 Base64
Base64是網路上最常見的用於傳輸8Bit位元組程式碼的編碼方式之一,大家可以檢視RFC2045~RFC2049,上面有MIME的詳細規範。Base64編碼可用於在HTTP環境下傳遞較長的標識資訊。例如,在Java
Persistence系統Hibernate中,就採用了Base64來將一個較長的唯一識別符號(一般為128-bit的UUID)編碼為一個字串,用作HTTP表單和HTTP
GET URL中的引數。在其他應用程式中,也常常需要把二進位制資料編碼為適合放在URL(包括隱藏表單域)中的形式。此時,採用Base64編碼具有不可讀性,即所編碼的資料不會被人用肉眼所直接看到。
/**
* @file Base64.java
* @date 2016年8月5日
* @version 3.4.1
*
* Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
*/
package encrypt;
import java.io.UnsupportedEncodingException;
import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;
/**
*
*
* @author chengjian.he
* @version 3.4, 2016年8月5日 上午10:32:23
* @since Yeexun 3.4
*/
public class Base64 {
// 加密
public String getBase64(String str) {
byte[] b = null;
String s = null;
try {
b = str.getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (b != null) {
s = new BASE64Encoder().encode(b);
}
return s;
}
// 解密
public String getFromBase64(String s) {
byte[] b = null;
String result = null;
if (s != null) {
BASE64Decoder decoder = new BASE64Decoder();
try {
b = decoder.decodeBuffer(s);
result = new String(b, "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
public static void main(String args[]){
Base64 b6 = new Base64();
System.out.println(b6.getBase64("ILoveYou"));
System.out.println(b6.getFromBase64(b6.getBase64("ILoveYou")));
}
}
SUxvdmVZb3U=
ILoveYou
3 Base64Encoder
一直以來Base64的加密解密都是使用sun.misc包下的BASE64Encoder及BASE64Decoder的sun.misc.BASE64Encoder/BASE64Decoder類。這人個類是sun公司的內部方法,並沒有在java
api中公開過,不屬於JDK標準庫範疇,但在JDK中包含了該類,可以直接使用。
/**
* @file Base64Encoder.java
* @date 2016年8月5日
* @version 3.4.1
*
* Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
*/
package encrypt;
/**
*
*
* @author chengjian.he
* @version 3.4, 2016年8月5日 上午10:44:22
* @since Yeexun 3.4
*/
public class Base64Encoder {
public static String getBASE64(String s) {
if (s == null)
return null;
return (new sun.misc.BASE64Encoder()).encode(s.getBytes());
}
// 將 BASE64 編碼的字串 s 進行解碼 解密
public static String getFromBASE64(String s) {
if (s == null)
return null;
sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
try {
byte[] b = decoder.decodeBuffer(s);
return new String(b);
} catch (Exception e) {
return null;
}
}
public static String mTOa(Object ming){
return Base64Encoder.getBASE64(Base64Encoder.getBASE64(Base64Encoder.getBASE64((String)ming)));
}
public static String aTOm(String an){
return Base64Encoder.getFromBASE64(Base64Encoder.getFromBASE64(Base64Encoder.getFromBASE64(an)));
}
public static void main(String[] args) {
String a = mTOa("100000.89".toString());
System.out.println(a);//加密
System.out.println(aTOm(a));//解密
}
}
4 RSA
RSA公鑰加密演算法是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。1987年首次公佈,當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。 RSA是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰資料加密標準。 今天只有短的RSA鑰匙才可能被強力方式解破。到2008年為止,世界上還沒有任何可靠的攻擊RSA演算法的方式。只要其鑰匙的長度足夠長,用RSA加密的資訊實際上是不能被解破的。但在分散式計算和量子計算機理論日趨成熟的今天,RSA加密安全性受到了挑戰。 RSA演算法基於一個十分簡單的數論事實:將兩個大質數相乘十分容易,但是想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密金鑰。缺點
編輯 1)產生金鑰很麻煩,受到素數產生技術的限制,因而難以做到一次一密。 2)安全性,RSA的安全性依賴於大數的因子分解,但並沒有從理論上證明破譯RSA的難度與大數分解難度等價,而且密碼學界多數人士傾向於因子分解不是NP問題。現今,人們已能分解140多個十進位制位的大素數,這就要求使用更長的金鑰,速度更慢;另外,人們正在積極尋找攻擊RSA的方法,如選擇密文攻擊,一般攻擊者是將某一資訊作一下偽裝(Blind),讓擁有私鑰的實體簽署。然後,經過計算就可得到它所想要的資訊。實際上,攻擊利用的都是同一個弱點,即存在這樣一個事實:乘冪保留了輸入的乘法結構: (XM)d = Xd *Md mod n 前面已經提到,這個固有的問題來自於公鑰密碼系統的最有用的特徵--每個人都能使用公鑰。但從演算法上無法解決這一問題,主要措施有兩條:一條是採用好的公鑰協議,保證工作過程中實體不對其他實體任意產生的資訊解密,不對自己一無所知的資訊簽名;另一條是決不對陌生人送來的隨機文件簽名,簽名時首先使用One-Way Hash Function對文件作HASH處理,或同時使用不同的簽名演算法。除了利用公共模數,人們還嘗試一些利用解密指數或φ(n)等等攻擊. 3)速度太慢,由於RSA 的分組長度太大,為保證安全性,n 至少也要 600 bits以上,使運算代價很高,尤其是速度較慢,較對稱密碼演算法慢幾個數量級;且隨著大數分解技術的發展,這個長度還在增加,不利於資料格式的標準化。SET(Secure Electronic Transaction)協議中要求CA採用2048位元長的金鑰,其他實體使用1024位元的金鑰。為了速度問題,人們廣泛使用單,公鑰密碼結合使用的方法,優缺點互補:單鑰密碼加密速度快,人們用它來加密較長的檔案,然後用RSA來給檔案金鑰加密,極好的解決了單鑰密碼的金鑰分發問題。/**
* @file RSATool.java
* @date 2016年8月5日
* @version 3.4.1
*
* Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
*/
package encrypt;
/**
*
*
* @author chengjian.he
* @version 3.4, 2016年8月5日 上午10:51:35
* @since Yeexun 3.4
*/
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
public class RSATool {
public static void makekeyfile(String pubkeyfile, String privatekeyfile)
throws NoSuchAlgorithmException, FileNotFoundException, IOException {
// KeyPairGenerator類用於生成公鑰和私鑰對,基於RSA演算法生成物件
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化金鑰對生成器,金鑰大小為1024位
keyPairGen.initialize(1024);
// 生成一個金鑰對,儲存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私鑰
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公鑰
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 生成私鑰
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
privatekeyfile));
oos.writeObject(privateKey);
oos.flush();
oos.close();
oos = new ObjectOutputStream(new FileOutputStream(pubkeyfile));
oos.writeObject(publicKey);
oos.flush();
oos.close();
System.out.println("make file ok!");
}
/**
*
* @param k
* @param data
* @param encrypt
* 1 加密 0解密
* @return
* @throws NoSuchPaddingException
* @throws Exception
*/
public static byte[] handleData(Key k, byte[] data, int encrypt)
throws Exception {
if (k != null) {
Cipher cipher = Cipher.getInstance("RSA");
if (encrypt == 1) {
cipher.init(Cipher.ENCRYPT_MODE, k);
byte[] resultBytes = cipher.doFinal(data);
return resultBytes;
} else if (encrypt == 0) {
cipher.init(Cipher.DECRYPT_MODE, k);
byte[] resultBytes = cipher.doFinal(data);
return resultBytes;
} else {
System.out.println("引數必須為: 1 加密 0解密");
}
}
return null;
}
public static void main(String[] args) throws Exception {
String pubfile = "d:/temp/pub.key";
String prifile = "d:/temp/pri.key";
makekeyfile(pubfile, prifile);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(pubfile));
RSAPublicKey pubkey = (RSAPublicKey) ois.readObject();
ois.close();
ois = new ObjectInputStream(new FileInputStream(prifile));
RSAPrivateKey prikey = (RSAPrivateKey) ois.readObject();
ois.close();
// 使用公鑰加密
String msg = "~O(∩_∩)O哈哈~";
String enc = "UTF-8";
// 使用公鑰加密私鑰解密
System.out.println("原文: " + msg);
byte[] result = handleData(pubkey, msg.getBytes(enc), 1);
System.out.println("加密: " + new String(result, enc));
byte[] deresult = handleData(prikey, result, 0);
System.out.println("解密: " + new String(deresult, enc));
msg = "嚯嚯";
// 使用私鑰加密公鑰解密
System.out.println("原文: " + msg);
byte[] result2 = handleData(prikey, msg.getBytes(enc), 1);
System.out.println("加密: " + new String(result2, enc));
byte[] deresult2 = handleData(pubkey, result2, 0);
System.out.println("解密: " + new String(deresult2, enc));
}
}
make file ok!
原文: ~O(∩_∩)O哈哈~
加密: �A N�ډB�����ym��r�C��ʇ�������U
5 AES對稱加密
加密技術可以分為對稱與非對稱兩種.
對稱加密,解密,即加密與解密用的是同一把祕鑰,常用的對稱加密技術有DES,AES等
而非對稱技術,加密與解密用的是不同的祕鑰,常用的非對稱加密技術有RSA等
為什麼要有非對稱加密,解密技術呢
假設這樣一種場景A要傳送一段訊息給B,但是又不想以明文傳送,所以就需要對訊息進行加密.如果採用對稱加密技術,那麼加密與解密用的是同一把祕鑰.除非B事先就知道A的祕鑰,並且儲存好.這樣才可以解密A發來的訊息.
由於對稱技術只有一把祕鑰,所以祕鑰的管理是一個很麻煩的問題.而非對稱技術的誕生就解決了這個問題.非對稱加密與解密使用的是不同的祕鑰,並且祕鑰對是一一對應的,即用A的私鑰加密的密文只有用A的公鑰才能解密.
這樣的話,每個人都有兩把祕鑰,私鑰和公鑰,私鑰是隻有自己才知道的,不能告訴別人,而公鑰是公開的,大家都可以知道.這樣,當A想要傳送訊息給B的時候,只需要用B的公鑰對訊息進行加密就可以了,由於B的私鑰只有B才擁有,所以A用B的公鑰加密的訊息只有B才能解開.而B想更換自己的祕要時也很方便,只須把公鑰告訴大家就可以了.
那麼,既然非對稱加密如此之好,對稱加密就沒有存在的必要了啊,其實不然,由於非對稱加密演算法的開銷很大,所以如果直接以非對稱技術來加密傳送的訊息效率會很差.那麼怎麼辦呢?解決的辦法也很簡單,就是把對稱加密技術與非對稱加密技術結合起來使用.
還是這個例子:A要傳送一個訊息給B.
一,A先生成一個對稱祕鑰,這個祕鑰可以是隨機生成的,
二,A用B的公鑰加密第一步生成的這個對稱祕鑰
三,A把加密過的對稱祕鑰發給B
四,A用第一步生成的這個對稱祕鑰加密實際要發的訊息
五,A把用對稱祕鑰加密的訊息發給B
對於B
他先收到A發來的對稱祕鑰,這個祕鑰是用B的公鑰加密過的,所以B需要用自己的私鑰來解密這個祕鑰
然後B又收到A發來的密文,這時候用剛才解密出來的祕鑰來解密密文
這樣子的整個過程既保證了安全,又保證了效率.
接下來是Java實現:
我這個Java實現使用的是AES的對稱加密和RSA的非對稱加密(DES的對稱加密實現方法和AES的是一樣的,但是由於DES演算法本身有缺陷,容易被破解,所以現在多用其升級版AES對稱加密)
/**
* @file AES.java
* @date 2016年8月5日
* @version 3.4.1
*
* Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
*/
package encrypt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
*
*
* @author chengjian.he
* @version 3.4, 2016年8月5日 上午11:35:13
* @since Yeexun 3.4
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
public class AES {
private Key key;
/**
* 生成AES對稱祕鑰
* @throws NoSuchAlgorithmException
*/
public void generateKey() throws NoSuchAlgorithmException {
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom random = new SecureRandom();
keygen.init(random);
this.key = keygen.generateKey();
}
/**
* 加密
* @param in
* @param out
* @throws InvalidKeyException
* @throws ShortBufferException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws IOException
*/
public void encrypt(InputStream in, OutputStream out) throws InvalidKeyException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, IOException {
this.crypt(in, out, Cipher.ENCRYPT_MODE);
}
/**
* 解密
* @param in
* @param out
* @throws InvalidKeyException
* @throws ShortBufferException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws IOException
*/
public void decrypt(InputStream in, OutputStream out) throws InvalidKeyException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, IOException {
this.crypt(in, out, Cipher.DECRYPT_MODE);
}
/**
* 實際的加密解密過程
* @param in
* @param out
* @param mode
* @throws IOException
* @throws ShortBufferException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
*/
public void crypt(InputStream in, OutputStream out, int mode) throws IOException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(mode, this.key);
int blockSize = cipher.getBlockSize();
int outputSize = cipher.getOutputSize(blockSize);
byte[] inBytes = new byte[blockSize];
byte[] outBytes = new byte[outputSize];
int inLength = 0;
boolean more = true;
while (more) {
inLength = in.read(inBytes);
if (inLength == blockSize) {
int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
out.write(outBytes, 0, outLength);
} else {
more = false;
}
}
if (inLength > 0)
outBytes = cipher.doFinal(inBytes, 0, inLength);
else
outBytes = cipher.doFinal();
out.write(outBytes);
out.flush();
}
public void setKey(Key key) {
this.key = key;
}
public Key getKey() {
return key;
}
public static void main(String[] args) throws Exception {
AES aes = new AES();
aes.generateKey();
File file = new File("D:/aa.jpg");
FileInputStream in = new FileInputStream(file);
File file1 = new File("D:/temp/pub.key");
FileOutputStream out = new FileOutputStream(file1);
aes.encrypt(in, out);
aes.decrypt(in, out);
}
}