mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2025-12-04 04:45:12 +02:00
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:
@@ -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'
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user