メインコンテンツまでスキップ

DigestInfo を付与したハッシュを用いた署名作成

このページでは、DigestInfo(RFC 8017 で定義されたハッシュ値の構造体)を付与したハッシュを用いた署名作成を説明します。

この方式は、署名対象データと署名値を J-LIS 等に提出する場合の署名方式で必要とされる形式です。

シーケンス

実装例

以下のコード例では、DigestInfo を付与したハッシュを用いた署名トランザクションを開始します。

署名対象データ

利用規約に同意します。
package main

import (
"bytes"
"crypto/sha256"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
)

const (
// デジタル庁から発行されたクライアント ID です。ご自身のクライアント ID で置き換えてください。
clientID = "<YOUR_CLIENT_ID>"
// 署名トランザクション開始エンドポイント
startTxURL = "https://sb-auth-and-sign.go.jp/api/sign-transactions"
// 署名するデータ
signDoc = "利用規約に同意します。"
// クライアントクレデンシャルフローによって取得したトークン
token = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhVFN4RHpxM1B6c2dhak0wN3YzWmxGSm8tajBWUGdaVWQ1d0NPOUJydkx3In0..."
)

// RFC 8017 で定義された DigestInfo 構造体
type DigestInfo struct {
Algorithm pkix.AlgorithmIdentifier
Digest []byte
}

func run() error {
// 署名対象データの SHA-256 ハッシュ値を計算します。
hash := sha256.Sum256([]byte(signDoc))

// DigestInfo 構造体を作成します(通常の署名との違い)
digestInfo := DigestInfo{
Algorithm: pkix.AlgorithmIdentifier{
// SHA-256 の OID (2.16.840.1.101.3.4.2.1)
Algorithm: asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1},
Parameters: asn1.NullRawValue,
},
Digest: hash[:],
}

// DigestInfo を DER 形式でエンコードします(通常の署名との違い)
derDigestInfo, err := asn1.Marshal(digestInfo)
if err != nil {
return fmt.Errorf("failed to marshal digest info: %w", err)
}

// Base64 エンコードした DigestInfo を署名対象として使用
digest := base64.StdEncoding.EncodeToString(derDigestInfo)
fmt.Println("digest with DigestInfo:", digest)

// リクエストボディを作成します。
buf := bytes.NewBuffer(nil)
if err := json.NewEncoder(buf).Encode(map[string]any{
"client_id": clientID,
"title": "署名APIのテスト(DigestInfo 付き)",
"identification_code": "0001",
// DigestInfo を付与したハッシュ値を設定(通常の署名との違い)
"data": digest,
}); err != nil {
return fmt.Errorf("failed to encode request body: %w", err)
}

// リクエストを作成します。
req, err := http.NewRequest(http.MethodPost, startTxURL, buf)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))

// 署名トランザクション開始エンドポイントにリクエストを送信します。
resp, err := http.DefaultClient.Do(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()

// レスポンスを読み込みます。
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}

fmt.Println(string(respBody))
return nil
}

func main() {
if err := run(); err != nil {
panic(err)
}
}

実行結果

DigestInfo を付与した署名トランザクションの開始に成功すると、以下のようなレスポンスが返されます:

digest with DigestInfo: MDEwDQYJYIZIAWUDBAIBBQAEIFyId8qi/cogFEJLqL16sfsSmTsGpnh2cezgqE0XZSa0
{
"sign_transaction_id": "37a52ac8-b232-ebac-a454-04d68916489f",
"client_id": "RP00000001",
"state": "CREATED",
"expiration_datetime": "2022-01-01T00:00:00+09:00"
}

ハッシュ値の比較

  • 通常の SHA-256 ハッシュ: XIh3yqL9yiAUQkuovXqx+xKZOwameHZx7OCoTRdlJrQ=
  • DigestInfo 付きハッシュ: MDEwDQYJYIZIAWUDBAIBBQAEIFyId8qi/cogFEJLqL16sfsSmTsGpnh2cezgqE0XZSa0

通常の署名との違い

DigestInfo を付与した署名では、以下の点が異なります:

  1. DigestInfo 構造体の作成: RFC 8017 で定義された構造体でハッシュ値をラップ
  2. DER エンコード: DigestInfo を ASN.1 DER 形式でエンコード
  3. 署名対象データ: 単純なハッシュではなく、DigestInfo をエンコードしたデータを使用

次のステップ

DigestInfo を付与した署名でも、後続の手順は通常の署名と同じです:

  1. 署名トランザクションの開始(DigestInfo 付きハッシュを使用)
  2. 認可コードフローでデジタル認証アプリで署名を実行
  3. 署名トランザクションの結果取得
  4. 署名検証で検証を実行

Verify API での検証対応

PocketSign Verify API は、デジタル認証アプリから得られた署名の検証で、以下の両方に自動対応します:

  • DigestInfo 付き: RFC 8017 準拠の DigestInfo 構造体でラップしたハッシュを用いた署名
  • DigestInfo なし: 単純な SHA-256 ハッシュを用いた署名

このため、署名検証の実装コードを変更する必要はありません。

その他、デジタル認証アプリサービス API の使い方やエラーの詳細等については、デジタル認証アプリのマニュアルをご覧ください。