📘

Golangで電子署名

2023/04/07に公開

この記事では、Golangを使用して電子署名を実装する方法について説明します。

電子署名とは?

電子署名は、電子的なデータを証明するために使用される技術です。電子署名は、電子メール、Webサイト、PDFファイル、ソフトウェアなど、さまざまな形式の電子データに適用することができます。電子署名は、データの完全性、真正性、および認証を確保することができます。

電子署名の作成

RSA秘密鍵を生成する必要があります。以下のコマンドを使用して、秘密鍵を生成することができます。

openssl genrsa -out private.pem 2048

秘密鍵から公開鍵を生成

次のコードで先ほどのコマンドで生成した秘密鍵から公開鍵を生成できます。

package main

import (
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
)

func main() {
    // ファイルを読み込む
    file, err := ioutil.ReadFile("example.txt")
    if err != nil {
        log.Fatalln(err)
    }

    // RSA秘密鍵を読み込む
    privateKeyBytes, err := ioutil.ReadFile("key.pem")
    if err != nil {
        log.Fatalln(err)
    }
    privateKeyBlock,_ := pem.Decode(privateKeyBytes)
    fmt.Println(privateKeyBlock)
    
    privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
    if err != nil {
        log.Fatalln(err)
    }

    // ファイルのハッシュ値を計算する
    hash := sha256.Sum256(file)

    // ハッシュ値に署名する
    signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
    if err != nil {
        panic(err)
    }

    // 署名をファイルに書き込む
    err = ioutil.WriteFile("example.txt.sig", signature, 0644)
    if err != nil {
        panic(err)
    }

    fmt.Println("電子署名が作成されました。")
}

秘密鍵で電子署名するコード

次のコードで、ファイルに対し、秘密鍵で電子署名ファイルを生成することができます。

package main

import (
    // "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "log"
    "io/ioutil"
)

func main() {
    // RSA秘密鍵を読み込む
    privateKeyBytes, err := ioutil.ReadFile("key.pem")
    if err != nil {
            log.Fatalln(err)
    }
    privateKeyBlock,_ := pem.Decode(privateKeyBytes)
    privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
    if err != nil {
        log.Fatalln(err)
    }

    // RSA秘密鍵からRSA公開鍵を生成する
    publicKey := privateKey.PublicKey

    // RSA公開鍵をPEM形式にエンコードする
    publicKeyBytes, err := x509.MarshalPKIXPublicKey(&publicKey)
    if err != nil {
        log.Fatalln(err)
    }
    publicKeyBlock := &pem.Block{
        Type:  "PUBLIC KEY",
        Bytes: publicKeyBytes,
    }
    publicKeyPem := pem.EncodeToMemory(publicKeyBlock)

    fmt.Println(string(publicKeyPem))
}

公開鍵で電子署名を検証するコード

次のコードで、公開鍵と電子署名ファイルにより、ファイルが変更されてないか確認できます。

package main

import (
    "crypto"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
)

func main() {
    // ファイルを読み込む
    file, err := ioutil.ReadFile("example.txt")
    if err != nil {
        log.Fatalln(err)
    }

    // 公開鍵を読み込む
    publicKeyBytes, err := ioutil.ReadFile("public_key.pem")
    if err != nil {
        log.Fatalln(err)
    }
    publicKeyBlock, _ := pem.Decode(publicKeyBytes)
    publicKey, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes)
    if err != nil {
        log.Fatalln(err)
    }

    // 署名を読み込む
    signature, err := ioutil.ReadFile("example.txt.sig")
    if err != nil {
        log.Fatalln(err)
    }

    // ファイルのハッシュ値を計算する
    hash := sha256.Sum256(file)

    // 署名を検証する
    err = rsa.VerifyPKCS1v15(publicKey.(*rsa.PublicKey), crypto.SHA256, hash[:], signature)
    if err != nil {
        log.Fatalln("電子署名が正しくありません。")
    }

    fmt.Println("電子署名が正しいことが確認されました。")
}

Discussion