加密算法
5、非对称加密
非对称加密算法与对称加密算法的主要差别在于非对称加密算法用于加密和解密的密钥不相同,一个公开,称为公钥;一个保密,称为私钥。因此,非对称密码算法也称为双钥或公钥加密算法。
非对称加密算法解决了对称加密算法密钥分配问题,并极大地提高了算法安全性。多种B2C或B2B应用均使用非对称加密算法作为数据加密的核心算法。
非对称加密算法有别于对称加密算法,将密钥一分为二,公钥公开,私钥保密。公钥通过非安全通道发放,私钥则由发放者保留。公钥与私钥相对应,成对出现。公钥加密的数据,只可使用私钥对其解密。反之,私钥加密的数据,只可使用公钥对其解密。
5.1、非对称加密算法的分类
非对称加密算法源于DH算法(Diffie-Hellman,密钥交换算法),由W.Diffie和M.Hellman共同提出。该算法为非对称加密算法奠定了基础,堪称非对称加密算法之鼻祖。
DH算法提出后,国际上相继出现了各种实用性更强的非对称加密算法,其构成主要是基于数学问题的求解,主要分为两类:
- 基于因子分解难题:RSA算法是最为典型的非对称加密算法,该算法由美国麻省理工学院(MIT)的Ron Rivest、AdiShamir和Leonard Adleman.三位学者提出,并以这三位学者的姓氏开头字母命名,称为RSA算法。RSA算法是当今应用范围最为广泛的非对称加密算法,也是第一个既能用于数据加密也能用于数字签名的算法。
- 基于离散对数难题:ElGamal算法由Taher ElGamal提出,以自己的名字命名。该算法既可用于加密/解密,也可用于数字签名,并为数字签名算法形成标准提供参考。美国的DSS(Digital Signature Standard,数据签名标谁)的DSA(Digital Signature Algorithm,数字签名算法)经ElGamal算法演变而来。
ECC(Elliptical Curve Cryptography,椭圆曲线加密)算法以椭圆曲线理论为基础,在创建密钥时可做到更快、更小,并且更有效。ECC算法通过椭圆曲线方程式的性质产生密钥,而不是采用传统的方法利用大质数的积来产生。
在Java6中仅提供了DH和RSA两种算法实现,而在Java7中新增了ECDH算法实现,用于增强密钥交换算法,提升HTTPS服务的安全性。通过Boucy Castle可以获得ElGamal算法支持。
5.2、DH(密钥交换算法)
1)、定义
DH(Diffie-Hellman)密钥交换算法主要是为了解决密钥交换而发展出来的。DH算法是一个密钥协商算法,仅能用于密钥分配,不能用于加密或解密。DH算法的安全性基于有限域上的离散对数难题,基于这种安全性,通过DH算法进行密钥分配,使得消息的收发双方可以安全地交换一个秘密密钥,再通过这个密钥对数据进行加密和解密处理。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
DH | 512至1024位(密钥长度为64倍数,范围在512-1024位之间) | 1024 | 无 | 无 | Java7实现 |
2)、应用场景
仅用于密钥交换场景,不适用于数据传输的加解密,如AB两个系统需要交换密钥,则过程如下:
- A系统构建密钥:构建一对公私密钥Private Key1和Public Key1;
- A系统向B系统公布自己的公钥(Public Key1);
- B系统使用A公布的公钥(Public Key1)建立一对密钥:Private Key2和Public Key2;
- B系统向A系统公布自己的公钥Public Key2;
- A系统使用自己的私钥Private Key1和B系统的公钥Public Key2构建本地密钥;
- B系统使用自己的私钥Private Key2和A系统的公钥Public Key1构建本地密钥
虽然AB系统使用了不同的密钥建立自己的本地密钥,但是AB系统获得本地密钥是一致的。
3)、Java实现
注意:JDK8 update 161之后,DH的密钥长度至少为512位,但AES算法密钥不能达到这样的长度,长度不一致所以导致报错。
解决方案,添加如下VM参数:
-Djdk.crypto.KeyAgreement.legacyKDF=true
public class DHCoder {
/**
* 非对称加密密钥算法
*/
public static final String KEY_ALGORITHM = "DH";
/**
* 本地密钥算法,即对称加密密钥算法,可选DES、DESede和AES算法
*/
public static final String SECRET_KEY_ALGORITHM = "AES";
/**
* 默认密钥长度,DH算法默认密钥长度为1024 密钥长度必须是64的倍数,其范围在512-1024位之间
*/
private static final int KEY_SIZE = 512;
/**
* 公钥
*/
private static final String PUBLIC_KEY = "DHPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "DHPrivateKey";
/**
* 初始化甲方密钥
* @return
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
//实例化密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥对生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//甲方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
//甲方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
//将密钥对存储在Map中
Map<String, Object> keyMap = new HashMap<>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 初始化乙方密钥
* @param key 甲方公钥
* @return map 乙方密钥map
* @throws Exception
*/
public static Map<String, Object> initKey(byte[] key) throws Exception {
//解析甲方公钥
//转换公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//产生公钥
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
//由甲方公钥构建乙方密钥
DHParameterSpec dhParameterSpec = ((DHPublicKey)publicKey).getParams();
//实例化密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
//初始化密钥对生成器
keyPairGenerator.initialize(dhParameterSpec);
//产生密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//乙方公钥
DHPublicKey pubKey = (DHPublicKey) keyPair.getPublic();
//乙方私钥
DHPrivateKey priKey = (DHPrivateKey) keyPair.getPrivate();
//将密钥对存储在Map中
Map<String, Object> keyMap = new HashMap<>();
keyMap.put(PUBLIC_KEY, pubKey);
keyMap.put(PRIVATE_KEY, priKey);
return keyMap;
}
/**
* 构建密钥
* @param publicKey 公钥
* @param privateKey 私钥
* @return byte[] 本地密钥
* @throws Exception
*/
public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) throws Exception {
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
//产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);
//初始化私钥
//密钥材料转换
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);
//产生私钥
PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//实例化
KeyAgreement keyAgreement = KeyAgreement.getInstance(keyFactory.getAlgorithm());
keyAgreement.init(priKey);
keyAgreement.doPhase(pubKey, true);
//生成本地密钥
SecretKey secretKey = keyAgreement.generateSecret(SECRET_KEY_ALGORITHM);
return secretKey.getEncoded();
}
/**
* 获取私钥
* @param keyMap
* @return
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 获取公钥
* @param keyMap
* @return
*/
public static byte[] getPublickKey(Map<String, Object> keyMap) {
Key key = (Key)keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 加密
* @param data 待加密数据
* @param key 密钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
// 生成本地密钥
SecretKey secretKey = new SecretKeySpec(key, SECRET_KEY_ALGORITHM);
// 数据加密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 解密<
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密后数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
// 生成本地密钥
SecretKey secretKey = new SecretKeySpec(key, SECRET_KEY_ALGORITHM);
// 数据解密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
//甲方密钥
Map<String, Object> aMap = initKey();
//乙方密钥,DH算法根据甲方公钥生成
Map<String, Object> bMap = initKey(getPublickKey(aMap));
//甲方本地密钥,使用甲方私钥和乙方公钥构建
byte[] aKey = getSecretKey(getPublickKey(bMap), getPrivateKey(aMap));
System.out.println("甲方本地密钥:" + HexUtil.encodeHexStr(aKey));
//甲方加密
byte[] encrypt = encrypt(str.getBytes(), aKey);
System.out.println("加密后的数据:" + HexUtil.encodeHexStr(encrypt));
//乙方本地密钥,使用乙方私钥和甲方公钥构建
byte[] bKey = getSecretKey(getPublickKey(aMap), getPrivateKey(bMap));
System.out.println("乙方本地密钥:" + HexUtil.encodeHexStr(bKey));
//乙方解密
byte[] decrypt = decrypt(encrypt, aKey);
System.out.println("解密后的数据:" + new String(decrypt));
}
}
5.3、ECDH(密钥交换算法)
1)、定义
ECDH是基于椭圆曲线加密算法的密钥交换算法(这里将ECC简化为EC,同知椭圆曲线加密算法),密钥较短但密钥安全性更强,其原理与DH算法完全一致。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
ECDH | 112~571位 | 256 | - | - | Java7实现 |
注意:实测Java8已经移除ECDH支持,推荐使用Bouncy Castle实现
在代码实现方面,ECDH算法与DH算法的差别主要是密钥长度和对称加密算法。
ECDH算法密钥长度似乎没有什么规律,官方也没有明确说明。从对密钥长度的测试结果来看,ECDH算法密钥长度共有3种:112、256和571位。
2)、Bouncy Castle实现
public class ECDHCoder {
/**
* 非对称加密算法ECDH
*/
public static final String KEY_ALGORITHM = "ECDH";
/**
* 本地密钥算法,即对称加密密钥算法,可选Blowfish、RC2、RC4算法
*/
public static final String SECRET_ALGORITHM = "Blowfish";
/**
* 密钥长度
*/
private static final int KEY_SIZE = 256;
/**
* 公钥
*/
private static final String PUBLIC_KEY = "ECDHPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "ECDHPrivateKey";
/**
* 初始化甲方密钥
*/
public static Map<String, Object> initKey() throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 初始化乙方密钥
*/
public static Map<String, Object> initKey(byte[] key) throws Exception {
//解析甲方公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);
//由甲方公钥构建乙方密钥
ECParameterSpec ecParameterSpec = ((ECPublicKey)pubKey).getParams();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
keyPairGenerator.initialize(ecParameterSpec);
KeyPair keyPair = keyPairGenerator.genKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 构建本地密钥
*/
public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//还原公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);
//还原私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);
PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//生成本地密钥
KeyAgreement keyAgreement = KeyAgreement.getInstance(keyFactory.getAlgorithm());
keyAgreement.init(priKey);
keyAgreement.doPhase(pubKey, true);
SecretKey secretKey = keyAgreement.generateSecret(SECRET_ALGORITHM);
return secretKey.getEncoded();
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 加密
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITHM);
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 解密
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITHM);
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
//甲方密钥
Map<String, Object> aMap = initKey();
//乙方密钥,ECDH算法根据甲方公钥生成
Map<String, Object> bMap = initKey(getPublicKey(aMap));
//甲方本地密钥,使用甲方私钥和乙方公钥构建
byte[] aKey = getSecretKey(getPublicKey(bMap), getPrivateKey(aMap));
System.out.println("甲方本地密钥:" + HexUtil.encodeHexStr(aKey));
//甲方加密
byte[] encrypt = encrypt(str.getBytes(), aKey);
System.out.println("加密后的数据:" + HexUtil.encodeHexStr(encrypt));
//乙方本地密钥,使用乙方私钥和甲方公钥构建
byte[] bKey = getSecretKey(getPublicKey(aMap), getPrivateKey(bMap));
System.out.println("乙方本地密钥:" + HexUtil.encodeHexStr(bKey));
//乙方解密
byte[] decrypt = decrypt(encrypt, aKey);
System.out.println("解密后的数据:" + new String(decrypt));
}
}
甲方本地密钥:3f7b466360aa115a0090e8373a492fa2
加密后的数据:c06a4c9ae28083d6808dcb55328e296c
乙方本地密钥:3f7b466360aa115a0090e8373a492fa2
解密后的数据:hello world
5.4、RSA(典型非对称加密算法)
1)、定义
RSA是三个创建者的姓名缩写,是唯一被广泛接受并实现的通用公开加密算法,目前已经成为非对称加密的国际标准,也可以用于数据加密,也可以用于数据签名。RSA算法公钥长度远小于私钥长度,并遵循“公钥加密私钥解密”和“私钥加密公钥解密”两项加密解密原则。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
RSA | 512-65536位(密钥长度必须是64的倍数) | 1024 | ECB | NoPadding PKCS1Padding OAEPWITHMD5AndMGF1Padding OAEPWITHSHA!AndMGF1Pading OAEPWITHSHA256AndMGF1Padding OAEPWITHSHA384AndMGF1Padding OAEPWITHSHA512AndMGF1Padding | Java7实现 |
RSA | 同上 | 2048 | NONE | NoPadding PKCS1Padding OAEPWithMD5AndMGF1Padding OAEPWithSHA1AndMGF1Padding OAEPWithSHA224AndMGF1Padding OAEPWithSHA256AndMGF1Padding OAEPWithSHA384AndMGF1Padding OAEPWithSHA512AndMFG1Padding ISO9796-1Padding | Bouncy Castle实现 |
2)、模型分析
仍以甲乙两方收发消息为例。甲方作为消息的发送方,乙方作为消息的接收方。我们假设甲乙双方在消息传递之前已将RSA算法作为消息传递的加密算法。为完成加密消息传递,甲乙双方需要以下操作:
- 由消息发送的一方构建密钥对,这里由甲方完成。
- 由消息发送的一方公布公钥至消息接收方,这里由甲方将公钥公布给乙方。
甲方可以向乙方发送加密消息,乙方也同样可以向甲方发送加密消息。
公钥是通过甲方发送给乙方的,其在传递过程中很有可能被截获,也就是说窃听者很有可能获得公钥。如果窃听者获得了公钥,向甲方发送数据,甲方是无法辨别消息的真伪的。因此,虽然可以使用公钥对数据加密,但这种方式还是会有存在一定的安全隐患。如果要建立更安全的加密消息传递模型,就需要甲乙两方构建两套非对称加密算法密钥,仅遵循“私钥加密,公钥解密”的方式进行加密消息传递。
3)、Java实现
public class RSACoder {
/**
* 非对称加密密钥算法
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA密钥长度,默认1024位,密钥长度必须是64的倍数,范围在512-65535之间
*/
private static final int KEY_SIZE = 512;
/**
* 初始化密钥
*
* @return Map 密钥map
* @throws Exception
*/
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();
//私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//封装密钥
Map<String, Object> keyMap = new HashMap<>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 获取私钥
*
* @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) {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 公钥加密
*
* @param data 待加密的数据
* @param key 公钥
* @return byte[] 加密后的数据
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
//获得公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
//对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 私钥加密
*
* @param data 待加密的数据
* @param key 私钥
* @return byte[] 加密后的数据
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {
//获得私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
*
* @param data 待解密的数据
* @param key 公钥
* @return 解密后的数据
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {
//获得公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
//对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
*
* @param data 待解密的数据
* @param key 私钥
* @return byte[] 解密后的数据
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
//获取私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
Map<String, Object> map = initKey();
System.out.println("私钥:" + HexUtil.encodeHexStr(getPrivateKey(map)));
System.out.println("公钥:" + HexUtil.encodeHexStr(getPublicKey(map)));
//私钥加密公钥解密
byte[] encryptByPrivateKey = encryptByPrivateKey(str.getBytes(), getPrivateKey(map));
System.out.println("私钥加密后:" + HexUtil.encodeHexStr(encryptByPrivateKey));
byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, getPublicKey(map));
System.out.println("公钥解密后:" + new String(decryptByPublicKey));
//公钥加密私钥解密
byte[] encryptByPublicKey = encryptByPublicKey(str.getBytes(), getPublicKey(map));
System.out.println("公钥加密后:" + HexUtil.encodeHexStr(encryptByPrivateKey));
byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, getPrivateKey(map));
System.out.println("私钥解密后:" + new String(decryptByPrivateKey));
}
}
4)、Bouncy Castle实现
Bouncy Castle提供了更多的填充方式,比如ISO9796-1Padding。
public class RSACoderByBC {
/**
* 非对称加密密钥算法
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 算法/工作模式/填充方式
*/
public static final String CIPHER_ALGORITHM = "RSA/NONE/ISO9796-1Padding";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA密钥长度,默认2048位,密钥长度必须是64的倍数,范围在512-65535之间
*/
private static final int KEY_SIZE = 2048;
/**
* 初始化密钥
*
* @return Map 密钥map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
Security.addProvider(new BouncyCastleProvider());
//实例化密钥对生成器
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, Object> keyMap = new HashMap<>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 获取私钥
*
* @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) {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 公钥加密
*
* @param data 待加密的数据
* @param key 公钥
* @return byte[] 加密后的数据
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
//获得公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
//对数据加密
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 私钥加密
*
* @param data 待加密的数据
* @param key 私钥
* @return byte[] 加密后的数据
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {
//获得私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//对数据加密
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
*
* @param data 待解密的数据
* @param key 公钥
* @return 解密后的数据
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {
//获得公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
//对数据解密
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
*
* @param data 待解密的数据
* @param key 私钥
* @return byte[] 解密后的数据
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
//获取私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//对数据解密
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
Map<String, Object> map = initKey();
System.out.println("私钥:" + HexUtil.encodeHexStr(getPrivateKey(map)));
System.out.println("公钥:" + HexUtil.encodeHexStr(getPublicKey(map)));
//私钥加密公钥解密
byte[] encryptByPrivateKey = encryptByPrivateKey(str.getBytes(), getPrivateKey(map));
System.out.println("私钥加密后:" + HexUtil.encodeHexStr(encryptByPrivateKey));
byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, getPublicKey(map));
System.out.println("公钥解密后:" + new String(decryptByPublicKey));
//公钥加密私钥解密
byte[] encryptByPublicKey = encryptByPublicKey(str.getBytes(), getPublicKey(map));
System.out.println("公钥加密后:" + HexUtil.encodeHexStr(encryptByPrivateKey));
byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, getPrivateKey(map));
System.out.println("私钥解密后:" + new String(decryptByPrivateKey));
}
}
5.5、EIGamal(常用非对称加密算法)
1)、定义
ElGamal公钥密码算法是1985年由塔希尔·盖莫尔提出,是一个基于迪菲-赫尔曼密钥交换的非对称加密算法,是在密码协议中有着重要应用的一类公钥密码算法,其安全性是基于有限域上离散对数学问题的难解性。它至今仍是一个安全性良好的公钥密码算法,是一种既可用于加密又可用于数字签名的公钥密码算法。
由于ElGamal算法具有较好的安全性,因此得到了广泛的应用。著名的美国数字签名标准(Digital Signature Standard,DSS)就是采用了ElGamal签名方案的一种变形一DSA(Digital Signature Algorithm)。ElGamal的一个不足之处是它的密文会成倍扩张。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
ELGamal | 160~16384位(密钥长度必须是8的倍数) | 1024 | ECB NONE | NoPadding PKCS1Padding OAEPWithMD5AndMGF1Padding OAEPWithSHA1AndMGF1Padding OAEPWithSHA224AndMGF1Padding OAEPWithSHA256AndMGF1Padding OAEPWitSHA384AndMGF1Padding OAEPWithSHA512AndMGF1Padding ISO9796-1Padding | Bouncy Castl实现 |
2)、模型分析
Java7并没有提供ElGamal算法实现,而相关ElGamal算法的实现资料也相当有限。以Bouncy Castle对于ElGamal算法实现为例,描述基于ElGamal算法的消,息传递模型,如图所示。
ElGamal算法在构建密钥时的操作流程几乎与RSA算法完全一致。不同的是,这次密钥对的构建者换了主人。我们仍以甲乙两方收发消息为例,甲方作为消息的发送方,乙方作为消息的接收方。我们假设甲乙双方在消息传递之前已将ElGamal算法作为消息传递的加密算法。为完成加密消息传递,甲乙双方需要以下操作:
- 由消息发送的一方构建密钥对,这里由乙方完成。
- 由消息发送的一方公布公钥至消息接收方,这里由乙方将公钥公布给甲方。
3)、Bouncy Castl实现
JCE框架为其他非对称加密算法实现提供了一个构建密钥对方式,均基于DH算法参数材料DHParameterSpec
类。
注意,仅支持公钥加密,私钥解密。
public class ElGamlCoder {
//非对称加密密钥算法
public static final String KEY_ALGORITHM = "ElGamal";
//密钥长度
private static final int KEY_SIZE = 256;
//公钥
private static final String PUBLIC_KEY = "ElGamalPublicKey";
//私钥
private static final String PRIVATE_KEY = "ElGamalPrivateKey";
/**
* 生成密钥
*/
public static Map<String, Object> initKey() throws Exception {
//加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
//实例化算法参数生成器
AlgorithmParameterGenerator apg = AlgorithmParameterGenerator.getInstance(KEY_ALGORITHM);
//初始化算法参数生成器
apg.init(KEY_SIZE);
//生成算法参数
AlgorithmParameters algorithmParameters = apg.generateParameters();
//构建参数材料
DHParameterSpec elParams = algorithmParameters.getParameterSpec(DHParameterSpec.class);
//实例化秒对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥对生成器
keyPairGenerator.initialize(elParams, new SecureRandom());
//生成密钥对
KeyPair keyPair = keyPairGenerator.genKeyPair();
//获取密钥
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
//封装
Map<String, Object> map = new HashMap<>(2);
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
return map;
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 公钥加密
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
*/
public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
Map<String, Object> keyMap = initKey();
//公钥加密
byte[] encryptByPublicKey = encryptByPublicKey(str.getBytes(), getPublicKey(keyMap));
System.out.println("公钥加密后:" + HexUtil.encodeHexStr(encryptByPublicKey));
//私钥解密
byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, getPrivateKey(keyMap));
System.out.println("私钥解密后:" + new String(decryptByPrivateKey));
}
}
6、数字签名算法(带密钥的消息摘要算法)
数字签名算法可以看做是一种带有密钥的消息摘要算法,并且这种密钥包含了公钥和私钥。也就是说,数字签名算法是非对称加密算法和消息摘要算法的结合体。
数字签名算法是公钥基础设施(Public Key Infrastructure,PKI)以及许多网络安全机制(SSL/TLS、VPN等)的基础。
数字签名算法要求能够验证数据完整性、认证数据来源,并起到抗否认的作用。这3点与OSI参考模型中的数据完整性服务、认证(鉴别)服务和抗否认性服务相对应。
消息摘要算法是验证数据完整性的最佳算法,因此,该算法成为数字签名算法中的必要组成部分。
基于数据完整性验证,我们希望数据的发送方(以下称甲方)可以对自己所发送的数据做相应的处理(签名处理),同时给出对应的凭证(签名),并且数据的接收方(以下称乙方)可以验证该签名是否与数据甲方发送的数据相符。
如果任何机构都可以进行签名处理,那签名本身就失去了验证的意义。因此,签名操作只能由甲方来完成,验证签名操作则由乙方来完成。既然签名操作仅限于甲方,那么签名操作本身是基于甲方的某些私有信息完成的操作。并且,用于验证操作的相关信息是由甲方公布给乙方。
用于签名的相关信息私有,用于验证的相关信息公有,且这两种信息必须成对出现。非对称加密算法中的私钥和公钥满足这种关系,成为数字签名算法中的重要元素。
数字签名算法包含签名和验证两项操作,遵循“私钥签名,公钥验证”的签名验证方式签名时需要使用私钥和待签名数据,验证时则需要公钥、签名值和待签名数据,其核心算法主要是消息摘要算法。因此,我们可以把数字签名算法近似看成是一种附加了公钥和私钥的消息摘要算法。
与摘要值的表示方式相同,签名值也常以十六进制字符串的形式来表示。
注意:数字签名算法在实际运用时,通常是先使用消息摘要算法对原始消息做摘要处理,然后再使用私钥对摘要值做签名处理;验证签名时,则使用公钥验证消息的摘要值。
Java8提供的数字签名算法支持:
SUN version 1.8
type=Signature,algorithm=SHA1withDSA
type=Signature,algorithm=NONEwithDSA
type=Signature,algorithm=SHA224withDSA
type=Signature,algorithm=SHA256withDSA
SunRsaSign version 1.8
type=Signature,algorithm=MD2withRSA
type=Signature,algorithm=MD5withRSA
type=Signature,algorithm=SHA1withRSA
type=Signature,algorithm=SHA224withRSA
type=Signature,algorithm=SHA256withRSA
type=Signature,algorithm=SHA384withRSA
type=Signature,algorithm=SHA512withRSA
SunEC version 1.8
type=Signature,algorithm=NONEwithECDSA
type=Signature,algorithm=SHA1withECDSA
type=Signature,algorithm=SHA224withECDSA
type=Signature,algorithm=SHA256withECDSA
type=Signature,algorithm=SHA384withECDSA
type=Signature,algorithm=SHA512withECDSA
SunJSSE version 1.8
type=Signature,algorithm=MD2withRSA
type=Signature,algorithm=MD5withRSA
type=Signature,algorithm=SHA1withRSA
type=Signature,algorithm=MD5andSHA1withRSA
6.1、数字签名算法的分类
数字签名算法主要包括RSA、DSA和ECDSA共3种算法。其中,RSA算法源于整数因子分解问题,DSA和ECDSA算法源于离散对数问题。
- 作为非对称加密算法,RSA算法堪称典型,同样也是数字签名算法中的经典。基于RSA算法密钥,结合消息摘要算法可形成对应的签名算法。如结合消息摘要算法MD5算法,可形成MD5 withRSA算法。
- DSA算法是继RSA算法后出现的基于DSS的数字签名算法,旨在形成数字签名标准。DSA算法主要为后续数字签名算法的形成奠定基础。
- ECDSA算法是椭圆曲线加密算法ECC与DSA算法的结合,相对于传统签名算法,它具有速度快、强度高、签名短等优点,其用途也越来越广泛。
上述算法中,在Java语言中都可获得相应的支持。其中,Java7完全实现了DSA算法,部分RSA算法需要由Bouncy Castle提供支持,ECDSA算法则完全需要Bouncy Castle提供支持。
6.2、模型分析
在图中,甲方作为消息的发送方,乙方作为消息的接收方。我们假设甲乙双方在消息传递之前已将指定了将要使用的数字签名算法(如RSA算法)。为完成签名验证,甲乙双方需要以下操作:
- 由消息发送的一方构建密钥对,这里由甲方完成。
- 由消息发送的一方公布公钥至消息接收方,这里由甲方将公钥公布给乙方。
完成这两步操作后,甲方向乙方发送的数据就可以做验证了:
在图中,甲方向乙方发送数据时需要附加签名,数据与签名形成一则消息发送给接收者。
签名与实体(这里指签名前的数据)不可分离,作为一个整体发送给乙方。并且,私钥仅用于签名,公钥仅用于验证。
6.3、RSA算法(经典数字签名算法)
1)、定义
RSA数字签名算法是Diffie和Hellman提出数字签名思想后的第一个数字签名算法,是由Rivest、Shamir和Adleman.三人共同完成的,该签名算法源于RSA公钥密码算法的思想,将RSA公钥密码算法按照数字签名的方式运用。RSA数字签名算法是迄今为止应用最为广泛的数字签名算法。
Java7不仅提供了RSA加密算法相关实现,同时也提供了RSA数字签名算法实现。RSA数字签名算法的密钥实现与RSA加密算法一致,算法名称同为“RSA”,密钥产生与
转换完全一致。
RSA数字签名算法主要分为MD系列和SHA系列两大类。
- MD系列主要包括MD2 withRSA和MD5 withRSA共2种数字签名算法;
- SHA系列主要包括SHA1 withRSA、SHA224 withRSA、SHA256 withRSA、SHA384 withRSA和SHA512 withRSA共5种数字签名算法,其中,SHA224 withRSA、SHA256 withRSA、SHA384 withRSA和SHA512 withRSA这4种数字签名算法需要由第三方加密组件包提供,例如Bouncy Castle。Java6则只提供了MD2 withRSA,MD5 withRSA和SHA1withRSA共3种数字签名算法。从Java7开始,增加了NONEwithRSA、SHA256 withRSA、SHA384 withRSA和SHA512 withRSA等算法。至此,Java平台共支持7种RSA系列的数字签名算法。
算法 | 密钥长度 | 密钥长度默认值 | 签名长度 | 备注 |
---|---|---|---|---|
NONEwithRSA | 512~65536(密钥长度必须时6的倍数) | 1024 | 与密钥长度相同 | Java7实现 |
MD2withRSA | 同上 | 同上 | 同上 | 同上 |
MD5withRSA | 同上 | 同上 | 同上 | 同上 |
SHA1withRSA | 同上 | 同上 | 同上 | 同上 |
SHA224withRSA | 同上 | 2048 | 同上 | Bouncy Castle实现 |
SHA256withRSA | 同上 | 同上 | 同上 | 同上 |
SHA384withRSA | 同上 | 同上 | 同上 | 同上 |
SHA512withRSA | 同上 | 同上 | 同上 | 同上 |
RIPEMD128withRSA | 同上 | 同上 | 同上 | 同上 |
RIPEMD160withRSA | 同上 | 同上 | 同上 | 同上 |
在上表中,SHA256withRSA,SHA384withRSA和SHA512withRSA在Java平台中默认长度为1024位,由Java7提供实现。
2)、Java实现
public class RSASignatureCoder {
//数字签名:密钥算法
public static final String KEY_ALGORITHM = "RSA";
//数字签名:签名/验证算法
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
//公钥
private static final String PUBLIC_KEY = "RSAPublicKey";
//私钥
private static final String PRIVATE_KEY = "RSAPrivateKey";
//RSA密钥长度,JAVA默认1024位,长度必须是64的倍数,范围在512~65535之间
private static final int KEY_SIZE = 512;
/**
* 初始化密钥
*/
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();
//私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//封装
Map<String, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 签名
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
//转换密钥材料
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//获取私钥对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initSign(priKey);
//更新数据
signature.update(data);
//签名
return signature.sign();
}
/**
* 校验
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
//转换密钥材料
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成公钥
PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initVerify(pubKey);
//更新数据
signature.update(data);
//验证
return signature.verify(sign);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
Map<String, Object> keyMap = initKey();
//签名
byte[] sign = sign(str.getBytes(), getPrivateKey(keyMap));
System.out.println(HexUtil.encodeHexStr(sign));
//验证
boolean verify = verify(str.getBytes(), getPublicKey(keyMap), sign);
System.out.println(verify);
}
}
3)、Bouncy Castle实现
默认密钥长度为2048位,支持SHA224withRSA等算法。
public class RSASignatureCoderByBC {
//数字签名:密钥算法
public static final String KEY_ALGORITHM = "RSA";
//数字签名:签名/验证算法
public static final String SIGNATURE_ALGORITHM = "SHA224withRSA";
//公钥
private static final String PUBLIC_KEY = "RSAPublicKey";
//私钥
private static final String PRIVATE_KEY = "RSAPrivateKey";
//RSA密钥长度,BouncyCastle默认为2048位,长度必须是64的倍数,范围在512~65535之间
private static final int KEY_SIZE = 2048;
/**
* 初始化密钥
*/
public static Map<String, Object> initKey() throws Exception {
Security.addProvider(new BouncyCastleProvider());
//实例化密钥生成器
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, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 签名
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
//转换密钥材料
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//获取私钥对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initSign(priKey);
//更新数据
signature.update(data);
//签名
return signature.sign();
}
/**
* 校验
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
//转换密钥材料
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成公钥
PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initVerify(pubKey);
//更新数据
signature.update(data);
//验证
return signature.verify(sign);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
Map<String, Object> keyMap = initKey();
//签名
byte[] sign = sign(str.getBytes(), getPrivateKey(keyMap));
System.out.println(HexUtil.encodeHexStr(sign));
//验证
boolean verify = verify(str.getBytes(), getPublicKey(keyMap), sign);
System.out.println(verify);
}
}
6.4、DSA算法(数字签名标准算法)
1)、定义
RSA作为经典数字签名算法,很快就成了数字签名算法的研究对象,并逐步转为标准DSS,并形成了DSA算法,这为后续数字签名算法的提出奠定了基础,如ECDSA(椭圆曲线数字签名算法)。
1991年,美国国家标准技术协会公布了数字签名标准(Digital Signature Standard,DSS),于1994年正式生效,并作为美国联邦信息处理标准。DSS本质上是ElGamal数字签名算法,DSS使用的算法称为数字签名算法(Digital Signature Algorithm,DSA)。
DSA算法与RSA算法都是数字证书中不可或缺的两种算法。两者不同的是,DSA算法仅包含数字签名算法,使用DSA算法的数字证书无法进行加密通信,而RSA算法既包含加密/解密算法,同时兼有数字签名算法。
Java7提供了DSA算法实现,在实现层面,我们可以认为DSA算法实现就是RSA数字签名算法实现的简装版。与RSA数字签名算法实现相比,DSA算法仅支持SHA系列的消息摘要算法。
Java6仅支持SHA1 withDSA算法,在Java7中扩展了SHA224 withDSA、SHA256 withDSA、SHA384 withDSA和SHA512 withDSA这四种数字签名算法。以往这些扩展算法可以通过BouncyCastle来实现。
算法 | 密钥长度 | 密钥长度默认值 | 签名长度 | 备注 |
---|---|---|---|---|
SHA1withDSA | 512~1024位(密钥长度必须是64的倍数) | 1024 | - | Java7实现 |
SHA224withDSA | 同上 | 同上 | 同上 | 同上 |
SHA256withDSA | 同上 | 同上 | 同上 | 同上 |
SHA384withDSA | 同上 | 同上 | 同上 | 同上 |
SHA512withDSA | 同上 | 同上 | 同上 | 同上 |
2)、Java实现
public class DSACoder {
//数字签名:密钥算法
public static final String ALGORITHM = "DSA";
//数字签名:签名/验证算法
public static final String SIGNATURE_ALGORITHM = "SHA1withDSA";
//公钥
private static final String PUBLIC_KEY = "DSAPublicKey";
//私钥
private static final String PRIVATE_KEY = "DSAPrivateKey";
//DSA密钥长度,默认1024位,范围在512~1024之间,必须是64的倍数
private static final int KEY_SIZE = 1024;
/**
* 初始化密钥
*/
public static Map<String, Object> initKey() throws Exception {
//实例化密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
//初始化密钥对生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//公钥
DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
//私钥
DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
//封装
Map<String, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 签名
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
//转换密钥材料
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
//获取私钥对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initSign(priKey);
//更新数据
signature.update(data);
//签名
return signature.sign();
}
/**
* 校验
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
//转换密钥材料
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
//生成公钥
PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initVerify(pubKey);
//更新数据
signature.update(data);
//验证
return signature.verify(sign);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
Map<String, Object> keyMap = initKey();
//签名
byte[] sign = sign(str.getBytes(), getPrivateKey(keyMap));
System.out.println(HexUtil.encodeHexStr(sign));
//验证
boolean verify = verify(str.getBytes(), getPublicKey(keyMap), sign);
System.out.println(verify);
}
}
6.5、ECDSA算法(椭圆曲线数字签名算法)
1)、定义
ECDSA(Elliptic Curve Digital Signature Algorithm,椭圆曲线数字签名算法)于l999年作为ANSI标准,并于20O0年成为IEEE和NIST的标准。
ECDSA算法相对于传统签名算法具有速度快、强度高、签名短等优点,其用途也越来越广泛。微软操作系统的25位的产品密钥中就使用了椭圆曲线签名算法,产品密钥就是签名的十六进制串表示形式。
Java6对数字签名算法的支持很有限,除了RSA算法就只有DSA算法了。在Java7出现以前,我们只能通过Bouncy Castle?实现椭圆曲线数字签名算法ECDSA。
Java7对数字签名算法的支持由DSA和RSA算法扩大到ECDSA算法范畴。目前可支持的算法包括NONEwithECDSA、SHA1 withECDSA、SHA224 withECDSA、SHA256 withECDSA、SHA384 withECDSA和SHA512 withECDSA共6种算法。
Bouncy Castle支持多种数字签名算法,除了Java7已经支持的算法外,还支持RIPEMD160 withECDSA算法。
算法 | 密钥长度 | 密钥长度默认值 | 签名长度 | 备注 |
---|---|---|---|---|
NONEwithECDSA | 112~571 | 256 | 128 | Java7实现 |
SHA1withECDSA | 同上 | 同上 | 160 | 同上 |
SHA224withECDSA | 同上 | 同上 | 224 | 同上 |
SHA256withECDSA | 同上 | 同上 | 256 | 同上 |
SHA384withECDSA | 同上 | 同上 | 384 | 同上 |
SHA512withECDSA | 同上 | 同上 | 512 | 同上 |
RIPEMD160withECDSA | 同上 | 同上 | 160 | Bouncy Castle实现 |
2)、Java实现
public class ECDSACoder {
//数字签名:密钥算法
public static final String KEY_ALGORITHM = "EC";
/**
* 数字签名:签名/验证算法
* Java支持以下6种
* NONEwithECDSA
* HA1withECDSA
* SHA224withECDSA
* SHA256withECDSA
* SHA384withECDSA
* SHA512withECDSA
*/
public static final String SIGNATURE_ALGORITHM = "SHA512withECDSA";
//公钥
private static final String PUBLIC_KEY = "ECDSAPublicKey";
//私钥
private static final String PRIVATE_KEY = "ECDSAPrivateKey";
//密钥长度
private static final int KEY_SIZE = 256;
/**
* 初始化密钥
*/
public static Map<String, Object> initKey() throws Exception {
//实例化密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥对生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//公钥
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
//私钥
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
//封装
Map<String, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 签名
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
//转换密钥材料
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//获取私钥对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initSign(priKey);
//更新数据
signature.update(data);
//签名
return signature.sign();
}
/**
* 校验
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
//转换密钥材料
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成公钥
PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initVerify(pubKey);
//更新数据
signature.update(data);
//验证
return signature.verify(sign);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
Map<String, Object> keyMap = initKey();
//签名
byte[] sign = sign(str.getBytes(), getPrivateKey(keyMap));
System.out.println(HexUtil.encodeHexStr(sign));
//验证
boolean verify = verify(str.getBytes(), getPublicKey(keyMap), sign);
System.out.println(verify);
}
}
3)、Bouncy Castle实现
public class ECDSACoderByBC {
//数字签名:密钥算法
public static final String KEY_ALGORITHM = "EC";
/**
* 数字签名:签名/验证算法
* BouncyCastle提供RIPEMD160withECDSA实现
*/
public static final String SIGNATURE_ALGORITHM = "RIPEMD160withECDSA";
//公钥
private static final String PUBLIC_KEY = "ECDSAPublicKey";
//私钥
private static final String PRIVATE_KEY = "ECDSAPrivateKey";
//密钥长度
private static final int KEY_SIZE = 256;
/**
* 初始化密钥
*/
public static Map<String, Object> initKey() throws Exception {
//实例化密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥对生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//公钥
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
//私钥
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
//封装
Map<String, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 签名
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
Security.addProvider(new BouncyCastleProvider());
//转换密钥材料
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//获取私钥对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initSign(priKey);
//更新数据
signature.update(data);
//签名
return signature.sign();
}
/**
* 校验
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
Security.addProvider(new BouncyCastleProvider());
//转换密钥材料
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成公钥
PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);
//实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initVerify(pubKey);
//更新数据
signature.update(data);
//验证
return signature.verify(sign);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
Map<String, Object> keyMap = initKey();
//签名
byte[] sign = sign(str.getBytes(), getPrivateKey(keyMap));
System.out.println(HexUtil.encodeHexStr(sign));
//验证
boolean verify = verify(str.getBytes(), getPublicKey(keyMap), sign);
System.out.println(verify);
}
}