对费对称加密(RSA)在使用中的理解
一、公钥加密
假设一下,我找了两个数字,一个是1,一个是2。我喜欢2这个数字,就保留起来,不告诉你们(私钥),然后我告诉大家,1是我的公钥。
我有一个文件,不能让别人看,我就用1加密了。别人找到了这个文件,但是他不知道2就是解密的私钥啊,所以他解不开,只有我可以用
数字2,就是我的私钥,来解密。这样我就可以保护数据了。
我的好朋友x用我的公钥1加密了字符a,加密后成了b,放在网上。别人偷到了这个文件,但是别人解不开,因为别人不知道2就是我的私钥,
只有我才能解密,解密后就得到a。这样,我们就可以传送加密的数据了。
二、私钥签名
如果我用私钥加密一段数据(当然只有我可以用私钥加密,因为只有我知道2是我的私钥),结果所有的人都看到我的内容了,因为他们都知
道我的公钥是1,那么这种加密有什么用处呢?
但是我的好朋友x说有人冒充我给他发信。怎么办呢?我把我要发的信,内容是c,用我的私钥2,加密,加密后的内容是d,发给x,再告诉他
解密看是不是c。他用我的公钥1解密,发现果然是c。
这个时候,他会想到,能够用我的公钥解密的数据,必然是用我的私钥加的密。只有我知道我得私钥,因此他就可以确认确实是我发的东西。
这样我们就能确认发送方身份了。这个过程叫做数字签名。当然具体的过程要稍微复杂一些。用私钥来加密数据,用途就是数字签名。
总结:公钥和私钥是成对的,它们互相解密。
公钥加密,私钥解密。
私钥数字签名,公钥验证。
举例
比如有两个用户Alice和Bob,Alice想把一段明文通过双钥加密的技术发送给Bob,Bob有一对公钥和私钥,那么加密解密的过程如下:
- Bob将他的公开密钥传送给Alice。
- Alice用Bob的公开密钥加密她的消息,然后传送给Bob。
- Bob用他的私人密钥解密Alice的消息。
上面的过程可以用下图表示,Alice使用Bob的公钥进行加密,Bob用自己的私钥进行解密。

例子和图出自《网络安全基础 应用与标准第二版》
RSA算法
RSA 公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够 抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对
其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

Java代码示例
下面就开始介绍RSA加密解密JAVA类的具体实现。
01 |
import java.security.MessageDigest; |
03 |
import sun.misc.BASE64Decoder; |
04 |
import sun.misc.BASE64Encoder; |
08 |
public static final String
KEY_SHA= "SHA" ; |
09 |
public static final String
KEY_MD5= "MD5" ; |
17 |
public static byte []
decryptBASE64(String key) throws Exception{ |
18 |
return ( new BASE64Decoder()).decodeBuffer(key); |
27 |
public static String
encryptBASE64( byte []
key) throws Exception{ |
28 |
return ( new BASE64Encoder()).encodeBuffer(key); |
37 |
public static byte []
encryptMD5( byte []
data) throws Exception{ |
38 |
MessageDigest
md5 = MessageDigest.getInstance(KEY_MD5); |
49 |
public static byte []
encryptSHA( byte []
data) throws Exception{ |
50 |
MessageDigest
sha = MessageDigest.getInstance(KEY_SHA); |
先提供Coder编码类,该类封装了基本的Base64、md5和SHA加密解密算法。Java对这些算法的实现提供了很好的API封装,开发人员只需调用这些API就可很简单方便的实现数据的加密与解密。
下面提供RSA加密解密类,该类为Coder类子类,因为其中对RSA公私密钥的保存进行了一层Base64加密处理。
RSA加密解密类静态常量
1 |
public static final String
KEY_ALGORTHM= "RSA" ; |
2 |
public static final String
SIGNATURE_ALGORITHM= "MD5withRSA" ; |
4 |
public static final String
PUBLIC_KEY = "RSAPublicKey" ; |
5 |
public static final String
PRIVATE_KEY = "RSAPrivateKey" ; |
RSA加密解密的实现,需要有一对公私密钥,公私密钥的初始化如下:
06 |
public static Map<String,Object>
initKey() throws Exception{ |
07 |
KeyPairGenerator
keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORTHM); |
08 |
keyPairGenerator.initialize( 1024 ); |
09 |
KeyPair
keyPair = keyPairGenerator.generateKeyPair(); |
12 |
RSAPublicKey
publicKey = (RSAPublicKey) keyPair.getPublic(); |
14 |
RSAPrivateKey
privateKey = (RSAPrivateKey) keyPair.getPrivate(); |
16 |
Map<String,Object>
keyMap = new HashMap<String,
Object>( 2 ); |
17 |
keyMap.put(PUBLIC_KEY,
publicKey); |
18 |
keyMap.put(PRIVATE_KEY,
privateKey); |
从代码中可以看出密钥的初始化长度为1024位,密钥的长度越长,安全性就越好,但是加密解密所用的时间就会越多。而一次能加密的密文长度也与密钥的长度成正比。一次能加密的密文长度为:密钥的长度/8-11。所以1024bit长度的密钥一次可以加密的密文为1024/8-11=117bit。所以非对称加密一般都用于加密对称加密算法的密钥,而不是直接加密内容。对于小文件可以使用RSA加密,但加密过程仍可能会使用分段加密。
从map中获取公钥、私钥
07 |
public static String
getPublicKey(Map<String, Object> keyMap) throws Exception{ |
08 |
Key
key = (Key) keyMap.get(PUBLIC_KEY); |
09 |
return encryptBASE64(key.getEncoded()); |
18 |
public static String
getPrivateKey(Map<String, Object> keyMap) throws Exception{ |
19 |
Key
key = (Key) keyMap.get(PRIVATE_KEY); |
20 |
return encryptBASE64(key.getEncoded()); |
对于RSA产生的公钥、私钥,我们可以有两种方式可以对信息进行加密解密。私钥加密-公钥解密 和 公钥加密-私钥解密。
私钥加密
08 |
public static byte []
encryptByPrivateKey( byte []
data,String key) throws Exception{ |
10 |
byte []
keyBytes = decryptBASE64(key); |
12 |
PKCS8EncodedKeySpec
pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes); |
13 |
KeyFactory
keyFactory = KeyFactory.getInstance(KEY_ALGORTHM); |
14 |
Key
privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); |
17 |
Cipher
cipher = Cipher.getInstance(keyFactory.getAlgorithm()); |
18 |
cipher.init(Cipher.ENCRYPT_MODE,
privateKey); |
20 |
return cipher.doFinal(data); |
私钥解密
02 |
*
用私钥解密<span style="color:#000000;"></span> * @param data 加密数据 |
07 |
public static byte []
decryptByPrivateKey( byte []
data,String key) throws Exception{ |
09 |
byte []
keyBytes = decryptBASE64(key); |
11 |
PKCS8EncodedKeySpec
pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes); |
12 |
KeyFactory
keyFactory = KeyFactory.getInstance(KEY_ALGORTHM); |
13 |
Key
privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); |
15 |
Cipher
cipher = Cipher.getInstance(keyFactory.getAlgorithm()); |
16 |
cipher.init(Cipher.DECRYPT_MODE,
privateKey); |
18 |
return cipher.doFinal(data); |
公钥加密
08 |
public static byte []
encryptByPublicKey( byte []
data,String key) throws Exception{ |
10 |
byte []
keyBytes = decryptBASE64(key); |
12 |
X509EncodedKeySpec
x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes); |
13 |
KeyFactory
keyFactory = KeyFactory.getInstance(KEY_ALGORTHM); |
14 |
Key
publicKey = keyFactory.generatePublic(x509EncodedKeySpec); |
17 |
Cipher
cipher = Cipher.getInstance(keyFactory.getAlgorithm()); |
18 |
cipher.init(Cipher.ENCRYPT_MODE,
publicKey); |
20 |
return cipher.doFinal(data); |
公钥解密
08 |
public static byte []
decryptByPublicKey( byte []
data,String key) throws Exception{ |
10 |
byte []
keyBytes = decryptBASE64(key); |
11 |
X509EncodedKeySpec
x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes); |
12 |
KeyFactory
keyFactory = KeyFactory.getInstance(KEY_ALGORTHM); |
13 |
Key
publicKey = keyFactory.generatePublic(x509EncodedKeySpec); |
16 |
Cipher
cipher = Cipher.getInstance(keyFactory.getAlgorithm()); |
17 |
cipher.init(Cipher.DECRYPT_MODE,
publicKey); |
19 |
return cipher.doFinal(data); |
关于数字签名,先了解下何为数字签名。数字签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。数字签名是非对称密钥加密技术与数字摘要技术的应用。简单地说,所谓数字签名就是附加在数据单元上的一些数据,或是对数据单元所作的密码变换。这种数据或变换允许数据单元的接收者用以确认数据单元的来源和数据单元的完整性并保护数据,防止被人(例如接收者)进行伪造。
数字签名的主要功能如下:
保证信息传输的完整性、发送者的身份认证、防止交易中的抵赖发生。
数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。
数字签名是个加密的过程,数字签名验证是个解密的过程。
数字签名算法依靠公钥加密技术来实现的。在公钥加密技术里,每一个使用者有一对密钥:一把公钥和一把私钥。公钥可以自由发布,但私钥则秘密保存;还有一个要求就是要让通过公钥推算出私钥的做法不可能实现。
普通的数字签名算法包括三种算法:
1.密码生成算法;
2.标记算法;
3.验证算法。
通过RSA加密解密算法,我们可以实现数字签名的功能。我们可以用私钥对信息生成数字签名,再用公钥来校验数字签名,当然也可以反过来公钥签名,私钥校验。
私钥签名
04 |
*
@param privateKey //私钥 |
08 |
public static String
sign( byte []
data,String privateKey) throws Exception{ |
10 |
byte []
keyBytes = decryptBASE64(privateKey); |
12 |
PKCS8EncodedKeySpec
pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes); |
14 |
KeyFactory
keyFactory = KeyFactory.getInstance(KEY_ALGORTHM); |
16 |
PrivateKey
privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec); |
18 |
Signature
signature = Signature.getInstance(SIGNATURE_ALGORITHM); |
19 |
signature.initSign(privateKey2); |
20 |
signature.update(data); |
22 |
return encryptBASE64(signature.sign()); |
公钥校验
09 |
public static boolean verify( byte []
data,String publicKey,String sign) throws Exception{ |
11 |
byte []
keyBytes = decryptBASE64(publicKey); |
13 |
X509EncodedKeySpec
x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes); |
15 |
KeyFactory
keyFactory = KeyFactory.getInstance(KEY_ALGORTHM); |
17 |
PublicKey
publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec); |
19 |
Signature
signature = Signature.getInstance(SIGNATURE_ALGORITHM); |
20 |
signature.initVerify(publicKey2); |
21 |
signature.update(data); |
23 |
return signature.verify(decryptBASE64(sign)); |
更多详细的代码请参考
http://blog.youkuaiyun.com/wangqiuyun/article/details/42143957