Closed mohachouch closed 6 days ago
/cc @brianquinlan
Hey @mohachouch,
Could you explain your use case here? If you were going to use a Kotlin API, what would you do with privateKey
?
It is possible to get the byte representation of a java.security.Key
with [Key.getEncoded()
](https://developer.android.com/reference/java/security/Key#getEncoded()). Assuming that it is an X509 key then you should be able to load it into a SecurityContext
with setTrustedCertificateBytes
Let me know if that is not what you are trying to do.
Cheers, Brian
Hey @brianquinlan,
Thanks for you help.
As part of our company, we have an Android tablet application that allows very secure operations.
For this, https exchanges are secured by a client certificate. The certificate is automatically sent to the tablets and I do not have direct access to the certificate.
Currently we use Xamarin, we have direct access to the native Http Client so we can easily pass the KeyStore certificate to the HTTP session but in flutter, I don't know how to do it.
val certificateChain = KeyChain.getCertificateChain(mainActivity.applicationContext, alias)?.get(0);
val privateKey = KeyChain.getPrivateKey(mainActivity.applicationContext, alias);
certificateChain .encoded is not null but not working BAD_PKCS12_DATA
privateKey.encode is null
I tried to pass the certificate to the security context, but I got a BAD_PKCS12_DATA error
I'm a bit lost and I don't know exactly how to retrieve the client certificate from Android KeyChain and how to pass it to the security context.
Thanks
I tried to solve the problem all day, the client authentication works fine when I add the test certificate in the assets, but impossible from the KeyChain. Unable to retrieve certificate and private key.
I'm starting to tell myself that it's not possible and that I'll have to use the native http client
Please help me, I have to present a proof of concept of the project to my superiors
Hi @mohachouch,
What call fails: SecurityContext.useCertificateChain
or SecurityContext.usePrivateKey
?
useCertificateChain
and usePrivateKey
expect the data to be in PEM
or PKCS12
format.
Certificate.encoded
is ASN.1 DER
format.
PEM
is just base64 encoded DER with a header and footer.
If you can use the basic_utils
package then a crypto function might help.
Like crlDerToPem
(though that might put the wrong header and footer).
You can make your own converter based on the header/foots that you need i.e.
final privateKey = formatKeyString(base64.encode(bytes),
X509Utils. BEGIN_PRIVATE_KEY, X509Utils.END_PRIVATE_KEY)
final certChain = formatKeyString(base64.encode(bytes),
X509Utils.BEGIN_CERT, X509Utils.END_CERT)
Thank you @brianquinlan for the explanation of the different certficate formats.
For certficate retrieval on Kotlin I use this function
val certificateChain = KeyChain.getCertificateChain(mainActivity.applicationContext, alias)!!.get(0);
With the conversion to the PEM format, the certificate, it's OK, Thanks you
But, I still have a problem for the private key
For private key retrieval on Kotlin I use this function :
val privateKey = KeyChain.getPrivateKey(mainActivity.applicationContext, alias);
val encoded = privateKey.encoded
The problem is that the value of encoded is null.
The type of my private key is : AndroidKeyStoreRSAPrivateKey
The implementation in android is to return null for encoded
The key store documentation said : Once keys are in the keystore, you can use them for cryptographic operations, with the key material remaining non-exportable. https://developer.android.com/training/articles/keystore
Do you have an idea ?
Thanks
@mohachouch - how do you use val certificateChain = KeyChain.getCertificateChain(mainActivity.applicationContext, alias)!!.get(0);
in your current code?
I use platform channels to switch from Kotlin to flutter.
I don't think the Keychain can be accessed from flutter.
KeyChain.choosePrivateKeyAlias allows me to retrieve the alias certificate that the user has selected
KeyChain.choosePrivateKeyAlias
KeyChain.getCertificateChain allows me to retrieve the certificate
val certificateChain = KeyChain.getCertificateChain(mainActivity.applicationContext, alias)!!.get(0);
KeyChain.getPrivateKey allows me to retrieve the private key
val privateKey = KeyChain.getPrivateKey(mainActivity.applicationContext, alias);
But the problem is that it is of type AndroidKeyStoreRSAPrivateKey and the value of getEncoded() is null
So I feel like I couldn't extract the private key from the Android KeyChain to Flutter and I'll have to use the native http client
@brianquinlan Can you confirm that this is not possible with the current http client implementation? Thanks
It is not possible using dart:io
HttpClient
but it might be possible using another dart HTTP client.
Could you tell me how you are using this in your existing Xamarin application? I'd like to understand what APIs you are using.
For example, are you using a custom SSLSocketFactory
?
Out of curiosity I poked a bit around in BoringSSL and Android to understand if this sort of thing is supported and how.
It seems that if we wanted to support this in our SecureSocket
we would need to implement a BoringSSL ENGINE which can perform operations with opaque private keys. (example from AOSP source here). The operations have to be performed by talking to keystore service over IPC. Here is some example of how keystore client in C++ could look like.
This is all very poorly documented territory.
@mraleph We'd also have to expose PrivateKey
(or similar) as a Dart API.
Yes @brianquinlan, wa are using a custom SSLSocketFactory with native http client OkHttp
I will share an sample un GitHub tomorow
@mraleph We'd also have to expose
PrivateKey
(or similar) as a Dart API.
Yeah, we would need some API changes. Maybe we don't explicitly need PrivateKey
but something like
class SecurityContext {
void setClientCertificateFromKeyStore(String keyStoreAlias);
}
Though this might not be all too portable.
Yeah @mraleph
We can add an AndroidSecurityContext
class AndroidSecurityContext extends SecurityContext {
Future<String> askAliasFromKeyStore()
void setClientCertificateFromKeyStore(String keyStoreAlias);
}
Hey @brianquinlan, here is an github gist of using the private key and certificate in Native/Koltin
https://gist.github.com/mohachouch/c079ca23568cc75c6a50c0f4f924f51d
I think the native implementation in dart can be very interesting. Flutter is starting to be used more and more in business and I have no doubt that other people will have the same need.
Thanks
Hey @mohachouch,
As far as I know, there is nothing in the Dart ecosystem that will allow you to do this without writing a native plugin.
If you decide to take the plugin approach, it would probably end up looking like package:cronet_http
but you'd wrap OkHttp
instead of Cronet
.
Hey @brianquinlan
I started the implementation with okhttp. I got a lot of inspiration from cronethttp (Thanks)
https://github.com/mohachouch/native_http_client
It's not production ready but it's work 😉
Hey @mohachouch,
I see that your repo is now non-public. Did you decide on a different approach?
Hey @brianquinlan,
Oup's, I think now it's ok, the repo is public
We'll start by using this approach. But we really hope it will be supported by the Dart team. (Native client or dart client with client certificate from keychain)
Thanks
Closing issue here and will track this feature in https://github.com/dart-lang/http/issues/1237
Hello,
We want to use flutter/dart for a large application in France.
The application must be able to retrieve the client certificate from the Android KeyChain.
Android side (Kotlin):
Dart side: The security context only accepts an array of bytes.
We will begin migrating from Xamarin in January. Is it possible to do this? If not, we will have to go to another technology.
Thank you