mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2024-12-22 20:40:16 +02:00
MOB-42 Fixed JWT generation issue.
This commit is contained in:
parent
c46c3082b7
commit
82b1538867
@ -1,6 +1,7 @@
|
|||||||
package com.tarkvaraprojekt.mobileauthapp
|
package com.tarkvaraprojekt.mobileauthapp
|
||||||
|
|
||||||
import android.content.Intent
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
import android.nfc.NfcAdapter
|
import android.nfc.NfcAdapter
|
||||||
import android.nfc.tech.IsoDep
|
import android.nfc.tech.IsoDep
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -9,16 +10,15 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
|
||||||
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
|
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
|
||||||
import com.tarkvaraprojekt.mobileauthapp.auth.Authenticator
|
import com.tarkvaraprojekt.mobileauthapp.auth.Authenticator
|
||||||
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentAuthBinding
|
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentAuthBinding
|
||||||
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
|
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment that asks the user to detect the ID card with mobile NFC chip.
|
* Fragment that asks the user to detect the ID card with mobile NFC chip.
|
||||||
@ -31,8 +31,6 @@ class AuthFragment : Fragment() {
|
|||||||
|
|
||||||
private var binding: FragmentAuthBinding? = null
|
private var binding: FragmentAuthBinding? = null
|
||||||
|
|
||||||
private val args: CanFragmentArgs by navArgs()
|
|
||||||
|
|
||||||
private lateinit var timer: CountDownTimer
|
private lateinit var timer: CountDownTimer
|
||||||
|
|
||||||
private var timeRemaining: Int = 90
|
private var timeRemaining: Int = 90
|
||||||
@ -71,7 +69,6 @@ class AuthFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getInfoFromIdCard(adapter: NfcAdapter) {
|
private fun getInfoFromIdCard(adapter: NfcAdapter) {
|
||||||
if (args.reading) {
|
|
||||||
adapter.enableReaderMode(activity, { tag ->
|
adapter.enableReaderMode(activity, { tag ->
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
@ -89,7 +86,7 @@ class AuthFragment : Fragment() {
|
|||||||
viewModel.setGender(response[3])
|
viewModel.setGender(response[3])
|
||||||
viewModel.setCitizenship(response[4])
|
viewModel.setCitizenship(response[4])
|
||||||
viewModel.setExpiration(response[5])
|
viewModel.setExpiration(response[5])
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread{
|
||||||
binding!!.timeCounter.text = getString(R.string.data_read)
|
binding!!.timeCounter.text = getString(R.string.data_read)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -106,31 +103,17 @@ class AuthFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, NfcAdapter.FLAG_READER_NFC_A, null)
|
}, NfcAdapter.FLAG_READER_NFC_A, null)
|
||||||
} else { //We want to create a JWT instead of reading the info from the card.
|
|
||||||
goToNextFragment()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun goToNextFragment() {
|
private fun goToNextFragment() {
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
if (args.auth) {
|
|
||||||
val action = AuthFragmentDirections.actionAuthFragmentToResultFragment(mobile = args.mobile)
|
|
||||||
findNavController().navigate(action)
|
|
||||||
} else {
|
|
||||||
findNavController().navigate(R.id.action_authFragment_to_userFragment)
|
findNavController().navigate(R.id.action_authFragment_to_userFragment)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun goToTheStart() {
|
private fun goToTheStart() {
|
||||||
viewModel.clearUserInfo()
|
viewModel.clearUserInfo()
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
if (args.reading) {
|
|
||||||
findNavController().navigate(R.id.action_authFragment_to_homeFragment)
|
findNavController().navigate(R.id.action_authFragment_to_homeFragment)
|
||||||
} else {
|
|
||||||
val resultIntent = Intent()
|
|
||||||
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
|
|
||||||
requireActivity().finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
@ -21,6 +21,7 @@ import java.security.MessageDigest;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
package com.tarkvaraprojekt.mobileauthapp.auth
|
package com.tarkvaraprojekt.mobileauthapp.auth
|
||||||
|
|
||||||
import android.os.Message
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
|
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
|
||||||
import io.jsonwebtoken.SignatureAlgorithm
|
import io.jsonwebtoken.SignatureAlgorithm
|
||||||
import org.bouncycastle.util.encoders.Base64
|
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import javax.crypto.Mac
|
|
||||||
import kotlin.experimental.and
|
|
||||||
|
|
||||||
class Authenticator(val comms : Comms) {
|
class Authenticator(val comms : Comms) {
|
||||||
|
|
||||||
@ -19,43 +15,45 @@ class Authenticator(val comms : Comms) {
|
|||||||
var iss = "https://self-issued.me" // Will be specified at a later date.
|
var iss = "https://self-issued.me" // Will be specified at a later date.
|
||||||
val algorithmUsedForSigning = SignatureAlgorithm.ES384
|
val algorithmUsedForSigning = SignatureAlgorithm.ES384
|
||||||
|
|
||||||
fun authenticate(challenge: String, originUrl: String, pin1: String) : String {
|
fun authenticate(challenge: String, originUrl: String, pin1: String): String {
|
||||||
|
|
||||||
// Ask PIN 1 from the user and get the authentication certificate from the ID card.
|
// Ask PIN 1 from the user and get the authentication certificate from the ID card.
|
||||||
val authenticationCertificate : ByteArray = comms.getCertificate(true);
|
val authenticationCertificate: ByteArray = comms.getCertificate(true);
|
||||||
|
|
||||||
// Encode the certificate in base64.
|
// Encode the certificate in base64.
|
||||||
val base64cert = String(Base64.encode(authenticationCertificate))
|
val base64cert = java.util.Base64.getEncoder().encodeToString(authenticationCertificate)
|
||||||
|
|
||||||
// Get current epoch time.
|
// Get current epoch time.
|
||||||
val epoch = LocalDateTime.now(ZoneOffset.UTC).atZone(ZoneOffset.UTC).toEpochSecond()
|
val epoch = LocalDateTime.now(ZoneOffset.UTC).atZone(ZoneOffset.UTC).toEpochSecond()
|
||||||
|
|
||||||
// Get expiration time.
|
// Get expiration time.
|
||||||
val exp = LocalDateTime.now(ZoneOffset.UTC).plusSeconds(5 * 60L).atZone(ZoneOffset.UTC).toEpochSecond()
|
val exp = LocalDateTime.now(ZoneOffset.UTC).plusSeconds(5 * 60L).atZone(ZoneOffset.UTC)
|
||||||
|
.toEpochSecond()
|
||||||
|
|
||||||
// Get subject value.
|
// TODO: Get subject value.
|
||||||
val sub = authenticationCertificate[0] // TODO:
|
val sub = "FAMILYNAME.NAME"
|
||||||
|
|
||||||
// Get header and claims.
|
// Get header and claims.
|
||||||
val header = """{"typ":"$type","alg":"$algorithm","x5c":"$base64cert"}"""
|
val header = """{"typ":"$type","alg":"$algorithm","x5c":["$base64cert"]}"""
|
||||||
val claims = """{"iat":"$epoch","exp":"$exp","aud":"$originUrl","iss":"$iss","sub":"$sub","nonce":"$challenge","cnf":{"tbh":""}}"""
|
val claims =
|
||||||
|
"""{"iat":"$epoch","exp":"$exp","aud":"$originUrl","iss":"$iss","sub":"$sub","nonce":"$challenge","cnf":{"tbh":""}}"""
|
||||||
|
|
||||||
var jwt = String(Base64.encode(header.toByteArray(Charsets.UTF_8))) + "." + String(Base64.encode(claims.toByteArray(Charsets.UTF_8)))
|
val jwt = base64Encode(header.toByteArray(Charsets.UTF_8)) + "." + base64Encode(
|
||||||
jwt = jwt.replace("=", "")
|
claims.toByteArray(Charsets.UTF_8)
|
||||||
|
)
|
||||||
Log.v("JWT", jwt)
|
|
||||||
|
|
||||||
// Send the authentication token hash to the ID card for signing and get signed authentication token as response.
|
// Send the authentication token hash to the ID card for signing and get signed authentication token as response.
|
||||||
val encoded = MessageDigest.getInstance("SHA-384").digest(jwt.toByteArray())
|
val encoded =
|
||||||
|
MessageDigest.getInstance("SHA-384").digest(jwt.toByteArray(StandardCharsets.UTF_8))
|
||||||
val signed = comms.authenticate(pin1, encoded)
|
val signed = comms.authenticate(pin1, encoded)
|
||||||
|
|
||||||
val jws = jwt + "." + String(Base64.encode(signed))
|
|
||||||
|
|
||||||
Log.v("Token", jws)
|
|
||||||
|
|
||||||
// Return the signed authentication token.
|
// Return the signed authentication token.
|
||||||
return jws
|
return jwt + "." + base64Encode(signed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun base64Encode(bytes: ByteArray) : String? {
|
||||||
|
val encoded = java.util.Base64.getUrlEncoder().encodeToString(bytes)
|
||||||
|
return encoded.replace("=", "")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -45,7 +45,11 @@ function createParametrizedIntentUrl(challenge, action) {
|
|||||||
if (action == null) {
|
if (action == null) {
|
||||||
console.error("There has to be an action for intent.")
|
console.error("There has to be an action for intent.")
|
||||||
}
|
}
|
||||||
return intentUrl + "?" + "action=" + action + (challenge != null ? "&challenge=" + challenge : "");
|
else if (challenge == null) {
|
||||||
|
console.error("Challenge missing, can't authenticate without it.")
|
||||||
|
} else {
|
||||||
|
return intentUrl + "?" + "action=" + action + "&challenge=" + challenge + "&authUrl=" + originUrl + authenticationRequestUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAndroid() {
|
function isAndroid() {
|
||||||
|
Loading…
Reference in New Issue
Block a user