zoukankan      html  css  js  c++  java
  • Android+Java中使用RSA加密实现接口调用时的校验功能

    场景

    RSA加密

    RSA算法是一种非对称加密算法,那么何为非对称加密算法呢?

    一般我们理解上的加密是这样子进行的:原文经过了一把钥匙(密钥)加密后变成了密文,然后将密文传递给接收方,接收方再用这把钥匙(密钥)解开密文。在这个过程中,其实加密和解密使用的是同一把钥匙,这种加密方式称为对称加密。

    而非对称加密就是和对称加密相对,加密用的钥匙和解密所用的钥匙,并不是同一把钥匙。非对称加密首先会创建两把钥匙,而这两把钥匙是成对的分别称为公钥和私钥。在进行加密时我们使用公钥进行加密,而在解密的时候就必须要使用私钥才能进行解密,这就是非对称加密算法。

    假如使用非对称加密,甲发送消息给乙,这时候乙会预先创建好两把钥匙,私钥乙自己保存好,然后把公钥发送给甲,甲使用公钥对信息进行加密,然后传给乙。最后乙使用自己的私钥对数据进行解密。这个过程中,公钥还是有可能被第三者所截获,但是不同的是,这个第三者纵然得到了公钥,也无法解开密文,因为解密密文所需要的私钥从始至终一直在乙的手里。因此这个过程是安全的。

    在一个Android应用中录音完成后将录音文件上传到SpringBoot搭建的后台接口中。

    由于Android应用中没有登录功能,所以需要对一串自定义字符串进行加密并传输,然后在SpringBoot后台进行解密验证。防止上传接口暴露。

    注:

    博客主页:
    https://blog.csdn.net/badao_liumang_qizhi
    关注公众号
    霸道的程序猿
    获取编程相关电子书、教程推送与免费下载。

    实现

    首先在SpringBoot端新建一个RsaUtils工具类

    import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
    import java.io.ByteArrayOutputStream;
    import java.security.KeyFactory;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.Signature;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    /*import java.util.Base64;*/
    
    import javax.crypto.Cipher;
    
    //java 后端
    public class RsaUtils {
        //私钥
        public static String privateKey = "自己生成的私钥";
        //公钥
        private static String publicKey = "自己生成的公钥";
        /**
         * RSA最大加密明文大小
         */
        private static final int MAX_ENCRYPT_BLOCK = 117;
    
        /**
         * RSA最大解密密文大小
         */
        private static final int MAX_DECRYPT_BLOCK = 128;
    
        /**
         * 获取密钥对
         *
         * @return 密钥对
         */
        public static KeyPair getKeyPair() throws Exception {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
            generator.initialize(1024);
            return generator.generateKeyPair();
        }
    
        /**
         * 获取私钥
         *
         * @param privateKey 私钥字符串
         * @return
         */
        public static PrivateKey getPrivateKey(String privateKey) throws Exception {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    
            byte[] decodedKey = com.sun.org.apache.xerces.internal.impl.dv.util.Base64.decode(new String(privateKey.getBytes()));
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
            return keyFactory.generatePrivate(keySpec);
        }
    
        /**
         * 获取公钥
         *
         * @param publicKey 公钥字符串
         * @return
         */
        public static PublicKey getPublicKey(String publicKey) throws Exception {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] decodedKey = Base64.decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
            return keyFactory.generatePublic(keySpec);
        }
    
        /**
         * RSA加密
         *
         * @param data      待加密数据
         * @param publicKey 公钥
         * @return
         */
        public static String encrypt(String data, PublicKey publicKey) throws Exception {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int inputLen = data.getBytes().length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offset = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offset > 0) {
                if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
                }
                out.write(cache, 0, cache.length);
                i++;
                offset = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            out.close();
            // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
            // 加密后的字符串
            return new String(Base64.encode((encryptedData)));
        }
    
        /**
         * RSA解密
         *
         * @param data       待解密数据
         * @param privateKey 私钥
         * @return
         */
        public static String decrypt(String data, PrivateKey privateKey) throws Exception {
    
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] dataBytes = Base64.decode(data);
            int inputLen = dataBytes.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offset = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offset > 0) {
                if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
                }
                out.write(cache, 0, cache.length);
                i++;
                offset = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            // 解密后的内容
            return new String(decryptedData, "UTF-8");
        }
    
        /**
         * 签名
         *
         * @param data       待签名数据
         * @param privateKey 私钥
         * @return 签名
         */
        public static String sign(String data, PrivateKey privateKey) throws Exception {
            byte[] keyBytes = privateKey.getEncoded();
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey key = keyFactory.generatePrivate(keySpec);
            Signature signature = Signature.getInstance("MD5withRSA");
            signature.initSign(key);
            signature.update(data.getBytes());
            return Base64.encode(signature.sign());
        }
    
        /**
         * 验签
         *
         * @param srcData   原始字符串
         * @param publicKey 公钥
         * @param sign      签名
         * @return 是否验签通过
         */
        public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
            byte[] keyBytes = publicKey.getEncoded();
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey key = keyFactory.generatePublic(keySpec);
            Signature signature = Signature.getInstance("MD5withRSA");
            signature.initVerify(key);
            signature.update(srcData.getBytes());
            return signature.verify(Base64.decode(sign));
        }
    
    
    /*    public static void main(String[] args) {
            try {
                // 生成密钥对
                KeyPair keyPair = getKeyPair();
                String privateKey = new String(Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()));
                String publicKey = new String(Base64.getEncoder().encode(keyPair.getPublic().getEncoded()));
                System.out.println("私钥:" + privateKey);
                System.out.println("公钥:" + publicKey);
    
    
                // RSA加密
    *//*            String data = "待加密的文字内容";
                String encryptData = encrypt(data, getPublicKey(publicKey));
                System.out.println("加密后内容:" + encryptData);
                // RSA解密
                String decryptData = decrypt("encryptData ", getPrivateKey(privateKey));
                System.out.println("解密后内容:" + decryptData);
    
                // RSA签名
                String sign = sign(data, getPrivateKey(privateKey));
                // RSA验签
                boolean result = verify(data, getPublicKey(publicKey), sign);
                System.out.print("验签结果:" + result);*//*
            } catch (Exception e) {
                e.printStackTrace();
                System.out.print("加解密异常");
            }
        }*/
    
    }

    然后运行此工具类的main方法中的生成密钥对的方法,获取到生成的公钥和密钥对。

    然后将它们赋值到最上面的privateKey和publicKey。

    然后在Android端中也新建一个工具类RsaUtils

    package com.badao.badaoimclient.common;
    
    import android.util.Base64;
    import java.io.ByteArrayOutputStream;
    import java.security.KeyFactory;
    import java.security.PublicKey;
    import java.security.spec.X509EncodedKeySpec;
    import javax.crypto.Cipher;
    
    public class RsaUtils{
    
        //公钥
        public static String publicKey="跟Java端同样的公钥";
        /**
         * RSA最大加密明文大小
         */
        private static final int MAX_ENCRYPT_BLOCK = 117;
    
        /**
         * RSA最大解密密文大小
         */
        private static final int MAX_DECRYPT_BLOCK = 128;
    
     
    
        /**
         * 获取公钥
         *
         * @param publicKey 公钥字符串
         * @return
         */
        public static PublicKey getPublicKey(String publicKey) throws Exception {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] decodedKey =Base64.decode(publicKey.getBytes(), Base64.DEFAULT);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
            return keyFactory.generatePublic(keySpec);
        }
    
        /**
         * RSA加密
         *
         * @param data 待加密数据
         * @param publicKey 公钥
         * @return
         */
        public static String encrypt(String data, PublicKey publicKey) throws Exception {
            Cipher cipher ;
            cipher= Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int inputLen = data.getBytes().length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offset = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offset > 0) {
                if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
                }
                out.write(cache, 0, cache.length);
                i++;
                offset = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            out.close();
            // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
            // 加密后的字符串
            return new String(Base64.encode(encryptedData, Base64.DEFAULT));
        }
    
    }

    这里的公钥与上面生成的公钥一致。

    注意着两个工具类的区别

    在Android工具类中的Base64引入的是

    import android.util.Base64;

    而在Java中引入的Base64是

    import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

    注意这里为什么不是引用java.util.Base64,因为会有换行导致的转移字符的问题。

    然后在Android中对字符串进行加密

    //获取加密字符串
    String escode = "";
    try {
          escode = RsaUtils.encrypt(key,RsaUtils.getPublicKey(RsaUtils.publicKey));
        } catch (Exception e) {
          e.printStackTrace();
    }

    将其作为接口调用的参数传递到Java中进行解密

           if(decode.equals(RsaUtils.decrypt(key,RsaUtils.getPrivateKey(RsaUtils.privateKey))))
            {
                try
                {
                    // 上传文件路径
                    String filePath = RuoYiConfig.getUploadPath();
                    // 上传并返回新文件名称
                    String fileName = FileUploadUtils.upload(filePath, file);
                    String url = serverConfig.getUrl() + fileName;
                    AjaxResult ajax = AjaxResult.success();
                    ajax.put("fileName", fileName);
                    ajax.put("url", url);
                    return ajax;
                }
                catch (Exception e)
                {
                    return AjaxResult.error(e.getMessage());
                }
            }else {
                return  AjaxResult.error("非法访问");
            }

    这样就限制了只能通过指定的移动端对文件上传接口进行访问。

    在移动端调用接口进行测试

    可见调用接口前加密成功

    并且能用过后台接口的解密校验

    这样别的第三方请求接口就没法请求

     

  • 相关阅读:
    __PRETTY_FUNCTION__, __FUNCTION__, __func__
    Python.with
    golang配置
    论单位转个人的社保金融社保卡的作用
    面试题
    平衡是一门艺术
    画原型是节省人力的最好办法
    推荐给非互联网主体的用户
    iOS 点击返回键崩溃的未解之谜
    服务请求比较慢SYN flooding
  • 原文地址:https://www.cnblogs.com/badaoliumangqizhi/p/14166479.html
Copyright © 2011-2022 走看看