Java 數(shù)據(jù)加密和編碼的幾種方式

最近正好在項目中用到數(shù)據(jù)加密,于是從網(wǎng)上查閱一些資料债热,了解各種加密方式并寫代碼驗證砾嫉,就在本篇文章中做個總結(jié)吧。

我將從這幾個方面介紹 Java 中的加密方式以及相關(guān)的概念:

1. 異或加密
2. MD5 算法
3. Base64 編碼
4. DES 加密
5. AES 加密
6. RSA 加密

從嚴(yán)格意義上來說窒篱,MD5 和 Base64 不屬于加密焕刮,它們分別是信息摘要算法和編碼方式舶沿,但是網(wǎng)上好多人都說 MD5 加密、Base64 加密配并,我覺得有必要糾正一下括荡。對于其他的幾種加密方式,下面我會一一進(jìn)行舉例說明溉旋。

1. 異或加密

異或運算(xor)有個特點:數(shù) a 兩次異或同一個數(shù) b 仍然為 a畸冲,即(a^b)^b=a。利用這個原理可以實現(xiàn)數(shù)據(jù)的加密和解密功能观腊。
舉個栗子:a=10100001邑闲,b=00000110

    a=a^b;   // a=10100111
    b=b^a梧油;   // b=10100001苫耸,此時 b 等于 a

異或運算直接對二進(jìn)制數(shù)據(jù)進(jìn)行操作,對每一位(bit)上的數(shù)據(jù)進(jìn)行變換儡陨。所以輸入和輸出的數(shù)據(jù)長度相同褪子,不占用額外的空間,可以用于字符和文件的加密骗村,效率比較高嫌褪。下面我們用代碼實踐一下:

    // 加密的密鑰,構(gòu)造一定長度的字節(jié)數(shù)組
    private final static byte[] KEY_BYTES = "Vp6flFpGW86g7hi6MhD3Zl2eThJTjPnIjXE4".getBytes();
    private final static int KEY_LENGTH = KEY_BYTES.length;

    /**
     * 異或運算加密
     *
     * @param input 要加密的內(nèi)容
     * @return 加密后的數(shù)據(jù)
     */
    public static byte[] xorEncode(byte[] input) {
        int keyIndex = 0;
        int length = input.length;
        for (int i = 0; i < length; i++) {
            input[i] = (byte) (input[i] ^ KEY_BYTES[(keyIndex++ % KEY_LENGTH)]);
        }
        return input;
    }

    /**
     * 異或運算解密
     *
     * @param input 要解密的內(nèi)容
     * @return 解密后的數(shù)據(jù)
     */
    public static byte[] xorDecode(byte[] input) {
        int keyIndex = 0;
        int length = input.length;
        for (int i = 0; i < length; i++) {
            input[i] = (byte) (input[i] ^ KEY_BYTES[(keyIndex++ % KEY_LENGTH)]);
        }
        return input;
    }

為了方便查看加密后的內(nèi)容叙身,這里對輸出結(jié)果做了一下 Base64 編碼渔扎。

輸入:123456abcdef,輸出:Z0IFUl1aJzooFCIx

2. MD5 編碼

MD5 是將任意長度的數(shù)據(jù)字符串轉(zhuǎn)化成短小的固定長度的值的單向操作信轿,任意兩個字符串不應(yīng)有相同的散列值晃痴。因此 MD5 經(jīng)常用于校驗字符串或者文件,因為如果文件的 MD5 不一樣财忽,說明文件內(nèi)容也是不一樣的倘核,如果發(fā)現(xiàn)下載的文件和給定的 MD5 值不一樣,就要慎重使用即彪。

MD5 主要用做數(shù)據(jù)一致性驗證紧唱、數(shù)字簽名和安全訪問認(rèn)證,而不是用作加密隶校。比如說用戶在某個網(wǎng)站注冊賬戶時漏益,輸入的密碼一般經(jīng)過 MD5 編碼,更安全的做法還會加一層鹽(salt)深胳,這樣密碼就具有不可逆性绰疤。然后把編碼后的密碼存入數(shù)據(jù)庫,下次登錄的時候把密碼 MD5 編碼舞终,然后和數(shù)據(jù)庫中的作對比轻庆,這樣就提升了用戶賬戶的安全性癣猾。

使用 Java 實現(xiàn)簡單的 MD5 編碼:

    /**
     * 計算字符串的 MD5
     *
     * @param text 原文
     * @return 密文
     */
    public static String md5Encode(String text) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(text.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                String hexString = Integer.toHexString(b & 0xFF);
                if (hexString.length() == 1) {
                    hexString = "0" + hexString;
                }
                sb.append(hexString);
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            logger.error(e);
        }
        return null;
    }

輸入:123456abcdef,輸出:6f3b8ded65bd7a4db11625ac84e579bb

3. Base64 編碼

Base64 編碼是我們程序開發(fā)中經(jīng)常使用到的編碼方法余爆,它用 64 個可打印字符來表示二進(jìn)制數(shù)據(jù)纷宇。這 64 個字符是:小寫字母 a-z、大寫字母 A-Z蛾方、數(shù)字 0-9像捶、符號"+"、"/"(再加上作為墊字的"="转捕,實際上是 65 個字符)作岖,其他所有符號都轉(zhuǎn)換成這個字符集中的字符唆垃。Base64 編碼通常用作存儲五芝、傳輸一些二進(jìn)制數(shù)據(jù)編碼方法,所以說它本質(zhì)上是一種將二進(jìn)制數(shù)據(jù)轉(zhuǎn)成文本數(shù)據(jù)的方案辕万。

在 Java 中使用 Base64 很簡單枢步,系統(tǒng)的 API 已經(jīng)封裝好方法,我們直接調(diào)用即可渐尿。

// 編碼
 String encode = Base64.encodeToString(" 123456abcdef".getBytes(), Base64.DEFAULT);

// 解碼
 byte[] decodeByte = Base64.decode(encode .getBytes(), Base64.DEFAULT);
String decode = new String(decodeByte);

輸入:123456abcdef醉途,輸出:MTIzNDU2YWJjZGVm

無論是編碼還是解碼都會有一個參數(shù) flags,系統(tǒng) API 提供了以下幾種:

  • DEFAULT:表示使用默認(rèn)的方法來加密砖茸。
  • NO_PADDING:表示省略加密字符串最后的 "="隘擎。
  • NO_WRAP:表示省略所有的換行符(設(shè)置后 CRLF 就會失去作用)。
  • CRLF:表示使用 CR凉夯、LF 這一對作為一行的結(jié)尾而不是 Unix 風(fēng)格的 LF货葬。
  • URL_SAFE:表示加密時不使用對 URL 和文件名有特殊意義的字符來作為加密字符,就是以 "-" 和 "_" 代替 "+" 和 "/"劲够。

4. DES 加密

DES 是一種對稱加密算法震桶,所謂對稱加密算法就是:加密和解密使用相同密鑰的算法。DES 加密算法出自 IBM 的研究征绎,后來被美國政府正式采用蹲姐,之后開始廣泛流傳。但近些年使用越來越少人柿,因為 DES 使用 56 位密鑰柴墩,以現(xiàn)代的計算能力,24 小時內(nèi)即可被破解凫岖。

順便說一下 3DES(Triple DES)江咳,它是 DES 向 AES 過渡的加密算法,使用 3 條 56 位的密鑰對數(shù)據(jù)進(jìn)行三次加密隘截。是 DES 的一個更安全的變形扎阶。它以 DES 為基本模塊汹胃,通過組合分組方法設(shè)計出分組加密算法。比起最初的 DES东臀,3DES 更為安全着饥。

使用 Java 實現(xiàn) DES 加密解密,注意密碼長度要是 8 的倍數(shù)惰赋。加密和解密的 Cipher 構(gòu)造參數(shù)一定要相同宰掉,不然會報錯。

    /* 加密使用的 key */
    private final static byte[] KEY_BYTES = "Vp6fhlFXKpGW8k6QPRg7Q6Jb7HyAhRi6MIhJ2YtGD3Zl26eTthJTj5PnIjXH5EI4".getBytes();

   /**
     * DES 加密
     *
     * @param content 待加密內(nèi)容
     * @param key     加密的密鑰
     * @return 加密后的字節(jié)數(shù)組
     */
    public static byte[] encryptDES(byte[] content, byte[] key) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(desKey);
            // DES 是加密方式, EBC 是工作模式, PKCS5Padding 是填充模式
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
            return cipher.doFinal(content);
        } catch (Exception e) {
            logger.error(e);
        }
        return null;
    }

 /**
     * DES 解密
     *
     * @param content 待解密內(nèi)容
     * @param key     解密的密鑰
     * @return 解密的數(shù)據(jù)
     */
    public static byte[] decryptDES(byte[] content, byte[] key) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(desKey);
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, random);
            return cipher.doFinal(content);
        } catch (Exception e) {
            logger.error(e);
        }
        return null;
    }

輸入:123456abcdef赁濒,輸出:j1kR1+ZraO2Tg78dHueoTg==

5. AES 加密

高級加密標(biāo)準(zhǔn)(英語:Advanced Encryption Standard轨奄,縮寫:AES),在密碼學(xué)中又稱 Rijndael 加密法拒炎,是美國聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)挪拟。這個標(biāo)準(zhǔn)用來替代原先的 DES,已經(jīng)被多方分析且廣為全世界所使用击你。簡單說就是 DES 的增強版玉组,比 DES 的加密強度更高。

AES 與 DES 一樣丁侄,一共有四種加密模式:電子密碼本模式(ECB)惯雳、加密分組鏈接模式(CBC)、加密反饋模式(CFB)和輸出反饋模式(OFB)鸿摇。關(guān)于加密模式的介紹石景,推薦這篇文章:高級加密標(biāo)準(zhǔn)AES的工作模式(ECB、CBC拙吉、CFB潮孽、OFB)

    /* 加密使用的 key */
    private static final String AES_KEY = "KUbHwTqBy6TBQ2gN";
    /* 加密使用的 IV */
    private static final String AES_IV = "pIbF6GR3XEN1PG05";

 /**
     * AES 加密
     *
     * @param content 待解密內(nèi)容
     * @param key     密鑰
     * @return 解密的數(shù)據(jù)
     */
    public static byte[] encryptAES(byte[] content, byte[] key) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            // AES 是加密方式, CBC 是工作模式, PKCS5Padding 是填充模式
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            // IV 是初始向量,可以增強密碼的強度
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(AES_IV.getBytes()));
            return cipher.doFinal(content);
        } catch (Exception e) {
            logger.error(e);
        }
        return null;
    }

    /**
     * AES 解密
     *
     * @param content 待解密內(nèi)容
     * @param key     密鑰
     * @return 解密的數(shù)據(jù)
     */
    public static byte[] decryptAES(byte[] content, byte[] key) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(AES_IV.getBytes()));
            return cipher.doFinal(content);
        } catch (Exception e) {
            logger.error(e);
        }
        return null;
    }

輸入:123456abcdef庐镐,輸出:ho9cn9SvmeisfJy6Pv96oQ==

6. RSA 加密

RSA算法是一種非對稱加密算法恩商,所謂非對稱就是該算法需要一對密鑰,若使用其中一個加密必逆,則需要用另一個才能解密怠堪。目前它是最有影響力和最常用的公鑰加密算法,能夠抵抗已知的絕大多數(shù)密碼攻擊名眉。從提出到現(xiàn)今的三十多年里粟矿,經(jīng)歷了各種攻擊的考驗,逐漸為人們接受损拢,普遍認(rèn)為是目前最優(yōu)秀的公鑰方案之一陌粹。

該算法基于一個的數(shù)論事實:將兩個大質(zhì)數(shù)相乘十分容易,但是想要對其乘積進(jìn)行因式分解卻極其困難福压,因此可以將乘積公開作為加密密鑰掏秩。由于進(jìn)行的都是大數(shù)計算或舞,RSA 最快的情況也比 DES 慢上好幾倍,比對應(yīng)同樣安全級別的對稱密碼算法要慢 1000 倍左右蒙幻。所以 RSA 一般只用于少量數(shù)據(jù)加密映凳,比如說交換對稱加密的密鑰。

使用 RSA 加密主要有這么幾步:生成密鑰對邮破、公開公鑰诈豌、公鑰加密私鑰解密、私鑰加密公鑰解密抒和。

    public static final String AES = "AES";
    public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";

   /**
     * 隨機生成 RSA 密鑰對
     *
     * @param keyLength 密鑰長度, 范圍: 512~2048, 一般 1024
     * @return 密鑰對
     */
    public static KeyPair generateRSAKeyPair(int keyLength) {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
            kpg.initialize(keyLength);
            return kpg.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            logger.error(e);
        }
        return null;
    }

    /**
     * 公鑰加密
     *
     * @param data      原文
     * @param publicKey 公鑰
     * @return 加密后的數(shù)據(jù)
     */
    public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        try {
            KeyFactory kf = KeyFactory.getInstance(RSA);
            PublicKey keyPublic = kf.generatePublic(keySpec);
            Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, keyPublic);
            return cipher.doFinal(data);
        } catch (Exception e) {
            logger.error(e);
        }
        return null;
    }

    /**
     * 私鑰加密
     *
     * @param data       待加密數(shù)據(jù)
     * @param privateKey 密鑰
     * @return 加密后的數(shù)據(jù)
     */
    public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        try {
            KeyFactory kf = KeyFactory.getInstance(RSA);
            PrivateKey keyPrivate = kf.generatePrivate(keySpec);
            Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, keyPrivate);
            return cipher.doFinal(data);
        } catch (Exception e) {
            logger.error(e);
        }
        return null;
    }

    /**
     * 公鑰解密
     *
     * @param data      待解密數(shù)據(jù)
     * @param publicKey 密鑰
     * @return 解密后的數(shù)據(jù)
     */
    public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        try {
            KeyFactory kf = KeyFactory.getInstance(RSA);
            PublicKey keyPublic = kf.generatePublic(keySpec);
            Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, keyPublic);
            return cipher.doFinal(data);
        } catch (Exception e) {
            logger.error(e);
        }
        return null;
    }

    /**
     * 私鑰解密
     *
     * @param data  待解密的數(shù)據(jù)
     * @param privateKey 私鑰
     * @return 解密后的數(shù)據(jù)
     */
    public static byte[] decryptByPrivateKey(byte[] data, byte[] privateKey) {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        try {
            KeyFactory kf = KeyFactory.getInstance(RSA);
            PrivateKey keyPrivate = kf.generatePrivate(keySpec);
            Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, keyPrivate);
            return cipher.doFinal(data);
        } catch (Exception e) {
            logger.error(e);
        }
        return null;
    }

密鑰對生成后是一串灰常長特別長的數(shù)據(jù)矫渔,大家可以對其 Base64 后看看公鑰和隨機性如何。使用測試數(shù)據(jù)123456abcdef加密過摧莽、Base64 編碼后的結(jié)果如下庙洼。加密這么短的字符串竟然生成這么長的密文,難怪 RSA 算法這么慢范嘱!

密文 Base64 后:X6yx1XfkVk4DZpnzcCSZr2oK+WMP7Azm4fBcGNEwWPrRdtf9isfMeKgQsI6kqOF5Vb5b5IYAIqHZRE5QcDbIM/3bTWVTVg/t7enGCUSxValIvJ/A37syWTUXlh59DZzBMgzG4rbziGCc8CGyO03XFq8gCncr4NMZXQwkKI8Alds=

一般來說送膳,客戶端和服務(wù)端的通信過程是這樣的:服務(wù)端生成 RSA 加密的密鑰對,把公鑰給客戶端丑蛤,私鑰偷偷保留∷貉郑客戶端在首次使用公鑰加密數(shù)據(jù)受裹,然后發(fā)送給服務(wù)端。服務(wù)端接收并處理后虏束,會把對稱加密的密鑰下發(fā)給客戶端棉饶。客戶端接收到對稱加密的密鑰镇匀,以后的通信就會使用對稱加密的方式照藻。當(dāng)然也可以由客戶端生成對稱加密的密鑰,然后用公鑰加密發(fā)給服務(wù)端汗侵。這樣在雙方交換密鑰時保證了安全幸缕,之后的對稱加密保證了效率。

好了晰韵,關(guān)于 Java 加密和編碼的方式就先介紹這么多发乔,歡迎大家留言交流~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市雪猪,隨后出現(xiàn)的幾起案子栏尚,更是在濱河造成了極大的恐慌,老刑警劉巖只恨,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件译仗,死亡現(xiàn)場離奇詭異抬虽,居然都是意外死亡,警方通過查閱死者的電腦和手機纵菌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門斥赋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人产艾,你說我怎么就攤上這事疤剑。” “怎么了闷堡?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵隘膘,是天一觀的道長。 經(jīng)常有香客問我杠览,道長弯菊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任踱阿,我火速辦了婚禮管钳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘软舌。我一直安慰自己才漆,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布佛点。 她就那樣靜靜地躺著醇滥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪超营。 梳的紋絲不亂的頭發(fā)上鸳玩,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音演闭,去河邊找鬼不跟。 笑死,一個胖子當(dāng)著我的面吹牛米碰,可吹牛的內(nèi)容都是我干的窝革。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼见间,長吁一口氣:“原來是場噩夢啊……” “哼聊闯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起米诉,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤菱蔬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拴泌,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡魏身,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚪腐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箭昵。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖回季,靈堂內(nèi)的尸體忽然破棺而出家制,到底是詐尸還是另有隱情,我是刑警寧澤泡一,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布颤殴,位于F島的核電站,受9級特大地震影響鼻忠,放射性物質(zhì)發(fā)生泄漏涵但。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一帖蔓、第九天 我趴在偏房一處隱蔽的房頂上張望矮瘟。 院中可真熱鬧,春花似錦塑娇、人聲如沸澈侠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽埋涧。三九已至,卻和暖如春奇瘦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背劲弦。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工耳标, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邑跪。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓次坡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親画畅。 傳聞我的和親對象是個殘疾皇子砸琅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容