apple / swift-crypto

Open-source implementation of a substantial portion of the API of Apple CryptoKit suitable for use on Linux platforms.
https://apple.github.io/swift-crypto
Apache License 2.0
1.47k stars 166 forks source link

Encrypted PEM keys #264

Open fpseverino opened 1 month ago

fpseverino commented 1 month ago

New API Proposal: Support for encrypted PEM private keys

Motivation:

Currently, the library can read and handle unencrypted PEM-encoded private keys, but it can't directly load encrypted PEM keys. This can be a limitation in scenarios where users need to store their private keys in an insecure place or when working with third-party APIs that give the users encrypted PEM keys (e.g., PassKit/Apple Wallet). Also, there is a bit of inconsistency within the ecosystem, as NIOSSL supports them, but only for TLS/SSL related stuff.

Importance:

The best solution I know of at the moment is to use an openssl executable (where available) inside a blocking Process. That's how we do it currently in the vapor-community/PassKit library.

cc @0xTim

Lukasa commented 1 month ago

Thanks for filing this!

One of the wrinkles of supporting encrypted PEM keys is that those encryption schemes often rely on very weak cryptography that we don't otherwise want to support in Swift Crypto. A similar issue occurs with PKCS#12 support.

I think a pragmatic compromise might be to add interfaces in CryptoExtras that directly call into the underlying BoringSSL functions to perform the decryption, rather than attempt to add fully-fledged support for those cryptosystems to Crypto itself.

See-also #263.

fpseverino commented 3 days ago

What would be the best approach to add this functionality? Maybe create some internal helper methods that decrypt the key, and then add to each individual CryptoExtras algorithm another private key initializer that first decrypts it and then passes it to the traditional initializer?

Lukasa commented 10 hours ago

That approach might work, but it'll require us to reverse-engineer the encrypted PEM format. Probably a suitable strategy though if you wanted to tackle it!

fpseverino commented 9 hours ago

Got it! What other strategies can we consider?

Lukasa commented 9 hours ago

I think the other strategy is to call the higher-level BoringSSL function to parse the encrypted key format, which will return a structured BoringSSL pointer. That can then be either passed directly to a package-level initializer (on Linux) or deserialized and reserialized (on Apple platforms).

I think that's a bit less appealing, even though it's probably easier. Partly it's less appealing because it means there are multiple separate key format parsers, which is a bit rough.