Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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 @@ -100,8 +100,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 @@ -110,13 +110,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 @@ -126,8 +126,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 @@ -216,7 +216,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 @@ -262,7 +262,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