CTR 计数器模式

计数器模式CTR是分组密码模式中的一种。通过将逐次累加的计数器进行加密来生成密钥流的流密码。每次加密时会生成一个不同的值来作为计数器的初始值。

  • 可以事先进行加密、解密的准备。
  • 加密、解密使用相同结构。
  • 对包含某些错误比特的密文进行解密时,只有明文中响应的比特会出错。
  • 加密和解密均支持并行运算。可以以任意顺序对分组进行加密和解密。

在CTR模式基础上添加认证功能的模式称为GCM模式。生成密文的同时生成用于认证的信息。用于识别主动攻击者发送的伪造的密文。

混合密码系统

混合密码系统能够解决对称密码密钥分配的问题和公钥密码速度慢的问题。使用快速的对称密码来对消息进行加密,再使用公钥密码对对称密码的密钥进行加密,由于对称密码的密钥一般比消息本身要短,因此可以忽略公钥密码速度慢的问题。

会话密钥是对称密码的密钥,同时也是公钥密码的明文。

一个问题

由于会话密钥已经通过公钥密码进行加密,因此会话密钥的长度较短也没有问题。

错!当会话密钥长度较短,当攻击者已知加密方式、分组密码模式的情况下可以通过 brute-force 暴力破解组合消息的后半段部分。增加风险。

RSA-OAEP 最优非对称加密填充

RSA-OAEP 是RSA的改良算法,在加密时会在明文前面填充一些认证信息,包括明文散列值以及一定数量的0。

RSA-OEAP 最大的优点是认证信息。好处是可以防御选择密文攻击(攻击者将任意密文发送给服务器,服务器发送解密提示),服务器可以固定返回错误消息decryption error,而不是将具体的错误内容告知攻击者。

ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &key, []byte(secretMessage), label)

sha256是我们选择的单向散列函数。至于为什么需要rand.Reader,这是OAEP算法需要的,通过随机数使得每次生成的密文呈现不同的排列方式,进一步提高安全性。key 是接收人的公钥。[]byte(secretMessage)是加密信息的字节表现形式。label用于给公钥添加标签,不会被加密。

如果公钥标签不同,假设我们修改下文的RSA_OAEP_EncryptRSA_OAEP_Decrypt 函数中的 label := []byte("标签")。那么当两个函数各自label不同时,会发生如下错误。根据官方文档,如果给定的公钥用于加密两种类型的消息,则可以使用不同的标签值来确保攻击者不能将用于一种目的的密文用于另一种目的。如果不需要,它可以是空的。

crypto/rsa: decryption error
panic: crypto/rsa: decryption error goroutine 1 [running]:
main.CheckError({0x10092e0, 0xc0000603d0})
C:/Users/小能喵喵喵/Desktop/Go/Cryptography/对称加密/HybridCryptoSystem/rsa_crypto.go:14 +0x9d
main.RSA_OAEP_Decrypt(0xc00031a4e0?, {0xc00031a4e0?, 0xfe8637?})
C:/Users/小能喵喵喵/Desktop/Go/Cryptography/对称加密/HybridCryptoSystem/rsa_crypto.go:37 +0x134
main.hybridDecrypt(0x1009360?, {0xc000316fc0?, 0xfe2c44?})
C:/Users/小能喵喵喵/Desktop/Go/Cryptography/对称加密/HybridCryptoSystem/main.go:56 +0xbd
main.main()
C:/Users/小能喵喵喵/Desktop/Go/Cryptography/对称加密/HybridCryptoSystem/main.go:35 +0x2dc
exit status 2

任何拥有密文签名、密文消息和公钥的人都可以使用 RSA 验证来确保消息确实来自颁发公钥的一方。如果数据或签名不匹配,则验证过程失败。只有拥有私钥的一方才能签署消息,但拥有公钥的任何人都可以验证它。

msg := []byte("verifiable message")

//在签名之前,我们需要对我们的消息进行哈希处理
msgHash := sha256.New()
_, err = msgHash.Write(msg)
if err != nil {
panic(err)
}
msgHashSum := msgHash.Sum(nil) //为了生成签名,我们提供了一个随机数生成器,我们的私钥,我们使用的散列算法和消息的散列信息
signature, err := rsa.SignPSS(rand.Reader, privateKey, crypto.SHA256, msgHashSum, nil)
if err != nil {
panic(err)
} //为了验证签名,我们提供了公钥,散列算法,我们消息的哈希和和我们之前生成的签名
//有一个可选的"选项"参数,现在可以省略
err = rsa.VerifyPSS(&publicKey, crypto.SHA256, msgHashSum, signature, nil)
if err != nil {
fmt.Println("could not verify signature: ", err)
return
}
//如果我们没有从 `VerifyPSS` 方法中得到任何错误,这意味着我们的签名有效
fmt.Println("signature verified")

代码

aes_crypto.go

package main

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"io"
) // ^ 生成会话密钥
func AES_Key_Generator() []byte {
var key []byte = make([]byte, 32)
_, err := io.ReadFull(rand.Reader, key)
CheckError(err)
return key
} func AES_Encrypt(key []byte, message string) (encoded string, err error) {
//从输入字符串创建字节切片
plainText := []byte(message) //使用密钥创建新的 AES 密码
block, err := aes.NewCipher(key) //如果 NewCipher 失败,退出:
CheckError(err) // ^ 使密文成为大小为 BlockSize + 消息长度的字节切片,这样传值后修改不会更改底层数组
cipherText := make([]byte, aes.BlockSize+len(plainText)) // ^ iv 是初始化向量 (16字节)
iv := cipherText[:aes.BlockSize]
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
return
} // ^ 加密数据,给定加密算法用的密钥,以及初始化向量
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(cipherText[aes.BlockSize:], plainText) return base64.RawStdEncoding.EncodeToString(cipherText), err
} func AES_Decrypt(key []byte, secure string) (decoded string, err error) {
//删除 base64 编码:
cipherText, err := base64.RawStdEncoding.DecodeString(secure) //如果解码字符串失败,退出:
CheckError(err) //使用密钥和加密消息创建新的 AES 密码
block, err := aes.NewCipher(key) //如果 NewCipher 失败,退出:
CheckError(err) //如果密文的长度小于 16 字节
if len(cipherText) < aes.BlockSize {
err = errors.New("密文分组长度太小")
return
} // ^ iv 是初始化向量 (16字节)
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:] //解密消息
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(cipherText, cipherText) return string(cipherText), err
}

rsa_crypto.go

package main

import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"fmt"
) func CheckError(e error) {
if e != nil {
fmt.Println(e.Error())
panic(e)
}
} // ^ 生成密钥与公钥
func RSA_Generator() (*rsa.PrivateKey, rsa.PublicKey) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
CheckError(err)
return privateKey, privateKey.PublicKey
} func RSA_OAEP_Encrypt(key rsa.PublicKey, secretMessage string) string {
label := []byte("标签")
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &key, []byte(secretMessage), label)
CheckError(err)
return base64.RawStdEncoding.EncodeToString(ciphertext)
} func RSA_OAEP_Decrypt(privKey *rsa.PrivateKey, cipherText string) string {
ct, _ := base64.RawStdEncoding.DecodeString(cipherText)
label := []byte("标签")
// ^ 解密过程中的 rand.Reader 不是必选的,但一般要提供用来防止 side channel attacks.
plaintext, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privKey, ct, label)
CheckError(err)
return string(plaintext)
}

main.go

package main

import (
"bufio"
"crypto/rsa"
"encoding/base64"
"fmt"
"os"
"strings"
) func main() {
privateKey, publicKey := RSA_Generator()
fmt.Printf("\n\t共同N: %v\n\t公钥E: %v\n\t私钥D: %v\n", publicKey.N, publicKey.E, privateKey.D) reader := bufio.NewReader(os.Stdin) var message string // 如果没有给运行附加参数
if len(os.Args) != 2 {
fmt.Printf("\n\t获取不到程序运行附加参数,手动输入字符串\n")
fmt.Printf("\t字符串:")
message, _ = reader.ReadString('\n')
} else {
message = os.Args[1]
} encrypted := hybridEncrypt(publicKey, message) //打印密钥和密文
fmt.Printf("\n\t加密后: %s\n", encrypted) //解密文本
decrypted := hybridDecrypt(privateKey, encrypted) //打印重新解密的文本:
fmt.Printf("\t解密后: %s\n\n", decrypted)
} // ^ 实现混合密码系统加密
func hybridEncrypt(publicKey rsa.PublicKey, message string) string {
AES_key := AES_Key_Generator()
encrypted, err := AES_Encrypt(AES_key, message)
CheckError(err)
head := RSA_OAEP_Encrypt(publicKey, string(AES_key))
return base64.RawStdEncoding.EncodeToString([]byte(head + "|组合加密的会话密钥与消息|" + encrypted))
} // ^ 实现混合密码系统解密
func hybridDecrypt(privateKey *rsa.PrivateKey, message string) string {
mes, err := base64.RawStdEncoding.DecodeString(message)
CheckError(err)
splits := strings.Split(string(mes), "|组合加密的会话密钥与消息|")
head, encrypted := splits[0], splits[1]
AES_key := RSA_OAEP_Decrypt(privateKey, head)
decrypted, err := AES_Decrypt([]byte(AES_key), encrypted)
CheckError(err)
return decrypted
}

Console

PS C:\Users\小能喵喵喵\Desktop\Go\Cryptography\HybridCryptoSystem> go run .

        共同N: 27640386706278435101453826167880213380728173376705028554783165783279386289425385951480103560680095783771843646857109485131423632909299132644495753077858557656973966723671714379227388216997874772747942697857840884958167490215992557618790374058240600396830575755231210656224988403861978602412271792545641212145711389619141451899401914724049344335805426393895927923656887621076309994751218768872654198553908831397491866304367960002592041669717397769735292092598621160683203319490180735669006214925366299990837670410127282438512376259066563653513041612145434668849944713663380228178995120226612134278958851534636750809001
公钥E: 65537
私钥D: 21389172708682283842260567223157071907527493061928308026852438358469476412838678128832773121746357899720601348098910654110504746067032447503938265671965405062383611126402047618025191776020373515722598134212130559541228073703466172690799911811094069748771879235035946845758125744264465032292267939621163527075589206186236252262969497020056020747918051369037068208691174340727908489331589607362106509257993751131469180199656580210783551516628845816453711183613127441188183231286809500171237776949663326262795200507964067032050374872022735029012264732773085633122224061096049966893027129668814251907555802752863797705473 获取不到程序运行附加参数,手动输入字符串
字符串:系统对于不同输入,反馈时间动态变化,利用这个特性获取机密信息的方法称之为时序攻击。 加密后: ei9MamphdU9KQkVCUS9kYlBVSzNFaXN3UUVaamo2R3loMTRGaXNlamJVd1hFd3dOM0FDMUZYYkIrQlJ6M1pTaG5xeE9admNsNVgyY3dEMG4zVEM2NXA1S1M2a29ha3ZlL2JZV2ZlNlcxSUYyRC92TDZiY3ZSVldZdnJSUEJKc2VtVnV5WE9sdWcyWWlKZ2o2dW85R25IQTYyMS9abUlpM1BrNEhCYUlxT1BYQWp3TGxsSzFqZi9KSUZxSytPZ1NHeUVsMExIMm0vTmxkU1NRV093U1RTK3BVQmtJcXBGMENmbE9RSVRUTUgvREsyUFV3OGRtbjkxWnFYVFN3SnQ2ZjVkTUE4cWd6MUNjaVhaUFRGVVhaU1JVWUFTWWFXZGFHRW9yZndZcnQ0bWg0MTBqV1ZQODl6NnZGNHQyRGlrYi9JNFdlalJvdTB2QU9HWmViSWl0Q1lBfOe7hOWQiOWKoOWvhueahOS8muivneWvhumSpeS4jua2iOaBr3xXeGl3YXRwZWRBT2JWRFRxdm9EQVFMK2pyMkVGU1JGWmRIV0pxSGdMVExRcE0vdmxOcjF1ZEFKMDAzSlVESU4wNkdIbCt4cm1IakhLZHdPL204N1I3VE5YM1Q2cE1FdjV6L3VjMVRwck1yQVN6cEFhc0JEUjJwTGJJVEVocXVEeUIvLzRWZDYzYXVqR2xVY0NDNkk3SlRTQVc1bDBTNExlMHdJbytJaU1kSEt3b2l1TTd1UGlGVEUvYzN0Zw
解密后: 系统对于不同输入,反馈时间动态变化,利用这个特性获取机密信息的方法称之为时序攻击。

参考资料

rsa package - crypto/rsa - Go Packages

Implementing RSA Encryption and Signing in Golang (With Examples) (sohamkamani.com)

密码学奇妙之旅、02 混合加密系统、AES、RSA标准、Golang代码的更多相关文章

  1. 加密解密 AES RSA MD5 SHA

    加密解密: 对称加密:加密和解密相同秘钥.常见算法:AES, XTEA, 3DES. 非对称加密: 公钥加密 私钥加密. 加密和解密秘钥不同.常见算法:RSA OpenSSL> genrsa - ...

  2. 密码学奇妙之旅、01 CFB密文反馈模式、AES标准、Golang代码

    CFB密文反馈模式 CFB密文反馈模式属于分组密码模式中的一种.加密与解密使用同一结构,加密步骤生成用于异或的密钥流. 其弥补了ECB电子密码本模式的不足(明文中的重复排列会反映在密文中,通过删除替换 ...

  3. 密码学奇妙之旅、03 HMAC单向散列消息认证码、Golang代码

    HMAC 单向散列消息认证码 消息认证码MAC是用于确认完整性并进行认证的技术,消息认证码的输入包括任意长度的消息和一个发送者和接收者之间共享的密钥(可能还需要共享盐值). HMAC是使用单向散列函数 ...

  4. 我的Android进阶之旅------>Android采用AES+RSA的加密机制对http请求进行加密

    前言 未加密的抓包截图 加密之后的抓包截图 基本需求及概念 AES算法 AES基本原理及算法流程 AES算法流程 RSA算法 RSA算法基本原理及流程 RSA算法实现流程 AES与RSA相结合数据加密 ...

  5. 前后端API交互数据加密——AES与RSA混合加密完整实例

    前言 前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用R ...

  6. iOS开发之 AES+Base64数据混合加密与解密

    2016-04-08 09:03 编辑: liubinqww 分类:iOS开发 来源:liubinqww 投稿 4 889     "APP的数据安全已经牵动着我们开发者的心,简单的MD5/ ...

  7. NGK引入反量子加密系统来应对量子计算攻击

    当前,区块链和分布式账本技术已有了长足发展并广泛应用与多种场景中,原因在于其提供透明性,冗余性和问责性的能力.就区块链而言,此类特征是通过公钥加密和哈希函数提供的.但是,随着量子计算机技术的发展和量子 ...

  8. AES加密和Base64混合加密

    /// <summary> /// AES加密方法(AES加密和Base64混合加密) /// </summary> /// <param name="toEn ...

  9. DES加密系统的实现

    这是一个高内聚低耦合可复用的DES加密系统的实现. Github 链接:https://github.com/cyendra/CyDES 要实现加密系统,先考虑数据的基本单位. 在DES加密中,数据是 ...

随机推荐

  1. (一)java基础篇---第一个程序

    先认识java的基础知识 1.变量命名规则 :1)变量名由数字字母下划线组成,2)不能使用java的关键字,比如public这种,3)遵循小驼峰命名法 2.数据类型 2.1基本数据类型有8种 其中分为 ...

  2. led的进化

    1.一个led亮100ns,灭400ns,循环 2.一个led亮2500ns,灭5000ns,亮7500ns,灭10000ns循环 3.以2500ns为变化周期,20000ns为一个循环,每个周期的亮 ...

  3. HTML表单学习

    HTML表单学习 前言 HTML基础学习会由HTML基础标签学习.HTML表单学习和一张思维导图总结HTML基础三篇文章构成,文章中博主会提取出重点常用的知识和经常出现的bug,提高学习的效率,后续会 ...

  4. Mybatis的使用(2)

    1 :模糊查询 #{}占位符和¥{}的区别 #{}占位符:传参大部分用#{}传参,它的底层是PreparedStatement对象,是安全的数据库访问,它能够防止sql注入 1.1:如果parmete ...

  5. 远见而明察近观若明火|Centos7.6环境基于Prometheus和Grafana结合钉钉机器人打造全时监控(预警)Docker容器服务系统

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_181 我们知道,奉行长期主义的网络公司,势必应在软件开发流程管理体系上具备规范意识,即代码提交有CR(CodeReview),功能 ...

  6. Odoo 如何下载指定版本源码 && .cfg配置参数

    # 我们使用应用市场上的模块的时候,经常会碰到模块只兼容特定版本,要将模块兼容到自己版本来的时候,就需要下载它原兼容odoo的版本运行. # 这里的-b后面加的11.0就是它的版本.在git中也就是分 ...

  7. 八个解决你80%需求的CSS动画库

    八个解决你80%需求的CSS动画库 点击打开视频讲解 在学习和工作的过程中,我们总免不了要写各种各样的css动画,给某个部分添加动画效果,如果觉得自己写的动画效果单一乏味,不妨试试这8个CSS动画库, ...

  8. Luogu2574 XOR的艺术 (分块)

    本是要练线段树的,却手贱打了个分块 //#include <iostream> #include <cstdio> #include <cstring> #incl ...

  9. RAID磁盘阵列技术

    RAID磁盘阵列技术 1.RAID概述 RAID(Redundant Array of Independent Disk),从字面意思讲的是基于独立磁盘的具有冗余的磁盘阵列,其核心思想是将多块独立磁盘 ...

  10. D8调试工具——jsvu的使用细则

    d8 is V8's own developer shell. D8 是一个非常有用的调试工具,你可以把它看成是 debug for V8 的缩写.我们可以使用 d8 来查看 V8 在执行 JavaS ...