常用数字签名算法RSA与DSA的Java程序内实现示例

时间:2022-01-07 03:40:48

RSA加密算法
我们来回顾一下RSA的加密算法。我们从公钥加密算法和签名算法的定义出发,用比较规范的语言来描述这一算法。
RSA公钥加密*包含如下3个算法:KeyGen(密钥生成算法),Encrypt(加密算法)以及Decrypt(解密算法)。
密钥生成算法以安全常数作为输入,输出一个公钥PK,和一个私钥SK。安全常数用于确定这个加密算法的安全性有多高,一般以加密算法使用的质数p的大小有关。越大,质数p一般越大,保证*有更高的安全性。在RSA中,密钥生成算法如下:算法首先随机产生两个不同大质数p和q,计算N=pq。随后,算法计算欧拉函数。接下来,算法随机选择一个小于的整数e,并计算e关于的模反元素d。最后,公钥为PK=(N, e),私钥为SK=(N, d)。
加密算法以公钥PK和待加密的消息M作为输入,输出密文CT。在RSA中,加密算法如下:算法直接输出密文为
解密算法以私钥SK和密文CT作为输入,输出消息M。在RSA中,解密算法如下:算法直接输出明文为。由于e和d在下互逆,因此我们有:
所以,从算法描述中我们也可以看出:公钥用于对数据进行加密,私钥用于对数据进行解密。当然了,这个也可以很直观的理解:公钥就是公开的密钥,其公开了大家才能用它来加密数据。私钥是私有的密钥,谁有这个密钥才能够解密密文。否则大家都能看到私钥,就都能解密,那不就乱套了。
下面就来看一下Java中的简单实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.stone.security;
 
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;
 
import javax.crypto.Cipher;
 
/**
 * RSA算法 公钥加密 非对称加密
 */
public class RSA {
 public static final String KEY_ALGORITHM = "RSA";
 public static final String CIPHER_ALGORITHM_ECB1 = "RSA/ECB/PKCS1Padding";
 public static final String CIPHER_ALGORITHM_ECB2 = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; //不能用
 public static final String CIPHER_ALGORITHM_ECB3 = "OAEPWithSHA-256AndMGF1Padding"; //不能用
 
 static PublicKey publicKey;
 static PrivateKey privateKey;
 static Cipher cipher;
 static KeyPair keyPair;
 
 public static void main(String[] args) throws Exception {
 method1("斯柯达U*(Sfsad7f()*^%%$");
 method2("斯柯达U*(Sfsad7f()*^%%$");
 method3("斯柯达U*(Sfsad7f()*^%%$");
  
 }
 
 /**
 * 公钥加密,私钥解密 使用默认CIPHER_ALGORITHM_ECB1
 * @param str
 * @throws Exception
 */
 static void method1(String str) throws Exception {
 KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
 KeyPair keyPair = keyGenerator.generateKeyPair();
 publicKey = keyPair.getPublic();
 privateKey = keyPair.getPrivate();
 cipher = Cipher.getInstance(KEY_ALGORITHM);
 cipher.init(Cipher.ENCRYPT_MODE, publicKey); //公钥加密
 byte[] encrypt = cipher.doFinal(str.getBytes());
 System.out.println("公钥加密后1:" + Arrays.toString(encrypt));
  
 cipher.init(Cipher.DECRYPT_MODE, privateKey);//私钥解密
 byte[] decrypt = cipher.doFinal(encrypt);
 System.out.println("私钥解密后1:" + new String(decrypt));
 }
 
 /**
 * 私钥加密,公钥解密 使用默认CIPHER_ALGORITHM_ECB1
 * @param str
 * @throws Exception
 */
 static void method2(String str) throws Exception {
 KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
 KeyPair keyPair = keyGenerator.generateKeyPair();
 publicKey = keyPair.getPublic();
 privateKey = keyPair.getPrivate();
 cipher = Cipher.getInstance(KEY_ALGORITHM);
 cipher.init(Cipher.ENCRYPT_MODE, privateKey); //私钥加密
 byte[] encrypt = cipher.doFinal(str.getBytes());
 System.out.println("私钥加密后2:" + Arrays.toString(encrypt));
  
 cipher.init(Cipher.DECRYPT_MODE, publicKey);//公钥解密
 byte[] decrypt = cipher.doFinal(encrypt);
 System.out.println("公钥解密后2:" + new String(decrypt));
 }
 
 /**
 * 私钥加密,公钥解密 使用CIPHER_ALGORITHM_ECB1 = RSA/ECB/PKCS1Padding
 * @param str
 * @throws Exception
 */
 static void method3(String str) throws Exception {
 KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
 KeyPair keyPair = keyGenerator.generateKeyPair();
 publicKey = keyPair.getPublic();
 privateKey = keyPair.getPrivate();
 cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB1);
 cipher.init(Cipher.ENCRYPT_MODE, privateKey); //私钥加密
 byte[] encrypt = cipher.doFinal(str.getBytes());
 System.out.println("私钥加密后3:" + Arrays.toString(encrypt));
  
 cipher.init(Cipher.DECRYPT_MODE, publicKey);//公钥解密
 byte[] decrypt = cipher.doFinal(encrypt);
 System.out.println("公钥解密后3:" + new String(decrypt));
 }
}

DSA算法和数字签名
DSA 一般用于数字签名和认证。
DSA是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。
DSA是基于整数有限域离散对数难题的,其安全性与RSA相比差不多。
在DSA数字签名和认证中,发送者使用自己的私钥对文件或消息进行签名,接受者收到消息后使用发送者的公钥
来验证签名的真实性。DSA只是一种算法,和RSA不同之处在于它不能用作加密和解密,也不能进行密钥交换,
只用于签名,它比RSA要快很多.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package com.stone.security;
 
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.SecureRandom;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
 
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
 
/**
 * DSA-Digital Signature Algorithm 是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。
 * 简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。
 * 如果数据和签名不匹配则认为验证失败!即 传输中的数据 可以不再加密,接收方获得数据后,拿到公钥与签名 验证数据是否有效
 */
public class DSA {
 /**
 *不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名
 */
 public static final String KEY_ALGORITHM = "RSA";
 public static final String SIGNATURE_ALGORITHM = "MD5withRSA";*/
 
 public static final String KEY_ALGORITHM = "DSA";
 public static final String SIGNATURE_ALGORITHM = "DSA";
 
 public static final String DEFAULT_SEED = "$%^*%^()(HJG8awfjas7"; //默认种子
 public static final String PUBLIC_KEY = "DSAPublicKey";
 public static final String PRIVATE_KEY = "DSAPrivateKey";
 
 public static void main(String[] args) throws Exception {
 String str = "!@#$!#^$#&ZXVDF呆军工路爱着你*()_+";
 byte[] data = str.getBytes();
  
 Map<String, Object> keyMap = initKey();// 构建密钥
 PublicKey publicKey = (PublicKey) keyMap.get(PUBLIC_KEY);
 PrivateKey privateKey = (PrivateKey) keyMap.get(PRIVATE_KEY);
 System.out.println("私钥format:" + privateKey.getFormat());
 System.out.println("公钥format:" + publicKey.getFormat());
  
  
 // 产生签名
 String sign = sign(data, getPrivateKey(keyMap));
  
 // 验证签名
 boolean verify1 = verify("aaa".getBytes(), getPublicKey(keyMap), sign);
 System.err.println("经验证 数据和签名匹配:" + verify1);
  
 boolean verify = verify(data, getPublicKey(keyMap), sign);
 System.err.println("经验证 数据和签名匹配:" + verify);
 }
 
 /**
 * 生成密钥
 *
 * @param seed 种子
 * @return 密钥对象
 * @throws Exception
 */
 public static Map<String, Object> initKey(String seed) throws Exception {
 System.out.println("生成密钥");
  
 KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
 SecureRandom secureRandom = new SecureRandom();
 secureRandom.setSeed(seed.getBytes());
 //Modulus size must range from 512 to 1024 and be a multiple of 64
 keygen.initialize(640, secureRandom);
  
 KeyPair keys = keygen.genKeyPair();
 PrivateKey privateKey = keys.getPrivate();
 PublicKey publicKey = keys.getPublic();
  
 Map<String, Object> map = new HashMap<String, Object>(2);
 map.put(PUBLIC_KEY, publicKey);
 map.put(PRIVATE_KEY, privateKey);
 return map;
 }
 
 /**
 * 生成默认密钥
 *
 * @return 密钥对象
 * @throws Exception
 */
 public static Map<String, Object> initKey() throws Exception {
 return initKey(DEFAULT_SEED);
 }
 
 /**
 * 取得私钥
 *
 * @param keyMap
 * @return
 * @throws Exception
 */
 public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
 Key key = (Key) keyMap.get(PRIVATE_KEY);
 return encryptBASE64(key.getEncoded()); //base64加密私钥
 }
 
 /**
 * 取得公钥
 *
 * @param keyMap
 * @return
 * @throws Exception
 */
 public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
 Key key = (Key) keyMap.get(PUBLIC_KEY);
 return encryptBASE64(key.getEncoded()); //base64加密公钥
 }
 
 /**
 * 用私钥对信息进行数字签名
 * @param data 加密数据
 * @param privateKey 私钥-base64加密的
 * @return
 * @throws Exception
 */
 public static String sign(byte[] data, String privateKey) throws Exception {
 System.out.println("用私钥对信息进行数字签名");
  
 byte[] keyBytes = decryptBASE64(privateKey);
 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
 KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
 PrivateKey priKey = factory.generatePrivate(keySpec);//生成 私钥
  
 //用私钥对信息进行数字签名
 Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
 signature.initSign(priKey);
 signature.update(data);
 return encryptBASE64(signature.sign());
 }
 
 /**
 * BASE64Encoder 加密
 * @param data 要加密的数据
 * @return 加密后的字符串
 */
 private static String encryptBASE64(byte[] data) {
 BASE64Encoder encoder = new BASE64Encoder();
 String encode = encoder.encode(data);
 return encode;
 }
 
 /**
 * BASE64Decoder 解密
 * @param data 要解密的字符串
 * @return 解密后的byte[]
 * @throws Exception
 */
 private static byte[] decryptBASE64(String data) throws Exception {
 BASE64Decoder decoder = new BASE64Decoder();
 byte[] buffer = decoder.decodeBuffer(data);
 return buffer;
 }
 
 /**
 * 校验数字签名
 * @param data 加密数据
 * @param publicKey
 * @param sign 数字签名
 * @return
 * @throws Exception
 */
 public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
 byte[] keyBytes = decryptBASE64(publicKey);
 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
 PublicKey pubKey = keyFactory.generatePublic(keySpec);
  
 Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
 signature.initVerify(pubKey);
 signature.update(data);
  
 return signature.verify(decryptBASE64(sign)); //验证签名
 }
 
}