MOB-42 Added backend server, two frontend webpages and rest endpoints for getting challenge, submitting authentication token and getting authentication object. MOB-21 Added JWT creation, but whole process still needs some work.

This commit is contained in:
TanelOrumaa
2021-11-08 17:30:56 +02:00
parent 44469b8533
commit f9cd30922e
32 changed files with 1492 additions and 5 deletions

View File

@@ -62,4 +62,9 @@ dependencies {
//SecureDataStoring
implementation("androidx.security:security-crypto:1.0.0")
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2',
'org.bouncycastle:bcprov-jdk15on:1.60',
'io.jsonwebtoken:jjwt-gson:0.11.2'
}

View File

@@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
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

View File

@@ -419,6 +419,7 @@ public class Comms {
InternalAuthenticate[4] = (byte) (0x1d + 16 * (token.length / 16));
InternalAuthenticate[6] = (byte) (0x11 + 16 * (token.length / 16));
response = getResponse(token, InternalAuthenticate, "Internal Authenticate");
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
throw new RuntimeException("Signing the token failed.");
}
@@ -429,6 +430,7 @@ public class Comms {
return Arrays.copyOf(signature, indexOfTerminator);
}
private byte[] getResponse(byte[] data, byte[] command, String log) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
byte[] response = idCard.transceive(createSecureAPDU(data, command));
Log.i(log, Hex.toHexString(response));

View File

@@ -1,22 +1,61 @@
package com.tarkvaraprojekt.mobileauthapp.auth
import android.nfc.tech.IsoDep
import android.os.Message
import android.util.Log
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
import java.math.BigInteger
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) {
public fun authenticate(nonce: BigInteger, challengeUrl: String, pin1: String) {
val type = "JWT"
val algorithm = "ES384"
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 {
// Ask PIN 1 from the user and get the authentication certificate from the ID card.
val authenticationCertificate : ByteArray = comms.getCertificate(true);
// Create the authentication token (OpenID X509)
// Encode the certificate in base64.
val base64cert = String(Base64.encode(authenticationCertificate))
// Hash the authentication token.
// 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()
// Get subject value.
val sub = authenticationCertificate[0] // TODO:
// 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":""}}"""
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)
// 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 signed = comms.authenticate(pin1, encoded)
val jws = jwt + "." + String(Base64.encode(signed))
Log.v("Token", jws)
// Return the signed authentication token.
return jws
}
}