数据加解密算法是信息安全领域的重要组成部分,它们用于保护数据的机密性、完整性和真实性。在实际应用中,综合使用数据加解密算法以构建多层次的安全防护体系。例如,HTTPS协议结合了对称加密(如AES)用于快速数据传输加密,以及非对称加密(如RSA)用于安全地交换对称密钥。同时,使用摘要算法确保数据完整性,并通过数字签名验证消息来源的合法性。这种组合方式既解决了数据的机密性问题,又保障了通信的完整性和认证性,是现代网络安全架构的基石。
算法分类
IV)等,以增加加密的安全性。MAC的主要目的是提供消息的完整性和认证。它不仅验证数据是否被篡改,还确保接收者能够验证消息确实来自已知的发送者。作用
使用场景
VPN、SSL/TLS加密通信。解决的问题
随机数生成算法是指用于生成看似随机数列的一系列算法,这些数列虽然在计算机中被称为“伪随机数”,因为它们实际上是通过确定性的算法计算出来的,但它们在统计特性上足够接近真正的随机性,从而适用于多种应用场景。计算机生成的随机数依赖于初始种子值,一个好的种子来源(如系统时间、硬件噪声等)能够增加随机数序列的不可预测性。
使用场景
OTP)用于账户安全。SSL/TLS握手过程中生成随机数。解决的问题
代表性算法
伪随机数生成器(PRNG)
:基于确定性算法,从一个随机种子开始生成看似随机的数列。常见的PRNG包括:
PRNG,以其周期长和随机性良好而著称。SHA-1哈希函数的PRNG,曾被用于生成加密密钥。真随机数生成器(TRNG)
:基于物理过程的随机性,如电子噪声或量子效应,来生成随机数。例如:
SHA-1 PRNG(Pseudorandom Number Generator)是一种基于SHA-1哈希算法的伪随机数生成器。它使用 SHA-1算法对种子值进行哈希,并使用哈希结果生成伪随机数序列。
特点
SHA-1 PRNG使用SHA-1哈希算法作为其核心算法,SHA-1在安全性上已经有一定的破解风险,因此SHA-1 PRNG也可能存在安全性问题。在对安全性要求较高的场景下,建议使用更安全的算法。SHA-1 PRNG生成的随机数序列具有很长的周期,通常情况下可以满足大多数应用的需求。但是,由于其基于SHA-1算法的特性,周期长度可能受到SHA-1哈希算法的限制。SHA-1 PRNG的速度较快,生成随机数的效率高,适用于大部分需要高质量随机数的应用场景。示例代码
x1public void testSHA1PRNG() throws NoSuchAlgorithmException {2 SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");3 byte[] randomBytes;4 for (int i = 0; i < 5; i++) {5 randomBytes = new byte[16];6 secureRandom.nextBytes(randomBytes);7 System.out.println("Random bytes: " + this.byteArrayToHexString(randomBytes));8 }9}1011private String byteArrayToHexString(byte[] bytes) {12 StringBuilder stringBuilder = new StringBuilder();13 for (byte b : bytes) {14 stringBuilder.append(String.format("%02x", b));15 }16 return stringBuilder.toString();17}在上述示例代码中,使用SHA1PRNG生成随机字节数组,然后将其转换为十六进制字符串后输出,运行并观察控制台输出:
xxxxxxxxxx51Random bytes: f5b548d8527a02de15492034499ea4762Random bytes: 7009d835283685b8a06b75eeeac2ea493Random bytes: 9eb62777f5ad603c5c9feb509e7545a64Random bytes: fb402efe9c121183ec7bee8529da49dd5Random bytes: d5060453969d094461d0d8823af13b45默认情况下,使用SHA1PRNG算法生成的数据都是随机的,重复执行上述的示例代码,每次生成的数据都是不同的,这是因为生成随机数的种子是变化的,如果对其设置一个固定的种子数据,同一个SecureRandom对象可以生成不同的随机数据,但是使用相同的种子重新创建SecureRandom对象后,生成的数据是一致的,即固定种子的随机数生成序列是固定的。
xxxxxxxxxx161public void testSHA1PRNG() throws NoSuchAlgorithmException {2 for (int i = 0; i < 3; i++) {3 SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");4 byte[] randomBytes = new byte[16];5 secureRandom.nextBytes(randomBytes);6 System.out.println("Random bytes: " + this.byteArrayToHexString(randomBytes));7 }8 System.out.println("==================");9 for (int i = 0; i < 3; i++) {10 SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");11 secureRandom.setSeed("jianggujin".getBytes());12 byte[] randomBytes = new byte[16];13 secureRandom.nextBytes(randomBytes);14 System.out.println("Random bytes: " + this.byteArrayToHexString(randomBytes));15 }16}运行调整后的示例代码,观察控制台输出可以看出固定种子后的数据变化。
xxxxxxxxxx71Random bytes: a1c88f17dec80195a2eeafbbee530a472Random bytes: a52dec60e560c48bcd7a5127948e531d3Random bytes: 52ab81956c4e097969dc4615ed20ad044==================5Random bytes: e929c5d3c2e8d63ed03c86bf2ec514c56Random bytes: e929c5d3c2e8d63ed03c86bf2ec514c57Random bytes: e929c5d3c2e8d63ed03c86bf2ec514c5信息编码算法是将信息(包括文本、图像、音频、视频等)转换成适合存储、处理、传输的数字形式的过程。编码的目标在于有效地利用有限的资源(如带宽、存储空间)并确保信息的完整性、安全性和高效性。编码不仅仅是简单的二进制转换,还涉及到数据压缩、错误检测与纠正、安全性增强等多个方面。
使用场景
解决的问题
代表性算法
URL编码是一种将URL中的特殊字符转换为可安全传输的ASCII字符串的方法。它将非 ASCII字符和特殊字符转换成%后跟两位十六进制数字的形式,以便在URL中进行传输。
使用场景
URL中传输包含特殊字符的数据时,需要进行URL编码以确保传输的安全性和准确性。URL时,需要对参数值进行URL编码,以避免解析错误和安全问题。Web表单提交中,对表单数据进行URL编码可以确保数据的完整性和正确性。示例代码
xxxxxxxxxx51String str = "蒋固金";2String encoded = URLEncoder.encode(str, "UTF-8");3System.out.println("Encoded URL: " + encoded);4String decoded = URLDecoder.decode(encoded, "UTF-8");5System.out.println("Decoded URL: " + decoded);xxxxxxxxxx51str := "蒋固金"2encoded := url.QueryEscape(str)3fmt.Println("Encoded URL:", encoded)4decoded, _ := url.QueryUnescape(encoded)5fmt.Println("Decoded URL:", decoded)运行并观察控制台输出:
xxxxxxxxxx21Encoded URL: %E8%92%8B%E5%9B%BA%E9%87%912Decoded URL: 蒋固金Base32是一种基于32个字符的编码方式,用于将二进制数据转换为可打印的ASCII字符串。它将每5个比特的二进制数据转换为一个字符,因此每个字符表示32种可能性,Base32字符集通常由大写字母A-Z和数字2-7组成。
特点
Base32编码生成的字符串只包含可打印字符,适用于将二进制数据表示为文本格式。Base32编码可以容忍少量数据损坏或错误,因为每个字符只包含一部分原始数据。Base32每个字符只能表示5个比特的数据,因此它的数据密度较低,编码后的字符串长度较长。使用场景:
Base32编码可用于URL中传输二进制数据,尤其是在需要避免使用非 ASCII字符的情况下。Base32编码可以将二进制数据以文本形式存储在文件或数据库中。Base32编码常用于计算数据的校验和,如CRC32校验等。示例代码
xxxxxxxxxx61String str = "jianggujin";2Base32 base32 = new Base32();3String encoded = base32.encodeAsString(str.getBytes(StandardCharsets.UTF_8));4System.out.println("Encoded string: " + encoded);5String decoded = new String(base32.decode(encoded), StandardCharsets.UTF_8);6System.out.println("Decoded string: " + decoded);注
Base32使用commons-codec提供的实现。
xxxxxxxxxx51<dependency>2 <groupId>commons-codec</groupId>3 <artifactId>commons-codec</artifactId>4 <version>1.15</version>5</dependency>xxxxxxxxxx51str := "jianggujin"2encoded := base32.StdEncoding.EncodeToString([]byte(str))3fmt.Println("Encoded string:", encoded)4decoded, _ := base32.StdEncoding.DecodeString(encoded)5fmt.Println("Decoded string:", string(decoded))运行并观察控制台输出:
xxxxxxxxxx21Encoded string: NJUWC3THM52WU2LO2Decoded string: jianggujinBase62编码是一种基于62个字符的编码方式,用于将二进制数据转换为可打印的ASCII字符串。它将每6个比特的二进制数据转换为一个字符,因此每个字符表示62种可能性,Base62字符集通常由大小写字母A-Z和数字0-9组成,共62个字符。。
特点
Base62编码生成的字符串长度相对较短,数据密度高,适用于需要短字符串表示较长数据的场景。Base62编码生成的字符串包含大小写字母和数字,易于人类阅读和理解。Base64编码。由于Base62使用了更少的字符,因此在编码相同数量的二进制数据时,理论上可以产生更短的文本字符串。这意味着Base62的数据密度更高,因为它可以用更少的字符表示更多的信息。使用场景
Base62编码可用于URL中传输二进制数据,尤其是在需要短链接的场景下。Base62编码常用于短链接服务,将长URL转换为短字符串以便分享和传播。Base62编码可用于将二进制数据以文本形式存储在文件或数据库中,节省存储空间并提高可读性。示例代码
xxxxxxxxxx51String str = "jianggujin";2String encoded = Base62.encode(str);3System.out.println("Encoded string: " + encoded);4String decoded = Base62.decodeStr(encoded);5System.out.println("Decoded string: " + decoded);注
Base62使用hutool提供的实现。
xxxxxxxxxx51<dependency>2 <groupId>cn.hutool</groupId>3 <artifactId>hutool-all</artifactId>4 <version>5.8.9</version>5</dependency>xxxxxxxxxx51str := "jianggujin"2encoded := base62.StdEncoding.EncodeToString([]byte(str))3fmt.Println("Encoded string:", encoded)4decoded, _ := base62.StdEncoding.DecodeString(encoded)5fmt.Println("Decoded string:", string(decoded))Go语言中未提供base62实现,可使用如下自定义实现。
xxxxxxxxxx1491package base6223import (4 "math"5 "reflect"6 "strconv"7 "unsafe"8)910const encodeStd = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"1112// StdEncoding is the standard base62 encoding.13var StdEncoding = NewEncoding(encodeStd)1415type CorruptInputError int641617func (e CorruptInputError) Error() string {18 return "illegal base62 data at input byte " + strconv.FormatInt(int64(e), 10)19}2021/*22 * Encodings23 */2425// An Encoding is a radix 62 encoding/decoding scheme, defined by a 62-character alphabet.26type Encoding struct {27 encode [62]byte28 decodeMap [256]byte29}3031// NewEncoding returns a new padded Encoding defined by the given alphabet,32// which must be a 62-byte string that does not contain the padding character33// or CR / LF ('\r', '\n').34func NewEncoding(encoder string) *Encoding {35 if len(encoder) != 62 {36 panic("encoding alphabet is not 62-bytes long")37 }38 for i := 0; i < len(encoder); i++ {39 if encoder[i] == '\n' || encoder[i] == '\r' {40 panic("encoding alphabet contains newline character")41 }42 }4344 e := new(Encoding)45 copy(e.encode[:], encoder)4647 for i := 0; i < len(e.decodeMap); i++ {48 e.decodeMap[i] = 0xFF49 }50 for i := 0; i < len(encoder); i++ {51 e.decodeMap[encoder[i]] = byte(i)52 }53 return e54}5556/*57 * Encoder58 */5960// Encode encodes src using the encoding enc.61func (enc *Encoding) Encode(src []byte) []byte {62 if len(src) == 0 {63 return nil64 }6566 // enc is a pointer receiver, so the use of enc.encode within the hot67 // loop below means a nil check at every operation. Lift that nil check68 // outside of the loop to speed up the encoder.69 _ = enc.encode7071 rs := 072 cs := int(math.Ceil(math.Log(256) / math.Log(62) * float64(len(src))))73 dst := make([]byte, cs)74 for i := range src {75 c := 076 v := int(src[i])77 for j := cs - 1; j >= 0 && (v != 0 || c < rs); j-- {78 v += 256 * int(dst[j])79 dst[j] = byte(v % 62)80 v /= 6281 c++82 }83 rs = c84 }85 for i := range dst {86 dst[i] = enc.encode[dst[i]]87 }88 if cs > rs {89 return dst[cs-rs:]90 }91 return dst92}9394// EncodeToString returns the base62 encoding of src.95func (enc *Encoding) EncodeToString(src []byte) string {96 buf := enc.Encode(src)97 return string(buf)98}99100/*101 * Decoder102 */103104// Decode decodes src using the encoding enc.105// If src contains invalid base62 data, it will return the106// number of bytes successfully written and CorruptInputError.107// New line characters (\r and \n) are ignored.108func (enc *Encoding) Decode(src []byte) ([]byte, error) {109 if len(src) == 0 {110 return nil, nil111 }112113 // Lift the nil check outside of the loop. enc.decodeMap is directly114 // used later in this function, to let the compiler know that the115 // receiver can't be nil.116 _ = enc.decodeMap117118 rs := 0119 cs := int(math.Ceil(math.Log(62) / math.Log(256) * float64(len(src))))120 dst := make([]byte, cs)121 for i := range src {122 if src[i] == '\n' || src[i] == '\r' {123 continue124 }125 c := 0126 v := int(enc.decodeMap[src[i]])127 if v == 255 {128 return nil, CorruptInputError(src[i])129 }130 for j := cs - 1; j >= 0 && (v != 0 || c < rs); j-- {131 v += 62 * int(dst[j])132 dst[j] = byte(v % 256)133 v /= 256134 c++135 }136 rs = c137 }138 if cs > rs {139 return dst[cs-rs:], nil140 }141 return dst, nil142}143144// DecodeString returns the bytes represented by the base62 string s.145func (enc *Encoding) DecodeString(s string) ([]byte, error) {146 sh := (*reflect.StringHeader)(unsafe.Pointer(&s))147 bh := reflect.SliceHeader{Data: sh.Data, Len: sh.Len, Cap: sh.Len}148 return enc.Decode(*(*[]byte)(unsafe.Pointer(&bh)))149}注
上述实现摘自https://github.com/deatil/go-encoding/blob/main/base62/base62.go
运行并观察控制台输出:
xxxxxxxxxx21Encoded string: 2VkxGnG6YAPIP02Decoded string: jianggujinBase64编码是一种将二进制数据转换为可打印ASCII字符串的编码方式。它将每6个比特的输入数据转换为一个可打印字符,以便于传输和存储,Base64字符集通常由大小写字母A-Z、a-z、数字0-9和两个额外的字符(通常是+和/)组成,共64个字符。
特点
Base64编码生成的字符串只包含可打印字符,适用于将二进制数据表示为文本格式。Base64每个字符只能表示6个比特的数据,因此它的数据密度较低,编码后的字符串长度较长。Base64编码可以容忍少量数据损坏或错误,因为每个字符只包含一部分原始数据。使用场景
Base64编码常用于在网络上传输二进制数据,如在邮件附件、图像、音频等数据的传输中。Base64编码可以将二进制数据以文本形式存储在文件或数据库中,方便存储和读取。Base64编码被用于表示密钥和签名等数据。示例代码
xxxxxxxxxx51String str = "jianggujin";2String encoded = Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));3System.out.println("Encoded string: " + encoded);4String decoded = new String(Base64.getDecoder().decode(encoded));5System.out.println("Decoded string: " + decoded);xxxxxxxxxx51str := "jianggujin"2encoded := base64.StdEncoding.EncodeToString([]byte(str))3fmt.Println("Encoded string:", encoded)4decoded, _ := base64.StdEncoding.DecodeString(encoded)5fmt.Println("Decoded string:", string(decoded))运行并观察控制台输出:
xxxxxxxxxx21Encoded string: amlhbmdndWppbg==2Decoded string: jianggujin提示
上述示例中使用的是标准的Base64模式,除了标准的Base64编码模式外,还有一些常见的变体模式,包括URL安全模式、MIME类型模式和无填充模式。这些模式在编码过程中会对字符集、填充方式等进行调整,以适应不同的使用场景。以下是它们之间的区别:
Base64编码中,使用字符+和/分别表示62和63,这些字符在URL中有特殊含义,需要进行转义。因此,URL安全模式将+和/替换为-和 _,以避免在URL中引起歧义。常用于在URL中传输Base64编码的数据,以避免URL转义和解码的问题。MIME类型模式在标准的Base64编码基础上,使用字符+和/表示 62和63,但是会在编码后的字符串末尾添加一个或两个=作为填充字符。常用于表示 MIME类型的数据,如邮件附件等。Base64编码中不使用填充字符=。这意味着编码后的字符串长度可能不是4的倍数。在一些特定场景下,需要节省空间或者不需要严格的长度要求时可以使用无填充模式。这些变体模式在编码和解码过程中的基本原理与标准的Base64编码相同,只是在字符集和填充方式上有所不同,以适应不同的使用场景。
十六进制编码是一种将数据转换为十六进制表示的编码方式。它将数据中的每个字节转换为两位十六进制数字表示,以便于传输和存储。在十六进制编码中,每个字节的高四位和低四位分别转换为对应的十六进制数字,例如,字节值0xAB被表示为AB。
特点
使用场景
示例代码
xxxxxxxxxx461private final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8',2 '9', 'a', 'b', 'c', 'd', 'e', 'f' };345public void testHex() throws Exception {6 String str = "jianggujin";7 String encoded = new String(encodeHex(str.getBytes(StandardCharsets.UTF_8)));8 System.out.println("Encoded string: " + encoded);9 String decoded = new String(decodeHex(encoded));10 System.out.println("Decoded string: " + decoded);11}1213private char[] encodeHex(byte[] data) {14 int l = data.length;15 char[] out = new char[l << 1];16 for (int i = 0, j = 0; i < l; i++) {17 out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];18 out[j++] = DIGITS_LOWER[0x0F & data[i]];19 }20 return out;21}2223private byte[] decodeHex(String data) {24 int len = data.length();25 if ((len & 0x01) != 0) {26 throw new IllegalArgumentException("Odd number of characters.");27 }28 byte[] out = new byte[len >> 1];29 for (int i = 0, j = 0; j < len; i++) {30 int f = toDigit(data.charAt(j), j) << 4;31 j++;32 f = f | toDigit(data.charAt(j), j);33 j++;34 out[i] = (byte) (f & 0xFF);35 }36 return out;37}3839private static int toDigit(char ch, int index) {40 final int digit = Character.digit(ch, 16);41 if (digit == -1) {42 throw new IllegalArgumentException("Illegal hexadecimal character " 43 + ch + " at index " + index);44 }45 return digit;46}xxxxxxxxxx51str := "jianggujin"2encoded := hex.EncodeToString([]byte(str))3fmt.Println("Encoded string:", encoded)4decoded, _ := hex.DecodeString(encoded)5fmt.Println("Decoded string:", string(decoded))运行并观察控制台输出:
xxxxxxxxxx21Encoded string: 6a69616e6767756a696e2Decoded string: jianggujin哈希算法,也称为散列函数或摘要算法,是一种将任意长度的输入数据(消息)映射为固定长度输出值(哈希值或摘要)的数学函数。哈希值通常较小,具有以下特性:输入敏感性(即使是微小的输入变化也会导致哈希值的巨大不同)、不可逆性(从哈希值很难或几乎不可能复原原始输入数据)、确定性(相同输入总是产生相同输出)、高效性(计算速度快)。哈希算法在设计上应尽量避免冲突,即不同的输入产生相同的输出(哈希碰撞),但实际上完全避免碰撞在数学上是不可能的,只能尽量降低其发生的概率。
使用场景
解决的问题
代表性算法
SHA-256、SHA-512等,是目前广泛使用的一系列安全哈希算法。SHA-2的后继者,基于不同的数学基础,旨在提供更长远的安全性。SHA-2算法。RIPEMD-160,曾被用在某些加密货币中。MD5(Message Digest Algorithm 5)是一种常用的哈希算法,用于产生消息摘要。它将任意长度的消息作为输入,经过一系列操作生成固定长度(128比特或16字节)的哈希值。MD5算法的核心原理是将输入的消息按照一定的规则分成若干个块,并对每个块进行一系列的位操作和模运算,最终生成一个128比特(16字节)的哈希值。MD5算法的设计是为了保证输入消息的微小变化会导致输出哈希值的大幅度变化,从而提高了哈希值的唯一性。
特点
MD5算法生成的哈希值固定长度为128比特,无论输入消息的长度如何。MD5算法是单向哈希算法,不可逆,即无法从哈希值反推出原始消息。MD5算法在计算哈希值时非常高效,适用于对大量数据进行哈希计算。MD5算法存在碰撞(两个不同的消息产生相同的哈希值)的概率。使用场景
MD5常用于校验文件完整性,通过比对文件的MD5哈希值来验证文件是否被篡改。MD5可以用于存储用户密码的哈希值,以增加密码的安全性。MD5可以用于生成消息摘要,用于数字签名和身份验证等场景。示例代码
xxxxxxxxxx41String str = "jianggujin";2MessageDigest md = MessageDigest.getInstance("MD5");3byte[] messageDigest = md.digest(str.getBytes(StandardCharsets.UTF_8));4System.out.println("MD5 hash: " + new String(encodeHex(messageDigest)));xxxxxxxxxx31str := "jianggujin"2encoded := md5.Sum([]byte(str))3fmt.Println("MD5 hash:", hex.EncodeToString(encoded[:]))运行并观察控制台输出:
xxxxxxxxxx11MD5 hash: acab4efdfd3b8efcdec37fe160d7be0eSHA(安全哈希算法)是一组密码哈希函数,用于产生消息的哈希值。SHA算法家族包括了多个版本,如SHA-1、SHA-256、SHA-512等,它们都是在输入数据上执行相似操作但输出长度不同的哈希函数。SHA算法基于密集型的位操作、逻辑运算和模运算。它将输入数据按照一定的方式分块,然后对每个块进行一系列的位运算和置换,最终产生一个固定长度的哈希值。
特点
SHA算法生成的哈希值长度不同,但每个版本的输出长度是固定的。SHA算法是单向哈希函数,不可逆,即无法从哈希值反推出原始消息。SHA算法在设计上具有很高的碰撞概率,即使输入数据发生微小变化,输出哈希值也会有显著差异。SHA算法是一种被广泛使用且安全可靠的哈希算法,通常用于数字签名、消息完整性校验等安全领域。使用场景
SHA算法常用于生成数字签名,用于验证数据的完整性和真实性。SHA算法可用于存储用户密码的哈希值,增加密码的安全性。SHA算法可用于消息认证码(HMAC)的计算,用于验证消息的真实性和完整性。SHA算法可用于校验文件完整性,如下载文件的哈希值与预期值进行比对。示例代码
xxxxxxxxxx101String str = "jianggujin";2MessageDigest md = MessageDigest.getInstance("SHA1");3byte[] messageDigest = md.digest(str.getBytes(StandardCharsets.UTF_8));4System.out.println("SHA1 hash: " + new String(encodeHex(messageDigest)));5md = MessageDigest.getInstance("SHA-256");6messageDigest = md.digest(str.getBytes(StandardCharsets.UTF_8));7System.out.println("SHA256 hash: " + new String(encodeHex(messageDigest)));8md = MessageDigest.getInstance("SHA-512");9messageDigest = md.digest(str.getBytes(StandardCharsets.UTF_8));10System.out.println("SHA512 hash: " + new String(encodeHex(messageDigest)));xxxxxxxxxx71str := "jianggujin"2encoded := sha1.Sum([]byte(str))3fmt.Println("SHA1 hash:", hex.EncodeToString(encoded[:]))4encoded256 := sha256.Sum256([]byte(str))5fmt.Println("SHA256 hash:", hex.EncodeToString(encoded256[:]))6encoded512 := sha512.Sum512([]byte(str))7fmt.Println("SHA512 hash:", hex.EncodeToString(encoded512[:]))运行并观察控制台输出:
xxxxxxxxxx31SHA1 hash: 26635165490065775f7edee885392424852da15a2SHA256 hash: e99631966983730acf4c1fa45669227980c19ef1f7fbea8fab2dc897b881cd303SHA512 hash: 6a677109e89384062e6078767417ed3176128451ecb91fae59bb3f407a2752579e6c99de14af00c7d8abacc78395cd587bfd945b9045f8ca227bb2fc8b140cdaSM3是中国国家密码管理局(中国密码学会)制定的一种密码哈希算法,用于产生消息的哈希值。它是中国政府采用的国家密码算法标准之一,具有自主知识产权。SM3算法基于分组密码结构,采用了类似于SHA-256的分组运算、置换运算和轮函数等步骤,但在设计上有一些独特之处。它将消息按照一定的规则分组,然后对每个分组进行一系列的位运算和置换操作,最终生成一个256位(32字节)的哈希值。
特点
SM3算法生成的哈希值长度固定为256比特(32字节)。SM3算法在设计上考虑了碰撞攻击,具有很高的抗碰撞能力。SM3是中国政府采用的国家密码算法标准,具有自主知识产权,被广泛应用于政府和企业信息安全领域。SM3算法在计算哈希值时性能高效,适用于大规模数据的哈希计算。使用场景
SM3算法可用于校验文件完整性,验证数据在传输过程中是否被篡改。SM3算法可用于生成数字签名,用于验证数据的完整性和真实性。SM3算法可用于密码学应用中的哈希计算和消息认证等场景。示例代码
xxxxxxxxxx51Security.addProvider(new BouncyCastleProvider());2String str = "jianggujin";3MessageDigest md = MessageDigest.getInstance("SM3");4byte[] messageDigest = md.digest(str.getBytes(StandardCharsets.UTF_8));5System.out.println("SM3 hash: " + new String(encodeHex(messageDigest)));注
Java默认未提供SM3实现,示例代码依赖bouncycastle。
xxxxxxxxxx51<dependency>2 <groupId>org.bouncycastle</groupId>3 <artifactId>bcprov-jdk15to18</artifactId>4 <version>1.72</version>5</dependency>xxxxxxxxxx31str := "jianggujin"2encoded := sm3.Sm3Sum([]byte(str))3fmt.Println("SM3 hash:", hex.EncodeToString(encoded))注
Go默认未提供SM3实现,示例代码依赖github.com/tjfoc/gmsm/sm3。
运行并观察控制台输出:
xxxxxxxxxx11SM3 hash: 26b306b040c8b1a31728baf37ac7fb88285356eb20f762a0e2ae9e00ff338dad消息认证码通常使用对称密钥加密算法,如HMAC(基于哈希的消息认证码)或CMAC(Cipher-based MAC)。消息认证码通常用于保护数据的完整性,防止篡改和伪造攻击。它可以确保消息在传输过程中没有被篡改或伪造,并且只有知道密钥的合法用户才能够验证消息的真实性。
使用场景
解决的问题
MAC可以确保数据在传输过程中没有被篡改或损坏。接收方可以使用相同的密钥和MAC算法对接收到的消息计算出一个认证标签,如果接收到的消息的认证标签与计算出的认证标签不一致,说明消息可能已经被篡改,从而保证了数据的完整性。MAC可以验证消息的真实性,确保消息来自于合法的发送方,并且没有被伪造。只有知道密钥的合法用户才能够计算出正确的MAC,从而验证消息的真实性。MAC通常与时间戳或随机数结合使用,以防止重放攻击。通过在消息中包含时间戳或随机数,并将其作为输入计算MAC,可以确保每个消息都是唯一的,防止攻击者重放之前的消息来欺骗系统。MAC可以用于验证用户的身份信息的真实性,确保用户具有相应的权限和权限。代表性算法
ChaCha20结合使用,用于实现高性能的加密和认证。HMAC(基于哈希的消息认证码)是一种用于验证消息完整性和真实性的密码学算法。它结合了哈希函数和密钥,用于生成一个固定长度的认证码,以确保消息的完整性和真实性。HMAC算法基于哈希函数和密钥的组合。它将消息和密钥作为输入,通过一系列的哈希计算生成一个固定长度的认证码。在计算过程中,消息被与一个内部的固定值(称为ipad)进行异或操作,然后再与另一个内部固定值(称为opad)进行连接,并且再次进行哈希计算,最终生成认证码。
特点
HMAC可以验证消息的完整性,确保消息在传输过程中没有被篡改。HMAC使用密钥来生成认证码,只有持有正确密钥的一方才能验证消息的真实性。HMAC基于哈希函数,具有很高的抗碰撞能力,即使输入消息发生微小变化,也会导致认证码的变化。HMAC可以使用多种哈希函数和不同长度的密钥,具有一定的灵活性和可配置性。使用场景
HMAC用于验证消息的真实性和完整性,防止消息被篡改或伪造。HMAC可以用于身份验证系统中生成和验证令牌,确保只有持有正确密钥的用户才能访问受保护的资源。HMAC可以用于生成数字签名,用于证明数据的来源和完整性。HMAC可以用于API认证中,确保只有经过授权的客户端才能访问API。示例代码
xxxxxxxxxx201byte[] key = "hmackey".getBytes(StandardCharsets.UTF_8);2String str = "jianggujin";3Mac mac = Mac.getInstance("HmacSHA1");4SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA1");5mac.init(secretKeySpec);6byte[] macBytes = mac.doFinal(str.getBytes(StandardCharsets.UTF_8));7String encoded = Base64.getEncoder().encodeToString(macBytes);8System.out.println("HMAC-SHA1: " + encoded);9mac = Mac.getInstance("HmacSHA256");10secretKeySpec = new SecretKeySpec(key, "HmacSHA256");11mac.init(secretKeySpec);12macBytes = mac.doFinal(str.getBytes(StandardCharsets.UTF_8));13encoded = Base64.getEncoder().encodeToString(macBytes);14System.out.println("HMAC-SHA256: " + encoded);15mac = Mac.getInstance("HmacSHA512");16secretKeySpec = new SecretKeySpec(key, "HmacSHA512");17mac.init(secretKeySpec);18macBytes = mac.doFinal(str.getBytes(StandardCharsets.UTF_8));19encoded = Base64.getEncoder().encodeToString(macBytes);20System.out.println("HMAC-SHA512: " + encoded);xxxxxxxxxx141key := []byte("hmackey")2str := "jianggujin"3hasher := hmac.New(sha1.New, key)4hasher.Write([]byte(str))5encoded := hasher.Sum(nil)6fmt.Println("HMAC-SHA1:", base64.StdEncoding.EncodeToString(encoded))7hasher = hmac.New(sha256.New, key)8hasher.Write([]byte(str))9encoded = hasher.Sum(nil)10fmt.Println("HMAC-SHA256:", base64.StdEncoding.EncodeToString(encoded))11hasher = hmac.New(sha512.New, key)12hasher.Write([]byte(str))13encoded = hasher.Sum(nil)14fmt.Println("HMAC-SHA512:", base64.StdEncoding.EncodeToString(encoded))运行并观察控制台输出:
xxxxxxxxxx31HMAC-SHA1: ArjjXjWOC4/DUfH+H5kwx60dkac=2HMAC-SHA256: 3D77xOtAm6FWkQOKepGH7C8duj5WZQ0WBawPUYMzmbA=3HMAC-SHA512: jtK/TWbD6EzngWlxA0FdRixVZR2npb9b5MbgSReTei9cO1A0Jd1TWVQ9mWUSXI5cbdz+mvhuvikFkPcOBXbm2A==相关信息
消息认证码(Message Authentication Code, MAC)算法与摘要运算(通常指的是哈希函数或散列函数)在表面上可能看起来相似,因为它们都从输入数据产生固定长度的输出,但实际上它们的设计目标和应用场景有所不同:
消息认证码(MAC)算法
MAC的主要目的是提供消息的完整性和认证。它不仅验证数据是否被篡改,还确保接收者能够验证消息确实来自已知的发送者。MAC算法在计算过程中需要使用一个密钥。这个密钥是预先共享的,只有合法的发送方和接收方知道,这使得MAC能够提供认证功能。MAC提供机密性之外的认证服务,意味着即使消息被截获,没有密钥的第三方无法伪造有效的MAC值。摘要运算(哈希函数)
MD5、SHA-1、SHA-256)在计算时不涉及密钥,是无密钥的函数。MAC是带有密钥的,提供消息认证和完整性检查,适合于需要验证消息来源的场景。哈希函数是无密钥的,仅提供完整性校验,不涉及认证,适用于不需要识别发送方身份的情景,如文件完整性验证。两者虽都能检验数据完整性,但MAC通过密钥的加入,额外提供了认证功能,这是它们之间最本质的区别。
对称加密算法是一种加密和解密过程使用相同密钥的加密技术。这意味着发送方和接收方必须事先共享同一把密钥,并且在保密的前提下保存好这把密钥。加密时,发送方使用该密钥将明文(即未加密的信息)转换为密文(即加密后的信息);解密时,接收方再利用相同的密钥将密文还原回明文。由于加解密使用的是相同的密钥,这类算法也被称作“秘密密钥算法”或“共享密钥算法”。
使用场景
解决的问题
代表性算法
128位的块大小和多种密钥长度(128、192、256位)为特点。56位有效密钥长度),已不再被认为是安全的。DES的一个改进,使用三个密钥对数据进行三次加密,以提高安全性。Bruce Schneier设计,是一个快速、高效的对称加密算法,适用于多种加密需求。Blowfish算法的后继者,提供更强的安全性和更大的密钥长度。ChaCha20的前身,也是一个流加密算法,以其简单和高效而闻名。异或加密是一种简单而古老的加密技术,也称为异或运算加密。它使用异或运算对明文和密钥进行位级别的操作,以生成密文。异或加密的原理非常简单,就是对明文和密钥的每一个比特进行异或运算。异或运算的规则是:如果两个比特相同,则结果为0;如果两个比特不同,则结果为 1。因此,当明文和密钥进行异或运算时,可以得到密文。
特点
使用场景
示例代码
xxxxxxxxxx151public void testXOR() throws Exception {2 byte[] key = "xorkey".getBytes(StandardCharsets.UTF_8);3 String str = "jianggujin";4 byte[] encoded = xor(str.getBytes(StandardCharsets.UTF_8), key);5 System.out.println("Encoded string: " + Base64.getEncoder().encodeToString(encoded));6 String decoded = new String(xor(encoded, key));7 System.out.println("Decoded string: " + decoded);8}910private byte[] xor(byte[] data, byte[] key) {11 for (int i = 0; i < data.length; i++) {12 data[i] ^= key[i % key.length];13 }14 return data;15}xxxxxxxxxx151func TestXOR(t *testing.T) {2 key := []byte("xorkey")3 str := "jianggujin"4 encoded := XOR([]byte(str), key)5 fmt.Println("Encoded string:", base64.StdEncoding.EncodeToString(encoded))6 decoded := XOR(encoded, key)7 fmt.Println("Decoded string:", string(decoded))8}910func XOR(data, key []byte) []byte {11 for i, v := range data {12 data[i] = v ^ key[i%len(key)]13 }14 return data15}运行并观察控制台输出:
xxxxxxxxxx21Encoded string: EgYTBQIeDQUbBQ==2Decoded string: jianggujinAES(Advanced Encryption Standard,高级加密标准)是一种对称密钥加密算法,被广泛应用于保护敏感数据的安全性。它是美国联邦政府采用的加密标准,也是目前最常用的对称加密算法之一。AES加密算法采用分组密码结构,将明文按照固定长度(128、192或256 位)进行分组,然后通过一系列的轮函数和密钥迭代,对每个分组进行加密处理,最终生成密文。AES共有10轮、12轮或14轮轮函数,取决于密钥长度(128、192或256 位)。
特点
AES加密算法采用了先进的分组密码结构和多轮加密过程,具有较高的安全性,抵抗常见的密码攻击。AES加密算法在硬件和软件上都有高效的实现,加密和解密速度快,适用于大规模数据的加密处理。AES支持多种密钥长度(128、192或256位),具有一定的灵活性和可配置性。AES是美国联邦政府采用的加密标准,被广泛应用于政府、企业和个人的信息安全领域。使用场景
AES可用于保护敏感数据的机密性,如用户密码、支付信息、个人身份信息等。AES可用于保护网络通信的安全性,如HTTPS、SSL/TLS等协议中的数据加密。AES可用于加密存储介质中的数据,如硬盘、数据库、云存储等。AES可用于生成数字签名,用于验证数据的完整性和真实性。在使用AES加密算法时,除了指定密钥长度外,还需要指定工作模式(Mode of Operation)和填充模式(Padding Scheme)。
工作模式(Mode of Operation)
工作模式定义了对待加密的数据块的方式,常见的工作模式包括:
ECB模式不提供数据的机密性和完整性保护。CFB模式,但是输出的是密钥流而不是密文块。IV)与计数器相结合产生密钥流,然后将明文与密钥流进行异或操作来加密数据。填充模式(Padding Scheme)
填充模式用于处理明文块的长度与加密算法要求的数据块长度不匹配的情况。常见的填充模式包括:
PKCS#5或PKCS#7标准的填充方案,将填充字节的值设置为填充的长度,解密时可通过填充长度来识别和去除填充。相关信息
PKCS5Padding和PKCS7Padding都是填充方案,用于在加密过程中对明文进行填充,以满足加密算法要求的数据块长度的整数倍。
主要区别
填充字节的值
:
适用范围
:
1到8字节之间(包含),因此适用于块长度为64位(8字节)的加密算法。1到255字节之间的加密算法。总体来说,PKCS7Padding是PKCS5Padding的一个超集,它可以处理更广泛的块长度范围。在实践中,PKCS7Padding更常见,并且在大多数情况下,PKCS7Padding也适用于 PKCS5Padding所适用的情况。
示例代码(AES/CBC/PKCS5Padding)
xxxxxxxxxx191byte[] key = "AD42F6697B035B7580E4FEF93BE20BAD".getBytes(StandardCharsets.UTF_8);2byte[] iv = "AD42F6697B035B75".getBytes(StandardCharsets.UTF_8);3String str = "jianggujin";4Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");5SecretKeySpec secretKey = new SecretKeySpec(key, "AES");6// 不需要设置向量的工作模式采用如下方式7// cipher.init(Cipher.ENCRYPT_MODE, secretKey);8cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));9byte[] encrypted = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));10System.out.println("Encrypted string: " + Base64.getEncoder().encodeToString(encrypted));1112cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");13secretKey = new SecretKeySpec(key, "AES");14// 不需要设置向量的工作模式采用如下方式15// cipher.init(Cipher.DECRYPT_MODE, secretKey);16cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));1718byte[] decrypted = cipher.doFinal(encrypted);19System.out.println("Decrypted string: " + new String(decrypted, StandardCharsets.UTF_8));xxxxxxxxxx641func TestAESCBCPKCS5Padding(t *testing.T) {2 key := []byte("AD42F6697B035B7580E4FEF93BE20BAD")3 iv := []byte("AD42F6697B035B75")4 str := "jianggujin"5 block, _ := aes.NewCipher(key)6 encrypted, _ := CBC(block, iv, []byte(str), true, aes.BlockSize)78 fmt.Println("Encrypted string:", base64.StdEncoding.EncodeToString(encrypted))9 decoded, _ := CBC(block, iv, encrypted, false, aes.BlockSize)10 fmt.Println("Decrypted string:", string(decoded))11}1213// cbc模式加解密14func CBC(block cipher.Block, iv []byte, in []byte, mode bool, blockSize int)15 (out []byte, err error) {16 var inData []byte17 if mode {18 inData = pkcs7Padding(in, blockSize)19 } else {20 inData = in21 }22 // 如后续出现IV受影响,则需要复制新的IV数据23 //iv := make([]byte, blockSize)24 //copy(iv, o.Iv)25 out = make([]byte, len(inData))2627 if mode {28 cipher.NewCBCEncrypter(block, iv).CryptBlocks(out, inData)29 } else {30 cipher.NewCBCDecrypter(block, iv).CryptBlocks(out, inData)31 out, err = pkcs7UnPadding(out, blockSize)32 }3334 return out, err35}3637// PKCS7Padding38// 假设每个区块大小为blockSize39// <1>已对齐,填充一个长度为blockSize且每个字节均为blockSize的数据。40// <2>未对齐,需要补充的字节个数为n,则填充一个长度为n且每个字节均为n的数据。41func pkcs7Padding(src []byte, blockSize int) []byte {42 padding := blockSize - len(src)%blockSize43 padtext := bytes.Repeat([]byte{byte(padding)}, padding)44 return append(src, padtext...)45}4647// 在解密后,需要将填充的字符去掉,取最后一位即知道存在多少个补充位48func pkcs7UnPadding(src []byte, blockSize int) ([]byte, error) {49 length := len(src)50 unpadding := int(src[length-1])51 if unpadding > blockSize || unpadding == 0 {52 return nil, errors.New("invalid pkcs7 padding (unpadding > BlockSize ||53 unpadding == 0)")54 }5556 pad := src[len(src)-unpadding:]57 for i := 0; i < unpadding; i++ {58 if pad[i] != byte(unpadding) {59 return nil, errors.New("invalid pkcs7 padding (pad[i] != unpadding)")60 }61 }6263 return src[:(length - unpadding)], nil64}运行并观察控制台输出:
xxxxxxxxxx21Encrypted string: H8dXZjZthY6iw+6hmjY71A==2Decrypted string: jianggujinDES(Data Encryption Standard,数据加密标准)是一种对称密钥加密算法,是最早广泛应用的分组密码算法之一。DES使用64位密钥对64位的数据块进行加密和解密,经过多轮的置换和替换操作,生成密文。DES加密算法采用分组密码结构,将明文按照固定长度(64 位)进行分组,然后通过一系列的置换、替换和轮函数,对每个分组进行加密处理,最终生成密文。DES共有16轮轮函数,每轮使用不同的子密钥对数据进行处理。
特点
DES曾经是加密领域的标准,但随着计算能力的增强,现已不再安全,易受到穷举搜索等攻击。DES的密钥长度固定为64位,其中8位用作奇偶校验,实际有效密钥长度为56位。DES只提供加密功能,没有提供完整性验证或认证功能。DES的加密和解密速度相对较快,适用于对称加密领域。使用场景
DES加密算法,需要维护和兼容。DES曾经是密码学领域的研究热点,对于学习密码学的学生和研究人员具有一定的参考意义。DES进行数据加密,如某些内部通信或测试系统。工作模式与填充模式参见
AES部分内容.
示例代码(DES/CBC/PKCS5Padding)
xxxxxxxxxx191byte[] key = "12345678".getBytes(StandardCharsets.UTF_8);2byte[] iv = "12345678".getBytes(StandardCharsets.UTF_8);3String str = "jianggujin";4Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");5SecretKeySpec secretKey = new SecretKeySpec(key, "DES");6// 不需要设置向量的工作模式采用如下方式7// cipher.init(Cipher.ENCRYPT_MODE, secretKey);8cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));9byte[] encrypted = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));10System.out.println("Encrypted string: " + Base64.getEncoder().encodeToString(encrypted));1112cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");13secretKey = new SecretKeySpec(key, "DES");14// 不需要设置向量的工作模式采用如下方式15// cipher.init(Cipher.DECRYPT_MODE, secretKey);16cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));1718byte[] decrypted = cipher.doFinal(encrypted);19System.out.println("Decrypted string: " + new String(decrypted, StandardCharsets.UTF_8));xxxxxxxxxx91key := []byte("12345678")2iv := []byte("12345678")3str := "jianggujin"4block, _ := des.NewCipher(key)5encrypted, _ := CBC(block, iv, []byte(str), true, des.BlockSize)67fmt.Println("Encrypted string:", base64.StdEncoding.EncodeToString(encrypted))8decoded, _ := CBC(block, iv, encrypted, false, des.BlockSize)9fmt.Println("Decrypted string:", string(decoded))运行并观察控制台输出:
xxxxxxxxxx21Encrypted string: olFmr5i2TfrLnA5c1mz3pA==2Decrypted string: jianggujin3DES(Triple Data Encryption Standard)是对DES加密算法的改进版本,通过对数据应用三次DES加密来提高安全性。它采用了三个不同的密钥对数据进行加密和解密,从而增强了加密的强度和安全性。在3DES中包含了两种不同的加密模式:双倍长密钥模式(Two-Key Triple DES)和三倍长密钥模式(Three-Key Triple DES)。
56位的密钥,分别作为第一轮和第三轮的密钥,而第二轮的密钥与第一轮相同。56位的密钥,分别作为三轮的密钥。特点
3DES使用了三次DES加密,比单次DES加密更加安全,对抗穷举搜索攻击的能力更强。3DES兼容DES加密算法,可以在不修改现有系统的情况下进行升级。3DES是DES的直接后继者,保留了DES的许多特点和优点,同时增加了更高的安全性。DES加密和解密操作,3DES的性能较低,不适合在对性能要求较高的场景中使用。使用场景
DES加密算法的遗留系统,可以通过将DES替换为 3DES 来提高安全性。3DES加密算法来保护敏感数据。3DES作为经典的加密算法,仍然具有一定的研究价值,用于密码学领域的学术研究和教学。工作模式与填充模式参见
AES部分内容.
示例代码(DESede/CBC/PKCS5Padding)
xxxxxxxxxx191byte[] key = "123456781234567812345678".getBytes(StandardCharsets.UTF_8);2byte[] iv = "12345678".getBytes(StandardCharsets.UTF_8);3String str = "jianggujin";4Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");5SecretKeySpec secretKey = new SecretKeySpec(key, "DESede");6// 不需要设置向量的工作模式采用如下方式7// cipher.init(Cipher.ENCRYPT_MODE, secretKey);8cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));9byte[] encrypted = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));10System.out.println("Encrypted string: " + Base64.getEncoder().encodeToString(encrypted));1112cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");13secretKey = new SecretKeySpec(key, "DESede");14// 不需要设置向量的工作模式采用如下方式15// cipher.init(Cipher.DECRYPT_MODE, secretKey);16cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));1718byte[] decrypted = cipher.doFinal(encrypted);19System.out.println("Decrypted string: " + new String(decrypted, StandardCharsets.UTF_8));xxxxxxxxxx91key := []byte("123456781234567812345678")2iv := []byte("12345678")3str := "jianggujin"4block, _ := des.NewTripleDESCipher(key)5encrypted, _ := CBC(block, iv, []byte(str), true, des.BlockSize)67fmt.Println("Encrypted string:", base64.StdEncoding.EncodeToString(encrypted))8decoded, _ := CBC(block, iv, encrypted, false, des.BlockSize)9fmt.Println("Decrypted string:", string(decoded))运行并观察控制台输出:
xxxxxxxxxx21Encrypted string: olFmr5i2TfrLnA5c1mz3pA==2Decrypted string: jianggujinSM4是一种分组密码算法,也称为国密算法,由中国国家密码管理局设计并公开的。它是一种对称加密算法,适用于数据加密和解密,以及数据完整性验证等场景。SM4算法基于Feistel网络结构,具有32轮加密和解密过程。它采用了非线性变换、置换运算和密钥混合技术,通过对数据块的多次迭代处理,实现数据的加密和解密。
特点
SM4采用了混沌运算和S盒代替传统的置换运算,增强了算法的非线性特性,提高了安全性。SM4算法具有较高的运算速度,适用于大规模数据加密和解密的场景。SM4算法已被ISO/IEC标准化,并被广泛应用于政府、金融、电信等领域。SM4算法是国家自主设计的密码算法,具有自主可控的特点。使用场景
SM4算法是中国国家密码管理局发布的国家密码算法,适用于政府和军事领域的数据加密和解密。SM4算法已被广泛应用于金融、电信等领域的数据加密和安全通信。SM4算法可以用于保护数据的安全性。工作模式与填充模式参见
AES部分内容.
示例代码(SM4/CBC/PKCS5Padding)
xxxxxxxxxx201Security.addProvider(new BouncyCastleProvider());2byte[] key = "1234567812345678".getBytes(StandardCharsets.UTF_8);3byte[] iv = "1234567812345678".getBytes(StandardCharsets.UTF_8);4String str = "jianggujin";5Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding");6SecretKeySpec secretKey = new SecretKeySpec(key, "SM4");7// 不需要设置向量的工作模式采用如下方式8// cipher.init(Cipher.ENCRYPT_MODE, secretKey);9cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));10byte[] encrypted = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));11System.out.println("Encrypted string: " + Base64.getEncoder().encodeToString(encrypted));1213cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding");14secretKey = new SecretKeySpec(key, "SM4");15// 不需要设置向量的工作模式采用如下方式16// cipher.init(Cipher.DECRYPT_MODE, secretKey);17cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));1819byte[] decrypted = cipher.doFinal(encrypted);20System.out.println("Decrypted string: " + new String(decrypted, StandardCharsets.UTF_8));注
Java默认未提供SM4实现,示例代码依赖bouncycastle。
xxxxxxxxxx51<dependency>2 <groupId>org.bouncycastle</groupId>3 <artifactId>bcprov-jdk15to18</artifactId>4 <version>1.72</version>5</dependency>xxxxxxxxxx91key := []byte("1234567812345678")2iv := []byte("1234567812345678")3str := "jianggujin"4block, _ := sm4.NewCipher(key)5encrypted, _ := CBC(block, iv, []byte(str), true, sm4.BlockSize)67fmt.Println("Encrypted string:", base64.StdEncoding.EncodeToString(encrypted))8decoded, _ := CBC(block, iv, encrypted, false, sm4.BlockSize)9fmt.Println("Decrypted string:", string(decoded))注
Go默认未提供SM4实现,示例代码依赖github.com/tjfoc/gmsm/sm4。
运行并观察控制台输出:
xxxxxxxxxx21Encrypted string: KastFVtO8pcdAWTmGs/s1Q==2Decrypted string: jianggujin非对称加密算法是一种加密和解密过程使用不同密钥的加密技术,它需要一对密钥:公开密钥(公钥)和私有密钥(私钥)。公钥可以公开给任何人,用于加密信息,而私钥必须保密,仅由信息的接收者持有,用于解密信息。这种机制解决了密钥分发和安全通信的难题,因为即使公钥广为人知,没有对应的私钥也无法解密信息。非对称加密算法的加密过程和解密过程是数学上相关的,但直接从公钥推算出私钥在计算上是不可行的,这确保了系统的安全性。
使用场景
解决的问题
代表性算法
Ron Rivest、Adi Shamir和Leonard Adleman发明,是最早也是最知名的非对称加密算法之一,广泛应用于数字签名和密钥交换。RSA,使用更短的密钥长度就能达到相同的安全级别,适用于资源受限的环境。DH密钥交换协议的扩展。RSA是一种非对称加密算法,是目前应用最广泛的公钥加密算法之一。RSA算法基于数论中的大数分解问题,其安全性依赖于大整数分解的困难性。RSA算法基于两个大素数的乘积作为公钥的模数,公钥由模数和指数组成,私钥由模数和另一个指数组成。加密过程使用公钥对数据进行加密,解密过程使用私钥对密文进行解密。RSA算法的安全性基于大数分解问题的困难性,即给定一个大整数N,找到两个大素数p和q,使得N = p * q,是一个困难的问题。
特点
RSA算法使用不同的密钥进行加密和解密,公钥用于加密,私钥用于解密,具有较高的安全性。RSA算法不仅可以用于加密数据,还可以用于生成和验证数字签名,确保数据的完整性和来源可信性。RSA算法支持不同长度的密钥,密钥越长,安全性越高,但加密和解密速度越慢。RSA算法被广泛应用于安全通信、数字签名、身份认证等领域。使用场景
RSA算法可以用于保护通信中的数据隐私和完整性,例如SSL/TLS、SSH 等安全协议。RSA算法可以用于生成和验证数字签名,确保数据的完整性和来源可信性,例如数字证书、电子邮件签名等。RSA算法可以用于安全地交换对称加密算法的密钥,例如在Diffie-Hellman密钥交换中使用。RSA算法可以用于实现身份认证功能,例如在公钥基础设施(PKI)中用于用户身份验证。示例代码(生成密钥)
xxxxxxxxxx51KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");2keyPairGen.initialize(1024, new SecureRandom());3KeyPair pair = keyPairGen.generateKeyPair();4System.out.println("Private: " + Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded()));5System.out.println("Public: " + Base64.getEncoder().encodeToString(pair.getPublic().getEncoded()));xxxxxxxxxx61key, _ := rsa.GenerateKey(rand.Reader, 1024)2out, _ := x509.MarshalPKCS8PrivateKey(key)3fmt.Println("Private:", base64.StdEncoding.EncodeToString(out))4publicKey := key.PublicKey5out, _ = x509.MarshalPKIXPublicKey(&publicKey)6fmt.Println("Public:", base64.StdEncoding.EncodeToString(out))示例代码(加解密)
xxxxxxxxxx171X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(PUBLIC_KEY));2KeyFactory keyFactory = KeyFactory.getInstance("RSA");3PublicKey publicKey = keyFactory.generatePublic(keySpec);4Cipher cipher = Cipher.getInstance("RSA");5cipher.init(Cipher.ENCRYPT_MODE, publicKey);67String str = "jianggujin";8byte[] encrypted = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));9System.out.println("Encrypted string: " + Base64.getEncoder().encodeToString(encrypted));1011PKCS8EncodedKeySpec pkcs8KeySpec = 12 new PKCS8EncodedKeySpec(Base64.getDecoder().decode(PRIVATE_KEY));13PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);14cipher = Cipher.getInstance("RSA");15cipher.init(Cipher.DECRYPT_MODE, privateKey);16byte[] decrypted = cipher.doFinal(encrypted);17System.out.println("Decrypted string: " + new String(decrypted, StandardCharsets.UTF_8));xxxxxxxxxx111str := "jianggujin"2pubKey, _ := base64.StdEncoding.DecodeString(PUBLIC_KEY)3publicKey, _ := x509.ParsePKIXPublicKey(pubKey)4encrypted, _ := rsa.EncryptPKCS1v15(rand.Reader, publicKey.(*rsa.PublicKey), []byte(str))5fmt.Println("Encrypted string:", base64.StdEncoding.EncodeToString(encrypted))67priKey, _ := base64.StdEncoding.DecodeString(PRIVATE_KEY)8privateKey, _ := x509.ParsePKCS8PrivateKey(priKey)910decrypted, _ := rsa.DecryptPKCS1v15(rand.Reader, privateKey.(*rsa.PrivateKey), encrypted)11fmt.Println("Decrypted string:", string(decrypted))示例代码(签名验签)
xxxxxxxxxx171String str = "jianggujin";2KeyFactory keyFactory = KeyFactory.getInstance("RSA");3PKCS8EncodedKeySpec pkcs8KeySpec = 4 new PKCS8EncodedKeySpec(Base64.getDecoder().decode(PRIVATE_KEY));5PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);6Signature signature = Signature.getInstance("SHA256withRSA");7signature.initSign(privateKey);8signature.update(str.getBytes(StandardCharsets.UTF_8));9byte[] signed = signature.sign();10System.out.println("Signed string: " + Base64.getEncoder().encodeToString(signed));1112X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(PUBLIC_KEY));13PublicKey publicKey = keyFactory.generatePublic(keySpec);14signature = Signature.getInstance("SHA256withRSA");15signature.initVerify(publicKey);16signature.update(str.getBytes(StandardCharsets.UTF_8));17System.out.println("Verify: " + signature.verify(signed));xxxxxxxxxx111str := "jianggujin"2priKey, _ := base64.StdEncoding.DecodeString(PRIVATE_KEY)3privateKey, _ := x509.ParsePKCS8PrivateKey(priKey)4hashed := sha256.Sum256([]byte(str))5signed, _ := rsa.SignPKCS1v15(rand.Reader, privateKey.(*rsa.PrivateKey), crypto.SHA256, hashed[:])6fmt.Println("Signed string:", base64.StdEncoding.EncodeToString(signed))78pubKey, _ := base64.StdEncoding.DecodeString(PUBLIC_KEY)9publicKey, _ := x509.ParsePKIXPublicKey(pubKey)10fmt.Println("Encrypted string:", rsa.VerifyPKCS1v15(publicKey.(*rsa.PublicKey),11 crypto.SHA256, hashed[:], signed) == nil)相关信息
签名算法是一种用于确保数据完整性和认证数据来源的密码学技术。它通常与加密算法结合使用,用于生成数字签名,以便验证数据的真实性和完整性。下面是签名算法的详细介绍和说明:
基本原理
签名算法基于公钥加密和私钥解密的原理。发送方使用私钥对数据进行签名,接收方使用发送方的公钥来验证签名。如果数据在传输过程中被篡改,签名验证将失败,因为篡改后的数据与原始签名不匹配。
加密与签名的区别
虽然加密和签名都使用了公钥和私钥,但它们的目的和机制有所不同。加密是为了保护数据的隐私性,而签名是为了确保数据的完整性和认证数据来源。
签名的过程
验证签名的过程
SM2是一种基于椭圆曲线密码学(ECC)的国密算法,由中国密码学家提出,用于数字签名、密钥交换、公钥加密等场景。SM2算法是中国政府采用的国家密码算法标准之一。SM2算法基于椭圆曲线离散对数问题,其核心原理是在椭圆曲线上进行点运算,实现数字签名、密钥交换等功能。SM2算法采用了一套基于国际标准的椭圆曲线参数,具有较高的安全性和效率。
特点
SM2算法是中国政府采用的国家密码算法标准,具有较高的安全性和可信度。SM2算法基于椭圆曲线密码学,具有较高的安全性和效率。SM2算法支持数字签名功能,可以用于数据认证、身份认证等场景。SM2算法支持密钥交换功能,可以安全地交换密钥,用于加密通信。SM2算法具有较高的运算效率,适用于大规模数据的加密和解密。使用场景
SM2算法可以用于安全通信场景,如SSL/TLS协议中的数字签名和密钥交换过程。SM2算法可以用于数字签名场景,如电子合同签署、电子证书认证等。SM2算法可以用于密钥交换场景,如密钥协商、密钥派生等。SM2算法的公钥和私钥有多种格式类型,每种格式都有其特定的用途和特点。常用格式与说明如下:
私钥格式
PKCS#1格式,这是一种较为传统的RSA私钥格式,但也可以用在ECC上。PKCS#8是一种更通用的私钥表示方式,它包含了私钥以及一些额外的信息,如算法标识符。公钥格式
04开头,后跟两个256位数字;一个用于点的x坐标,另一个用于点的y坐标。04,只包含x和y坐标的值。ASN.1 DER编码格式表示,这种格式在证书和某些加密系统中广泛使用。PKCS#8格式,公钥也可以使用PKCS#8格式,它同样包含了公钥以及一些算法参数和标识符。相关信息
在讨论SM2算法时,经常会提到的D值、Q值(压缩与未压缩)、Q值x、Q值y,这些术语与椭圆曲线密码学(ECC)中的点表示和密钥生成过程相关。基本解释如下:
D值:在椭圆曲线密码学中,D值通常指的是私钥。它是用户选择的一个随机大整数,用来与椭圆曲线上的一点进行点乘运算,从而得到对应的公钥点。在SM2算法中,D值是私钥的表示,用于签名和解密操作。示例:761cdbbaeb1203adbf2afce9777ff6c247c8f30454055329d518bb429f330b99。
Q值(压缩与未压缩):Q值通常指的是公钥,在椭圆曲线密码学中,公钥是由椭圆曲线上的一点表示的。这个点可以以两种形式存储或传输:
x坐标和y坐标,通常会有额外的字节来标识是哪个坐标在哪个半平面(通常是通过一个额外的字节,如0x04,后面跟着x和y坐标)。示例:04641B729BB329D780B853DA48B9755015B6C7ABE21EF9B61DF4D855BC1CBE41114C34776539182D0DFA6CA002959C72F2B6BE1B05DE5DD1A41C47F650DB780BB9。x坐标,并且根据y坐标的奇偶性添加一个额外的字节(0x02表示y是偶数,0x03表示y是奇数)。这样可以节省存储空间,尤其是在带宽敏感的应用中。示例:03641B729BB329D780B853DA48B9755015B6C7ABE21EF9B61DF4D855BC1CBE4111。Q值x:指的是公钥点在压缩或未压缩格式中明确表示的x坐标。它是椭圆曲线上点的横坐标部分。示例:641B729BB329D780B853DA48B9755015B6C7ABE21EF9B61DF4D855BC1CBE4111。
Q值y:指的是公钥点的y坐标。在未压缩格式的公钥表示中会明确包括y坐标,而在压缩格式中,y坐标不直接给出,但可以通过x坐标和椭圆曲线方程计算得到。示例:4C34776539182D0DFA6CA002959C72F2B6BE1B05DE5DD1A41C47F650DB780BB9。
总的来说,D值是私钥,Q值代表公钥,而Q值x和Q值y分别是公钥点在坐标平面上的x坐标和y坐标,它们根据压缩与否的不同格式,可能以不同的方式表示或传输。
加密数据格式
SM2加密后的数据通常包含三个部分:随机产生的公钥C1、密文C2和使用SM3算法计算得到的消息摘要C3。ASN.1格式表示,这在某些硬件加密机中使用。相关信息
SM2加密数据最初使用C1C2C3格式数据,在新版标准中为C1C3C2格式,在实际使用中需要注意区分并在必要时进行数据转换。Java中常用的bouncycastle默认使用的格式为C1C2C3。
签名数据格式
SM2签名通常由两个部分组成,R和S,它们直接拼接在一起表示。ASN.1格式定义,这符合国标(GM/T 0009-2012)规定。示例代码(生成密钥)
xxxxxxxxxx141Security.addProvider(new BouncyCastleProvider());2X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");3ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), 4 x9ECParameters.getG(), x9ECParameters.getN());5KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC", "BC");6keyPairGen.initialize(ecParameterSpec, new SecureRandom());7KeyPair pair = keyPairGen.generateKeyPair();8BCECPrivateKey privateKey = (BCECPrivateKey) pair.getPrivate();9BCECPublicKey publicKey = (BCECPublicKey) pair.getPublic();10System.out.println("D值: " + privateKey.getD().toString(16));11System.out.println("Q值(未压缩): " + new String(encodeHex(publicKey.getQ().getEncoded(false))));12System.out.println("Q值(压缩): " + new String(encodeHex(publicKey.getQ().getEncoded(true))));13System.out.println("Q值X: " + publicKey.getQ().getAffineXCoord());14System.out.println("Q值Y: " + publicKey.getQ().getAffineYCoord());注
Java默认未提供SM2实现,示例代码依赖bouncycastle。
xxxxxxxxxx51<dependency>2 <groupId>org.bouncycastle</groupId>3 <artifactId>bcprov-jdk15to18</artifactId>4 <version>1.72</version>5</dependency>xxxxxxxxxx41key, _ := sm2.GenerateKey(rand.Reader)2fmt.Println("D值:", key.D.Text(16))3fmt.Println("Q值X:", key.X.Text(16))4fmt.Println("Q值Y:", key.Y.Text(16))注
Go默认未提供SM2实现,示例代码依赖github.com/tjfoc/gmsm/sm2。
示例代码(逆向公私钥)
xxxxxxxxxx581X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");2ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(),3 x9ECParameters.getG(), x9ECParameters.getN());45String priKey = "MIICSwIBADCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////v////////////////////8AAAAA" +6 "//////////8wRAQg/////v////////////////////8AAAAA//////////wEICjp" +7 "+p6dn140TVqeS89lCafzl4n1FauPkt28vUFNlA6TBEEEMsSuLB8ZgRlfmQRGajnJlI/jC7" +8 "/yZgvhcVpFiTNMdMe8Nzai9PZ3nFm9zuNraSFT0KmHfMYqR0AC3zLlITnwoAIhAP////7" +9 "///////////////9yA99rIcYFK1O79Ak51UEjAgEBBIIBVTCCAVECAQEEIHYc27rrEgOtvyr86Xd" +10 "/9sJHyPMEVAVTKdUYu0KfMwuZoIHjMIHgAgEBMCwGByqGSM49AQECIQD////+/////////////////////wAAAAD" +11 "//////////zBEBCD////+/////////////////////wAAAAD//////////AQgKOn6np2fXjRNWp5Lz2UJp/OXifUVq4" +12 "+S3by9QU2UDpMEQQQyxK4sHxmBGV+ZBEZqOcmUj+MLv/JmC" +13 "+FxWkWJM0x0x7w3NqL09necWb3O42tpIVPQqYd8xipHQALfMuUhOfCgAiEA/////v" +14 "///////////////3ID32shxgUrU7v0CTnVQSMCAQGhRANCAARkG3KbsynXgLhT2ki5dVAVtser4h75th302FW8HL5BEUw0d2U5GC0N+mygApWccvK2vhsF3l3RpBxH9lDbeAu5";15String pubKey = "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////v////////////////////8AAAAA" +16 "//////////8wRAQg/////v////////////////////8AAAAA//////////wEICjp" +17 "+p6dn140TVqeS89lCafzl4n1FauPkt28vUFNlA6TBEEEMsSuLB8ZgRlfmQRGajnJlI/jC7" +18 "/yZgvhcVpFiTNMdMe8Nzai9PZ3nFm9zuNraSFT0KmHfMYqR0AC3zLlITnwoAIhAP////7" +19 "///////////////9yA99rIcYFK1O79Ak51UEjAgEBA0IABGQbcpuzKdeAuFPaSLl1UBW2x6viHvm2HfTYVbwcvkERTDR3ZTkYLQ36bKAClZxy8ra+GwXeXdGkHEf2UNt4C7k=";20String X = "641b729bb329d780b853da48b9755015b6c7abe21ef9b61df4d855bc1cbe4111";21String Y = "4c34776539182d0dfa6ca002959c72f2b6be1b05de5dd1a41c47f650db780bb9";22String Q = "04641B729BB329D780B853DA48B9755015B6C7ABE21EF9B61DF4D855BC1CBE41114C34776539182D0DFA6CA002959C72F2B6BE1B05DE5DD1A41C47F650DB780BB9";23//String Q = "03641B729BB329D780B853DA48B9755015B6C7ABE21EF9B61DF4D855BC1CBE4111";2425String D = "761cdbbaeb1203adbf2afce9777ff6c247c8f30454055329d518bb429f330b99";26// 尝试D值27ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(new BigInteger(D, 16), ecParameterSpec);28BCECPrivateKey privateKey = new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);29System.out.println("私钥: " + privateKey.getD().toString(16));3031PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(priKey));32KeyFactory keyFactory = KeyFactory.getInstance("EC");33privateKey = (BCECPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);34System.out.println("私钥: " + privateKey.getD().toString(16));3536// 尝试Q值37ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(38x9ECParameters.getCurve().decodePoint(HexUtil.decodeHex(Q)), ecParameterSpec);39BCECPublicKey publicKey = new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);40System.out.println("公钥X:" + publicKey.getQ().getAffineXCoord());41System.out.println("公钥Y:" + publicKey.getQ().getAffineYCoord());42System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey.getQ().getEncoded(false)).toUpperCase());43System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey.getQ().getEncoded(true)).toUpperCase());4445X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey));46publicKey = (BCECPublicKey) keyFactory.generatePublic(keySpec);47System.out.println("公钥X:" + publicKey.getQ().getAffineXCoord());48System.out.println("公钥Y:" + publicKey.getQ().getAffineYCoord());49System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey.getQ().getEncoded(false)).toUpperCase());50System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey.getQ().getEncoded(true)).toUpperCase());5152ecPublicKeySpec = new ECPublicKeySpec(53 x9ECParameters.getCurve().createPoint(new BigInteger(X, 16), new BigInteger(Y, 16)), ecParameterSpec);54publicKey = new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);55System.out.println("公钥X:" + publicKey.getQ().getAffineXCoord());56System.out.println("公钥Y:" + publicKey.getQ().getAffineYCoord());57System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey.getQ().getEncoded(false)).toUpperCase());58System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey.getQ().getEncoded(true)).toUpperCase());示例代码(加解密)
xxxxxxxxxx131PublicKey publicKey = null; // 参考上面内容获取2Cipher cipher = Cipher.getInstance("SM2");3cipher.init(Cipher.ENCRYPT_MODE, publicKey);45String str = "jianggujin";6byte[] encrypted = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));7System.out.println("Encrypted string: " + Base64.getEncoder().encodeToString(encrypted));89PrivateKey privateKey = null; // 参考上面内容获取10cipher = Cipher.getInstance("SM2");11cipher.init(Cipher.DECRYPT_MODE, privateKey);12byte[] decrypted = cipher.doFinal(encrypted);13System.out.println("Decrypted string: " + new String(decrypted, StandardCharsets.UTF_8));示例代码(签名验签)
xxxxxxxxxx141String str = "jianggujin";2PrivateKey privateKey = null; // 参考上面内容获取3Signature signature = Signature.getInstance("SM3withSM2");4signature.initSign(privateKey);5signature.update(str.getBytes(StandardCharsets.UTF_8));6byte[] signed = signature.sign();7System.out.println("Signed string: " + Base64.getEncoder().encodeToString(signed));89X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(PUBLIC_KEY));10PublicKey publicKey = null; // 参考上面内容获取11signature = Signature.getInstance("SM3withSM2");12signature.initVerify(publicKey);13signature.update(str.getBytes(StandardCharsets.UTF_8));14System.out.println("Verify: " + signature.verify(signed));示例代码(C1C2C3 <=> C1C3C2)
xxxxxxxxxx281private final X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");2/**3 * bc加解密使用旧标c1||c2||c3,此方法在加密后调用,将结果转化为c1||c3||c24 */5private byte[] changeC1C2C3ToC1C3C2(byte[] c1c2c3) {6 // sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。7 final int c1Len = (this.x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1;8 final int c3Len = 32; // new SM3Digest().getDigestSize();9 byte[] result = new byte[c1c2c3.length];10 System.arraycopy(c1c2c3, 0, result, 0, c1Len); // c111 System.arraycopy(c1c2c3, c1c2c3.length - c3Len, result, c1Len, c3Len); // c312 System.arraycopy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.length - c1Len - c3Len); // c213 return result;14}1516/**17 * bc加解密使用旧标c1||c3||c2,此方法在解密前调用,将密文转化为c1||c2||c3再去解密18 */19private byte[] changeC1C3C2ToC1C2C3(byte[] c1c3c2) {20 // sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。21 final int c1Len = (this.x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1;22 final int c3Len = 32; // new SM3Digest().getDigestSize();23 byte[] result = new byte[c1c3c2.length];24 System.arraycopy(c1c3c2, 0, result, 0, c1Len); // c1: 0->6525 System.arraycopy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.length - c1Len - c3Len); // c226 System.arraycopy(c1c3c2, c1Len, result, c1c3c2.length - c3Len, c3Len); // c327 return result;28}示例代码(ASN1 <=> R_S)
xxxxxxxxxx491private final X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");2private final int RS_LEN = 32;34/**5 * BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s6 */7public byte[] rsAsn1ToPlain(byte[] rsDer) throws Exception {8 BigInteger[] decode = StandardDSAEncoding.INSTANCE.decode(this.x9ECParameters.getN(), rsDer);9 byte[] r = this.bigIntToFixedLengthBytes(decode[0]);10 byte[] s = this.bigIntToFixedLengthBytes(decode[1]);1112 byte[] result = new byte[r.length + s.length];13 System.arraycopy(r, 0, result, 0, r.length);14 System.arraycopy(s, 0, result, r.length, s.length);15 return result;16}1718/**19 * BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式20 */21public byte[] rsPlainToAsn1(byte[] sign) throws Exception {22 if (sign.length != RS_LEN * 2) {23 throw new IllegalArgumentException("err rs. ");24 }25 BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN));26 BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, RS_LEN * 2));27 return StandardDSAEncoding.INSTANCE.encode(this.x9ECParameters.getN(), r, s);28}2930/**31 * BigInteger转固定长度bytes32 */33private byte[] bigIntToFixedLengthBytes(BigInteger rOrS) {34 // for sm2p256v1, n is 00fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123,35 // r and s are the result of mod n, so they should be less than n and have length<=3236 byte[] rs = rOrS.toByteArray();37 if (rs.length == RS_LEN) {38 return rs;39 } else if (rs.length == RS_LEN + 1 && rs[0] == 0) {40 return Arrays.copyOfRange(rs, 1, RS_LEN + 1);41 } else if (rs.length < RS_LEN) {42 byte[] result = new byte[RS_LEN];43 Arrays.fill(result, (byte) 0);44 System.arraycopy(rs, 0, result, RS_LEN - rs.length, rs.length);45 return result;46 } else {47 throw new RuntimeException("Error rs: " + CodecUtils.encodeHexString(rs, true));48 }49}CRC(循环冗余校验)算法主要用于数据通信领域中的错误检测,它是一种广泛应用于数字网络和存储设备中的差错控制方法。
CRC算法的分类
CRC算法并没有严格的分类体系,但根据生成多项式的不同,可以有多种不同的CRC算法,常见的包括:
8位生成多项式,如CRC-8-ATM。16位生成多项式,如CRC-16-CCITT、CRC-16-ANSI等。32位生成多项式,是通信领域中最常用的,如CRC-32-IEEE 802.3。64位生成多项式,适用于需要更高检测能力的场合。CRC算法解决的问题
CRC算法主要解决的是数据传输过程中的比特错误问题,通过计算数据流与特定生成多项式之间的模二除法余数,生成一个校验码附加到数据后面。接收端重新计算CRC并与接收到的CRC校验码比较,如果一致,则认为数据在传输过程中没有发生错误或者发生了可检测的错误。
与信息摘要的异同
相同点:CRC算法和信息摘要算法(如MD5、SHA系列)都属于散列函数的范畴,它们都接受输入数据并产生固定长度的输出,且输出对输入非常敏感,即使输入数据有微小变化,输出也会显著不同。
不同点
:
CRC主要用于错误检测,侧重于检测数据在传输或存储过程中的随机错误;而信息摘要算法主要用于数据完整性和身份验证,侧重于防止数据被篡改或确保数据来源的可靠性。CRC算法在设计上并未考虑抗碰撞性,容易受到精心构造的攻击。128位、256位等),且算法更为复杂,适用于安全认证;CRC的输出较短(如8位、16位、32位等),计算相对简单,适用于快速错误检测。与消息认证算法的异同
相同点:两者都涉及数据完整性验证,可以用于检测数据是否被篡改。
不同点
:
HMAC、CMAC)除了验证数据完整性外,还常用于提供消息认证服务,确保数据来源的真实性,适用于安全通信。而CRC主要解决物理层或链路层的数据传输错误,不直接提供安全认证功能。CRC算法不依赖密钥,安全性较低。CRC算法复杂,计算成本较高,但在需要高度安全的环境下是必要的。示例代码
xxxxxxxxxx41String str = "jianggujin";2CRC32 crc32 = new CRC32();3crc32.update(str.getBytes(StandardCharsets.UTF_8));4System.out.println("CRC32 value: " + crc32.getValue());xxxxxxxxxx31str := "jianggujin"2value := crc32.ChecksumIEEE([]byte(str))3fmt.Println("CRC32 value:", value)运行并观察控制台输出:
xxxxxxxxxx11CRC32 value: 724585211SM9算法属于基于身份的密码。基于身份的密码是一种“高级”的公钥密码方案,在具备常规公钥密码加密、签名等密码功能的同时,基于身份的密码体系不需要CA中心和数字证书体系。SM9方案的基本原理是,可以由用户的唯一身份ID(如对方的电子邮件地址、域名或ID号等),从系统的全局主密钥中导出对应的私钥或公钥,导出密钥的正确性是由算法保证的,因此在进行加密、验签的时候,只需要获得解密方或签名方的ID即可,不再需要对方的数字证书了。因此如果应用面对的是一个内部的封闭环境,所有参与用户都是系统内用户,那么采用SM9方案而不是SM2证书和CA的方案,可以简化系统的开发、设计和使用,并降低后续CA体系的维护成本。
对应数字证书体系中的CA中心,SM9体系中也存在一个权威中心,用于生成全局的主密钥(MasterKey),并且为系统中的每个用户生成、分配用户的私钥。和SM2密钥对一样,SM9的主密钥也包含私钥和公钥,其中主公钥(PublicMasterKey)是可以导出并公开给系统中全体用户的。而SM9中用户的密钥对比较特殊,其中的公钥并不能从私钥中导出,SM9用户密钥需要包含用户的ID起到公钥的作用,在加密和验证签名等密码计算中,真正的用户公钥是在计算中,在运行时通过用户ID从主公钥中导出的。因此从应用的角度看,SM9中用户的公钥就是一个字符串形式的ID。
幂等性是指一个操作或接口,无论执行多少次,其结果都是一样的,系统的状态不会因为重复执行而发生改变。换句话说,多次请求的效果与单次请求相同,这对于确保数据一致性特别重要,尤其是在网络不稳定或用户重复提交请求时。
使用场景
实现方式
防篡改旨在保护数据在传输过程中不被未经授权的第三方修改。这通常通过数据签名、消息认证码(MAC)、数字签名或加密来实现,确保数据的完整性和来源的真实性。
使用场景
实现方式
HTTPS协议,确保数据在传输过程中不被查看或修改。MAC值,服务端通过共享密钥验证MAC,确保数据未被篡改。SSL/TLS协议,提供端到端的数据加密和完整性保护。防重放机制用于阻止攻击者捕获有效的数据包或请求然后重新发送,以欺骗系统执行非授权的操作。它通过添加时间戳、序列号、一次性令牌或使用加密技术来验证请求的新鲜度和合法性。
使用场景
实现方式
nonce,拒绝重复的nonce。防信息泄露指的是采取措施防止敏感信息在未经许可的情况下被披露给未经授权的实体。这包括数据加密、访问控制、最小权限原则、安全审计等手段。
使用场景
SQL注入、XSS攻击。实现方式