今日Go中的密码学

2020年12月30日11:08:59 发表评论 34 次浏览

本文概述

密码术是在存在第三方对手的情况下进行安全通信的技术的实践和研究。在Web应用程序中, 开发人员使用加密技术来确保用户数据的安全, 并确保系统不会被不良行为者利用, 这些不良行为者可能会利用系统漏洞来谋取个人利益。

大多数编程语言都有自己的通用密码原语, 算法等实现。在本文中, 我们将研究Go编程语言中如何处理加密技术以及当今可用的加密程序包。

首先, 让我们看一下标准Go库中的加密软件包。

Go的标准加密套件

如果你一直在编写Go语言, 那么你将同意标准的Go库非常强大, 涵盖从HTTP到编码甚至测试的所有内容。因此, Go附带其自己的加密程序包也就不足为奇了。

密码包本身包含常见的密码常量, 基本密码原理的实现, 等等。然而, 它的大部分价值在于其子包。加密程序包具有各种子程序包, 每个子程序包都专注于单个加密算法, 原理或标准。

我们有aes软件包, 重点放在AES(高级加密标准); hmac, 重点是HMAC(基于哈希的消息身份验证代码)用于数字签名和验证;还有很多其他借助这些软件包, 我们可以执行与加密相关的不同任务, 例如加密, 解密, 散列等。让我们来探讨一下该如何做。

散列

散列基本上是获取任意大小的输入并生成固定大小的输出的过程。至少, 良好的哈希算法永远不会为两个不同的输入产生相同的输出, 并且始终会为给定的输入产生相同的输出。

Go加密软件包中支持多种不同的哈希算法, 例如SHA-256, SHA-1和MD5, 以及其他几种。这是一个函数的实现, 该函数使用SHA-256哈希算法对纯文本进行哈希处理, 并以十六进制格式返回哈希值。

func hashWithSha256(plaintext string) (string, error) {
   h := sha256.New()
   if _, err := io.WriteString(h, plaintext);err != nil{
      return "", err
   }
   r := h.Sum(nil)
   return hex.EncodeToString(r), nil
}

func main(){
  hash, err := hashWithSha256("hashsha256")
  if err != nil{
     log.Fatal(err)
  }
  fmt.Println(hash)  //c4107b10d93310fb71d89fb20eec1f4eb8f04df12e3f599879b03be243093b14
}

如你所见, 新sha256子包的函数返回一个实现以下类型的类型:哈希界面。实现此接口的任何类型也都实现Writer接口。因此, 我们可以简单地将纯文本写入其中, 并使用和方法, 并将结果编码为十六进制格式。

该代码也可以与其他哈希算法一起使用-你只需要从适当的包中创建哈希即可。因此, 如果我们使用MD5算法进行哈希处理, 我们将拥有:

我们为制作了一个自定义演示.
不完全是。点击这里查看.

今日Go中的密码学1
h := md5.New()

对称密钥加密

我们也可以实施对称密钥密码学仅使用Go标准库。对称密钥加密仅涉及加密纯文本并使用相同密钥解密相应的密文。

使用Go加密软件包, 我们可以利用流和分组密码进行加密和解密。让我们看一下如何使用AES和AES来实现对称密钥加密CBC(密码块链接)模式。

首先, 我们编写一个函数来创建具有给定密钥的新分组密码。 AES仅采用密钥长度为128、192或256位的密钥。因此, 我们将对给定的密钥进行哈希处理并将该哈希作为我们的分组密码的密钥进行传递。该函数返回一个块来自密码子包和一个错误。

func newCipherBlock(key string) (cipher.Block, error){
   hashedKey, err := hashWithSha256(key)
   if err != nil{
      return nil, err
   }
   bs, err := hex.DecodeString(hashedKey)
   if err != nil{
      return nil, err
   }
   return aes.NewCipher(bs[:])
}

在开始编写用于加密和解密的函数之前, 我们需要为以下两个函数编写函数填充并填充我们的明文。填充只是增加明文长度的行为, 因此它可以是固定大小(通常是块大小)的倍数。这通常是通过在纯文本中添加字符来完成的。

填充方案有很多, 由于Go不会自动填充明文, 因此我们必须自己做。这个GitHub要点按用户呼应环展示了一种使用PKCS7填充方案填充明文的简单方法, 该方法已在第10.3节中定义RFC 2315.

var (
   // ErrInvalidBlockSize indicates hash blocksize <= 0.
   ErrInvalidBlockSize = errors.New("invalid blocksize")

   // ErrInvalidPKCS7Data indicates bad input to PKCS7 pad or unpad.
   ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data (empty or not padded)")

   // ErrInvalidPKCS7Padding indicates PKCS7 unpad fails to bad input.
   ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
)

func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
   if blocksize <= 0 {
      return nil, ErrInvalidBlockSize
   }
   if b == nil || len(b) == 0 {
      return nil, ErrInvalidPKCS7Data
   }
   n := blocksize - (len(b) % blocksize)
   pb := make([]byte, len(b)+n)
   copy(pb, b)
   copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
   return pb, nil
}

func pkcs7Unpad(b []byte, blocksize int) ([]byte, error) {
   if blocksize <= 0 {
      return nil, ErrInvalidBlockSize
   }
   if b == nil || len(b) == 0 {
      return nil, ErrInvalidPKCS7Data
   }

   if len(b)%blocksize != 0 {
      return nil, ErrInvalidPKCS7Padding
   }
   c := b[len(b)-1]
   n := int(c)
   if n == 0 || n > len(b) {
      fmt.Println("here", n)
      return nil, ErrInvalidPKCS7Padding
   }
   for i := 0; i < n; i++ {
      if b[len(b)-n+i] != c {
         fmt.Println("hereeee")
         return nil, ErrInvalidPKCS7Padding
      }
   }
   return b[:len(b)-n], nil
}

现在我们已经了解了这一点, 我们可以编写用于加密和解密的函数。

//encrypt encrypts a plaintext
func encrypt(key, plaintext string) (string, error) {
   block, err := newCipherBlock(key)
   if err != nil {
      return "", err
   }

  //pad plaintext
   ptbs, _ := pkcs7Pad([]byte(plaintext), block.BlockSize())

   if len(ptbs)%aes.BlockSize != 0 {
      return "", errors.New("plaintext is not a multiple of the block size")
   }

   ciphertext := make([]byte, len(ptbs))

  //create an Initialisation vector which is the length of the block size for AES
   var iv []byte = make([]byte, aes.BlockSize)
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {
      return "", err
   }

   mode := cipher.NewCBCEncrypter(block, iv)

  //encrypt plaintext
   mode.CryptBlocks(ciphertext, ptbs)

  //concatenate initialisation vector and ciphertext
   return hex.EncodeToString(iv) + ":" + hex.EncodeToString(ciphertext), nil
}


//decrypt decrypts ciphertext
func decrypt(key, ciphertext string) (string, error) {
   block, err := newCipherBlock(key)
   if err != nil {
      return "", err
   }

  //split ciphertext into initialisation vector and actual ciphertext
   ciphertextParts := strings.Split(ciphertext, ":")
   iv, err := hex.DecodeString(ciphertextParts[0])
   if err != nil {
      return "", err
   }
   ciphertextbs, err := hex.DecodeString(ciphertextParts[1])
   if err != nil {
      return "", err
   }

   if len(ciphertextParts[1]) < aes.BlockSize {
      return "", errors.New("ciphertext too short")
   }

   // CBC mode always works in whole blocks.
   if len(ciphertextParts[1])%aes.BlockSize != 0 {
      return "", errors.New("ciphertext is not a multiple of the block size")
   }

   mode := cipher.NewCBCDecrypter(block, iv)


   // Decrypt cipher text
   mode.CryptBlocks(ciphertextbs, ciphertextbs)

  // Unpad ciphertext
   ciphertextbs, err = pkcs7Unpad(ciphertextbs, aes.BlockSize)
   if err != nil{
      return "", err
   }

   return string(ciphertextbs), nil
}

现在我们可以像这样测试我们的功能:

func main() {
  pt := "Highly confidential message!"
  key := "aSecret"

   ct, err := encrypt(key, pt)
   if err != nil{
      log.Fatalln(err)
   }
   fmt.Println(ct)  //00af9595ed8bae4c443465aff651e4f6:a1ceea8703bd6aad969a64e7439d0664320bb2f73d9a31433946b81819cb0085

   ptt, err := decrypt(key, ct)
   if err != nil{
      log.Fatalln(err)
   }
   fmt.Println(ptt)  //Highly confidential message!

}

公钥加密

公钥加密与对称密钥加密的不同之处在于, 不同的密钥用于加密和解密。存在两个不同的密钥:用于解密的私钥和用于加密的公钥。

RSA是公钥密码系统的流行示例, 可以使用以下命令在Go中实现:rsa分包。

要实现RSA, 我们必须首先生成我们的私有和公共密钥。为此, 我们可以使用GenerateKey然后从私钥生成公钥。

func main(){
//create an RSA key pair of size 2048 bits
  priv, err := rsa.GenerateKey(rand.Reader, 2048)
  if err != nil{
     log.Fatalln(err)
  }

  pub := priv.Public()
}

现在, 我们可以将RSA与OAEP加密和解密我们喜欢的明文和密文。

func main(){
    ...
    options := rsa.OAEPOptions{
     crypto.SHA256, []byte("label"), }

  message := "Secret message!"

  rsact, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, pub.(*rsa.PublicKey), []byte(message), options.Label)
  if err != nil{
     log.Fatalln(err)
  }

  fmt.Println("RSA ciphertext", hex.EncodeToString(rsact))

  rsapt, err := priv.Decrypt(rand.Reader, rsact, &options)
  if err != nil{
     log.Fatalln(err)
  }

  fmt.Println("RSA plaintext", string(rsapt))

}

数字签名

数字签名是密码学的另一种应用。数字签名基本上使我们能够验证跨网络传输的消息是否未被攻击者篡改。

实施数字签名的常用方法是使用消息认证码(MAC), 特别是HMAC。 HMAC使用哈希函数, 是确保消息真实性的安全方法。我们可以使用hmac分包。

这是一个如何完成的示例:

/*hmacs make use of an underlying hash function so we have to specify one*/
mac := hmac.New(sha256.New, []byte("secret"))
mac.Write([]byte("Message whose authenticity we want to guarantee"))
macBS := mac.Sum(nil) //

falseMac := []byte("someFalseMacAsAnArrayOfBytes")
equal := hmac.Equal(falseMac, macBS)
fmt.Println(equal)  //false - therefore the message to which this hmac is attached has been tampered

加密

除了Go标准密码库外, Go生态系统中还有其他与密码学相关的软件包。其中之一是加密.

bcrypt包是流行的哈希算法的Go实现加密。 Bcrypt是用于哈希密码的行业标准算法, 大多数语言都具有某种形式的bcrypt实现。

在这个软件包中, 我们可以使用GenerateFromPassword功能和成本。

hash, err := bcrypt.GenerateFromPassword("password", bcrypt.DefaultCost)

然后, 我们可以通过执行以下操作来检查给定的密码是否与给定的哈希匹配:

err := bcrypt.CompareHashAndPassword([]byte("hashedPassword"), []byte("password"))

总结

本文就是这样!希望本文能使你了解Go生态系统的稳健性, 至少在密码学方面。你还可以签出Go标准库的内容这里看看Go带来了什么。

日志火箭:全面了解你的网络应用

LogRocket仪表板免费试用横幅

日志火箭是一个前端应用程序监视解决方案, 可让你重播问题, 就好像问题发生在你自己的浏览器中一样。 notlogy无需猜测错误发生的原因, 也不要求用户提供屏幕截图和日志转储, 而是让你重播会话以快速了解出了什么问题。无论框架如何, 它都能与任何应用完美配合, 并具有用于记录来自Redux, Vuex和@ ngrx / store的其他上下文的插件。

除了记录Redux动作和状态外, notlogy还会记录控制台日志, JavaScript错误, 堆栈跟踪, 带有标题+正文, 浏览器元数据和自定义日志的网络请求/响应。它还使用DOM来记录页面上的HTML和CSS, 甚至可以为最复杂的单页面应用程序重新创建像素完美的视频。

免费试用

.

一盏木

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: