RAS非对称加密技术在分布式项目中的应用

Posted 张子行的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RAS非对称加密技术在分布式项目中的应用相关的知识,希望对你有一定的参考价值。

本文目录

前言

这段时间一方面忙着毕业论文的事情,一边忙着工作上的事情,也是好久都没有动笔了,其实在这段时间技术上也收货蛮多的了,包括编码规范上的,还有技术深度上的,SQL上的等等,遇到了很多企业开发项目上的问题,但是我最最最想分享的技术,首当其冲就是本文了,后续的内容会不定时更新,感兴趣的小伙伴可以关注一下我。本文要分享的东西是,Oauth2 分布式认证授权相关的东西,用到了Oauth2、RSA加密相关的技术,其中遇到了一些问题,最后也是解决了,特此记录一下,并分享一些心得感悟!~

RSA非对称加密技术的定义

简单点来说总结如下:

  • 公钥加密的数据只有私钥能解开
  • 私钥加密的数据只有公钥钥能解开

由于加密、解密用到的秘钥不是同一把,因而被赋予非对称的美名,那么 非对称加密的好处 在哪呢?

  • 安全:公钥任何人都可以获取,私钥保存在自己服务器上。加密、解密用到的秘钥不是同一把。
  • 我个人觉得扩展性比较好。假设我们开发了一个认证中心,下面对接了5套子服务,无论新增多少套新的子服务需要使用到认证中心,颁发给子服务一个公钥进行开发即可,解密的逻辑认证中心无需更改。总结即:一个私钥可以对接多个公钥。如果使用对称加密技术,是不是每增加一套子服务,认证中心也要增加一套解密的逻辑!

RAS非对称加密技术使用场景

  1. 任何涉及到网络传输数据的地方都可以使用(登录过程中的密码公钥加密,后端私钥解密。一对一聊天对发送人发送的内容加密,接收人私钥解密等等)
  2. 根据业务需求对你需要的数据进行加密,这里就不一一列举了

RSA秘钥初始化

没啥好说的就是利用JAVA IO流相关的方法在指定路劲中创建我们的秘钥文件。下面的代码逻辑总结:如果没有文件目录就先创建目录,接着该目录下不存在秘钥文件就接着创建文件,存在就不创建文件。采用了 KeyPairGenerator.generateKeyPair()的方式初始化秘钥

    @Value("$rsa.private.key.path")
    private String privateKeyFilePath;

    @Value("$rsa.public.key.path")
    private String publicKeyFilePath;

    @PostConstruct
    public void initRSAKey() 
        log.info("init rsa key file....");
        FileWriter privateKeyFileWriter = null;
        FileWriter publicKeyFileWriter = null;
        try 
            System.err.println("privateKeyFilePath:" + privateKeyFilePath);
            System.err.println("publicKeyFilePath:" + publicKeyFilePath);
            File privateKeyFile = new File(privateKeyFilePath);
            File publicKeyFile = new File(publicKeyFilePath);

            File privateKeyParentFile = privateKeyFile.getParentFile();
            File publicKeyParentFile = publicKeyFile.getParentFile();
            log.info("privateKeyParentFile:" + privateKeyParentFile + " mkdir " + " publicKeyParentFile:" + publicKeyParentFile + " mkdir");
            if (!privateKeyFile.exists()) privateKeyParentFile.mkdir();
            if (!publicKeyParentFile.exists()) publicKeyParentFile.mkdir();
            log.info("privateKeyFilePath:" + privateKeyFilePath + " is file:" + privateKeyFile.isFile() + ",publicKeyFilePath:" + publicKeyFilePath + " is file:" + publicKeyFile.isFile());
            if (privateKeyFile.isFile() && publicKeyFile.isFile()) return;

            boolean createPrivateKeyFile = privateKeyFile.createNewFile();
            boolean createPublicKeyFile = publicKeyFile.createNewFile();

            if (!createPrivateKeyFile || !createPublicKeyFile) 
                privateKeyFile.delete();
                publicKeyFile.delete();
                log.info("rsa key file create failed!");
                return;
            

            KeyPair keyPair = MyRSA.initKey();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            byte[] privateKeyByte = privateKey.getEncoded();
            byte[] publicKeyByte = publicKey.getEncoded();

            privateKeyFileWriter = new FileWriter(privateKeyFile);
            privateKeyFileWriter.write(Base64.encodeBase64String(privateKeyByte));
            privateKeyFileWriter.flush();

            publicKeyFileWriter = new FileWriter(publicKeyFile);
            publicKeyFileWriter.write(Base64.encodeBase64String(publicKeyByte));
            publicKeyFileWriter.flush();
            log.info("init rsa key file success!");
         catch (Exception e) 
            e.printStackTrace();
            log.error("init rsa key file error;Exception Message:" + e.getMessage());
         finally 
            if (privateKeyFileWriter != null) 
                try 
                    privateKeyFileWriter.close();
                 catch (IOException e) 
                    e.printStackTrace();
                    log.error("init rsa key file error;Exception Message:" + e.getMessage());
                
            
            if (publicKeyFileWriter != null) 
                try 
                    publicKeyFileWriter.close();
                 catch (IOException e) 
                    e.printStackTrace();
                    log.error("init rsa key file error;Exception Message:" + e.getMessage());
                
            
        
    

养成好的开发习惯,将路劲配置在YAML文件中!

rsa:
  public:
    key:
      path: D:\\\\auth_key\\\\public.key
  private:
    key:
      path: D:\\\\auth_key\\\\private.key

秘钥初始化文件、加密、解密、获取秘钥文件工具类

工具类没啥好说的,开箱即用,里面包含了如下方法:

  • 私钥加密
  • 私钥解密
  • 公钥加密
  • 公钥解密
  • 获取私钥
  • 获取公钥
  • 初始化公钥和私钥
public class MyRSA 

    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 密钥长度,DH算法的默认密钥长度是1024
     * 密钥长度必须是64的倍数,在512到65536位之间
     */
    private static final int KEY_SIZE = 512;


    public static KeyPair initKey() throws Exception 
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        return keyPairGenerator.generateKeyPair();
    

    public static PublicKey getPublicKey(String pulickPath, String algorithm) throws Exception 
        // 将文件内容转为字符串
        String publicKeyString = FileUtils.readFileToString(new File(pulickPath), Charset.defaultCharset());
        // 获取密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 构建密钥规范 进行Base64解码
        X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
        // 生成公钥
        return keyFactory.generatePublic(spec);
    

    public static PrivateKey getPrivateKey(String priPath, String algorithm) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException 
        // 将文件内容转为字符串
        String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
        // 获取密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 构建密钥规范 进行Base64解码
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
        // 生成私钥
        return keyFactory.generatePrivate(spec);
    


    /**
     * 私钥加密
     *
     * @param data 待加密数据
     * @param key  密钥
     * @return byte[] 加密数据
     */
    public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception 

        //取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    

    /**
     * 公钥加密
     *
     * @param data 待加密数据
     * @param key  密钥
     * @return byte[] 加密数据
     */
    public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception 

        //实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

        //数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        return cipher.doFinal(data);
    

    /**
     * 私钥解密
     *
     * @param data 待解密数据
     * @param key  密钥
     * @return byte[] 解密数据
     */
    public static byte[] decryptByPrivateKey(byte[] data, byte[] key) 
        try 
            //取得私钥
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //生成私钥
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
            //数据解密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(data);
         catch (Exception e) 
            throw new RSAException(e.getMessage());
        
    

    /**
     * 公钥解密
     *
     * @param data 待解密数据
     * @param key  密钥
     * @return byte[] 解密数据
     */
    public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception 

        //实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
        //数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, pubKey);
        return cipher.doFinal(data);
    

后端测试加密、解密

事先我将秘钥直接生成在 D:/auth_key/public.key 路劲下了,直接调用工具类中的方法进行一波加密、解密的操作,效果入下图

PublicKey publickey = MyRSA.getPublicKey("D:/auth_key/public.key", "RSA");
String s = Base64.encodeBase64String(MyRSA.encryptByPublicKey("123456".getBytes(), publickey.getEncoded()));
System.err.println("公钥加密后的数据:"+s);

PrivateKey privateKey = MyRSA.getPrivateKey("D:/auth_key/private.key", "RSA");
String pwd = new String(MyRSA.decryptByPrivateKey(Base64.decodeBase64(s.getBytes()), privateKey.getEncoded()));
System.err.println("私钥解密后的数据:"+pwd);

公钥获取接口开发

接口就是使用基本的JAVA IO流相关的方法,读取公钥文件中的内容,返回给前端

@Value("$rsa.public.key.path")
private String publicKeyFilePath;

@ApiOperation(value = "获取RSA公钥接口")
@GetMapping("/rsa/pubKey")
public Result pubKey() 
    try 
        BufferedReader keyFileReader = new BufferedReader(new FileReader(publicKeyFilePath));
        String publicKey = keyFileReader.readLine();
        return Result.success(publicKey);
     catch (IOException e) 
        e.printStackTrace();
        return Result.failed("解析公钥文件出现错误");
    

❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀到此本文结束❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

附页

其实初始化秘钥文件还有另外一种方法,那就是利用 .jks 文件,初始化 KeyPair 这么一个Bean,需要用到公钥私钥的时候从注入 KeyPair 这个Bean,调用其中的获取 公钥、私钥方法即可。.jks 文件可以用 JAVA JDK 中自带的 Key Tool 工具类生成。这种方式初始化的 KeyPair 用于 JWT 的非对称加密校验。下面来简单测试一下,如下图(KeyPair生成的公钥加密、私钥解密)。值得一提的是利用jks文件生成的 KeyPair ,从中提取出来的公钥加密的数据,不能利用 keyPairGenerator生成的 KeyPair,从中提取的私钥解密

    @Autowired
    private KeyPair keyPair;
    
    @Test
    public void getKey() throws Exception 
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAKey key = new RSAKey.Builder(rsaPublicKey).build();
        String rsas = Base64.encodeBase64String(MyRSA.encryptByPublicKey("rsa公钥加密".getBytes(), key.toPublicKey().getEncoded()));
        System.err.println("KeyPair公钥加密的数据: "+rsas);

        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        String b = new String(MyRSA.decryptByPrivateKey(Base64.decodeBase64(rsas.getBytes()), rsaPrivateKey.getEncoded()));
        System.err.println("KeyPair私钥钥解密的数据: "+b);


        PublicKey publickey = MyRSA.getPublicKey("D:/auth_key/public.key", "RSA");
        String s = Base64.encodeBase64String(MyRSA.encryptByPublicKey("123456".getBytes(), publickey.getEncoded()));
        System.err.println("公钥加密后的数据:"+s);

        PrivateKey privateKey = MyRSA.getPrivateKey("D:/auth_key/private.key", "RSA");
        String pwd = new String(MyRSA.decryptByPrivateKey(Base64.decodeBase64(s.getBytes()), privateKey.getEncoded()));
        System.err.println("私钥解密后的数据:"+pwd);
    


关于 KeyPair 的东西本文只是提了一下,具体的初始化 KeyPair 以及 JWT 非对称加密校验的过程,之后有时间会陆续更新文章~。

前端加密测试

在输入框中放入公钥秘钥,点击test然后弹框中内容就是加密后的数据。

源码如下

<html>
<head>
    <script type="text/javascript" src="./jsencrypt.js"></script>
    <script type="text/javascript">
        document.title = "RSA加密示例"
        function a()
            var pubkey = document.getElementById('pubkey').value;
            var encrypt = new JSEncrypt();
            encrypt.setPublicKey(pubkey);
            var encrypted = encrypt.encrypt(document.getElementById('input').value);
            var decrypt = new JSEncrypt();
            decrypt.setPrivateKey(document.getElementById('privkey').value);
            var uncrypted = decrypt.decrypt(encrypted);
            console.log('输入框内容: '+document.getElementById('input').value);
            console.log('加密后内容: '+encrypted);
            console.log('解密后内容: '+uncryptedRAS非对称加密与数字证书数字签名

ras可以加密php代码吗

人人都懂区块链——非对称加密和授权技术&分布式账本

干货|golang实现非对称加密技术实践

CEC知识科普丨区块链的非对称加密技术

k8s系列-03-认证的密码学原理之对称加密和非对称加密