RSA加密解密及数字签名Java实现及部分原理

简介:

转载至:

https://my.oschina.net/jiangli0502/blog/171263

参考:

http://blog.csdn.net/he_qiao/article/details/44758711


如果大家对RSA算法原理很感兴趣,推荐下面这两篇文章:

http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html

http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html


RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

    RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密算法

    RSA算法是一种非对称密码算法,所谓非对称,就是指该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密。

    关于RSA算法的原理,这里就不再详加介绍,网上各种资源一大堆。下面就开始介绍RSA加密解密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
import  java.security.MessageDigest; import  sun.misc.BASE64Decoder; import  sun.misc.BASE64Encoder; public  class  Coder {  
     public  static  final  String KEY_SHA= "SHA" ;    public  static  final  String KEY_MD5= "MD5" ;  
     /**
      * BASE64解密
      * @param key
      * @return
      * @throws Exception
      */
     public  static  byte [] decryptBASE64(String key)  throws  Exception{       return  ( new  BASE64Decoder()).decodeBuffer(key);
     }  
     /**
      * BASE64加密
      * @param key
      * @return
      * @throws Exception
      */
     public  static  String encryptBASE64( byte [] key) throws  Exception{         return  ( new  BASE64Encoder()).encodeBuffer(key);
     }  
     /**
      * MD5加密
      * @param data
      * @return
      * @throws Exception
      */
     public  static  byte [] encryptMD5( byte [] data) throws  Exception{
         MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
         md5.update(data);        return  md5.digest();
     }  
     /**
      * SHA加密
      * @param data
      * @return
      * @throws Exception
      */
     public  static  byte [] encryptSHA( byte [] data) throws  Exception{
         MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
         sha.update(data);        return  sha.digest();
     }
}


     先提供Coder编码类,该类封装了基本的Base64、md5和SHA加密解密算法。Java对这些算法的实现提供了很好的API封装,开发人员只需调用这些API就可很简单方便的实现数据的加密与解密。

    下面提供RSA加密解密类,该类为Coder类子类,因为其中对RSA公私密钥的保存进行了一层Base64加密处理。

    RSA加密解密类静态常量

?
1
2
3
4
        public  static  final  String KEY_ALGORTHM= "RSA" ; //
     public  static  final  String SIGNATURE_ALGORITHM= "MD5withRSA" ;   
     public  static  final  String PUBLIC_KEY =  "RSAPublicKey" ; //公钥
     public  static  final  String PRIVATE_KEY =  "RSAPrivateKey" ; //私钥



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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
      * 初始化密钥
      * @return
      * @throws Exception
      */
     public  static  Map<String,Object> initKey() throws  Exception{
         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORTHM);
         keyPairGenerator.initialize( 1024 );
         KeyPair keyPair = keyPairGenerator.generateKeyPair();       
         //公钥
         RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();         //私钥
         RSAPrivateKey privateKey =  (RSAPrivateKey) keyPair.getPrivate();     
         Map<String,Object> keyMap =  new  HashMap<String, Object>( 2 );
         keyMap.put(PUBLIC_KEY, publicKey);
         keyMap.put(PRIVATE_KEY, privateKey);      
         return  keyMap;
     }


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

    从map中获取公钥、私钥


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
      * 取得公钥,并转化为String类型
      * @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());     
     }    /**
      * 取得私钥,并转化为String类型
      * @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());     
     }


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

    私钥加密


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
      * 用私钥加密
      * @param data   加密数据
      * @param key    密钥
      * @return
      * @throws Exception
      */
     public  static  byte [] encryptByPrivateKey( byte [] data,String key) throws  Exception{
         //解密密钥      byte[] keyBytes = decryptBASE64(key);
         //取私钥       PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
         
         //对数据加密     Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());       cipher.init(Cipher.ENCRYPT_MODE, privateKey);     
         return  cipher.doFinal(data);
     }


    私钥解密

?
1
2
3
4
5
6
7
8
9
10
11
12
/**
      * 用私钥解密 * @param data    加密数据
      * @param key    密钥
      * @return
      * @throws Exception
      */
     public  static  byte [] decryptByPrivateKey( byte [] data,String key) throws  Exception{
         //对私钥解密     byte[] keyBytes = decryptBASE64(key);       
         PKCS8EncodedKeySpec pkcs8EncodedKeySpec =  new  PKCS8EncodedKeySpec(keyBytes);        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
         //对数据解密     Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());       cipher.init(Cipher.DECRYPT_MODE, privateKey);     
         return  cipher.doFinal(data);
     }

    公钥加密

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
      * 用公钥加密
      * @param data   加密数据
      * @param key    密钥
      * @return
      * @throws Exception
      */
     public  static  byte [] encryptByPublicKey( byte [] data,String key) throws  Exception{
         //对公钥解密     byte[] keyBytes = decryptBASE64(key);
         //取公钥       X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);       KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
         
         //对数据解密     Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());       cipher.init(Cipher.ENCRYPT_MODE, publicKey);      
         return  cipher.doFinal(data);
     }

    私钥加密

?
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
      * 用公钥解密
      * @param data   加密数据
      * @param key    密钥
      * @return
      * @throws Exception
      */
     public  static  byte [] decryptByPublicKey( byte [] data,String key) throws  Exception{
         //对私钥解密     byte[] keyBytes = decryptBASE64(key);        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);       KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
         
         //对数据解密     Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());       cipher.init(Cipher.DECRYPT_MODE, publicKey);      
         return  cipher.doFinal(data);
     }


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

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


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

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

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

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

    普通的数字签名算法包括三种算法:

    1.密码生成算法;

    2.标记算法;

   3.验证算法

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

    私钥签名
 


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
      * 用私钥对信息生成数字签名
      * @param data   //加密数据
      * @param privateKey //私钥
      * @return
      * @throws Exception
      */
     public  static  String sign( byte [] data,String privateKey) throws  Exception{      //解密私钥
         byte [] keyBytes = decryptBASE64(privateKey);      //构造PKCS8EncodedKeySpec对象
         PKCS8EncodedKeySpec pkcs8EncodedKeySpec =  new  PKCS8EncodedKeySpec(keyBytes);         //指定加密算法
         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);         //取私钥匙对象
         PrivateKey privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);         //用私钥对信息生成数字签名
         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
         signature.initSign(privateKey2);
         signature.update(data);    
         return  encryptBASE64(signature.sign());
     }

    公钥校验 



?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
      * 校验数字签名
      * @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对象
         X509EncodedKeySpec x509EncodedKeySpec =  new  X509EncodedKeySpec(keyBytes);        //指定加密算法
         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);         //取公钥匙对象
         PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);
         
         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
         signature.initVerify(publicKey2);
         signature.update(data);      //验证签名是否正常
         return  signature.verify(decryptBASE64(sign));
         
     }

       对于RSA如何加密文件、图片等信息,加密的信息又如何保存,怎样保存解密后的信息,以及操作过程中遇到的错误将如何处理,将在后面的文章中介绍给大家。 






      本文转自建波李 51CTO博客,原文链接:http://blog.51cto.com/jianboli/1886907,如需转载请自行联系原作者



相关文章
|
19天前
|
Java 调度
Java并发编程:深入理解线程池的原理与实践
【4月更文挑战第6天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将从线程池的基本原理入手,逐步解析其工作过程,以及如何在实际开发中合理使用线程池以提高程序性能。同时,我们还将关注线程池的一些高级特性,如自定义线程工厂、拒绝策略等,以帮助读者更好地掌握线程池的使用技巧。
|
1月前
|
存储 移动开发 安全
Flutter加固原理及加密处理
Flutter加固原理及加密处理
39 0
|
1月前
|
开发框架 Java API
java反射机制的原理与简单使用
java反射机制的原理与简单使用
17 1
|
27天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
75 0
|
29天前
|
Java
软件工程设计原理里氏替换原则 ,具体实现及JAVA代码举例
里氏替换原则(Liskov Substitution Principle, LSP)是面向对象设计的基本原则之一,由Barbara Liskov提出。这个原则指出,如果类 S 是类 T 的子类型,则程序中使用 T 的对象的地方都可以不经修改地使用 S 的对象。换句话说,子类的对象应该能够替换掉它们的父类对象,而不影响程序的正确性。这个原则强调了继承关系中的行为兼容性,保证了基类和派生类之间的正确抽象和继承关系。
24 3
|
13天前
|
安全 网络协议 网络安全
网络原理(5)--HTTPS是如何进行加密的
网络原理(5)--HTTPS是如何进行加密的
11 0
|
14天前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
16 0
|
17天前
|
Java 数据安全/隐私保护
java base64 加密 解密
java base64 加密 解密
|
23天前
|
Java 开发者
软件工程设计原理接口隔离原则 ,具体实现及JAVA代码举例
【4月更文挑战第7天】接口隔离原则(Interface Segregation Principle, ISP)是面向对象设计原则之一,旨在减少不必要的依赖关系,通过拆分庞大且臃肿的接口为更小、更具体的接口来实现。这个原则强调“客户端不应该被迫依赖于它不使用的接口”,意味着一个类不应该被迫实现它不使用的方法。
16 1
|
23天前
|
Java
软件工程设计原理依赖倒置原则 ,具体实现及JAVA代码举例
【4月更文挑战第5天】在软件工程中,依赖倒置原则(Dependency Inversion Principle, DIP)是一项重要的设计原则,它是SOLID原则中的一个组成部分。这个原则主张高层模块不应该依赖于低层模块,而是应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。这种设计方法有助于降低代码间的耦合度,增强系统的灵活性和可维护性
20 0