Identities: let the user specify a subject CN
This commit is contained in:
parent
b6d855fdeb
commit
1d69c075a1
|
@ -4,8 +4,10 @@ import android.security.keystore.KeyGenParameterSpec
|
|||
import android.security.keystore.KeyProperties
|
||||
import android.util.Log
|
||||
import androidx.room.*
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.KeyStore
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
object Identities {
|
||||
@Entity
|
||||
|
@ -58,11 +60,20 @@ object Identities {
|
|||
Database.INSTANCE.identityDao().delete(*identities)
|
||||
}
|
||||
|
||||
fun generateClientCert(alias: String) {
|
||||
fun generateClientCert(alias: String, commonName: String) {
|
||||
val algo = KeyProperties.KEY_ALGORITHM_RSA
|
||||
val kpg = KeyPairGenerator.getInstance(algo, "AndroidKeyStore")
|
||||
val purposes = KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
|
||||
val spec = KeyGenParameterSpec.Builder(alias, purposes)
|
||||
.apply {
|
||||
if (commonName.isNotEmpty()) {
|
||||
try {
|
||||
setCertificateSubject(X500Principal("CN=$commonName"))
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e(TAG, "generateClientCert: bad common name: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
.setDigests(KeyProperties.DIGEST_SHA256)
|
||||
.build()
|
||||
kpg.initialize(spec)
|
||||
|
|
|
@ -82,27 +82,39 @@ class IdentitiesFragment : Fragment(), IdentitiesAdapter.Listener, IdentityEditD
|
|||
vm.saveIdentity(identity)
|
||||
}
|
||||
|
||||
private fun openNewIdentityEditor() {
|
||||
/**
|
||||
* Open the new identity wizard.
|
||||
*
|
||||
* There is a first dialog to ask the user about the desired subject common name,
|
||||
* then the certificate is generated and the edition dialog is opened.
|
||||
*/
|
||||
private fun openIdentityWizard() {
|
||||
InputDialog(requireContext(), getString(R.string.input_common_name))
|
||||
.show(
|
||||
onOk = { text ->
|
||||
toast(requireContext(), R.string.generating_keypair)
|
||||
vm.newIdentity.observe(viewLifecycleOwner) { identity ->
|
||||
if (identity == null)
|
||||
return@observe
|
||||
vm.newIdentity.removeObservers(viewLifecycleOwner)
|
||||
vm.newIdentity.value = null
|
||||
IdentityDialog(requireContext(), identity, this).show()
|
||||
IdentityEditDialog(requireContext(), identity, this).show()
|
||||
}
|
||||
vm.createNewIdentity()
|
||||
vm.createNewIdentity(text)
|
||||
},
|
||||
onDismiss = {}
|
||||
)
|
||||
}
|
||||
|
||||
class IdentitiesViewModel : ViewModel() {
|
||||
val identities: MutableLiveData<List<Identity>> by lazy { MutableLiveData<List<Identity>>() }
|
||||
val newIdentity: MutableLiveData<Identity> by lazy { MutableLiveData<Identity>() }
|
||||
|
||||
fun createNewIdentity() {
|
||||
fun createNewIdentity(commonName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val alias = "identity-${UUID.randomUUID()}"
|
||||
Identities.generateClientCert(alias)
|
||||
val newIdentityId = Identities.insert(alias)
|
||||
Identities.generateClientCert(alias, commonName)
|
||||
val newIdentityId = Identities.insert(alias, commonName)
|
||||
newIdentity.postValue(Identities.get(newIdentityId))
|
||||
}
|
||||
.invokeOnCompletion { refreshIdentities() }
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
<string name="confirm">Confirm</string>
|
||||
<string name="confirm_identity_delete">Are you sure you want to delete this identity? The client certificate cannot be retrieved afterwards.</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="identity_usages">Active URL paths</string>
|
||||
<string name="identity_usages">Active URL path</string>
|
||||
<string name="input_common_name">Enter a name to use as the certificate\'s subject common name. This can be left empty.</string>
|
||||
|
||||
</resources>
|
Reference in a new issue