Go语言教程之边写边学:密码学 Cryptography

什么是密码学?

术语"密码学"是从两个希腊词演变而来的,即加密和图形。根据希腊语,加密意味着秘密,图形意味着写作。随着比特币、以太坊和莱特币等所有加密货币的引入,加密货币一词变得越来越流行。

简单来说,改变消息的方式使其含义对可能抓住它们的敌人或对手隐藏的过程称为密码学。密码学是秘密写作的科学,它带来了许多技术来保护以不可读格式存在的信息。只有指定的收件人才能将此不可读格式转换为可读格式。

在安全的电子交易中,采用加密技术来保护电子邮件、信用卡详细信息、音频/视频广播、存储介质和其他敏感信息。通过使用加密系统,发送方可以首先加密消息,然后通过网络传递消息。另一方面,接收者可以解密消息并恢复其原始内容。

 

密码学的组成部分

明文(Plaintext):明文可以是文本、二进制代码或需要转换为任何人都无法读取的格式的图像,除了那些携带秘密来解锁它的人。它是指发件人希望发送的原始未加密或纯消息。

密文(Ciphertext):在加密过程中明被转换为匆忙格式,由此产生的格式称为密文。它与加密消息有关,接收方接收该消息。但是,密文就像加密过程为重现最终输出而操作的明文。此最终输出包含原始消息包括格式,除非官方知道正确的方法或可以破解代码,否则无法检索。

加密(Encryption):接收信息并将其转换为可以逆转的不可读格式。这是加密明文的过程,以便它可以提供密文。加密需要一种称为密码和密钥的算法。没有人可以在不知道密钥的情况下解密加密消息上的重要信息。明文使用加密密码转换为密文。

解密(Decryption):这与加密过程相反,在加密过程中,它使用解密算法和密钥将密文转换回明文。在对称加密中,用于解密的密钥与用于加密的密钥相同。另一方面,在非对称加密或公钥加密中,用于解密的密钥与用于加密的密钥不同。

密码(Ciphers):加密和解密算法统称为密码。也许加密过程中最棘手,最有趣和最奇怪的部分是算法或密码。算法或密码只不过是一个公式,其中包含说明如何对信息实施加密/解密过程的各种步骤。基本密码获取位并返回位,它不关心位是否表示文本信息、图像或视频。

钥匙(Key):密钥通常是密码操作的一个数字或一组数字。在技术术语中,密钥是用于控制给定加密算法的输出(密文和明文)的离散信息。加密和解密算法需要此密钥分别加密或解密消息。发送方使用加密算法和密钥将明文转换为密文。另一方面,接收器使用相同的解密算法和密钥将密文转换回明文。密钥越长,攻击者解密消息的难度就越大。

密码学示例(经典密码)

下面是非常基本的示例,我们创建了一个简单的密码来加密和解密明文为密文,反之亦然。算法cipherAlgorithm() 与加密和解密相同。我们使用的密钥是01、10和15来加密和解密消息。每次密钥不同时,加密的输出都不同。这种密码根据密钥值来转换字母,密钥在密码学中起着重要作用。

package main
 
import (
    "fmt"
    "unicode"
)
 
// 加密解密接口type Cipher interface {
    Encryption(string) string
    Decryption(string) string
}
 
// 密钥type cipher []int
 
// 加密算法func (c cipher) cipherAlgorithm(letters string, shift func(int, int) int) string {
    shiftedText := ""
    for _, letter := range letters {
        if !unicode.IsLetter(letter) {
            continue
        }
        shiftDist := c[len(shiftedText)%len(c)]
        s := shift(int(unicode.ToLower(letter)), shiftDist)
        switch {
        case s < 'a':
            s += 'z' - 'a' + 1
        case 'z' < s:
            s -= 'z' - 'a' + 1
        }
        shiftedText += string(s)
    }
    return shiftedText
}
 
// 加密func (c *cipher) Encryption(plainText string) string {
    return c.cipherAlgorithm(plainText, func(a, b int) int { return a + b })
}
 
// 解密func (c *cipher) Decryption(cipherText string) string {
    return c.cipherAlgorithm(cipherText, func(a, b int) int { return a - b })
}
 
// 创建新的凯撒密码func NewCaesar(key int) Cipher {
    return NewShift(key)
}
 
// 创建新的密码.
func NewShift(shift int) Cipher {
    if shift < -25 || 25 < shift || shift == 0 {
        return nil
    }
    c := cipher([]int{shift})
    return &c
}
 
func main() {
    c := NewCaesar(1)
    fmt.Println("Encrypt Key(01) abcd =>", c.Encryption("abcd"))
    fmt.Println("Decrypt Key(01) bcde =>", c.Decryption("bcde"))
    fmt.Println()
 
    c = NewCaesar(10)
    fmt.Println("Encrypt Key(10) abcd =>", c.Encryption("abcd"))
    fmt.Println("Decrypt Key(10) klmn =>", c.Decryption("klmn"))
    fmt.Println()
 
    c = NewCaesar(15)
    fmt.Println("Encrypt Key(15) abcd =>", c.Encryption("abcd"))
    fmt.Println("Decrypt Key(15) pqrs =>", c.Decryption("pqrs"))
}

输出

Encrypt Key(01) abcd => bcde
Decrypt Key(01) bcde => abcd
 
Encrypt Key(10) abcd => klmn
Decrypt Key(10) klmn => abcd
 
Encrypt Key(15) abcd => pqrs
Decrypt Key(15) pqrs => abcd

Go为加密提供了多个选项,例如encryption, hashing。Go有支持对称加密算法的软件包:base64、AES和DES。

 

什么是哈希?

获取明文并将其转换为明文信息的摘要的过程称为哈希。哈希的输出称为哈希、哈希值或消息摘要。哈希是密码学的一个有趣领域,与加密算法不同。哈希会创建无法轻易逆转的加扰输出。从技术上讲,哈希生成一个固定长度的值,该值在一个方向上相对容易计算,但几乎不可能逆转。

 

哈希基础知识

哈希、哈希值或消息摘要是一个值,它是给定到哈希算法中的明文或密文的输出。无论输入到哈希算法中的内容是什么,哈希都是固定长度的,并且始终具有一定的长度。生成的哈希值的长度由算法本身的设计固定。我们还将哈希作为文件或消息的摘要,通常采用数字格式。哈希用于数字签名、文件和消息身份验证,并用于保护敏感数据的完整性。

哈希可以归入单向函数的类别。这表明哈希在生成时可以计算,但很难(或不可能)反向计算。使用哈希,初始计算相对容易生成消息的精简版本,但不应从哈希重新创建原始消息。

 

加密哈希函数

对消息进行哈希处理的最简单方法是将其切成块,并使用类似的算法连续处理每个块。此方法称为迭代哈希。迭代哈希使用压缩函数将输入转换为较小的输出;并将输入转换为相同大小的输出,以便任何两个不同的输入提供两个不同的输出。加密哈希函数是那些基于分组密码的哈希函数。加密哈希函数的示例包括基于消息摘要 (MD) 算法(如MD2、MD4和MD5)和基于安全哈希算法 (SHA)(如SHA-1、SHA-224、SHA-256、SHA-384和SHA-512)。

 

消息摘要算法5 (MD5)

这种加密哈希算法由Ron Rivest于1991年开发。此算法将可变长度的消息作为输入,并生成128位的固定长度消息摘要。此算法使用大端方案,其中32位字的最低有效字节将存储在低地址字节位置。该算法经历了四轮,每轮有16次迭代,因此总共使用64次迭代。因此,它需要一个128位缓冲区。与SHA-1相比,这不太安全,但运行速度更快。此算法需要21的28次方次用于从给定消息摘要中检测原始消息的操作和2的64次方次用于检测生成相同消息摘要的两条消息的操作。

 

安全哈希算法 (SHA-1)

此算法将可变长度的消息作为输入,并生成160位的固定长度消息摘要。此算法使用小端方案将消息解释为32位字序列。在此算法中,32位字的最高有效字节存储在低地址字节位置。该算法经历四轮,每轮有20次迭代,因此总共使用80次迭代。因此,它需要一个160位缓冲区。与MD5相比,这更安全,但运行速度更慢。此算法需要2160用于从给定消息摘要中检测原始消息的操作和2的80次方次用于检测生成相同消息摘要的两条消息的操作。

package main
 
import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"fmt"
)
 
func main() {
	fmt.Println("\n----------------Small Message----------------\n")
	message := []byte("Today web engineering has modern apps adhere to what is known as a single-page app (SPA) model.")
 
	fmt.Printf("Md5: %x\n\n", md5.Sum(message))
	fmt.Printf("Sha1: %x\n\n", sha1.Sum(message))
	fmt.Printf("Sha256: %x\n\n", sha256.Sum256(message))
	fmt.Printf("Sha512: %x\n\n", sha512.Sum512(message))
 
	fmt.Println("\n\n----------------Large Message----------------\n")
	message = []byte("Today web engineering has modern apps adhere to what is known as a single-page app (SPA) model. This model gives you an experience in which you never navigate to particular pages or even reload a page.  It loads and unloads the various views of our app into the same page itself. If you've ever run popular web apps like Gmail, Facebook, Instagram, or Twitter, you've used a single-page app. In all those apps, the content gets dynamically displayed without requiring you to refresh or navigate to a different page. React gives you a powerful subjective model to work with and supports you to build user interfaces in a declarative and component-driven way.")
 
	fmt.Printf("Md5: %x\n\n", md5.Sum(message))
	fmt.Printf("Sha1: %x\n\n", sha1.Sum(message))
	fmt.Printf("Sha256: %x\n\n", sha256.Sum256(message))
	fmt.Printf("Sha512: %x\n\n", sha512.Sum512(message))
}

输出

----------------Small Message----------------
 
Md5: d53c7002ebeeaa872f02efdda82f76f0
 
Sha1: b56fd6c3eabc1ea6e880ffb7762d31a4b39bbcc9
 
Sha256: 0e81731d26a2ffd898be00c40bf0ab3c24ec82a0837311701193dd861efdb944
 
Sha512: 97be88309470c98e8ff840a74567c3948263251f3ac6f232a6e228cebc6daa6d95569a96
6a5f2ba5d694dc644d02c144849e7a181a159b583a2060e2ce3ad375
 
 
 
----------------Large Message----------------
 
Md5: f52c27fb5fa09afbb717e5ded9ef5707
 
Sha1: 86b1102f10518ef02089742862695e132ca6b5fe
 
Sha256: 88c9eca72b7b635458945710d12f6709af44dd02c0a3bb9b16c7e352b6d13f84
 
Sha512: 5400f78f1642d5b7df7a0c60e22e95ba04a94d16d33d008fc78a5002958f853ecc469bb8
786632c0be47a0c5b44d5f78aa9878abe7f00b688d8712c921aa81b8

由于MD5和SHA-1中的漏洞,一些机构早在2年就开始使用SHA-2011。SHA-2更安全,它具有256位和512位块大小。互联网上使用的常见哈希是SHA-2,SHA-1现已弃用,如果正在使用,则应替换。

我们通常在公钥加密、消息身份验证、密钥协议协议、数字签名、完整性验证、标识保护和许多其他加密联系人中使用此哈希函数。无论我们是加密电子邮件、在手机上发送消息、连接到HTTPS网站还是通过IPSec或SSH连接到远程机器,引擎盖下的某个地方总是有一个哈希函数。

我们使用哈希函数的真实示例列表:

  • 云存储系统使用哈希函数来分析相同的文件并查找更改的文件。
  • Git版本控制系统使用哈希函数来检测存储库中的文件。
  • 比特币在其工作量证明系统中使用哈希函数。
  • 取证分析师使用哈希值来验证数字工件是否未更改。
  • NIDS使用哈希来分析通过网络的已知恶意数据。

 

基于哈希的MAC (HMAC)

基于哈希的MAC (HMAC) 将长消息作为输入并生成固定长度的输出。在此方案中,发送方使用MAC对消息进行签名,接收方使用共享密钥对其进行验证。它使用称为密钥前缀(密钥在前,消息在后)或密钥后缀(密钥在消息之后)的两种方法之一对密钥与消息进行哈希处理。

 

HMAC的高级设计

消息认证码(MAC)是信息的一小部分或小算法,主要用于对消息进行身份验证,维护消息的完整性和真实性保证。基于哈希的消息身份验证代码是从加密哈希函数(如MD5和SHA-1)派生的消息身份验证代码。HMAC背后的基本思想是使用现有消息摘要算法中的密钥添加层。即使攻击者用盐获得了散列密码的数据库,如果没有密钥,他们仍然很难破解它们。由于MD5和SHA-1等算法不依赖于密钥,因此HMAC已被选为实现IP安全的强制性MAC。HMAC可以使用任何现有的消息摘要算法(哈希函数)。它将嵌入式哈希函数生成的消息摘要视为黑盒。然后,它使用共享对称密钥对消息摘要进行加密,从而生成最终输出,即MAC。HMAC是通过使用加密哈希函数(如MD5或SHA-1)计算MAC的方法。

当我们使用SHA-1时,相应的MAC将被称为HMAC-SHA1,或者如果使用SHA-2,那么我们会说HMAC-SHA256。最好将密钥存储在单独的位置(如环境变量)中,而不是存储在具有哈希密码和盐的数据库中。

package main
 
import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/base64"
	"fmt"
	"io"
)
 
var secretKey = "4234kxzjcjj3nxnxbcvsjfj"
 
// Generate a salt string with 16 bytes of crypto/rand data.
func generateSalt() string {
	randomBytes := make([]byte, 16)
	_, err := rand.Read(randomBytes)
	if err != nil {
		return ""
	}
	return base64.URLEncoding.EncodeToString(randomBytes)
}
 
func main() {
	message := "Today web engineering has modern apps adhere to what is known as a single-page app (SPA) model."
	salt := generateSalt()
	fmt.Println("Message: " + message)
	fmt.Println("\nSalt: " + salt)
 
	hash := hmac.New(sha256.New, []byte(secretKey))
	io.WriteString(hash, message+salt)
	fmt.Printf("\nHMAC-Sha256: %x", hash.Sum(nil))
 
	hash = hmac.New(sha512.New, []byte(secretKey))
	io.WriteString(hash, message+salt)
	fmt.Printf("\n\nHMAC-sha512: %x", hash.Sum(nil))
}

输出

Message: Today web engineering has modern apps adhere to what is known as a sing
le-page app (SPA) model.
 
Salt: iWk9q-tQgWQTnqDgdoxaXQ==
 
HMAC-Sha256: b158c5a1bbcdac3cf87fe761030828cb5811b0a6fdfa6366c7bdfddba6391728
 
HMAC-sha512: e350ca7f0349c2b16a410f224b1ad0c8fc9319708b1dd2be9e83a53b3d4b93d9dd1
f0637ea27641edcfac3d3196795d9889778bd4894ad332ba643d0735aa089

 

高级加密标准 (AES)

美国国家标准与技术研究院(NIST)于1997年2000月宣布呼吁创建一个创建新密码的项目。许多团体提出了各种密码。对各种密码的速度和安全参数进行了检查,经过几轮研究和检查,NIST最终选择了一种称为Rijndael的算法。Rijndael在算法的安全性,成本,弹性,完整性和监视方面被选为最佳算法,因此NIST在<>年<>月选择Rijndael作为高级加密标准(AES)。

26年2001月2003日,AES成为FIPS(联邦信息处理标准)标准。AES指定用于保护电子数据的FIPS批准的加密算法。美国政府 (NSA) 于 <> 年 <> 月接受并宣布AES足够安全,可以保护高度机密的信息,最高可达超级机密级别。

Rijndael之所以被命名,是因为它是由鲁汶Katholieke大学电气工程系的两位比利时密码学家Joan Daemen博士和Vincent Rijmen博士开发的。Rijndael或AES是无专利的,创建者已经将各种参考实现作为公共领域。

 

go中的AES加密和解密

下面的示例程序将使用密钥加密文本消息并解密文件,密钥基本上是一个16字节(128位)密码。该程序将创建两个包含加密数据的aes.enc文件和包含AES密钥的aes.key

此示例的用途有限。将其用作您自己的应用程序的参考。

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
)

const (
	keyFile       = "aes.key"
	encryptedFile = "aes.enc"
)

var IV = []byte("1234567812345678")

func readKey(filename string) ([]byte, error) {
	key, err := ioutil.ReadFile(filename)
	if err != nil {
		return key, err
	}
	block, _ := pem.Decode(key)
	return block.Bytes, nil
}

func createKey() []byte {
	genkey := make([]byte, 16)
	_, err := rand.Read(genkey)
	if err != nil {
		log.Fatalf("Failed to read new random key: %s", err)
	}
	return genkey
}

func saveKey(filename string, key []byte) {
	block := &pem.Block{
		Type:  "AES KEY",
		Bytes: key,
	}
	err := ioutil.WriteFile(filename, pem.EncodeToMemory(block), 0644)
	if err != nil {
		log.Fatalf("Failed in saving key to %s: %s", filename, err)
	}
}

func aesKey() []byte {
	file := fmt.Sprintf(keyFile)
	key, err := readKey(file)
	if err != nil {
		log.Println("Creating a new AES key")
		key = createKey()
		saveKey(file, key)
	}
	return key
}

func createCipher() cipher.Block {
	c, err := aes.NewCipher(aesKey())
	if err != nil {
		log.Fatalf("Failed to create the AES cipher: %s", err)
	}
	return c
}

func encryption(plainText string) {
	bytes := []byte(plainText)
	blockCipher := createCipher()
	stream := cipher.NewCTR(blockCipher, IV)
	stream.XORKeyStream(bytes, bytes)
	err := ioutil.WriteFile(fmt.Sprintf(encryptedFile), bytes, 0644)
	if err != nil {
		log.Fatalf("Writing encryption file: %s", err)
	} else {
		fmt.Printf("Message encrypted in file: %s\n\n", encryptedFile)
	}
}

func decryption() []byte {
	bytes, err := ioutil.ReadFile(fmt.Sprintf(encryptedFile))
	if err != nil {
		log.Fatalf("Reading encrypted file: %s", err)
	}
	blockCipher := createCipher()
	stream := cipher.NewCTR(blockCipher, IV)
	stream.XORKeyStream(bytes, bytes)
	return bytes
}

func main() {

	var plainText = "AES is now being used worldwide for encrypting digital information, including financial, and government data."
	encryption(plainText)

	fmt.Printf("Decrypted Message: %s", decryption())
}

输出

Message encrypted in file: aes.enc
 
Decrypted Message: AES is now being used worldwide for encrypting digital inform
ation, including financial, and government data.

 

AES的高级设计

AES的算术运算基于伽罗瓦归档,其中有GF(2N) 结构,其中N = 8。AES是一种对称密码,它使用相同的密钥进行加密和解密过程。此对称密码使用128位密钥值加密128位明文块,一次生成128位密文。AES需要较大的128位密钥大小来实现加密和解密过程。

AES 128位密码使用10作(具有单个步骤集合的替换和排列网络设计)来执行加密和解密过程。根据密钥类型和轮数操作,三个版本是AES-128使用10轮,AES-192使用12轮和AES-256使用14作可用。

AES整个数据块在每一轮中都以相同的方式处理。在AES中,明文在生成密码之前必须经过 Nr 轮数。同样,每轮包括四个不同的操作。一个操作是排列,另外三个是替换。它们是SubBytes,ShiftRows,MixColumns和AddRoundKey。

在AES中,加密过程中使用的所有转换都将具有解密过程中使用的反向转换。AES中的每一轮解密过程都使用逆变换InvSubBytes()、InvShiftRows() 和InvMixColumns()。

 

使用AES进行强加密

由于AES是在DES之后产生的,因此所有已识别的针对DES的攻击都已在AES上进行了演示,并且所有最终结果都是有效的。AES比DES更有信心进行暴力攻击,因为它的可变密钥大小和块大小更大。AES不容易受到统计攻击,并且已经测试过,使用常用技术无法对AES中的密文进行统计分析。截至今天,尚未检测到对AES的差分,线性和成功攻击。AES最好的部分是其中使用的算法非常基本,可以使用廉价的处理器和最少的内存快速实现。AES比DES需要更高的处理和更多的传输轮次,我们可以相对地看出这是AES的缺点。

 

Rivest–Shamir–Adleman(RSA)

1977年,麻省理工学院(MIT)的三位年轻科学家Ron Rivest,Adi Shamir和Leonard Adleman采用了公钥密码学的概念,并开发了一种我们称之为RSA算法的算法。使用他们姓氏的第一个字母,他们得出了RSA。此算法使用公钥加密(也称为非对称加密),因此它使用两个不同的密钥来加密和解密数据。在RSA中,生成一对密钥,其中一个密钥向外部世界透露,称为公钥,另一个密钥对用户保密,称为私钥。他们开发了这种算法来解决两个关键问题:创建安全通信,而不必信任单独的密钥分发协调员和您的密钥,并验证消息是否完好无损地来自声明的发件人。

 

RSA的基本概念

在非对称密钥加密中,它会生成一对密钥。公钥正在发布,另一方面,私钥保持机密。这两个键在数字上相互链接。由于它使用单向函数生成这些密钥,因此在知道公钥后无法生成私钥,反之亦然。通过密钥加密的消息使用类似的密钥解密是不切实际的。因此,消息的保密性仍然是安全的。

让我们假设Alice和Bob需要使用RSA算法在他们之间传输秘密消息。他们首先生成正确的密钥集并发布公钥,以便其他机构可以访问它。
它们的公钥和私钥的表示如下:
Alice :有Public A(公钥A) 和Private A(私钥A)

Bob:有Public B和Private B

当Alice向 Bob发送消息时,她使用Public-B加密消息 (M),并使用公式生成密文 (C):
C = Public-B(M)

收到密码B后,Bob可以使用他的私钥Private-B解密消息。这可以正式表示为:
M = Private-B(C)

每一方都对对方保密他或她的私钥。因此,使用公钥加密的消息只能使用其相关的私钥解密。

  • 当前日期:
  • 北京时间:
  • 时间戳:
  • 今年的第:18周
  • 我的 IP:3.149.10.88
农历
五行
冲煞
彭祖
方位
吉神
凶神
极简任务管理 help
+ 0 0 0
Task Idea Collect