Skip to content

Commit 59b37c4

Browse files
committed
MOPPAND-1514 Use EC signature for central configuration.
1 parent 5cc9c74 commit 59b37c4

13 files changed

Lines changed: 170 additions & 116 deletions

File tree

codemagic.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ workflows:
100100
echo "Using repo.ria configuration"
101101
echo $REPO_RIA_DEFAULT_PROPERTIES | base64 --decode > $CONFIG_DIR/configuration.properties
102102
echo $REPO_RIA_CONFIG | base64 --decode > $CONFIG_DIR/default-config.json
103-
echo $REPO_RIA_PUB_KEY | base64 --decode > $CONFIG_DIR/default-config.pub
104-
echo $REPO_RIA_RSA | base64 --decode > $CONFIG_DIR/default-config.rsa
103+
echo $REPO_RIA_PUB_KEY | base64 --decode > $CONFIG_DIR/default-config.ecpub
104+
echo $REPO_RIA_RSA | base64 --decode > $CONFIG_DIR/default-config.ecc
105105
echo $REPO_RIA_TRUSTED_TEST_MP | base64 --decode > $TSL_FILES_DIR/trusted-test-mp.xml
106106
echo $REPO_RIA_TL_MP_TEST_EE | base64 --decode > $TSL_FILES_DIR/tl-mp-test-EE.xml
107107
# EE_T.xml is too large to use in environmental variable. Using compressed variant

config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCacheTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ class ConfigurationCacheTest {
6262

6363
assertEquals(3, File(context.cacheDir, CACHE_CONFIG_FOLDER).listFiles()?.size ?: 0)
6464
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.json").exists())
65-
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.pub").exists())
66-
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.rsa").exists())
65+
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.ecpub").exists())
66+
assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.ecc").exists())
6767
}
6868

6969
@Test

config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ class ConfigurationLoaderTest {
110110

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

116116
File(context.cacheDir, CACHE_CONFIG_FOLDER).mkdirs()
117117
Files.copy(confFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_JSON))

config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepository.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ interface CentralConfigurationRepository {
3131
suspend fun fetchConfiguration(): String
3232

3333
@Throws(Exception::class)
34-
@GET("config.pub")
34+
@GET("config.ecpub")
3535
suspend fun fetchPublicKey(): String
3636

3737
@Throws(Exception::class)
38-
@GET("config.rsa")
38+
@GET("config.ecc")
3939
suspend fun fetchSignature(): String
4040

4141
suspend fun setupProxy(

config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/Constant.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ object Constant {
3838
"default-configuration.properties"
3939

4040
const val DEFAULT_CONFIG_JSON = "default-config.json"
41-
const val DEFAULT_CONFIG_RSA = "default-config.rsa"
42-
const val DEFAULT_CONFIG_PUB = "default-config.pub"
41+
const val DEFAULT_CONFIG_RSA = "default-config.ecc"
42+
const val DEFAULT_CONFIG_PUB = "default-config.ecpub"
4343

4444
const val CACHED_CONFIG_JSON = "active-config.json"
45-
const val CACHED_CONFIG_RSA = "active-config.rsa"
46-
const val CACHED_CONFIG_PUB = "active-config.pub"
45+
const val CACHED_CONFIG_RSA = "active-config.ecc"
46+
const val CACHED_CONFIG_PUB = "active-config.ecpub"
4747

4848
const val CONFIGURATION_PREFERENCES = "ConfigurationPreferences"
4949
const val CACHE_CONFIG_FOLDER = "/config/"

config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifier.kt

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ package ee.ria.DigiDoc.configuration.utils
2323

2424
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog
2525
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
26-
import org.bouncycastle.crypto.params.RSAKeyParameters
27-
import org.bouncycastle.crypto.util.PublicKeyFactory
2826
import org.bouncycastle.openssl.PEMParser
2927
import java.io.IOException
3028
import java.io.StringReader
@@ -35,8 +33,9 @@ import java.security.NoSuchAlgorithmException
3533
import java.security.PublicKey
3634
import java.security.Signature
3735
import java.security.SignatureException
36+
import java.security.interfaces.ECPublicKey
3837
import java.security.spec.InvalidKeySpecException
39-
import java.security.spec.RSAPublicKeySpec
38+
import java.security.spec.X509EncodedKeySpec
4039

4140
object SignatureVerifier {
4241
private val LOG_TAG = javaClass.simpleName
@@ -46,45 +45,48 @@ object SignatureVerifier {
4645
publicKeyPEM: String,
4746
signedContent: String,
4847
): Boolean {
49-
val publicKeyInfo: SubjectPublicKeyInfo =
50-
parsePublicKeyInfo(publicKeyPEM)
48+
val publicKeyInfo = parsePublicKeyInfo(publicKeyPEM)
5149
val publicKey = convertPublicKeyInfoToPublicKey(publicKeyInfo)
5250
return verifySignature(signature, publicKey, signedContent)
5351
}
5452

5553
private fun convertPublicKeyInfoToPublicKey(publicKeyInfo: SubjectPublicKeyInfo): PublicKey =
5654
try {
57-
val keyParams: RSAKeyParameters =
58-
PublicKeyFactory.createKey(publicKeyInfo) as RSAKeyParameters
59-
val publicKeySpec = RSAPublicKeySpec(keyParams.modulus, keyParams.exponent)
60-
val keyFactory = KeyFactory.getInstance("RSA")
61-
keyFactory.generatePublic(publicKeySpec)
55+
val keySpec = X509EncodedKeySpec(publicKeyInfo.encoded)
56+
val keyFactory = KeyFactory.getInstance("EC")
57+
keyFactory.generatePublic(keySpec)
6258
} catch (e: InvalidKeySpecException) {
6359
errorLog(LOG_TAG, "PublicKey conversion failed", e)
6460
throw IllegalStateException(
65-
"Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey",
61+
"Failed to convert SubjectPublicKeyInfo to EC java.security.PublicKey",
6662
e,
6763
)
6864
} catch (e: NoSuchAlgorithmException) {
6965
errorLog(LOG_TAG, "PublicKey conversion failed", e)
7066
throw IllegalStateException(
71-
"Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey",
67+
"Failed to convert SubjectPublicKeyInfo to EC java.security.PublicKey",
7268
e,
7369
)
7470
} catch (e: IOException) {
7571
errorLog(LOG_TAG, "PublicKey conversion failed", e)
7672
throw IllegalStateException(
77-
"Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey",
73+
"Failed to convert SubjectPublicKeyInfo to EC java.security.PublicKey",
7874
e,
7975
)
8076
}
8177

82-
private fun parsePublicKeyInfo(PKCS1PublicKeyPEM: String): SubjectPublicKeyInfo {
78+
private fun parsePublicKeyInfo(publicKeyPem: String): SubjectPublicKeyInfo {
8379
try {
84-
PEMParser(StringReader(PKCS1PublicKeyPEM))
85-
.use { pemParser -> return pemParser.readObject() as SubjectPublicKeyInfo }
80+
PEMParser(StringReader(publicKeyPem)).use { pemParser ->
81+
return pemParser.readObject() as SubjectPublicKeyInfo
82+
}
8683
} catch (e: IOException) {
87-
throw IllegalStateException("Failed to parse PEM encoded PKCS#1 public key", e)
84+
throw IllegalStateException("Failed to parse PEM encoded public key", e)
85+
} catch (e: ClassCastException) {
86+
throw IllegalStateException(
87+
"PEM did not contain SubjectPublicKeyInfo. Make sure it's 'BEGIN PUBLIC KEY' (SPKI).",
88+
e,
89+
)
8890
}
8991
}
9092

@@ -94,10 +96,13 @@ object SignatureVerifier {
9496
signedContent: String,
9597
): Boolean =
9698
try {
97-
val signature = Signature.getInstance("SHA512withRSA")
99+
val jcaAlg = pickEcdsaAlgorithm(publicKey)
100+
val signature = Signature.getInstance(jcaAlg)
98101
signature.initVerify(publicKey)
99102
signature.update(signedContent.toByteArray(StandardCharsets.UTF_8))
100-
signature.verify(signatureBytes)
103+
val result = signature.verify(signatureBytes)
104+
105+
result
101106
} catch (e: NoSuchAlgorithmException) {
102107
errorLog(LOG_TAG, "Signature verification failed", e)
103108
throw IllegalStateException("Failed to verify signature", e)
@@ -107,5 +112,22 @@ object SignatureVerifier {
107112
} catch (e: InvalidKeyException) {
108113
errorLog(LOG_TAG, "Signature verification failed", e)
109114
throw IllegalStateException("Failed to verify signature", e)
115+
} catch (e: java.lang.Exception) {
116+
errorLog(LOG_TAG, "Signature verification failed", e)
117+
throw IllegalStateException("Failed to verify signature", e)
110118
}
119+
120+
private fun pickEcdsaAlgorithm(publicKey: PublicKey): String {
121+
val ecKey =
122+
publicKey as? ECPublicKey
123+
?: throw IllegalStateException("Public key is not EC (ECDSA). Got: ${publicKey.algorithm}")
124+
125+
val fieldSizeBits = ecKey.params.curve.field.fieldSize
126+
127+
return when {
128+
fieldSizeBits <= 256 -> "SHA256withECDSA"
129+
fieldSizeBits <= 384 -> "SHA384withECDSA"
130+
else -> "SHA512withECDSA"
131+
}
132+
}
111133
}

config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifierTest.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ class SignatureVerifierTest {
4040
}
4141
try {
4242
classLoader.getResourceAsStream("config.json").use { configJsonStream ->
43-
classLoader.getResourceAsStream("config.rsa").use { configSignatureStream ->
43+
classLoader.getResourceAsStream("config.ecc").use { configSignatureStream ->
4444
classLoader
45-
.getResourceAsStream("config.pub")
45+
.getResourceAsStream("config.ecpub")
4646
.use { configSignaturePublicKeyStream ->
47-
val configJson: String =
48-
FileUtil.readFileContent(configJsonStream)
47+
val configJson: ByteArray =
48+
FileUtil.readFileContentBytes(configJsonStream)
4949
val configSignature: ByteArray =
5050
FileUtil.readFileContentBytes(configSignatureStream)
5151
val configSignaturePublicKey: String =
@@ -54,7 +54,7 @@ class SignatureVerifierTest {
5454
SignatureVerifier.verify(
5555
configSignature,
5656
configSignaturePublicKey,
57-
configJson,
57+
configJson.toString(Charsets.UTF_8),
5858
),
5959
)
6060
}
@@ -77,12 +77,12 @@ class SignatureVerifierTest {
7777
}
7878
try {
7979
classLoader.getResourceAsStream("config.json").use { configJsonStream ->
80-
classLoader.getResourceAsStream("config.rsa").use { configSignatureStream ->
80+
classLoader.getResourceAsStream("config.ecc").use { configSignatureStream ->
8181
classLoader
82-
.getResourceAsStream("config.pub")
82+
.getResourceAsStream("config.ecpub")
8383
.use { configSignaturePublicKeyStream ->
84-
val configJson: String =
85-
FileUtil.readFileContent(configJsonStream)
84+
val configJson: ByteArray =
85+
FileUtil.readFileContentBytes(configJsonStream)
8686
val configSignature: ByteArray =
8787
FileUtil.readFileContentBytes(configSignatureStream)
8888
val configSignaturePublicKey: String =
@@ -91,7 +91,7 @@ class SignatureVerifierTest {
9191
SignatureVerifier.verify(
9292
configSignature,
9393
configSignaturePublicKey,
94-
configJson + "a",
94+
configJson.toString(Charsets.UTF_8) + "a",
9595
),
9696
)
9797
}
138 Bytes
Binary file not shown.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBT8nzRGnA7I5s5c+wi/kz4a86j449
3+
mRfeDzQRoulSKQSEvryfdFRoze4p6XvY++FOVmuHglszbxcRv1oqT5aAEuYBxOS0
4+
Z85VukvGON3NxD2SBcifANr9f0LTBvu+p9+wOZm+nsK/9CMsCjEGrQ7KGjyQ7Vfk
5+
pJoriwpp3JnCvIuLSak=
6+
-----END PUBLIC KEY-----

0 commit comments

Comments
 (0)