一、簡介
1.1 概念
非對稱加密需要兩個(gè)密鑰:公鑰(publickey) 和私鑰 (privatekey)。公鑰和私鑰是一對,如果用公鑰對數(shù)據(jù)加密,那么只能用對應(yīng)的私鑰解密。如果用私鑰對數(shù)據(jù)加密,只能用對應(yīng)的公鑰進(jìn)行解密。因?yàn)榧用芎徒饷苡玫氖遣煌拿荑€,所以稱為非對稱加密。
非對稱加密算法的保密性好,它消除了最終用戶交換密鑰的需要。但是加解密速度要遠(yuǎn)遠(yuǎn)慢于對稱加密,在某些極端情況下,甚至能比對稱加密慢上1000倍。
1.2 特點(diǎn)
算法強(qiáng)度復(fù)雜、安全性依賴于算法與密鑰但是由于其算法復(fù)雜,而使得加密解密速度沒有對稱加密解密的速度快。對稱密碼體制中只有一種密鑰,并且是非公開的,如果要解密就得讓對方知道密鑰。所以保證其安全性就是保證密鑰的安全,而非對稱密鑰體制有兩種密鑰,其中一個(gè)是公開的,這樣就可以不需要像對稱密碼那樣傳輸對方的密鑰了。這樣安全性就大了很多。
1.3 工作原理

- (1) A 要向 B 發(fā)送信息,A 和 B 都要產(chǎn)生一對用于加密和解密的公鑰和私鑰。
- (2) A 的私鑰保密,A 的公鑰告訴 B;B 的私鑰保密,B 的公鑰告訴 A。
- (3) A 要給 B 發(fā)送信息時(shí),A 用 B 的公鑰加密信息,因?yàn)?A 知道 B 的公鑰。
- (4) A 將這個(gè)消息發(fā)給 B (已經(jīng)用 B 的公鑰加密消息)。
- (5) B 收到這個(gè)消息后,B 用自己的私鑰解密 A 的消息。其他所有收到這個(gè)報(bào)文的人都無法解密,因?yàn)橹挥?B 才有 B 的私鑰。
1.4 主要算法
RSA、Elgamal、背包算法、Rabin、D-H、ECC (橢圓曲線加密算法)。使用最廣泛的是 RSA 算法,Elgamal 是另一種常用的非對稱加密算法。
1.5 應(yīng)用場景
- (1) 信息加密
收信者是唯一能夠解開加密信息的人,因此收信者手里的必須是私鑰。發(fā)信者手里的是公鑰,其它人知道公鑰沒有關(guān)系,因?yàn)槠渌税l(fā)來的信息對收信者沒有意義。
- (2) 登錄認(rèn)證
客戶端需要將認(rèn)證標(biāo)識傳送給服務(wù)器,此認(rèn)證標(biāo)識 (可能是一個(gè)隨機(jī)數(shù)) 其它客戶端可以知道,因此需要用私鑰加密,客戶端保存的是私鑰。服務(wù)器端保存的是公鑰,其它服務(wù)器知道公鑰沒有關(guān)系,因?yàn)榭蛻舳瞬恍枰卿浧渌?wù)器。
- (3) 數(shù)字簽名
數(shù)字簽名是為了表明信息沒有受到偽造,確實(shí)是信息擁有者發(fā)出來的,附在信息原文的后面。就像手寫的簽名一樣,具有不可抵賴性和簡潔性。
簡潔性:對信息原文做哈希運(yùn)算,得到消息摘要,信息越短加密的耗時(shí)越少。
不可抵賴性:信息擁有者要保證簽名的唯一性,必須是唯一能夠加密消息摘要的人,因此必須用私鑰加密 (就像字跡他人無法學(xué)會一樣),得到簽名。如果用公鑰,那每個(gè)人都可以偽造簽名了。
- (4) 數(shù)字證書
問題起源:對1和3,發(fā)信者怎么知道從網(wǎng)上獲取的公鑰就是真的?沒有遭受中間人攻擊?
這樣就需要第三方機(jī)構(gòu)來保證公鑰的合法性,這個(gè)第三方機(jī)構(gòu)就是 CA (Certificate Authority),證書中心。
CA 用自己的私鑰對信息原文所有者發(fā)布的公鑰和相關(guān)信息進(jìn)行加密,得出的內(nèi)容就是數(shù)字證書。
信息原文的所有者以后發(fā)布信息時(shí),除了帶上自己的簽名,還帶上數(shù)字證書,就可以保證信息不被篡改了。信息的接收者先用 CA給的公鑰解出信息所有者的公鑰,這樣可以保證信息所有者的公鑰是真正的公鑰,然后就能通過該公鑰證明數(shù)字簽名是否真實(shí)了。
二、RSA算法
2.1 簡介
RSA 是目前最有影響力的公鑰加密算法,該算法基于一個(gè)十分簡單的數(shù)論事實(shí):將兩個(gè)大素?cái)?shù)相乘十分容易,但想要對其乘積進(jìn)行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰,即公鑰,而兩個(gè)大素?cái)?shù)組合成私鑰。公鑰是可發(fā)布的供任何人使用,私鑰則為自己所有,供解密之用。
2.2 工作流程
A 要把信息發(fā)給 B 為例,確定角色:A 為加密者,B 為解密者。首先由 B 隨機(jī)確定一個(gè) KEY,稱之為私鑰,將這個(gè) KEY 始終保存在機(jī)器 B 中而不發(fā)出來;然后,由這個(gè) KEY 計(jì)算出另一個(gè) KEY,稱之為公鑰。這個(gè)公鑰的特性是幾乎不可能通過它自身計(jì)算出生成它的私鑰。接下來通過網(wǎng)絡(luò)把這個(gè)公鑰傳給 A,A 收到公鑰后,利用公鑰對信息加密,并把密文通過網(wǎng)絡(luò)發(fā)送到 B,最后 B 利用已知的私鑰,就能對密文進(jìn)行解碼了。以上就是 RSA 算法的工作流程。
2.3 運(yùn)算速度
由于進(jìn)行的都是大數(shù)計(jì)算,使得 RSA 最快的情況也比 DES 慢上好幾倍,無論是軟件還是硬件實(shí)現(xiàn)。速度一直是 RSA 的缺陷。一般來說只用于少量數(shù)據(jù)加密。RSA 的速度是對應(yīng)同樣安全級別的對稱密碼算法的1/1000左右。
比起 DES 和其它對稱算法來說,RSA 要慢得多。實(shí)際上一般使用一種對稱算法來加密信息,然后用 RSA 來加密比較短的公鑰,然后將用 RSA 加密的公鑰和用對稱算法加密的消息發(fā)送給接收方。
這樣一來對隨機(jī)數(shù)的要求就更高了,尤其對產(chǎn)生對稱密碼的要求非常高,否則的話可以越過 RSA 來直接攻擊對稱密碼。
2.4 公鑰傳遞安全
和其它加密過程一樣,對 RSA 來說分配公鑰的過程是非常重要的。分配公鑰的過程必須能夠抵擋中間人攻擊。假設(shè) A 交給 B 一個(gè)公鑰,并使 B 相信這是A 的公鑰,并且 C 可以截下 A 和 B 之間的信息傳遞,那么 C 可以將自己的公鑰傳給 B,B 以為這是 A 的公鑰。C 可以將所有 B 傳遞給 A 的消息截下來,將這個(gè)消息用自己的密鑰解密,讀這個(gè)消息,然后將這個(gè)消息再用 A 的公鑰加密后傳給 A。理論上 A 和 B 都不會發(fā)現(xiàn) C 在偷聽它們的消息,今天人們一般用數(shù)字認(rèn)證來防止這樣的攻擊。
2.5 攻擊
(1) 針對 RSA 最流行的攻擊一般是基于大數(shù)因數(shù)分解。1999年,RSA-155 (512 bits) 被成功分解,花了五個(gè)月時(shí)間(約8000 MIPS 年)和224 CPU hours 在一臺有3.2G 中央內(nèi)存的 Cray C916計(jì)算機(jī)上完成。
RSA-158 表示如下:
39505874583265144526419767800614481996020776460304936454139376051579355626529450683609727842468219535093544305870490251995655335710209799226484977949442955603= 3388495837466721394368393204672181522815830368604993048084925840555281177× 11658823406671259903148376558383270818131012258146392600439520994131344334162924536139
2009年12月12日,編號為 RSA-768 (768 bits, 232 digits) 數(shù)也被成功分解。這一事件威脅了現(xiàn)通行的1024-bit 密鑰的安全性,普遍認(rèn)為用戶應(yīng)盡快升級到2048-bit 或以上。
RSA-768表示如下:
1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413= 3347807169895689878604416984821269081770479498371376856891 2431388982883793878002287614711652531743087737814467999489× 3674604366679959042824463379962795263227915816434308764267 6032283815739666511279233373417143396810270092798736308917
(2) 秀爾算法
量子計(jì)算里的秀爾算法能使窮舉的效率大大的提高。由于 RSA 算法是基于大數(shù)分解 (無法抵抗窮舉攻擊),因此在未來量子計(jì)算能對 RSA 算法構(gòu)成較大的威脅。一個(gè)擁有 N 量子位的量子計(jì)算機(jī),每次可進(jìn)行2^N 次運(yùn)算,理論上講,密鑰為1024位長的 RSA 算法,用一臺512量子比特位的量子計(jì)算機(jī)在1秒內(nèi)即可破解。
2.6 例子
- 首先引入commons-codec
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
- 密鑰對對象
/**
* @author: huangyibo
* @Date: 2022/4/29 18:47
* @Description: 非對稱加密 密鑰對對象
*/
public class RsaKeyPair {
private String publicKey;
private String privateKey;
public RsaKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
}
- RSA工具類
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @author: huangyibo
* @Date: 2022/4/28 18:03
* @Description: RSA 非對稱加密算法
*/
public class RSAUtil {
//RSA編碼
public static final String ALGORITHM = "RSA";
/**
* 默認(rèn)種子
*/
private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";
/**
* 構(gòu)建RSA密鑰對
* @return
* @throws NoSuchAlgorithmException
*/
public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
// 初始化隨機(jī)產(chǎn)生器
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(DEFAULT_SEED.getBytes());
keyPairGenerator.initialize(1024, secureRandom);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
return new RsaKeyPair(publicKeyString, privateKeyString);
}
/**
* 公鑰解密
*
* @param publicKeyText 公鑰
* @param text 加密字符串
* @return 明文
* @throws Exception 解密過程中的異常信息
*/
public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 私鑰加密
*
* @param privateKeyText 私鑰
* @param text 加密數(shù)據(jù)
* @return 密文
* @throws Exception 加密過程中的異常信息
*/
public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 私鑰解密
*
* @param privateKeyText 私鑰
* @param text 加密字符串
* @return 明文
* @throws Exception 解密過程中的異常信息
*/
public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 公鑰加密
* @param publicKeyText 公鑰
* @param text 加密字符串
* @return 密文
* @throws Exception 加密過程中的異常信息
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
}
- RSA測試
/**
* @author: huangyibo
* @Date: 2022/4/29 18:57
* @Description:
*/
public class RSATest {
private static final String src = "abcdefghijklmnopqrstuvwxyz";
public static void main(String[] args) throws Exception {
System.out.println("\n");
RsaKeyPair rsaKeyPair = RSAUtil.generateKeyPair();
System.out.println("公鑰:" + rsaKeyPair.getPublicKey());
System.out.println("私鑰:" + rsaKeyPair.getPrivateKey());
System.out.println("\n");
test1(rsaKeyPair, src);
System.out.println("\n");
test2(rsaKeyPair, src);
System.out.println("\n");
}
/**
* 公鑰加密私鑰解密
*/
private static void test1(RsaKeyPair keyPair, String source) throws Exception {
System.out.println("***************** 公鑰加密私鑰解密開始 *****************");
String text1 = RSAUtil.encryptByPublicKey(keyPair.getPublicKey(), source);
String text2 = RSAUtil.decryptByPrivateKey(keyPair.getPrivateKey(), text1);
System.out.println("加密前:" + source);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (source.equals(text2)) {
System.out.println("解密字符串和原始字符串一致,解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失敗");
}
System.out.println("***************** 公鑰加密私鑰解密結(jié)束 *****************");
}
/**
* 私鑰加密公鑰解密
*
* @throws Exception
*/
private static void test2(RsaKeyPair keyPair, String source) throws Exception {
System.out.println("***************** 私鑰加密公鑰解密開始 *****************");
String text1 = RSAUtil.encryptByPrivateKey(keyPair.getPrivateKey(), source);
String text2 = RSAUtil.decryptByPublicKey(keyPair.getPublicKey(), text1);
System.out.println("加密前:" + source);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (source.equals(text2)) {
System.out.println("解密字符串和原始字符串一致,解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失敗");
}
System.out.println("***************** 私鑰加密公鑰解密結(jié)束 *****************");
}
}
- RSA分段加解密、簽名、驗(yàn)簽:http://www.itdecent.cn/p/f68f5e5ad331
三、DSA算法
3.1 簡介
DSA (Digital Signature Algorithm) 是 Schnorr 和 ElGamal 簽名算法的變種,被美國 NIST 作為 DSS (DigitalSignature Standard)。 DSA 是基于整數(shù)有限域離散對數(shù)難題的。
簡單的說,這是一種更高級的驗(yàn)證方式,用作數(shù)字簽名。不單單只有公鑰、私鑰,還有數(shù)字簽名。私鑰加密生成數(shù)字簽名,公鑰驗(yàn)證數(shù)據(jù)及簽名,如果數(shù)據(jù)和簽名不匹配則認(rèn)為驗(yàn)證失敗。數(shù)字簽名的作用就是校驗(yàn)數(shù)據(jù)在傳輸過程中不被修改,數(shù)字簽名,是單向加密的升級。
3.2 處理過程

- (1) 使用消息摘要算法將發(fā)送數(shù)據(jù)加密生成數(shù)字摘要。
- (2) 發(fā)送方用自己的私鑰對摘要再加密,形成數(shù)字簽名。
- (3) 將原文和加密的摘要同時(shí)傳給對方。
- (4) 接受方用發(fā)送方的公鑰對摘要解密,同時(shí)對收到的數(shù)據(jù)用消息摘要算法產(chǎn)生同一摘要。
- (5) 將解密后的摘要和收到的數(shù)據(jù)在接收方重新加密產(chǎn)生的摘要相互對比,如果兩者一致,則說明在傳送過程中信息沒有破壞和篡改。否則,則說明信息已經(jīng)失去安全性和保密性。
3.3 例子
- 非對稱加密算法基類
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @author: huangyibo
* @Date: 2022/4/28 18:03
* @Description: 非對稱加密算法基類
*/
public class EncryptAlgorithmBase {
/**
* 默認(rèn)種子
*/
private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";
/**
* 生成密鑰實(shí)際方法,可以使用多種方式
* 提供以下多種方式
* { "DSA", "SHA1withDSA", "1024" }, { "DSA", "SHA256withDSA", "1024" },
* { "DSA", "SHA256withDSA", "2048" }, { "RSA", "SHA256withRSA", "1024" },
* { "RSA", "SHA256withRSA", "2048" }, { "RSA", "SHA256withRSA", "3192" },
* { "RSA", "SHA512withRSA", "1024" }, { "RSA", "SHA512withRSA", "2048" },
* { "RSA", "SHA512withRSA", "3192" }, { "RSA", "MD5withRSA", "1024" },
* { "RSA", "MD5withRSA", "2048" },
* { "RSA", "MD5withRSA", "3192" }, { "EC", "SHA1withECDSA", "128" },
* { "EC", "SHA1withECDSA", "256" },
* { "EC", "SHA256withECDSA", "128" }, { "EC", "SHA256withECDSA", "256" },
* { "EC", "SHA512withECDSA", "128" }, { "EC", "SHA512withECDSA", "256" },
*
* @param algorithm
* @param bit
* @return
* @throws Exception
*/
protected static RsaKeyPair generateKeyPair(String algorithm, int bit) throws Exception {
//KeyPairGenerator類用于生成公鑰和私鑰對,基于RSA算法生成對象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
//初始化密鑰對生成器,密鑰大小為96-1024位
keyPairGenerator.initialize(bit, new SecureRandom());
// 生成一個(gè)密鑰對,保存在keyPair中
KeyPair keyPair = keyPairGenerator.generateKeyPair();
String publicKey = ByteUtils.bytesToHexString(keyPair.getPublic().getEncoded());
String privateKey = ByteUtils.bytesToHexString(keyPair.getPrivate().getEncoded());
return new RsaKeyPair(publicKey, privateKey);
}
/**
* 非對稱加密簽名 - 私鑰加密
* @param str
* @param privateKey
* @param algorithm
* @param signAlgorithm
* @return
* @throws Exception
*/
public static String sign(String str, String privateKey, String algorithm, String signAlgorithm) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(ByteUtils.hexStringToBytes(privateKey));
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PrivateKey dsaPrivateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance(signAlgorithm);
signature.initSign(dsaPrivateKey);
signature.update(str.getBytes());
return ByteUtils.bytesToHexString(signature.sign());
}
/**
* 非對稱加密驗(yàn)證 - 公鑰驗(yàn)證
* @param sign
* @param str
* @param publicKey
* @param algorithm
* @param signAlgorithm
* @return
* @throws Exception
*/
public static boolean verify(String sign, String str, String publicKey,String algorithm,String signAlgorithm) throws Exception {
//base64編碼的公鑰
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(ByteUtils.hexStringToBytes(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PublicKey dsaPublicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance(signAlgorithm);
signature.initVerify(dsaPublicKey);
signature.update(str.getBytes());
return signature.verify(ByteUtils.hexStringToBytes(sign));
}
}
- 對稱加密 密鑰對對象
/**
* @author: huangyibo
* @Date: 2022/4/29 18:47
* @Description: 非對稱加密 密鑰對對象
*/
public class RsaKeyPair {
private String publicKey;
private String privateKey;
public RsaKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
}
- DSA 加解密實(shí)現(xiàn)
/**
* @author: huangyibo
* @Date: 2022/4/28 18:52
* @Description: DSA 加解密實(shí)現(xiàn)
*/
public class DSAUtils extends EncryptAlgorithmBase {
//字符編碼
public static final String ALGORITHM = "DSA";
public static final String SIGN_ALGORITHM = "SHA1withDSA";
/**
* DSA 驗(yàn)簽
*
* @param str 加密字符串
* @param publicKey 公鑰
* @return 密文
* @throws Exception 加密過程中的異常信息
*/
public static boolean verify(String sign, String str, String publicKey) throws Exception {
return verify(sign, str, publicKey, ALGORITHM, SIGN_ALGORITHM);
}
/**
* DSA 簽名
*
* @param str 加密字符串
* @param privateKey 私鑰
* @return 銘文
* @throws Exception 解密過程中的異常信息
*/
public static String sign(String str, String privateKey) throws Exception {
return sign(str, privateKey, ALGORITHM, SIGN_ALGORITHM);
}
public static void main(String[] args) throws Exception {
RsaKeyPair rsaKeyPair = generateKeyPair(ALGORITHM, 1024);
System.out.println(rsaKeyPair.getPublicKey());
System.out.println(rsaKeyPair.getPrivateKey());
String message = "我要測試DSA";
String sign = sign(message, rsaKeyPair.getPrivateKey());
System.out.println(verify(sign, message, rsaKeyPair.getPublicKey()));
}
}
- RSA 加解密實(shí)現(xiàn)
/**
* @author: huangyibo
* @Date: 2022/4/28 18:52
* @Description: RSA 加解密實(shí)現(xiàn)
*/
public class RSAUtils extends EncryptAlgorithmBase {
//字符編碼
public static final String ALGORITHM = "RSA";
public static final String SIGN_ALGORITHM = "SHA512withRSA";
/**
* RSA 驗(yàn)簽
*
* @param str 加密字符串
* @param publicKey 公鑰
* @return 密文
* @throws Exception 加密過程中的異常信息
*/
public static boolean verify(String sign, String str, String publicKey) throws Exception {
return verify(sign, str, publicKey, ALGORITHM, SIGN_ALGORITHM);
}
/**
* RSA 簽名
*
* @param str 加密字符串
* @param privateKey 私鑰
* @return 銘文
* @throws Exception 解密過程中的異常信息
*/
public static String sign(String str, String privateKey) throws Exception {
return sign(str, privateKey, ALGORITHM, SIGN_ALGORITHM);
}
public static void main(String[] args) throws Exception {
RsaKeyPair rsaKeyPair = generateKeyPair(ALGORITHM, 1024);
System.out.println(rsaKeyPair.getPublicKey());
System.out.println(rsaKeyPair.getPrivateKey());
String message = "我要測試DSA";
String sign = sign(message, rsaKeyPair.getPrivateKey());
System.out.println(verify(sign, message, rsaKeyPair.getPublicKey()));
}
}
- ECDSA簽名算法
/**
* @author: huangyibo
* @Date: 2022/4/28 18:52
* @Description: ECDSA簽名算法
*/
public class ECDSAUtils extends EncryptAlgorithmBase {
//字符編碼
public static final String ALGORITHM = "EC";
public static final String SIGN_ALGORITHM = "SHA256withECDSA";
/**
* ECDSA 驗(yàn)簽
*
* @param str 加密字符串
* @param publicKey 公鑰
* @return 密文
* @throws Exception 加密過程中的異常信息
*/
public static boolean verify(String sign, String str, String publicKey) throws Exception {
return verify(sign, str, publicKey, ALGORITHM, SIGN_ALGORITHM);
}
/**
* ECDSA 簽名
*
* @param str 加密字符串
* @param privateKey 私鑰
* @return 銘文
* @throws Exception 解密過程中的異常信息
*/
public static String sign(String str, String privateKey) throws Exception {
return sign(str, privateKey, ALGORITHM, SIGN_ALGORITHM);
}
public static void main(String[] args) throws Exception {
RsaKeyPair rsaKeyPair = generateKeyPair(ALGORITHM, 256);
System.out.println(rsaKeyPair.getPublicKey());
System.out.println(rsaKeyPair.getPrivateKey());
String message = "我要測試DSA";
String sign = sign(message, rsaKeyPair.getPrivateKey());
System.out.println(verify(sign, message, rsaKeyPair.getPublicKey()));
}
}
- 基本數(shù)據(jù)類型轉(zhuǎn)換
/**
* @author: huangyibo
* @Date: 2022/4/28 18:47
* @Description: 基本數(shù)據(jù)類型轉(zhuǎn)換(主要是byte和其它類型之間的互轉(zhuǎn))
*/
public class ByteUtils {
/**
*
* <pre>
* 將4個(gè)byte數(shù)字組成的數(shù)組合并為一個(gè)float數(shù).
* </pre>
*
* @param arr
* @return
*/
public static float byte4ToFloat(byte[] arr) {
if (arr == null || arr.length != 4) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且是4位!");
}
int i = byte4ToInt(arr);
return Float.intBitsToFloat(i);
}
/**
*
* <pre>
* 將一個(gè)float數(shù)字轉(zhuǎn)換為4個(gè)byte數(shù)字組成的數(shù)組.
* </pre>
*
* @param f
* @return
*/
public static byte[] floatToByte4(float f) {
int i = Float.floatToIntBits(f);
return intToByte4(i);
}
/**
*
* <pre>
* 將八個(gè)byte數(shù)字組成的數(shù)組轉(zhuǎn)換為一個(gè)double數(shù)字.
* </pre>
*
* @param arr
* @return
*/
public static double byte8ToDouble(byte[] arr) {
if (arr == null || arr.length != 8) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且是8位!");
}
long l = byte8ToLong(arr);
return Double.longBitsToDouble(l);
}
/**
*
* <pre>
* 將一個(gè)double數(shù)字轉(zhuǎn)換為8個(gè)byte數(shù)字組成的數(shù)組.
* </pre>
*
* @param i
* @return
*/
public static byte[] doubleToByte8(double i) {
long j = Double.doubleToLongBits(i);
return longToByte8(j);
}
/**
*
* <pre>
* 將一個(gè)char字符轉(zhuǎn)換為兩個(gè)byte數(shù)字轉(zhuǎn)換為的數(shù)組.
* </pre>
*
* @param c
* @return
*/
public static byte[] charToByte2(char c) {
byte[] arr = new byte[2];
arr[0] = (byte) (c >> 8);
arr[1] = (byte) (c & 0xff);
return arr;
}
/**
*
* <pre>
* 將2個(gè)byte數(shù)字組成的數(shù)組轉(zhuǎn)換為一個(gè)char字符.
* </pre>
*
* @param arr
* @return
*/
public static char byte2ToChar(byte[] arr) {
if (arr == null || arr.length != 2) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且是2位!");
}
return (char) (((char) (arr[0] << 8)) | ((char) arr[1]));
}
/**
*
* <pre>
* 將一個(gè)16位的short轉(zhuǎn)換為長度為2的8位byte數(shù)組.
* </pre>
*
* @param s
* @return
*/
public static byte[] shortToByte2(Short s) {
byte[] arr = new byte[2];
arr[0] = (byte) (s >> 8);
arr[1] = (byte) (s & 0xff);
return arr;
}
/**
*
* <pre>
* 長度為2的8位byte數(shù)組轉(zhuǎn)換為一個(gè)16位short數(shù)字.
* </pre>
*
* @param arr
* @return
*/
public static short byte2ToShort(byte[] arr) {
if (arr != null && arr.length != 2) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且是2位!");
}
return (short) (((short) arr[0] << 8) | ((short) arr[1] & 0xff));
}
/**
*
* <pre>
* 將short轉(zhuǎn)換為長度為16的byte數(shù)組.
* 實(shí)際上每個(gè)8位byte只存儲了一個(gè)0或1的數(shù)字
* 比較浪費(fèi).
* </pre>
*
* @param s
* @return
*/
public static byte[] shortToByte16(short s) {
byte[] arr = new byte[16];
for (int i = 15; i >= 0; i--) {
arr[i] = (byte) (s & 1);
s >>= 1;
}
return arr;
}
public static short byte16ToShort(byte[] arr) {
if (arr == null || arr.length != 16) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且長度為16!");
}
short sum = 0;
for (int i = 0; i < 16; ++i) {
sum |= (arr[i] << (15 - i));
}
return sum;
}
/**
*
* <pre>
* 將32位int轉(zhuǎn)換為由四個(gè)8位byte數(shù)字.
* </pre>
*
* @param sum
* @return
*/
public static byte[] intToByte4(int sum) {
byte[] arr = new byte[4];
arr[0] = (byte) (sum >> 24);
arr[1] = (byte) (sum >> 16);
arr[2] = (byte) (sum >> 8);
arr[3] = (byte) (sum & 0xff);
return arr;
}
/**
* <pre>
* 將長度為4的8位byte數(shù)組轉(zhuǎn)換為32位int.
* </pre>
*
* @param arr
* @return
*/
public static int byte4ToInt(byte[] arr) {
if (arr == null || arr.length != 4) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且是4位!");
}
return (int) (((arr[0] & 0xff) << 24) | ((arr[1] & 0xff) << 16) | ((arr[2] & 0xff) << 8) | ((arr[3] & 0xff)));
}
/**
*
* <pre>
* 將長度為8的8位byte數(shù)組轉(zhuǎn)換為64位long.
* </pre>
*
* 0xff對應(yīng)16進(jìn)制,f代表1111,0xff剛好是8位 byte[]
* arr,byte[i]&0xff剛好滿足一位byte計(jì)算,不會導(dǎo)致數(shù)據(jù)丟失. 如果是int計(jì)算. int[] arr,arr[i]&0xffff
*
* @param arr
* @return
*/
public static long byte8ToLong(byte[] arr) {
if (arr == null || arr.length != 8) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且是8位!");
}
return (long) (((long) (arr[0] & 0xff) << 56) | ((long) (arr[1] & 0xff) << 48) | ((long) (arr[2] & 0xff) << 40)
| ((long) (arr[3] & 0xff) << 32) | ((long) (arr[4] & 0xff) << 24)
| ((long) (arr[5] & 0xff) << 16) | ((long) (arr[6] & 0xff) << 8) | ((long) (arr[7] & 0xff)));
}
/**
* 將一個(gè)long數(shù)字轉(zhuǎn)換為8個(gè)byte數(shù)組組成的數(shù)組.
*/
public static byte[] longToByte8(long sum) {
byte[] arr = new byte[8];
arr[0] = (byte) (sum >> 56);
arr[1] = (byte) (sum >> 48);
arr[2] = (byte) (sum >> 40);
arr[3] = (byte) (sum >> 32);
arr[4] = (byte) (sum >> 24);
arr[5] = (byte) (sum >> 16);
arr[6] = (byte) (sum >> 8);
arr[7] = (byte) (sum & 0xff);
return arr;
}
/**
*
* <pre>
* 將int轉(zhuǎn)換為32位byte.
* 實(shí)際上每個(gè)8位byte只存儲了一個(gè)0或1的數(shù)字
* 比較浪費(fèi).
* </pre>
*
* @param num
* @return
*/
public static byte[] intToByte32(int num) {
byte[] arr = new byte[32];
for (int i = 31; i >= 0; i--) {
// &1 也可以改為num&0x01,表示取最地位數(shù)字.
arr[i] = (byte) (num & 1);
// 右移一位.
num >>= 1;
}
return arr;
}
/**
*
* <pre>
* 將長度為32的byte數(shù)組轉(zhuǎn)換為一個(gè)int類型值.
* 每一個(gè)8位byte都只存儲了0或1的數(shù)字.
* </pre>
*
* @param arr
* @return
*/
public static int byte32ToInt(byte[] arr) {
if (arr == null || arr.length != 32) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且長度是32!");
}
int sum = 0;
for (int i = 0; i < 32; ++i) {
sum |= (arr[i] << (31 - i));
}
return sum;
}
/**
*
* <pre>
* 將長度為64的byte數(shù)組轉(zhuǎn)換為一個(gè)long類型值.
* 每一個(gè)8位byte都只存儲了0或1的數(shù)字.
* </pre>
*
* @param arr
* @return
*/
public static long byte64ToLong(byte[] arr) {
if (arr == null || arr.length != 64) {
throw new IllegalArgumentException("byte數(shù)組必須不為空,并且長度是64!");
}
long sum = 0L;
for (int i = 0; i < 64; ++i) {
sum |= ((long) arr[i] << (63 - i));
}
return sum;
}
/**
*
* <pre>
* 將一個(gè)long值轉(zhuǎn)換為長度為64的8位byte數(shù)組.
* 每一個(gè)8位byte都只存儲了0或1的數(shù)字.
* </pre>
*
* @param sum
* @return
*/
public static byte[] longToByte64(long sum) {
byte[] arr = new byte[64];
for (int i = 63; i >= 0; i--) {
arr[i] = (byte) (sum & 1);
sum >>= 1;
}
return arr;
}
/**
* <pre>
* 把byte[]轉(zhuǎn)換成16進(jìn)制進(jìn)制字符串
* </pre>
* @param src
* @return
*/
public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* <pre>
* 把16進(jìn)制進(jìn)制字符串轉(zhuǎn)換成byte[]
* </pre>
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* 將16進(jìn)制字符轉(zhuǎn)換為字節(jié)
* @param c
* @return
*/
private static byte charToByte(char c) {
return (byte) "0123456789abcdef".indexOf(c);
}
/**
* byte[]轉(zhuǎn)換成bit
* @param bytes
* @return
*/
public static String bytesToBits(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for(byte b:bytes){
sb.append(byteToBits(b));
}
return sb.toString();
}
/**
* byte轉(zhuǎn)換成8位bit
* @param b
* @return
*/
public static String byteToBits(byte b) {
int z = b; z |= 256;
String str = Integer.toBinaryString(z);
int len = str.length();
return str.substring(len-8, len);
}
/**
* 計(jì)算校驗(yàn)和
* @param bytes
* @return
*/
public static final int calculateCheckSum(byte[] bytes) {
int sum = 0;
for (byte b : bytes) {
sum += (short)b;
}
return sum > 65535 ? (sum-65535) : sum;
}
}
/**
* @author: huangyibo
* @Date: 2022/4/28 18:20
* @Description: GZip 轉(zhuǎn)化的工具類
*/
public class GZipUtils {
public final static int GZIP_MAGIC = 0x8b1f;
private static final Logger logger = LoggerFactory.getLogger(GZipUtils.class);
/**
* 從輸入流中構(gòu)建消息
* @param is 輸入流
* @throws IOException 讀流失敗則拋出異常
*/
public static byte[] buildMsg(InputStream is) throws Exception {
byte[] messages=null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] receiveBuf = new byte[1024];
int len=0;
while ((len = is.read(receiveBuf)) != -1) {
try {
baos.write(receiveBuf, 0, len);
} catch (Exception e) {
logger.error("Fail to do message check sum!", e);
throw e;
}
}
if (baos.size() == 0) { // 判斷是否有消息數(shù)??
return null;
}
messages = baos.toByteArray();
try {
baos.close();
} catch (IOException e) {
logger.error("Fail to close byte array output stream!", e);
throw e;
}
if(GZipUtils.checkIfGzip(messages)) {
try {
return GZipUtils.unGZip(messages);
} catch (Exception e) {
return messages;
}
}
return messages;
}
/**
* gZip解壓方法
*
* @throws Exception
*/
public static byte[] unGZip(byte[] data) throws Exception {
byte[] b = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPInputStream gzip =null;
ByteArrayInputStream bis =null;
try {
bis = new ByteArrayInputStream(data);
gzip = new GZIPInputStream(bis);
byte[] buf = new byte[1024 * 1024];
int num = -1;
while ((num = gzip.read(buf, 0, buf.length)) != -1) {
baos.write(buf, 0, num);
}
b = baos.toByteArray();
} catch (Throwable ex) {
throw new Exception("UNGzip the byte [] error,please check the data format", ex);
}finally {
closeStream(baos);
closeStream(gzip);
closeStream(bis);
}
return b;
}
/**
* 關(guān)閉流
* @param clo
*/
private static void closeStream(Closeable clo) {
if(null!=clo) {
try {
clo.close();
} catch (Exception e) {
logger.error("Fail to close Stream!", e);
}
}
}
/**
* 數(shù)據(jù)壓縮
*
* @param is 輸入流
* @param os 輸出流
* @throws Exception
*/
public static void compress(InputStream is, OutputStream os) throws Exception {
GZIPOutputStream gos = new GZIPOutputStream(os);
int count;
byte[] data = new byte[1024];
while ((count = is.read(data, 0, 1024)) != -1) {
gos.write(data, 0, count);
}
gos.finish();
gos.flush();
gos.close();
}
/**
* 數(shù)據(jù)壓縮
*
* @param data
* @return
* @throws Exception
*/
public static byte[] compress(byte[] data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 壓縮
compress(bais, baos);
byte[] output = baos.toByteArray();
baos.flush();
baos.close();
bais.close();
return output;
}
private static int readUByte(byte by) {
int b = by & 0xFF;
if (b <= -1 || b > 255) {
return -1;
}
return b;
}
public static boolean checkIfGzip(byte[] bytes){
if(null==bytes || bytes.length<3) {
return false;
}
if(readUByte(bytes[2])!=8){
return false;
}
int b =readUByte(bytes[0] );
int b2 =readUByte(bytes[1] );
if (b == -1 || b2 == -1) {
return false;
}
int res=(b2<< 8) | b;
if(res!= GZIP_MAGIC){
return false;
}
return true;
}
private static int readUByte(InputStream in) throws IOException {
int b = in.read();
if (b == -1) {
throw new EOFException();
}
if (b < -1 || b > 255) {
throw new IOException( ".read() returned value out of range -1..255: " + b);
}
System.out.println(b);
return b;
}
private static int readUShort(InputStream in) throws IOException {
int b = readUByte(in);
return (readUByte(in) << 8) | b;
}
}
RSA、DSA總結(jié):
非對稱性加密還有很多,RSA和DSA是比較常用和常見的加密方式,安全性來講兩者差不多,DSA只是一種算法,和RSA不同之處在于它不能用作加密和解密,也不能進(jìn)行密鑰交換,只用于簽名,它比RSA要快很多,RSA啥都好,但是RSA算法的秘鑰很長,加密的計(jì)算量比較大,安全性較高,但是加密速度比較慢,所以RSA加密常用于少量的核心數(shù)據(jù)的加密。
四、ECC算法
4.1 簡介
橢圓加密算法(ECC)是一種公鑰加密算法,最初由 Koblitz 和 Miller 兩人于1985年提出,其數(shù)學(xué)基礎(chǔ)是利用橢圓曲線上的有理點(diǎn)構(gòu)成 Abel 加法群上橢圓離散對數(shù)的計(jì)算困難性。公鑰密碼體制根據(jù)其所依據(jù)的難題一般分為三類:大整數(shù)分解問題類、離散對數(shù)問題類、橢圓曲線類。有時(shí)也把橢圓曲線類歸為離散對數(shù)類。
ECC 的主要優(yōu)勢是在某些情況下它比其他的方法使用更小的密鑰 (比如 RSA),提供相當(dāng)?shù)幕蚋叩燃壍陌踩CC 的另一個(gè)優(yōu)勢是可以定義群之間的雙線性映射,基于 Weil 對或是 Tate 對;雙線性映射已經(jīng)在密碼學(xué)中發(fā)現(xiàn)了大量的應(yīng)用,例如基于身份的加密。不過一個(gè)缺點(diǎn)是加密和解密操作的實(shí)現(xiàn)比其他機(jī)制花費(fèi)的時(shí)間長。
ECC 被廣泛認(rèn)為是在給定密鑰長度的情況下,最強(qiáng)大的非對稱算法,因此在對帶寬要求十分緊的連接中會十分有用。
比特幣錢包公鑰的生成使用了橢圓曲線算法,通過橢圓曲線乘法可以從私鑰計(jì)算得到公鑰, 這是不可逆轉(zhuǎn)的過程。
4.2 優(yōu)勢
(1) 安全性高,有研究表示160位的橢圓密鑰與1024位的 RSA 密鑰安全性相同。
(2) 處理速度快,在私鑰的加密解密速度上,ECC 算法比 RSA、DSA 速度更快,存儲空間占用小,帶寬要求低。
4.3 例子
https://github.com/esxgx/easy-ecc
Java 中 Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey 均不支持 ECC 算法。
4.4 ECC算法詳解:
http://www.itdecent.cn/p/58c1750c6f22
五、DH算法
5.1 簡介
DH,全稱為"Diffie-Hellman",它是一種確保共享 KEY 安全穿越不安全網(wǎng)絡(luò)的方法,也就是常說的密鑰一致協(xié)議。由公開密鑰密碼體制的奠基人 Diffie 和 Hellman 所提出的一種思想。簡單的說就是允許兩名用戶在公開媒體上交換信息以生成"一致"的、可以共享的密鑰。也就是由甲方產(chǎn)出一對密鑰 (公鑰、私鑰),乙方依照甲方公鑰產(chǎn)生乙方密鑰對 (公鑰、私鑰)。
以此為基線,作為數(shù)據(jù)傳輸保密基礎(chǔ),同時(shí)雙方使用同一種對稱加密算法構(gòu)建本地密鑰 (SecretKey) 對數(shù)據(jù)加密。這樣,在互通了本地密鑰 (SecretKey) 算法后,甲乙雙方公開自己的公鑰,使用對方的公鑰和剛才產(chǎn)生的私鑰加密數(shù)據(jù),同時(shí)可以使用對方的公鑰和自己的私鑰對數(shù)據(jù)解密。不單單是甲乙雙方兩方,可以擴(kuò)展為多方共享數(shù)據(jù)通訊,這樣就完成了網(wǎng)絡(luò)交互數(shù)據(jù)的安全通訊。
- 5.2 例子
具體例子可以移步到這篇文章:非對稱密碼之DH密鑰交換算法
參考:
https://blog.csdn.net/u014294681/article/details/86705999
https://www.cnblogs.com/wangzxblog/p/13667634.html
https://www.cnblogs.com/taoxw/p/15837729.html
https://www.cnblogs.com/fangfan/p/4086662.html
https://www.cnblogs.com/utank/p/7877761.html
https://blog.csdn.net/m0_59133441/article/details/122686815
https://www.cnblogs.com/muliu/p/10875633.html
https://www.cnblogs.com/wf-zhang/p/14923279.html
http://www.itdecent.cn/p/7a927db713e4
https://blog.csdn.net/ljx1400052550/article/details/79587133
https://blog.csdn.net/yuanjian0814/article/details/109815473