欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

实现数字签名

程序员文章站 2024-03-19 13:05:10
...

实现数字签名

数字签名算法(DSA)

数字签名算法(DSA,Digital Signature Algorithm),是一种公开**算法,不能用于加密,只能用于数字签名。主要用作为接收者验证数字的完整性和数据发送者的身份,DSA算法的安全性基于解离散对数的困难性。

package main

import (
    "crypto/dsa"
    "crypto/rand"
    "fmt"
)

func main() {
    var params dsa.Parameters

    //生成参数 
    if e := dsa.GenerateParameters(&params, rand.Reader, dsa.L1024N160); e != nil {
        fmt.Println(e)
    }

    //生成私钥 
    var priv dsa.PrivateKey

    priv.Parameters = params
    if e := dsa.GenerateKey(&priv, rand.Reader); e != nil {
        fmt.Println(e)
    }

    //根据私钥生成公钥 
    pub := priv.PublicKey

    //消息 
    message := []byte("hello world")

    //使用私钥进行签名,产生整数对(r,s) 
    r, s, e := dsa.Sign(rand.Reader, &priv, message)
    if e != nil {
        fmt.Println(e)
    }

    //认证 
    fmt.Printf("认证 %q (r:%s,s:%s)\n", message, r, s)
    if dsa.Verify(&pub, message, r, s) {
        fmt.Println("认证正确!")
    } else {
        fmt.Println("认证失败!")
    }
}

公钥加密算法(RSA)

公钥加密算法于1987年首次公开,RSA是提出这个算法的三人姓氏开头字母组成,可用于加密,也可以用于数字签名。RSA的安全性基于大数分解的困难性。

加密算法:

  • 最优非对称加密填充(OAEP,Optimal Asymmetric Encryption Padding),在随机预言模型下,用来处理非对称加密前的明文;

  • 公钥密码学标准(PKCS,The Public-Key Cryptography Standards),是由美国RSA数据安全公司及其合作伙伴制定的一组公钥密码学标准,其中包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。

签名认证:

  • 公钥密码学标准(PKCS);

  • 概率签名方案(PSS,Probabilistic Signature Scheme),与PKCS不同的是,它支持添加盐(Salt)。

package main

import (
    "crypto"
    "crypto/md5"
    "crypto/rand"
    "crypto/rsa"
    "fmt"
)

func main() {
    //生成私钥 
    priv, e := rsa.GenerateKey(rand.Reader, 1024)
    if e != nil {
        fmt.Println(e)
    }

    //根据私钥产生公钥 
    pub := &priv.PublicKey

    //明文 
    plaintext := []byte("Hello world")

    //加密生成密文 
    fmt.Printf("%q\n加密:\n", plaintext)
    ciphertext, e := rsa.EncryptOAEP(md5.New(), rand.Reader, pub, plaintext, nil)
    if e != nil {
        fmt.Println(e)
    }
    fmt.Printf("\t%x\n", ciphertext)

    //解密得到明文 
    fmt.Printf("解密:\n")
    plaintext, e = rsa.DecryptOAEP(md5.New(), rand.Reader, priv, ciphertext, nil)
    if e != nil {
        fmt.Println(e)
    }
    fmt.Printf("\t%q\n", plaintext)

    //消息先进行Hash处理 
    h := md5.New()
    h.Write(plaintext)
    hashed := h.Sum(nil)
    fmt.Printf("%q MD5 Hashed:\n\t%x\n", plaintext, hashed)

    //签名 
    opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.MD5}
    sig, e := rsa.SignPSS(rand.Reader, priv, crypto.MD5, hashed, opts)
    if e != nil {
        fmt.Println(e)
    }
    fmt.Printf("签名:\n\t%x\n", sig)

    //认证 
    fmt.Printf("验证结果:")
    if e := rsa.VerifyPSS(pub, crypto.MD5, hashed, sig, opts); e != nil {
        fmt.Println("失败:", e)
    } else {
        fmt.Println("成功.")
    }
}

椭圆曲线加密算法

ECDSA的全名是Elliptic Curve DSA,即椭圆曲线DSA。它是Digital Signature Algorithm (DSA)应用了椭圆曲线加密算法的变种。椭圆曲线算法的原理很复杂,但是具有很好的公开**算法特性,通过公钥无法逆向获得私钥。

  1. 签名过程

    假设要签名的消息是一个字符串:“Hello World!”。DSA签名的第一个步骤是对待签名的消息生成一个消息摘要。不同的签名算法使用不同的消息摘要算法。而ECDSA256使用SHA256生成256比特的摘要。
    摘要生成结束后,应用签名算法对摘要进行签名:
    产生一个随机数k
    利用随机数k,计算出两个大数r和s。将r和s拼在一起就构成了对消息摘要的签名。
    这里需要注意的是,因为随机数k的存在,对于同一条消息,使用同一个算法,产生的签名是不一样的。从函数的角度来理解,签名函数对同样的输入会产生不同的输出。因为函数内部会将随机值混入签名的过程。

  2. 验证过程
    关于验证过程,这里不讨论它的算法细节。从宏观上看,消息的接收方从签名中分离出r和s,然后利用公开的**信息和s计算出r。如果计算出的r和接收到的r值相同,则表示验证成功。否则,表示验证失败。

package main

import (
    "fmt"
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/sha256"
    "math/big"
)

func main() {
    //明文 
    message := []byte("Hello world")

    key, err := NewSigningKey()
    if err != nil {
        return
    }

    signature, err := Sign(message, key)

    fmt.Printf("签名后:%x\n", signature)
    if err != nil {
        return
    }

    if !Verify(message, signature, &key.PublicKey) {
        fmt.Println("验证失败!")
        return
    }else{
        fmt.Println("验证成功!")
    }
}

func NewSigningKey() (*ecdsa.PrivateKey, error) {
    key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    return key, err
}

// Sign signs arbitrary data using ECDSA.
func Sign(data []byte, privkey *ecdsa.PrivateKey) ([]byte, error) {
    // hash message
    digest := sha256.Sum256(data)

    // sign the hash
    r, s, err := ecdsa.Sign(rand.Reader, privkey, digest[:])
    if err != nil {
        return nil, err
    }

    // encode the signature {R, S}
    // big.Int.Bytes() will need padding in the case of leading zero bytes
    params := privkey.Curve.Params()
    curveOrderByteSize := params.P.BitLen() / 8
    rBytes, sBytes := r.Bytes(), s.Bytes()
    signature := make([]byte, curveOrderByteSize*2)
    copy(signature[curveOrderByteSize-len(rBytes):], rBytes)
    copy(signature[curveOrderByteSize*2-len(sBytes):], sBytes)

    return signature, nil
}

// Verify checks a raw ECDSA signature.
// Returns true if it's valid and false if not.
func Verify(data, signature []byte, pubkey *ecdsa.PublicKey) bool {
    // hash message
    digest := sha256.Sum256(data)

    curveOrderByteSize := pubkey.Curve.Params().P.BitLen() / 8

    r, s := new(big.Int), new(big.Int)
    r.SetBytes(signature[:curveOrderByteSize])
    s.SetBytes(signature[curveOrderByteSize:])

    return ecdsa.Verify(pubkey, digest[:], r, s)
}