非對(duì)稱加密算法 RSA+對(duì)稱AES

非對(duì)稱加密算法系列文章,推薦閱讀順序:

  1. 非對(duì)稱加密算法 (RSA、DSA)概述
  2. 非對(duì)稱加密算法 RSA+對(duì)稱AES
  3. 非對(duì)稱加密算法 (DSA)

一、RSA 部分

1.1 簡(jiǎn)介

RSA是3個(gè)發(fā)明者的名字縮寫, 是目前最有影響力的公鑰加密算法,該算法基于一個(gè)十分簡(jiǎn)單的數(shù)論事實(shí):將兩個(gè)大素?cái)?shù)相乘十分容易,但想要對(duì)其乘積進(jìn)行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰,即公鑰,而兩個(gè)大素?cái)?shù)組合成私鑰。公鑰是可發(fā)布的供任何人使用,私鑰則為自己所有,供解密之用。

1.2 工作流程

A 要把信息發(fā)給 B 為例,確定角色:A 為加密者,B 為解密者。首先由 B 隨機(jī)確定一個(gè) KEY,稱之為私鑰,將這個(gè) KEY 始終保存在機(jī)器 B 中而不發(fā)出來;然后,由這個(gè) KEY 計(jì)算出另一個(gè) KEY,稱之為公鑰。這個(gè)公鑰的特性是幾乎不可能通過它自身計(jì)算出生成它的私鑰。接下來通過網(wǎng)絡(luò)把這個(gè)公鑰傳給 A,A 收到公鑰后,利用公鑰對(duì)信息加密,并把密文通過網(wǎng)絡(luò)發(fā)送到 B,最后 B 利用已知的私鑰,就能對(duì)密文進(jìn)行解碼了。以上就是 RSA 算法的工作流程。

1.3 攻擊

2009年12月12日,編號(hào)為 RSA-768 (768 bits, 232 digits) 數(shù)也被成功分解。這一事件威脅了現(xiàn)通行的1024-bit 密鑰的安全性,普遍認(rèn)為用戶應(yīng)盡快升級(jí)到2048-bit或以上。

1.4 例子1

包括生成公私鑰對(duì),轉(zhuǎn)換成字符串可以保存在數(shù)據(jù)庫、配置文件或者配置中心。
加解密字符串是發(fā)送端使用公鑰加密數(shù)據(jù),在接收端使用私鑰解密數(shù)據(jù)。數(shù)字簽名的公私鑰使用順序正好相反。

package com.erbadagang.springboot.rsa;


import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @description 大一統(tǒng)的例子,包括生成公私鑰對(duì),加解密字符串。
 * @ClassName: RsaDemo
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/7/25 10:33
 * @Copyright:
 */
public class RsaDemo {
    private static final String ALGO = "RSA";
    private static final String CHARSET = "UTF-8";

    /*
     * 用于存儲(chǔ)隨機(jī)產(chǎn)生的公鑰與私鑰
     */
    private static Map<Integer, String> KEY_CACHE = new HashMap<>();

    /**
     * 隨機(jī)生成密鑰對(duì)
     *
     * @throws NoSuchAlgorithmException
     */
    private static void generateKeyPair() throws NoSuchAlgorithmException {
        // KeyPairGenerator 類用于生成公鑰和私鑰對(duì),基于RSA算法生成對(duì)象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGO);
        // 初始化密鑰對(duì)生成器,密鑰大小為 96-1024 位
        keyPairGen.initialize(2048, new SecureRandom());
        // 生成一個(gè)密鑰對(duì),保存在 keyPair 中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 得到私鑰
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 得到公鑰
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
        // 得到私鑰字符串
        String privateKeyString = new String(Base64.getEncoder().encode((privateKey.getEncoded())));
        // 將公鑰和私鑰保存到 Map
        KEY_CACHE.put(0, publicKeyString);
        KEY_CACHE.put(1, privateKeyString);
    }

    /**
     * RSA公鑰加密
     *
     * @param data      加密字符串
     * @param publicKey 公鑰
     * @return 密文
     * @throws Exception 加密過程中的異常信息
     */
    private static String encrypt(String data, String publicKey) throws Exception {
        // base64 編碼的公鑰
        byte[] decoded = Base64.getDecoder().decode(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(ALGO).generatePublic(new X509EncodedKeySpec(decoded));
        // RSA加密
        Cipher cipher = Cipher.getInstance(ALGO);
        // 公鑰加密
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes(CHARSET)));
    }

    /**
     * RSA私鑰解密
     *
     * @param data       加密字符串
     * @param privateKey 私鑰
     * @return 銘文
     * @throws Exception 解密過程中的異常信息
     */
    private static String decrypt(String data, String privateKey) throws Exception {
        byte[] inputByte = Base64.getDecoder().decode(data.getBytes(CHARSET));
        // base64 編碼的私鑰
        byte[] decoded = Base64.getDecoder().decode(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(ALGO).generatePrivate(new PKCS8EncodedKeySpec(decoded));
        // RSA 解密
        Cipher cipher = Cipher.getInstance(ALGO);
        // 私鑰解密
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        return new String(cipher.doFinal(inputByte));
    }

    public static void main(String[] args) {
        String originData = "郭秀志 Test Asymmetric encrypt!";
        System.out.println("originData = " + originData);
        try {
            generateKeyPair();

            String publicKey = KEY_CACHE.get(0);
            System.out.println("publicKey = " + publicKey);

            String encryData = encrypt(originData, publicKey);
            System.out.println("encryData = " + encryData);

            String privateKey = KEY_CACHE.get(1);
            System.out.println("privateKey = " + privateKey);
            String decryData = decrypt(encryData, privateKey);
            System.out.println("decryData = " + decryData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

運(yùn)行結(jié)果:

originData = 郭秀志 Test Asymmetric encrypt!
publicKey = MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi8DngVAbAS5vBUgJjAaTflHVKSVfRGqDV81pmxT+XAJzZ2M26J+F5U1Vx+bb0MReKTEOs2q6VLwQGEsJN2pSG7zrRwwKiJtkxQoEaeJ0f4mmFlhRnD0q2xbSjkoZaSAbWy7KWuyEaDzjspl8xBodB1kvLORBqVJ07YRy5a3/2KeEoHE8B6brNMBrLUZ5UNs4hn97iMLbuUsYfp07kRaKCCjWemqPOCijKjWfIqpbpzoEw92hr/RDrkOS4N6DMXQ+DOpW7b2JoZC/pqZ/GY0tKgLpFdEN+lUXG9SoB1sRO2CVKUdDZ/iT6FlnQG2Fej8YaYNvKBGGlBPSqW0pdJ9j0wIDAQAB
encryData = h+q0jd4Sf64eT0mdfW8k2RPYNkt5A7qVjqvmv1QN33GEmEeE51TJhnaSDdZyletaS8HVcu+PJw/V/69bJ2SymkXG7cIFcw6OA1iLn6KGvE6lOaKfST8ARZzVteaQr9CRdGijNqum1r/rLrvtUHb+mpyirC9YqIZgH1AcFIw+zZtYphw/sNDRCbIG9F4QsotLJpZmE1ukJw+lOFFEbXVmpaiYmUM3Uw3RCHMVlws/oqZgRElqhB7PUDffuk03dczfX1LL1GarSC6HGHIpsL+WzhM0TeOhxrUyl5rqblAPQ4TtbGXN5tTlvXMFIl/KoL9rv/izY3UsjMJ2RM3ZHSBB7w==
privateKey = MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLwOeBUBsBLm8FSAmMBpN+UdUpJV9EaoNXzWmbFP5cAnNnYzbon4XlTVXH5tvQxF4pMQ6zarpUvBAYSwk3alIbvOtHDAqIm2TFCgRp4nR/iaYWWFGcPSrbFtKOShlpIBtbLspa7IRoPOOymXzEGh0HWS8s5EGpUnTthHLlrf/Yp4SgcTwHpus0wGstRnlQ2ziGf3uIwtu5Sxh+nTuRFooIKNZ6ao84KKMqNZ8iqlunOgTD3aGv9EOuQ5Lg3oMxdD4M6lbtvYmhkL+mpn8ZjS0qAukV0Q36VRcb1KgHWxE7YJUpR0Nn+JPoWWdAbYV6Pxhpg28oEYaUE9KpbSl0n2PTAgMBAAECggEAVxHNalx9JqRGWRUDlbD3LalQU/1LuHVf9VEuLYfL3YlNWymOKSpSIoWnHw9u/keJnsJItXGWO3qb0HbXfCYKl+uA7lfLLEccZkKSc2G6UUdyKdPGrL/TNoKmli4GXN+7C3lAa5uV9teQyVIlUIwwb8aZxK9FVXfhD6YIa56XmuP/c4z+uqyTdjC1YFS8TPL+mLh4qOiiYejkhhX6KLh9bHvCWTkDGTZIZjLdxoDqV5bjxWpJ0F+6Kn66PlfegrPPcjW5OhP/Z+c3rKtuEPnf51ug69AyZjYzYIqRZOezGYgjaqg8is3MDdWZOx0FsZpUC9o6dwIzsDgYFZDxydFD0QKBgQDJJkrYFluePlajtCyBjyyLkvIkZZKOE3r5jjbzUNZYG9sZWVrvAYsReQZiEw30h5yEOJOFOkmbpBW7TNABWh3N1rPLzqXX+EW9fhA2Y1+uiC+3/7pqK80L/JleJXFVyuVbfiThf0EnwcowHnKOWc8ZfcqhUSpO/lnkEasr1qkY6wKBgQCx3LhZLFc39D/DFxrHZmY02ZN9Ffn6++UVjA/IhPNYiEZl4SUd04hF8b4QQUta00rLr2JRNYlJEg32SxuC9bmf8XbX3UitLdZ5re5s/5WSvNzwf7U06USj1fhPLwfCCjubQiSwP9BNPJ/2ITuOtnQcZmZ5Wmgs15diQMCYucymuQKBgQDHUf+GIpmEtAcMTqRveZ1NbT4uXMwdpyYLliXTc34Cbw/sDYQzI9dXaBKwKmuArMSmrJ1ZvklkRfMW12WigVbZOnCNe2cRHD6XKA0Op+gPPXnznR9ux5p2z0Z2aSnmNpiR0ezf2kaJC9m7VuBzOIEkpGae9Zu0DQysF+oDFcIYIwKBgBOVafbnmvLeQecJNDmgXMCU9FhhgxTPh3nH4jUB7olg999f2uZd1DNfWr4Pcmydty6WMQ0gB+2zvzXPL0hMJhQmUh+Sjd4DngnnzMjTm3R8txcD+L/Kr3QaqyyM0R3cYpPFxKRjYlwewL4pCpW8ISy/Waki+zV0x4ZZ+trWGmKBAoGAR9t9gmX99Yck32RZd2t9wkAQ8TUfaRNtworLXti5cV6bsNPf6XDowEKC2NX9AoXjPbBaTsry37SpOe7NWskVhE8sFAwa2m4uk/PVGLxn5rvEyn6tAPnguSn+Ov6pDYv5vSUFk7R+M/bP1cCoLaqHENt3SaJjnJ9+B2/ZfT7MQLg=
decryData = 郭秀志 Test Asymmetric encrypt!

使用了java.util的Base64進(jìn)行encode和decode。但是要注意:java中有自帶的Base64算法類,但是安卓中卻沒有,之前出現(xiàn)的情況是,使用的Base64類不統(tǒng)一,比如在安卓客戶端開發(fā)使用的Base64算法是使用第三方提供的jar包,而java服務(wù)端中使用的是JDK自帶的Base64,導(dǎo)致從客戶端傳過來的密文,服務(wù)端解析出錯(cuò)。
org.apache.commons.codec.binary.Base64的高版本已經(jīng)沒有對(duì)應(yīng)的方法。

1.5 例子2(文件存儲(chǔ)公私鑰)

package com.erbadagang.springboot.rsa;

import org.apache.commons.codec.binary.Base64;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

/**
 * 生成公私鑰對(duì)并存儲(chǔ)到本地文件。
 *
 * @ClassName: RsaDemoWithFile
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/7/25 10:58
 * @Copyright:
 */
public class RsaDemoWithFile {
    /**
     * 生成公鑰和私鑰, 一般一次性生成, 存儲(chǔ)在文件中進(jìn)行分發(fā)和使用
     */
    public static void generateKey() {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(1024);  //一般來說, 長(zhǎng)度為2048是最好的, 也是推薦的
            KeyPair kp = kpg.genKeyPair();
            PublicKey pbkey = kp.getPublic();
            PrivateKey prkey = kp.getPrivate();
            // 保存公鑰
            FileOutputStream f1 = new FileOutputStream("d:/pubkey.dat");
            ObjectOutputStream b1 = new ObjectOutputStream(f1);
            b1.writeObject(pbkey);
            // 保存私鑰
            FileOutputStream f2 = new FileOutputStream("d:/privatekey.dat");
            ObjectOutputStream b2 = new ObjectOutputStream(f2);
            b2.writeObject(prkey);
        } catch (Exception e) {
        }
    }

    /**
     * 公鑰加密, 一般調(diào)用者傳遞明文, 從本地存儲(chǔ)讀取公鑰進(jìn)行加密
     *
     * @param plainTxt
     * @return
     * @throws Exception
     */
    public static String pubEncrypt(String plainTxt) throws Exception {
        String s = Base64.encodeBase64String(plainTxt.getBytes("UTF-8"));
        // 獲取公鑰及參數(shù)e,n
        FileInputStream f = new FileInputStream("d:/pubkey.dat");
        ObjectInputStream b = new ObjectInputStream(f);
        RSAPublicKey pbk = (RSAPublicKey) b.readObject();
        BigInteger e = pbk.getPublicExponent();
        BigInteger n = pbk.getModulus();
        // 獲取明文m
        byte ptext[] = s.getBytes("UTF-8");
        BigInteger m = new BigInteger(ptext);
        // 計(jì)算密文c
        BigInteger c = m.modPow(e, n);
        // 保存密文
        String ciperTxt = c.toString();
        return ciperTxt;
    }

    /**
     * 私鑰解密, 一般調(diào)用者傳遞密文, 從本地存儲(chǔ)讀取私鑰進(jìn)行解密
     *
     * @param ciperTxt
     * @return
     * @throws Exception
     */
    public static String privDecrypt(String ciperTxt) throws Exception {
        BigInteger c = new BigInteger(ciperTxt);
        // 讀取私鑰
        FileInputStream f = new FileInputStream("d:/privatekey.dat");
        ObjectInputStream b = new ObjectInputStream(f);
        RSAPrivateKey prk = (RSAPrivateKey) b.readObject();
        BigInteger d = prk.getPrivateExponent();
        // 獲取私鑰參數(shù)及解密
        BigInteger n = prk.getModulus();
        BigInteger m = c.modPow(d, n);
        // 顯示解密結(jié)果
        byte[] mt = m.toByteArray();
        String plainTxt = new String(Base64.decodeBase64(mt), "UTF-8");
        return plainTxt;
    }

    public static void main(String args[]) {
        try {
            generateKey();
            String ciperTxt = pubEncrypt("郭秀志 Test Asymmetric encrypt!");
            System.out.println("公鑰加密密文:" + ciperTxt);
            System.out.println("私鑰解密:" + privDecrypt(ciperTxt));
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }
}

運(yùn)行結(jié)果:

公鑰加密密文:6099844164314059443431591651751615722055254322730619374416036868657674637290798963105567557375836230466843160570341925833251877311218292252966007503499880207100523576112983947344123430250478502342666667129135836235200008011709395758839438577626394237727966426413570214930347318988576727557310041590164979452
私鑰解密:郭秀志 Test Asymmetric encrypt!

1.6 例子3(使用controller加解密)

1.6.1 生成公私鑰對(duì)的工具類RsaGenerator

package com.erbadagang.springboot.rsa.util;

import lombok.Getter;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

/**
 * @description RSA公私鑰對(duì)生成工具, 加解密處理注意:密鑰長(zhǎng)度2048
 * @ClassName: RsaGenerator
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/7/25 11:16
 * @Copyright:
 */
@Getter
public class RsaGenerator {

    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 獲取公鑰的key
     */
    private RSAPublicKey publicKey;

    /**
     * 獲取私鑰的key
     */
    private RSAPrivateKey privateKey;

    /**
     * <p>
     * 生成密鑰對(duì)(公鑰和私鑰)
     * </p>
     *
     * @return
     * @throws Exception
     */
    public void generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(2048);
        KeyPair keyPair = keyPairGen.generateKeyPair();

        this.publicKey = (RSAPublicKey) keyPair.getPublic();
        this.privateKey = (RSAPrivateKey) keyPair.getPrivate();
    }

}

1.6.2 獲取公私鑰對(duì)的Controller方法getRsaKey()

package com.erbadagang.springboot.rsa.controller;

import com.erbadagang.springboot.rsa.util.RsaGenerator;
import org.apache.commons.codec.binary.Base64;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

/**
 * RsaController作用是:通過controller來生成公私鑰對(duì),加密及解密。
 *
 * @ClassName: RsaController
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/7/25 11:09
 * @Copyright:
 */
@RestController
@RequestMapping("rsa")
public class RsaController {

    /**
     * 獲取RSA加密形式的publicKey和privateKey
     */
    @GetMapping("/genKey")
    public Map<String, String> getRsaKey() throws NoSuchAlgorithmException {
        RsaGenerator rsaGenerator = new RsaGenerator();
        rsaGenerator.generateKeyPair();
        Map<String, String> map = new HashMap<>(4);
        map.put("privateKey", Base64.encodeBase64String(rsaGenerator.getPrivateKey().getEncoded()));
        map.put("publicKey", Base64.encodeBase64String(rsaGenerator.getPublicKey().getEncoded()));
        return map;
    }
}

訪問上面URLhttp://localhost:8080/rsa/genKey運(yùn)行返回結(jié)果:

{
  "privateKey": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKr3Zhb/aM7DHnhT0PN2Fb7hnQxCfjwkurtROSVNZhDvaxAiNhuC/wUIUDd9tAGQSgIgEag6ISaFMcRIMTADpN6QJyiUcVy2M4LmRO55VotmMB9g6OUBmsBju3TL58Iloy05j1x/T0PG9N0KegKDbIwG8/FQsKXm5kfDOV5piPPReveiykfv6kh8ZqCbSBtIVpgdPDD5r13Cj4+Q0xGabznGXv5gC0xhKyZr3ayGPGcUvB2eW3FAFn6qOLMKvbawnhDZPkiNdGU+yD6Uz27R5Fyy8s1wGK3Uk+yPA23mxVXsYu19a+8JINRV1acmYGGXsYJmAi445kNsgFVsO28TLdAgMBAAECggEAC6plqd4D1sCRbr3gccvCMsRVgAqKMTWxnURix/1SCWwPDskMuEcdmztHLJftapcGCSFr5tbEsUKH5gybbrCIqotKtMTp7nsyTr180H3Lv6cfs7ExzUcW8yu4rCginoprnplHKH5FvvjrfxMPUsx9urg4ruzLIeGlgOsVHP+UsEm9oqGSA1O7fzWQfJszuLRSVAu8GFYYzpGMm5FMVfgPHpZZUrZRcnJnMwQVNzwNh121zDmbx7Win/LKAD4C6s6g/KDKs7L1QYhiEBvmI0AkCcaLtdl3YhiZfNSbxRnJ364nnLFZSPvyvLl0zxFyPWIX61jYKSqu6uM3nogT5JnvgQKBgQDrxhDKwLCYmOd2QZ5EPK9/d+y1SOgwKxW243SAZobRIVTUkJW3NzJe5lxisbMEDsxRyUKHm0aL49bNkQeklBmnMAmtSG5MMgTz/fqRjN7o9+bWukdOwFVRNfDJ/dbHKCDr+dwCiHDyu/qkcCfjIBUR+mS1z2x3LWUIxwb0MQkuVQKBgQDcErrH5zeJ86obWDhhEstD0JgYf/pGXpGQadEcuOall0Uhc/hRlTSTH7QC+k3J5zQ8WdG78vxEAAuPMC7JU7Nm0GKw0xmp/nq0nwvmF+psAw+uNNtbIn1o3uUjqdt27Sv7rS512mKX8S1Mj7y2jtn45lLv9VeYDsSL0O2k2wZqaQKBgQChYhq+XbTDTu4oQPQPKybJbpIE6Jmd1u/vFrP467TeUx1YvnrsRQjicnXMTGwHnAV4+fTjE4LvYA34+YusuH7ytGv7Q3fUCezgAfnQRQeTmZRVaH5Exlvf0bc229x2x935CDbzOOdvDwKaKfbzfVNO0gC7ffZ1gQoGPw1gemwZXQKBgQDEED+tpxX45kevsuoPueGzmhxW/3VmygvfYBa4AxchgeJKCnq5nDdJt931JTC2ZzBHcDIFw1Xx8yRZPjEAlnxnZdH2/SuJIroJPwUnyjjEX/nRVy/yQoj+LE5ydnqaunQL9d9Fifl6qpiT9B7Jef1B3VkYhTiztLxwYAPIcoWFuQKBgFulQsCQYESMeT59Vf9cSUTlVs/Hen2TsKeHhaqhx3SyRsHLvDnTz5JxL/5GZS8dCb+VZdCxcMYtfaFfY2wzVBDRj7wOFLf6RuQ5mSwHpapRv2XV4pImVRAjktFQsHAyN7fflhpVy/cGWw7tovJPjmIDNGIq8hdiIdo6CfepGoTB",
  "publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyq92YW/2jOwx54U9DzdhW+4Z0MQn48JLq7UTklTWYQ72sQIjYbgv8FCFA3fbQBkEoCIBGoOiEmhTHESDEwA6TekCcolHFctjOC5kTueVaLZjAfYOjlAZrAY7t0y+fCJaMtOY9cf09DxvTdCnoCg2yMBvPxULCl5uZHwzleaYjz0Xr3ospH7+pIfGagm0gbSFaYHTww+a9dwo+PkNMRmm85xl7+YAtMYSsma92shjxnFLwdnltxQBZ+qjizCr22sJ4Q2T5IjXRlPsg+lM9u0eRcsvLNcBit1JPsjwNt5sVV7GLtfWvvCSDUVdWnJmBhl7GCZgIuOOZDbIBVbDtvEy3QIDAQAB"
}

二、RSA加密算法+AES加密算法

2.1 AES概念

高級(jí)加密標(biāo)準(zhǔn)(Advanced Encryption Standard,縮寫:AES),在密碼學(xué)中又稱Rijndael加密法,是美國(guó)聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)。這個(gè)標(biāo)準(zhǔn)用來替代原先的DES。

2.2 數(shù)據(jù)傳送的兩個(gè)模式

2.2.1 客戶端傳輸重要信息給服務(wù)端,服務(wù)端返回的信息不需加密的情況

客戶端傳輸重要信息給服務(wù)端,服務(wù)端返回的信息不需加密,例如綁定銀行卡的時(shí)候,需要傳遞用戶的銀行卡號(hào),手機(jī)號(hào)等重要信息,客戶端這邊就需要對(duì)這些重要信息進(jìn)行加密,使用RSA公鑰加密,服務(wù)端使用RSA解密,然后返回一些普通信息,比如狀態(tài)碼code,提示信息msg,提示操作是成功還是失敗。這種場(chǎng)景下,僅僅使用RSA加密是可以的。

2.2.2 客戶端傳輸重要信息給服務(wù)端,服務(wù)端返回的信息需加密的情況

客戶端傳輸重要信息給服務(wù)端,服務(wù)端返回的信息需加密,例如客戶端登錄的時(shí)候,傳遞用戶名和密碼等資料,需要進(jìn)行加密,服務(wù)端驗(yàn)證登錄信息后,返回令牌token需要進(jìn)行加密,客戶端解密后保存。此時(shí)就需要結(jié)合這兩種算法了。

客戶端使用公鑰加密,服務(wù)端使用私鑰解密的過程。也許你會(huì)這么想,既然可以如此,那服務(wù)端那邊信息也可以通過RSA加密后,傳遞加密信息過來,客戶端進(jìn)行解密。但是,這樣做,顯示是不安全的。原因是,由于客戶端并沒有保存私鑰,只有公鑰,只可以服務(wù)端進(jìn)行私鑰加密,客戶端進(jìn)行公鑰解密,但由于公鑰是公開,別人也可以獲取到公鑰,如果信息被他們截取,他們同樣可以通過公鑰進(jìn)行解密,那么這樣子加密,就毫無意義了,所以這個(gè)時(shí)候,就要結(jié)合對(duì)稱算法,實(shí)現(xiàn)客戶端與服務(wù)端之前的安全通信了。另外一個(gè)重要原因是上篇文章提到的:“作為加密使用的 RSA 有著隨密鑰長(zhǎng)度增加,性能急劇下降的問題”,所以使用AES加密業(yè)務(wù)數(shù)據(jù),僅僅使用RSA來加解密AES的對(duì)稱密鑰。

2.3 AES加解密過程

于AES屬于對(duì)稱算法,加密和解密需要使用同一把密鑰,所以,服務(wù)端要解密傳遞過來的內(nèi)容,就需要密鑰 + 密文。
客戶端使用AES進(jìn)行加密,服務(wù)端要進(jìn)行解密的話,需要用到產(chǎn)生的密鑰,那密鑰必須從客戶端傳輸?shù)椒?wù)端,如果不對(duì)密鑰進(jìn)行加密,那加密就沒有意義了。所以這里終于談到了重點(diǎn),RSA算法+AES算法結(jié)合使用。

2.3.1 客戶端使用RSA + AES對(duì)重要信息進(jìn)行加密步驟

1.客戶端隨機(jī)產(chǎn)生AES的密鑰;
2.對(duì)重要信息進(jìn)行AES加密;
3.通過使用服務(wù)端RSA公鑰對(duì)AES密鑰進(jìn)行加密。
這樣在傳輸?shù)倪^程中,即時(shí)加密后的AES密鑰被別人截取,對(duì)其也無濟(jì)于事,因?yàn)樗⒉恢婪?wù)端RSA的私鑰,無法解密得到原本的AES密鑰,就無法解密用AES加密后的重要信息。

2.3.2 服務(wù)端使用RSA + AES對(duì)重要信息進(jìn)行解密步驟

1.對(duì)加密后的AES密鑰進(jìn)行服務(wù)端RSA私鑰解密,拿到密鑰原文;
2.對(duì)加密后的重要信息進(jìn)行AES解密,拿到原始內(nèi)容。

現(xiàn)實(shí)開發(fā)中,服務(wù)端有時(shí)也需要向客戶端傳遞重要信息,比如登錄的時(shí)候,返回token給客戶端,作為令牌,這個(gè)令牌就需要進(jìn)行加密,原理也是差不多的,比上面多一個(gè)步驟而已,就是將解密后的AES密鑰,對(duì)將要傳遞給客戶端的數(shù)據(jù)token進(jìn)行AES加密,返回給客戶端,由于客戶端和服務(wù)端都已經(jīng)拿到同一把AES鑰匙,所以客戶端可以解密服務(wù)端返回的加密后的數(shù)據(jù)。如果客戶端想要將令牌進(jìn)行保存,則需要使用自己定義的默認(rèn)的AES密鑰進(jìn)行加密后保存,需要使用的時(shí)候傳入默認(rèn)密鑰和密文,解密后得到原token。

2.4 代碼:


    /**
     * 發(fā)送端邏輯:
     * 1.客戶端隨機(jī)產(chǎn)生AES的密鑰;
     * 2.對(duì)重要信息進(jìn)行AES加密;
     * 3.通過使用服務(wù)端RSA公鑰對(duì)AES密鑰進(jìn)行加密。
     * 接收端邏輯:
     * 1.對(duì)加密后的AES密鑰進(jìn)行服務(wù)端RSA私鑰解密,拿到密鑰原文;
     * 2.對(duì)加密后的重要信息進(jìn)行AES解密,拿到原始內(nèi)容。
     */
    @GetMapping("/encryptDecryptSign")
    public void encryptDecryptSign() throws Exception {
        //發(fā)送方公私鑰
        String sendPrivateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKr3Zhb/aM7DHnhT0PN2Fb7hnQxCfjwkurtROSVNZhDvaxAiNhuC/wUIUDd9tAGQSgIgEag6ISaFMcRIMTADpN6QJyiUcVy2M4LmRO55VotmMB9g6OUBmsBju3TL58Iloy05j1x/T0PG9N0KegKDbIwG8/FQsKXm5kfDOV5piPPReveiykfv6kh8ZqCbSBtIVpgdPDD5r13Cj4+Q0xGabznGXv5gC0xhKyZr3ayGPGcUvB2eW3FAFn6qOLMKvbawnhDZPkiNdGU+yD6Uz27R5Fyy8s1wGK3Uk+yPA23mxVXsYu19a+8JINRV1acmYGGXsYJmAi445kNsgFVsO28TLdAgMBAAECggEAC6plqd4D1sCRbr3gccvCMsRVgAqKMTWxnURix/1SCWwPDskMuEcdmztHLJftapcGCSFr5tbEsUKH5gybbrCIqotKtMTp7nsyTr180H3Lv6cfs7ExzUcW8yu4rCginoprnplHKH5FvvjrfxMPUsx9urg4ruzLIeGlgOsVHP+UsEm9oqGSA1O7fzWQfJszuLRSVAu8GFYYzpGMm5FMVfgPHpZZUrZRcnJnMwQVNzwNh121zDmbx7Win/LKAD4C6s6g/KDKs7L1QYhiEBvmI0AkCcaLtdl3YhiZfNSbxRnJ364nnLFZSPvyvLl0zxFyPWIX61jYKSqu6uM3nogT5JnvgQKBgQDrxhDKwLCYmOd2QZ5EPK9/d+y1SOgwKxW243SAZobRIVTUkJW3NzJe5lxisbMEDsxRyUKHm0aL49bNkQeklBmnMAmtSG5MMgTz/fqRjN7o9+bWukdOwFVRNfDJ/dbHKCDr+dwCiHDyu/qkcCfjIBUR+mS1z2x3LWUIxwb0MQkuVQKBgQDcErrH5zeJ86obWDhhEstD0JgYf/pGXpGQadEcuOall0Uhc/hRlTSTH7QC+k3J5zQ8WdG78vxEAAuPMC7JU7Nm0GKw0xmp/nq0nwvmF+psAw+uNNtbIn1o3uUjqdt27Sv7rS512mKX8S1Mj7y2jtn45lLv9VeYDsSL0O2k2wZqaQKBgQChYhq+XbTDTu4oQPQPKybJbpIE6Jmd1u/vFrP467TeUx1YvnrsRQjicnXMTGwHnAV4+fTjE4LvYA34+YusuH7ytGv7Q3fUCezgAfnQRQeTmZRVaH5Exlvf0bc229x2x935CDbzOOdvDwKaKfbzfVNO0gC7ffZ1gQoGPw1gemwZXQKBgQDEED+tpxX45kevsuoPueGzmhxW/3VmygvfYBa4AxchgeJKCnq5nDdJt931JTC2ZzBHcDIFw1Xx8yRZPjEAlnxnZdH2/SuJIroJPwUnyjjEX/nRVy/yQoj+LE5ydnqaunQL9d9Fifl6qpiT9B7Jef1B3VkYhTiztLxwYAPIcoWFuQKBgFulQsCQYESMeT59Vf9cSUTlVs/Hen2TsKeHhaqhx3SyRsHLvDnTz5JxL/5GZS8dCb+VZdCxcMYtfaFfY2wzVBDRj7wOFLf6RuQ5mSwHpapRv2XV4pImVRAjktFQsHAyN7fflhpVy/cGWw7tovJPjmIDNGIq8hdiIdo6CfepGoTB";
        String sendPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyq92YW/2jOwx54U9DzdhW+4Z0MQn48JLq7UTklTWYQ72sQIjYbgv8FCFA3fbQBkEoCIBGoOiEmhTHESDEwA6TekCcolHFctjOC5kTueVaLZjAfYOjlAZrAY7t0y+fCJaMtOY9cf09DxvTdCnoCg2yMBvPxULCl5uZHwzleaYjz0Xr3ospH7+pIfGagm0gbSFaYHTww+a9dwo+PkNMRmm85xl7+YAtMYSsma92shjxnFLwdnltxQBZ+qjizCr22sJ4Q2T5IjXRlPsg+lM9u0eRcsvLNcBit1JPsjwNt5sVV7GLtfWvvCSDUVdWnJmBhl7GCZgIuOOZDbIBVbDtvEy3QIDAQAB";

        //接收方公私鑰
        String recPrivateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCP3no8bYUMsdW1sSsIaUxaWDxxOBlDYNCuoVn7BlyptCnIOkqu86dVCB0dRxG90KlZoCtXhfYQgZkIkzUYdAeQsIHJr2PZn+z8XuqQllqDXDbBi0VXIx0JS8pjNP9pe/3iyLOWK7UPZkew1+OaomKnjpjFAYUxBlakFloP02mmaOfCHxkHzLWQRw/uBD7WCmCtKP5Mism78s5QuZxyfbMopECloVU8YzYc6tm5nygPOvudIxZtCJDNRTmgXtDra/WnvWE/+hVBjvCO1uPxLI5krPwdlYuBzlrylmuCoVZr9K5mJcMLt4SH2ApWLx/hyWJbyB1FZlhM4DzGIw2oRnnVAgMBAAECggEAfmAPP8V0ehI8h71474qPZ0zayxlcF7OTm9JgGAEepHN9wER0Ffoxop/d8znae8IvAGuRpvAllZpBsyacHT7O5moll+RY8XFp2sYFhbyNBZabAqgz4LcXanMI9Nw4/4/LFMr39ZGvGjfeAZmidNLvlf/MckFDnizTLo/zzLMIuwNW7ZNAYCokNa+/MwmTly6d1fuoazYNvv+4u7GVkEU9pNDwqeQP0JRY1O5uN+7RLfH7IZ3ObuKIvoJOIZCzEgSqA0DTvqgRkNHm3L5oRkVXyv85rO55n3EFwZ6boe1KaHRGX0WHTpkGWcto9Sz3tmKIexBCeveV+XjwRya5RJrcAQKBgQDdikiTSYmXMREdQfab4+BXrK4Wv5c0l+DdfGczdn4/wl5TMV7nEXh9zOSgET1Wxt6yULFQalofHQzZym5VENsEe2kjSEWf39m4Y+wRHjhsG89wSDEGL+dYVsS74Np0doREVzOssXSltfV2iOAwBgublMQJ41hLcC7ypMZugtKz+QKBgQCmP1WtgFB5uugEKaqQul9N/17g1PZ1vbIRAoYhinbsBrlhc+H6YonFXPECIcc84Hds7dhxkcLUlUawybTo/JzpvQBTB823TH1U/VuA+uE3ajQv2lZC8hYWu4hpwndsdqePrmmXVJFT2aH12E9y5yaoVesvFPQ350x/NVLxfQUzvQKBgQCsW+XTEaeGhZo3FRb0efoUvDhFYpIVTQSZzSvNkibvHB2exA59383Ksho9nqwGU3r3aGhLlDLBeiyBVUk5zX9YoVtPI+9nTxVoq/UB7G0hTxG43bGmiqaGyBsPwQS1D3Aga2e8t+N0+Xgb3KnvMwTc6oUK3GHZb1JXXXM0j3u2oQKBgCHeDy88T6is2e1XK6c2QIocNxDocZkE3wy2DesxUQ6+Q+/FcsjWYCizyWlcxkDxnYK0ZX6laiJykqcbQF6ib7jyRumjUlZAH9w7jPOWqGDoot8IxL/4n2VcKOsasceH2JTdvCcXFFAXqvXxbiYDTw3GCxZZV3M4DI5xp4cIqBGlAoGAUHdGKaCauzdUrXB8wV9symBQvrEbmAXlHI713CpDPBHjYZ+STvYbMncSxI2IXELZkE0UHYQYqDgc+h+1n+r5lgi+ljBcFAiZBlU4cdrdzkEvJ6/OMqyAlko5+0hOr1tgkuPU1jQe33JuOaKvzqtGjmV8hkxh2wH0FSGBfLLd4IE=";
        String recPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj956PG2FDLHVtbErCGlMWlg8cTgZQ2DQrqFZ+wZcqbQpyDpKrvOnVQgdHUcRvdCpWaArV4X2EIGZCJM1GHQHkLCBya9j2Z/s/F7qkJZag1w2wYtFVyMdCUvKYzT/aXv94sizliu1D2ZHsNfjmqJip46YxQGFMQZWpBZaD9Nppmjnwh8ZB8y1kEcP7gQ+1gpgrSj+TIrJu/LOULmccn2zKKRApaFVPGM2HOrZuZ8oDzr7nSMWbQiQzUU5oF7Q62v1p71hP/oVQY7wjtbj8SyOZKz8HZWLgc5a8pZrgqFWa/SuZiXDC7eEh9gKVi8f4cliW8gdRWZYTOA8xiMNqEZ51QIDAQAB";

//        String AesKey =RandomStringUtils.randomAlphanumeric(16);

        //輸入的明文參數(shù)
        String inputJson = "{\"name\": \"guo\",\"age\": 40}";

        /**
         * 發(fā)送端業(yè)務(wù)開始
         */
        byte[] aesKey = AesUtils.generateAesKey(128);
        // AES 對(duì)稱加密業(yè)務(wù)數(shù)據(jù)
        String encryptData = AesUtils.encrypt(inputJson, aesKey);//注意key為16位
        System.out.println("encryptData = " + encryptData);
        // RSA 私鑰簽名數(shù)據(jù)
        String signature = RsaUtils.sign(inputJson, sendPrivateKey);
        // RSA 公鑰加密AES密鑰傳送給接收方
        String encryptAesKey = RsaUtils.encrypt(aesKey, recPublicKey);

        /**
         * 接收端業(yè)務(wù)開始
         */
        byte[] decryptedData;
        boolean signChecked;
        // 使用"接收端RSA私鑰"進(jìn)行【非對(duì)稱解密操作】發(fā)送端的“AES對(duì)稱加密Key”
        byte[] encryptKeyByte = RsaUtils.decrypt(encryptAesKey, recPrivateKey);
        System.out.println("encryptAesKey = " + Base64.encodeBase64String(encryptKeyByte));
        // 使用對(duì)稱加密AES Key對(duì)發(fā)送端對(duì)稱加密后的業(yè)務(wù)數(shù)據(jù)內(nèi)容,進(jìn)行【對(duì)稱解密操作】,獲取解密后的數(shù)據(jù)內(nèi)容。
        decryptedData = AesUtils.decrypt(encryptData, encryptKeyByte);
        System.out.println("decryptedData解密后的業(yè)務(wù)數(shù)據(jù) = " + new String(decryptedData));
        // 使用“發(fā)送端RSA公鑰”對(duì)發(fā)送端使用“RSA私鑰進(jìn)”行非對(duì)稱加密簽名后的數(shù)據(jù)進(jìn)行【非對(duì)稱解密驗(yàn)簽操作】,判斷數(shù)據(jù)完整性。
        signChecked = RsaUtils.checkSign(decryptedData, signature, sendPublicKey);
        System.out.println("signChecked = " + signChecked);
    }

2.5 測(cè)試

訪問URLhttp://localhost:8080/rsa/encryptDecryptSign。
輸出日志:

encryptData = gB6NMdYoL/CiKVuxPx7+b39KE4BEowmALf3Z98P8yas=
encryptAesKey = CdgT8AViaDMl9R000drYmg==
decryptedData解密后的業(yè)務(wù)數(shù)據(jù) = {"name": "guo","age": 40}
signChecked = true

底線


本文源代碼使用 Apache License 2.0開源許可協(xié)議,這里是本文源碼Gitee地址,可通過命令git clone+地址下載代碼到本地,也可直接點(diǎn)擊鏈接通過瀏覽器方式查看源代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容