ハイライト
iOS 15 では、ヘルス アプリで、SMART Health Cards 仕様に基づいて検証可能な健康記録 (Verifiable Health Record) を保存および共有できます。ユーザーは、QR コードをスキャンするかファイルをダウンロードすることで、ワクチン接種記録、検査結果、その他の認証情報をインポートできます。これらの記録は暗号的に署名されており、第三者は CryptoKit を使用してその信頼性を検証できます。開発者が使用できるのは、
HKVerifiableClinicalRecordQueryユーザーにこれらの記録を共有するよう依頼してください。
主な内容
検証可能な健康記録とは何ですか?
(00:23)
検証可能な医療記録は、医療発行者 (病院、研究所など) によって発行されるデジタル認証情報であり、次の内容が含まれます。
- 複数の FHIR リソース (患者リソース + 臨床リソース)
- SMART Health Card形式のパッケージ
- JSON Web Signature (JWS) 暗号署名
通常の健康記録との違い:
- 発行者による署名があり、誰でも真正性を確認できます
- 必要な情報のみが含まれ、データは最小限に抑えられます
- オフラインで検証できるため、発行者のサーバーに問い合わせる必要はありません
インポート方法
(03:55)
ユーザーは次の 3 つの方法でインポートできます。
- Health Records Connect (米国、英国、カナダのみ)
- ヘルスケアアプリで健康記録が有効な医療機関とつながります
- 検証可能な記録を自動的にダウンロードします
- ファイルをダウンロード
- 開ける
.smart-health-cardsファイル拡張子 - ヘルスケアアプリが自動的に認識してインポートします
- QR コードをスキャン
- カメラで SMART Health Card QR コードをスキャンします
- ヘルスケアアプリのインポートインターフェイスに直接ジャンプします
HealthKit へのアクセス
(04:42)
検証可能な健康記録へのアクセスには以下が必要です。
com.apple.developer.healthkitentitlement- 検証可能な健康記録 特別な権利
- 各クエリにはユーザーからの明示的な承認が必要です
ライセンス モデルは従来の健康記録とは異なります。
- 事前の型式許可申請は不要です。
- クエリが実行されるたびに認証シートがポップアップ表示されます
- ユーザーは具体的にどのレコードを共有するかを選択します
- 認可は 1 回限りであり、長期的なアクセスは確立されません。
##詳細
HKVerifiableClinicalRecordQuery
(04:47)
import HealthKit
// クエリを作成する
let recordTypes = ["https://smarthealth.cards#health-card",
"https://smarthealth.cards#immunization"]
let predicate = HKQuery.predicateForClinicalRecords(
with RelevantDateWithin: DateInterval(start: Date().addingTimeInterval(-7*86400),
end: Date())
)
let query = HKVerifiableClinicalRecordQuery(
recordTypes: recordTypes,
predicate: predicate
) { query, records, error in
guard let records = records, !records.isEmpty else {
print("No records shared or error: \(error?.localizedDescription ?? "")")
return
}
// ユーザーが共有したレコードを処理する
for record in records {
print("Issuer: \(record.issuer)")
print("JWS: \(record.jws)")
}
}
// クエリを実行する(認可 sheet が表示される)
healthStore.execute(query)
キーポイント:
recordTypesクエリ対象のレコード タイプを指定する文字列配列です。- 指定されたすべてのタイプを含むレコードのみが認証シートに表示されます。
predicate時間範囲をさらにフィルタリングできます- 実行するたびに権限シートがポップアップ表示されます。
JWS の解析と検証
(10:54)
import CryptoKit
import Combine
// JWS データ構造を定義する
struct SmartHealthCardJWS: Codable {
let header: JWSHeader
let payload: JWSPayload
let signature: Data
struct JWSHeader: Codable {
let alg: String // 签名算法
let kid: String // 公開鍵 ID
let zip: String? // 圧縮アルゴリズム
}
struct JWSPayload: Codable {
let iss: String // 発行者 URL
let nbf: Date // 有効開始時刻
let exp: Date? // 有効期限
let vc: VerifiableCredential
struct VerifiableCredential: Codable {
let type: [String]
let credentialSubject: CredentialSubject
struct CredentialSubject: Codable {
let fhirVersion: String
let fhirBundle: FHIRBundle
}
}
struct FHIRBundle: Codable {
let resourceType: String
let entry: [Entry]
struct Entry: Codable {
let resource: FHIRResource
}
}
}
// 从紧凑序列化格式解析
init(fromCompactSerialization string: String) throws {
let parts = string.split(separator: ".").map(String.init)
guard parts.count == 3 else {
throw NSError(domain: "JWSParsing", code: -1, userInfo: nil)
}
// Header をデコードする
let headerData = Data(base64URLEncoded: parts[0]) ?? Data()
let headerDecoder = JSONDecoder()
header = try headerDecoder.decode(JWSHeader.self, from: headerData)
// Payload をデコードする(圧縮されている可能性がある)
let payloadData = Data(base64URLEncoded: parts[1]) ?? Data()
let decoderPayload: Data
if header.zip == "DEF" {
decoderPayload = try payloadData.decompressed(using: .deflate)
} else {
decoderPayload = payloadData
}
let payloadDecoder = JSONDecoder()
payload = try payloadDecoder.decode(JWSPayload.self, from: decoderPayload)
// 保存签名
signature = Data(base64URLEncoded: parts[2]) ?? Data()
}
}
// 署名を検証する
extension SmartHealthCardJWS {
func verifySignature() -> AnyPublisher<Bool, Error> {
// 発行者の公開鍵を取得する
let issuerURL = URL(string: payload.iss)!
let jwksURL = issuerURL.appendingPathComponent(".well-known/jwks.json")
return URLSession.shared.dataTaskPublisher(for: jwksURL)
.map(\.data)
.decode(type: JWKSResponse.self, decoder: JSONDecoder())
.map { jwks in
// kid に対応する公開鍵を見つける
guard let jwk = jwks.keys.first(where: { $0.kid == self.header.kid }) else {
throw NSError(domain: "JWSVerification", code: -2, userInfo: nil)
}
// CryptoKit の公開鍵に変換する
let publicKey = try P256.Signing.PublicKey(x963Representation: jwk.toX963())
// 署名を検証する
let signedData = (parts[0] + "." + parts[1]).data(using: .utf8)!
return publicKey.isValidSignature(self.signature, for: signedData)
}
.eraseToAnyPublisher()
}
}
// 使用例
let jwsString = "..." // HKVerifiableClinicalRecord.jws から取得
let jws = try SmartHealthCardJWS(fromCompactSerialization: jwsString)
jws.verifySignature()
.sink { isValid in
print("Signature is valid: \(isValid)")
}
.store(in: &cancellables)
キーポイント:
- JWS はコンパクトなシリアル化形式を使用します: header.payload.signature
- ヘッダーはアルゴリズム (ES256) と公開鍵 ID を指定します
- ペイロードは DEFLATE で圧縮されているため、最初に解凍する必要があります
- 発行者からの公開鍵
.well-known/jwks.json得る - CryptoKitの使用
P256.Signing.PublicKey署名を検証する
データ抽出
(13:36)
// 提取疫苗接种信息
func extractImmunizations(from record: HKVerifiableClinicalRecord) -> [Immunization] {
guard let jwsData = record.jws.data(using: .utf8),
let jws = try? SmartHealthCardJWS(fromCompactSerialization: jwsData) else {
return []
}
var immunizations: [Immunization] = []
for entry in jws.payload.vc.fhirBundle.entry {
if let resource = entry.resource as? ImmunizationResource {
immunizations.append(Immunization(
vaccine: resource.vaccineCode,
date: resource.occurrenceDateTime,
manufacturer: resource.manufacturer
))
}
}
return immunizations
}
キーポイント:
- FHIR バンドルには複数のリソースが含まれています
- 各リソースには対応する resourceType フィールドがあります。
- 詳細については、「セッションを焼き付けずに FHIR を処理する」を参照してください。
重要ポイント
1. 医療および健康アプリに健康認証情報の検証を統合する
アプリにワクチン接種や検査結果の検証が含まれる場合は、紙の証明書や単純な写真のアップロードの代わりに、検証可能な健康記録を使用できます。ユーザーは確認済みの認証情報をヘルスケア App から直接共有し、追加の写真は必要ありません。
開始方法: 統合HKVerifiableClinicalRecordQuery、健康認証情報の検証を必要とするページでクエリをトリガーします。ユーザーの承認後、必要な特定の情報 (ワクチンの種類、ワクチン接種日など) を抽出します。
2. 健康診断書発行システムの構築
あなたの組織が医療機関の場合は、SMART Health Card の発行者になることができます。発行プロセスには次のものが含まれます。
- FHIR リソースを構築する
- SMART Health Card にパッケージ化する
- 組織の秘密鍵で署名する
- ユーザーがダウンロードする QR コードまたはファイルを生成します
開始方法: SMART Health Cards の仕様を参照し、CryptoKit を使用して署名ロジックを実装します。秘密キーが HSM に安全に保管され、公開キーが公開されていることを確認します。.well-known/jwks.json。
3. 非医療アプリでユーザーのプライバシーを保護する
アプリが医療用ではない場合でも、ユーザーの健康情報が公開される可能性があります。検証可能な医療記録を使用することは、機密データを直接収集するよりもプライバシーに優しく、どの情報を共有するかをユーザーが制御し、各共有は 1 回限りの使用となります。
開始方法: アプリに健康関連のシナリオ (ジム、保険など) が含まれる場合は、ユーザーの医療記録を直接収集しないでください。アプリが必要なフィールドのみを読み取って、ヘルスケアアプリから検証可能な記録を共有するようにユーザーをガイドします。
関連セッション
- HealthKit の新機能 — iOS 15 の HealthKit のすべてのアップデート
- 研究とケアのアプリを構築する、パート 1 — ResearchKit と CareKit を使用して臨床研究アプリを構築する
- 火傷を負わずに FHIR を処理する — FHIR データを正しく処理するためのベスト プラクティス
- Apple のプライバシーの柱に注目 — Apple の 4 つのプライバシーの柱の技術的実装
コメント
GitHub Issues · utterances