zoukankan      html  css  js  c++  java
  • RSA加密解密与加签验签

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

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

      RSA公开密钥密码体制。所谓的公开密钥密码体制就是使用不同的加密密钥与解密密钥,是一种“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。

      在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK。

      基于这种理论,1978年出现了著名的RSA算法,它通常是先生成一对RSA 密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开,甚至可以在网络服务器中注册。为提高保密强度,RSA密钥至少为500位长,一般推荐使用1024位。这就使加密的计算量很大。

      RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作。RSA是被研究得最广泛的公钥算法,从提出到现今的三十多年里,经历了各种攻击的考验,逐渐为人们接受,截止2017年被普遍认为是最优秀的公钥方案之一。

      SET(Secure Electronic Transaction)协议中要求CA采用2048bits长的密钥,其他实体使用1024比特的密钥。RSA密钥长度随着保密级别提高,增加很快。

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

      我们接下来看下Java中如何实现RSA加密解密与加签验签。我们先来看RSA加密解密。  

    /**
     * RSA加密解密操作步骤
     */
    public class Test1 {
        public static void main(String[] args) throws Exception {
            //先给出一个待加密的字符串
            String data="青青子衿,悠悠我心。但为君故,沉吟至今。";
            //1.构建公私钥匙对
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            //2.获取钥匙对中的公钥
            PublicKey publicKey = keyPair.getPublic();
            //3.获取钥匙对中的私钥
            PrivateKey privateKey = keyPair.getPrivate();
            //4.对待加密的数据进行加密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE,publicKey);
            byte[] bytesEncrypt = cipher.doFinal(data.getBytes());//产生的是乱码,需要用Base64进行转码
            //5.Base64编码
            byte[] encodeBase64 = Base64.getEncoder().encode(bytesEncrypt);
            System.out.println("加密后的数据:"+new String(encodeBase64));
            //6.在解密时,先对用Base64编码的信息进行解码
            byte[] bytesDecode = Base64.getDecoder().decode(encodeBase64);
            //7.解密
            Cipher cipher2=Cipher.getInstance("RSA");
            cipher2.init(Cipher.DECRYPT_MODE,privateKey);
            byte[] bytesDecrypt = cipher2.doFinal(bytesDecode);
            System.out.println("解密后的数据:"+new String(bytesDecrypt));
        }
    }
    

    公钥和私钥本身存储的信息是乱码,在实际使用中,我们还可以通过Base64将这些乱码编码为可识别的ASCII码,然后将公钥和私钥信息持久化存储到文件中,在以后需要使用时,可以从文件中读取公钥和私钥信息。为此,我们可以写一个RSA的工具类,从一个储存公钥和私钥信息的文件里读取公钥和私钥信息,然后定义获取公钥和私钥的方法,以及加密和解密数据的方法。首先,我们提供一对公私钥信息,假定公钥信息储存在一个名称为rsa_public_key.pem的文件里,信息如下:

    -----BEGIN RSA PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDY90KtriCa4KjNe3mgrGGbDB95
    8A2byBKf+wOmPmOopP3gGeg7+DFAPNYCC+tL8h2bpUI3IPKOm2Hon8kM/p628i1J
    Z7JjopYVZW6JKqA2ImyneeUEK748FXwXTRAAMCTqQG/7a178BGawTdHi6hk+M6UF
    lT0EhL6JA8ULKFoiHwIDAQAB
    -----BEGIN RSA PUBLIC KEY-----
    

    假定私钥信息储存在一个名称为rsa_private_key.pem的文件里,信息如下::

    -----BEGIN RSA PRIVATE KEY-----
    MIICXQIBAAKBgQDY90KtriCa4KjNe3mgrGGbDB958A2byBKf+wOmPmOopP3gGeg7
    +DFAPNYCC+tL8h2bpUI3IPKOm2Hon8kM/p628i1JZ7JjopYVZW6JKqA2ImyneeUE
    K748FXwXTRAAMCTqQG/7a178BGawTdHi6hk+M6UFlT0EhL6JA8ULKFoiHwIDAQAB
    AoGBAIJFhF2wLZeQyQoH13Gnzzs/Pi8C+cjNipFQMFLDJyd9WYoTRCOt1DST0pOM
    AI2rJCfuRCHBwKHrnhAE0LzirPxkmvyHTIBXIoz3fHiSkIKkUVG04BcgTYpNKWPB
    ISlzdhSaw7CnmJjTthTrD5LLPtpqUl350lUYFEHVNR6Ys9JRAkEA9JUEVxzSvQkV
    V6hxhbvlxl0mATbPfiNKDBTPdr48dyYdgluAoGfAPf9rmgoCpdEd2hZBIfdy7xdL
    LvP7ztb/rQJBAOMYNC/lZLz9A9cDJ5bibrJnmyRG0SAGAzu4ffYdBoGb0kRRKzTe
    5jxfRnbiUPQU4GQXhADfikGn2ogRqbtDsnsCQQCJdp+D3n1LJanLJK75PQv9myjb
    EdU4zdi2RZP85xrQ1KlNNORsQyO3NLFjWDD4xTmD83IUByGf43WsJBDoxcnZAkA3
    i84IARX42/I6fz0JvOzSmmDqKKAyMwZLbz7wGf1jalet+iSVVAgAsFUt8wFWEl0o
    XlAdXpAUqxfavGdFtLNNAkABS576xgLcLmyw51f9hoM9RiamLn+WNzoA5TLOZjGI
    dZZnX/A8SoFYGoJoN1O0hp5DxDdl+gjW/mH51+gliEIB
    -----END RSA PRIVATE KEY-----
    

     接下来,我们写一个RSAUtil工具类,代码如下:  

    /**
     * 工具类
     */
    public class RSAUtil {
        /*
            读取秘钥数据
         */
        public static byte[] readKeyDatas(String keyFilePath){
            BufferedReader bufferedReader=null;
            try{
                bufferedReader = new BufferedReader(new FileReader(keyFilePath));
                String str=null;
                StringBuilder stringBuilder=new StringBuilder();
                while ((str=bufferedReader.readLine())!=null){
                    if(str.contains("---")){
                        continue;
                    }
                    stringBuilder.append(str);
                }
                return stringBuilder.toString().getBytes();
            }catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
        /*
            生成公钥
         */
        public static PublicKey getPublicKey(String publicKeyPath){
            //1.读取公钥文件,获取公钥数据
            byte[] bytesPublicBase64 = readKeyDatas(publicKeyPath);
            //2.对读取回来的数据进行Base64解码
            byte[] bytesPublic = Base64.getDecoder().decode(bytesPublicBase64);
            //3.把解码后的数据,重新封装成一个PublicKey对象
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytesPublic);
            KeyFactory keyFactory=null;
            try {
                keyFactory = KeyFactory.getInstance("RSA");
                PublicKey publicKey = keyFactory.generatePublic(keySpec);
                return publicKey;
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            }
            return null;
        }
        /*
            生成私钥
         */
        public static PrivateKey getPrivateKey(String privateKeyPath){
            //1.读取私钥文件,获取私钥数据
            byte[] bytesPrivateBase64 = readKeyDatas(privateKeyPath);
            //2.对读取回来的数据进行Base64解码
            byte[] bytesPrivate = Base64.getDecoder().decode(bytesPrivateBase64);
            //3.把解码后的数据,重新封装成一个PrivateKey对象
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytesPrivate);
            KeyFactory keyFactory=null;
            try {
                keyFactory = KeyFactory.getInstance("RSA");
                PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
                return privateKey;
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            }
            return null;
        }
        /*
            加密数据
         */
        public static String encodeData(PublicKey publicKey,String originData){
            try {
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(Cipher.ENCRYPT_MODE,publicKey);
                byte[] bytesEncrypt = cipher.doFinal(originData.getBytes());
                //Base64编码
                byte[] bytesEncryptBase64 = Base64.getEncoder().encode(bytesEncrypt);
                return new String(bytesEncryptBase64);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            }
            return null;
        }
        /*
            解密数据
         */
        public static String decodeData(PrivateKey privateKey,String encodeData){
            try {
                //Base64解码
                byte[] bytesEncrypt = Base64.getDecoder().decode(encodeData);
                //加密
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(Cipher.DECRYPT_MODE,privateKey);
                byte[] bytesDecrypt = cipher.doFinal(bytesEncrypt);
                return new String(bytesDecrypt);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    这样的话,以后需要使用公钥和私钥,以及加密解密时,调用上述工具类中的相应方法即可。

    最后,我们再来看用RSA如何对数据进行加签验签,具体代码如下:

    /**
     * 加签  验签    签名验证:验证数据的合法来源   即验证数据来源的合法性
     * 加签:私钥
     * 验签:公钥
     */
    public class Test2 {
        private static String privateKeyPath="储存私钥信息的文件路径";
        private static String publicKeyPath="储存公钥信息的文件路径";
        public static void main(String[] args) throws Exception {
            String data="验证该数据是否为合法的服务器发送";
            /**
             * 加签过程
             */
            PrivateKey privateKey = RSAUtil.getPrivateKey(privateKeyPath);
            Signature signature = Signature.getInstance("Sha1WithRSA");
            signature.initSign(privateKey);
            signature.update(data.getBytes("UTF-8"));
            byte[] signed = signature.sign();
            /**
             * 验签过程
             */
            PublicKey publicKey = RSAUtil.getPublicKey(publicKeyPath);
            Signature signature2 = Signature.getInstance("Sha1WithRSA");
            signature2.initVerify(publicKey);
            signature2.update(data.getBytes("UTF-8"));
            boolean verify = signature2.verify(signed);
            System.out.println("验签结果:"+verify);
        }
    }
    

  • 相关阅读:
    Linux-003-Resource temporarily unavailable
    JMeter学习-041-响应数据中文乱码解决方法
    Java学习-050-AES256 之 java.security.InvalidKeyException: Illegal key size or default parameters 解决方法
    Java学习-049-正则工具类
    JMeter学习-040-JMeter图形化 HTML 报表概要说明
    JMeter学习-039-JMeter 3.0 生成 dashboard HTML 报告图表中文乱码
    JMeter学习-038-JMeter Linux 环境配置
    Linux-002-执行命令时,提示: -bash: {命令}: command not found
    JMeter学习-037-JMeter调试工具之四-BeanShell+jmeter.log
    JMeter学习-036-JMeter调试工具之三---Debug Sampler
  • 原文地址:https://www.cnblogs.com/jdy1022/p/13897493.html
Copyright © 2011-2022 走看看