加解密-RSA

RSA如果要加密长的信息时,需要在一个固定大小的块(block)上进行分块操作。

如果想支持256bit以上的明文,那么就需要自己实现分组加密。例如256bit密钥,采用了padding,那么一次可以加密的字符长度不能超过32-11=21个byte。同时分组加密后的明文在解密的时候也需要对应分组去解密。
EncryptPKCS1v15使用PKCS#1 v1.5规定的填充方案和RSA算法加密msg。明文长度(Bytes) <= 密钥长度(Bytes)- 11字节。
注意:使用本函数加密明文(而不是会话密钥)是危险的,请尽量在新协议中使用RSA OAEP。

RSA 的block size是跟key length 以及所使用的填充模式有关的。填充方式有以下几种。

RSA_PKCS1_PADDING 填充模式,最常用的模式。

输入block长度 : block size比RSA 钥模长(modulus) 短至少11个字节, 也就是RSA_size(rsa) – 11
输出结果长度 : 和modulus一样长

例如,对于1024bit的密钥,blockSize = 1024/8 – 11 = 117 字节

RSA_PKCS1_OAEP_PADDING

输入block长度:RSA_size(rsa) – 41
输出结果长度 : 和modulus一样长

RSA_NO_PADDING

不填充

解密的时候,如果密文长度过长,也需要切分成多个块进行解密,block size 和 key length 是相等的。


import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"os"
"flag"
"encoding/hex"
"bytes"
)

func splitBySize(src []byte, splitSize int) [][]byte {
srcSize := len(src)

var arr [][]byte
if srcSize <= splitSize {
arr = append(arr, src)
} else {
groups := len(src) / splitSize
for i := 0; i < groups; i++ {
block := src[:splitSize]

arr = append(arr, block)
src = src[splitSize:]

if 0 == len(src) {
break
}

if len(src) < splitSize {
arr = append(arr, src)
}
}
}
return arr
}

// 加密
func RsaEncrypt(publicKey []byte, origData []byte) ([]byte, error) {
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, errors.New("decode public key error")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, errors.New("parse public key error")
}
pub := pubInterface.(*rsa.PublicKey)
//如果明文大约密钥长度-11 需要分段加密
blockSize := (pub.N.BitLen()+7)/8 - 11

blocks := splitBySize(origData, blockSize)
buffer := bytes.Buffer{}
for _, block := range blocks {
encData, err := rsa.EncryptPKCS1v15(rand.Reader, pub, block)
if err != nil {
return nil, err
}
buffer.Write(encData)
}

return buffer.Bytes(), nil

}

// 解密
func RsaDecrypt(privateKey []byte, encData []byte) ([]byte, error) {
//解码pem格式的私钥------BEGIN RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("private key error!")
}

var key interface{}
var errParsePK error
if block.Type == "RSA PRIVATE KEY" {
//RSA PKCS1
key, errParsePK = x509.ParsePKCS1PrivateKey(block.Bytes)
} else if block.Type == "PRIVATE KEY" {
//pkcs8格式的私钥解析
key, errParsePK = x509.ParsePKCS8PrivateKey(block.Bytes)
}

if errParsePK != nil {
return nil, errParsePK
}
priv := key.(*rsa.PrivateKey)
blockSize := (priv.N.BitLen()) / 8

blocks := splitBySize(encData, blockSize)

buffer := bytes.Buffer{}
for _, block := range blocks {
encData, err := rsa.DecryptPKCS1v15(rand.Reader, priv, block)
if err != nil {
return nil, err
}
buffer.Write(encData)
}

return buffer.Bytes(), nil
}

var kpub = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQ29tNQd8P3RtLuxdSJUUdpVkK
WmPzrnkgVT58ENoOseqx3MUKSJhHnPWnbf4nB6+koG3pdw5g1hO4C8mLUQhXNGKu
9P9u5VtdVmSbhX9bfh5sxtAZZnjp4QAQppGwCuso8OkKuPsTCpTb+e69O2XkYlM3
wybBK7bEgfBvV4/4pwIDAQAB
-----END PUBLIC KEY-----`

var kpriv = `-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDQ29tNQd8P3RtLuxdSJUUdpVkKWmPzrnkgVT58ENoOseqx3MUK
SJhHnPWnbf4nB6+koG3pdw5g1hO4C8mLUQhXNGKu9P9u5VtdVmSbhX9bfh5sxtAZ
Znjp4QAQppGwCuso8OkKuPsTCpTb+e69O2XkYlM3wybBK7bEgfBvV4/4pwIDAQAB
AoGASBzhJ1erEOsi1Y8PiklcB9RjXfO/tw9yuvszy9p1r8U2Z5r2XYqbKX7EQr7z
lb5NouXRjV6SFFVWEaHgQL4FxpZcKC7Agq4LcD8y4K2d7lntK33XINSVkfNbF7YO
l9AseCEj/3rYef4xuMVyYLcOP1Iu+5WKZEBtiw/GI/0n/kECQQDabkF8aOpsvzOu
u3dBt3NPq54kR2m2aH8VFHHWpYSWwwZuPQ68Vj5F/mYyPZsNFqJBS5lkEVDJ7icy
X/GgNHh5AkEA9MgkCWRQoHRb204XuVIFgebST8fUhwmCi1TkFSJz9s0JJnGCctjR
bybr5spzfEsdBCHBWLnTRktbrZaDMyvyHwJAfzYnPWV84ciZOPJFCijlJ0kz2L0e
e/Vvzb8SbYgIKCV84Ub+BgHUUr9OlYP7gSvlY1G8C+NpwbDJczUda8gjQQJAYjk1
jLc+S0Sl33iUuH6bSycOWYk8VPuR8MsdwwVSN0TLWSvPRrqOP+YEX1X5xXR2rkPF
TA81Ik32+c8d0pjdzQJAfYYxlPUsrmsMOp9ywpHKfsn64nZGYOqdNmjAbMHu9vrJ
WvEW2Y7BmwhVmY10DOIzt0JOA/XTbdQ6UMqEoNVylw==
-----END RSA PRIVATE KEY-----`

func doTest(plainMsg string) {

encData, err := RsaEncrypt([]byte(kpub), []byte(plainMsg))
if err != nil {
fmt.Println("Error")
fmt.Println(err.Error())
}
fmt.Println(hex.EncodeToString(encData))

origData, err := RsaDecrypt([]byte(kpriv), encData)
if err != nil {
fmt.Println("Error")
fmt.Println(err.Error())
return
}

plainTxt := string(origData)
fmt.Println(plainTxt)

if plainMsg != plainTxt{
fmt.Println("encrypt and decrypt Error")
}else {
fmt.Println("encrypt and decrypt ok")
}
}

func main() {

//GenRsaKey(1024)

// fmt.Println(os.Args[1:])

plainMsg := flag.String("plainMsg", "", "plain message to encrypt")
encMsg := flag.String("encMsg", "", "ecrypted message to decrypt")
isEnc := flag.Bool("isEnc", false, "1 is encrypt, 0 is decrypt")
flag.Parse()

if *plainMsg == "" && *encMsg == "" {
fmt.Println("Error: no param")
return
}

doTest(*plainMsg)

if *isEnc {
encData, err := RsaEncrypt([]byte(kpub), []byte(*plainMsg))
if err != nil {
fmt.Println("Error")
fmt.Println(err.Error())
}
fmt.Println(hex.EncodeToString(encData))
} else {
bData, err := hex.DecodeString(*encMsg)
if err != nil {
fmt.Println("Error")
fmt.Println(err.Error())
return
}
origData, err := RsaDecrypt([]byte(kpriv), bData)
if err != nil {
fmt.Println("Error")
fmt.Println(err.Error())
return
}
fmt.Println(string(origData))
}
}

先说PEM,最初是为了增强邮件安全,将X.509证书用base64进行重新编码随着邮件一起传输,现在基本就用来生成公钥/私钥文件供用户下载了,原本的功能反而被忘记了。

这里有了个新概念,X.509,这是由国际电信联盟(ITU-T)制定的ASN.1规范下数字证书标准,它规定了证书应包含哪些信息和使用什么样的编码格式(默认DER二进制编码)。

ASN.1,Abstract Syntax Notation One,抽象语法标记,一种ISO/ITU-T 标准,描述了一种对数据进行表示、编码、传输和解码的数据格式,嗯,这么说不好理解,可以类比下开发语言提供的各种基础类型,X.509使用了这些基础类型来定义了数字证书的数据结构。(参考链接,ASN.1)

PKSC,The Public-Key Cryptography Standards,公钥密码学标准,由美帝的RSA公司制定的一系列标准,这里我们只讨论#7/#8/#12,#7/#12是对X.509证书进行扩展、加密用于交换。#8是一种私钥格式标准。openssl生成的私钥,可以转换成pkcs8格式。

总结下:
ASN.1 提供了抽象语法,类似于编程语言中的关键字。
X.509 是使用这些抽象语法定义了证书的数据结构和编码规范。
PEM是将X.509基础证书用base64重新编码存为ASCII文件,用于和邮件一起传输保证邮件安全性。
PKCS是美帝RSA公司制定的一系列规范,#7/#12对基础X.509证书和证书对应的私钥进行扩充、加密、重新编码后用于交换,#8定义了一种私钥格式标准。