feat: ED25519 keypair instead of P256 (#59)
Reviewed-on: #59 Co-authored-by: Florian Griffon <florian.griffon@epitech.eu> Co-committed-by: Florian Griffon <florian.griffon@epitech.eu>
This commit is contained in:
parent
675b1c0c7b
commit
ed07022916
@ -1,28 +0,0 @@
|
||||
package com.icing.dialer
|
||||
|
||||
import java.security.KeyStore
|
||||
|
||||
object KeyDeleterHelper {
|
||||
|
||||
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
|
||||
|
||||
/**
|
||||
* Deletes the key pair associated with the given alias from the Android Keystore.
|
||||
*
|
||||
* @param alias The alias of the key pair to delete.
|
||||
* @throws Exception if deletion fails.
|
||||
*/
|
||||
fun deleteKeyPair(alias: String) {
|
||||
try {
|
||||
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
|
||||
|
||||
if (!keyStore.containsAlias(alias)) {
|
||||
throw Exception("No key found with alias \"$alias\" to delete.")
|
||||
}
|
||||
|
||||
keyStore.deleteEntry(alias)
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Failed to delete key pair: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package com.icing.dialer
|
||||
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyProperties
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.KeyStore
|
||||
|
||||
object KeyGeneratorHelper {
|
||||
|
||||
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
|
||||
|
||||
/**
|
||||
* Generates an ECDSA P-256 key pair and stores it in the Android Keystore.
|
||||
*
|
||||
* @param alias Unique identifier for the key pair.
|
||||
* @throws Exception if key generation fails.
|
||||
*/
|
||||
fun generateECKeyPair(alias: String) {
|
||||
try {
|
||||
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
|
||||
|
||||
// Check if the key already exists
|
||||
if (keyStore.containsAlias(alias)) {
|
||||
throw Exception("Key with alias \"$alias\" already exists.")
|
||||
}
|
||||
|
||||
val keyPairGenerator = KeyPairGenerator.getInstance(
|
||||
KeyProperties.KEY_ALGORITHM_EC,
|
||||
ANDROID_KEYSTORE
|
||||
)
|
||||
|
||||
val parameterSpec = KeyGenParameterSpec.Builder(
|
||||
alias,
|
||||
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
|
||||
)
|
||||
.setAlgorithmParameterSpec(java.security.spec.ECGenParameterSpec("secp256r1"))
|
||||
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512)
|
||||
.setUserAuthenticationRequired(false) // Set to true if you require user authentication
|
||||
.build()
|
||||
|
||||
keyPairGenerator.initialize(parameterSpec)
|
||||
keyPairGenerator.generateKeyPair()
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Failed to generate EC key pair: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.icing.dialer
|
||||
|
||||
import java.security.PrivateKey
|
||||
import android.os.Build
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyProperties
|
||||
import android.util.Base64
|
||||
@ -8,15 +8,21 @@ import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.KeyStore
|
||||
import java.security.PrivateKey
|
||||
import java.security.Signature
|
||||
import java.security.spec.ECGenParameterSpec
|
||||
|
||||
class KeystoreHelper(private val call: MethodCall, private val result: MethodChannel.Result) {
|
||||
|
||||
private val ANDROID_KEYSTORE = "AndroidKeyStore"
|
||||
|
||||
fun handleMethodCall() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
result.error("UNSUPPORTED_API", "ED25519 requires Android 11 (API 30) or higher", null)
|
||||
return
|
||||
}
|
||||
when (call.method) {
|
||||
"generateKeyPair" -> generateECKeyPair()
|
||||
"generateKeyPair" -> generateEDKeyPair()
|
||||
"signData" -> signData()
|
||||
"getPublicKey" -> getPublicKey()
|
||||
"deleteKeyPair" -> deleteKeyPair()
|
||||
@ -25,7 +31,7 @@ class KeystoreHelper(private val call: MethodCall, private val result: MethodCha
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateECKeyPair() {
|
||||
private fun generateEDKeyPair() {
|
||||
val alias = call.argument<String>("alias")
|
||||
if (alias == null) {
|
||||
result.error("INVALID_ARGUMENT", "Alias is required", null)
|
||||
@ -44,16 +50,14 @@ class KeystoreHelper(private val call: MethodCall, private val result: MethodCha
|
||||
KeyProperties.KEY_ALGORITHM_EC,
|
||||
ANDROID_KEYSTORE
|
||||
)
|
||||
|
||||
val parameterSpec = KeyGenParameterSpec.Builder(
|
||||
alias,
|
||||
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
|
||||
)
|
||||
.setAlgorithmParameterSpec(java.security.spec.ECGenParameterSpec("secp256r1"))
|
||||
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512)
|
||||
.setAlgorithmParameterSpec(ECGenParameterSpec("ed25519"))
|
||||
.setDigests(KeyProperties.DIGEST_SHA256)
|
||||
.setUserAuthenticationRequired(false)
|
||||
.build()
|
||||
|
||||
keyPairGenerator.initialize(parameterSpec)
|
||||
keyPairGenerator.generateKeyPair()
|
||||
|
||||
@ -73,17 +77,14 @@ class KeystoreHelper(private val call: MethodCall, private val result: MethodCha
|
||||
|
||||
try {
|
||||
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
|
||||
|
||||
val privateKey = keyStore.getKey(alias, null) as? PrivateKey ?: run {
|
||||
result.error("KEY_NOT_FOUND", "Private key not found for alias \"$alias\".", null)
|
||||
return
|
||||
}
|
||||
|
||||
val signature = Signature.getInstance("SHA256withECDSA")
|
||||
val signature = Signature.getInstance("Ed25519")
|
||||
signature.initSign(privateKey)
|
||||
signature.update(data.toByteArray())
|
||||
val signedBytes = signature.sign()
|
||||
|
||||
val signatureBase64 = Base64.encodeToString(signedBytes, Base64.DEFAULT)
|
||||
result.success(signatureBase64)
|
||||
} catch (e: Exception) {
|
||||
|
@ -1,30 +0,0 @@
|
||||
package com.icing.dialer
|
||||
|
||||
import java.security.KeyStore
|
||||
import java.security.PublicKey
|
||||
import android.util.Base64
|
||||
|
||||
object PublicKeyHelper {
|
||||
|
||||
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
|
||||
|
||||
/**
|
||||
* Retrieves the public key associated with the given alias.
|
||||
*
|
||||
* @param alias The alias of the key pair.
|
||||
* @return The public key as a Base64-encoded string.
|
||||
* @throws Exception if retrieval fails.
|
||||
*/
|
||||
fun getPublicKey(alias: String): String {
|
||||
try {
|
||||
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
|
||||
|
||||
val certificate = keyStore.getCertificate(alias) ?: throw Exception("Certificate not found for alias \"$alias\".")
|
||||
val publicKey: PublicKey = certificate.publicKey
|
||||
|
||||
return Base64.encodeToString(publicKey.encoded, Base64.DEFAULT)
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Failed to retrieve public key: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package com.icing.dialer
|
||||
|
||||
import android.security.keystore.KeyProperties
|
||||
import java.security.KeyStore
|
||||
import java.security.Signature
|
||||
import android.util.Base64
|
||||
import java.security.PrivateKey
|
||||
|
||||
object SignerHelper {
|
||||
|
||||
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
|
||||
|
||||
/**
|
||||
* Signs the provided data using the private key associated with the given alias.
|
||||
*
|
||||
* @param alias The alias of the key pair.
|
||||
* @param data The data to sign.
|
||||
* @return The signature as a Base64-encoded string.
|
||||
* @throws Exception if signing fails.
|
||||
*/
|
||||
fun signData(alias: String, data: ByteArray): String {
|
||||
try {
|
||||
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
|
||||
|
||||
val privateKey = keyStore.getKey(alias, null) as? PrivateKey?: throw Exception("Private key not found for alias \"$alias\".")
|
||||
|
||||
val signature = Signature.getInstance("SHA256withECDSA")
|
||||
signature.initSign(privateKey)
|
||||
signature.update(data)
|
||||
val signedBytes = signature.sign()
|
||||
|
||||
return Base64.encodeToString(signedBytes, Base64.DEFAULT)
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Failed to sign data: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ class AsymmetricCryptoService {
|
||||
final String _aliasPrefix = 'icing_';
|
||||
final Uuid _uuid = Uuid();
|
||||
|
||||
/// Generates an ECDSA P-256 key pair with a unique alias and stores its metadata.
|
||||
/// Generates an ED25519 key pair with a unique alias and stores its metadata.
|
||||
Future<String> generateKeyPair({String? label}) async {
|
||||
try {
|
||||
// Generate a unique identifier for the key
|
||||
|
Loading…
Reference in New Issue
Block a user