Open OneManStudioDotSe opened 3 years ago
KeyStore.ProtectionParameter is an optional specification provided to add requirements (protection) for a KeyStore entry to be able to access/read/use/delete it. It is a standard Android interface, documented here: https://developer.android.com/reference/java/security/KeyStore.ProtectionParameter. You can decide what implementation class to use or it can be null for no special protection. I would recommend looking at some standard Android KeyStore examples to familiarize yourself with the interface. A non-null example of its use can be found in the code examples on the KeyStore page: https://developer.android.com/reference/java/security/KeyStore.html
KeyStore.ProtectionParameter is an optional specification provided to add requirements (protection) for a KeyStore entry to be able to access/read/use/delete it. It is a standard Android interface, documented here: https://developer.android.com/reference/java/security/KeyStore.ProtectionParameter. You can decide what implementation class to use or it can be null for no special protection. I would recommend looking at some standard Android KeyStore examples to familiarize yourself with the interface. A non-null example of its use can be found in the code examples on the KeyStore page: https://developer.android.com/reference/java/security/KeyStore.html
Thank you for the pointer and the example :)
I am still very new to Blockchain and signing of keys, so could you help me a bit? Based on the example that you linked, which will be my private and secret keys when used with eosio's keystore signature provider?
Originally I was only focused on your question about KeyStore.ProtectionParameter but looking at your other code now there are some potential issues there as well. Let's back up for a moment. What are you trying to do? Are you wanting to use an existing key or generate a new key in the Android KeyStore?
What you are doing in your original example is generating a new key with an alias (activeKey) and that returns a key in EOS format in keyResult, so your usage of val alias = keyResult
is incorrect. You would need to say val alias = activeKey
, if activeKey
was really a String being used as a key alias.
Then the KeyStore.ProtectionParameter password input to the sign method might be irrelevant unless you want more protection on the KeyStore entry. If you haven't set it up specifically then you really don't need it and you can pass in null
.
If you want to use a key that was created somewhere else and import it for signing messages, then you would normally need to look at the softkey signature provider but that only has the code to sign transactions. First let's get a better picture of what you need to do, then we might be able to provide a code snippet to help out as long as its not too involved.
Thank you for the interest to support me @opi-smccoole.
Let me take a step back and give you the bigger picture so you can hopefully point me to the right direction.
I am working on a mobile app where a user should be able to create a profile and login with his credentials.
The first thing I have to do when the user creates a profile is to generate the public and private keys. I am currently doing this with EosioAndroidKeyStoreUtility.generateAndroidKeyStoreKey(alias:String)
which is the equivalent of iOS's:
let newKey = try vault.newVaultKey(secureEnclave: true, protection: .whenUnlockedThisDeviceOnly, bioFactor: .none, metadata: [String: Any])
One question I have is how to get the public and private keys for the generated keys with the method above.
I then send some data to the backend which responds with an id
and a userName/walletId
.
When the user should then login he should use his userName/walletId
and sign it with the public key that was generated when the user was created. What I want to do is "sign" the userName/walletId
with the publicKey
with the use of eosio's SDK so I can go forward with the next steps. The equivalent action at the iOS SDK is done through the following:
let signature = vault.sign(message: message, eosioPublicKey: publicKey, requireBio: true) { (signature, error) in
// handle signature or error
}
The backend also knows the publicKey
for the specific userName/walletId
.
So my question(s) are how to perform those two tasks.
Any help is welcome :)
Like @opi-smccoole mentioned above, the EOSIO AndroidKeyStore Signature Provider library does not have a way to insert an existing key. It is impossible to extract the private key from the Android Keystore (that is how it works).
These are my answers for your questions:
EosioAndroidKeyStoreUtility.generateAndroidKeyStoreKey(alias:String)
, it will return back a public key that tight to a private key inside the Android Keystore. You can use the alias
that you put in when you generate the key to access its functionalities (but not extract it). Here is how you can use your public key to get the alias you want so you don't have to remember/store your alias.
// Get the list of aliases and public keys out from the Android Keystore in Pairs:
// the loadStoreParameter is optional so it could be null
val aliasKeyPairs: List<Pair<String, String>> =
EosioAndroidKeyStoreUtility.getAllAndroidKeyStoreKeysInEOSFormat(
password = this.password,
loadStoreParameter = this.loadStoreParameter
)
// Then you could use your public key to search for alias in that list
How do I sign a string with the previous public key?
When you find out what is the alias
you could use to sign from the above code.
val contentToSign : String = "username/walletId"
val byteArrayContentToSign: ByteArray = contentToSign.toByteArray()
val alias = "your alias"
val publicKey = "your public key"
// Put on your content in byte array to sign by using your alias
val rawSignature : ByteArray = EosioAndroidKeyStoreUtility.sign(
data = byteArrayContentToSign,
alias = alias,
loadStoreParameter = null,
password = null
) ?: throw Exception()
val signature : String = EOSFormatter.convertDERSignatureToEOSFormat(
rawSignature,
byteArrayContentToSign,
EOSFormatter.convertEOSPublicKeyToPEMFormat(publicKey)
)
Here is the working Unit test I just created which generate a new key and sign string:
@Test
fun testSignAString() {
val contentToSign : String = "username/walletId"
val byteArrayContentToSign: ByteArray = contentToSign.toByteArray()
val alias = "your_alias"
// Generate key
val publicKey : String = EosioAndroidKeyStoreUtility.generateAndroidKeyStoreKey(alias)
// Put on your content in byte array to sign by using your alias
val rawSignature : ByteArray = EosioAndroidKeyStoreUtility.sign(
data = byteArrayContentToSign,
alias = alias,
loadStoreParameter = null,
password = null
) ?: throw Exception()
val signature : String = EOSFormatter.convertDERSignatureToEOSFormat(
rawSignature,
byteArrayContentToSign,
EOSFormatter.convertEOSPublicKeyToPEMFormat(publicKey)
)
assert(signature.isNotBlank())
println("Signature $signature")
}
Hope that help. Please let me know if this does not work for you or you have any other question.
Like @opi-smccoole mentioned above, the EOSIO AndroidKeyStore Signature Provider library does not have a way to insert an existing key. It is impossible to extract the private key from the Android Keystore (that is how it works).
These are my answers for your questions:
- How do I generate the public and private keys for a user? When you call
EosioAndroidKeyStoreUtility.generateAndroidKeyStoreKey(alias:String)
, it will return back a public key that tight to a private key inside the Android Keystore. You can use thealias
that you put in when you generate the key to access its functionalities (but not extract it).Here is how you can use your public key to get the alias you want so you don't have to remember/store your alias.
// Get the list of aliases and public keys out from the Android Keystore in Pairs: // the loadStoreParameter is optional so it could be null val aliasKeyPairs: List<Pair<String, String>> = EosioAndroidKeyStoreUtility.getAllAndroidKeyStoreKeysInEOSFormat( password = this.password, loadStoreParameter = this.loadStoreParameter ) // Then you could use your public key to search for alias in that list
- How do I sign a string with the previous public key? When you find out what is the
alias
you could use to sign from the above code.val contentToSign : String = "username/walletId" val byteArrayContentToSign: ByteArray = contentToSign.toByteArray() val alias = "your alias" val publicKey = "your public key" // Put on your content in byte array to sign by using your alias val rawSignature : ByteArray = EosioAndroidKeyStoreUtility.sign( data = byteArrayContentToSign, alias = alias, loadStoreParameter = null, password = null ) ?: throw Exception() val signature : String = EOSFormatter.convertDERSignatureToEOSFormat( rawSignature, byteArrayContentToSign, EOSFormatter.convertEOSPublicKeyToPEMFormat(publicKey) )
Here is the working Unit test I just created which generate a new key and sign string:
@Test fun testSignAString() { val contentToSign : String = "username/walletId" val byteArrayContentToSign: ByteArray = contentToSign.toByteArray() val alias = "your_alias" // Generate key val publicKey : String = EosioAndroidKeyStoreUtility.generateAndroidKeyStoreKey(alias) // Put on your content in byte array to sign by using your alias val rawSignature : ByteArray = EosioAndroidKeyStoreUtility.sign( data = byteArrayContentToSign, alias = alias, loadStoreParameter = null, password = null ) ?: throw Exception() val signature : String = EOSFormatter.convertDERSignatureToEOSFormat( rawSignature, byteArrayContentToSign, EOSFormatter.convertEOSPublicKeyToPEMFormat(publicKey) ) assert(signature.isNotBlank()) println("Signature $signature") }
Hope that help. Please let me know if this does not work for you or you have any other question.
Your answer really helped me solve most of my problems and also understand things a lot more :) I can now generate keys, sign strings and handle the content in the way that I want.
My new problem though is that the generated signature that is something like "SIG_R1_K3o1Lb...cwWn
" is not what the Telos testnet that I am checking against expects (which is something like "PUB_K1_8YiBwKsG...P1oQw
". It seems that the signatures are generated differently. Do you have any clues towards which direction I should look to in order to find what is the root of the problem?
Sounds like the Telos testnet is expecting the signing to happen with K1 keys. The Android Keystore Signature Provider only generates and works with R1 keys. K1 keys can be used for signing Transactions using Android Softkey Signature Provider https://github.com/EOSIO/eosio-java-softkey-signature-provider but it does not provide support for signing arbitrary message data. If you need to do that you'll need to explore using some other library like bitcoinj or wrapping a C based library like libsecp256k1.
I have just started using this library and I have run into a problem that seems quite obvious but I can't get a grasp of it.
What I want to do is sign a string (walletId) with another string (userKey).
I generate a new key:
Prepare the fields that I need to sign the walletId
but I don't know what should be the contents of the
password
field. Thesign
method expects something of typeKeyStore.ProtectionParameter
as a parameter for the password but I have no idea where I can get a field of such type and from which source.Any help would be very much appreciated.