I am trying to make a digital signature (PKCS12 file) using iText, but I cannot find how to have an appearance with the information of the certificate used for the signature.
UPDATE 1 :
the following test class allows you to sign a document using a p12 certificate and impose a visual on the PDF document:
package ma.sigital.dsp.prosigngateway.service;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Date;
import com.itextpdf.text.Rectangle;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
public class Test {
public static final String DEST = "B:/Hash-et-Reconstitution/tmp/SIGNED-Doc_test.pdf";
public static final String KEYSTORE = "C:/lp7command/tools/certificate.p12";
public static final String SRC = "B:/Hash-et-Reconstitution/tmp/Doc_test.pdf";
public static final String IMG = "C:/Repo_LP7/logo-cire.png";
public static final char[] PASSWORD = "123456".toCharArray();
public static final String[] RESULT_FILES = new String[] {
"signature_appearance_1.pdf",
"signature_appearance_2.pdf",
"signature_appearance_3.pdf",
"signature_appearance_4.pdf"
};
public void sign(String src, String name, String dest,
Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider,
CryptoStandard subfilter,
String reason, String location, RenderingMode renderingMode,
Image image)
throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setVisibleSignature(new Rectangle(200, 100, 600, 5),2,"sig");
appearance.setLayer2Text("Signed on " + new Date().toString());
appearance.setRenderingMode(renderingMode);
appearance.setSignatureGraphic(image);
// Creating the signature
PrivateKeySignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, subfilter);
}
public static void main(String[] args) throws GeneralSecurityException, IOException, DocumentException {
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream(KEYSTORE), PASSWORD);
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
Certificate[] chain = ks.getCertificateChain(alias);
Image image = Image.getInstance(IMG);
Test app = new Test();
app.sign(SRC, "Signature1", String.format(DEST, 3), chain, pk,
DigestAlgorithms.SHA256, provider.getName(), CryptoStandard.CMS,
"Appearance 3", "Ghent", RenderingMode.GRAPHIC_AND_DESCRIPTION, image);
}
}
Related
I used BouncyCastle to verify signature and extract infos from a ".p7m" file (original content, signer etc.).
Now I need to validate and extract infos from the TimeStamp within the same ".p7m" file.
How can I validate the TimeStampToken? I wrote this code that works well for the Signature but it doesn't validate the TimeStamp. I passed the "cert" variable to the build() method to validate the Signature and the TimeStampToken both. For the Signature it's ok, but for the TimeStamp it doesn't work :(
Where do I wrong? Thanks in advance.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Security;
import java.util.Collection;
import java.util.Iterator;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataParser;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.util.Store;
public class Launcher3 {
public static void main(String[] args) throws Exception {
File myFile=new File("D:\\fdr\\bouncycastle\\New Text Document.txt.p7m");
byte[] bytesArray = readContentIntoByteArray(myFile);
FileOutputStream fos = new FileOutputStream("D:\\fdr\\bouncycastle\\New Text Document.txt");
byte[] bytesArrayOriginalFile=getData(bytesArray);
fos.write(bytesArrayOriginalFile);
fos.close();
verifySign(bytesArray);
}
static public void verifySign(byte[] signedData) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
CMSSignedDataParser sp = new CMSSignedDataParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), signedData);
sp.getSignedContent().drain();
Store certStore = sp.getCertificates();
SignerInformationStore signers = sp.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
System.out.println("info 1: "+cert.getIssuer());
System.out.println("info 2: "+cert.getSubject());
System.out.println("date from: "+cert.getNotBefore());
System.out.println("date to: "+cert.getNotAfter());
System.out.println("Serial n. "+cert.getSerialNumber());
System.out.println("verify returns: " + signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
// --------------------------------------------------------------------------------------------------------------------^
// LOOK AT HERE: it works!
AttributeTable attrs = signer.getUnsignedAttributes();
Attribute att = attrs.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
ASN1Encodable dob = att.getAttrValues().getObjectAt(0);
byte[] encodedTsp = dob.toASN1Primitive().getEncoded();
TimeStampToken result = null;
if(encodedTsp!=null) {
CMSSignedData cms = new CMSSignedData(encodedTsp);
result = new TimeStampToken(cms);
System.out.println("timestamp: "+result.getTimeStampInfo().getGenTime());
System.out.println("serial n. "+result.getTimeStampInfo().getSerialNumber());
System.out.println("tsa: "+result.getTimeStampInfo().getTsa());
System.out.println("policy: "+result.getTimeStampInfo().getPolicy());
result.validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
// ------------------------------------------------------------------------------^
// LOOK AT HERE: it doesn't work!
}
}
}
static public byte[] getData(final byte[] p7bytes) throws CMSException, IOException {
CMSSignedData signedData = new CMSSignedData(p7bytes);
CMSProcessable signedContent = signedData.getSignedContent();
return (byte[])signedContent.getContent();
}
private static byte[] readContentIntoByteArray(File file)
{
FileInputStream fileInputStream = null;
byte[] bFile = new byte[(int) file.length()];
try
{
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return bFile;
}
}
I solved by myself (and I'm so happy). I need to find the certificate of the timestamptoken in this way:
Store storeTt = result.getCertificates();
Collection collTt = storeTt.getMatches(result.getSID());
Iterator certIt2 = collTt.iterator();
X509CertificateHolder cert2 = (X509CertificateHolder)certIt2.next();
System.out.println("timestamp's verify: "+result.isSignatureValid(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert2)));
result.validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert2));
Auth0 provides two JWT libraries, one for Node: node-jsonwebtoken, and one for Java: java-jwt.
I created the private/public key pair, and used it successfully in Node with node-jsonwebtoken:
var key = fs.readFileSync('private.key');
var pem = fs.readFileSync('public.pem');
var header = {...};
var payload = {...};
header.algorithm = "RS256";
var message = jsonwebtoken.sign(payload, key, header);
var decoded = jsonwebtoken.verify(message, pem, {algorithm: "RS256"});
But I found no way of doing the same in Java with java-jwt.
Anyone has a working example of how to use private/public keys for JWT in Java?
I used the following code for JWT in Java. Try it.
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class JWTJavaWithPublicPrivateKey {
public static void main(String[] args) {
System.out.println("generating keys");
Map<String, Object> rsaKeys = null;
try {
rsaKeys = getRSAKeys();
} catch (Exception e) {
e.printStackTrace();
}
PublicKey publicKey = (PublicKey) rsaKeys.get("public");
PrivateKey privateKey = (PrivateKey) rsaKeys.get("private");
System.out.println("generated keys");
String token = generateToken(privateKey);
System.out.println("Generated Token:\n" + token);
verifyToken(token, publicKey);
}
public static String generateToken(PrivateKey privateKey) {
String token = null;
try {
Map<String, Object> claims = new HashMap<String, Object>();
// put your information into claim
claims.put("id", "xxx");
claims.put("role", "user");
claims.put("created", new Date());
token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS512, privateKey).compact();
} catch (Exception e) {
e.printStackTrace();
}
return token;
}
// verify and get claims using public key
private static Claims verifyToken(String token, PublicKey publicKey) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
System.out.println(claims.get("id"));
System.out.println(claims.get("role"));
} catch (Exception e) {
claims = null;
}
return claims;
}
// Get RSA keys. Uses key size of 2048.
private static Map<String, Object> getRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
Map<String, Object> keys = new HashMap<String, Object>();
keys.put("private", privateKey);
keys.put("public", publicKey);
return keys;
}
}
Maven Dependency
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
Recent versions (since 3.0.0) of the auth0 java-jwt library supports RSA and ECDSA for signing JWT tokens using a public/private key pair.
Example of signing a JWT using java-jwt (based on the documentation).
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Map;
class JwtPKSigningExample {
public static void main(String[] args) throws Exception {
Map<String, Object> keys = generateRSAKeys();
String token = null;
try {
RSAPrivateKey privateKey = (RSAPrivateKey) keys.get("private");
Algorithm algorithm = Algorithm.RSA256(null, privateKey);
token = JWT.create()
.withIssuer("pk-signing-example")
.sign(algorithm);
} catch (JWTCreationException x) {
throw x;
}
try {
RSAPublicKey publicKey = (RSAPublicKey) keys.get("public");
Algorithm algorithm = Algorithm.RSA256(publicKey, null);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("pk-signing-example")
.build();
DecodedJWT jwt = verifier.verify(token);
System.out.println(jwt.getToken());
} catch (JWTVerificationException x) {
throw x;
}
}
private static Map<String, Object> generateRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return Map.of("private", keyPair.getPrivate(), "public", keyPair.getPublic());
}
}
That particular library doesn't support it. But you can check others for Java that do. See here: https://jwt.io/
package com.java;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Enumeration;
import org.jose4j.json.internal.json_simple.parser.ParseException;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.keys.resolvers.JwksVerificationKeyResolver;
import org.jose4j.keys.resolvers.VerificationKeyResolver;
import org.jose4j.lang.JoseException;
public class JWTSigningAndVerification {
public static void main(String args[]) throws Exception {
String jwt = generateJWT();
validateJWTwithJWKS(jwt);
}
private static String generateJWT() throws FileNotFoundException, KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, JoseException {
JwtClaims jwt_claims = new JwtClaims();
jwt_claims.setSubject("sub");
jwt_claims.setIssuer("https://domain");
jwt_claims.setIssuedAtToNow();
jwt_claims.setExpirationTimeMinutesInTheFuture(1000000);
jwt_claims.setGeneratedJwtId();
jwt_claims.setClaim("sid", "sessionid");
jwt_claims.setClaim("email", "test#mail.com");
jwt_claims.setClaim("given_name", "first");
jwt_claims.setClaim("family_name", "last");
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(jwt_claims.toJson());
String KeyPassword = "p12-key-password";
File file = new File("path-to-key.p12");
InputStream stream = new FileInputStream(file);
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(stream, KeyPassword.toCharArray());
Enumeration e = store.aliases();
String alias = (String) e.nextElement();
PrivateKey key = (PrivateKey) store.getKey(alias, KeyPassword.toCharArray());
jws.setKey(key);
jws.setKeyIdHeaderValue("1");
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_PSS_USING_SHA512);
jws.setHeader("typ", "JWT");
String jwt = jws.getCompactSerialization();
System.out.println(jwt);
return jwt;
}
private static void validateJWTwithJWKS(String jwt) throws JoseException, FileNotFoundException, IOException,
ParseException, InvalidJwtException, MalformedClaimException {
JsonWebKeySet jsonWebKeySet = new JsonWebKeySet("json-jwks-escaped");
VerificationKeyResolver verificationKeyResolver = new JwksVerificationKeyResolver(jsonWebKeySet.getJsonWebKeys());
JwtConsumer jwtConsumer = new JwtConsumerBuilder().setVerificationKeyResolver(verificationKeyResolver).build();
JwtClaims claims = jwtConsumer.processToClaims(jwt);
System.out.println("sub:- " + claims.getSubject());
}
}
I want to perform digital signature program and make signed pdf.
For that
1. I use following source code
Java Code
2. For certificate create self signed certificate using this link and export to pfx.
How to create self signed certi
3. Now try to execute following program but still give following error
/*
* This class is part of the book "iText in Action - 2nd Edition"
* written by Bruno Lowagie (ISBN: 9781935182610)
* For more info, go to: http://itextpdf.com/examples/
* This example only works with the AGPL version of iText.
*/
package iText;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Properties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.CertificateInfo;
import com.itextpdf.text.pdf.security.CertificateVerification;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.PdfPKCS7;
import com.itextpdf.text.pdf.security.VerificationException;
public class Signatures {
/**
* The resulting PDF
*/
public static String ORIGINAL = "C:\\Users\\02948\\Desktop\\hello.pdf";
/**
* The resulting PDF
*/
public static String SIGNED1 = "C:\\Users\\02948\\Desktop\\signature_1.pdf";
/**
* The resulting PDF
*/
public static String SIGNED2 = "C:\\Users\\02948\\Desktop\\signature_2.pdf";
/**
* Info after verification of a signed PDF
*/
public static String VERIFICATION = "C:\\Users\\02948\\Desktop\\verify.txt";
/**
* The resulting PDF
*/
public static String REVISION = "C:\\Users\\02948\\Desktop\\revision_1.pdf";
/**
* A properties file that is PRIVATE. You should make your own properties
* file and adapt this line.
*/
public static String PATH = "C:\\Users\\02948\\Documents\\NetBeansProjects\\DigiSig\\src\\digisig\\key.properties";
/**
* Some properties used when signing.
*/
public static Properties properties = new Properties();
/**
* One of the resources.
*/
// public static final String RESOURCE = "resources/img/logo.gif";
/**
* Creates a PDF document.
*
* #param filename the path to the new PDF document
* #throws DocumentException
* #throws IOException
*/
public void createPdf(String filename) throws IOException, DocumentException {
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(filename));
document.open();
document.add(new Paragraph("Hello World!"));
document.close();
}
/**
* Manipulates a PDF file src with the file dest as result
*
* #param src the original PDF
* #param dest the resulting PDF
* #throws IOException
* #throws DocumentException
* #throws GeneralSecurityException
*/
public void signPdfFirstTime(String src, String dest)
throws IOException, DocumentException, GeneralSecurityException {
// String path = properties.getProperty("PRIVATE");
// String keystore_password = properties.getProperty("PASSWORD");
// String key_password = properties.getProperty("PASSWORD");
String path = "C:\\Users\\02948\\Desktop\\kandarp.pfx";
String keystore_password = "kandarp";
String key_password = "kandarp";
KeyStore ks = KeyStore.getInstance("pkcs12", "BC");
ks.load(new FileInputStream(path), keystore_password.toCharArray());
String alias = (String) ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, key_password.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
// reader and stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
// appearance.setImage(Image.getInstance(RESOURCE));
appearance.setReason("I've written this.");
appearance.setLocation("Foobar");
appearance.setVisibleSignature(new Rectangle(72, 732, 144, 780), 1, "first");
// digital signature
ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", "BC");
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, es, chain, null, null, null, 0, CryptoStandard.CMS);
}
/**
* Manipulates a PDF file src with the file dest as result
*
* #param src the original PDF
* #param dest the resulting PDF
* #throws IOException
* #throws DocumentException
* #throws GeneralSecurityException
*/
public void signPdfSecondTime(String src, String dest)
throws IOException, DocumentException, GeneralSecurityException {
String path = "resources/encryption/.keystore";
String keystore_password = "f00b4r";
String key_password = "f1lmf3st";
String alias = "foobar";
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream(path), keystore_password.toCharArray());
PrivateKey pk = (PrivateKey) ks.getKey(alias, key_password.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
// reader / stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
// appearance
PdfSignatureAppearance appearance = stamper
.getSignatureAppearance();
appearance.setReason("I'm approving this.");
appearance.setLocation("Foobar");
appearance.setVisibleSignature(new Rectangle(160, 732, 232, 780), 1, "second");
// digital signature
ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", "BC");
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, es, chain, null, null, null, 0, CryptoStandard.CMS);
}
/**
* Verifies the signatures of a PDF we've signed twice.
*
* #throws GeneralSecurityException
* #throws IOException
*/
public void verifySignatures() throws GeneralSecurityException, IOException {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
CertificateFactory cf = CertificateFactory.getInstance("X509");
FileInputStream is1 = new FileInputStream(properties.getProperty("ROOTCERT"));
X509Certificate cert1 = (X509Certificate) cf.generateCertificate(is1);
ks.setCertificateEntry("cacert", cert1);
FileInputStream is2 = new FileInputStream("resources/encryption/foobar.cer");
X509Certificate cert2 = (X509Certificate) cf.generateCertificate(is2);
ks.setCertificateEntry("foobar", cert2);
PrintWriter out = new PrintWriter(new FileOutputStream(VERIFICATION));
PdfReader reader = new PdfReader(SIGNED2);
AcroFields af = reader.getAcroFields();
ArrayList<String> names = af.getSignatureNames();
for (String name : names) {
out.println("Signature name: " + name);
out.println("Signature covers whole document: " + af.signatureCoversWholeDocument(name));
out.println("Document revision: " + af.getRevision(name) + " of " + af.getTotalRevisions());
PdfPKCS7 pk = af.verifySignature(name);
Calendar cal = pk.getSignDate();
Certificate[] pkc = pk.getCertificates();
out.println("Subject: " + CertificateInfo.getSubjectFields(pk.getSigningCertificate()));
out.println("Revision modified: " + !pk.verify());
List<VerificationException> errors = CertificateVerification.verifyCertificates(pkc, ks, null, cal);
if (errors.size() == 0) {
out.println("Certificates verified against the KeyStore");
} else {
out.println(errors);
}
}
out.flush();
out.close();
}
/**
* Extracts the first revision of a PDF we've signed twice.
*
* #throws IOException
*/
public void extractFirstRevision() throws IOException {
PdfReader reader = new PdfReader(SIGNED2);
AcroFields af = reader.getAcroFields();
FileOutputStream os = new FileOutputStream(REVISION);
byte bb[] = new byte[1028];
InputStream ip = af.extractRevision("first");
int n = 0;
while ((n = ip.read(bb)) > 0) {
os.write(bb, 0, n);
}
os.close();
ip.close();
}
/**
* Main method.
*
* #param args no arguments needed
* #throws DocumentException
* #throws IOException
* #throws GeneralSecurityException
*/
public static void main(String[] args)
throws IOException, DocumentException, GeneralSecurityException {
Security.addProvider(new BouncyCastleProvider());
FileInputStream fis = new FileInputStream("C:\\Users\\02948\\Desktop\\kandarp.pfx");
String password = "kandarp";
properties.load(new FileInputStream(PATH));
Signatures signatures = new Signatures();
signatures.createPdf(ORIGINAL);
signatures.signPdfFirstTime(ORIGINAL, SIGNED1);
signatures.signPdfSecondTime(SIGNED1, SIGNED2);
signatures.verifySignatures();
signatures.extractFirstRevision();
}
}
Error comes . pl help
Exception in thread "main" java.lang.NullPointerException
at com.itextpdf.text.pdf.security.PrivateKeySignature.<init>(PrivateKeySignature.java:77)
at iText.Signatures.signPdfFirstTime(Signatures.java:135)
at iText.Signatures.main(Signatures.java:252)
Java Result: 1
BUILD SUCCESSFUL (total time: 1 second)
I also got the same problem. Changing the Security.Provider At the time of creating KeyStore works.
Either change your code from
KeyStore ks = KeyStore.getInstance("pkcs12", "BC");
to
KeyStore ks = KeyStore.getInstance("pkcs12");
or use any Other Provider: SunRsaSign, SunEC, SunJSSE, SunJGSS
More info about Provider class: https://docs.oracle.com/javase/7/docs/api/java/security/Provider.html
I have a bouncy castle code that encrypts and sign a message(listed below) ; now I need to find the code for opposite end ; Verify the signature & decrypt the message. My search to find a solution for the past 2 days has been futile, as I could only find example for either verifying the signature (or) for decryption ; but not both. Please find below the encryption & signing code ; would greatly appreciate any hint or direction to Verify the signature & decrypt the message
Note : The method encryptAndSign() is called to encrypt and sign the Content/data
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.naming.NamingException;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public final class EncryptorNsigner {
public byte[] encryptAndSign(final String content, final String selfSignedCert,
final String encryptionCert) throws CertificateException, IOException,
KeyStoreException, Exception {
byte[] signedBytes;
//Get key store
KeyStore keyStore = this.getKeyStore();
//Setup provider
CMSSignedDataGenerator signatureGenerator = this.setUpProvider(
keyStore, selfSignedCert);
//Encrypt data
byte[] encryptBytes = this.encryptData(content.getBytes("UTF-8"),
getEncyptionCertFromKeystore(keyStore, encryptionCert), null);
//Sign the encrypted data
signedBytes = this.signPkcs7(encryptBytes, signatureGenerator);
return Base64.encode(signedBytes);
}
private KeyStore getKeyStore() throws NamingException, KeyStoreException,
FileNotFoundException, CertificateException, IOException,
NoSuchAlgorithmException{
...
return keystore;
}
private X509Certificate getEncyptionCertFromKeystore(final KeyStore keystore, final String encryptionCertName)
throws CertificateException, IOException, KeyStoreException{
Certificate c = keystore.getCertificate(encryptionCertName);
X509CertificateHolder certHolder = new X509CertificateHolder(c.getEncoded());
return new JcaX509CertificateConverter().setProvider( "BC" )
.getCertificate(certHolder );
}
private CMSSignedDataGenerator setUpProvider(final KeyStore keystore, String signCertName) throws Exception {
Security.addProvider(new BouncyCastleProvider());
//Get certificate chain
Certificate[] certchain = (Certificate[]) keystore.getCertificateChain(signCertName);
final List<Certificate> certlist = new ArrayList<Certificate>();
//Add the certificates in Certificate chain to the Certificate list
for (int i = 0, length = certchain == null ? 0 : certchain.length; i < length; i++) {
certlist.add(certchain[i]);
}
//Class for storing certificates for later lookup
Store certstore = new JcaCertStore(certlist);
//Get certificate
Certificate cert = keystore.getCertificate(signCertName);
String algorithm = "SHA1withRSA";
String keyStorePwd = "....";
ContentSigner signer = new JcaContentSignerBuilder(algorithm).setProvider("BC").
build((PrivateKey) (keystore.getKey(signCertName, keyStorePwd.toCharArray())));
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").
build()).build(signer, (X509Certificate) cert));
generator.addCertificates(certstore);
return generator;
}
private byte[] signPkcs7(final byte[] content, final CMSSignedDataGenerator generator)
throws CMSException, IOException {
CMSTypedData cmsdata = new CMSProcessableByteArray(content);
CMSSignedData signeddata = generator.generate(cmsdata, true);
return signeddata.getEncoded();
}
private byte[] encryptData(byte[] dataToEncrypt, X509Certificate recipientCert, String algorithmName) throws IOException{
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
CMSEnvelopedData ed;
AlgorithmIdentifier digestAlgorithm;
if (algorithmName!=null && !algorithmName.isEmpty()){
digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(new DefaultSignatureAlgorithmIdentifierFinder().find(algorithmName));
try {
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
ed = edGen.generate(new CMSProcessableByteArray(dataToEncrypt),
new JceCMSContentEncryptorBuilder(digestAlgorithm.getAlgorithm())
.setProvider("BC").build());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
else{
try {
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
ed = edGen.generate(new CMSProcessableByteArray(dataToEncrypt),
new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC) //Default algorithmName = DES_EDE3_CBC
.setProvider("BC").build());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
return ed.getEncoded();
}
}
2 years waiting for an answer. I hope you find the solution.
To help others, here is a sample for reading SMIME emails and decrypt attachments. It's based on the fact that the sender used your public key to encrypt and you got your key and password to decrypt the message.
Security.addProvider(new BouncyCastleProvider());
// IMAP encrypt
final KeyStore ks = KeyStore.getInstance("PKCS12");
final String password = CERTIFICAT_SMIME_PWD;
ks.load(this.class.getClassLoader()
.getResourceAsStream(CERTIFICAT_SMIME_PFX), password.toCharArray());
final String alias = ks.aliases().nextElement();
final PrivateKey pKey = (PrivateKey) ks.getKey(alias, password.toCharArray());
final SMIMEEnveloped m = new SMIMEEnveloped((MimeMessage) message);
final RecipientInformationStore recipients = m.getRecipientInfos();
final X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
final RecipientId recId = new JceKeyTransRecipientId(cert);
final RecipientInformation recipient = recipients.get(recId);
final byte[] content = recipient.getContent(new JceKeyTransEnvelopedRecipient(pKey)
.setProvider(BouncyCastleProvider.PROVIDER_NAME));
final String providerName = BouncyCastleProvider.PROVIDER_NAME;
final MimeBodyPart res = SMIMEUtil.toMimeBodyPart(content);
final MimeMultipart parts = (MimeMultipart) res.getContent();
for (int i = 0; i < parts.getCount(); i++) {
final BodyPart part = parts.getBodyPart(i);
if (part.getContent() instanceof Multipart) {
multipart = (Multipart) part.getContent();
}
}
I read certificates from keystore browser, there is some problem with getting private key, but public key get perfect. Below is code:
KeyStore keystore1 = KeyStore.getInstance("Windows-MY");
keystore1.load(null, null);
if (keystore1 != null) {
Enumeration<String> enumeration = keystore1.aliases();
while (enumeration.hasMoreElements()) {
String alias = enumeration.nextElement();
if (alias.equals("myalias")) {
char[] keypwd = "123456".toCharArray();
KeyStore.PrivateKeyEntry keyEnt = (KeyStore.PrivateKeyEntry) keystore1.getEntry(alias, new KeyStore.PasswordProtection(keypwd));
System.out.println("getPublicKey: " + keyEnt.getCertificate().getPublicKey().getEncoded());
//show RSAPrivateKey [size=2048 bits, type=Exchange, container={5089EC94-FF45-4339-ACCF-E6ECCCB16899}]
System.out.println("privateKey111: " + keyEnt.getPrivateKey());
}
}
}
Public key output is correct, but private key looks like this:
RSAPrivateKey [size=2048 bits, type=Exchange, container={5089EC94-FF45-4339-ACCF-E6ECCCB16899}]
Password is correct. How can I get private key?
Here is a private key exporter I have used, it reads JKS keystore maybe you could somehow convert keystore first or modify your code accordingly.
c:\test>java -classes ./classes ExportPrivateKey mystore.ks JKS mystorepwd myalias mycert_priv.crt
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import sun.misc.BASE64Encoder;
public class ExportPrivateKey {
private File keystoreFile;
private String keyStoreType;
private char[] password;
private String alias;
private File exportedFile;
public static KeyPair getPrivateKey(KeyStore keystore, String alias, char[] password) {
try {
Key key=keystore.getKey(alias,password);
if(key instanceof PrivateKey) {
Certificate cert=keystore.getCertificate(alias);
PublicKey publicKey=cert.getPublicKey();
return new KeyPair(publicKey,(PrivateKey)key);
}
} catch (UnrecoverableKeyException e) {
} catch (NoSuchAlgorithmException e) {
} catch (KeyStoreException e) { }
return null;
}
public void export() throws Exception{
KeyStore keystore=KeyStore.getInstance(keyStoreType);
BASE64Encoder encoder=new BASE64Encoder();
keystore.load(new FileInputStream(keystoreFile),password);
KeyPair keyPair=getPrivateKey(keystore,alias,password);
PrivateKey privateKey=keyPair.getPrivate();
String encoded=encoder.encode(privateKey.getEncoded());
FileWriter fw=new FileWriter(exportedFile);
fw.write(“—–BEGIN PRIVATE KEY—–\n“);
fw.write(encoded);
fw.write(“\n“);
fw.write(“—–END PRIVATE KEY—–”);
fw.close();
}
public static void main(String args[]) throws Exception{
ExportPrivateKey export=new ExportPrivateKey();
export.keystoreFile=new File(args[0]);
export.keyStoreType=args[1];
export.password=args[2].toCharArray();
export.alias=args[3];
export.exportedFile=new File(args[4]);
export.export();
}
}