@perfood/capacitor-crypto-api
v8.0.0
Published
Capacitor plugin that uses Secure Enclave (iOS) or StrongBox/TEE (Android) to generate key-pairs, sign, verify, encrypt or decrypt data. It also provides a way to authenticate the user via biometrics and passkey.
Readme
@perfood/capacitor-crypto-api
This capacitor plugin provides a unified cryptographic API for secure key management and cryptographic operations using platform-specific secure hardware:
- iOS: Secure Enclave
- Android: StrongBox / TEE
- Web (development): WebCrypto API
It supports generating and storing elliptic curve key pairs and using them for:
- ECDSA: signing and verifying data
- ECDH: key agreement (e.g. for symmetric encryption)
Secure Hardware & Cryptographic Capabilities
iOS – Secure Enclave
"Works only with NIST P-256 elliptic curve keys. These keys can only be used for creating and verifying cryptographic signatures, or for elliptic curve Diffie-Hellman key exchange (and by extension, symmetric encryption)." - Apple Developer Documentation
On iOS, the Secure Enclave imposes the following constraints:
- Only NIST P-256 elliptic curve keys are supported
- Supports ECDSA and ECDH
- Private keys never leave the Secure Enclave
- Keys can optionally be protected by biometrics
Android – StrongBox / TEE
On Android, the plugin uses StrongBox when available and falls back to the Trusted Execution Environment (TEE).
- Supports ECDSA and ECDH
- Hardware-backed key storage
- Behavior depends on device manufacturer and Android version
- Keys can optionally be protected by biometrics
Format of the signature
Secure Enclave (iOS) and StrongBox/TEE (Android) return the signature in ASN.1 DER format. The WebCrypto API returns the signature in raw (IEEE P1363) format.
This plugin has the functions derToP1363 and p1363ToDer to convert the signature from ASN.1 DER to raw (IEEE P1363) format and vice versa.
For development
For development and testing in the browser:
- Uses the WebCrypto API
- Key pairs are stored in the browser's localStorage
- Supports ECDSA and ECDH
WebCrypto API is only available in secure contexts (https)
Use Cases
Two-Factor / Cryptographic Authentication (ECDSA)
This plugin can be used to implement a strong cryptographic authentication mechanism.
- The client generates an ECDSA key pair in secure hardware
- The public key is registered on the server
- The server sends a cryptographic challenge
- The client signs the challenge using the private key and sends the signed data back to the server
- The server verifies the signature using the stored public key
Because the private key never leaves secure hardware, this provides strong protection against key exfiltration. If the keys are protected by biometrics, the native biometrics dialog box is displayed.
There is an example in the example directory.
Secure Key Exchange & Encryption (ECDH)
The plugin supports Elliptic Curve Diffie-Hellman (ECDH) to securely derive shared secrets.
Typical workflow:
- Client and server each have an ECDH key pair
- The public keys are exchanged
- Both sides derive the same shared secret using ECDH
- The derived secret is used as a symmetric key (e.g. AES)
- Data can now be securely:
- Encrypted on one side
- Decrypted on the other side
Supported use cases:
- End-to-end encrypted communication
- Secure storage encryption
Private keys remain protected in Secure Enclave (iOS) or StrongBox/TEE (Android).
Passkey (WebAuthn / FIDO2)
The plugin supports Passkeys for passwordless authentication using native platform dialogs.
Registration
A passkey can be created after a successful login, ensuring account ownership.
- Client requests registration options from the backend (WebAuthn PublicKeyCredentialCreationOptions)
- Backend returns challenge and relying party information
- Client calls registerPasskey(...)
- Native passkey dialog is shown (Face ID / Touch ID / Biometrics)
- Client returns assertion to backend
- Backend verifies the attestation
- On success: credentialId and publicKey can be stored in the user database
Authentication
- Client requests authentication options from backend
- Backend sends challenge
- Client calls authenticateWithPasskey(...)
- Native authentication dialog is shown
- Client returns assertion to backend
- Backend verifies the signature using the stored public key
Install
npm install @perfood/capacitor-crypto-api
npx cap syncAPI
getECDSATags()getECDHTags()generateKey(...)loadKey(...)deleteKey(...)sign(...)verify(...)encrypt(...)decrypt(...)isBiometricsEnabled(...)getBiometricsStatus(...)getAvailableHardware()isDevicePasscodeSet()hasSecureHardware()registerPasskey(...)authenticateWithPasskey(...)- Interfaces
- Type Aliases
getECDSATags()
getECDSATags() => Promise<GetTagsResponse>Returns all ECDSA key-pair tags that are available in the Secure Enclave (iOS) or StrongBox/TEE (Android).
Returns: Promise<GetTagsResponse>
getECDHTags()
getECDHTags() => Promise<GetTagsResponse>Returns all ECDH key-pair tags that are available in the Secure Enclave (iOS) or StrongBox/TEE (Android).
Returns: Promise<GetTagsResponse>
generateKey(...)
generateKey(options: GenerateKeyOptions) => Promise<GenerateKeyResponse>Generates a key-pair in the Secure Enclave (iOS) or StrongBox/TEE (Android), tags it for alter referencing and returns the public-key only, since the private-key is protected and can't be extracted. Possibility to secure private key with biometry.
| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| options | GenerateKeyOptions |
Returns: Promise<GenerateKeyResponse>
Since: 1.0.0
loadKey(...)
loadKey(options: LoadKeyOptions) => Promise<LoadKeyResponse>Loads the public-key from the Secure Enclave (iOS) or StrongBox/TEE (Android).
| Param | Type |
| ------------- | --------------------------------------------------------- |
| options | LoadKeyOptions |
Returns: Promise<LoadKeyResponse>
Since: 1.0.0
deleteKey(...)
deleteKey(options: DeleteKeyOptions) => Promise<void>Deletes the key-pair from the Secure Enclave (iOS) or StrongBox/TEE (Android).
| Param | Type |
| ------------- | ------------------------------------------------------------- |
| options | DeleteKeyOptions |
Since: 1.0.0
sign(...)
sign(options: SignOptions) => Promise<SignResponse>Signs the data in the Secure Enclave (iOS) or StrongBox/TEE (Android). Uses the private-key associated with the tag.
Only ECDSA is supported.
| Param | Type |
| ------------- | --------------------------------------------------- |
| options | SignOptions |
Returns: Promise<SignResponse>
Since: 1.0.0
verify(...)
verify(options: VerifyOptions) => Promise<VerifyResponse>Verifies the signature of the data with the foreign public-key.
Only ECDSA is supported.
| Param | Type |
| ------------- | ------------------------------------------------------- |
| options | VerifyOptions |
Returns: Promise<VerifyResponse>
Since: 1.0.0
encrypt(...)
encrypt(options: EncryptOptions) => Promise<EncryptResponse>Encrypt data with AES-GCM.
| Param | Type |
| ------------- | --------------------------------------------------------- |
| options | EncryptOptions |
Returns: Promise<EncryptResponse>
decrypt(...)
decrypt(options: DecryptOptions) => Promise<DecryptResponse>Decrypt data with AES-GCM.
| Param | Type |
| ------------- | --------------------------------------------------------- |
| options | DecryptOptions |
Returns: Promise<DecryptResponse>
isBiometricsEnabled(...)
isBiometricsEnabled(options: BiometricsEnabledOptions) => Promise<BiometricsEnabledResponse>Is biometry enabled on the device?
| Param | Type |
| ------------- | ----------------------------------------------------------------------------- |
| options | BiometricsEnabledOptions |
Returns: Promise<BiometricsEnabledResponse>
getBiometricsStatus(...)
getBiometricsStatus(options: BiometricsStatusOptions) => Promise<BiometricsStatusResponse>Get the status of biometry on the device.
| Param | Type |
| ------------- | --------------------------------------------------------------------------- |
| options | BiometricsStatusOptions |
Returns: Promise<BiometricsStatusResponse>
getAvailableHardware()
getAvailableHardware() => Promise<AvailableHardwareResponse>Get the list of available biometry hardware on the device.
Returns: Promise<AvailableHardwareResponse>
isDevicePasscodeSet()
isDevicePasscodeSet() => Promise<DevicePasscodeResponse>Is the device passcode set on the device?
Returns: Promise<DevicePasscodeResponse>
hasSecureHardware()
hasSecureHardware() => Promise<SecureHardwareResponse>Does the device have secure hardware like StrongBox?
Returns: Promise<SecureHardwareResponse>
registerPasskey(...)
registerPasskey(options: RegisterPasskeyOptions) => Promise<RegisterPasskeyResult>| Param | Type |
| ------------- | ------------------------------------------------------------------------- |
| options | RegisterPasskeyOptions |
Returns: Promise<RegisterPasskeyResult>
authenticateWithPasskey(...)
authenticateWithPasskey(options: AuthenticateWithPasskeyOptions) => Promise<AuthenticateWithPasskeyResult>| Param | Type |
| ------------- | ----------------------------------------------------------------------------------------- |
| options | AuthenticateWithPasskeyOptions |
Returns: Promise<AuthenticateWithPasskeyResult>
Interfaces
GetTagsResponse
| Prop | Type | Description |
| ---------- | --------------------- | ------------------ |
| tags | string[] | The key-pair tags. |
GenerateKeyResponse
| Prop | Type | Description |
| --------------- | ------------------- | -------------------------------- |
| publicKey | string | The public-key in base64 format. |
GenerateKeyOptions
| Prop | Type | Description |
| --------------- | ----------------------------------------------------- | ------------------------------------------------------- |
| tag | string | The key-pair tag. |
| algorithm | 'ecdsa' | 'ecdh' | The elliptic curve algorithm |
| type | BiometryType | Biometry type if key is possibly secured with biometry. |
LoadKeyResponse
| Prop | Type | Description |
| --------------- | ------------------- | -------------------------------- |
| publicKey | string | The public-key in base64 format. |
LoadKeyOptions
| Prop | Type | Description |
| --------------- | ------------------------------ | -------------------------------------------------------- |
| tag | string | The key-pair tag. |
| algorithm | 'ecdsa' | 'ecdh' | The elliptic curve algorithm was used to create the key. |
DeleteKeyOptions
| Prop | Type | Description |
| --------------- | ------------------------------ | -------------------------------------------------------- |
| tag | string | The key-pair tag. |
| algorithm | 'ecdsa' | 'ecdh' | The elliptic curve algorithm was used to create the key. |
SignResponse
| Prop | Type | Description |
| --------------- | ------------------- | ------------------------------- |
| signature | string | The signature in base64 format. |
SignOptions
| Prop | Type | Description |
| ---------- | ----------------------------------------------------- | ------------------------------------------------------- |
| tag | string | The key-pair tag. |
| data | string | The data to sign. |
| type | BiometryType | Biometry type if key is possibly secured with biometry. |
VerifyResponse
| Prop | Type | Description |
| -------------- | -------------------- | ---------------------------------- |
| verified | boolean | Whether the signature is verified. |
VerifyOptions
| Prop | Type | Description |
| ---------------------- | ------------------- | ---------------------------------------- |
| foreignPublicKey | string | The foreign public-key in base64 format. |
| data | string | The signed data. |
| signature | string | The signature in base64 format. |
EncryptResponse
| Prop | Type | Description |
| ---------------- | ------------------- | ------------------------------------------------- |
| iv | string | The iv in base64 format. |
| ciphertext | string | The ciphertext (encrypted data) in base64 format. |
EncryptOptions
| Prop | Type | Description |
| ---------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
| tag | string | The key-pair tag. |
| foreignPublicKey | string | The foreign public-key in base64 format. |
| plaintext | string | The plaintext to be encrypted. |
| type | BiometryType | Biometry type if key is possibly secured with biometry. |
DecryptResponse
| Prop | Type | Description |
| --------------- | ------------------- | ------------------------ |
| plaintext | string | The decrypted plaintext. |
DecryptOptions
| Prop | Type | Description |
| ---------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
| tag | string | The key-pair tag. |
| foreignPublicKey | string | The foreign public-key in base64 format. |
| iv | string | The iv in base64 format. |
| ciphertext | string | The ciphertext (encrypted data) in base64 format. |
| type | BiometryType | Biometry type if key is possibly secured with biometry. |
BiometricsEnabledResponse
| Prop | Type | Description |
| --------------- | -------------------- | ---------------------------- |
| isEnabled | boolean | Whether biometry is enabled. |
BiometricsEnabledOptions
| Prop | Type | Description |
| ---------- | ----------------------------------------------------- | ------------------------------------------------------ |
| type | BiometryType | The biometry type whose availability is to be checked. |
BiometricsStatusResponse
| Prop | Type | Description |
| ------------ | ------------------------------------------------------------- | --------------------------------- |
| status | BiometricsStatus | Status of biometry on the device. |
BiometricsStatusOptions
| Prop | Type | Description |
| ---------- | ----------------------------------------------------- | ------------------------------------------------------ |
| type | BiometryType | The biometry type whose availability is to be checked. |
AvailableHardwareResponse
| Prop | Type | Description |
| -------------- | --------------------------------- | ------------------------------------ |
| hardware | BiometricsHardware[] | List of available biometry hardware. |
DevicePasscodeResponse
| Prop | Type | Description |
| ------------------------- | -------------------- | ------------------------------- |
| isDevicePasscodeSet | boolean | Whether device passcode is set. |
SecureHardwareResponse
| Prop | Type | Description |
| ----------------------- | -------------------- | --------------------------------------- |
| hasSecureHardware | boolean | Whether the device has secure hardware. |
RegisterPasskeyResult
| Prop | Type |
| ----------------------------- | ------------------------------------------------------------------- |
| id | string |
| rawId | string |
| type | string |
| response | { attestationObject: string; clientDataJSON: string; } |
| authenticatorAttachment | string |
RegisterPasskeyOptions
| Prop | Type |
| ---------------------------- | ------------------------------------------------------------------------ |
| challenge | string |
| rp | { id: string; name: string; } |
| user | { id: string; name: string; displayName: string; } |
| pubKeyCredParams | PublicKeyCredentialParameters[] |
| authenticatorSelection | { residentKey: 'required'; userVerification: 'preferred'; } |
| extensions | { credProps: boolean; } |
PublicKeyCredentialParameters
| Prop | Type |
| ---------- | ------------------------- |
| type | 'public-key' |
| alg | -7 |
AuthenticateWithPasskeyResult
| Prop | Type |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------- |
| id | string |
| rawId | string |
| type | string |
| response | { authenticatorData: string; clientDataJSON: string; signature: string; userHandle?: string; } |
| authenticatorAttachment | string |
AuthenticateWithPasskeyOptions
| Prop | Type |
| ---------------------- | ------------------------- |
| challenge | string |
| rpId | string |
| allowCredentials | Credential[] |
| userVerification | 'required' |
Credential
| Prop | Type |
| ---------- | ------------------------- |
| type | 'public-key' |
| id | string |
Type Aliases
BiometryType
'BIOMETRY' | 'BIOMETRY_OR_PASSCODE' | 'PASSCODE'
BiometricsStatus
'SUCCESS' | 'HARDWARE_UNAVAILABLE' | 'NONE_ENROLLED' | 'UNKNOWN'
BiometricsHardware
'FINGER' | 'IRIS' | 'FACE'
