1. 程式人生 > >Java加密解密之非對稱加密

Java加密解密之非對稱加密

非對稱加密演算法需要兩個金鑰來進行加密和解密,這兩個祕鑰是公開金鑰(public key,簡稱公鑰)和私有金鑰(private key,簡稱私鑰)。使用公鑰加密的,要使用私鑰解密。反之,使用私鑰加密的,要使用公鑰解密。

和對稱加密的區別是:
對稱加密:加密和解密時使用的是同一個祕鑰
非對稱加密:加密和解密時使用的是不同的祕鑰

非對稱加密與對稱加密相比,其安全性更好:對稱加密的通訊雙方使用相同的祕鑰,如果一方的祕鑰遭洩露,那麼整個通訊就會被破解。而非對稱加密使用一對祕鑰,一個用來加密,一個用來解密,而且公鑰是公開的,祕鑰是自己儲存的,不需要像對稱加密那樣在通訊之前要先同步祕鑰。
非對稱加密的缺點是加密和解密花費時間長、速度慢,只適合對少量資料進行加密。

非對稱加密中使用的主要演算法有:RSA、DSA、Elgamal、揹包演算法、Rabin、D-H、ECC(橢圓曲線加密演算法)等。

下面演示一個Java(1.8.0_144)使用RSA演算法的非對稱加密

package com.security.cipher;

import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.Cipher;

/**
 * 這裡面的API裡面有很多是呼叫getInstance方法,這個方法的引數有algorithm或者transformation
 * 一:algorithm:演算法
 * 
 * 二:transformation:有兩種格式
 * 1:演算法/模式/填充方式。如:DES/CBC/PKCS5Padding
 * 2:演算法。                              如:DES
 * 
 * 其中,algorithm、transformation的值,不區分大小寫
 * 
 * Java加密解密官方參考文件:
 * https://docs.oracle.com/javase/8/docs/technotes/guides/security/index.html
 * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
 */
public class CipherTest {
	
	/*
	 * 使用KeyPairGenerator生成金鑰對KeyPair
	 * KeyPair物件中有公鑰、私鑰
	 * 
	 * 其中,KeyPairGenerator.getInstance(algorithm)支援的演算法有:RSA、DSA
	 * 全部支援的演算法見官方文件
	 * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator
	 *  
	 */
	public static KeyPair newKeyPair(String algorithm) throws NoSuchAlgorithmException {
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		return keyPair;
	}
	
	/**
	 * 加密,對字串進行加密,返回結果為byte陣列
	 * 儲存的時候,可以把byte陣列進行base64編碼成字串,或者把byte陣列轉換成16進位制的字串
	 * 
	 * 其中,transformation支援的全部演算法見官方文件:
	 * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
	 */
	public static byte[] encrypt(String transformation, Key key, String password) throws Exception {
		Cipher cipher = Cipher.getInstance(transformation);
		//加密模式
		cipher.init(Cipher.ENCRYPT_MODE, key);
		return cipher.doFinal(password.getBytes());
	}
	
	/**
	 * 解密,返回結果為原始字串
	 * 
	 * 其中,transformation支援的全部演算法見官方文件:
	 * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
	 */
	public static String decrypt(String transformation, Key key, byte[] data) throws Exception {
		Cipher cipher = Cipher.getInstance(transformation);
		//解密模式
		cipher.init(Cipher.DECRYPT_MODE, key);
		byte[] result = cipher.doFinal(data);
		String password = new String(result);
		return password;
	}
	
	public static void main(String[] args) throws Exception {
		String password = "123456";
		
		//這裡指定的演算法,必須是非對稱加密演算法
		String algorithm = "RSA";
		String transformation = algorithm;
		
		//使用非對稱加密,需要一對祕鑰,這一對祕鑰,可以從KeyPair物件中獲取
		KeyPair keyPair = newKeyPair(algorithm);
		
		//獲取公鑰
		PublicKey publicKey = keyPair.getPublic();
		
		//獲取私鑰
		PrivateKey privateKey = keyPair.getPrivate();
		
		//公鑰加密私鑰解密
		{
			//公鑰加密
			byte[] passData = encrypt(transformation, publicKey, password);
			//私鑰解密
			String pass = decrypt(transformation, privateKey, passData);
			
			System.out.println("解密後的密碼 : " + pass);
		}
		
		//私鑰加密公鑰解密
		{
			//私鑰加密
			byte[] passData = encrypt(transformation, privateKey, password);
			//公鑰解密
			String pass = decrypt(transformation, publicKey, passData);
			
			System.out.println("解密後的密碼 : " + pass);
		}
	}
}

可以看出,用法和對稱加密差不多。關鍵是在於要有一個公鑰,一個私鑰。

上面的示例演示的是使用KeyPairGenerator、KeyPair物件獲取公鑰、私鑰。除了這種方法,還有一個方法,就是使用keytool。下面將演示如何使用keytool獲取公鑰私鑰

keytool -genkeypair -alias testkey -keyalg RSA -keystore test.jks -keypass 112233 -storetype jks -storepass 123456

輸入命令之後,根據提示,輸入對應的資訊


這樣,就可以得到一個test.jks的檔案,這個檔案中,就有公鑰,私鑰。獲取方式如下:

public static KeyPair newKeyPairByKeyStore(String path) throws Exception {
	KeyStore keyStore = KeyStore.getInstance("jks");
	String storePass = "123456";
	
	try(InputStream input = new FileInputStream(path)) {
		keyStore.load(input, storePass.toCharArray());
	}
	
	String keyPass = "112233";
	PrivateKey privateKey = (PrivateKey)keyStore.getKey("testkey", keyPass.toCharArray());
	
	PublicKey publicKey = keyStore.getCertificate("testkey").getPublicKey();
	
	return new KeyPair(publicKey, privateKey);
}

同樣的,使用openssl也可以