iOSセキュリティAPIを使用したデータの暗号化、復号化、署名、検証の完全ガイド

1. 暗号化と復号化の基礎知識

暗号化と復号化を行うには、公開鍵と秘密鍵が必要です。これらの鍵は通常、セキュアエンクラーブ、ランダムな文字列、パスワード、または証明書から取得します。

2. 署名と検証とは?

データに署名することで、デジタル署名が作成されます。元のデータと署名を提供することにより、データの整合性を検証できます。

デジタル署名は、受信者が送信者の身元の真実性とデータの整合性を確認するための保証です。簡潔に言えば、これは手書きの署名のようなものです。デジタル署名は、暗号化/復号化とは異なる別の鍵ペアを使用します。

デジタル署名は、アプリケーションをテストやApp Storeへのアップロードの際にXcodeがコードに適用するなど、多くの電子分野で利用されています。

送信側の署名プロセス

  1. 送信者は特定のハッシュアルゴリズム(例:sha256)を使用してデータを暗号化します。このハッシュアルゴリズムの出力は「ダイジェスト」(例:6e04f289)と呼ばれます。
  2. 次に、送信者は自分の秘密鍵を使用してダイジェストを暗号化します。この暗号化されたダイジェストがデータの「デジタル署名」になります。
  3. 送信者は元のデータと暗号化されたダイジェストを一緒に送ります。注意してください。暗号化されたダイジェストは送信者の身元を確認するためにのみ使用され、メッセージそのものは署名と検証に使用される同一の秘密鍵によって暗号化されることはありません。メッセージの暗号化は、前のセクションで説明したように作成された秘密鍵を使用して行われます。

受信側の検証プロセス

  1. 受信者はメッセージとそのデジタル署名を受け取ります。
  2. 受信者は送信者の公開鍵を使用してデジタル署名を復号化します -> 結果として「ダイジェスト」が得られます。
  3. 受信者は送信者と同じハッシュアルゴリズムを使用して受け取った平文データに対してハッシュ演算を行います -> これにより「ダイジェスト」が出力されます。
  4. ステップ2(デジタル署名を復号化した後)のダイジェスト = ステップ3(受け取った平文データに対してハッシュした後)のダイジェスト -> 受信者はデータの整合性を検証できます。

このチュートリアルでは、デジタル署名のために秘密鍵はセキュアエンクラーブに保存され、公開鍵はセキュアエンクラーブ外に保存され、受信者と共有されます。

証明書から公開鍵と秘密鍵を取得する

class SecureCryptoManager {
    private var certificateData: Data
    private var passphrase: String

    init(fileName: String = "Certificate", fileExtension: String = "p12", passphrase: String = "1233") {
        self.certificateData = Self.loadCertificate(named: fileName, withExtension: fileExtension) ?? Data()
        self.passphrase = passphrase
    }

    static func loadCertificate(named fileName: String, withExtension ext: String) -> Data? {
        return Utility.loadData(fromFileNamed: fileName, withExtension: ext)
    }

    func encrypt(data: Data, using algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA512AESGCM) -> Data? {
        guard let certData = Utility.parsePKCS12(certificateData: self.certificateData, passphrase: self.passphrase) else { return nil }
        guard let keyPair = Utility.extractKeys(from: certData.pkcs12Entry) else { return nil }
        return encrypt(value: data, with: keyPair.publicKey, using: algorithm)
    }

    func decrypt(data: Data, using algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA512AESGCM) -> Data? {
        guard let certData = Utility.parsePKCS12(certificateData: self.certificateData, passphrase: self.passphrase) else { return nil }
        guard let keyPair = Utility.extractKeys(from: certData.pkcs12Entry) else { return nil }
        return decrypt(encrypted: data, with: keyPair.privateKey, using: algorithm)
    }

    func sign(data: Data, using algorithm: SecKeyAlgorithm = .rsaSignatureMessagePKCS1v15SHA512) -> Data? {
        guard let certData = Utility.parsePKCS12(certificateData: self.certificateData, passphrase: self.passphrase) else { return nil }
        guard let keyPair = Utility.extractKeys(from: certData.pkcs12Entry) else { return nil }
        return sign(data: data, with: keyPair.privateKey, using: algorithm)
    }

    func verify(data: Data, signature: Data, using algorithm: SecKeyAlgorithm = .rsaSignatureMessagePKCS1v15SHA512) -> Bool {
        guard let certData = Utility.parsePKCS12(certificateData: self.certificateData, passphrase: self.passphrase) else { return false }
        guard let keyPair = Utility.extractKeys(from: certData.pkcs12Entry) else { return false }
        return verify(data: data, signature: signature, with: keyPair.publicKey, using: algorithm)
    }

    private func encrypt(value: Data, with publicKey: SecKey, using algorithm: SecKeyAlgorithm) -> Data? {
        guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) else {
            debugPrint("サポートされていない暗号化アルゴリズム")
            return nil
        }
        var error: Unmanaged<CFError>?
        guard let encryptedData = SecKeyCreateEncryptedData(publicKey, algorithm, value as CFData, &error) else {
            let errorMessage = error?.takeRetainedValue()
            debugPrint("暗号化エラー: \(String(describing: errorMessage))")
            return nil
        }
        return encryptedData as Data
    }

    private func decrypt(encrypted: Data, with privateKey: SecKey, using algorithm: SecKeyAlgorithm) -> Data? {
        guard SecKeyIsAlgorithmSupported(privateKey, .decrypt, algorithm) else {
            debugPrint("サポートされていない復号化アルゴリズム")
            return nil
        }
        var error: Unmanaged<CFError>?
        guard let decryptedData = SecKeyCreateDecryptedData(privateKey, algorithm, encrypted as CFData, &error) as Data? else {
            let errorMessage = error?.takeRetainedValue()
            debugPrint("復号化エラー: \(String(describing: errorMessage))")
            return nil
        }
        return decryptedData
    }

    private func sign(data: Data, with privateKey: SecKey, using algorithm: SecKeyAlgorithm) -> Data? {
        guard SecKeyIsAlgorithmSupported(privateKey, .sign, algorithm) else {
            debugPrint("サポートされていない署名アルゴリズム")
            return nil
        }
        var error: Unmanaged<CFError>?
        guard let signature = SecKeyCreateSignature(privateKey, algorithm, data as CFData, &error) as Data? else {
            let errorMessage = error?.takeRetainedValue()
            debugPrint("署名エラー: \(String(describing: errorMessage))")
            return nil
        }
        return signature as Data
    }

    private func verify(data: Data, signature: Data, with publicKey: SecKey, using algorithm: SecKeyAlgorithm) -> Bool {
        guard SecKeyIsAlgorithmSupported(publicKey, .verify, algorithm) else {
            debugPrint("サポートされていない検証アルゴリズム")
            return false
        }
        guard !data.isEmpty else {
            debugPrint("空の署名データを使用した検証はできません")
            return false
        }
        var error: Unmanaged<CFError>?
        guard SecKeyVerifySignature(publicKey, algorithm, data as CFData, signature as CFData, &error) else {
            debugPrint("検証エラー: \(String(describing: error?.takeRetainedValue() as Error?))")
            return false
        }
        return true
    }
}

iOSセキュアエンクラーブからキーを生成する

// アクセス制御オブジェクトを作成
let accessControl = SecAccessControlCreateWithFlags(
    kCFAllocatorDefault, // 1
    kSecAttrAccessibleWhenUnlockedThisDeviceOnly, // 2
    [.privateKeyUsage, .biometryAny], // 3
    nil // 4
)!

let secureTag = "YourKey".data(using: .utf8)! // 5

let privateKeyAttributes: [String: AnyObject] = [
    kSecAttrIsPermanent as String: true as AnyObject, // 6
    kSecAttrApplicationTag as String: secureTag as AnyObject, // 7
    kSecAttrAccessControl as String: accessControl // 8
]

let keyAttributes = [
    kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, // 9
    kSecAttrKeySizeInBits as String: 256, // 10
    kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, // 11
    kSecPrivateKeyAttrs as String: privateKeyAttributes // 12
] as CFDictionary

var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(keyAttributes, &error) else {
    throw error!.takeRetainedValue() as Error
}

guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
    print("公開鍵生成エラー")
    return ""
}

署名の作成

let message = "AshishAwasthi"
guard let messageData = message.data(using: .utf8) else {
    print("署名する無効なメッセージ")
    return nil
}

// 1. 署名を作成
guard let signatureData = SecKeyCreateSignature(privateKey,
                                               .ecdsaSignatureMessageX962SHA256,
                                               messageData as CFData,
                                               nil) else {
    print("署名エラー")
    return nil
}

// 2. 署名データを取得
let signedData = signatureData as Data
let signedString = signedData.base64EncodedString()
print("署名文字列", signedString)

// 3. 署名を検証
let verificationMessage = "AshishAwasthi"
guard let verificationData = verificationMessage.data(using: .utf8) else {
    print("検証する無効なメッセージ")
    return false
}

guard let signature = signedData else {
    print("検証する無効な署名")
    return false
}

let isVerified = SecKeyVerifySignature(publicKey,
                                       .ecdsaSignatureMessageX962SHA256,
                                       verificationData as CFData,
                                       signature as CFData,
                                       nil)

タグ: iOS セキュリティAPI 暗号化 復号化 署名

7月21日 18:34 投稿