RSA数据加密和解密以及数字签名的理解和Java代码示例

本文详细介绍了RSA非对称加密技术的工作原理,包括公钥加密与私钥解密的过程,以及如何利用私钥签名和公钥验证来确保信息的真实性和完整性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对费对称加密(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有一对公钥和私钥,那么加密解密的过程如下:

  1. Bob将他的公开密钥传送给Alice。
  2. Alice用Bob的公开密钥加密她的消息,然后传送给Bob。
  3. 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;
02
03 import sun.misc.BASE64Decoder;
04 import sun.misc.BASE64Encoder;
05
06 public class Coder {
07      
08     public static final String KEY_SHA="SHA";
09     public static final String KEY_MD5="MD5";
10      
11     /**
12      * BASE64解密
13      * @param key
14      * @return
15      * @throws Exception
16      */
17     public static byte[] decryptBASE64(String key) throws Exception{
18         return (new BASE64Decoder()).decodeBuffer(key);
19     }
20      
21     /**
22      * BASE64加密
23      * @param key
24      * @return
25      * @throws Exception
26      */
27     public static String encryptBASE64(byte[] key)throws Exception{
28         return (new BASE64Encoder()).encodeBuffer(key);
29     }
30      
31     /**
32      * MD5加密
33      * @param data
34      * @return
35      * @throws Exception
36      */
37     public static byte[] encryptMD5(byte[] data)throws Exception{
38         MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
39         md5.update(data);
40         return md5.digest();
41     }
42      
43     /**
44      * SHA加密
45      * @param data
46      * @return
47      * @throws Exception
48      */
49     public static byte[] encryptSHA(byte[] data)throws Exception{
50         MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
51         sha.update(data);
52         return sha.digest();
53     }
54 }

     先提供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";
3
4 public static final String PUBLIC_KEY = "RSAPublicKey";//公钥
5 public static final String PRIVATE_KEY = "RSAPrivateKey";//私钥

RSA加密解密的实现,需要有一对公私密钥,公私密钥的初始化如下:

01 /**
02      * 初始化密钥
03      * @return
04      * @throws Exception
05      */
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();
10          
11         //公钥
12         RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
13         //私钥
14         RSAPrivateKey privateKey =  (RSAPrivateKey) keyPair.getPrivate();
15          
16         Map<String,Object> keyMap = new HashMap<String, Object>(2);
17         keyMap.put(PUBLIC_KEY, publicKey);
18         keyMap.put(PRIVATE_KEY, privateKey);
19          
20         return keyMap;
21     }

      从代码中可以看出密钥的初始化长度为1024位,密钥的长度越长,安全性就越好,但是加密解密所用的时间就会越多。而一次能加密的密文长度也与密钥的长度成正比。一次能加密的密文长度为:密钥的长度/8-11。所以1024bit长度的密钥一次可以加密的密文为1024/8-11=117bit。所以非对称加密一般都用于加密对称加密算法的密钥,而不是直接加密内容。对于小文件可以使用RSA加密,但加密过程仍可能会使用分段加密。

 从map中获取公钥、私钥

01 /**
02      * 取得公钥,并转化为String类型
03      * @param keyMap
04      * @return
05      * @throws Exception
06      */
07     public static String getPublicKey(Map<String, Object> keyMap)throws Exception{
08         Key key = (Key) keyMap.get(PUBLIC_KEY); 
09         return encryptBASE64(key.getEncoded());    
10     }
11
12     /**
13      * 取得私钥,并转化为String类型
14      * @param keyMap
15      * @return
16      * @throws Exception
17      */
18     public static String getPrivateKey(Map<String, Object> keyMap) throws Exception{
19         Key key = (Key) keyMap.get(PRIVATE_KEY); 
20         return encryptBASE64(key.getEncoded());    
21     }

 对于RSA产生的公钥、私钥,我们可以有两种方式可以对信息进行加密解密。私钥加密-公钥解密 和 公钥加密-私钥解密。

    私钥加密

01 /**
02      * 用私钥加密
03      * @param data  加密数据
04      * @param key   密钥
05      * @return
06      * @throws Exception
07      */
08     public static byte[] encryptByPrivateKey(byte[] data,String key)throws Exception{
09         //解密密钥
10         byte[] keyBytes = decryptBASE64(key);
11         //取私钥
12         PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
13         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
14         Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
15          
16         //对数据加密
17         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
18         cipher.init(Cipher.ENCRYPT_MODE, privateKey);
19          
20         return cipher.doFinal(data);
21     }

    私钥解密

01 /**
02      * 用私钥解密<span style="color:#000000;"></span> * @param data  加密数据
03      * @param key   密钥
04      * @return
05      * @throws Exception
06      */
07     public static byte[] decryptByPrivateKey(byte[] data,String key)throws Exception{
08         //对私钥解密
09         byte[] keyBytes = decryptBASE64(key);
10          
11         PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
12         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
13         Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
14         //对数据解密
15         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
16         cipher.init(Cipher.DECRYPT_MODE, privateKey);
17          
18         return cipher.doFinal(data);
19     }
    公钥加密
01 /**
02      * 用公钥加密
03      * @param data  加密数据
04      * @param key   密钥
05      * @return
06      * @throws Exception
07      */
08     public static byte[] encryptByPublicKey(byte[] data,String key)throws Exception{
09         //对公钥解密
10         byte[] keyBytes = decryptBASE64(key);
11         //取公钥
12         X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
13         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
14         Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
15          
16         //对数据解密
17         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
18         cipher.init(Cipher.ENCRYPT_MODE, publicKey);
19          
20         return cipher.doFinal(data);
21     }
    公钥解密
01 /**
02      * 用公钥解密
03      * @param data  加密数据
04      * @param key   密钥
05      * @return
06      * @throws Exception
07      */
08     public static byte[] decryptByPublicKey(byte[] data,String key)throws Exception{
09         //对公钥解密
10         byte[] keyBytes = decryptBASE64(key);
11         X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
12         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
13         Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
14          
15         //对数据解密
16         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
17         cipher.init(Cipher.DECRYPT_MODE, publicKey);
18          
19         return cipher.doFinal(data);
20     }

关于数字签名,先了解下何为数字签名。数字签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。数字签名是非对称密钥加密技术与数字摘要技术的应用。简单地说,所谓数字签名就是附加在数据单元上的一些数据,或是对数据单元所作的密码变换。这种数据或变换允许数据单元的接收者用以确认数据单元的来源和数据单元的完整性并保护数据,防止被人(例如接收者)进行伪造。

    数字签名的主要功能如下:

    保证信息传输的完整性、发送者的身份认证、防止交易中的抵赖发生。

    数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。

    数字签名是个加密的过程,数字签名验证是个解密的过程。

     数字签名算法依靠公钥加密技术来实现的。在公钥加密技术里,每一个使用者有一对密钥:一把公钥和一把私钥。公钥可以自由发布,但私钥则秘密保存;还有一个要求就是要让通过公钥推算出私钥的做法不可能实现。
    普通的数字签名算法包括三种算法:
    1.密码生成算法;
    2.标记算法;

   3.验证算法

    通过RSA加密解密算法,我们可以实现数字签名的功能。我们可以用私钥对信息生成数字签名,再用公钥来校验数字签名,当然也可以反过来公钥签名,私钥校验。

    私钥签名

01 /**
02      *  用私钥对信息生成数字签名
03      * @param data  //加密数据
04      * @param privateKey    //私钥
05      * @return
06      * @throws Exception
07      */
08     public static String sign(byte[] data,String privateKey)throws Exception{
09         //解密私钥
10         byte[] keyBytes = decryptBASE64(privateKey);
11         //构造PKCS8EncodedKeySpec对象
12         PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
13         //指定加密算法
14         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
15         //取私钥匙对象
16         PrivateKey privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
17         //用私钥对信息生成数字签名
18         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
19         signature.initSign(privateKey2);
20         signature.update(data);
21          
22         return encryptBASE64(signature.sign());
23     }
    公钥校验 


01 /**
02      * 校验数字签名
03      * @param data  加密数据
04      * @param publicKey 公钥
05      * @param sign  数字签名
06      * @return
07      * @throws Exception
08      */
09     public static boolean verify(byte[] data,String publicKey,String sign)throws Exception{
10         //解密公钥
11         byte[] keyBytes = decryptBASE64(publicKey);
12         //构造X509EncodedKeySpec对象
13         X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
14         //指定加密算法
15         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
16         //取公钥匙对象
17         PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);
18          
19         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
20         signature.initVerify(publicKey2);
21         signature.update(data);
22         //验证签名是否正常
23         return signature.verify(decryptBASE64(sign));
24          
25     }
更多详细的代码请参考

http://blog.youkuaiyun.com/wangqiuyun/article/details/42143957



1、数字签名原理 用RSA算法数字签名,总的来说,就是签名者用私钥参数d加密,也就是签名;验证者用签字者的公钥参数e解密来完成认证。 下面简要描述数字签名认证的过程。 (1)、生成密钥 为用户随机生成一对密钥:公钥(e,n)私钥(d,n). (2)、签名过程 a) 计算消息的散列值H(M). b) 用私钥(d,n)加密散列值:s=(H(M)) mod n,签名结果就是s. c) 发送消息签名(M,s). (3)、认证过程 a) 取得发送方的公钥(e,n). b) 解密签名s:h=s mod n. c) 计算消息的散列值H(M). d) 比较,如果h=H(M),表示签名有效;否则,签名无效。 根据上面的过程,我们可以得到RSA数字签名的框图如图2-1: 图 2-1 RSA数字签名框图 2、 假设Alice想Bob通信,以本地两个文件夹AliceBob模拟两个用户,实现消息M签名的模拟分发 (1)、Alice通过RSA算法生成一对密钥:公钥(e,n)私钥(d,n),将公私钥分别存入pubKey.txtpriKey.txt中。 pubKey.txt中公钥如下: priKey.txt中私钥如下: (2)、将Alice中的pubKey.txt拷到Bob中,模拟公玥的分发。 (3)、将Alice中的消息info.txt做散列,将散列后的值存入hashInfo.txt中。 (4)、将Alice中的消息hashInfo.txt签名sign.txt拷到Bob中,实现M密文状态下的签名与模拟分发、消息传递。 (5)Bob取得公钥pubKey.txt,用公钥解密签名,计算消息的散列值H(M).比较,如果h=H(M),表示签名有效;否则,签名无效。 后台运行结果如下:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值