diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SignatureComponent.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SignatureComponent.kt index 45d0f058..356008cd 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SignatureComponent.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SignatureComponent.kt @@ -172,10 +172,15 @@ fun SignatureComponent( .wrapContentHeight(), contentAlignment = Alignment.Center, ) { - val iconTestTagSuffix = if (isTimestamped) "Timestamp" else "Signature" + val iconTestTagSuffix = + if (isTimestamped || signature.isDigitalSeal) { + "Timestamp" + } else { + "Signature" + } Icon( imageVector = - if (isTimestamped) { + if (isTimestamped || signature.isDigitalSeal) { ImageVector.vectorResource(R.drawable.ic_m3_approval_48dp_wght400) } else { ImageVector.vectorResource(R.drawable.ic_icon_signature) @@ -213,7 +218,7 @@ fun SignatureComponent( testTagsAsResourceId = true }.testTag("signatureComponentSignatureName"), name = nameText, - allCaps = isTimestamped || showNameAsAllCaps, + allCaps = isTimestamped || signature.isDigitalSeal || showNameAsAllCaps, ) Text( text = signedTime, diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SigningNavigation.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SigningNavigation.kt index c01dc659..e44739b2 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SigningNavigation.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SigningNavigation.kt @@ -1224,7 +1224,7 @@ fun SigningNavigation( modifier = modifier, showSheet = showSignatureBottomSheet, clickedSignature = clickedSignature, - isTimestamp = isTimestampedContainer, + isTimestamp = isTimestampedContainer || clickedSignature.value?.isDigitalSeal == true, signedContainer = signedContainer, signingViewModel = signingViewModel, sharedSignatureViewModel = sharedSignatureViewModel, diff --git a/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureWrapperTest.kt b/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureWrapperTest.kt new file mode 100644 index 00000000..5ad99637 --- /dev/null +++ b/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureWrapperTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2017 - 2026 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.libdigidoclib.domain.model + +import org.bouncycastle.asn1.ASN1ObjectIdentifier +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x509.CertificatePolicies +import org.bouncycastle.asn1.x509.Extension +import org.bouncycastle.asn1.x509.PolicyInformation +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import java.math.BigInteger +import java.security.KeyPairGenerator +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.Date + +class SignatureWrapperTest { + @Test + fun signatureWrapper_isCertificateDigitalSeal_trueForESealOid_7_3() { + val certDer = generateCertWithPolicy("1.3.6.1.4.1.10015.7.3") + assertTrue(SignatureWrapper.isCertificateDigitalSeal(certDer)) + } + + @Test + fun signatureWrapper_isCertificateDigitalSeal_trueForESealOid_7_1() { + val certDer = generateCertWithPolicy("1.3.6.1.4.1.10015.7.1") + assertTrue(SignatureWrapper.isCertificateDigitalSeal(certDer)) + } + + @Test + fun signatureWrapper_isCertificateDigitalSeal_trueForESealOid_2_1() { + val certDer = generateCertWithPolicy("1.3.6.1.4.1.10015.2.1") + assertTrue(SignatureWrapper.isCertificateDigitalSeal(certDer)) + } + + @Test + fun signatureWrapper_isCertificateDigitalSeal_falseForIdCardOid() { + val certDer = generateCertWithPolicy("1.3.6.1.4.1.10015.1.1") + assertFalse(SignatureWrapper.isCertificateDigitalSeal(certDer)) + } + + @Test + fun signatureWrapper_isCertificateDigitalSeal_falseForEmptyByteArray() { + assertFalse(SignatureWrapper.isCertificateDigitalSeal(ByteArray(0))) + } + + private fun generateCertWithPolicy(policyOid: String): ByteArray { + val keyPairGen = KeyPairGenerator.getInstance("RSA") + keyPairGen.initialize(2048) + val keyPair = keyPairGen.generateKeyPair() + + val now = Date() + val notAfter = Date.from(Instant.now().plus(365, ChronoUnit.DAYS)) + val subject = X500Name("CN=Test") + + val builder = JcaX509v3CertificateBuilder(subject, BigInteger.ONE, now, notAfter, subject, keyPair.public) + builder.addExtension( + Extension.certificatePolicies, + false, + CertificatePolicies(arrayOf(PolicyInformation(ASN1ObjectIdentifier(policyOid)))), + ) + + val signer = JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private) + return builder.build(signer).encoded + } +} diff --git a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureInterface.kt b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureInterface.kt index d1d2c7f8..9cba8be9 100644 --- a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureInterface.kt +++ b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureInterface.kt @@ -48,6 +48,7 @@ interface SignatureInterface : Serializable { val ocspCertificateDer: ByteArray val timeStampCertificateDer: ByteArray val archiveTimeStampCertificateDer: ByteArray + val isDigitalSeal: Boolean val validator: ValidatorInterface } diff --git a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureWrapper.kt b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureWrapper.kt index 4a30593b..fe86eea2 100644 --- a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureWrapper.kt +++ b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/SignatureWrapper.kt @@ -23,6 +23,7 @@ package ee.ria.DigiDoc.libdigidoclib.domain.model import ee.ria.DigiDoc.common.certificate.CertificateServiceImpl import ee.ria.DigiDoc.common.model.Certificate +import ee.ria.DigiDoc.common.model.EIDType import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog import ee.ria.libdigidocpp.Signature @@ -92,6 +93,18 @@ class SignatureWrapper( ByteArray(0) } + override val isDigitalSeal: Boolean = isCertificateDigitalSeal(signingCertificateDer) + + companion object { + fun isCertificateDigitalSeal(signingCertificateDer: ByteArray): Boolean = + try { + val certService = CertificateServiceImpl() + certService.extractEIDType(certService.parseCertificate(signingCertificateDer)) == EIDType.E_SEAL + } catch (_: Exception) { + false + } + } + override val validator: ValidatorInterface = ValidatorWrapper(Signature.Validator(signature)) private fun signatureName(signature: Signature): String {