解析 非对称加密之RSA (go语言实现)

坚持原创,如有错误请指正

非对称加密是区块链常用的一类加密方式,比如说有下面几种:

RSA

secp256k1 (椭圆曲线)

ElGamal等等

接下来我会尝试用明白易懂的语言讲解

讲解RSA之前,很多同学可能跟比特币的签名方式搞混,所以我就先简单的说下比特币转账签名.比特币采用UTXO模型(例子,我有5btc,我要给你转1btc. 实际操作是5个btc全转出,1btc给你,剩下4个btc再转给我自己,每次交易等式都要配平),扯远了.

每次的转账记录可以简单地看为

{
"付款地址":A
"收款地址":B
"转账数量":1btc
}  ==>计算其hash为H

接下来我要把该交易发送到其他节点,其他节点接收到广播信息,会首先进行验证,如果验证通过则会再次转发到其他节点中, 最终交易池会选取该交易,等待矿工打包入区块中(等待时间由网络拥堵状况和手续费等因素决定).

转账第一步,对这笔交易进行消息摘要,也就是计算其hash,唯一的交易交易记录有唯一的hash值,碰撞的概率接近于零.假设hash为H.
第二步,对这个hash进行签名,其实这里就是一个非对称加密了.
使用我的账户的私钥对 H 进行加密,得到一段密文.其他节点使用我的公钥对其解密,得到一段代码H2 . 如果H2与通过转账记录计算得到的H相等,验证通过.
签名过程即为用私钥对交易摘要的加密过程,验证过程为用公钥解密的过程(实际上规则要更复杂一些,这里只是方便大家理解).

接下来初步的讲下RSA
RSA也分为公钥和私钥
密文=明文(待加密) ^E(E次方) 再取N的余数 ==>加密
明文(待加密)=密文 ^D(D次方) 再取N的余数 ==>解密
与比特币签名不同的是,这里是公钥加密,私钥解密(加密快,解密慢)
比特币签名(加密)慢,验证(解密)快.

D 21616157824859089947467865763905877208766194802261304393547211764767872553922733033101317695599363434608856645166753
N 26861170560633109174956418630434195575102580750015151802056322369493708310600722979313108414200817132792257978619899
E 65537

将公钥私钥进行打印,发现
公钥格式{N E}
私钥格式{{N E} D [..........]}

可以发现,公钥长度较短, 私钥长度较长,私钥中包含了 公钥

接下来计算创建私钥花费的时间,直观的让你明白RSA的效率
func main() {
    var timeSum time.Duration
    i := 0
    bits := 2304//创造自定义位数的key
    for ; i < 60; {
        now1 := time.Now()
        
        test.GenerateRsaKey(bits)//调用test包下的方法
        now2 := time.Now()
        timeSub := now2.Sub(now1)
        timeSum += timeSub
        i++
        fmt.Println("第", i, "次任务,耗时毫秒数", timeSub.Nanoseconds()/1000000)
    }
    fmt.Println("平均毫秒数为", int64(timeSum.Nanoseconds())/int64(1000000)/int64(i), ",运行次数为", i, ",位数为", bits)
}
func GenerateRsaKey(bits int) {

   privateKey, _ := rsa.GenerateKey(rand.Reader, bits)
   fmt.Println("D",privateKey.D) 
   fmt.Println("N",privateKey.N)
   fmt.Println("E",privateKey.E)
}

不同长度密钥(单位bit),创建私钥任务耗时如下
平均毫秒数为 1699 ,运行次数为 60 ,位数为 2304
平均毫秒数为 885 ,运行次数为 60 ,位数为 2048
平均毫秒数为 581 ,运行次数为 60 ,位数为 1792
平均毫秒数为 79 ,运行次数为 60 ,位数为 1024
平均毫秒数为 35 ,运行次数为 60 ,位数为 768
平均毫秒数为 11 ,运行次数为 60 ,位数为 512
平均毫秒数为 5 ,运行次数为 60 ,位数为 384

随着位数的提高,耗时指数性增长.

抗暴力破解能力对比图,本图截取自<应用密码学>.可以看出相同密钥长度,非对称加密比不上对称加密,而非对称加密又是个极耗资源的算法.


image.png

生成公钥私钥

func GenerateRsaKey(bits int) {

    privateKey, _ := rsa.GenerateKey(rand.Reader, bits)
    
    //保存私钥
    //使用X509规范,对公钥私钥进行格式化
    x509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)

    //pem格式编码 将所有内容替换为0-9 a-z A-Z, 基于base64解码 末尾=用于补齐,末尾=个数为0-2个.

    block := pem.Block{
        Type:  "pem私钥",
        Bytes: x509PrivateKey,
    }
    fmt.Println(block)
    privateKeyFile, _ := os.Create("private.pem")
    pem.Encode(privateKeyFile, &block)
    privateKeyFile.Close()
    //保存公钥
    x509publicKey := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)

    publicBlock := pem.Block{
        Type:  "公钥",
        Bytes: x509publicKey,
    }
    publicKeyFile, _ := os.Create("public.pem")
    pem.Encode(publicKeyFile, &publicBlock)
    publicKeyFile.Close()

}

接下来进行加密解密操作
代码千篇一律

func main() {
    
    var timeSum time.Duration
    i := 0
    bits := 2304//设置密钥位数
    for ; i < 100; {
        now1 := time.Now()
        ////创造多长的rsa
        //test.GenerateRsaKey(bits)
        //明文
         text :=[]byte("我就是明文啦,要对我进行加密")
        rsa_encrypt := test.Rsa_Encrypt(text, "public.pem")
        fmt.Println("密文为", string(rsa_encrypt))
        rsa_decrypt := test.Rsa_Decrypt(rsa_encrypt, "private.pem")
        fmt.Println("原文为", string(rsa_decrypt))
        now2 := time.Now()
        timeSub := now2.Sub(now1)
        timeSum += timeSub
        i++
        fmt.Println("第", i, "次任务,耗时毫秒数", timeSub.Nanoseconds()/1000000)
    }
    fmt.Println("平均毫秒数为", int64(timeSum.Nanoseconds())/int64(1000000)/int64(i), ",运行次数为", i, ",位数为", bits)
    fmt.Println("总耗时为", int64(timeSum.Nanoseconds())/int64(1000000), "毫秒")
}

被调用部分代码

func Rsa_Encrypt(plainText []byte,path string) []byte{
    //1.读取文件
    file, _ := os.Open(path)
    // 获取文件信息
    fileInfo, _ := file.Stat()

    //2.读取文件的内容 pem
    data := make([]byte,fileInfo.Size())
    file.Read(data)
    //3.pem解码 --- x509编码的内容
    block, _ := pem.Decode(data)
    //4.x509解码(反序列化)
    publicKey, _ := x509.ParsePKCS1PublicKey(block.Bytes)

    cipherText, _ := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
    return cipherText
}
// 解密操作
func Rsa_Decrypt(cipherText []byte,path string) []byte{
    //1.读取文件
    file, _ := os.Open(path)
    //2.读取文件的内容
    fileInfo, _ := file.Stat()
    data := make([]byte,fileInfo.Size())
    file.Read(data)

    //3.pem 解码
    block, _ := pem.Decode(data)
    //4.x509解码
    privateKey, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
    //5. 解密操作
    plainText, _ := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)

    return plainText
}

平均毫秒数为 15 ,运行次数为 100 ,位数为 2304,总耗时为1506毫秒
平均毫秒数为 1 ,运行次数为 100 ,位数为 768,总耗时为 155 毫秒

由此可见,随着位数的增加,加密解密操作耗时指数型增长,感兴趣的可以与对称加密进行对比,对相同明文进行加密解密,效率有极大不同. 对称加密快很多.

接下来进行一个实验
设置密钥位数 bits=768
明文为 sdfdsfsdggs1231241412`efwe23efqfeqafqawegeetwt23452346ty24wgesewrwqfsaegvsaedaesd3ear

运行程序,输出结果
密文为 !���yɄm���"cj0!nt��g2cY���YJw@R���֏C'��&�D*'R0c�NU��^��W�i�����,g�Ā���-��z]����"
��\N��

将明文结尾再添加X
明文为sdfdsfsdggs1231241412`efwe23efqfeqafqawegeetwt23452346ty24wgesewrwqfsaegvsaedaesd3earX
运行程序
发现密文没有输出,原因是明文长度超出范围了.
现在对bits进行操作,将768改为1024
运行程序,这次有结果输出了.
密文为 "L������ׇ��d��'��U�JP���MT��k��� aʅܚJ��\�������\w��ئ�"':��qp;��.}_�����]�KF������ ��?(����F,�*��紛+�GA�x�͘ʩ�6�N��� �=���

为什么呢?
文章最上面说过
密文=明文(待加密) ^E(E次方) 再取N的余数
也就是说N限制了明文的最大长度. 如果明文过长,就会发生碰撞(不同的明文产生了相同的密文,这里可以好好理解下),而N与密钥长度有关.

1.可加密明文的最大长度与密钥长度有关,密钥越长,可加密明文的最大长度越长.
2.rsa可加密的数据仍然很小,传输大数据不太可能.
假设用rsa加密一部电影,需要将电影进行拆分,分开加密解密,按照非对称加密的效率,几十年可能都搞不定.
引申:加密大文件,要用对称加密先加密,再将对称加密的密钥进行非对称加密.这样就能兼顾效率和安全.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容