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