java加解密實例

本文主要小結一下java裏頭的AES以及RSA加解密。java

AES


使用AES加密時須要幾個參數:android

  • 密鑰長度(Key Size)

AES算法下,key的長度有三種:12八、192和256 bits。因爲歷史緣由,JDK默認只支持不大於128 bits的密鑰,而128 bits的key已可以知足商用安全需求。算法

  • 加密模式(Cipher Mode)

分組密碼算法只能加密固定長度的分組,可是咱們須要加密的明文長度可能會超過度組密碼的分組長度,這時就須要對分組密碼算法進行迭代,以便將一段很長的明文所有加密。而迭代的方法就稱爲分組密碼的模式。
AES屬於塊加密(Block Cipher),塊加密中有CBC、ECB、CTR、OFB、CFB等幾種工做模式。安全

  • ECB過於簡單而不安全(ECB模式因爲每塊數據的加密是獨立的所以加密和解密均可以並行計算,ECB模式最大的缺點是相同的明文塊會被加密成相同的密文塊,這種方法在某些環境下不能提供嚴格的數據保密性);
  • CFB可被施以重放攻擊;
  • OFB 和 CTR 均可被主動攻擊者反轉密文,而引發解密後明文中的相應比特也發生變化;CTR比之OFB,多出能支持併發計算的特性,此外CTR是流式密碼;
  • CBC雖不支持並行計算,可是倒是這些模式中最爲安全的

本文使用CBC模式。併發

CBC模式對於每一個待加密的密碼塊在加密前會先與前一個密碼塊的密文異或而後再用加密器加密。第一個明文塊與一個叫初始化向量的數據塊異或。CBC模式相比ECB有更高的保密性,但因爲對每一個數據塊的加密依賴與前一個數據塊的加密因此加密沒法並行。與ECB同樣在加密前須要對數據進行填充,不是很適合對流數據進行加密。加密

  • 填充方式(Padding)

因爲塊加密只能對特定長度的數據塊進行加密,所以CBC、ECB模式須要在最後一數據塊加密前進行數據填充。
JDK則提供了PKCS5Padding。spa

  • 初始向量(Initialization Vector)

使用除ECB之外的其餘加密模式均須要傳入一個初始向量,其大小與Block Size相等(AES的Block Size爲128 bits)code

生成AES KEY

public static String genKeyAES() throws Exception {
        KeyGenerator kenGen = KeyGenerator.getInstance(AES);
        kenGen.init(KEY_SIZE);
        SecretKey key = kenGen.generateKey();
        String base64 = Base64.getEncoder().encodeToString(key.getEncoded());
        return base64;
    }

這裏KEY_SIZE採用128
後面統一用base64將byte[]轉爲字符串,方便展現、排查。ip

加解密

public static byte[] aesEncryptBytes(byte[] contentBytes, byte[] keyBytes) throws Exception {
        return cipherOperation(contentBytes, keyBytes, Cipher.ENCRYPT_MODE);
    }

    public static byte[] aesDecryptBytes(byte[] contentBytes, byte[] keyBytes) throws Exception {
        return cipherOperation(contentBytes, keyBytes, Cipher.DECRYPT_MODE);
    }

    private static byte[] cipherOperation(byte[] contentBytes, byte[] keyBytes, int mode) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes,AES);

        byte[] initParam = SIXTEEN_CHAR_INIT_VECTOR.getBytes(CHARSET);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);

        Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_PADDING);
        cipher.init(mode, secretKey, ivParameterSpec);

        return cipher.doFinal(contentBytes);
    }

這裏AES_CBC_PKCS5_PADDING爲AES/CBC/PKCS5Padding,使用簡寫的AES默認就是這個值ci

RSA

生成密鑰對

public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        return keyPair;
    }

    public static String getPublicKey(KeyPair keyPair){
        PublicKey publicKey = keyPair.getPublic();
        byte[] bytes = publicKey.getEncoded();
        return Base64.getEncoder().encodeToString(bytes);
    }

    public static String getPrivateKey(KeyPair keyPair){
        PrivateKey privateKey = keyPair.getPrivate();
        byte[] bytes = privateKey.getEncoded();
        return Base64.getEncoder().encodeToString(bytes);
    }

這裏KEY_SIZE用1024,RSA256,RSA512,RSA1024,RSA2048這四種,RSA後面的N表明位數(多少bit),位數越大,加密強度越大,須要破解須要的時間也就越長

目前主流密鑰長度至少都是1024bits以上,低於1024bit的密鑰已經不建議使用(安全問題)。那麼上限在哪裏?沒有上限,多大均可以使用。因此,主流的模值是1024位

加解密

public static byte[] publicEncrypt(byte[] content,PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA);
        cipher.init(Cipher.ENCRYPT_MODE,publicKey);
        byte[] bytes = cipher.doFinal(content);
        return bytes;
    }

    public static byte[] privateDecrypt(byte[] content,PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA);
        cipher.init(Cipher.DECRYPT_MODE,privateKey);
        byte[] bytes = cipher.doFinal(content);
        return bytes;
    }

這裏RSA即"RSA",默認是RSA/ECB/PKCS1Padding

AES與RSA結合

  • RSA 比 AES 更難破解,由於它不須要擔憂密鑰在傳遞過程當中有泄露,只存在暴力破解一種可能;
  • AES的優點是以分組爲輪,加解密速度很是快,通常而言,AES 速度上數百倍於 RSA

在實際應用中,咱們會混合應用AES和RSA:

  • 1生成一個一次性隨機密鑰,算法上採用 AES 的CBC模式 aes-128-cbc(加密分組爲128比特)對文件進行加密
  • 2加密完成後,爲了安全的傳遞這個一次性隨機密鑰,咱們使用接收方的RSA公鑰 對其進行加密,隨加密後的文件一塊兒發送
  • 3接收方使用RSA私鑰進行解密,獲得AES密鑰原文,並用AES解密文件

這樣就充分利用了二者的優點.

public void testHyperCodec(){
        KeyPair keyPair = RSAUtil.getKeyPair();
        String pubKey = RSAUtil.getPublicKey(keyPair);
        String priKey = RSAUtil.getPrivateKey(keyPair);


        String password = "1234567890";

        String aesRawKey = AESUtil.genKeyAES();
        System.out.println("aes raw key:"+aesRawKey);

        String rsaEntryptAesKey = RSAUtil.publicEncryptString(aesRawKey,pubKey);
        System.out.println(rsaEntryptAesKey);
        String aesEntryptContent = AESUtil.aesEncryptStringByBase64Key(password,aesRawKey);
        System.out.println(aesEntryptContent);

        //decode
        String decodedAesKey = RSAUtil.privateDecryptString(rsaEntryptAesKey,priKey);
        String decodedPwd = AESUtil.aesDecryptStringByBase64Key(aesEntryptContent,decodedAesKey);

        System.out.println("aes key decoded:"+decodedAesKey);
        System.out.println(decodedPwd);
}

doc

相關文章
相關標籤/搜索