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("非法訪問");
        }

 

這樣就限制了只能通過指定的移動端對文件上傳接口進行訪問。

在移動端調用接口進行測試

 

 

可見調用接口前加密成功

並且能用過后台接口的解密校驗

 

 

這樣別的第三方請求接口就沒法請求

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM