Search code examples
javaandroiditextsignaturegost3410

IText 7 Unknown key algorithm: ECGOST3410


I'm trying to digitally sign pdf document on android (api 26) using IText 7('com.itextpdf:itext7-core:7.1.17') with the GOST3410 algorithm. BouncyCastle libraries: 'org.bouncycastle:bcprov-jdk15on:1.54' and 'org.bouncycastle:bcpkix-jdk15on:1.54' Here's my function:

fun redButton(
    pdfByteArray: ByteArray,
    certificates: Array<java.security.cert.Certificate>,
    privateKey: PrivateKey,
    contentResolver: ContentResolver,
    outUri: Uri
) {
    val provider = BouncyCastleProvider()
    Security.removeProvider(provider.name)
    Security.addProvider(provider)

    val pdfInputStream = ByteArrayInputStream(pdfByteArray)
    val reader = PdfReader(pdfInputStream)
    val outputStream = contentResolver.openOutputStream(outUri)
    val signer = PdfSigner(reader, outputStream, false)

    val appearance = signer.signatureAppearance

    appearance.reason = "study"
    appearance.setReuseAppearance(false)

    val privateKeySignature = PrivateKeySignature(
        privateKey,
        "GOST3411",
        provider.name
    )
    val bouncyCastleDigest = BouncyCastleDigest()
    signer.signDetached(
        bouncyCastleDigest,
        privateKeySignature,
        certificates,
        null,
        null,
        null,
        0,
        PdfSigner.CryptoStandard.CMS
    )
}

This code throws exception:

com.itextpdf.kernel.PdfException: Unknown key algorithm: ECGOST3410.
at com.itextpdf.signatures.PdfPKCS7.setExternalDigest(PdfPKCS7.java:695)
at com.itextpdf.signatures.PdfSigner.signDetached(PdfSigner.java:646)
at com.itextpdf.signatures.PdfSigner.signDetached(PdfSigner.java:538)
at com.example.digitalsignature.app.services.SigningTestIText.redButton(SigningTestIText.kt:38)

If this lib doesn't support GOST3410 can i write my custom byte array in signature space in pdf file?


Solution

  • As mkl said custom signature container implementing IExternalSignatureContainer works well. Here's class example from PrivateKeySignatureContainerBC:

    class PrivateKeySignatureContainerBC(
        signatureAlgorithm: String?,
        privateKey: PrivateKey?,
        private val x509Certificate: X509Certificate,
        private val subfilter: PdfName
    ) : IExternalSignatureContainer {
        override fun sign(data: InputStream): ByteArray {
            return try {
                val msg: CMSTypedData = CMSTypedDataInputStream(data)
                val signCert = X509CertificateHolder(x509Certificate.encoded)
                val gen = CMSSignedDataGenerator()
                gen.addSignerInfoGenerator(
                    JcaSignerInfoGeneratorBuilder(
                        JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
                    )
                        .build(contentSigner, signCert)
                )
                gen.addCertificates(JcaCertStore(Collections.singleton(signCert)))
                val sigData = gen.generate(msg, false)
                sigData.encoded
            } catch (e: IOException) {
                throw GeneralSecurityException(e)
            }
        }
    
        override fun modifySigningDictionary(signDic: PdfDictionary) {
            signDic.put(PdfName.Filter, PdfName("MKLx_GENERIC_SIGNER"))
            signDic.put(PdfName.SubFilter, subfilter)
        }
    
        private val contentSigner: ContentSigner = JcaContentSignerBuilder(signatureAlgorithm).build(privateKey)
    
        internal inner class CMSTypedDataInputStream(var `in`: InputStream) : CMSTypedData {
            override fun getContentType(): ASN1ObjectIdentifier {
                return PKCSObjectIdentifiers.data
            }
    
            override fun getContent(): Any {
                return `in`
            }
    
            override fun write(out: OutputStream) {
                val buffer = ByteArray(8 * 1024)
                var read: Int
                while (`in`.read(buffer).also { read = it } != -1) {
                    out.write(buffer, 0, read)
                }
                `in`.close()
            }
        }
    }
    

    Suggested class call by signer.signExternalContainer:

    signer.signExternalContainer(
                PrivateKeySignatureContainerBC(
                    "GOST3411withECGOST3410",
                    privateKey,
                    certificate,
                    PdfName.Adbe_pkcs7_detached
                ),
                _estimatedSize
            )