JAVA加密系列(三)- 非對稱加密算法 RSA、DSA
非對稱加密算法需要兩個密鑰:公開密鑰(publickey:簡稱公鑰)和私有密鑰(privatekey:簡稱私鑰)。公鑰與私鑰是一對,如果用公鑰對數據進行加密,只有用對應的私鑰才能解密。因爲加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。 非對稱加密算法實現機密信息交換的基本過程是:甲方生成一對密鑰並將公鑰公開,需要向甲方發送信息的其他角色(乙方)使用該密鑰(甲方的公鑰)對機密信息進行加密後再發送給甲方;甲方再用自己私鑰對加密後的信息進行解密。甲方想要回復乙方時正好相反,使用乙方的公鑰對數據進行加密,同理,乙方使用自己的私鑰來進行解密。
算法介紹
常見算法
- RSA 第一個能同時用於加密和數字簽名的算法,也易於理解和操作。RSA是被研究得最廣泛的公鑰算法,從提出到現今的三十多年裏,經歷了各種攻擊的考驗,逐漸爲人們接受,截止2017年被普遍認爲是最優秀的公鑰方案之一。
- DSA Elgamal和Schnorr數字簽名的一個變種,DSA數字簽名優於Elgamal數字簽名的地方在於它的簽名長度較短,並且某些可以破解Elgamal方案的攻擊不適用DSA數字簽名
- ECC 橢圓曲線密碼編碼學,是目前已知的公鑰體制中,對每比特所提供加密強度最高的一種體制。在軟件註冊保護方面起到很大的作用,一般的序列號通常由該算法產生。
- Elgamal 其基礎是DiffieˉHellman密鑰交換算法,應對中間人攻擊時非常脆弱。
RSA
X509EncodedKeySpec和PKCS8EncodedKeySpec分別對應值公鑰和私鑰的規範 想對算法編碼和規範有更多瞭解的可以更深入的學習,以下例子只展示最簡單的使用
public class RSAUtil {
//必須16位 否則會報錯 偏移量
private static String IV = "asdfgh1234567890";
//鑰匙 必須16位
private static String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCA6exxuVOdLvxb6u+Gf5FvZD6zGO1sqDbW7zBgga/8LVZBktaljn0OzCjQKbfe/AQgyh/nIVi1zFO1+FC9cGSiWPGo1uX9kY5Chm0MS8UhtNfO13sOD0U/47MMOduuQ3WP+EJ0wFRvpzyGAp0vKqfCj3Hyrtaf8o7CqTGoLV7G7QIDAQAB";
private static String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIDp7HG5U50u/Fvq74Z/kW9kPrMY7WyoNtbvMGCBr/wtVkGS1qWOfQ7MKNApt978BCDKH+chWLXMU7X4UL1wZKJY8ajW5f2RjkKGbQxLxSG0187Xew4PRT/jsww5265DdY/4QnTAVG+nPIYCnS8qp8KPcfKu1p/yjsKpMagtXsbtAgMBAAECgYBXf+94BWWmg9TQgvdPYFkTtYQFRj8pCEgovTMl3gDYduFcItHEj6F8oMB3AkoGdSJMK0VaOT0gMG8FTWVoH9h9iSM5uu/wsqr7mFI13+18bGFNv0OotV4UNpvlXqSksoRmetZDtaDPhO9/6anD1zY8VrJMXzJfJXhNdGFArEjpwQJBAOBsvzNDy1w/QtLobDm+TSOVVNtoPMO5qddR5PRnRWtpKASY4dQDl/KPa1fDrNp3oGfqA5FPLrQCmd6502Mvmn0CQQCTDRgarAuH9UVgGDeR+E18lWBsZEM4mGHFi9aigeLEw0th8PqZpdrPJixSo4S2lKcopkkagljiYONLlVeznBkxAkEAvRvloZUm73x/GqmvSJkK90kGUDvtuB/i9gWUID5FSNU7W2RYJwdAKqyfjzzbktvq1qVijDdk61qlvgBoF9QtIQJAcIammVJqCIHxspUVgQfHE7yi6o7Wuaoxtx9JAVXvF65yMuJagdTe2YFWjW4/kg+y0nJcooJ4TdLiW+ZOFE0xIQJBAILYIuxY5OpAm8cMqLOtldczj4sFfZb7hhojcEm3+/0ezNhzhZWPMI6txFieDel40a/qdPr8EPZjc0uYKTtlKNc=";
public static void main(String[] args) {
// genKeyPair();
//字符串編碼
System.out.println("編碼結果:" + encrypt("hello word", PUBLIC_KEY));
//字符串解碼
System.out.println("編碼結果:" + decrypt("NEo+98UAvp0q69M1mxorD/zrL4QjhBGAFd2Wj2nGcsF8Txb9r8VnUvFqGUM7ZNwXZdcnzW/SZqbGc8rHT0mmtPGWMlzJV8jMAWUgOh9jozyablwWU8Uj4BItiderZSQTrD+3E+ZL5TkkQKwmNCmdaABmozpkUz4B893m3GzhpjQ=",PRIVATE_KEY));
}
/**
* 隨機生成密鑰對
*/
public static void genKeyPair() {
try {
// KeyPairGenerator類用於生成公鑰和私鑰對,基於RSA算法生成對象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密鑰對生成器,密鑰大小爲96-1024位
keyPairGen.initialize(1024);
// 生成一個密鑰對,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私鑰
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公鑰
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 得到公鑰字符串
String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
// 得到私鑰字符串
String privateKeyString = new String(Base64.getEncoder().encode((privateKey.getEncoded())));
// 打印公鑰和私鑰
System.out.println("公匙:" + publicKeyString);
System.out.println("私匙:" + privateKeyString);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 加密
*/
public static String encrypt(String str, String publicKey) {
try {
byte[] bytes = str.getBytes();
//獲得RSA公匙
RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey.getBytes())));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
bytes = cipher.doFinal(bytes);
return new String(Base64.getEncoder().encode(bytes));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 解密
*/
public static String decrypt(String str, String privateKey) {
try {
byte[] bytes = Base64.getDecoder().decode(str);
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey.getBytes())));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, rsaPrivateKey);
bytes = cipher.doFinal(bytes);
return new String(bytes);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
log
編碼結果:eZxSLJKOvj0Ee/Lg9cx61ppaKuWXSRxvDU1deLR0ll2x1GEW5WO8Q9B4rO4DxDWfCLqfdmunSs2YZkm8ZDBUYi0N6VV4mqoz2SjckJyVe3pjwh4DifdrjAoO+0aYPKMhlGLjMY4Iy5SmkuSmot2AjsKHBhoqGs8bPnZoT7S6iKY=
編碼結果:hello word
DSA
Digital Signature Algorithm 是Schnorr和ElGamal簽名算法的變種,被美國NIST作爲DSS(DigitalSignature Standard)。簡單的說,這是一種更高級的驗證方式,用作數字簽名。不單單隻有公鑰、私鑰,還有數字簽名。私鑰加密生成數字簽名,公鑰驗證數據及簽名。如果數據和簽名不匹配則認爲驗證失敗!也就是說傳輸中的數據可以不再加密,接收方獲得數據後,拿到公鑰與簽名比對數據是否有效!
public class DSAUtil {
private static String PUBLIC_KEY = "MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAMUKDkoben3PPK01uS7hXrxKSOgJpQxVigU7mkfQL87B5GtwUKWmdKM5nMC89avU/S3qTg95+dQKjgydYqe8Ej3SVzDwnGlkZzWGgUOgs0pQ8q/cJ++4eUETH3l4svsLzwDgKIoT3fDBUef3fZ95oWbe1pIIt8RN0KRFJw8U+r4A";
private static String PRIVATE_KEY = "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUa38CMSvzE1kkWgLiBQDrKCavBEk=";
public static void main(String[] args) {
//隨機生成成對密鑰
genKeyPair();
String sign=sign("hello word", PRIVATE_KEY);
System.out.println("數字簽名:" + sign);
//字符串解碼
System.out.println("數字簽名校驗:" + verify("hello word",sign,PUBLIC_KEY));
}
/**
* 隨機生成密鑰對
*/
public static void genKeyPair() {
try {
// KeyPairGenerator類用於生成公鑰和私鑰對,基於DSA算法生成對象 可以使用RSA算法生成 成對出現就好
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DSA");
// 初始化密鑰對生成器,密鑰大小爲96-1024位 可自定義隨機產生器 SecureRandom
keyPairGen.initialize(1024);
// 生成一個密鑰對,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私鑰
DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
// 得到公鑰
DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
// 得到公鑰字符串
String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
// 得到私鑰字符串
String privateKeyString = new String(Base64.getEncoder().encode((privateKey.getEncoded())));
// 打印公鑰和私鑰
System.out.println("公匙:" + publicKeyString);
System.out.println("私匙:" + privateKeyString);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 用私鑰對信息生成數字簽名
*/
public static String sign(String str, String privateKey) {
try {
byte[] bytes = str.getBytes();
//獲得DSA公匙
DSAPrivateKey keyFactory = (DSAPrivateKey) KeyFactory.getInstance("DSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey.getBytes())));
Signature signature=Signature.getInstance("DSA");
signature.initSign(keyFactory);
signature.update(bytes);
return new String(Base64.getEncoder().encode(signature.sign()));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 校驗數字簽名
*/
public static boolean verify(String str, String sign,String publicKey) {
try {
byte[] bytes =Base64.getDecoder().decode(sign);
DSAPublicKey keyFactory = (DSAPublicKey) KeyFactory.getInstance("DSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey.getBytes())));
Signature signature=Signature.getInstance("DSA");
signature.initVerify(keyFactory);
signature.update(str.getBytes());
return signature.verify(bytes);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
log
公匙:MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGANQY1Hf9XoQcFocGugqaEP6xPgxPsZ1C63Zs1WWg4Fk6CV8xuK78hgk+BhVit2/h97DWp7LyTDT4gVjCHLuV58TUmZyiupeo0FZrKqrAr+t3Der9+MmBDzfvkgPPIIZudhoqJcTpox8UNdJ4yyvgXxMN4DSoWJ4O28q0jBaxC/Mk=
私匙:MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUcsPeT/5R1AiwvTpJmzv5TQxq65Q=
數字簽名:MCwCFGB6J9g/R5su1o3TbZYMAcroY6YIAhQTtbtXUH47pGRDzMCu74eq0sOMCg==
數字簽名校驗:true
總結
非對稱性加密還有很多,RSA和DSA是比較常用和常見的加密方式,安全性來講兩者差不多,DSA只是一種算法,和RSA不同之處在於它不能用作加密和解密,也不能進行密鑰交換,只用於簽名,它比RSA要快很多,RSA啥都好,但是RSA算法的祕鑰很長,加密的計算量比較大,安全性較高,但是加密速度比較慢,所以RSA加密常用於少量的核心數據的加密。