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

利用者の識別

署名検証時、identify_usertrue に指定することで、利用者 ID を取得することができます。 この機能を使用すると、公的個人認証サービス(JPKI)が提供する証明書紐付け情報および証明書新旧紐付け情報を利用し、利用者に対して一意な利用者 ID を割り当てることができます。

利用者 ID

同一の人物に対して発行された署名用電子証明書と利用者証明用電子証明書については、そのいずれに対しても同一の利用者 ID が返されます。 これを利用することで、例えば署名用電子証明書によって登録した利用者に対して利用者証明用電子証明書によるログイン機能をシームレスに提供することができます。

利用者 ID は、引っ越しや有効期限到来等による証明書の更新後も、同一の値が返されます。

利用者情報の削除を行うと、以後は新しい利用者 ID が割り当てられます。

利用者 ID はテナント内で一意であり、同一の証明書であっても異なるテナントでは異なる利用者 ID が割り当てられます。

利用者 ID の自動紐付け

署名検証時に利用者の特定を要求せず、利用者 ID が割り当てられなかった場合であっても、 別の署名検証時や失効確認時に利用者特定が要求され、証明書に紐付く利用者が特定された場合には、当該証明書にも新しく割り当てられた利用者 ID が紐付けられます。

利用者識別の失敗

利用者 ID を割り当てる際に、公的個人認証サービス(JPKI)が提供する証明書紐付け情報および証明書新旧紐付け情報を利用しています。 証明書紐付け情報の取得に失敗した場合は署名検証は行われず、 エラーコード ERROR_REASON_ASSOCIATED_CERTIFICATE_NOT_EXISTS (紐付け情報なし)が返されます。

「紐付け情報なし」となる場合、以下のような原因が考えられます。

  • 受け取った署名用電子証明書に紐づく利用者証明用電子証明書が存在しない場合
  • 受け取った署名用電子証明書が有効期限切れの場合
モック環境における利用者 ID の割り当て

モック環境において生成される署名用電子証明書には、必ず対応する利用者証明用電子証明書が紐付きます。 そのため、「受け取った署名用電子証明書に紐づく利用者証明用電子証明書が存在しない場合」の検証をすることはできません。

また、モック環境では、有効期限切れの証明書を用いて利用者 ID を割り当てる場合の挙動が異なります。 モック環境では証明書の紐付けを行う際に証明書の有効期限をチェックしていないため、 有効期限切れの証明書に対しても紐付け情報の取得に成功し、利用者 ID を割り当てます。

この挙動により、有効期限切れの証明書を使用した署名検証リクエストで同時に利用者 ID も割り当てる場合、本来 ERROR_REASON_ASSOCIATED_CERTIFICATE_NOT_EXISTS エラーとなるべきリクエストに対しても署名検証結果が返されます。

実装例

クライアントライブラリのセットアップ方法は、クライアントライブラリをご参照ください。

package main

import (
"context"
"crypto/sha256"
"encoding/base64"
"fmt"
"log"
"net/http"

"buf.build/gen/go/pocketsign/apis/connectrpc/go/pocketsign/verify/v2/verifyv2connect"
verifyv2 "buf.build/gen/go/pocketsign/apis/protocolbuffers/go/pocketsign/verify/v2"
"connectrpc.com/connect"
"google.golang.org/protobuf/proto"
)

var (
// APIエンドポイントを指定します。この値は環境によって異なります。
baseUrl = "https://verify.mock.p8n.app"

// Verify APIのトークンです。ご自身のトークンに置き換えてください。
token = "<YOUR_API_TOKEN>"

// 署名対象のドキュメントです。署名作成時と同じものを指定しています。
document = "Hello, world!"

// 署名値と証明書です。これらの値は、アプリ上で作成したものを何らかの方法で予め受け取っておいてください。
rawSignature = "n4bVa46x6/Ud44p7+zHMPpsTjZG7yqtnqc2WlcrOD738q+K61FdMy0aezKMsIZp8UaXanfRDExwRxHDsV1n2fACLKdNkWilNrGhOq9QqlNy5w8+n7ax2O5CQ1hiPKPjZo1wlecDl8D1z2n5ePL/VtszdqZ95QyHvPKYzE5yeDwaCd9QHMtxvbd7qkaac0RujRSdO9MUE4PoKz1YmRieysOIIf0cwHTUWmTy+++UA7itHwp/pPhiz2QWY0QGGMuDfBclISD1wuzqcXSAqOooCBOga7iqOKArYmCyRnAVUtOZJ0TKPnijhrlLe33cyvGR58YC6yBv4i9gnjuIQ7j1fZQ=="
rawCertificate = "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"
)

func run() error {
// Base64をデコードしてバイト列にします。
signature, err := base64.StdEncoding.DecodeString(rawSignature)
if err != nil {
return err
}
certificate, err := base64.StdEncoding.DecodeString(rawCertificate)
if err != nil {
return err
}

// 文書のハッシュ値を計算します。
digest := sha256.Sum256([]byte(document))

// 署名検証リクエストを作成します。
request := connect.NewRequest(&verifyv2.VerifyRequest{
Signature: signature,
Certificate: certificate,

// 作成したハッシュ値を送信します。もし、間違った内容に署名されている場合はハッシュ値が一致しないため、検証に失敗します。
Digest: digest[:],
HashAlgorithm: verifyv2.Verification_HASH_ALGORITHM_SHA256,

// 利用者の特定を要求します。
IdentifyUser: proto.Bool(true),
})

// リクエストにAPIトークンを設定します。
request.Header().Set("Authorization", "Bearer "+token)

// APIクライアントを作成します。
client := verifyv2connect.NewVerificationServiceClient(http.DefaultClient, baseUrl)

// 署名検証リクエストを送信します。
response, err := client.Verify(context.Background(), request)
if err != nil {
return err
}

// 結果を表示します。
fmt.Printf("Result: %s\n", response.Msg.Verification.Result)
fmt.Printf("CommonName: %s\n", response.Msg.CertificateContent.GetJpkiCardDigitalSignatureContent().CommonName)
fmt.Printf("UserID: %s\n", response.Msg.User.Id)
return nil
}

func main() {
if err := run(); err != nil {
log.Fatalln(err)
}
}

リクエストに成功すると、以下のように結果が表示されます。

Result: RESULT_OK
CommonName: 原 智子
UserID: 4defded7-cf7b-4f76-a87c-be8c289f7b9a

なお、証明書に含まれる基本 4 情報(氏名、住所、生年月日、性別)や、利用者 ID は、何らかの理由によって署名検証が失敗した場合でも API レスポンスに含まれます。


次のステップ

取得した利用者 ID を用いて、当人認証によるログインを実装しましょう。

その他、API の詳しい使い方やエラーの詳細等については、API リファレンスをご覧ください。