4-对称加密

对称加密与非对称加密

按照密钥的特征不同, 密码体制分为对称密码体制和非对称密码体制。对称加密和非对称加密都是可逆加密,用于安全的传送数据。对称加密是指数据加密和解密采用同一个密钥,而非对称加密则使用不同的密钥进行加解密。对称加密的优势是加密和解密速度快,能够处理大数据量的加解密,但其安全性依赖于密钥的安全性,所以必须通过可信任的通道交换密钥

分组密码与流密码

按照对明文消息加密方式的不同, 密码体制分为分组密码(Block Cipher)和流密码(Stream Cipher)。分组密码也称为块密码,对称密码体制均为分组密码,分组密码加密时首先将明文分割为若干固定长度的组,如果数据不够一个分组,则通过填充额外的数据来补全成一个分组,然后逐个对每个分组进行加密。流密码也称为序列密码,是使用密钥流对明文中的每个二进制位或者字节进行加密。

分组密码的工作模式

分组密码的工作模式是指每个加密分组之间的关联方式,主要有ECB模式(Electronic Code Book,电子密码本)、CBC模式(Cipher Block Chain,密文分组链接)、CFB模式(Cipher Feed Back,密文反馈)、OFB模式(Output Feed Back,输出反馈)、CTR模式(Counter,计数器)。

不同的工作模式有不同的优缺点,比如ECB模式是最基本的工作模式,每个密文分组不影响其他分组,相同的明文加密后对应相同的密文,无初始化向量。而CBC模式是目前使用最广泛的工作模式,明文加密前需要与前面的密文进行异或操作,第一个明文与初始向量进行异或操作,因此只要选择不同的初始向量,加密后的密文就不同。关于模式的详细内容可参考其他的资料

填充模式

如果明文的长度不是分组的整数倍,那么最后的那部分消息就不够一个分组,这时就要对最后一个消息块进行填充。目前有多种可行的填充方式,如果加密时以某种方式填充,解密时必须能够识别这种填充方式并去除填充内容。常见填充方式有Zeros填充(全部填充为0)、X923 填充(最后一个字节为填充字节数,其他填0)、PKCS7填充(每个填充字节都为填充了的字节数)、ISO10126填充(最后一个字节为填充字节数,其他填随机数)等。

加密解密过程(以AES为例)

  1. 生成密钥
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
byte[] bytesKey = secretKey.getEncoded();
  1. 存储密钥

生成的密钥bytesKey为毫无意义的字节数组,不能通过UTF-8等字符编码方式解析成无乱码的字符串(new String(bytesKey, "UTF-8")为乱码)),所以存储密钥有两种方式

hex编码方式

hex编码方式,我们可以通过16进制与2进制转换方式,清晰的看到密钥的二进制数据,但是需要占用双倍的存储空间

二进制码:   0100_0001
重新分组:   0000_0100 0000_0001
十六进制:   4                 1
Hex编码:   41

base64编码方式

Base64编码就是把3个8位的二进制数据用4个ASCII可见字符展示出来。编码时,将3个8位二进制码重新分组成4个6位的二进制码,不足6位的,右侧补零,然后这4个6位的二进制码高位补两个0,形成4个8位的字节数据,然后取每个字节的十进制值在编码表中对应的字符作为最终的编码数据。Base64编码后的数据长度是源数据长度的4/3。标准的Base64编码要求最终的数据长度是4字节的整数倍,不足4字节的倍数时要用填充字符补齐,填充字符为等号“=”。编码表如下

 0 A     1 B     2 C     3 D     4 E     5 F     6 G     7 H    
 8 I     9 J    10 K    11 L    12 M    13 N    14 O    15 P    
16 Q    17 R    18 S    19 T    20 U    21 V    22 W    23 X    
24 Y    25 Z    26 a    27 b    28 c    29 d    30 e    31 f    
32 g    33 h    34 i    35 j    36 k    37 l    38 m    39 n    
40 o    41 p    42 q    43 r    44 s    45 t    46 u    47 v    
48 w    49 x    50 y    51 z    52 0    53 1    54 2    55 3    
56 4    57 5    58 6    59 7    60 8    61 9    62 +    63 /

例如ASCII码A的Base64编码过程为

二进制:     0100 0001
重新分组:   010000 01
低位补零:   010000 010000
高位补零:   00010000 00010000 
转十进制:   16       16
对应字符:   Q        Q

为了节省存储空间,密钥一般选择Base64进行编码存储为字符串A(保证无乱码),加密和解密的双方都得知A。

  1. 加密

发送方: 在发送数据B前,对A进行base64解码得到密钥字节数组C,使用C对B进行加密得到密文字节数组D

  1. 编码与解码

发送方:由于D也是没有意义的字节数组,在网络上传输时,需要将D进行base64编码成无乱码的字符串E,网络传输E(例如http://xxxx?text=E
接收方: 接收方收到的为密文的base64编码,所以需要进行base64解码得到密文的字节数组D

  1. 解密

接收方:对A进行base64解码得到密钥字节数组C,使用C对D进行解密得到原始信息B

AES示例

AES(Advanced Encryption Standard,高级数据加密标准)是DES的继任者,支持128、192、256位密钥长度。下面的代码是AES加密的示例,使用CBC工作模式和PKCS5填充模式,CBC工作模式需要16个字节的初始向量。

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Test;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
/**
 * Created by tingkl on 2017/9/7.
 */
public class AES {
    private static String src = "tingkl security aes";
    static String hexKey;
    static String base64Key;
    static {
        // 生成KEY
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(new SecureRandom());
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] bytesKey = secretKey.getEncoded();
            // 密钥是没有意义的56个字节,如果使用utf-8编码方式显示成字符串(new String(result))会是乱码
            // 所以一般直接显示密文的16进制字符串
            hexKey = Hex.encodeHexString(bytesKey);
            System.out.println("hexKey:" + hexKey);
            base64Key = Base64.encodeBase64String(bytesKey);
            System.out.println("base64Key:" + base64Key);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    public static Key getSecretKey(byte[] bytesKey) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
        // KEY转换
        Key key = new SecretKeySpec(bytesKey, "AES");
        return key;
    }

    public byte[] encrypt(String src, Key secretKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        // 加密
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] result = cipher.doFinal(src.getBytes());
        return result;
    }

    private String decrypt(byte[] encryptBytesReceived, Key secretKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        // 解密
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] result = cipher.doFinal(encryptBytesReceived);
        return new String(result);
    }

    @Test
    public void jdkAESHex() throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, DecoderException, InvalidKeySpecException {
        Key secretKeyHex = getSecretKey(Hex.decodeHex(hexKey.toCharArray()));
        // 加密
        byte[] encryptBytesToSend = encrypt(src, secretKeyHex);
        // 加密以后,不能用new String(result)来显示,因为加密完后是没有意义的一堆字节,
        // 如果使用魔种编码方式显示成字符串,会是乱码,所以一般直接显示密文的16进制字符串
        // 编码
        String hexEncryptBytes = Hex.encodeHexString(encryptBytesToSend);
        System.out.println("aes encrypt:" + hexEncryptBytes);

        // 网络传输时用hex,比如http传参

        // 解码
        byte[] encryptBytesReceived = Hex.decodeHex(hexEncryptBytes.toCharArray());
        // 解密
        String result = decrypt(encryptBytesReceived, secretKeyHex);
        System.out.println("aes decrypt:" + result);
    }

    @Test
    public void jdkAESBase64() throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
        Key secretKeyBase64 = getSecretKey(Base64.decodeBase64(base64Key));
        byte[] encryptBytesToSend = encrypt(src, secretKeyBase64);
        // 加密以后,不能用new String(result)来显示,因为加密完后是没有意义的一堆字节,
        // 如果使用魔种编码方式显示成字符串,会是乱码,所以一般直接显示密文的16进制字符串
        // 编码
        String hexEncryptBytes = Base64.encodeBase64String(encryptBytesToSend);
        System.out.println("aes encrypt:" + hexEncryptBytes);

        // 网络传输时,一般用Base64(因为base64编码是数据的4/3,而hex编码数据增长一倍),比如http传参

        // 解码
        byte[] encryptBytesReceived = Base64.decodeBase64(hexEncryptBytes);
        // 解密
        // 接受到网络传过来的参数,先转成密文字节数组
        String result = decrypt(encryptBytesReceived, secretKeyBase64);
        System.out.println("aes decrypt:" + result);
    }


    @Test
    public void compare() throws BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, DecoderException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
        jdkAESHex();
        jdkAESBase64();
        /*
        网络传输时,一般用Base64(因为base64编码是数据的4/3,而hex编码数据增长一倍),比如http传参
        hexKey:52a3968b25ba3695657d2930f0b2ee48
        base64Key:UqOWiyW6NpVlfSkw8LLuSA==
        aes encrypt:c26dc0a7eb32b956c8f8631f66dca5c079c22ad11f157cd40ba0abba5b37c860
        aes decrypt:tingkl security aes
        aes encrypt:wm3Ap+syuVbI+GMfZtylwHnCKtEfFXzUC6Cruls3yGA=
        aes decrypt:tingkl security aes
        * */
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容