MOB-42 Fixed JWT generation issue.

This commit is contained in:
TanelOrumaa 2021-11-08 22:41:09 +02:00
parent c46c3082b7
commit 82b1538867
4 changed files with 63 additions and 77 deletions

View File

@ -1,6 +1,7 @@
package com.tarkvaraprojekt.mobileauthapp
import android.content.Intent
import android.app.Activity
import android.content.Context
import android.nfc.NfcAdapter
import android.nfc.tech.IsoDep
import android.os.Bundle
@ -9,16 +10,15 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
import com.tarkvaraprojekt.mobileauthapp.auth.Authenticator
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentAuthBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
import java.lang.Exception
import kotlin.concurrent.thread
/**
* 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 val args: CanFragmentArgs by navArgs()
private lateinit var timer: CountDownTimer
private var timeRemaining: Int = 90
@ -71,66 +69,51 @@ class AuthFragment : Fragment() {
}
private fun getInfoFromIdCard(adapter: NfcAdapter) {
if (args.reading) {
adapter.enableReaderMode(activity, { tag ->
timer.cancel()
requireActivity().runOnUiThread {
binding!!.timeCounter.text = getString(R.string.card_detected)
}
val card = IsoDep.get(tag)
card.timeout = 32768
card.use {
try {
val comms = Comms(it, viewModel.userCan)
val response = comms.readPersonalData(byteArrayOf(1, 2, 6, 3, 4, 8))
viewModel.setUserFirstName(response[1])
viewModel.setUserLastName(response[0])
viewModel.setUserIdentificationNumber(response[2])
viewModel.setGender(response[3])
viewModel.setCitizenship(response[4])
viewModel.setExpiration(response[5])
requireActivity().runOnUiThread {
binding!!.timeCounter.text = getString(R.string.data_read)
}
} catch (e: Exception) {
requireActivity().runOnUiThread {
binding!!.timeCounter.text = getString(R.string.no_success)
}
// If the CAN is wrong we will also delete the saved CAN so that the user won't use it again.
viewModel.deleteCan(requireContext())
// Gives user some time to read the error message
Thread.sleep(1000)
goToTheStart()
} finally {
adapter.disableReaderMode(activity)
adapter.enableReaderMode(activity, { tag ->
timer.cancel()
requireActivity().runOnUiThread {
binding!!.timeCounter.text = getString(R.string.card_detected)
}
val card = IsoDep.get(tag)
card.timeout = 32768
card.use {
try {
val comms = Comms(it, viewModel.userCan)
val response = comms.readPersonalData(byteArrayOf(1, 2, 6, 3, 4, 8))
viewModel.setUserFirstName(response[1])
viewModel.setUserLastName(response[0])
viewModel.setUserIdentificationNumber(response[2])
viewModel.setGender(response[3])
viewModel.setCitizenship(response[4])
viewModel.setExpiration(response[5])
requireActivity().runOnUiThread{
binding!!.timeCounter.text = getString(R.string.data_read)
}
} catch (e: Exception) {
requireActivity().runOnUiThread {
binding!!.timeCounter.text = getString(R.string.no_success)
}
// If the CAN is wrong we will also delete the saved CAN so that the user won't use it again.
viewModel.deleteCan(requireContext())
// Gives user some time to read the error message
Thread.sleep(1000)
goToTheStart()
} finally {
adapter.disableReaderMode(activity)
}
}, NfcAdapter.FLAG_READER_NFC_A, null)
} else { //We want to create a JWT instead of reading the info from the card.
goToNextFragment()
}
}
}, NfcAdapter.FLAG_READER_NFC_A, null)
}
private fun goToNextFragment() {
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() {
viewModel.clearUserInfo()
timer.cancel()
if (args.reading) {
findNavController().navigate(R.id.action_authFragment_to_homeFragment)
} else {
val resultIntent = Intent()
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
requireActivity().finish()
}
findNavController().navigate(R.id.action_authFragment_to_homeFragment)
}
override fun onDestroy() {

View File

@ -21,6 +21,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;

View File

@ -1,16 +1,12 @@
package com.tarkvaraprojekt.mobileauthapp.auth
import android.os.Message
import android.util.Log
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
import io.jsonwebtoken.SignatureAlgorithm
import org.bouncycastle.util.encoders.Base64
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.time.LocalDateTime
import java.time.ZoneOffset
import javax.crypto.Mac
import kotlin.experimental.and
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.
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.
val authenticationCertificate : ByteArray = comms.getCertificate(true);
val authenticationCertificate: ByteArray = comms.getCertificate(true);
// Encode the certificate in base64.
val base64cert = String(Base64.encode(authenticationCertificate))
val base64cert = java.util.Base64.getEncoder().encodeToString(authenticationCertificate)
// Get current epoch time.
val epoch = LocalDateTime.now(ZoneOffset.UTC).atZone(ZoneOffset.UTC).toEpochSecond()
// 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.
val sub = authenticationCertificate[0] // TODO:
// TODO: Get subject value.
val sub = "FAMILYNAME.NAME"
// Get header and claims.
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 header = """{"typ":"$type","alg":"$algorithm","x5c":["$base64cert"]}"""
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)))
jwt = jwt.replace("=", "")
Log.v("JWT", jwt)
val jwt = base64Encode(header.toByteArray(Charsets.UTF_8)) + "." + base64Encode(
claims.toByteArray(Charsets.UTF_8)
)
// 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 jws = jwt + "." + String(Base64.encode(signed))
Log.v("Token", jws)
// 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("=", "")
}
}

View File

@ -45,7 +45,11 @@ function createParametrizedIntentUrl(challenge, action) {
if (action == null) {
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() {