1 公钥和私钥介绍
公钥和私钥是非对称加密技术的核心概念,广泛应用于加密通信、数字签名和身份验证等场景。这种加密技术包括两个数学上相关联的密钥:一个公钥和一个私钥。
公钥和私钥作为非对称加密技术的核心,提供了一种安全的方式来加密数据、实现数字签名和进行安全通信。随着信息技术的发展,对加密技术的依赖日益增加,公钥基础设施(PKI)和相关加密技术在确保数字时代安全性和隐私方面发挥着重要作用。不断的技术进步和新挑战的出现,要求不断发展和更新这些加密技术。
1.1 公钥(Public Key)
-
定义: 公钥是在非对称加密系统中使用的一个密钥,可以安全地公开和广泛分发。
-
功能: 主要用于加密数据和验证数字签名。在加密过程中,公钥用于加密信息,只有对应的私钥才能解密。
-
分发: 公钥被设计成可以公开共享,通常通过数字证书的形式分发。
1.2 私钥(Private Key)
-
定义: 私钥是与公钥成对出现的密钥,应严格保密。
-
功能: 用于解密通过公钥加密的数据和生成数字签名。在数字签名的场景中,私钥用于生成签名,而公钥用于验证签名。
-
保密性: 私钥的保密性是整个加密系统安全的关键。
1.3 加密和解密过程
在非对称加密中,加密和解密过程如下:
-
加密过程: 发送方使用接收方的公钥对消息进行加密,然后将加密后的消息发送给接收方。
-
解密过程: 接收方使用自己的私钥对收到的加密消息进行解密。
1.4 数字签名和验证
-
签名过程: 发送方使用自己的私钥对消息的散列(或摘要)进行签名,并将签名附加在消息上发送。
-
验证过程: 接收方使用发送方的公钥验证签名的真实性和消息的完整性。
1.5 密钥生成和管理
-
密钥生成: 密钥对由专门的算法生成,如RSA、ECDSA或DSA等。
-
密钥长度: 密钥的安全性与其长度成正比。通常,密钥长度越长,其安全性越高。
-
密钥管理: 私钥的安全存储和管理至关重要,常用的方法包括密码保护、硬件安全模块(HSM)等。
1.6 加密算法和标准
-
RSA: 最常用的非对称加密算法之一,适用于加密和数字签名。
-
ECC(椭圆曲线加密): 相比于RSA,ECC在相同的密钥长度下提供更高的安全性。
-
DSA(数字签名算法): 专门用于数字签名的非对称算法。
1.7 应用场景
-
安全通信: 如HTTPS、SSL/TLS等协议,用于安全地传输数据。
-
数字签名: 用于证实信息的来源和完整性,广泛应用于软件分发、电子文档签名等。
-
身份验证: 在多种在线服务和交易中验证用户身份。
-
数据加密: 用于保护敏感数据的隐私,如电子邮件加密、文件加密等。
1.8 安全性考虑
-
密钥强度: 密钥的强度取决于其长度和生成算法的复杂性。
-
密钥泄露风险: 私钥的泄露可能导致整个加密系统的崩溃。
-
前向保密: 确保即使长期密钥被泄露,也不会危及过去的通信。
1.9 挑战和未来发展
-
量子计算威胁: 量子计算的发展可能威胁现有的加密算法。
-
新算法的开发: 寻找对抗量子计算威胁的新加密技术。
-
易用性: 寻求在保持安全性的同时,简化加密技术的使用和管理。
2 场景化交互
2.1 公钥私钥
(1)鲍勃有两把钥匙,一把是公钥,另一把是私钥。
(2)送给他的朋友们----帕蒂、道格、苏珊----每人一把。
鲍勃把公钥
(3)苏珊要给鲍勃写一封保密的信。她写完后用鲍勃的公钥加密,就可以达到保密的效果。
(4)鲍勃收信后,用私钥解密,就看到了信件内容。这里要强调的是,只要鲍勃的私钥不泄露,这封信就是安全的,即使落在别人手里,也无法解密。
(5)鲍勃给苏珊回信,决定采用"数字签名"。他写完后先用Hash函数,生成信件的摘要(digest)。
(6)然后,鲍勃使用私钥,对这个摘要加密,生成"数字签名"(signature)。
(7)鲍勃将这个签名,附在信件下面,一起发给苏珊。
(8)苏珊收信后,取下数字签名,用鲍勃的公钥解密,得到信件的摘要。由此证明,这封信确实是鲍勃发出的。
(9)苏珊再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。
(10)复杂的情况出现了。道格想欺骗苏珊,他偷偷使用了苏珊的电脑,用自己的公钥换走了鲍勃的公钥。此时,苏珊实际拥有的是道格的公钥,但是还以为这是鲍勃的公钥。因此,道格就可以冒充鲍勃,用自己的私钥做成"数字签名",写信给苏珊,让苏珊用假的鲍勃公钥进行解密。
(11)后来,苏珊感觉不对劲,发现自己无法确定公钥是否真的属于鲍勃。她想到了一个办法,要求鲍勃去找"证书中心"(certificate authority,简称CA),为公钥做认证。证书中心用自己的私钥,对鲍勃的公钥和一些相关信息一起加密,生成"数字证书"(Digital Certificate)。
(12)鲍勃拿到数字证书以后,就可以放心了。以后再给苏珊写信,只要在签名的同时,再附上数字证书就行了。
(13)苏珊收信后,用CA的公钥解开数字证书,就可以拿到鲍勃真实的公钥了,然后就能证明"数字签名"是否真的是鲍勃签的。
2.2 数字证书
下面,我们看一个应用"数字证书"的实例:https协议。这个协议主要用于网页加密。
(1)首先,客户端向服务器发出加密请求。
(2)服务器用自己的私钥加密网页以后,连同本身的数字证书,一起发送给客户端。
(3)客户端(浏览器)的"证书管理器",有"受信任的根证书颁发机构"列表。客户端会根据这张列表,查看解开数字证书的公钥是否在列表之内。
(4)如果数字证书记载的网址,与你正在浏览的网址不一致,就说明这张证书可能被冒用,浏览器会发出警告。
(5)如果这张数字证书不是由受信任的机构颁发的,浏览器会发出另一种警告。
(6)数字证书如果是可靠的,客户端就可以使用证书中的服务器公钥,对信息进行加密,然后与服务器交换加密信息。
3 RSA算法
RSA公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
package cn.aizichan.utils.digest;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
public class RSACoder {
//非对称密钥算法
public static final String KEY_ALGORITHM="RSA";
/**
* 密钥长度,DH算法的默认密钥长度是1024
* 密钥长度必须是64的倍数,在512到65536位之间
* */
private static final int KEY_SIZE=512;
//公钥
private static final String PUBLIC_KEY="xiaoxiaorenzhe";
//私钥
private static final String PRIVATE_KEY="dadapangzi";
/**
* 初始化密钥对
* @return Map 甲方密钥的Map
* */
public static Map<String,Object> initKey() throws Exception{
//实例化密钥生成器
KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成密钥对
KeyPair keyPair=keyPairGenerator.generateKeyPair();
//甲方公钥
RSAPublicKey publicKey=(RSAPublicKey) keyPair.getPublic();
System.out.println("系数:"+publicKey.getModulus()+" 加密指数:"+publicKey.getPublicExponent());
//甲方私钥
RSAPrivateKey privateKey=(RSAPrivateKey) keyPair.getPrivate();
System.out.println("系数:"+privateKey.getModulus()+"解密指数:"+privateKey.getPrivateExponent());
//将密钥存储在map中
Map<String,Object> keyMap=new HashMap<String,Object>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 私钥加密
* @param data待加密数据
* @param key 密钥
* @return byte[] 加密数据
* */
public static byte[] encryptByPrivateKey(byte[] data,byte[] key) throws Exception{
//取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//生成私钥
PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);
//数据加密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
* @param data待加密数据
* @param key 密钥
* @return byte[] 加密数据
* */
public static byte[] encryptByPublicKey(byte[] data,byte[] key) throws Exception{
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
//产生公钥
PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);
//数据加密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
* */
public static byte[] decryptByPrivateKey(byte[] data,byte[] key) throws Exception{
//取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//生成私钥
PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);
//数据解密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
* */
public static byte[] decryptByPublicKey(byte[] data,byte[] key) throws Exception{
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
//产生公钥
PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);
//数据解密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
/**
* 取得私钥
* @param keyMap 密钥map
* @return byte[] 私钥
* */
public static byte[] getPrivateKey(Map<String,Object> keyMap){
Key key=(Key)keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
* @param keyMap 密钥map
* @return byte[] 公钥
* */
public static byte[] getPublicKey(Map<String,Object> keyMap) throws Exception{
Key key=(Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//初始化密钥
//生成密钥对
Map<String,Object> keyMap=RSACoder.initKey();
//公钥
byte[] publicKey=RSACoder.getPublicKey(keyMap);
//byte[] publicKey = b;
//私钥
byte[] privateKey=RSACoder.getPrivateKey(keyMap);
System.out.println("公钥:"+Base64.encode(publicKey));
System.out.println("私钥:"+Base64.encode(privateKey));
System.out.println("================密钥对构造完毕,甲方将公钥公布给乙方,开始进行加密数据的传输=============");
String str="aattaggcctegthththfef/aat.mp4";
System.out.println("===========甲方向乙方发送加密数据==============");
System.out.println("原文:"+str);
//甲方进行数据的加密
byte[] code1=RSACoder.encryptByPublicKey(str.getBytes(), publicKey);
System.out.println("甲方 使用乙方公钥加密后的数据:"+Base64.encode(code1));
System.out.println("===========乙方使用甲方提供的公钥对数据进行解密==============");
//乙方进行数据的解密
//byte[] decode1=RSACoder.decryptByPublicKey(code1, publicKey);
byte[] decode1=RSACoder.decryptByPrivateKey(code1, privateKey);
System.out.println("乙方解密后的数据:"+new String(decode1)+"");
System.out.println("===========反向进行操作,乙方向甲方发送数据==============");
str="乙方向甲方发送数据RSA算法";
System.out.println("原文:"+str);
//乙方使用公钥对数据进行加密
byte[] code2=RSACoder.encryptByPublicKey(str.getBytes(), publicKey);
System.out.println("===========乙方使用公钥对数据进行加密==============");
System.out.println("加密后的数据:"+Base64.encode(code2));
System.out.println("=============乙方将数据传送给甲方======================");
System.out.println("===========甲方使用私钥对数据进行解密==============");
//甲方使用私钥对数据进行解密
byte[] decode2=RSACoder.decryptByPrivateKey(code2, privateKey);
System.out.println("甲方解密后的数据:"+new String(decode2));
}
}