Java 與 js完美RSA非對稱加密

有這樣一個需求,前端登錄的用戶名密碼,密碼必需加密,但不可使用MD5,因為后臺要檢測密碼的復雜度,那么在保證安全的前提下將密碼傳到后臺呢,答案就是使用RSA非對稱加密算法解決 。

使用指南

java端
依賴 commons-codec
RSACoder.java

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

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by lake on 17-4-12.
 */
public class RSACoder {
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    public static byte[] decryptBASE64(String key) {
        return Base64.decodeBase64(key);
    }

    public static String encryptBASE64(byte[] bytes) {
        return Base64.encodeBase64String(bytes);
    }

    /**
     * 用私鑰對信息生成數字簽名
     *
     * @param data       加密數據
     * @param privateKey 私鑰
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        // 解密由base64編碼的私鑰
        byte[] keyBytes = decryptBASE64(privateKey);
        // 構造PKCS8EncodedKeySpec對象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        // KEY_ALGORITHM 指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取私鑰匙對象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 用私鑰對信息生成數字簽名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data);
        return encryptBASE64(signature.sign());
    }

    /**
     * 校驗數字簽名
     *
     * @param data      加密數據
     * @param publicKey 公鑰
     * @param sign      數字簽名
     * @return 校驗成功返回true 失敗返回false
     * @throws Exception
     */
    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        // 解密由base64編碼的公鑰
        byte[] keyBytes = decryptBASE64(publicKey);
        // 構造X509EncodedKeySpec對象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        // KEY_ALGORITHM 指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取公鑰匙對象
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data);
        // 驗證簽名是否正常
        return signature.verify(decryptBASE64(sign));
    }

    public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception{
        // 對密鑰解密
        byte[] keyBytes = decryptBASE64(key);
        // 取得私鑰
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 對數據解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 解密<br>
     * 用私鑰解密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(String data, String key)
            throws Exception {
        return decryptByPrivateKey(decryptBASE64(data),key);
    }

    /**
     * 解密<br>
     * 用公鑰解密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] data, String key)
            throws Exception {
        // 對密鑰解密
        byte[] keyBytes = decryptBASE64(key);
        // 取得公鑰
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509KeySpec);
        // 對數據解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 加密<br>
     * 用公鑰加密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(String data, String key)
            throws Exception {
        // 對公鑰解密
        byte[] keyBytes = decryptBASE64(key);
        // 取得公鑰
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509KeySpec);
        // 對數據加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data.getBytes());
    }

    /**
     * 加密<br>
     * 用私鑰加密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String key)
            throws Exception {
        // 對密鑰解密
        byte[] keyBytes = decryptBASE64(key);
        // 取得私鑰
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 對數據加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 取得私鑰
     *
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Key> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return encryptBASE64(key.getEncoded());
    }

    /**
     * 取得公鑰
     *
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Key> keyMap)
            throws Exception {
        Key key = keyMap.get(PUBLIC_KEY);
        return encryptBASE64(key.getEncoded());
    }

    /**
     * 初始化密鑰
     *
     * @return
     * @throws Exception
     */
    public static Map<String, Key> initKey() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator
                .getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        Map<String, Key> keyMap = new HashMap(2);
        keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公鑰
        keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私鑰
        return keyMap;
    }
}

測試RSACoderTest.java

import org.junit.Before;
import org.junit.Test;

import java.security.Key;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * Created by lake on 17-4-12.
 */
public class RSACoderTest {
    private String publicKey;
    private String privateKey;

    @Before
    public void setUp() throws Exception {
        Map<String, Key> keyMap = RSACoder.initKey();
        publicKey = RSACoder.getPublicKey(keyMap);
        privateKey = RSACoder.getPrivateKey(keyMap);
        System.err.println("公鑰: \n\r" + publicKey);
        System.err.println("私鑰: \n\r" + privateKey);
    }

    @Test
    public void test() throws Exception {
        System.err.println("公鑰加密——私鑰解密");
        String inputStr = "dounine";
        byte[] encodedData = RSACoder.encryptByPublicKey(inputStr, publicKey);
        byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData,
                privateKey);
        String outputStr = new String(decodedData);
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
        assertEquals(inputStr, outputStr);
    }

    @Test
    public void testSign() throws Exception {
        System.err.println("私鑰加密——公鑰解密");
        String inputStr = "dounine";
        byte[] data = inputStr.getBytes();
        byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);
        byte[] decodedData = RSACoder.decryptByPublicKey(encodedData, publicKey);
        String outputStr = new String(decodedData);
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
        assertEquals(inputStr, outputStr);
        System.err.println("私鑰簽名——公鑰驗證簽名");
        // 產生簽名
        String sign = RSACoder.sign(encodedData, privateKey);
        System.err.println("簽名:" + sign);
        // 驗證簽名
        boolean status = RSACoder.verify(encodedData, publicKey, sign);
        System.err.println("狀態:" + status);
        assertTrue(status);
    }
}

前端代碼

依賴 jsencrypt 項目

<script src="bin/jsencrypt.min.js"></script>
<script type="text/javascript">
    var encrypt = new JSEncrypt();
    encrypt.setPublicKey('java生成的公鑰');
    var encrypted = encrypt.encrypt('加密的字符串');
</script>

說明

前端生成加密的字符串encrypted,傳到后臺,java使用私鑰進行解密即可。


?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 有這樣一個需求,前端登錄的用戶名密碼,密碼必需加密,但不可使用MD5,因為后臺要檢測密碼的復雜度,那么在保證安全的...
    大豬大豬閱讀 214評論 0 0
  • 有這樣一個需求,前端登錄的用戶名密碼,密碼必需加密,但不可使用MD5,因為后臺要檢測密碼的復雜度,那么在保證安全的...
    大豬大豬閱讀 248評論 0 1
  • 有這樣一個需求,前端登錄的用戶名密碼,密碼必需加密,但不可使用MD5,因為后臺要檢測密碼的復雜度,那么在保證安全的...
    大豬大豬閱讀 9,481評論 1 10
  • 有這樣一個需求,前端登錄的用戶名密碼,密碼必需加密,但不可使用MD5,因為后臺要檢測密碼的復雜度,那么在保證安全的...
    大豬大豬閱讀 166評論 0 3
  • 有這樣一個需求,前端登錄的用戶名密碼,密碼必需加密,但不可使用MD5,因為后臺要檢測密碼的復雜度,那么在保證安全的...
    大豬大豬閱讀 202評論 0 0