SSL/TLS实战以及在kubeadm中的应用(上)

背景介绍

kubeadm是Kubernetes提供的自动化部署方案,可以极大地简化广为诟病的Kubernetes集群搭建的复杂性。kubeadm init命令对Kubernetes集群主节点的初始化流程中包含了以下若干步骤:

  1. PreflightPhase
  2. KubeletStartPhase
  3. CertsPhase
  4. KubeConfigPhase
  5. ControlPlanePhase
  6. EtcdPhase
  7. UploadConfigPhase
  8. UploadCertsPhase
  9. MarkControlPlanePhase
  10. BootstrapTokenPhase
  11. AddonPhase

CertsPhase用于生成https服务端/客户端需要使用的证书、公钥/秘钥等。

这一部分大量使用到了go函数库中crypto模块的功能。一般而言,crypto模块的功能在日常开发中涉及较少,对kubeadm代码会造成不小的障碍。本文梳理了go语言标准库中crypto模块常用的场景,以及针对各场景给出了简短的代码示例。本文主要关注加密、解密等安全功能在go语言中的实践,相关的理论知识(比如各算法的原理、产生背景以及应用)此处不再赘述。

小文件的hash计算

import (
   "crypto/md5"
   "crypto/sha1"
   "crypto/sha256"
   "crypto/sha512"
   "fmt"
   "io/ioutil"
)
data, err := ioutil.ReadFile(filename)
// 计算文件内容的hash值,并输出
fmt.Printf("Md5: %x\n\n", md5.Sum(data))
fmt.Printf("Sha1: %x\n\n", sha1.Sum(data))
fmt.Printf("Sha256: %x\n\n", sha256.Sum256(data))
fmt.Printf("Sha512: %x\n\n", sha512.Sum512(data))

大文件的hash计算

// 打开文件
file, err := os.Open(filename)
// 构建hasher对象(实现了writer接口)
hasher := md5.New()
_, err = io.Copy(hasher, file)
checksum := hasher.Sum(nil)

对称加密

生成随机数的三种方法

我们可以将生成随机数视为生成秘钥。

方法1
import (
   "crypto/rand"
   "math"
   "math/big"
)
limit := int64(math.MaxInt64) // 允许的最大值
randInt, err := rand.Int(rand.Reader, big.NewInt(limit))
方法2

binary.Read()方法将读取足够的字节以填充相应的数据类型

import (
   "crypto/rand"
   "encoding/binary"
)
var number uint32
err = binary.Read(rand.Reader, binary.BigEndian, &number)
方法3

直接生成相应长度的字节码slice

import (
   "crypto/rand"
)
numBytes := 4
randomBytes := make([]byte, numBytes)
rand.Read(randomBytes)
fmt.Println("Random byte values: ", randomBytes)

加密算法

常用的对称加密算法是AES,而我们偶尔能够听到的DES是一个相对于AES更老的版本。下面我们演示的例子是基于go标准库中提供的aes算法实现。初始向量(initialization vector)的作用是使得对相同消息的加密结果也不一样,进一步提升数据的安全性。

import (
   "crypto/aes"
   "crypto/cipher"
   "crypto/rand"
   "io"
)
func encrypt(key, message []byte) ([]byte, error) {
   
   // 初始化block cipher
   block, err := aes.NewCipher(key)

   // 创建字节slice以持有后续产生的加密消息
   cipherText := make([]byte, aes.BlockSize+len(message))

   // 生成Initialization Vector (IV) nonce
   // 这部分数据存储在slice的头部
   // 其长度与AES的block大小一样
   iv := cipherText[:aes.BlockSize]
   _, err = io.ReadFull(rand.Reader, iv)

   // 选择block cipher的工作模式
   // 此处使用cipher feedback (CFB)模式
   // 当然,CBCEncrypter也是一个备选项
   cfb := cipher.NewCFBEncrypter(block, iv)
   // 生成加密的消息,并将其存储在剩下的字节slice中
   cfb.XORKeyStream(cipherText[aes.BlockSize:], message)

   return cipherText, nil
}

解密

// AES解密
func decrypt(key, cipherText []byte) ([]byte, error) {
   
   // 初始化block cipher
   block, err := aes.NewCipher(key)

   // 将IV nonce与加密消息分开
   iv := cipherText[:aes.BlockSize]
   cipherText = cipherText[aes.BlockSize:]

   // 使用CFB block模式将加密消息解密
   cfb := cipher.NewCFBDecrypter(block, iv)
   cfb.XORKeyStream(cipherText, cipherText)

   return cipherText, nil
}

非对称加密

由于对称加密需要通讯双方共享秘钥,这一要求在互联网环境中会成为安全的隐患。由此安全专家们提出了RSA等非对称加密算法。

生成公钥/秘钥对

等价的openssl命令如下

# 生成秘钥
openssl genrsa -out priv.pem 2048
# 从私钥中提取公钥
openssl rsa -in priv.pem -pubout -out public.pem

对应的go代码如下

import (
   "crypto/rand"
   "crypto/rsa"
   "crypto/x509"
   "encoding/pem"
   "os"
)
// 生成公钥/秘钥对,并将其存储在PEM格式的文件中

privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
// 分别将秘钥公钥进行PEM格式的编码
privatePem := getPrivatePemFromKey(privateKey)
publicPem := generatePublicPemFromKey(privateKey.PublicKey)
// Save the PEM output to files   
savePemToFile(privatePem, privatePemFilename)  
savePemToFile(publicPem, publicPemFilename
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值