Add signature to PDF in Kotlin for Android
Showcases how to digitally sign a PDF after clicking on the signature form field. Get additional resources by visiting our guide on adding a digital signature to a PDF in Android
/* * Copyright © 2020-2025 PSPDFKit GmbH. All rights reserved. * * The PSPDFKit Sample applications are licensed with a modified BSD license. * Please see License for details. This notice may not be removed from this file. */
package com.pspdfkit.catalog.examples.kotlin
import android.content.Contextimport android.net.Uriimport android.widget.Toastimport com.pspdfkit.catalog.Rimport com.pspdfkit.catalog.SdkExampleimport com.pspdfkit.configuration.activity.PdfActivityConfigurationimport com.pspdfkit.document.DocumentSourceimport com.pspdfkit.document.PdfDocumentLoaderimport com.pspdfkit.document.providers.AssetDataProviderimport com.pspdfkit.signatures.DigitalSignatureMetadataimport com.pspdfkit.signatures.SignatureAppearanceimport com.pspdfkit.signatures.SignatureAppearance.SignatureAppearanceModeimport com.pspdfkit.signatures.SignerOptionsimport com.pspdfkit.signatures.SigningManagerimport com.pspdfkit.signatures.getPrivateKeyEntryFromP12Streamimport com.pspdfkit.signatures.getX509Certificatesimport com.pspdfkit.signatures.timestamp.TimestampDataimport com.pspdfkit.ui.PdfActivityIntentBuilderimport com.pspdfkit.utils.PdfLogimport java.io.Fileimport java.io.IOExceptionimport java.security.GeneralSecurityExceptionimport java.security.KeyStoreimport java.security.NoSuchAlgorithmExceptionimport java.security.PrivateKeyimport java.security.Signature
/** * An example showing how to leverage customSigning functionality in [SigningManager] to digitally sign document manually. * */class ManualSigningExample(context: Context) : SdkExample(context, R.string.manualSigningExampleTitle, R.string.manualSigningExampleDescription) {
override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) { val assetName = "Form_example.pdf"
val unsignedDocument = PdfDocumentLoader.openDocument(context, DocumentSource(AssetDataProvider(assetName))) val keyEntryWithCertificates = getPrivateKeyEntry(context) val privateKey = keyEntryWithCertificates.privateKey val certificates = keyEntryWithCertificates.getX509Certificates() val signatureFormFields = unsignedDocument.documentSignatureInfo.signatureFormFields val outputFile = File(context.filesDir, "signedDocument.pdf") outputFile.delete() // make sure output is deleted from previous runs.
/** Configure the appearance of the signature using [SignatureAppearance] **/ val appearance = SignatureAppearance( signatureAppearanceMode = SignatureAppearanceMode.SIGNATURE_ONLY ) val metadata = DigitalSignatureMetadata( signatureAppearance = appearance, timestampData = TimestampData("https://freetsa.org/tsr") )
/** [SignerOptions] contains all the required configuration for [SigningManager]*/ val signerOptions = SignerOptions.Builder(signatureFormFields[0], Uri.fromFile(outputFile)) .setCertificates(certificates) .setSignatureMetadata(metadata) .setType(digitalSignatureType) .build()
/** [SignerOptions] contains all the required configuration for [SigningManager]*/ SigningManager.signDocument( context = context, signerOptions = signerOptions, customSigning = { data, hashAlgorithm -> /** Here we are manually signing ByteArray with provided hashAlgorithm and private key, this is a mandatory step if * customer doesn't provide private key in [SignerOptions] */ data.signData(privateKey, hashAlgorithm) }, onFailure = { e -> Toast.makeText(context, "Error launching example. See logcat for details.", Toast.LENGTH_SHORT).show() PdfLog.e("ManualSigningExample", e, "Error while launching example.") } ) { val signedDocument = Uri.fromFile(outputFile) // Load and show the signed document. val intent = PdfActivityIntentBuilder.fromUri(context, signedDocument) .configuration(configuration.build()) .build() context.startActivity(intent) } }
private fun ByteArray.signData(privateKey: PrivateKey, hashAlgorithm: String?): ByteArray = try { /** We are using the [Signature] class from java.security package, to sign the byte arrays.*/
/** The signature algorithm can be, among others, the NIST standard DSA, using DSA and SHA-256. * The DSA algorithm using the SHA-256 message digest algorithm can be specified as SHA256withDSA. * In the case of RSA the signing algorithm could be specified as, for example, SHA256withRSA. * The algorithm name must be specified, as there is no default. * * Here 'hashAlgorithm' is SHA256 and 'key.algorithm' is RSA * providing algorithm as SHA256withRSA * for more details visit https://docs.oracle.com/javase/8/docs/api/java/security/Signature.html **/ val algorithm = "${hashAlgorithm}with${privateKey.algorithm}"
Signature.getInstance(algorithm).run { initSign(privateKey) // Initialize this object for signing. update(this@signData) // Updates the data to be signed or verified, using the specified array of bytes sign() // Returns the signature bytes of all the data updated } } catch (e: NoSuchAlgorithmException) { throw RuntimeException(e) }
/** * Loads the [KeyStore.PrivateKeyEntry] that will be used by our [SigningManager]. */ @Throws(IOException::class, GeneralSecurityException::class) private fun getPrivateKeyEntry(context: Context): KeyStore.PrivateKeyEntry { // Inside a p12 we have both the certificate (or certificate chain to the root CA) and private key used for signing. val keystoreFile = context.assets.open("digital-signatures/ExampleSigner.p12") return getPrivateKeyEntryFromP12Stream(keystoreFile, "test") }}
This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.