# Encrypt data

**URL:** <https://forums.swift.org/t/encrypt-data/76536>\
**Category:** Swift Crypto\
**Created:** [December 11, 2024, 12:40pm UTC](https://forums.swift.org/t/encrypt-data/76536 "2024-12-11T12:40:04Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 11, 2024, 12:40pm UTC](https://forums.swift.org/t/encrypt-data/76536/1 "2024-12-11T12:40:04Z")

</div>

Currently, I’m building an app where I use Supabase to store my data. However, I have some sensitive data that I’d like to protect. So, my idea is to generate a RSA key, save it in the keychain, sync it with iCloud, and then encrypt the data.

Does anyone know an easy and simple solution to protect the data?

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 11, 2024, 5:52pm UTC](https://forums.swift.org/t/encrypt-data/76536/2 "2024-12-11T17:52:12Z")

</div>

You probably want [AES.GCM](https://developer.apple.com/documentation/cryptokit/aes/gcm/seal(_:using:nonce:))?

I then store the Symmetric encryption key in Keychain?

But we would really benefit from knowing more about the parties - if plural.

Is it the user - trough the app - who is encrypting data and then later decrypting the data - with cloud as a data store ?

Or is it some other party then end user who is going to decrypt the data? If so you need some kind of IES, maybe ECIES over Curve25519 - that is a key agreement + encryption

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 11, 2024, 6:09pm UTC](https://forums.swift.org/t/encrypt-data/76536/3 "2024-12-11T18:09:09Z")

</div>

Thank you for your response.

The entire security aspect is quite new to me, and I’m also a bit apprehensive about making mistakes.

Currently, I’m developing a mental health app. Before, I used SwiftData. However, when I attempted to integrate CloudKit, the performance significantly degraded. In general, I believe SwiftData is still too much in Beta to be used. Therefore, I decided to switch to Supabase. My primary objective is to ensure the best possible privacy and security. I intend to encrypt only the plain text inputs, rather than all the data models.

My idea was to give the user the ability to encrypt and decrypt their data. To achieve this, I intended to utilize the keychain and synchronize the key with iCloud if the user opts for an alternative device. I had already reviewed the Apple documentation to understand the process, but unfortunately, most of the provided examples didn’t work for me.

Could you recommend some resources for a beginner in this field? I would like to avoid over-engineering my solution.

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 11, 2024, 6:31pm UTC](https://forums.swift.org/t/encrypt-data/76536/4 "2024-12-11T18:31:23Z")

</div>

Ok so it sounds like end user _Alice_ downloads you app. You , Roman, as an app developer wants to store Alices data in a centralized party, Supabase, cloud storage. You are concerned with the privacy of Alices data, since it is senstive, thus, do don't want to store her sensitive data as plaintext in Supabase. Thus you want to develop your app so that Alices data is encrypted in your app, before your app sends it to Supabase. If Alice wants to view her past data, she can do so, by letting your app query Supabase, and once fetched in your app, it will decrypt Alices sensitive data and display it to her.

Is that somewhat accurate?

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 11, 2024, 6:39pm UTC](https://forums.swift.org/t/encrypt-data/76536/5 "2024-12-11T18:39:19Z")

</div>

If so, it is very easy:

1. Upon app first launch you generate a Symmetric encryption key
2. Store the Symmetric encryption key in Keychain - it is just 32 bytes, so you can save it as a data entry - with iCloud sync enabled - will only work if user has enabled keychain icloud sync in iOS
3. Gather health data from end user Alice
4. Encrypt Alices sensitive data using AES.GCM I linked to above
5. Use encrypted data (send to Supabase)
6. Later download data from Supabase, decrypt it by:
7. … loading the Symmetric encryption key from Keychain
8. And call AES.GCM.open using the encrypted data and the key
9. Optionally you can allow user to manually export her encryption key

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 11, 2024, 9:47pm UTC](https://forums.swift.org/t/encrypt-data/76536/6 "2024-12-11T21:47:11Z")

</div>

Yeah, that's pretty accurate.

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 11, 2024, 9:51pm UTC](https://forums.swift.org/t/encrypt-data/76536/7 "2024-12-11T21:51:04Z")

</div>

Thank you so much for your valuable tips.

I have a question. How can I be certain that it only generates one key pair?

Also, would you recommend changing the key after a few months?

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 12, 2024, 6:34am UTC](https://forums.swift.org/t/encrypt-data/76536/8 "2024-12-12T06:34:37Z")

</div>

32 byte key with 256bit AES, no you dont need to worry about that. There are more keys then there are atoms in the entire universe.

Generate once and use forever.

But **how** you generate is ofc important. Must be a Cryptographically Secure Pseudo Random Number Generator (CSPRNG). You need only use [this init](https://developer.apple.com/documentation/cryptokit/symmetrickey/init(size:)), and specify 256bits

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 12, 2024, 7:28am UTC](https://forums.swift.org/t/encrypt-data/76536/9 "2024-12-12T07:28:56Z")

</div>

Nice, so that makes it easier.

I found these two pages from Apple Doc about generating and storing keys, do you think they are good?

> **[Generating New Cryptographic Keys | Apple Developer Documentation](https://developer.apple.com/documentation/security/generating-new-cryptographic-keys)**
>
> Create both asymmetric and symmetric cryptographic keys.

> **[Storing Keys in the Keychain | Apple Developer Documentation](https://developer.apple.com/documentation/security/storing-keys-in-the-keychain)**
>
> Store and access cryptographic keys in the keychain.

I’ve also saw that they recommend using the Secure Enclave to make it even more secure. But I’m not sure if it’s then still compatible with iCloud sync. Do you think this is need?

> **[Protecting keys with the Secure Enclave | Apple Developer Documentation](https://developer.apple.com/documentation/security/protecting-keys-with-the-secure-enclave)**
>
> Create an extra layer of security for your private keys.

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 12, 2024, 7:53am UTC](https://forums.swift.org/t/encrypt-data/76536/10 "2024-12-12T07:53:35Z")

</div>

I would advice you to ignore all those articles. They are more confusing than not.

I use [KeychainAccess package](https://github.com/kishikawakatsumi/KeychainAccess):

```swift
let keychain = Keychain(...).synchronizable(true) // `synchronizable` => sync to iCloud for all items by default

let encryptionKey = SymmetricKey(size: .bits256)
let encryptionKeyIdentifierInKeychain = "my.healt.app.encryption.key"
let keyData = encryptionKey.withUnsafeBytes { Data($0) }
keychain
.synchronizable(true) // Can set icloud sync per item also https://github.com/kishikawakatsumi/KeychainAccess?tab=readme-ov-file#one-shot-2
.set(keyData, key: encryptionKeyIdentifierInKeychain)

/// later when you want to access it:
guard 
    let encryptionKeyData = try keychain[encryptionKeyIdentifierInKeychain],
    encryptionKeyData.count * 8 != SymmetricKeySize.bits256 // 8 bits per byte
else { // throw if nil or wrong size... }
let encryptionKey = SymmetricKey(data: encryptionKeyData)

```

That is is.

All those APIs about specific KEYS in Keychain will just confuse things. That is more about doing Elliptic Curve Cryptography - i.e. asymmetric keys that you wanna store in KeyChain, maybe in Secure Enclave and use for EC signing. Which is **not** related to encryption.

If you take a look at the `SecureEnclave` namespace: [SecureEnclave | Apple Developer Documentation](https://developer.apple.com/documentation/cryptokit/secureenclave/) you will see that it only relates to asymmetric / public-key / elliptic curve cryptograhy.

If you want to make the app "even safer" you can do two things:

- Not sync the `SymmetricKey` to Keychain iCloud
- Chose to store the `SymmetricKey` **only** on the user device using [kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly](https://developer.apple.com/documentation/security/ksecattraccessiblewhenpasscodesetthisdeviceonly) - which you can configure using `KeychainAccess package`. **The implications of this is that if user removes her passcode =\> encryption key gets deleted =\> user can cannot decrypt her previous data.** Also if user changes phone =\> user cannot decrypt her previous data.

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 12, 2024, 11:50am UTC](https://forums.swift.org/t/encrypt-data/76536/11 "2024-12-12T11:50:38Z")

</div>

Thank you very much for your example.

I noticed that the last commit was made a year ago. So, is it still safe to use?

I think it would be better to leave that feature with the Secure Enclave. It probably wouldn’t be possible to sync it with iCloud.

I’m facing a tough decision between security and availability. I believe I need to make a trade-off. In this case, I prioritize that users can access my app across devices and that their key won’t be lost suddenly. I’m curious about Apple’s approach to this issue with the Apple Health app.

Or, how do you perceive the situation?

When I initialize the key, do I need to check if it’s already stored in iCloud? Additionally, do I need to enable the iCloud capability for the app?

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 13, 2024, 8:41am UTC](https://forums.swift.org/t/encrypt-data/76536/12 "2024-12-13T08:41:03Z")

</div>

We use Keychain Access in the [Radix Wallet](https://github.com/radixdlt/babylon-wallet-ios), it is a very popular package, it has not been updated in a while because the Keychain APIs does not really change. So yes I consider it safe to use. You can also take a look at [Valet](https://github.com/square/Valet), but I don't like that they want us to use their identifier, does not really mean anything, but feels more... "boxed in".

> I think it would be better to leave that feature with the Secure Enclave

What do you mean? which feature? You cannot use Secure Enclave. It does not support symmetric encryption.

**A common misconception** is that not using Secure Enclave is not safe... that is not true, Apples "root Keychain key" **itself** is stored in Secure Enclave, so whatever you store in ""just"" Keychain, is safe!

> It probably wouldn’t be possible to sync it with iCloud.

Correct, that is not possible - yet again, not relevant for you and your needs - but an Elliptic Curve Cryptography private key, e.g. on curve `P256` store in Secure Enclave **cannot leave the Secure Enclave** , that is the whole point! The Secure Enclave is a `HSM`: _hardware security_ module, much like e.g. Ledger Hardware wallets, and Elliptic Curve Cryptography **signing happens inside of the HSM**. But again, this is irrelevant for you.

> I’m facing a tough decision between security and availability. I believe I need to make a trade-off.

Go with availability is my recommendation.  
[kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly](https://developer.apple.com/documentation/security/kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) is typically for crypto wallets, where we protect a user entire life savings... I think the frustration of a user not being able to access her previous (health) data would have a bigger negative impact.

> do I need to enable the iCloud capability for the app?

(reversed the order of your two last questions...)  
No, this is NOT iCloud container. This **is Keychain** with Apples own extra feature of gratis also being able to sync it to iCloud. **If and only if end user has enabled that config in iOS settings!!!**.

> When I initialize the key, do I need to check if it’s already stored in iCloud

No. I think you would benefit from thinking about Keychain (possibly with iCloud sync, once again, not iCloud container) as `UserDefault`, but safe, and if end user has enable config of Keychain iCloud sync and if **you set the key-value item as `synchronizable(true)`** it will be synced to iCloud.

Just generate a SymmetricKey, save it to Keychain. Done. Use it.

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 14, 2024, 10:03am UTC](https://forums.swift.org/t/encrypt-data/76536/13 "2024-12-14T10:03:36Z")

</div>

Thank you for your in-depth explanation.

Sorry if I misspoke. I'll just leave Secure Enclave out of my app. When I read the text from Apple docs that it's a potential risk not to use Secure Enclave, I thought I had to use it. But in this case it was a misunderstanding. Thanks for the clarification.

> Keeping a private key in a keychain is a great way to secure it. The key data is encrypted on disk and accessible only to your app or the apps you authorize. However, to use the key, you must briefly copy a plain-text version of it into system memory. While this presents a reasonably small attack surface, there’s still the chance that if your app is compromised, the key could also become compromised. As an added layer of protection, you can protect a private key using the Secure Enclave.

I have now tried to implement it in my demo app. Do you think it's necessary to put it in a func or separate struct? Unfortunately it didn't work. Do you have any recommendations for me?

 ![Screenshot 2024-12-14 at 11.01.36](https://global.discourse-cdn.com/swift/original/3X/2/2/226251481af48ada61f5f7f1c105de2cf706059e.jpeg)

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 14, 2024, 10:11am UTC](https://forums.swift.org/t/encrypt-data/76536/14 "2024-12-14T10:11:08Z")

</div>

Ah just assert that count is 32. It is ok to hard code that

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 14, 2024, 10:21am UTC](https://forums.swift.org/t/encrypt-data/76536/15 "2024-12-14T10:21:29Z")

</div>

Would you outsource these API accesses into a func or separate struct?

What do you think what the error is here?

 ![Screenshot 2024-12-14 at 11.19.24](https://global.discourse-cdn.com/swift/original/3X/b/b/bb815941f46244f1b21d3382b37e2449bc03585e.jpeg)

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 14, 2024, 5:46pm UTC](https://forums.swift.org/t/encrypt-data/76536/16 "2024-12-14T17:46:17Z")

</div>

Ah you are using the subscript reader on the keychain which returns `String`, use `try keychain.getData(...)` instead.

Yes ofc you should write somekind of dependency for this. You probably wanna use [GitHub - pointfreeco/swift-dependencies: A dependency management library inspired by SwiftUI's "environment."](https://github.com/pointfreeco/swift-dependencies)

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 25, 2024, 12:35pm UTC](https://forums.swift.org/t/encrypt-data/76536/17 "2024-12-25T12:35:09Z")

</div>

At the moment, I’m attempting to implement the entire encryption and deception logic into my demo application. Regrettably, I haven’t been able to make it work. I consistently encounter an error while encrypting, but I’m at a loss as to why. Currently, it appears that I need to convert the „Data“ obtained after encryption back to a String before uploading it to Supabase. Alternatively, do you have any insights into whether it’s possible to upload the data directly from the encryption process to Supabase?

Here’s my code for encryption and decryption:

```swift
   Task {
                        do {
                            try cryptoManager.createEncryptionKey(encryptionKeyIdentifier: "ch.romanindermuehle.iSecret.encryptionKey")
                            
                            let encryptionKey = try cryptoManager.getEncryptionKey(encryptionKeyIdentifier: "ch.romanindermuehle.iSecret.encryptionKey")
                            
                            guard let textData = text.data(using: .utf8) else { return }
                            
                            guard let encryptedText = try? String(data: CryptoKit.AES.GCM.seal(textData, using: encryptionKey).ciphertext, encoding: .utf8) else { throw "Unable to encrypt" // I always get an error here }
                            
                            let secret = Secret(text: encryptedText)
                            
                            try await supabaseManager.upsertSecret(secret)
                            
                            dismiss()
                        } catch {
                            print(error)
                        }
                    }

```

```swift
do {
                    secrets = try await supabaseManager.getSecrets().map { secret in
                        let encryptionKey = try cryptoManager.getEncryptionKey(encryptionKeyIdentifier: "ch.romanindermuehle.iSecret.encryptionKey")
                        
                        let encryptedText = try CryptoKit.AES.GCM.SealedBox(combined: Data(secret.text.utf8))
                        
                        let decreptedText = try CryptoKit.AES.GCM.open(encryptedText, using: encryptionKey)
                        
                        return Secret(id: secret.id, text: String(decoding: decreptedText, as: UTF8.self), createdAt: secret.createdAt)
                    }
                    
                    
                } catch {
                    print(error)
                }

```

---

<div class="post-metadata">

**Author:** ![bjhomer](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bjhomer/32/313_2.png) [@bjhomer](https://forums.swift.org/u/bjhomer)\
**Post date:** [December 26, 2024, 6:34am UTC](https://forums.swift.org/t/encrypt-data/76536/18 "2024-12-26T06:34:13Z")

</div>

> [@romanindermuehle](#):
>
> `guard let encryptedText = try? String(data: CryptoKit.AES.GCM.seal(textData, using: encryptionKey).ciphertext, encoding: .utf8)`

This won’t work; the result of `GCM.seal()` is not valid UTF8 data. You want to take the data that comes out and work with that directly. That’s the ideal option.

```swift
let encryptedData = CryptoKit.AES.GCM.seal(textData, using: encryptionKey).ciphertext
// send encryptedData to the server

```

If you NEED to convert it to a string, you’d need to use an encoding that can handle arbitrary data, like hexadecimal or base64.

```swift
let base64String = encryptedData.base64EncodedStrinf()

```

---

<div class="post-metadata">

**Author:** ![romanindermuehle](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/romanindermuehle/32/35940_2.png) [@romanindermuehle](https://forums.swift.org/u/romanindermuehle)\
**Post date:** [December 26, 2024, 12:51pm UTC](https://forums.swift.org/t/encrypt-data/76536/19 "2024-12-26T12:51:18Z")

</div>

Thank you for your suggestion.

I’ve tried it out, but unfortunately, I’m encountering another error. It says that the parameter size is incorrect when I try to open the SealedBox. Do you have any idea why it’s throwing this error?

```swift
secrets = try await supabaseManager.getSecrets().map { secret in
                        let encryptionKey = try cryptoManager.getEncryptionKey(encryptionKeyIdentifier: "ch.romanindermuehle.iSecret.encryptionKey")
                        
                        let encryptedText = try CryptoKit.AES.GCM.SealedBox(combined: Data(base64Encoded: secret.text)!)
                        
                        let decreptedText = try CryptoKit.AES.GCM.open(encryptedText, using: encryptionKey)
                        
                        return Secret(id: secret.id, text: String(data: decreptedText, encoding: .utf8) ?? "", createdAt: secret.createdAt)
                    }

```

```swift
try cryptoManager.createEncryptionKey(encryptionKeyIdentifier: "ch.romanindermuehle.iSecret.encryptionKey")
                            
                            let encryptionKey = try cryptoManager.getEncryptionKey(encryptionKeyIdentifier: "ch.romanindermuehle.iSecret.encryptionKey")
                            
                            guard let textData = text.data(using: .utf8) else { return }
                            
                            guard let encryptedText = try? CryptoKit.AES.GCM.seal(textData, using: encryptionKey).ciphertext else { throw "Unable to encrypt" }
                            
                            let secret = Secret(text: encryptedText.base64EncodedString())
                            
                            try await supabaseManager.upsertSecret(secret)

```

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [December 26, 2024, 1:59pm UTC](https://forums.swift.org/t/encrypt-data/76536/20 "2024-12-26T13:59:28Z")

</div>

You need to read out `combined` and not the `ciphertext` from the SealedBox:

> **[combined | Apple Developer Documentation](https://developer.apple.com/documentation/cryptokit/aes/gcm/sealedbox/combined)**
>
> A combined element composed of the nonce, encrypted data, and authentication tag.

[Next page](https://forums.swift.org/t/encrypt-data/76536.md?page=2)
