1. 程式人生 > >JDK中JCA的簡單使用(三)---RSA加密解密

JDK中JCA的簡單使用(三)---RSA加密解密

Cipher 類

Cipher類提供用於加密和解密的加密密碼功能。加密是獲取資料(稱為明文)和 金鑰,並且生成資料(密文)對於不知道金鑰的第三方無意義的過程。解密是一個相反的過程:採用密文和金鑰並生成明文。

對稱與非對稱加密
有兩種主要的加密型別:對稱(也稱為金鑰)和非對稱(或公鑰加密))。在對稱加密中,加密和解密資料的金鑰相同。保持金鑰的私密性對於保持資料機密至關重要。另一方面,非對稱密碼術使用公鑰/私鑰對來加密資料。用一個金鑰加密的資料用另一個金鑰解密。使用者首先生成公鑰/私鑰對,然後在任何人都可以訪問的可信資料庫中釋出公鑰。希望與該使用者安全通訊的使用者使用檢索到的公鑰加密資料。只有私鑰的持有者才能解密。保密私鑰對於此計劃至關重要。
非對稱演算法(例如RSA)通常比對稱演算法慢得多。這些演算法不是為有效保護大量資料而設計的。實際上,非對稱演算法用於交換較小的金鑰,用於初始化對稱演算法。

流與分組密碼
密碼有兩種主要型別:塊和 流。分組密碼一次處理整個塊,通常長度為多個位元組。如果沒有足夠的資料來構成完整的輸入塊,則必須填充資料:即,在加密之前,必須新增虛擬位元組以使密碼的塊大小為倍。然後在解密階段剝離這些位元組。填充可以由應用程式完成,也可以通過初始化密碼來使用填充型別,例如“PKCS5PADDING”。相反,流密碼一次處理一個小單元(通常是一個位元組或甚至一個位元)的輸入資料。這允許密碼處理任意數量的資料而無需填充。

運作模式
當使用簡單的分組密碼進行加密時,兩個相同的明文塊將始終產生相同的密文塊。試圖打破密文的密碼分析者如果注意到重複文字的塊,將會有一個更容易的工作。為了增加文字的複雜性,反饋模式使用前一個輸出塊來改變輸入塊,然後再應用加密演算法。第一個塊需要一個初始值,這個值稱為初始化向量(IV)。由於IV只是在加密之前改變資料,因此IV應該是隨機的,但不一定需要保密。存在多種模式,諸如CBC(密碼塊連結),CFB(密碼反饋模式)和OFB(輸出反饋模式)。ECB(電子密碼本模式)是不受塊位置或其他密文塊影響的模式。由於ECB密文使用相同的明文/金鑰是相同的,因此該模式通常不適用於加密應用程式。

主要應用場景

需要將傳輸的資料進行加密,例如支付資訊等,這裡需要注意的是,加密解密字串有長度限制,需要分塊加解密

使用流程:
1.使用私鑰加密

/**
     * 私鑰加密
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return encrypt(data, cipher);
    }

2.使用公鑰解密

  public static byte[] decryptByPublicKey(byte[] encryptedData, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return decrypt(encryptedData, cipher);
    }

完整程式碼:

  /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    
    /**
     * 私鑰解密
     *
     * @param encryptedData 已加密資料
     * @param privateKey    私鑰(BASE64編碼)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        return decrypt(encryptedData, cipher);
    }

    /**
     * 公鑰解密
     *
     * @param encryptedData 已加密資料
     * @param publicKeyStr     公鑰
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKeyStr)
            throws Exception {
        PublicKey publicKey = getPublickey(publicKeyStr);
        return decryptByPublicKey(encryptedData,publicKey);
    }

    public static byte[] decryptByPublicKey(byte[] encryptedData, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return decrypt(encryptedData, cipher);
    }



    /**
     * 公鑰加密
     *
     * @param data      源資料
     * @param publicKey 公鑰(BASE64編碼)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 對資料加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        return encrypt(data, cipher);
    }

    /**
     *
     * 私鑰字串加密
     *
     * @param data       源資料
     * @param privateKey 私鑰(BASE64編碼)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
            throws Exception {
        PrivateKey privateK = getPrivateKey(privateKey);
        return encryptByPrivateKey(data,privateK);
    }

    /**
     * 私鑰加密
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return encrypt(data, cipher);
    }

    /**
     * 分塊加密
     *
     * @param data
     * @param cipher
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data, Cipher cipher) throws Exception {
        return dealWithData(data, cipher, MAX_ENCRYPT_BLOCK);
    }

    /**
     * 分塊解密
     *
     * @param encryptedData
     * @param cipher
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] encryptedData, Cipher cipher) throws Exception {
        return dealWithData(encryptedData, cipher, MAX_DECRYPT_BLOCK);
    }

    /**
     * 分塊處理資料
     *
     * @param data
     * @param cipher
     * @param block
     * @return
     * @throws Exception
     */
    public static byte[] dealWithData(byte[] data, Cipher cipher, int block) throws Exception {
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 對資料分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > block) {
                cache = cipher.doFinal(data, offSet, block);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * block;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    public static void main(String[] args) throws Exception {
        String data = "Signature類是一個引擎類,提供加密的數字簽名演算法,例如DSA或RSAwithMD5。加密安全簽名演算法採用任意大小的輸入和私鑰,並生成一個相對較短(通常是固定大小)的位元組串——簽名。RSA最大解密密文大小為128個位元組,RSA最大加密明文大小為117個位元組,測試加密解密字串";
        Map keyPair = getKeyPair();
        String privateKeyStr = getPrivateKeyStr(keyPair);
        String publicKeyStr = getPublicKeyStr(keyPair);

        byte[] encryptData = encryptByPrivateKey(data.getBytes(),privateKeyStr);
        String encryptStr = Base64.getEncoder().encodeToString(encryptData);
        log.info("加密後的字串為:{}",encryptStr);

        byte[] decryptData = Base64.getDecoder().decode(encryptStr);
        byte[] decrypt = decryptByPublicKey(decryptData,publicKeyStr);
        log.info("解密後的字串為:{}",new String(decrypt));

    }

獲得的Cipher物件getInstance必須初始化為四種模式之一,這四種模式被定義為類中的最終整數常量Cipher。這些模式可以通過它們的符號名稱引用,如下所示,以及每種模式的用途說明:
ENCRYPT_MODE
加密資料。
DECRYPT_MODE
解密資料。
WRAP_MODE
將a包裝java.security.Key成位元組,以便可以安全地傳輸金鑰。
UNWRAP_MODE
將先前包裝的金鑰展開到 java.security.Key物件中。

完整工具類下載:https://download.csdn.net/download/cappadocia_/10730247