目次

IDトークン獲得までの流れ

全フローチャート

IDトークン獲得のフローチャート

ログイン認可エンドポイントに到達KeyCloakIDトークンの検証リダイレクトurlトークンリクエスト

1.ログイン OIDC(OpenID Connect)スタート

2.ブラウザにリダイレクト+3.認可リクエスト+4.認証画面

2.1.ClientAdapter経由


2.2.認可サーバーの認可エンドポイントに到達 → 4.認証画面にジャンプ

2.2.1.到達urlのクエリパラメータ解説

2.2.2.state,code_challenge,nonce(Number Used Once)の比較

Parameter Used In Protects Against Checked by
state OAuth 2.0 CSRF Attacks Client app (compared with original value)
code_challenge OAuth + PKCE Authorization Code Interception Authorization Server (IdP)
nonce OpenID Connect Token Replay Attacks Client app (compared with original value)

5.認証情報入力~8.認可レスポンス

5.1.keycloak

5.2.FIDO生体認証

9.カスタムスキームによるアプリ起動

9.1.カスタムスキーム

10.トークンリクエスト

12.IDトークンの検証

12.1.IDトークンの実体

12.2.IDトークンのHeader

12.2.1.IDトークンのHeaderで標準的なもの

12.2.2.IDトークンのHeaderの実例

12.3.IDトークンのclaims

12.3.1.IDトークンのclaimsで標準的なもの

12.3.1.IDトークンのclaimsの実例

12.4.IDトークンのSignature

12.4.1.IDトークンの署名

12.4.2.IDトークンのSignatureの公開鍵

12.4.3.IDトークンのSignatureの検証コード

verifyjwtsignature.cs

OAutu2.0 と OpenIDConnect

提供機能の違い

OAuth 2.0 = 「入館証」(認可)

🔹 ポイント:OAuth 2.0は、「サードパーティアプリケーションによるHTTPサービスへの限定的なアクセスを可能にする認可フレームワーク」 を提供する。

OpenID Connect(OIDC) = 「身分証明書」(認証)

🔹 ポイント:OpenID Connectは、OAuth 2.0の仕組みを使って、「この人は本当に本人か?」(認証) を行う。

用語の違い

RemoteSignatureClientAdapterとRemoteSignatureService

提供機能の違い

RemoteSignatureClientAdapter

RemoteSignatureClientService

ExamplePublisherClientのライブラリとWebAPI

提供機能の違い

ライブラリ

/// <summary>ライブラリ署名(処方)</summary>
public PrescriptionXml PrescriptionSignLibrary()
{
	// クライアント証明書の取得
	// 環境に合わせてクライアント証明書を取得してください。
	var cert = CreateClientCertificate(
					Properties.Settings.Default.ClientCertificatePath,
					Properties.Settings.Default.ClientCertificatePassword);
 
	var proxy = CreateProxy();
 
	// 設定情報作成
	var config = new RemoteSignatureConfiguration(new Uri(this.SignServerUri), cert, proxy);
 
	// 処方箋の署名
	var signaturePrescription = new SignaturePrescriptionFacade(config);
	return signaturePrescription.Sign(this.CurrentToken, this.PrescriptionCsv);
}
 
 ⇓
 
public PrescriptionXml Sign(Token token, string prescriptionCsv)
{
 
	if (string.IsNullOrEmpty(prescriptionCsv))
	{
		throw new ArgumentNullException("prescriptionCsvのデータがありません");
	}
	try
	{
		string prescriptionData = EncodeBase64Data(prescriptionCsv);
		PrescriptionXml xml = new PrescriptionXml();
		xml.SetPrescriptionData(prescriptionData);
		string digest = xml.GetPrescriptionDocumentDigestValue();
		SignatureRequest request = new SignatureRequest
		{
			Token = token.Original,
			Digest = digest
		};
		FillInstitute(request, prescriptionCsv);
		SignatureResponse signedXml = GetSignedXml(request);
		xml.SignStatus = new SignStatus(signedXml);
		if (!string.IsNullOrEmpty(signedXml.SignedXmlData))
		{
			xml.SetPrescriptionSign(signedXml.SignedXmlData.Base64ToUtf());
		}
		return xml;
	}
	catch (Exception ex)
	{
		throw;
	}
}
 
 ⇓
 
private SignatureResponse GetSignedXml(SignatureRequest request)
{
	return new WebApiRequest(Configuration.Uri, Configuration.ClientCertificate, Configuration.Proxy).Post<SignatureRequest, SignatureResponse>("v1/Signature/Prescription/Sign", request);
}

WebAPI

/// <summary>WebApi署名(処方)</summary>
public byte[] PrescriptionSignWebApi()
{
	// 処方箋の署名
	var request = new JObject(
		new JProperty("token", this.CurrentToken.Original),
		new JProperty("csvData", ToBase64(this.PrescriptionCsv))
	);
 
	using (var client = new WebClient())
	{
		client.Headers[HttpRequestHeader.ContentType] = "application/json";
		client.Encoding = System.Text.Encoding.UTF8;
		var result = client.UploadString(this.SignApiUri + "signature/prescription/sign", request.ToString());
 
		var resJson = JObject.Parse(result);
		if (!resJson.SelectToken("isSuccess").Value<bool>())
			throw new Exception($"{resJson.SelectToken("statusMessage").Value<string>()}({resJson.SelectToken("statusCode").Value<string>()})");
 
		return Convert.FromBase64String(resJson.SelectToken("signedXmlData").Value<string>());
	}
}

「管理サーバーに何を渡しているのか」の考察

処方箋情報の扱い

string digest = xml.GetPrescriptionDocumentDigestValue();
SignatureRequest request = new SignatureRequest
{
	Token = token.Original,
	Digest = digest
};
FillInstitute(request, prescriptionCsv);
SignatureResponse signedXml = GetSignedXml(request);
xml.SignStatus = new SignStatus(signedXml);
if (!string.IsNullOrEmpty(signedXml.SignedXmlData))
{
	xml.SetPrescriptionSign(signedXml.SignedXmlData.Base64ToUtf());
}
return xml;

をみると、IDトークンとPrescriptionDocumentのHash値を、管理サーバーの署名APIに渡しているようである。PrescriptionDocument要素のハッシュ値のみ管理サーバーに渡し、管理サーバーがもっているKeyinfo要素とSignedProtperties要素とあわせた3要素を、管理サーバーのセカンドHPKI証明書の秘密鍵で署名したSignatureValueをもつPrescriptionSign要素を返却してきて、xml.SetPrescriptionSign(signedXml.SignedXmlData.Base64ToUtf());で両者を合体しているのだろう。

var request = new JObject(
	new JProperty("token", this.CurrentToken.Original),
	new JProperty("csvData", ToBase64(this.PrescriptionCsv))
	);
var result = client.UploadString(this.SignApiUri + "signature/prescription/sign", request.ToString());

をみると、IDトークンと処方箋csvをRemoteSignatueClientServiceに渡しているようである。↑のライブラリのコードから推察すると、RemoteSignatueClientService.exeがローカルでcsvDataから、PrescriptionDocument要素を作る。PrescriptionDocument要素のハッシュ値のみ管理サーバーに渡し、管理サーバーがもっているKeyinfo要素とSignedProtperties要素とあわせた3要素を、管理サーバーのセカンドHPKI証明書の秘密鍵で署名したSignatureValueをもつPrescriptionSign要素を返却してきて、ローカルでRemoteSignatueClientService.exeが処理したPrescriptionDocument要素と合体しているのだろう。

HPKIセカンド電子証明書管理サービスクライアント証明書の扱い

/// <summary>ライブラリ署名(処方)</summary>
public PrescriptionXml PrescriptionSignLibrary()
{
	// クライアント証明書の取得
	// 環境に合わせてクライアント証明書を取得してください。
	var cert = CreateClientCertificate(
					Properties.Settings.Default.ClientCertificatePath,
					Properties.Settings.Default.ClientCertificatePassword);
 
	var proxy = CreateProxy();
 
	// 設定情報作成
	var config = new RemoteSignatureConfiguration(new Uri(this.SignServerUri), cert, proxy);
 
	// 処方箋の署名
	var signaturePrescription = new SignaturePrescriptionFacade(config);
	return signaturePrescription.Sign(this.CurrentToken, this.PrescriptionCsv);
}

より、「HPKIセカンド電子証明書管理サービスクライアント証明書」を渡しているようである。

var request = new JObject(
	new JProperty("token", this.CurrentToken.Original),
	new JProperty("csvData", ToBase64(this.PrescriptionCsv))
	);
var result = client.UploadString(this.SignApiUri + "signature/prescription/sign", request.ToString());

では、「HPKIセカンド電子証明書管理サービスクライアント証明書」を渡していないように見える。多分、RemoteSignatureClientService.exeがappsettings.jsonの情報に基づいて認証を行っているのであろう。

どちらを選ぶべきか

Tools&Tips

Tools

Tips