IText 7 未知密钥算法:ECGOST3410

IText 7 Unknown key algorithm: ECGOST3410

我正在尝试使用 IText 7('com.itextpdf:itext7-core:7.1.17') 在 android (api 26) 上对 pdf 文档进行数字签名GOST3410 算法。 BouncyCastle 库:'org.bouncycastle:bcprov-jdk15on:1.54''org.bouncycastle:bcpkix-jdk15on:1.54' 这是我的函数:

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
    )
}

此代码抛出异常:

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)

如果这个库不支持 GOST3410,我可以在 pdf 文件的签名 space 中写入自定义字节数组吗?

作为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()
        }
    }
}

建议 class 致电 signer.signExternalContainer:

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