Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions codemagic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ workflows:
echo "Using repo.ria configuration"
echo $REPO_RIA_DEFAULT_PROPERTIES | base64 --decode > $CONFIG_DIR/configuration.properties
echo $REPO_RIA_CONFIG | base64 --decode > $CONFIG_DIR/default-config.json
echo $REPO_RIA_PUB_KEY | base64 --decode > $CONFIG_DIR/default-config.pub
echo $REPO_RIA_RSA | base64 --decode > $CONFIG_DIR/default-config.rsa
echo $REPO_RIA_PUB_KEY | base64 --decode > $CONFIG_DIR/default-config.ecpub
echo $REPO_RIA_RSA | base64 --decode > $CONFIG_DIR/default-config.ecc
echo $REPO_RIA_TRUSTED_TEST_MP | base64 --decode > $TSL_FILES_DIR/trusted-test-mp.xml
echo $REPO_RIA_TL_MP_TEST_EE | base64 --decode > $TSL_FILES_DIR/tl-mp-test-EE.xml
# EE_T.xml is too large to use in environmental variable. Using compressed variant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ class ConfigurationCacheTest {

assertEquals(3, File(context.cacheDir, CACHE_CONFIG_FOLDER).listFiles()?.size ?: 0)
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.json").exists())
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.pub").exists())
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.rsa").exists())
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.ecpub").exists())
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.ecc").exists())
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ import ee.ria.DigiDoc.configuration.properties.ConfigurationProperties
import ee.ria.DigiDoc.configuration.properties.ConfigurationPropertiesImpl
import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider
import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepository
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECC
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECPUB
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_JSON
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_PUB
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_RSA
import ee.ria.DigiDoc.configuration.utils.Constant.CACHE_CONFIG_FOLDER
import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_LAST_UPDATE_CHECK_DATE_PROPERTY_NAME
import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_PREFERENCES
Expand Down Expand Up @@ -111,13 +111,13 @@ class ConfigurationLoaderTest {

propertiesFile = AssetFile.getAssetFileAsFile(context, "config/configuration.properties")
confFile = AssetFile.getAssetFileAsFile(context, "config/default-config.json")
publicKeyFile = AssetFile.getAssetFileAsFile(context, "config/default-config.pub")
signatureFile = AssetFile.getAssetFileAsFile(context, "config/default-config.rsa")
publicKeyFile = AssetFile.getAssetFileAsFile(context, "config/default-config.ecpub")
signatureFile = AssetFile.getAssetFileAsFile(context, "config/default-config.ecc")

File(context.cacheDir, CACHE_CONFIG_FOLDER).mkdirs()
Files.copy(confFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_JSON))
Files.copy(publicKeyFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_PUB))
Files.copy(signatureFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_RSA))
Files.copy(publicKeyFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_ECPUB))
Files.copy(signatureFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_ECC))

proxySetting = ProxySetting.NO_PROXY
manualProxy = ManualProxy("", 80, "", "")
Expand All @@ -127,8 +127,8 @@ class ConfigurationLoaderTest {
fun tearDown() {
File(context.cacheDir, PROPERTIES_FILE_NAME).delete()
File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_JSON).delete()
File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_PUB).delete()
File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_RSA).delete()
File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_ECPUB).delete()
File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_ECC).delete()
File(context.cacheDir, CACHE_CONFIG_FOLDER).delete()
}

Expand Down Expand Up @@ -217,7 +217,7 @@ class ConfigurationLoaderTest {
// Write random text to file, to make sure that current signature and new signature are different
val configFolder = File(context.cacheDir, CACHE_CONFIG_FOLDER)
configFolder.mkdirs()
val signatureFile = File(configFolder, CACHED_CONFIG_RSA)
val signatureFile = File(configFolder, CACHED_CONFIG_ECC)

FileWriter(signatureFile).use { writer ->
writer.write("dGVzdDI=")
Expand Down Expand Up @@ -263,7 +263,7 @@ class ConfigurationLoaderTest {
// Write same signature to file, to make sure that current signature and new signature match
val configFolder = File(context.cacheDir, CACHE_CONFIG_FOLDER)
configFolder.mkdirs()
val signatureFile = File(configFolder, CACHED_CONFIG_RSA)
val signatureFile = File(configFolder, CACHED_CONFIG_ECC)
FileWriter(signatureFile).use { writer ->
writer.write(String(Base64.getDecoder().decode(centralSignature)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
package ee.ria.DigiDoc.configuration.cache

import android.content.Context
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECC
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECPUB
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_JSON
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_PUB
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_RSA
import ee.ria.DigiDoc.configuration.utils.Constant.CACHE_CONFIG_FOLDER
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil
import java.io.File
Expand All @@ -43,8 +43,8 @@ object ConfigurationCache {
signature: ByteArray,
) {
cacheFile(context, CACHED_CONFIG_JSON, confData)
cacheFile(context, CACHED_CONFIG_PUB, publicKey)
cacheFile(context, CACHED_CONFIG_RSA, signature)
cacheFile(context, CACHED_CONFIG_ECPUB, publicKey)
cacheFile(context, CACHED_CONFIG_ECC, signature)
}

private fun cacheFile(
Expand All @@ -71,6 +71,7 @@ object ConfigurationCache {
}
}

@Suppress("SameParameterValue")
private fun cacheFile(
context: Context,
fileName: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ import ee.ria.DigiDoc.configuration.properties.ConfigurationProperties
import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider
import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepository
import ee.ria.DigiDoc.configuration.utils.ConfigurationUtil
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECC
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECPUB
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_JSON
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_PUB
import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_RSA
import ee.ria.DigiDoc.configuration.utils.Constant.CACHE_CONFIG_FOLDER
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_ECC
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_ECPUB
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_JSON
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_PUB
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_RSA
import ee.ria.DigiDoc.network.proxy.ManualProxy
import ee.ria.DigiDoc.network.proxy.ProxySetting
import ee.ria.DigiDoc.utilsLib.date.DateUtil
Expand Down Expand Up @@ -106,8 +106,8 @@ class ConfigurationLoaderImpl
val cacheDir = File(context.cacheDir, CACHE_CONFIG_FOLDER)

val confFile = File(cacheDir, CACHED_CONFIG_JSON)
val publicKeyFile = File(cacheDir, CACHED_CONFIG_PUB)
val signatureFile = File(cacheDir, CACHED_CONFIG_RSA)
val publicKeyFile = File(cacheDir, CACHED_CONFIG_ECPUB)
val signatureFile = File(cacheDir, CACHED_CONFIG_ECC)

if (confFile.exists() && publicKeyFile.exists() && signatureFile.exists()) {
val signatureBytes = signatureFile.readBytes()
Expand Down Expand Up @@ -159,8 +159,8 @@ class ConfigurationLoaderImpl
val assets = context.assets

val confData = assets.open("config/${DEFAULT_CONFIG_JSON}").bufferedReader().use { it.readText() }
val publicKey = assets.open("config/${DEFAULT_CONFIG_PUB}").bufferedReader().use { it.readText() }
val signatureBytes = assets.open("config/${DEFAULT_CONFIG_RSA}").readBytes()
val publicKey = assets.open("config/${DEFAULT_CONFIG_ECPUB}").bufferedReader().use { it.readText() }
val signatureBytes = assets.open("config/${DEFAULT_CONFIG_ECC}").readBytes()

val signatureText = String(signatureBytes, Charsets.UTF_8)

Expand Down Expand Up @@ -219,7 +219,7 @@ class ConfigurationLoaderImpl
proxy: ManualProxy,
) {
val cachedSignature =
ConfigurationCache.getCachedFile(context, CACHED_CONFIG_RSA)
ConfigurationCache.getCachedFile(context, CACHED_CONFIG_ECC)

val currentSignature = cachedSignature.readBytes()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ interface CentralConfigurationRepository {
suspend fun fetchConfiguration(): String

@Throws(Exception::class)
@GET("config.pub")
@GET("config.ecpub")
suspend fun fetchPublicKey(): String

@Throws(Exception::class)
@GET("config.rsa")
@GET("config.ecc")
suspend fun fetchSignature(): String

suspend fun setupProxy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_DOWNLOAD_DATE_P
import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_UPDATE_INTERVAL_PROPERTY
import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_VERSION_SERIAL_PROPERTY
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIGURATION_PROPERTIES_FILE_NAME
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_ECC
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_ECPUB
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_JSON
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_PUB
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_RSA
import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_UPDATE_INTERVAL
import ee.ria.DigiDoc.configuration.utils.Constant.PROPERTIES_FILE_NAME
import ee.ria.DigiDoc.configuration.utils.Parser
Expand Down Expand Up @@ -179,8 +179,8 @@ object FetchAndPackageDefaultConfigurationTask {

private fun storeAsDefaultConfiguration(confData: ConfigurationData) {
confData.configurationJson?.let { storeFile(DEFAULT_CONFIG_JSON, it) }
confData.configurationSignature?.let { storeFile(DEFAULT_CONFIG_RSA, it) }
confData.configurationSignaturePublicKey?.let { storeFile(DEFAULT_CONFIG_PUB, it) }
confData.configurationSignature?.let { storeFile(DEFAULT_CONFIG_ECC, it) }
confData.configurationSignaturePublicKey?.let { storeFile(DEFAULT_CONFIG_ECPUB, it) }
}

private fun storeApplicationProperties(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
package ee.ria.DigiDoc.configuration.utils

object Constant {
const val FORCE_LOAD_CENTRAL_CONFIGURATION =
"ee.ria.digidoc.configuration.FORCE_LOAD_CENTRAL_CONFIGURATION"
const val LAST_CONFIGURATION_UPDATE = "ee.ria.digidoc.configuration.LAST_CONFIGURATION_UPDATE"

const val CENTRAL_CONFIGURATION_SERVICE_URL_PROPERTY = "central-configuration-service.url"
const val CONFIGURATION_UPDATE_INTERVAL_PROPERTY = "configuration.update-interval"
const val CONFIGURATION_VERSION_SERIAL_PROPERTY = "configuration.version-serial"
Expand All @@ -38,16 +34,15 @@ object Constant {
"default-configuration.properties"

const val DEFAULT_CONFIG_JSON = "default-config.json"
const val DEFAULT_CONFIG_RSA = "default-config.rsa"
const val DEFAULT_CONFIG_PUB = "default-config.pub"
const val DEFAULT_CONFIG_ECC = "default-config.ecc"
const val DEFAULT_CONFIG_ECPUB = "default-config.ecpub"

const val CACHED_CONFIG_JSON = "active-config.json"
const val CACHED_CONFIG_RSA = "active-config.rsa"
const val CACHED_CONFIG_PUB = "active-config.pub"
const val CACHED_CONFIG_ECC = "active-config.ecc"
const val CACHED_CONFIG_ECPUB = "active-config.ecpub"

const val CONFIGURATION_PREFERENCES = "ConfigurationPreferences"
const val CACHE_CONFIG_FOLDER = "/config/"
const val CONFIGURATION_INFO_FILE_NAME = "configuration-info.properties"
const val CONFIGURATION_LAST_UPDATE_CHECK_DATE_PROPERTY_NAME =
"configuration.last-update-check-date"
const val CONFIGURATION_UPDATE_DATE_PROPERTY_NAME = "configuration.update-date"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ package ee.ria.DigiDoc.configuration.utils

import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.crypto.params.RSAKeyParameters
import org.bouncycastle.crypto.util.PublicKeyFactory
import org.bouncycastle.openssl.PEMParser
import java.io.IOException
import java.io.StringReader
Expand All @@ -35,8 +33,9 @@ import java.security.NoSuchAlgorithmException
import java.security.PublicKey
import java.security.Signature
import java.security.SignatureException
import java.security.interfaces.ECPublicKey
import java.security.spec.InvalidKeySpecException
import java.security.spec.RSAPublicKeySpec
import java.security.spec.X509EncodedKeySpec

object SignatureVerifier {
private val LOG_TAG = javaClass.simpleName
Expand All @@ -46,45 +45,48 @@ object SignatureVerifier {
publicKeyPEM: String,
signedContent: String,
): Boolean {
val publicKeyInfo: SubjectPublicKeyInfo =
parsePublicKeyInfo(publicKeyPEM)
val publicKeyInfo = parsePublicKeyInfo(publicKeyPEM)
val publicKey = convertPublicKeyInfoToPublicKey(publicKeyInfo)
return verifySignature(signature, publicKey, signedContent)
}

private fun convertPublicKeyInfoToPublicKey(publicKeyInfo: SubjectPublicKeyInfo): PublicKey =
try {
val keyParams: RSAKeyParameters =
PublicKeyFactory.createKey(publicKeyInfo) as RSAKeyParameters
val publicKeySpec = RSAPublicKeySpec(keyParams.modulus, keyParams.exponent)
val keyFactory = KeyFactory.getInstance("RSA")
keyFactory.generatePublic(publicKeySpec)
val keySpec = X509EncodedKeySpec(publicKeyInfo.encoded)
val keyFactory = KeyFactory.getInstance("EC")
keyFactory.generatePublic(keySpec)
} catch (e: InvalidKeySpecException) {
errorLog(LOG_TAG, "PublicKey conversion failed", e)
throw IllegalStateException(
"Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey",
"Failed to convert SubjectPublicKeyInfo to EC java.security.PublicKey",
e,
)
} catch (e: NoSuchAlgorithmException) {
errorLog(LOG_TAG, "PublicKey conversion failed", e)
throw IllegalStateException(
"Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey",
"Failed to convert SubjectPublicKeyInfo to EC java.security.PublicKey",
e,
)
} catch (e: IOException) {
errorLog(LOG_TAG, "PublicKey conversion failed", e)
throw IllegalStateException(
"Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey",
"Failed to convert SubjectPublicKeyInfo to EC java.security.PublicKey",
e,
)
}

private fun parsePublicKeyInfo(PKCS1PublicKeyPEM: String): SubjectPublicKeyInfo {
private fun parsePublicKeyInfo(publicKeyPem: String): SubjectPublicKeyInfo {
try {
PEMParser(StringReader(PKCS1PublicKeyPEM))
.use { pemParser -> return pemParser.readObject() as SubjectPublicKeyInfo }
PEMParser(StringReader(publicKeyPem)).use { pemParser ->
return pemParser.readObject() as SubjectPublicKeyInfo
}
} catch (e: IOException) {
throw IllegalStateException("Failed to parse PEM encoded PKCS#1 public key", e)
throw IllegalStateException("Failed to parse PEM encoded public key", e)
} catch (e: ClassCastException) {
throw IllegalStateException(
"PEM did not contain SubjectPublicKeyInfo. Make sure it's 'BEGIN PUBLIC KEY' (SPKI).",
e,
)
}
}

Expand All @@ -94,10 +96,13 @@ object SignatureVerifier {
signedContent: String,
): Boolean =
try {
val signature = Signature.getInstance("SHA512withRSA")
val jcaAlg = pickEcdsaAlgorithm(publicKey)
val signature = Signature.getInstance(jcaAlg)
signature.initVerify(publicKey)
signature.update(signedContent.toByteArray(StandardCharsets.UTF_8))
signature.verify(signatureBytes)
val result = signature.verify(signatureBytes)

result
} catch (e: NoSuchAlgorithmException) {
errorLog(LOG_TAG, "Signature verification failed", e)
throw IllegalStateException("Failed to verify signature", e)
Expand All @@ -107,5 +112,22 @@ object SignatureVerifier {
} catch (e: InvalidKeyException) {
errorLog(LOG_TAG, "Signature verification failed", e)
throw IllegalStateException("Failed to verify signature", e)
} catch (e: java.lang.Exception) {
errorLog(LOG_TAG, "Signature verification failed", e)
throw IllegalStateException("Failed to verify signature", e)
}

private fun pickEcdsaAlgorithm(publicKey: PublicKey): String {
val ecKey =
publicKey as? ECPublicKey
?: throw IllegalStateException("Public key is not EC (ECDSA). Got: ${publicKey.algorithm}")

val fieldSizeBits = ecKey.params.curve.field.fieldSize

return when {
fieldSizeBits <= 256 -> "SHA256withECDSA"
fieldSizeBits <= 384 -> "SHA384withECDSA"
else -> "SHA512withECDSA"
}
}
}
Loading
Loading