web-dev-qa-db-ja.com

認証局による証明書要求への署名

TLS相互認証を使用して、goで作成されたAPIでクライアントを認証したいと思います。認証局を作成しました。ボブがクライアントで使用したいキーペアを持っているとしましょう。ボブは証明書要求を作成し、APIで承認および認証されるために、彼の証明書を検証するように要求しました。

これを使用して認証局を作成しました:

openssl genrsa -aes256 -out ca.key 4096
openssl req -new -x509 -sha256 -days 730 -key ca.key -out ca.crt

ボブはこれを使用して、証明書と証明書要求を作成しました。

openssl genrsa -out bob.key 4096
openssl req -new -key bob.key -out bob.csr

私はこれを達成したいのですが、行きます:

openssl x509 -req -days 365 -sha256 -in bob.csr -CA ca.crt -CAkey ca.key -set_serial 3 -out bob.crt

今のところ、これらのコマンドを使用して、ボブはこのtls.Configを使用して私のAPIへのTLS接続を作成できます。

func createTLSConfig(certFile string, keyFile string, clientCAFilepath string) (config *tls.Config, err error) {
    cer, err := tls.LoadX509KeyPair(certFile, keyFile)
    if err != nil {
        return nil, err
    }

    clientCAFile, err := ioutil.ReadFile(clientCAFilepath)
    if err != nil {
        return nil, err
    }
    clientCAPool := x509.NewCertPool()
    clientCAPool.AppendCertsFromPEM(clientCAFile)

    config = &tls.Config{
        Certificates: []tls.Certificate{cer},
        ClientAuth: tls.RequireAndVerifyClientCert,
        ClientCAs:  clientCAPool,
        CipherSuites: []uint16{
            tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
            tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        },
        PreferServerCipherSuites: true,
        SessionTicketsDisabled:   false,
        MinVersion:               tls.VersionTLS12,
        CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384},
    }

    return config, nil
}

しかし、ジュリアが今ログインしたい場合はどうなりますか?彼女はCSRを作成して私に送信する必要があり、私も彼女のCSRをCRTに対して手動で検証する必要があります。この手動操作を回避するためのアイデアは、ジュリアがCSRを送信して有効なCRTを取得できるレジスタエンドポイントを用意することです。エンドポイントは基本的に次のようになります。

func Register(c echo.Context) (err error) {
    // get Julia's csr from POST body
    csr := certificateFromBody(c.Body)

    // valid csr with ca to generate the crt
    crt := signCSR(csr, config.ClientCAPath)

    // return the crt to Julia
    return c.JSON(http.StatusCreated, base64.StdEncoding.EncodeToString(crt))
}

OpensslがCAを使用してCRSからCRTを作成する方法を理解するために時間を費やしましたが、成功しませんでした。

Golangには、 ParseCertificateRequest で作成できるcrypto/x509パッケージの CertificateRequestオブジェクト がありますが、このオブジェクトとCAを取得して、を返す関数が見つかりません。証明書。

ご協力ありがとうございました!

9
krostar

x509.CreateCertificate を使用できる場合があります。

CreateCertificateのパラメーターの1つは、「テンプレート」証明書です。

Juliaの CertificateRequest のフィールドを使用して、テンプレート証明書のフィールドを設定できます。

Goの generate cert スクリプトは、CreateCertificateの使用例を示しています。

これは、JuliaからのAPIリクエストが実際にはJuliaからのものであり、リクエストに署名して証明書を返すのに十分な信頼性があることを前提としています。

また、 GoでTLSに独自のPKIを使用する が役立つ場合があります。

1
Mark

これで機能します。CAを使用してCRTからCSRを検証するための基本的なソリューションは次のとおりです。

  • cA証明書をロードする
  • ca秘密鍵をロードする(パスワード付き)
  • ロードボブCSR
  • cSRおよびCA情報を使用して証明書テンプレートを作成します
  • テンプレートからCA秘密鍵を使用して証明書を生成します
  • ボブの証明書を保存します

実例:

package main

import (
    "crypto/Rand"
    "crypto/x509"
    "encoding/pem"
    "io/ioutil"
    "math/big"
    "os"
    "time"
)

func crsToCrtExample() {
    // load CA key pair
    //      public key
    caPublicKeyFile, err := ioutil.ReadFile("certs/ca-root.crt")
    if err != nil {
        panic(err)
    }
    pemBlock, _ := pem.Decode(caPublicKeyFile)
    if pemBlock == nil {
        panic("pem.Decode failed")
    }
    caCRT, err := x509.ParseCertificate(pemBlock.Bytes)
    if err != nil {
        panic(err)
    }

    //      private key
    caPrivateKeyFile, err := ioutil.ReadFile("certs/ca-mutu.key")
    if err != nil {
        panic(err)
    }
    pemBlock, _ = pem.Decode(caPrivateKeyFile)
    if pemBlock == nil {
        panic("pem.Decode failed")
    }
    der, err := x509.DecryptPEMBlock(pemBlock, []byte("ca private key password"))
    if err != nil {
        panic(err)
    }
    caPrivateKey, err := x509.ParsePKCS1PrivateKey(der)
    if err != nil {
        panic(err)
    }

    // load client certificate request
    clientCSRFile, err := ioutil.ReadFile("certs/bob.csr")
    if err != nil {
        panic(err)
    }
    pemBlock, _ = pem.Decode(clientCSRFile)
    if pemBlock == nil {
        panic("pem.Decode failed")
    }
    clientCSR, err := x509.ParseCertificateRequest(pemBlock.Bytes)
    if err != nil {
        panic(err)
    }
    if err = clientCSR.CheckSignature(); err != nil {
        panic(err)
    }

    // create client certificate template
    clientCRTTemplate := x509.Certificate{
        Signature:          clientCSR.Signature,
        SignatureAlgorithm: clientCSR.SignatureAlgorithm,

        PublicKeyAlgorithm: clientCSR.PublicKeyAlgorithm,
        PublicKey:          clientCSR.PublicKey,

        SerialNumber: big.NewInt(2),
        Issuer:       caCRT.Subject,
        Subject:      clientCSR.Subject,
        NotBefore:    time.Now(),
        NotAfter:     time.Now().Add(24 * time.Hour),
        KeyUsage:     x509.KeyUsageDigitalSignature,
        ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
    }

    // create client certificate from template and CA public key
    clientCRTRaw, err := x509.CreateCertificate(Rand.Reader, &clientCRTTemplate, caCRT, clientCSR.PublicKey, caPrivateKey)
    if err != nil {
        panic(err)
    }

    // save the certificate
    clientCRTFile, err := os.Create("certs/bob.crt")
    if err != nil {
        panic(err)
    }
    pem.Encode(clientCRTFile, &pem.Block{Type: "CERTIFICATE", Bytes: clientCRTRaw})
    clientCRTFile.Close()
}

ありがとうマーク!

11
krostar

これは、PKIに関するブログ投稿用に作成したデモプログラムのコードスニペットです。投稿全体: https://anchorloop.com/2017/09/25/security-iq-ii-public-key-infrastructure/

// Now read that number of bytes and parse the certificate request
asn1Data := make([]byte, asn1DataSize)
_, err = reader.Read(asn1Data)
if err != nil {
    return err
}
fmt.Println("Received Certificate Signing Request.")
certReq, err := x509.ParseCertificateRequest(asn1Data)
if err != nil {
    return err
}

// Create template for certificate creation, uses properties from the request and root certificate.
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := Rand.Int(Rand.Reader, serialNumberLimit)
if err != nil {
    return err
}
template := x509.Certificate {
    Signature: certReq.Signature,
    SignatureAlgorithm: certReq.SignatureAlgorithm,

    PublicKeyAlgorithm: certReq.PublicKeyAlgorithm,
    PublicKey: certReq.PublicKey,

    SerialNumber: serialNumber,
    Issuer: rootCert.Subject,
    Subject: certReq.Subject,
    NotBefore: time.Now(),
    NotAfter: time.Now().Add(time.Hour * 24 * 365),
    KeyUsage: x509.KeyUsageDigitalSignature,
    ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}

// Create certificate from template and root certificate, signed by the RootCA's private key.
certData, err := x509.CreateCertificate(Rand.Reader, &template, rootCert, template.PublicKey, privateKey)
if err != nil {
    return err
}
fmt.Println("Created Certificate from CSR, signed by RootCA's Private Key.")

基本的に:

  • CSRは、クライアントによって作成および送信されます。
  • 署名証明書の所有者はそれを解析し、CSRと署名証明書のプロパティの組み合わせから新しいx509.Certificateを作成します。
  • 署名者の秘密鍵はx509.CreateCertificateに渡されて署名されます。
  • その後、クライアントに送り返すことができます。

それがお役に立てば幸いです。

0
Kirk MacPhee