From 82b153886728cc2bb59865f24d69d604929350af Mon Sep 17 00:00:00 2001 From: TanelOrumaa Date: Mon, 8 Nov 2021 22:41:09 +0200 Subject: [PATCH] MOB-42 Fixed JWT generation issue. --- .../mobileauthapp/AuthFragment.kt | 91 ++++++++----------- .../mobileauthapp/NFC/Comms.java | 1 + .../mobileauthapp/auth/Authenticator.kt | 42 ++++----- .../src/main/resources/static/js/main.js | 6 +- 4 files changed, 63 insertions(+), 77 deletions(-) diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt index 91f0811..6cf616e 100644 --- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt +++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt @@ -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() { diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java index 76e1c3a..bccb6ee 100644 --- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java +++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java @@ -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; diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/auth/Authenticator.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/auth/Authenticator.kt index 0d7c862..383ad68 100644 --- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/auth/Authenticator.kt +++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/auth/Authenticator.kt @@ -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("=", "") + } } \ No newline at end of file diff --git a/demoBackend/src/main/resources/static/js/main.js b/demoBackend/src/main/resources/static/js/main.js index 4e2f7f0..f38d255 100644 --- a/demoBackend/src/main/resources/static/js/main.js +++ b/demoBackend/src/main/resources/static/js/main.js @@ -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() {