對稱加密算法和非對稱加密算法的完美結合

第一 對稱加密:
對稱加密是指,加密方和解密方使用同樣的祕鑰來進行加密和解密。
在對稱加密算法中,數據發信方將明文(原始數據)和加密密鑰(mi yue)一起經過特殊加密算法處理後,使其變成複雜的加密密文發送出去。常用的對稱加密算法:AES,RC4,3DES

傳輸的示意圖如下所示:

如上圖所示,此種方式屬於對稱加密,雙方擁有相同的密鑰,信息得到安全傳輸,但此種方式的缺點是:

(1)不同的客戶端、服務器數量龐大,所以雙方都需要維護大量的密鑰,維護成本很高

(2)因每個客戶端、服務器的安全級別不同,密鑰極易泄露

這裏舉例一個使用DES算法來實現對稱加密的例子:

public class DESUtils {

public byte[] initKey(){
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
keyGenerator.init(56);
SecretKey secretKey = keyGenerator.generateKey();
return  secretKey.getEncoded();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return null;
}

/**
  * @Method: encrypt
  * @Description: 加密算法
  * @param data 被加密字節數組
  * @param key  隨機對稱密鑰
  * @return
  * 返回類型:byte[]
  */
public byte[] encrypt(byte[] data,byte[] key){
SecretKey secretKey = new SecretKeySpec(key, "DES");
Cipher cipher;
try {
cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();


return null;
}

/**
  * @Method: decode
  * @Description: TODO
  * @param 被解密的字節數組
  * @param key 隨機對稱密鑰(和加密密鑰務必保持一致)
  * @return
  * 返回類型:byte[]
  */
public byte[] decode(byte[] data,byte[] key){
SecretKey secretKey = new SecretKeySpec(key, "DES");
Cipher cipher;
try {
cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();


return null;
}

測試代碼爲:

public static void testDes(){
DESUtils desUtils = new DESUtils();
byte[] key = desUtils.initKey();   // 
String originData = "123456";
System.out.println("原始數據:"+originData);
try {
// 將原始數據轉化爲字符串
byte[] arrayOrigin = originData.getBytes("utf-8");
// 對原始數據進行加密
byte[] encryption = desUtils.encrypt(arrayOrigin, key);
// 通過BASE64Encoder轉化處理
String encryptionStr = new BASE64Encoder().encode(encryption);
System.out.println("經過加密之後的字符串:"+encryptionStr);

try {
// 使用BASE64Decoder進行轉化處理
byte[] decoceOrigin = new BASE64Decoder().decodeBuffer(encryptionStr);
// 使用DES進行解密操作
byte[] decode = desUtils.decode(decoceOrigin, key);
// 把字節數組轉化爲字符串
String decodeStr = new String(decode,"utf-8");
System.out.println("對加密字符串進行解密:"+decodeStr);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

運行結果如下:

原始數據:123456
經過加密之後的字符串:jaKOVkHJtOQ=
對加密字符串進行解密:123456

第二 、非對稱加密:
非對稱加密算法實現機密信息交換的基本過程是:甲方生成一對密鑰並將其中的一把作爲公用密鑰向其它方公開;得到該公用密鑰的乙方使用該密鑰對機密信息進行加密後再發送給甲方;甲方再用自己保存的另一把專用密鑰對加密後的信息進行解密。甲方只能用其專用密鑰解密由其公用密鑰加密後的任何信息。非對稱加密算法:RSA,DSA/DSS 

如上圖所示,客戶端用公鑰對請求內容加密,服務器使用私鑰對內容解密,反之亦然,但上述過程也存在缺點:

公鑰是公開的(也就是黑客也會有公鑰),所以第 ④ 步私鑰加密的信息,如果被黑客截獲,其可以使用公鑰進行解密,獲取其中的內容。

實現非對稱加密算法例子:

public class AsymmetricEncryption {


public static final String KEY_ALGORITHM = "RSA";
/** 貌似默認是RSA/NONE/PKCS1Padding,未驗證 */
public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
public static final String PUBLIC_KEY = "publicKey";
public static final String PRIVATE_KEY = "privateKey";


/** RSA密鑰長度必須是64的倍數,在512~65536之間。默認是1024 */
public static final int KEY_SIZE = 2048;


public static final String PLAIN_TEXT = "hello world!";


/**
* @Method: generateKeyBytes
* @Description: 首先產生一個公鑰和私鑰對,使用HashMap保存起來
* @return 返回類型:Map<String,byte[]>
*/
public static Map<String, byte[]> generateKeyBytes() {


try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator
.getInstance(KEY_ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGenerator.generateKeyPair();


RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();


Map<String, byte[]> keyMap = new HashMap<String, byte[]>();
keyMap.put(PUBLIC_KEY, publicKey.getEncoded());
keyMap.put(PRIVATE_KEY, privateKey.getEncoded());
return keyMap;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}


/**
* 還原公鑰,X509EncodedKeySpec 用於構建公鑰的規範

* @param keyBytes
* @return
*/
public static PublicKey restorePublicKey(byte[] keyBytes) {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);


try {
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = factory.generatePublic(x509EncodedKeySpec);
return publicKey;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}


/**
* 還原私鑰,PKCS8EncodedKeySpec 用於構建私鑰的規範

* @param keyBytes
* @return
*/
public static PrivateKey restorePrivateKey(byte[] keyBytes) {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
keyBytes);
try {
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = factory
.generatePrivate(pkcs8EncodedKeySpec);
return privateKey;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}


/**
* 加密,三步走。

* @param key
* @param plainText
* @return
*/
public static byte[] RSAEncode(PublicKey key, byte[] plainText) {


try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainText);
} catch (Exception e) {
e.printStackTrace();
}


return null;
}


public static String RSADecode(PrivateKey key, byte[] encodedText) {


try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(encodedText));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

}

測試代碼如下:

public static void tesRSA(){
Map<String, byte[]> keyMap = AsymmetricEncryption.generateKeyBytes();

// 加密
PublicKey publicKey = AsymmetricEncryption.restorePublicKey(keyMap.get(AsymmetricEncryption.PUBLIC_KEY));
byte[] encodedText = AsymmetricEncryption.RSAEncode(publicKey, AsymmetricEncryption.PLAIN_TEXT.getBytes());
String rsaString = new BASE64Encoder().encode(encodedText);
System.out.println("RSA encoded: " + rsaString);

PrivateKey privateKey = AsymmetricEncryption.restorePrivateKey(keyMap.get(AsymmetricEncryption.PRIVATE_KEY));
System.out.println("RSA decoded: "+ AsymmetricEncryption.RSADecode(privateKey, encodedText));

}

運行結果如下所示:

RSA encoded: e7wIyV32VtiJhiraQGIYxD2TKtC4O6dRJx0lgVuEUsjCTSjqNFmEVNlYmFzd3ohIiW67XyIfEzWD
W9YFpFnDekRFLgeerh7c5gXMLVsVkf7k7XuTbiGmQOlOBUmL8VWpWVWTk8Rgn7Y+G7/dz9+DOEnH
csMnssKC/MBM80Ad5Za+QHqgb6BdZNHjZYzWpDIztBEUf/yHWfkGhmJahxo6Ff6y8er/shiP+qL3
hMJlw70TTGoGlrAWQqxUMYGPrv4IELi/iNSednXxo5bNNatJYke7FhKnuy8GEOWNH/K8Q52vl24L
cururJGLEJR6Hn/oaGxnXQbs2Fzo3vUziDj1cQ==
RSA decoded: hello world!

第三 非對稱和對稱完美結合

非對稱加密既然也有缺陷,那我們就將對稱加密,非對稱加密兩者結合起來,取其精華、去其糟粕,發揮兩者的各自的優勢:


如上圖所示

(1)第 ③ 步時,客戶端說:(咱們後續回話採用對稱加密吧,這是對稱加密的算法和對稱密鑰)這段話用公鑰進行加密,然後傳給服務器

(2)服務器收到信息後,用私鑰解密,提取出對稱加密算法和對稱密鑰後,服務器說:(好的)對稱密鑰加密

(3)後續兩者之間信息的傳輸就可以使用對稱加密的方式了

這是個非常非常經典的數據傳輸過程,也是Https傳輸協議裏面最經典的部分。也是把對稱加密和非對稱加密的作用發揮到了很好的地方。在https傳輸的過程中,如果單獨只用對稱加密,或者單獨使用非對稱加密都會出現問題。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章