,你就懂了信息安全的密码学

Posted 黑马程序员官方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了,你就懂了信息安全的密码学相关的知识,希望对你有一定的参考价值。

看懂这篇文章-你就懂了信息安全的密码学

一、前言

​ 一个信息系统缺少不了信息安全模块,今天就带着大家全面了解并学习一下信息安全中的密码学知识,本文将会通过案例展示让你了解抽象的密码学知识,阅读本文你将会有如下收获:

  • 熟悉现代密码学体系包含的主流密码技术
  • 掌握Base64和Hex编码技术的特性与使用案例
  • 掌握对称密码和非对称密码的特性与使用案例
  • 掌握混合密码系统和随机数的特征与使用案例

二、关于密码

提到密码,你的第一印象是什么?我们平时登录微信、邮箱都需要输入用户名和密码,或者用手机支付也需要输入支付密码,大部分人想到的可能就是这些情形中涉及到的密码。然而本文即将讨论的密码与此密码完全是不同的概念。实际上无论是微信还是支付宝或者其他系统要求输入的密码都只是一种身份验证的凭证,也就正确的密码是可以证明你是这个账号的主人的证据。这种密码准确来讲叫做口令比较合适,对应英文中的password、pin。

本文中提到的密码是什么呢?实际上,密码(cryptography)是一个极其庞大复杂的信息处理体系,涉及到信息的机密性、完整性、认证、不可否认性等众多方面,由此衍生出的很多保护我们信息安全的技术,这些技术我们一般统称为密码技术。密码技术是密码学的核心。

数学与密码技术的关系:数学是密码技术的基础,复杂的密码技术往往都会涉及到复杂的数学公式。

密码学:密码学是网络安全、信息安全、区块链等领域的基础,常见的对称密码、公钥密钥、散列函数等,都属于密码学范畴。密码学中的密码技术比如“密码”可以让窃听者无法解读窃取的信息,“单项散列函数”可以检测出消息是否被篡改,“数字签名”可以确定消息是否来源于合法的发送者。

三、信息安全

1. 概念

信息安全是指信息网络的硬件、软件及其系统中的数据受到保护,不受偶然的或者恶意的原因而遭到破坏 、 更改 、泄露、否认等,系统连续可靠正常地运行,信息服务不中断。 信息安全安全是建立在以密码技术为基础的计算机安全领域,辅以通信技术、计算机技术与网络技术等方面的内容。

2. 与密码学的关系

  • 密码学是保障信息安全的核心技术 ,但不是提供信息安全的唯一方式 。

  • 信息安全是密码学研究与发展的目的 。

  • 信息安全的理论基础是密码学,信息安全的问题根本解决往往依靠密码学理论 。

3. 密码学与信息安全常识

  • 不要使用保密的密码算法

  • 使用低强度的密码比不进行加密更危险

  • 任何密码总有一天会被破解

  • 密码只是信息安全的一部分

四、现代密码学体系

信息安全及密码学技术,是整个信息技术的基石。在西方语文中,密码学一词源于希腊语,krypto意思是隐藏,graphene是书写的意思。密码学的发展总共经历了四个阶段:远古密码、古典密码、近代密码和现代密码,这四个阶段的发展历史详细介绍可参见文章最后的附录部分,接下来本文内容主要介绍的是现代密码学所涉及到的密码知识。

1. 信息安全威胁与密码技术

该图完整的展示了信息安全面临的威胁与解决方案中会用到的密码技术,本文侧重于介绍有关于保证数据机密性的密码技术。

2. 密码算法及重要概念

将明文通过处理变换为密文的规则称为加密算法,将密文恢复成明文的规则称为解密算法,加密解密一起称为密码算法。

  • 明文 (Plaintext): 信息的原始数据
  • 密文(Ciphertext):明文经过编码变换所生成的数据
  • 加密(Encryption):对明文进行编码变换生成密文的过程
  • 解密(Decryption):将密文恢复成明文的过程
  • 密钥:密码算法需要用到密钥的,密钥就相当于保险库大门的钥匙,重要性不言而喻,所以切记不要泄露密码的密钥。 密钥 (Key):控制明文与密文之间相互变换的,分为加密密钥和解密密钥。

3. ASCII编码

ASCII码 是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646 。在这个页面,你可以找到8位的256个字符、ASCII码表和Windows-1252 (code page 1252,它是国际标准ISO 8859-1的一个扩展字符集) 标准保持一致;

ASCII码American Standard Code for Information Interchange 的缩写,而不是ASCⅡ(罗马数字2),有很多人在这个地方产生误解;

ASCII码 规范于1967年第一次发布,最后一次更新是在1986年,它包含了33个控制字符(具有某些特殊功能但是无法显示的字符)和95个可显示字符;

ASCII码大致可以分作三部分组成。
第一部分是:ASCII非打印控制字符
第二部分是:ASCII打印字符
第三部分是:扩展ASCII打印字符

3.1 第一部分:ASCII非打印控制字符表

ASCII表上的数字0–31分配给了控制字符,用于控制像打印机等一些外围设备。例如,12代表换页/新页功能。此命令指示打印机跳到下一页的开头。(参详ASCII码表中0-31)

3.2 第二部分:ASCII打印字符

数字 32–126 分配给了能在键盘上找到的字符,当您查看或打印文档时就会出现。数字127代表 DELETE 命令。(参详ASCII码表中32-127)

3.3 第三部分:扩展ASCII打印字符

扩展的ASCII字符满足了对更多字符的需求。扩展的ASCII包含ASCII中已有的128个字符(数字0–32显示在下图中),又增加了128个字符,总共是256个。即使有了这些更多的字符,许多语言还是包含无法压缩到256个字符中的符号。因此,出现了一些ASCII的变体来囊括地区性字符和符号。例如,许多软件程序把ASCII表(又称作ISO8859-1)用于北美、西欧、澳大利亚和非洲的语言。

4. 字符串的ASCII码与二进制位

public class ASCIITest 

 	@Test
    public void test01() 
        String str = "heima";
        byte[] bytes = str.getBytes();
        for (byte b : bytes) 
            //打印ascii码
            System.out.println(b);

            //获取二进制位
            String s = Integer.toBinaryString(b);
            System.out.println(s);
        
    
    
    
    @Test
    public void test02() throws UnsupportedEncodingException 
        String str = "黑马"; //中文UTF-8编码一个汉字占3个字节,中文GBK编码一个汉字占2个字节。
        byte[] bytes = str.getBytes("UTF-8");
        System.out.println("字节个数:"+ bytes.length);
        char[] chars = str.toCharArray();

        for (char c : chars) 
            //打印字符
            System.out.println(c);
            //字符类型会自动提升为int类型,获取二进制值(10进制转二进制)
            String s = Integer.toBinaryString(c);
            System.out.println(s);
        
    

   

5. Hex编码与Base64编码

我们知道在计算机中的字节共有256个组合,对应就是ascii码,而ascii码的128~255之间的值是不可见字符。而在网络上交换数据时,比如说从A地传到B地,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,这是不利于传输的。所以我们需要将字节转为正确的课可见字符,需要对字节数组进行进一步编码,常用编码格式用Hex编码和Base64编码。

5.1 Hex编码

1字节=8位2进制,比如小写字母a,ASCII表对应十进制为97,二进制表示为01100001

1字节=2位16进制,比如小写字母a,ASCII表对应十进制为97, 十六进制表示为61

  • 因为一个字节中存在8个 bit可以表示256个字符,而非扩展 ASCII 码只能表示0-127种字符,为了能完整地表示一个字节,可以将二进制数据转换为十六进制数据的方式来实现。所以 Hex 编码也被称作为 Base16 编码,相比于原先8位表示一个字节,Hex 编码能够只用2位表示一个字节。Hex 编码最常用于二进制文件查看时展示的编码,如010Editor 就可以支持查看二进制文件。
  • 使用16个可见字符来表示一个二进制数组,编码后数据大小将x2
  • 1个字符需要用2个可见字符来表示
5.1.1 代码示例

引入依赖

 <dependencies>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.14</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.5</version>
            <scope>test</scope>
        </dependency>
  </dependencies>

单元测试:

public class HexDemoTest 

 	@Test
    public void test01() 
        String data = "itcast" ;
        byte[] bytes = data.getBytes() ;
        //测试hex
        String encoded = Hex.encodeHexString(bytes) ;
        System.out.println(encoded);
    

   

5.2 base64编码

  • Base64编码要求把3个8位字节(3乘8=24)转化为4个6位的字节(4乘6=24),之后在6位的前面补两个0,形成8位一个字节的形式。 如果剩下的字符不足3个字节,则用0填充,输出字符使用’=‘,因此编码后输出的文本末尾可能会出现1或2个’='。为了保证所输出的编码位可读字符,Base64制定了一个编码表,以便进行统一转换。编码表的大小为2^6=64,这也是Base64名称的由来。标准base64只有64个字符(大写A到Z、小写a到z、数字0到9、“+”和“/”)以及用作后缀等号;

  • Base64是网络上最常见的用于传输8Bit字节码的可读性编码算法之一

  • 以每 3 个 字符(1Byte=8bit)为一组,然后针对每组,首先获取每个字符的 ASCII 编码(字符’a’=97=01100001),然后将 ASCII 编码转换成 8 bit 的二进制,得到一组 3 * 8=24 bit 的字节。然后再将这 24 bit 划分为 4 个 6 bit 的字节,并在每个 6 bit 的字节前面都填两个高位 0,得到 4 个 8 bit 的字节,然后将这 4 个 8 bit 的字节转换成十进制,对照 BASE64 编码表 (下表),得到对应编码后的字符。

  • 使用64个可见字符来表示一个二进制数组,编码后数据大小变成原来的4/3

  • 3个字符用4个可见字符来表示

5.2.1 原理示例1-足够三字节

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-orGBFmhj-1660212818737)(assets/base64示例1.png)]

  • 第一步:“jay”、“a”、"n"对应的ASCII码值分别为106,97,121,对应的二进制值是01101010、01100001、01111001。如图第二三行所示,由此组成一个24位的二进制字符串。
  • 第二步:如图第四行,将24位每6位二进制位一组分成四组。
  • 第三步:在上面每一组前面补两个0(红色背景),扩展成32个二进制位,此时变为四个字节:00011010、00100110、00000101、00111001。分别对应的值(Base64编码索引)为:26、38、5、57。
  • 第四步:用上面的值在Base64编码表中进行查找,分别对应:a、m、F、5。因此“jay”Base64编码之后就变为:amF5。
5.2.2 代码示例1-足够三字节

单元测试:

public class Base64DemoTest 

    @Test
    public void test01() 
        //jay正好三个字节,编码后输出的结果没有=
        System.out.println(Base64.encodeBase64String("jay".getBytes()));
    

5.2.3 原理示例2-不够三字节

如果字节不足三个怎么办,分组的时候不组8位的都补0,计算没结果的=号代替

5.2.4 代码示例2-不够三字节

单元测试:

public class Base64Demo 

    @Test
    public void test02() 
        //ja不够三个字节,编码后一定会有=
        System.out.println(Base64.encodeBase64String("ja".getBytes()));
    


5.3 代码示例-编码与解码

单元测试:

/**
 * hex编码与base64编码测试
 */
public class HexAndBase64Test 

    @Test
    public void test() throws DecoderException 
        String data = "黑马程序员" ;
        byte[] bytes = data.getBytes() ;
        //测试hex
        String encryStr = Hex.encodeHexString(bytes) ;
        String decryStr = new String(Hex.decodeHex(encryStr.toCharArray())) ;
        System.out.println("Hex编码解码:"+ encryStr  + " | " + decryStr) ;

        //测试base64
        encryStr = Base64.encodeBase64String(bytes) ;
        decryStr = new String(Base64.decodeBase64(encryStr.getBytes()) );
        System.out.println("Base64编码解码:"+ encryStr  + " | " + decryStr) ;
    

上面我们已经看到了Base64就是用6位(2的6次幂就是64)表示字符,因此成为Base64。同理,Base32就是用5位,Base16就是用4位。

对比: hex编码速度快,体积大;base64编码速度慢,体积小

6. 密码分类

6.1 对称密码

加密密钥和解密密钥相同,又称传统密码体制、共享密钥密码体制、秘密密钥体制或单密钥体制。从密钥使用方式上分为分组密码和序列密码 ,这点后文会有介绍。

**对称加密算法的优点:**算法公开、计算量小、加密速度快、加密效率高。

**对称加密算法的缺点:**交易双方都使用同样钥匙,安全性得不到保证。此外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的惟一钥匙,这会使得发收信双方所拥有的钥匙数量呈几何级数增长,密钥管理成为用户的负担。对称加密算法在分布式网络系统上使用较为困难,主要是因为密钥管理困难,使用成本较高。

对称加密通常使用的是相对较小的密钥,一般小于256 bit。因为密钥越大,加密越强,但加密与解密的过程越慢。如果你只用1 bit来做这个密钥,那黑客们可以先试着用0来解密,不行的话就再用1解;但如果你的密钥有1 MB大,黑客们可能永远也无法破解,但加密和解密的过程要花费很长的时间。密钥的大小既要照顾到安全性,也要照顾到效率,是一个trade-off。

常用对称加密算法

  1. DES(Data Encryption Standard):数据加密标准,速度较快,适用于加密大量数据的场合。
  2. 3DES(Triple DES):是基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高。
  3. AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高,支持128、192、256、512位密钥的加密。

算法特征

  1. 加密方和解密方使用同一个密钥,一旦密钥文件泄漏, 就会导致数据暴露
  2. 加密解密的速度比较快,适合数据比较长时的使用,可以加密大文件
  3. 密钥传输的过程不安全,且容易被破解,密钥管理也比较麻烦。
  4. 加密后编码表找不到对应字符, 出现乱码
  5. 一般结合Base64使用
6.1.1 DES
  • DES是1997年美国联邦信息处理标准中所采用的一种对称密码算法,一直以来被美国以及其他国家的政府和银行等广泛采用。随着计算机的快速发展,DES已经被暴力破解,1997年用时96天破译密钥,1998年41天破译密钥,到了1999年只用22小时15分钟就可以破译。

  • DES技术是一种将64比特的明文加密成64比特的密文的对称密码算法,因此理论上来讲,他的密钥长度也是64位,但因为在DES的密钥中每隔7比特,就会设置一个用于错误检查的比特,所以实际上DES的密钥的长度只有56比特。

  • DES是以64比特的明文(比特序列)为一个单位进行加密,这64比特的单位成为分组,一般来说,以分组为单位进行处理的密码算法称为分组密码。

  • DES每次每次只能加密64比特的数据,如果要加密的明文比较长,就需要对DES加密进行迭代(反复),而迭代的具体方案就称为模式。

Java中有关对称和非对称加密的核心类:javax.crypto.Cipher

代码示例

/**
 * DES加密算法测试
 */
public class DesTest 

    /**
     * 测试DES加密
     */
    @Test
    public void testEncrypt() throws Exception 
        //明文
        String text = "黑马程序员";
        //密钥,长度必须为8个字节(字符)
        byte[] secretKeyBytes = "12345678".getBytes();
        //secretKeyBytes  = generateSecretKey("DES", 56);
        // Cipher:获取密码对象,参数按"算法/模式/填充模式"
        Cipher cipher = Cipher.getInstance("DES");

        // 参数1:密钥,key的字节数组,参数2:加密算法
        SecretKeySpec sks = new SecretKeySpec(secretKeyBytes, "DES");
        //加密对象初始化数据,参数1:模式,有加密模式和解密模式,参数2:密钥规则
        cipher.init(Cipher.ENCRYPT_MODE,sks);
        //执行加密,得到加密结果
        byte[] bytes = cipher.doFinal(text.getBytes());
        //输出字节,因为ascii码有负数,解析不出来,所以乱码
        //将byte数组转成ASCII编码,必须确保byte数组的值在ASCII的可视字符范围,否则会出现乱码,
        //因为ASCII的取值范围比byte小,byte的取值范围是-128到127
        for (byte b : bytes) 
            System.out.println(b);
        
        // 打印密文
        System.out.println(new String(bytes));

        //将byte数组转成Base64编码。
        String result = Base64.encodeBase64String(bytes);
        System.out.println("加密后的值:" + result);
    

    /**
     * 测试DES解密
     */
    @Test
    public void testDecrypt() throws Exception 
        //密文
        String crpyt = "+rBmhkThnKQf8IJTM/qmMA==";

        //密钥,长度必须为8个字节(字符)
        byte[] secretKeyBytes = "12345678".getBytes();
        //获取Cipher对象
        Cipher cipher = Cipher.getInstance("DES");
        // 指定密钥规则
        SecretKeySpec sks = new SecretKeySpec(secretKeyBytes, "DES");
        cipher.init(Cipher.DECRYPT_MODE, sks);
        //解密,上面使用的base64编码,下面直接用密文
        byte[] bytes = cipher.doFinal(Base64.decodeBase64(crpyt));
        //  因为是明文,所以直接返回
        String text = new String(bytes);
        System.out.println("解密后的值:"+ text) ;
    

    /**
     * 生成密钥
     * @param algorithm 算法
     * @param len  密钥长度
     */
    public static byte[] generateSecretKey(String algorithm, int len) throws NoSuchAlgorithmException 
        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);//密钥生成器
        keyGenerator.init(len);//密钥长度
        SecretKey secretKey = keyGenerator.generateKey();//生成密钥
        return secretKey.getEncoded(); //密钥字节数组转字符串
    

6.1.2 3DES
  • 三重DES,是为了加强DES的强度,将DES重复3次所得到的一种密码算法。明文需经过3次DES处理才能得到最后密文,由于DES密钥实际长度为56比特,因此3DES的密钥密钥实际长度就是56*3=168比特。通过增加迭代次数提高安全性,常应用在银行等金融机构。

  • DES密钥长度是8字节(64比特),3DES密钥长度是24字节(192比特)

  • 三重DES不是进行三次DES加密(加密-加密-加密),而是加密-解密-加密的过程。

  • 加密过程:用第一支密钥对原文进行加密,再使用第二支密钥对第一步操作后的信息进行解密,最后使用第三支密钥对第二步操作后的信息进行加密得到最终密文。

    解密过程:用第三支密钥对密文进行解密,再采用第二支密钥进行加密,最后采用第一支密钥解密得到原文。

  • 三重DES中所有密钥都相同时,三重DES等同于普通DES,因为前两步加密解密后得到的是原来的明文。

  • EDE:表示加密(Encryption)-> 解密(Decryption)->加密(Encryption)这个流程。

  • 缺点:处理速度较慢、密钥计算时间较长、加密效率不高。

/**
 * 3DES加密算法测试
 */
public class Des3Test 

    /**
     * 测试3DES加密
     */
    @Test
    public void testEncrypt() throws Exception 
        //明文
        String text = "黑马程序员";
        //密钥,长度必须24个字节(字符)
        byte[] secretKeyBytes = "123456781234567812345678".getBytes();
        //可指定密钥实际长度是168
        //secretKeyBytes  = generateSecretKey("DESede", 168);
        // Cipher:获取密码对象,参数按"算法/模式/填充模式"
        Cipher cipher = Cipher.getInstance("DESede");

        // 参数1:密钥,key的字节数组,参数2:加密算法
        SecretKeySpec sks = new SecretKeySpec(secretKeyBytes, "DESede");
        //加密对象初始化数据,参数1:模式,有加密模式和解密模式,参数2:密钥规则
        cipher.init(Cipher.ENCRYPT_MODE,sks);
        //执行加密,得到加密结果
        byte[] bytes = cipher.doFinal(text.getBytes());

        //将byte数组转成Base64编码。
        String result = Base64.encodeBase64String(bytes);
        System.out.println("加密后的值:" + result);
    

    /**
     * 测试3DES解密
     */
    @Test
    public void testDecrypt() throws Exception 
        //密文
        String crpyt = "+rBmhkThnKQf8IJTM/qmMA==";

        //密钥,长度必须24个字节(字符)
        byte[] secretKeyBytes = "123456781234567812345678".getBytes();
        //获取Cipher对象
        Cipher cipher = Cipher.getInstance("DESede");
        // 指定密钥规则
        SecretKeySpec sks = new SecretKeySpec(secretKeyBytes, "DESede");
        cipher.init(Cipher.DECRYPT_MODE, sks);
        //解密,上面使用的base64编码,下面直接用密文
        byte[] bytes = cipher.doFinal(Base64.decodeBase64(crpyt));
        //  因为是明文,所以直接返回
        String text = new String(bytes);
        System.out.println("解密后的值:"+ text) ;
    

    /**
     * 生成密钥
     * @param algorithm 算法
     * @param len  密钥长度
     */
    public static byte[] generateSecretKey(String algorithm, int len) throws NoSuchAlgorithmException 
        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);//密钥生成器
        keyGenerator.init(len);//密钥长度
        SecretKey secretKey = keyGenerator.generateKey();//生成密钥
        return secretKey.getEncoded(); //密钥字节数组转字符串
    

6.1.3 AES
  • AES(Advanced Encryption Standard)是取代其前任标准(DES)而称为新标准的一种对称算法。
  • AES分组长度为128比特,密钥长度有128、192、256比特三种,AES-128、AES192和AES-256。
  • 至今还没有有效破解AES的方式

还是之前的代码,替换密钥值和加密算法即可

/**
 * AES加密算法测试
 */
public class AesTest 

    /**
     * 测试AES加密
     */
    @Test
    public void testEncrypt() throws Exception 
        //明文
        String text = "黑马程序员";
        //密钥,长度必须为16个字节(字符)
        byte[] secretKeyBytes = "123456781234

以上是关于,你就懂了信息安全的密码学的主要内容,如果未能解决你的问题,请参考以下文章

Oracle数据库为什么提示用户密码重置?看完你就懂了

非对称加密与安全证书看这一篇就懂了

大话「 非对称加密」,看完这篇你就懂了

看懂了这篇,你就懂了函数式接口

什么是机械可靠性设计方法,看完这里你就懂了!

看懂了这篇,你就懂了函数式接口