1
0

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 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,66 +69,51 @@ 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 { binding!!.timeCounter.text = getString(R.string.card_detected)
binding!!.timeCounter.text = getString(R.string.card_detected) }
} val card = IsoDep.get(tag)
val card = IsoDep.get(tag) card.timeout = 32768
card.timeout = 32768 card.use {
card.use { try {
try { val comms = Comms(it, viewModel.userCan)
val comms = Comms(it, viewModel.userCan) val response = comms.readPersonalData(byteArrayOf(1, 2, 6, 3, 4, 8))
val response = comms.readPersonalData(byteArrayOf(1, 2, 6, 3, 4, 8)) viewModel.setUserFirstName(response[1])
viewModel.setUserFirstName(response[1]) viewModel.setUserLastName(response[0])
viewModel.setUserLastName(response[0]) viewModel.setUserIdentificationNumber(response[2])
viewModel.setUserIdentificationNumber(response[2]) 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) {
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)
} }
} 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. }, NfcAdapter.FLAG_READER_NFC_A, null)
goToNextFragment()
}
} }
private fun goToNextFragment() { private fun goToNextFragment() {
timer.cancel() timer.cancel()
if (args.auth) { findNavController().navigate(R.id.action_authFragment_to_userFragment)
val action = AuthFragmentDirections.actionAuthFragmentToResultFragment(mobile = args.mobile)
findNavController().navigate(action)
} else {
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() {

View File

@ -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;

View File

@ -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("=", "")
}
} }

View File

@ -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() {