package com.qoppa.sample; import java.awt.geom.Rectangle2D; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; import org.bouncycastle.operator.DigestCalculator; import com.microsoft.aad.adal4j.AuthenticationContext; import com.microsoft.aad.adal4j.AuthenticationResult; import com.microsoft.aad.adal4j.ClientCredential; import com.microsoft.azure.keyvault.KeyVaultClient; import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials; import com.microsoft.azure.keyvault.models.KeyOperationResult; import com.microsoft.azure.keyvault.webkey.JsonWebKeySignatureAlgorithm; import com.qoppa.pdf.SigningInformation; import com.qoppa.pdf.form.SignatureField; import com.qoppa.pdfSecure.PDFSecure; import sun.security.x509.X509CertImpl; /** * This sample signs a pdf document externally using a Microsoft Azure key vault. * */ public class AzureContentSignerSample implements ContentSigner { private DigestCalculator m_DigestCalculator = new SHA256DigestCalculator(); private KeyVaultClient m_Client; private String m_KeyId; private String m_CertId; public AzureContentSignerSample(KeyVaultClient client, String keyId, String certId) { m_Client = client; m_KeyId = keyId; m_CertId = certId; } public AzureContentSignerSample(final String clientId, final String clientPassword, String keyId, String certId) { // store the key and certificate to use during signing m_KeyId = keyId; m_CertId = certId; // Create the key vault client with the client information m_Client = new KeyVaultClient(new KeyVaultCredentials() { @Override public String doAuthenticate(String authorization, String resource, String scope) { AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientPassword); return token.getAccessToken(); } private AuthenticationResult getAccessTokenFromClientCredentials(String authorization, String resource, String clientId, String clientKey) { AuthenticationContext context = null; AuthenticationResult result = null; ExecutorService service = null; try { service = Executors.newFixedThreadPool(1); context = new AuthenticationContext(authorization, false, service); ClientCredential credentials = new ClientCredential(clientId, clientKey); Future future = context.acquireToken(resource, credentials, null); result = future.get(); } catch (Exception e) { throw new RuntimeException(e); } finally { service.shutdown(); } if (result == null) { throw new RuntimeException("authentication result was null"); } return result; } }); } @Override public AlgorithmIdentifier getAlgorithmIdentifier() { return new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withRSA"); } @Override public OutputStream getOutputStream() { return m_DigestCalculator.getOutputStream(); } @Override public byte[] getSignature() { // Sign with Azure key vault KeyOperationResult res = m_Client.sign(m_KeyId, JsonWebKeySignatureAlgorithm.RS256, m_DigestCalculator.getDigest()); return res.result(); } public Certificate getCertificate() throws CertificateException { // get the certificate from the key vault client return new X509CertImpl(m_Client.getCertificate(m_CertId).cer()); } public Certificate[] getCertificateChain() throws CertificateException { // get the certificate chain from the key vault client return new Certificate [] {getCertificate()}; } class SHA256DigestCalculator implements DigestCalculator { private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); public AlgorithmIdentifier getAlgorithmIdentifier() { return new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256); } public OutputStream getOutputStream() { return bOut; } public byte[] getDigest() { byte[] bytes = bOut.toByteArray(); bOut.reset(); Digest sha256 = new SHA256Digest(); sha256.update(bytes, 0, bytes.length); byte[] digest = new byte[sha256.getDigestSize()]; sha256.doFinal(digest, 0); return digest; } } public static final String APP_ID = "MyAppId"; public static final String APP_PASSWORD ="MyAppPassword"; public static final String KEY_ID = "MyKeyId"; public static final String CERT_ID = "MyCertificateId"; public static void main(String [] args) throws Exception { PDFSecure doc = new PDFSecure("C:/test/sample.pdf", null); Rectangle2D signBounds = new Rectangle2D.Double (36, 36, 144, 48); SignatureField signField = doc.addSignatureField(0, "signature", signBounds); AzureContentSignerSample signer = new AzureContentSignerSample(APP_ID, APP_PASSWORD, KEY_ID, CERT_ID); SigningInformation signingInfo = new SigningInformation(signer, signer.getCertificate(), signer.getCertificateChain()); doc.signDocument(signField, signingInfo); doc.saveDocument("C:/test/sample_signed.pdf"); } }