java关于base64加密与生成签名的一种思想,base64中=的含义

目录

前言:

1.base64都可以加密什么?

1.1 为什么图片用base64传输?

1.2 base64的原理之类?

 1.2.1 base64的由来?

1.2.2 编码原理

1.2.3 具体的转换步骤

1.2.4 可能面试会需要知道的

1.2.5 位数不足情况

1.2.6 注意事项

2. 图片文件转base64的代码分析

3. base64的生成签名的一种小小思想

3.1 Mac类与SecretKeySpec 类的简单介绍

3.2 Mac类

3.2.1 代码示例

3.2.2 Mac支持的算法

3.3 SecretKeySpec

3.1 实现的接口类KeySpec

3.2 SecretKey

3.3 Key

3.3.1 秘钥的概念

3.3.2 java的实现

前言:

我感觉做技术就应该去知道各种细节,还有原理。

多去想一些问题,总有人说我问题太多,

有些问题很奇怪,同事也都不懂。

但是我觉得有问题就有答案,还是要弄明白的。

而且在想一个问题的时候,往往我能想到好多延伸的问题。

这些问题呢,面试也不会问,而且我看了也不一定就记住了。

但是我还是想要去知道。

一个喜欢QA的工程师。

以后如果自己有足够的能力,一定要去写书,0 0貌似做做公众号也成。

活个明白。

1.base64都可以加密什么?

base并非只是用于加密图片的。

1.1 为什么图片用base64传输?

之前一直没有去思索这个问题,因为觉得好像是一种常识一般。

Q:为什么传输图片是常用base64字符串转码,而不是直接传输byte[]?

首先 1byte = 8 bit ,如果全部用byte[]数组会很长,所以每6个字节对应一个新的字符。

这样的目的是为了精简传输。

另外做不严格的加密用,就是常见的迅雷的磁力链接都是base64的,加密比较快,而且可以恢复。

英文字母:
·字节数 : 1;编码:GB2312

字节数 : 1;编码:GBK

字节数 : 1;编码:GB18030

字节数 : 1;编码:ISO-8859-1

字节数 : 1;编码:UTF-8

字节数 : 4;编码:UTF-16

字节数 : 2;编码:UTF-16BE

字节数 : 2;编码:UTF-16LE

中文汉字:
字节数 : 2;编码:GB2312

字节数 : 2;编码:GBK

字节数 : 2;编码:GB18030

字节数 : 1;编码:ISO-8859-1

字节数 : 3;编码:UTF-8

字节数 : 4;编码:UTF-16

字节数 : 2;编码:UTF-16BE

字节数 : 2;编码:UTF-16LE
一般我们用的UTF-8 都是中文占三个字节,英文是占一个的~

1.2 base64的原理之类?

1.2 这部分来自于:
版权声明:本文为CSDN博主「二师兄-公众号-程序新视界」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wo541075754/article/details/81734770

 1.2.1 base64的由来?

目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一。在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输。那么,Base64到底起到什么作用呢?

在参数传输的过程中经常遇到的一种情况:使用全英文的没问题,但一旦涉及到中文就会出现乱码情况。与此类似,网络上传输的字符并不全是可打印的字符,比如二进制文件、图片等。Base64的出现就是为了解决此问题,它是基于64个可打印的字符来表示二进制的数据的一种方法。

电子邮件刚问世的时候,只能传输英文,但后来随着用户的增加,中文、日文等文字的用户也有需求,但这些字符并不能被服务器或网关有效处理,因此Base64就登场了。随之,Base64在URL、Cookie、网页传输少量二进制文件中也有相应的使用。

图片对应的文件流对应的也是byte[]数组,所以图片转成base64是都经过了byte[] 数组对应的?

1.2.2 编码原理

Base64的原理比较简单,每当我们使用Base64时都会先定义一个类似这样的数组:

['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']

上面就是Base64的索引表,字符选用了"A-Z、a-z、0-9、+、/" 64个可打印字符,这是标准的Base64协议规定。在日常使用中我们还会看到“=”或“==”号出现在Base64的编码结果中,“=”在此是作为填充字符出现,后面会讲到。

Q: =号是什么?

Q:==号又是什么?

后面会有解答

1.2.3 具体的转换步骤

第一步,将待转换的字符串每三个字节分为一组,每个字节占8bit,那么共有24个二进制位。
第二步,将上面的24个二进制位每6个一组,共分为4组。
第三步,在每组前面添加两个0,每组由6个变为8个二进制位,总共32个二进制位,即四个字节。
第四步,根据Base64编码对照表(见下图)获得对应的值。
 

总结: 6个bit为一组,然后三个字节变成四个字节,因为每个字节前面的bit 都填了两个0.

然后找到base64编码对应的表

0 A  17 R   34 i   51 z

1 B  18 S   35 j   52 0

2 C  19 T   36 k   53 1

3 D  20 U   37 l   54 2

4 E  21 V   38 m   55 3

5 F  22 W   39 n   56 4

6 G  23 X   40 o   57 5

7 H  24 Y   41 p   58 6

8 I  25 Z   42 q   59 7

9 J  26 a   43 r   60 8

10 K  27 b   44 s   61 9

11 L  28 c   45 t   62 +

12 M  29 d   46 u   63 /

13 N  30 e   47 v

14 O  31 f   48 w   

15 P  32 g   49 x

16 Q  33 h   50 y
 

1.2.4 可能面试会需要知道的

Base64字符表中的字符原本用6个bit就可以表示,现在前面添加2个0,变为8个bit,会造成一定的浪费。因此,Base64编码之后的文本,要比原文大约三分之一。
为什么使用3个字节一组呢?因为6和8的最小公倍数为24,三个字节正好24个二进制位,每6个bit位一组,恰好能够分为4组。

因为6和8的最小公倍数为24.所以文本用base64来表示的话,其实会更大一点。

1.2.5 位数不足情况

上面是按照三个字节来举例说明的,如果字节数不足三个,那么该如何处理?

两个字节:两个字节共16个二进制位,依旧按照规则进行分组。此时总共16个二进制位,每6个一组,则第三组缺少2位,用0补齐,得到三个Base64编码,第四组完全没有数据则用“=”补上。因此,上图中“BC”转换之后为“QKM=”;
一个字节:一个字节共8个二进制位,依旧按照规则进行分组。此时共8个二进制位,每6个一组,则第二组缺少4位,用0补齐,得到两个Base64编码,而后面两组没有对应数据,都用“=”补上。因此,上图中“A”转换之后为“QQ==”;

不够的用0补,如果不到四个字节,用==来补充。

所以base64中的=号就这么出现的


1.2.6 注意事项

大多数编码都是由字符串转化成二进制的过程,而Base64的编码则是从二进制转换为字符串。与常规恰恰相反,
Base64编码主要用在传输、存储、表示二进制领域,不能算得上加密,只是无法直接看到明文。也可以通过打乱Base64编码来进行加密。
中文有多种编码(比如:utf-8、gb2312、gbk等),不同编码对应Base64编码结果都不一样。
 

2. 图片文件转base64的代码分析

public static String imageToBase64(File file) {
        byte[] data = null;
        try {
            InputStream in = new FileInputStream(file);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //不应该用类的实例访问静态资源,已更改
        return new String(Base64.encode(data));
    }

读取图片文件,所以文件流都是可以转成byte数组的,然后就可以使用base64的转码工具进行转码

 

3. base64的生成签名的一种小小思想

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * @Description : SignUtils
 * @Date : 2019/9/19
 * @Author : 
 */
public class SignUtils {

    public String generateSignature(String accessId, String base64Key, long timestamp) {
        String accessKey = new String(Base64.getDecoder().decode(base64Key.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        String signKey = accessId + "-" + timestamp;
        try {
            Mac hmacSha256 = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(accessKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            hmacSha256.init(secretKey);
            return Base64.getEncoder().encodeToString(hmacSha256.doFinal(signKey.getBytes(StandardCharsets.UTF_8)));
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            //这边应该使用log,应避免sout
            System.out.println(e.getMessage());
            return null;
        }
    }
}

accessId 是提前给客户生成好的。

然后,accessKey是使用base64加密过的key。

不过看完base64的原理之后,base都是通过byte数组来进行加密解密的。

所以这个加密是先通过Mac和 SecretKeySpec 来进行第一层加密,然后在对其进行第二层加密。

3.1 Mac类与SecretKeySpec 类的简单介绍

首先,看到这两个类的时候。

第一个时间也是点进去源码观察。

所属

Javax.crypto

加密操作 提供类和接口

3.2 Mac类

这部分参考+ 转载:https://blog.csdn.net/mn960mn/article/details/78174234

消息摘要(数字摘要),它是把一个文本/文件 通过摘要函数(hash函数)计算出一个结果。然后把文本/文件和摘要结果一同发给接受者
接受者接收到文件之后,也进行摘要,把两个摘要结果进行对比。如果一致就说明文本/文件和摘要是一致的


但是,这里有个问题,假设A把文件和摘要发给B,中途被C截获了。C把文件改了,同时把改后的文件进行摘要。然后把改后的文件和重新生成的摘要发给B。
B收到结果之后,进行摘要,对比发现,是一致的。但是此时文件是被篡改过的,B也不知道。接收方并不能察觉到数据被篡改。
 

所以说,普通的消息摘要不能验证身份和防篡改

为了解决这个问题,我们可以使用MAC(消息认证码(带密钥的hash函数))去解决

MAC,全称 Message Authentication Code,也称为消息认证码(带密钥的Hash函数),通信实体双方使用的一种验证机制,保证消息数据完整性的一种工具(防止被截获)

在发送数据之前,发送方首先使用通信双方协商好的散列函数计算其摘要值。在双方共享的会话密钥作用下,由摘要值获得消息验证码。之后,它和数据一起被发送。接收方收到报文后,首先利用会话密钥还原摘要值,同时利用散列函数在本地计算所收到数据的摘要值,并将这两个数据进行比对。若两者相等,则报文通过认证。

说白了就是计算摘要的时候,需要一个秘钥key,没有秘钥key就无法计算

注意:相同的消息,不同的key,摘要结果不同。

这也就是上面代码中的思想

Mac.getInstance支持的算法有:HmacMD5、HmacSHA1、HmacSHA256等等

全部支持的算法见官方文档:

 https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac

3.2.1 代码示例

PS:

SecretKeySpec secretKey = new SecretKeySpec(accessKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
hmacSha256.init(secretKey);

这个SecretKeySpec 是Key类的一种子类,所以Mac类需要使用init来初始化一个消息认证码。

这边的accessKey使用的是一个base64的key进行解码的一串字符串。

然后看下面的代码:

package com.dk.learndemo;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;


/**
 * @Description : MacTest
 * @Date : 2019/9/20
 * @Author :
 */
public class MacTest {

    //秘钥(必须要是通信双方共享的)
    static final String STR_KEY = "266f5fe18e714688a083df4ca9f78064";

    /**
     * 其中,Mac.getInstance支持的算法有:HmacMD5、HmacSHA1、HmacSHA256等等
     * 全部支持的算法见官方文档:
     * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac
     */
    public static byte[] mac(String algorithm, Key key, byte[] data) throws Exception {
        Mac mac = Mac.getInstance(algorithm);
        //这里是关键,需要一个key(这里就是和普通的消息摘要的区别点)
        mac.init(key);

        byte[] result = mac.doFinal(data);
        return result;
    }

    public static void main(String[] args) throws Exception {
        byte[] data = Files.readAllBytes(Paths.get("C:\\Users\\DK\\Downloads/apache-tomcat-8.5.45.tar.gz"));

        //这边的字符串填上或者不填有什么区别么 对于解密的时候有什么影响
        Key key = new SecretKeySpec(STR_KEY.getBytes(), "");

        //结果证明这边的第二个参数随便些什么都不会有影响。
        Key key2 = new SecretKeySpec(STR_KEY.getBytes(), "111");

        //使用MD5算法计算摘要
        byte[] md5Digest = mac("HmacMD5", key, data);

        //使用SHA256算法计算摘要
        byte[] shaDigest = mac("HmacSHA256", key, data);

        //这边是用Key2来计算摘要
        byte[] shaDigest2 = mac("HmacSHA256", key2, data);

        //把摘要后的结果转换成十六进制的字符串(也可以使用Base64进行编码)
        System.out.println(Hex.encodeHexString(md5Digest));
        System.out.println(Hex.encodeHexString(shaDigest));
        System.out.println(Hex.encodeHexString(shaDigest2));
    }
}

结果:

47011b8e70c69a77d4332dc62a6a2269
5b8c523b8f1c8302cb73eadb970217a1aae234cca96d362a90ece77c2cf41129
5b8c523b8f1c8302cb73eadb970217a1aae234cca96d362a90ece77c2cf41129

Key key2 = new SecretKeySpec(STR_KEY.getBytes(), "111");

这个字符串填什么并不影响结果,影响结果的是使用什么算法来计算摘要。

然后,我们可以使用OpenSSL,加上上面使用的秘钥key,计算摘要

 

对比结果,发现是一致的。

3.2.2 Mac支持的算法

Security.getAlgorithms("Mac").forEach(System.out::println);

结果:

 PBEWITHHMACSHA512
PBEWITHHMACSHA224
PBEWITHHMACSHA256
HMACSHA384
PBEWITHHMACSHA384
HMACSHA256
HMACPBESHA1
HMACSHA224
HMACMD5
PBEWITHHMACSHA1
SSLMACSHA1
HMACSHA512
SSLMACMD5
HMACSHA1

3.3 SecretKeySpec

关于这个类,个人理解这个类是Key类的一个子类

但是实际上看源码中,这个类的继承关系是:

public class SecretKeySpec implements KeySpec, SecretKey {}具体的方法:

3.1 实现的接口类KeySpec

/**
 *
 * <P> This interface contains no methods or constants. Its only purpose
 * is to group (and provide type safety for) all key specifications.
 * All key specifications must implement this interface.
 *
 * @author Jan Luehe
 *
 *
 * @see java.security.Key
 * @see java.security.KeyFactory
 * @see EncodedKeySpec
 * @see X509EncodedKeySpec
 * @see PKCS8EncodedKeySpec
 * @see DSAPrivateKeySpec
 * @see DSAPublicKeySpec
 *
 * @since 1.2
 */

public interface KeySpec { }

此接口不包含任何方法或常量。它的唯一目的对所有关键规范进行分组(并提供类型安全)。所有关键规范都必须实现此接口。

3.2 SecretKey

package javax.crypto;

import java.security.Key;
import javax.security.auth.Destroyable;

public interface SecretKey extends Key, Destroyable {
    long serialVersionUID = -4795878709595146952L;
}

这边是接口继承了接口。

Key也是一个接口

3.3 Key

3.3.1 秘钥的概念

密钥分两种:对称密钥和非对称密钥。

非对称密钥里又包含公开密钥和私有密钥。

与密钥相关的还有一个概念是证书。证书主要用于鉴别密钥,通常将公开密钥放到证书里传输。

Java的安全体系里,密钥是通过JCE算法包实现的。操作密钥的引擎包含两部分:密钥生成器和密钥工厂。密钥生成器可以创建密钥,而密钥工厂将其进行包装展示到外部。所以对于编写程序来说,创建密钥包括两个步骤:1,用密钥生成器产生密钥;2,用密钥工厂将其输出为一个密钥规范或者一组字节码。

3.3.2 java的实现

Java里将密钥封装了一个接口——Key。非对称密钥有PublicKey和PrivateKey,均实现了该接口。从之前的“安全提供者框架”中的输出结果可以看到,不同的安全提供者提供了很多密钥生成算法,比较典型的是Sun的DSA和RSA以及JCE的Diffie-Hellman算法。

具体内容看:https://www.cnblogs.com/jpfss/p/8574341.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值