Apple CryptoKit 學習筆記

在做生物辨識的功能時遇到的學習筆記

Posted by willsbor Kang on 2021-10-06

生物辨識主要的目標是取代像是 OTP,可以做一個可信任的身份認證。

服務的流程大致上是:

  1. APP 先產生 private/public key
  2. APP 將 public key 傳到 BE

當要使用身份認證時

  1. APP 打 API 取得一個 BE 產生的 nonce-1
  2. APP 產生一個 nonce-2
  3. APP 用 nonce-1 + nonce-2 + 講好的規則 用 private key 做 signature
  4. APP 將 nonce-2 以及 signature 傳到 BE,BE 認證沒問題後,回傳一個一次性的 Token

在這個前期 POC 中,我先要試的是,如何產生 private/public 做 signature

Secure Envlave

雖然一直知道 Apple 有用另一個 SoC 做安全保護,但其實一直都沒去看他過XD。感覺官方文件已經寫的淺顯易懂了。

由文件可以知道,會產生一個 SoC 內的 UID,且不會外丟,來確保安全性。

在跟公司同事討論的過程裡,還蠻好奇產生的 private / public key 會存放在哪裡?
看了很多文章,以及實作。感覺應該還是在 KeyChain 內。
確定 Public Key 是可以拿得到。
至於 Private Key 的內容是否拿得到?但目前並沒有這樣的需求,所以可能就等之後有機會再研究看看了。

Apple CryptoKit

iOS 13 之後,Apple 提供了新的 Framework - CryptoKit

看了幾篇文章
https://www.raywenderlich.com/10846296-introducing-cryptokit#toc-anchor-016

https://developer.apple.com/documentation/cryptokit/storing_cryptokit_keys_in_the_keychain

https://medium.com/@alx.gridnev/ios-keychain-using-secure-enclave-stored-keys-8f7c81227f4

使用上系統已經做好了包裝

產生 Private key

1
2
3
4
5
// Check that the device has a Secure Enclave
if SecureEnclave.isAvailable {
// Generate private key in Secure Enclave
let privateKey = try SecureEnclave.P256.Signing.PrivateKey()
}

拿出 Public Key 丟給 BE

1
2
// Create public key data
let publicKeyData = privateKey.publicKey.compactRepresentation!

在測試的過程中我需要將它輸出成 PEM,iOS 14 以後有一個 function pemRepresentation 可以將回傳值直接儲存到檔案。

但 iOS 13 的要如何解決呢?我是用

1
let pemRepresentation = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE" + privateKey.publicKey.rawRepresentation.base64EncodedString()

我在寫這篇的時候沒注意到還有 compactRepresentation 不確定印出來的是什麼,之後可能可以試試。

APP 端做 signature

1
2
3
// Produce a signature
let dataSignature = try privateKey.signature(for: data)
let digestSignature = try privateKey.signature(for: digest)

權限控管

以及 可以結合 accessControl & Local Authentication (目前這部分還沒試過,下次再說)

異端驗證

我試圖模擬 API 回傳資料的狀況,需要有下面的資料:

  • public 的 PEM
  • 明文的 nonce-2
  • 算出來的 signature
    從手機 APP 複製出來,在 console mode 下用 openSSL 來驗證

reference:
https://www.scottbrady91.com/OpenSSL/Creating-Elliptical-Curve-Keys-using-OpenSSL
https://ithelp.ithome.com.tw/m/articles/10252000
https://link.medium.com/UU7H2Inz7jb

準備資料:

1
2
echo -n "i am nonce" > data
echo -n MEUCIQDtaDbSldK6M4yXv4g9gToQfzq55fjvUMcdkAr+ndOE0AIgQHLzFDnd/iWpaKOZpcBhZZ1599P/mghL1apIq+0NUe8= | base64 -D > sig

Verify:

1
openssl dgst -sha256 -verify public.pem -signature sig data

最後成功就會看到

1
Verified OK

ECDSA / ECDH

主要的疑問是來自這兩個參數

https://developer.apple.com/documentation/cryptokit/p256

enum P256.KeyAgreement

A mechanism used to create a shared secret between two users by performing NIST P-256 elliptic curve Diffie Hellman (ECDH) key exchange.

enum P256.Signing

A mechanism used to create or verify a cryptographic signature using the NIST P-256 elliptic curve digital signature algorithm (ECDSA).

看了一輪不太清楚實務上這兩個的差異與用途

後來讀了一些網路文章
https://easonwang.gitbook.io/crypto/ecdsa
https://zhuanlan.zhihu.com/p/66794410
https://github.com/Kitura/BlueECC

以及問了丟神 m(_ _)m,才大概知道

  • ECDH 其實是 symmetric key 做金鑰交換
  • ECDSA 是要拿來做 signature

所以依據目前的用途,應該選擇 signing 就 OK 了

另外也看到這篇文章這樣提到:

1
2
3
1)相同密鑰長度下,安全性能更高,如160位ECC已經與1024位RSA、DSA有相同的安全強度。
2)計算量小,處理速度快,在私鑰的處理速度上(解密和簽名),ECC遠 比RSA、DSA快得多。
3)存儲空間占用小 ECC的密鑰尺寸和系統參數與RSA、DSA相比要小得多, 所以占用的存儲空間小得多。

RSA

在這次的研究中,我就沒有實作他了,主要原因是因為 CryptoKit 並沒有 Support RSA,如果真的要實作,則可能需要用舊的 API 來處理。

加上考量上述的好處,或許可以建議後端也用 ECDSA 來處理。

other reference:

https://www.andyibanez.com/posts/cryptokit-not-enough/
https://ithelp.ithome.com.tw/m/articles/10251744