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)); //驗證簽名 } } |