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
Related
We can create XML Digital Signature using RSA keys. But how do I use elliptic curve keys to sign xml files ? I get exception at this line xmlSignature.sign(domSignCtx) as -
javax.xml.crypto.MarshalException: Invalid ECParameterSpec
at java.xml.crypto/org.jcp.xml.dsig.internal.dom.DOMKeyValue$EC.marshalPublicKey(DOMKeyValue.java:493)
at java.xml.crypto/org.jcp.xml.dsig.internal.dom.DOMKeyValue.marshal(DOMKeyValue.java:121)
at java.xml.crypto/org.jcp.xml.dsig.internal.dom.DOMKeyInfo.marshal(DOMKeyInfo.java:207)
at java.xml.crypto/org.jcp.xml.dsig.internal.dom.DOMKeyInfo.marshal(DOMKeyInfo.java:197)
at java.xml.crypto/org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(DOMXMLSignature.java:233)
at java.xml.crypto/org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(DOMXMLSignature.java:333)
at com.test.GenerateXMLSignature.generateXMLDigitalSignature(GenerateXMLSignature.java:117)
at com.test.GenerateXMLSignature.main(GenerateXMLSignature.java:60)
I am trying to add digital signature to the existing xml document. I am reading private key and public key from .pem file. And I can generate and verify same for SignatureMethod.RSA_SHA256. But for ECDSA_SHA256 I am getting Invalid ECParameterSpec exception. Please suggest how to generate XML Signature for secp256k1 curve using SHA256WithECDSA algorithm in java.
using:
java version "15.0.1" 2020-10-20
Java(TM) SE Runtime Environment (build 15.0.1+9-18)
Java HotSpot(TM) 64-Bit Server VM (build 15.0.1+9-18, mixed mode, sharing)
Below is my source code:
package com.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.Key;
import java.security.KeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Collections;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class GenerateXMLSignature {
public static final String INPUT_FILE_NAME = "D:\\XML\\staff-dom.xml";
public static final String OUTPUT_FILE_NAME = "D:\\XML\\sample.xml";
public static final String CERT_FILE_PATH = "D:\\XML\\openssl-0.9.8k_X64\\bin\\cert.pem";
public static final String PRIVATE_KEY_FILE_PATH = "D:\\XML\\openssl-0.9.8k_X64\\bin\\pkcs8file.pem";
public static final String PUBLIC_KEY_FILE_PATH = "D:\\XML\\openssl-0.9.8k_X64\\bin\\PublicKey.pem";
public static void main(String[] args) throws Exception {
generateXMLDigitalSignature(INPUT_FILE_NAME, OUTPUT_FILE_NAME, PRIVATE_KEY_FILE_PATH, PUBLIC_KEY_FILE_PATH);
}
/**
* Method used to attach a generated digital signature to the existing document
*
* #param originalXmlFilePath
* #param destnSignedXmlFilePath
* #param privateKeyFilePath
* #param publicKeyFilePath
* #throws Exception
*/
public static void generateXMLDigitalSignature(String originalXmlFilePath, String destnSignedXmlFilePath,
String privateKeyFilePath, String publicKeyFilePath) throws Exception {
int addProvider = Security.addProvider(new BouncyCastleProvider());
System.out.println(addProvider);
String[] curves = Security.getProvider("SunEC").getProperty("AlgorithmParameters.EC SupportedCurves").split("\\|");
System.out.println(Arrays.toString(curves));
// Get the XML Document object
Document doc = getXmlDocument(originalXmlFilePath);
// Create XML Signature Factory
XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM");
PrivateKey privateKey = new KryptoUtil().readECPrivateKey(privateKeyFilePath);
System.out.println("Private Key===="+ privateKey);
DOMSignContext domSignCtx = new DOMSignContext((Key) privateKey, doc.getDocumentElement());
Reference ref = null;
SignedInfo signedInfo = null;
try {
ref = xmlSigFactory.newReference("", xmlSigFactory.newDigestMethod(DigestMethod.SHA256, null),
Collections.singletonList(
xmlSigFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)),
null, null);
signedInfo = xmlSigFactory.newSignedInfo(
xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE,
(C14NMethodParameterSpec) null),
xmlSigFactory.newSignatureMethod(SignatureMethod.ECDSA_SHA256, null), Collections.singletonList(ref));
System.out.println(signedInfo.getSignatureMethod().getAlgorithm());
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
} catch (InvalidAlgorithmParameterException ex) {
ex.printStackTrace();
}
// Pass the Public Key File Path
KeyInfo keyInfo = null;
try {
keyInfo = getKeyInfo(xmlSigFactory, publicKeyFilePath);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// Create a new XML Signature
XMLSignature xmlSignature = xmlSigFactory.newXMLSignature(signedInfo, keyInfo);
try {
// Sign the document
xmlSignature.sign(domSignCtx);
} catch (Exception ex) {
ex.printStackTrace();
}
// Store the digitally signed document int0 a location
storeSignedDoc(doc, destnSignedXmlFilePath);
}
/**
* Method used to get the KeyInfo
*
* #param xmlSigFactory
* #param publicKeyPath
* #return KeyInfo
* #throws Exception
* #throws CertificateException
*/
private static KeyInfo getKeyInfo(XMLSignatureFactory xmlSigFactory, String publicKeyPath) throws CertificateException, Exception {
KeyInfo keyInfo = null;
KeyValue keyValue = null;
PublicKey publicKey = new KryptoUtil().readECPublicKey(publicKeyPath);
System.out.println("Public Key===="+ publicKey);
KeyInfoFactory keyInfoFact = xmlSigFactory.getKeyInfoFactory();
try {
keyValue = keyInfoFact.newKeyValue(publicKey);
} catch (KeyException ex) {
ex.printStackTrace();
}
keyInfo = keyInfoFact.newKeyInfo(Collections.singletonList(keyValue));
return keyInfo;
}
private static Document getXmlDocument(String originalXmlFilePath) {
Document doc = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
try {
System.out.println(originalXmlFilePath);
doc = dbf.newDocumentBuilder().parse(new FileInputStream(originalXmlFilePath));
} catch (ParserConfigurationException ex) {
ex.printStackTrace();
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (SAXException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
return doc;
}
/*
* Method used to store the signed XMl document
*/
private static void storeSignedDoc(Document doc, String destnSignedXmlFilePath) {
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer trans = null;
try {
trans = transFactory.newTransformer();
} catch (TransformerConfigurationException ex) {
ex.printStackTrace();
}
try {
StreamResult streamRes = new StreamResult(new File(destnSignedXmlFilePath));
trans.transform(new DOMSource(doc), streamRes);
} catch (TransformerException ex) {
ex.printStackTrace();
}
System.out.println("XML file with attached digital signature generated successfully ...");
}
}
/**
* Method used to get the generated Private Key
*
* #param filePath of the PrivateKey file
* #return PrivateKey
* #throws Exception
* #throws GeneralSecurityException
*/
public PrivateKey readECPrivateKey(String filePath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
File f = new File(filePath);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String privatePem = temp.replace("-----BEGIN PRIVATE KEY-----\n", "");
privatePem = privatePem.replace("-----END PRIVATE KEY-----", "");
org.bouncycastle.util.encoders.Base64 b64 = new org.bouncycastle.util.encoders.Base64();
byte[] decoded = b64.decode(privatePem);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory factory = KeyFactory.getInstance("EC");
PrivateKey privateKey = factory.generatePrivate(spec);
System.out.println("PrivateKey===="+ privateKey);
return privateKey;
}
/**
* Method used to get the generated Public Key
*
* #param filePath of the PublicKey file
* #return PublicKey
* #throws IOException
* #throws NoSuchProviderException
* #throws InvalidKeySpecException
* #throws NoSuchAlgorithmException
*/
public PublicKey readECPublicKey(String filePath) throws IOException, NoSuchProviderException, Exception {
Security.addProvider(new BouncyCastleProvider());
File f = new File(filePath);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String publicKeyPEM = temp.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
org.bouncycastle.util.encoders.Base64 b64 = new org.bouncycastle.util.encoders.Base64();
byte[] decoded = b64.decode(publicKeyPEM);
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("EC", "BC");
return kf.generatePublic(spec);
}
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);
}
}
import android.util.Log;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Date;
import java.util.Iterator;
import static android.support.constraint.Constraints.TAG;
public class RSAEncryption {
static dataToBeEncrypt = "Hello";
static String publickey = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"mI0EXEr27gEEALybxOEubZ39PX1t+oU9iXhTTmqrc/+ha+J6TYd+yvYcNEBIOzPI\n" +
"1fG2Tz30zJYwT9uZOyW7LAGKRcsIH6p4SiKGaEYBLEWuCQrGfXPAAXaH+WNsKOmz\n" +
"SAZilPwugEcWknIUDZzfU0AD8PPCLqhJM6Ij7AJ/SNa2xUIsyUW+PwBdABEBAAG0\n" +
"BWplZXRziM4EEwEKADgWIQRp0+dcK5ZTXht9TIcw0CiFbn2j6gUCXEr27gIbAwUL\n" +
"CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRAw0CiFbn2j6um7A/9Ttzl8xHa4MvuF\n" +
"htOEAb6sXOk17B9BQyd3bkUCYtBjmFA3Q7XzWln5lm2QQTxW+AnlVJYwQS5zvVC0\n" +
"8WgPh6XP9sCi6/2ar748wpS8dL1wEAwaMOSYGEDCUpmW2uEGsrsAKfiMlKGhLiPC\n" +
"uUvPJ0x2jjHn9HHO1e9BES9tlkRPJLiNBFxK9u4BBADQtF6EajJMi2IPsVDR6PTX\n" +
"swjGgVbYKqwZRUwfLecJ95VeiQzznWUMpWFze4ESlAjCYeJf37voDazs6Ol5Uqz7\n" +
"7d5GurEk4kNjQp4Pm+chFQWLWU1seMghyJqsXCjWApy0lNJ2YPNbYKUqdJsZpJgT\n" +
"7qofPvEEPlGRRqzZxdN8oQARAQABiLYEGAEKACAWIQRp0+dcK5ZTXht9TIcw0CiF\n" +
"bn2j6gUCXEr27gIbDAAKCRAw0CiFbn2j6ky9BACgADAD/VIFIUjuQtNa4GEcAS0T\n" +
"vtJvsL26qW/Gohl5nb1ix7MLBwiH/l1Co6K52GKAVUrZBCnjkJW5zyGTKUkPGbGY\n" +
"Loh481phsvpYgXHqol7UQivBoF14EO1dNmn61QFRn7D3zhNirziOKQl3kLSuEdc5\n" +
"VpRF7ubkHL3jRrdQFA==\n" +
"=/lmV\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
public static void main (String[] args){
String encrypted = null;
try {
encrypted = encryptToFile(dataToBeEncrypt, publicKey);
} catch (Exception e) {
Log.i(TAG, "Exception: " + e);
}
return encrypted;
}
public static String encryptToFile(String inputdata, String keyFile) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte[] original = inputdata.getBytes();
InputStream secKey = new ByteArrayInputStream(keyFile.getBytes("UTF-8"));
byte[] encrypted = encrypt(original, readPublicKey(secKey), true, true);
return new String(encrypted);
}
private static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
PGPPublicKey k = null;
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);
Iterator rIt = pgpPub.getKeyRings();
while (rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (kIt.hasNext()) {
k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
break;
}
}
}
return k;
}
/**
* Simple PGP encryptor between byte[].
*
* #param clearData
* The test to be encrypted
* #param //passPhrase
* The pass phrase (key). This method assumes that the key is a
* simple pass phrase, and does not yet support RSA or more
* sophisiticated keying.
* #param //fileName
* File name. This is used in the Literal Data Packet (tag 11)
* which is really inly important if the data is to be related to
* a file to be recovered later. Because this routine does not
* know the source of the information, the caller can set
* something here for file name use that will be carried. If this
* routine is being used to encrypt SOAP MIME bodies, for
* example, use the file name from the MIME type, if applicable.
* Or anything else appropriate.
*
* #param armor
* #return encrypted data.
* #exception IOException
* #exception PGPException
* #exception NoSuchProviderException
*/
public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey, boolean withIntegrityCheck, boolean armor)
throws IOException, PGPException, NoSuchProviderException {
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
OutputStream out = encOut;
if (armor) {
out = new ArmoredOutputStream(out);
}
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedDataGenerator.ZIP);
OutputStream cos = comData.open(bOut); // open it with the final
// destination
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
// we want to generate compressed data. This might be a user option
// later,
// in which case we would pass in bOut.
OutputStream pOut = lData.open(cos, // the compressed output stream
PGPLiteralData.BINARY, "", // "filename" to store
clearData.length, // length of clear data
new Date() // current time
);
pOut.write(clearData);
lData.close();
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(),
"BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes); // obtain the actual bytes from the compressed stream
cOut.close();
out.close();
return encOut.toByteArray();
}
}
I was trying to achieve encryption using RSA algorithm with given public key and text to be encrypted. Below is my code which I was trying where I am passing my public key and string and getting the encrypted data. The problem is below code works fine when I run it as Java Application but when I use it in my android code I always get org.bouncycastle.openpgp.PGPException: Exception creating cipher as exception for line OutputStream cOut = cPk.open(out, bytes.length);. Please suggest me how can I make it work with my Android application.
Also,
OutputStream pOut = lData.open(cos, // the compressed output stream PGPLiteralData.BINARY, "", // "filename" to store clearData.length, // length of clear data new Date() // current time );
I am passing file name as null. Is this creating issue?
I expect Encrypted Message Without Version: BCPG v1.46, but I am getting org.bouncycastle.openpgp.PGPException: Exception creating cipher at line OutputStream cOut = cPk.open(out, bytes.length);
Editted
Exception:
org.bouncycastle.openpgp.PGPException: Exception creating cipher
at org.bouncycastle.openpgp.PGPEncryptedDataGenerator.open(Unknown Source)
at org.bouncycastle.openpgp.PGPEncryptedDataGenerator.open(Unknown Source)
at com.example.Resources.RSAEncryption.encrypt(RSAEncryption.java:157)
at com.example.Resources.RSAEncryption.encryptToFile(RSAEncryption.java:74)
at com.example.Resources.RSAEncryption.encryption(RSAEncryption.java:62)
at com.example.EQR.QRUserCredentials.onActivityResult(QRUserCredentials.java:140)
at android.app.Activity.dispatchActivityResult(Activity.java:6931)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4090)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4137)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1529)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.security.NoSuchAlgorithmException: Provider BC does not provide CAST5/CFB/NoPadding
at javax.crypto.Cipher.createCipher(Cipher.java:921)
at javax.crypto.Cipher.getInstance(Cipher.java:901)
... 17 more
Finally, I found the solution.
Instead of CAST5 I am using AES_128 PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.AES_128, withIntegrityCheck, new SecureRandom(),"BC");
Below is the working code
import android.util.Log;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Date;
import java.util.Iterator;
import static android.support.constraint.Constraints.TAG;
public class RSAEncryption {
static dataToBeEncrypt = "Hello";
static String publickey = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"mI0EXEr27gEEALybxOEubZ39PX1t+oU9iXhTTmqrc/+ha+J6TYd+yvYcNEBIOzPI\n" +
"1fG2Tz30zJYwT9uZOyW7LAGKRcsIH6p4SiKGaEYBLEWuCQrGfXPAAXaH+WNsKOmz\n" +
"SAZilPwugEcWknIUDZzfU0AD8PPCLqhJM6Ij7AJ/SNa2xUIsyUW+PwBdABEBAAG0\n" +
"BWplZXRziM4EEwEKADgWIQRp0+dcK5ZTXht9TIcw0CiFbn2j6gUCXEr27gIbAwUL\n" +
"CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRAw0CiFbn2j6um7A/9Ttzl8xHa4MvuF\n" +
"htOEAb6sXOk17B9BQyd3bkUCYtBjmFA3Q7XzWln5lm2QQTxW+AnlVJYwQS5zvVC0\n" +
"8WgPh6XP9sCi6/2ar748wpS8dL1wEAwaMOSYGEDCUpmW2uEGsrsAKfiMlKGhLiPC\n" +
"uUvPJ0x2jjHn9HHO1e9BES9tlkRPJLiNBFxK9u4BBADQtF6EajJMi2IPsVDR6PTX\n" +
"swjGgVbYKqwZRUwfLecJ95VeiQzznWUMpWFze4ESlAjCYeJf37voDazs6Ol5Uqz7\n" +
"7d5GurEk4kNjQp4Pm+chFQWLWU1seMghyJqsXCjWApy0lNJ2YPNbYKUqdJsZpJgT\n" +
"7qofPvEEPlGRRqzZxdN8oQARAQABiLYEGAEKACAWIQRp0+dcK5ZTXht9TIcw0CiF\n" +
"bn2j6gUCXEr27gIbDAAKCRAw0CiFbn2j6ky9BACgADAD/VIFIUjuQtNa4GEcAS0T\n" +
"vtJvsL26qW/Gohl5nb1ix7MLBwiH/l1Co6K52GKAVUrZBCnjkJW5zyGTKUkPGbGY\n" +
"Loh481phsvpYgXHqol7UQivBoF14EO1dNmn61QFRn7D3zhNirziOKQl3kLSuEdc5\n" +
"VpRF7ubkHL3jRrdQFA==\n" +
"=/lmV\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
public static void main (String[] args){
String encrypted = null;
try {
encrypted = encryptToFile(dataToBeEncrypt, publicKey);
} catch (Exception e) {
Log.i(TAG, "Exception: " + e);
}
return encrypted;
}
public static String encryptToFile(String inputdata, String keyFile) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte[] original = inputdata.getBytes();
InputStream secKey = new ByteArrayInputStream(keyFile.getBytes("UTF-8"));
byte[] encrypted = encrypt(original, readPublicKey(secKey), true, true);
return new String(encrypted);
}
private static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
PGPPublicKey k = null;
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);
Iterator rIt = pgpPub.getKeyRings();
while (rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (kIt.hasNext()) {
k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
break;
}
}
}
return k;
}
/**
* Simple PGP encryptor between byte[].
*
* #param clearData
* The test to be encrypted
* #param //passPhrase
* The pass phrase (key). This method assumes that the key is a
* simple pass phrase, and does not yet support RSA or more
* sophisiticated keying.
* #param //fileName
* File name. This is used in the Literal Data Packet (tag 11)
* which is really inly important if the data is to be related to
* a file to be recovered later. Because this routine does not
* know the source of the information, the caller can set
* something here for file name use that will be carried. If this
* routine is being used to encrypt SOAP MIME bodies, for
* example, use the file name from the MIME type, if applicable.
* Or anything else appropriate.
*
* #param armor
* #return encrypted data.
* #exception IOException
* #exception PGPException
* #exception NoSuchProviderException
*/
public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey, boolean withIntegrityCheck, boolean armor)
throws IOException, PGPException, NoSuchProviderException {
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
OutputStream out = encOut;
if (armor) {
out = new ArmoredOutputStream(out);
}
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedDataGenerator.ZIP);
OutputStream cos = comData.open(bOut); // open it with the final
// destination
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
// we want to generate compressed data. This might be a user option
// later,
// in which case we would pass in bOut.
OutputStream pOut = lData.open(cos, // the compressed output stream
PGPLiteralData.BINARY, "", // "filename" to store
clearData.length, // length of clear data
new Date() // current time
);
pOut.write(clearData);
lData.close();
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
PGPEncryptedData.AES_128, withIntegrityCheck, new SecureRandom(),
"BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes); // obtain the actual bytes from the compressed stream
cOut.close();
out.close();
return encOut.toByteArray();
}
}
I am generating Keypair for PGP Encryption using this class and saving the Files to the disk.
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Date;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.sig.Features;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
public class RSAGen {
public static void Generate(String ID,String PassPhrase , String FileName) throws Exception {
char pass[] = PassPhrase.toCharArray();
PGPKeyRingGenerator krgen = generateKeyRingGenerator(ID, pass);
// Generate public key ring, dump to file.
PGPPublicKeyRing pkr = krgen.generatePublicKeyRing();
ArmoredOutputStream pubout = new ArmoredOutputStream(new BufferedOutputStream(new FileOutputStream(FileName + ".asc")));
pkr.encode(pubout);
pubout.close();
// Generate private key, dump to file.
PGPSecretKeyRing skr = krgen.generateSecretKeyRing();
BufferedOutputStream secout = new BufferedOutputStream(new FileOutputStream(FileName + ".skr"));
skr.encode(secout);
secout.close();
}
public final static PGPKeyRingGenerator generateKeyRingGenerator(String id, char[] pass) throws Exception{
return generateKeyRingGenerator(id, pass, 0xc0);
}
// Note: s2kcount is a number between 0 and 0xff that controls the number of times to iterate the password hash before use. More
// iterations are useful against offline attacks, as it takes more time to check each password. The actual number of iterations is
// rather complex, and also depends on the hash function in use. Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
// you more iterations. As a rough rule of thumb, when using SHA256 as the hashing function, 0x10 gives you about 64
// iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0, or about 1 million iterations. The maximum you can go to is
// 0xff, or about 2 million iterations. I'll use 0xc0 as a default -- about 130,000 iterations.
public final static PGPKeyRingGenerator generateKeyRingGenerator(String id, char[] pass, int s2kcount) throws Exception {
// This object generates individual key-pairs.
RSAKeyPairGenerator kpg = new RSAKeyPairGenerator();
// Boilerplate RSA parameters, no need to change anything
// except for the RSA key-size (2048). You can use whatever key-size makes sense for you -- 4096, etc.
kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), new SecureRandom(), 2048, 12));
// First create the master (signing) key with the generator.
PGPKeyPair rsakp_sign = new BcPGPKeyPair(PGPPublicKey.RSA_SIGN, kpg.generateKeyPair(), new Date());
// Then an encryption subkey.
PGPKeyPair rsakp_enc = new BcPGPKeyPair(PGPPublicKey.RSA_ENCRYPT, kpg.generateKeyPair(), new Date());
// Add a self-signature on the id
PGPSignatureSubpacketGenerator signhashgen = new PGPSignatureSubpacketGenerator();
// Add signed metadata on the signature.
// 1) Declare its purpose
signhashgen.setKeyFlags(false, KeyFlags.SIGN_DATA|KeyFlags.CERTIFY_OTHER);
// 2) Set preferences for secondary crypto algorithms to use when sending messages to this key.
signhashgen.setPreferredSymmetricAlgorithms
(false, new int[] {
SymmetricKeyAlgorithmTags.AES_256,
SymmetricKeyAlgorithmTags.AES_192,
SymmetricKeyAlgorithmTags.AES_128
});
signhashgen.setPreferredHashAlgorithms
(false, new int[] {
HashAlgorithmTags.SHA256,
HashAlgorithmTags.SHA1,
HashAlgorithmTags.SHA384,
HashAlgorithmTags.SHA512,
HashAlgorithmTags.SHA224,
});
// 3) Request senders add additional checksums to the message (useful when verifying unsigned messages.)
signhashgen.setFeature(false, Features.FEATURE_MODIFICATION_DETECTION);
// Create a signature on the encryption subkey.
PGPSignatureSubpacketGenerator enchashgen = new PGPSignatureSubpacketGenerator();
// Add metadata to declare its purpose
enchashgen.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS|KeyFlags.ENCRYPT_STORAGE);
// Objects used to encrypt the secret key.
PGPDigestCalculator sha1Calc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1);
PGPDigestCalculator sha256Calc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA256);
// bcpg 1.48 exposes this API that includes s2kcount. Earlier versions use a default of 0x60.
PBESecretKeyEncryptor pske = (new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha256Calc, s2kcount)).build(pass);
// Finally, create the keyring itself. The constructor takes parameters that allow it to generate the self signature.
PGPKeyRingGenerator keyRingGen =
new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsakp_sign,
id, sha1Calc, signhashgen.generate(), null,
new BcPGPContentSignerBuilder(rsakp_sign.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), pske);
// Add our encryption subkey, together with its signature.
keyRingGen.addSubKey(rsakp_enc, enchashgen.generate(), null);
return keyRingGen;
}
}
i found this class but it won't work with the latest version which is :
-bcpg-jdk15on-1.54
-bcprov-jdk15on-1.54
i got lot's of error message when using this class :
https://github.com/matthewmccullough/encryption-jvm-bootcamp/blob/master/bc-pgp/src/main/java/com/ambientideas/cryptography/KeyBasedFileProcessorUtil.java
package com.ambientideas.cryptography;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Iterator;
//Matthew McCullough: Rediculous as it sounds, many of the functions such as
// private static void encryptFile()
// private static void decryptFile()
// private static PGPPrivateKey findSecretKey()
// private static PGPPublicKey readPublicKey()
// for PGP in BouncyCastle are private, thus making it unbearable to use
// in a simple manner against whole file contents. Thus, this class is duplicated from the
// core of BouncyCastle (KeyBasedFileProcessor being the original name), but with the
// methods made public so that the test can use them.
/**
* A simple utility class that encrypts/decrypts public key based
* encryption files.
* <p>
* To encrypt a file: KeyBasedFileProcessor -e [-a|-ai] fileName publicKeyFile.<br>
* If -a is specified the output file will be "ascii-armored".
* If -i is specified the output file will be have integrity checking added.
* <p>
* To decrypt: KeyBasedFileProcessor -d fileName secretKeyFile passPhrase.
* <p>
* Note 1: this example will silently overwrite files, nor does it pay any attention to
* the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase
* will have been used.
* <p>
* Note 2: if an empty file name has been specified in the literal data object contained in the
* encrypted packet a file with the name filename.out will be generated in the current working directory.
*/
public class KeyBasedFileProcessorUtil
{
/**
* A simple routine that opens a key ring file and loads the first available key suitable for
* encryption.
*
* #param in
* #return
* #throws IOException
* #throws PGPException
*/
public static PGPPublicKey readPublicKey(
InputStream in)
throws IOException, PGPException
{
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);
//
// we just loop through the collection till we find a key suitable for encryption, in the real
// world you would probably want to be a bit smarter about this.
//
//
// iterate through the key rings.
//
Iterator<?> rIt = pgpPub.getKeyRings();
while (rIt.hasNext())
{
PGPPublicKeyRing kRing = (PGPPublicKeyRing)rIt.next();
Iterator<?> kIt = kRing.getPublicKeys();
while (kIt.hasNext())
{
PGPPublicKey k = (PGPPublicKey)kIt.next();
if (k.isEncryptionKey())
{
return k;
}
}
}
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
/**
* Search a secret key ring collection for a secret key corresponding to
* keyID if it exists.
*
* #param pgpSec a secret key ring collection.
* #param keyID keyID we want.
* #param pass passphrase to decrypt secret key with.
* #return
* #throws PGPException
* #throws NoSuchProviderException
*/
public static PGPPrivateKey findSecretKey(
PGPSecretKeyRingCollection pgpSec,
long keyID,
char[] pass)
throws PGPException, NoSuchProviderException
{
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null)
{
return null;
}
return pgpSecKey.extractPrivateKey(pass, "BC");
}
/**
* decrypt the passed in message stream
*/
public static void decryptFile(
InputStream in,
InputStream keyIn,
char[] passwd,
String defaultFileName,
String outputPath)
throws Exception
{
in = PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
//
// the first object might be a PGP marker packet.
//
if (o instanceof PGPEncryptedDataList)
{
enc = (PGPEncryptedDataList)o;
}
else
{
enc = (PGPEncryptedDataList)pgpF.nextObject();
}
//
// find the secret key
//
Iterator <?> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(keyIn));
while (sKey == null && it.hasNext())
{
pbe = (PGPPublicKeyEncryptedData)it.next();
sKey = findSecretKey(pgpSec, pbe.getKeyID(), passwd);
}
if (sKey == null)
{
throw new IllegalArgumentException("secret key for message not found.");
}
InputStream clear = pbe.getDataStream(sKey, "BC");
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData)
{
PGPCompressedData cData = (PGPCompressedData)message;
PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData)
{
PGPLiteralData ld = (PGPLiteralData)message;
String outFileName = ld.getFileName();
if (ld.getFileName().length() == 0)
{
outFileName = defaultFileName;
}
//MJM: Enhancement to allow targeting of output folder for decrypted files
if (outputPath == null || outputPath.length() > 0) {
outFileName = outputPath + outFileName;
}
FileOutputStream fOut = new FileOutputStream(outFileName);
InputStream unc = ld.getInputStream();
int ch;
while ((ch = unc.read()) >= 0)
{
fOut.write(ch);
}
}
else if (message instanceof PGPOnePassSignatureList)
{
throw new PGPException("encrypted message contains a signed message - not literal data.");
}
else
{
throw new PGPException("message is not a simple encrypted file - type unknown.");
}
if (pbe.isIntegrityProtected())
{
if (!pbe.verify())
{
System.err.println("message failed integrity check");
}
else
{
System.err.println("message integrity check passed");
}
}
else
{
System.err.println("no message integrity check");
}
}
public static void encryptFile(
OutputStream out,
String fileName,
PGPPublicKey encKey,
boolean armor,
boolean withIntegrityCheck)
throws IOException, NoSuchProviderException
{
if (armor)
{
out = new ArmoredOutputStream(out);
}
try
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedData.ZIP);
PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, new File(fileName));
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), "BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
out.close();
}
catch (PGPException e)
{
System.err.println(e);
if (e.getUnderlyingException() != null)
{
e.getUnderlyingException().printStackTrace();
}
}
}
public static void main(
String[] args)
throws Exception
{
Security.addProvider(new BouncyCastleProvider());
if (args.length == 0)
{
System.err.println("usage: KeyBasedFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
return;
}
if (args[0].equals("-e"))
{
if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia"))
{
FileInputStream keyIn = new FileInputStream(args[3]);
FileOutputStream out = new FileOutputStream(args[2] + ".asc");
encryptFile(out, args[2], readPublicKey(keyIn), true, (args[1].indexOf('i') > 0));
}
else if (args[1].equals("-i"))
{
FileInputStream keyIn = new FileInputStream(args[3]);
FileOutputStream out = new FileOutputStream(args[2] + ".bpg");
encryptFile(out, args[2], readPublicKey(keyIn), false, true);
}
else
{
FileInputStream keyIn = new FileInputStream(args[2]);
FileOutputStream out = new FileOutputStream(args[1] + ".bpg");
encryptFile(out, args[1], readPublicKey(keyIn), false, false);
}
}
else if (args[0].equals("-d"))
{
FileInputStream in = new FileInputStream(args[1]);
FileInputStream keyIn = new FileInputStream(args[2]);
decryptFile(in, keyIn, args[3].toCharArray(), new File(args[1]).getName() + ".out", null);
}
else
{
System.err.println("usage: KeyBasedFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
}
}
}
Error MEssages
here is the error messages got when running in intelliJ
I had similar issue, then i use THIS implementation for encrypting or decrypting PGP files and it worked fine.
Using the standard modern Java Core classes that have the JCE included (e.g. Java 1.8_303+), BouncyCastle Core, and Bouncy Castle provider, I have developed a Spring-based Service that can handle PGP encryption and decryption from public/private keys contained within Resource files. If you are not using Spring, you can strip out the Spring specific code and just leverage the public encrypt/decrypt methods along with the private support methods:
package com.your.organization.impl;
import com.your.organization.exception.EncryptionException; // Your own Exception class
import com.your.organization.service.PgpEncryptionService; // Your own Interface class
import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Iterator;
#Service("pgpEncryptionService")
public final class PgpEncryptionServiceImpl implements PgpEncryptionService {
#PostConstruct
public void initializeSecurityProviders() {
// Add the Bouncy Castle security Provider to the JVM
Security.addProvider(new BouncyCastleProvider());
}
/**
* Encrypts a cleared message {#link String} using the classpath PGPPublicKey using
* {#link ArmoredOutputStream} to further protect the encrypted message.
*
* #param message {#link String}
* #return Encrypted String with, or without, armoring
* #throws EncryptionException is thrown if the {#link PGPEncryptedDataGenerator} could not be initialized
* from the provided PGPPublicKey or if the encoded message {#link OutputStream}
* could not be opened
*/
public String encrypt(String message) throws EncryptionException {
/*
* Initialize an OutputStream or ArmoredOutputStream for the encrypted message based on the armor
* function input
*/
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
OutputStream armoredOutputStream = byteArrayOutputStream;
armoredOutputStream = new ArmoredOutputStream(armoredOutputStream);
// Initialize and configure the encryption generator using the provided PGPPublicKey
PGPEncryptedDataGenerator pgpEncryptedDataGenerator = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setSecureRandom(new SecureRandom())
.setProvider("BC"));
pgpEncryptedDataGenerator.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(getPublicKey())
.setProvider("BC"));
// Convert message String to byte[] using standard UTF-8
byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
// Open the PGPEncryptedDataGenerator from the ArmoredOutputStream initialized to the message body length
OutputStream encryptedOutputStream;
try {
encryptedOutputStream = pgpEncryptedDataGenerator.open(armoredOutputStream, messageBytes.length);
} catch (IOException | PGPException e) {
throw new EncryptionException("Could not open an OutputStream from the PGPEncryptedDataGenerator " +
"using the provided message body", e);
}
// Write the encrypted message to the encryptedOutputStream
try {
encryptedOutputStream.write(messageBytes);
} catch (IOException e) {
throw new EncryptionException("Could not write the message body to the encrypted OutputStream", e);
} finally {
// Close the encrypted message OutputStream
try {
encryptedOutputStream.close();
} catch (IOException e) {
// TODO: Log this
}
// Close the ArmoredOutputStream
try {
armoredOutputStream.close();
} catch (IOException e) {
// TODO: Log this
}
}
// Return the encrypted message OutputStream to a String
return byteArrayOutputStream.toString();
}
/**
* Decrypts an encrypted message {#link String} using the {#link PGPSecretKey} on the classpath and its
* password {#link String}
*
* #param encryptedMessage {#link String}
* #param password {#link String}
* #return String
* #throws EncryptionException is thrown if an encrypted message InputStream cannot be initialized from the
* encryptedMessage {#link String}, if the PGPEncryptedDataList from that stream
* contains no data, or if the password {#link String} for the
* {#link PGPSecretKey} is incorrect
*/
public String decrypt(String encryptedMessage, String password) throws EncryptionException {
// Convert the encrypted String into an InputStream
InputStream encryptedStream = new ByteArrayInputStream(encryptedMessage.getBytes(StandardCharsets.UTF_8));
try {
encryptedStream = PGPUtil.getDecoderStream(encryptedStream);
} catch (IOException e) {
throw new EncryptionException("Could not initialize the DecoderStream", e);
}
// Retrieve the PGPEncryptedDataList from the encryptedStream
JcaPGPObjectFactory jcaPGPObjectFactory = new JcaPGPObjectFactory(encryptedStream);
PGPEncryptedDataList pgpEncryptedDataList;
/*
* Evaluate the first object for a leading PGP marker packet and then return the encrypted
* message body as a PGPEncryptedDataList
*/
try {
Object nextDataObject = jcaPGPObjectFactory.nextObject();
if (nextDataObject instanceof PGPEncryptedDataList) {
pgpEncryptedDataList = (PGPEncryptedDataList) nextDataObject;
} else {
pgpEncryptedDataList = (PGPEncryptedDataList) jcaPGPObjectFactory.nextObject();
}
} catch (IOException e) {
throw new EncryptionException("Could not retrieve the encrupted message body", e);
}
// Retrieve the public key encrypted data from the encrypted message body
PGPPublicKeyEncryptedData pgpPublicKeyEncryptedData =
(PGPPublicKeyEncryptedData) pgpEncryptedDataList.getEncryptedDataObjects().next();
// Use the PGPPublicKeyEncryptedData and Secret Key password to decrypt the encoded message
InputStream decryptedInputStream;
try {
decryptedInputStream =
pgpPublicKeyEncryptedData.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider("BC")
.build(getPrivateKey(getSecretKey(), password)));
} catch (PGPException e) {
throw new EncryptionException("Could not decrypt the encoded message from the application " +
"Secret Key or the embedded Private Key", e);
}
// Convert the InputStream of the decrypted message to a String
try {
return IOUtils.toString(decryptedInputStream, StandardCharsets.UTF_8.name());
} catch (IOException e) {
throw new EncryptionException("Could not convert the decrypted InputStream to a UTF-8 String", e);
}
}
/**
* Helper method for retrieving the {#link PGPPublicKey} from the application classpath.
*
* #return PGPPublicKey
* #throws EncryptionException is thrown in the event that the PGP Public Key file does not contain a
* Public Key or if the Public Key cannot be located on the file system
*/
private PGPPublicKey getPublicKey() throws EncryptionException {
// Retrieve the application PGP public key file from the classpath
File publicKeyFile;
try {
publicKeyFile = new ClassPathResource("keys/yourpublickey-pub.asc").getFile();
} catch (IOException e) {
throw new EncryptionException("Could not retrieve the PGP Public Key from the classpath", e);
}
// Read Public Key from the file
FileInputStream pubKey;
try {
pubKey = new FileInputStream(publicKeyFile);
} catch (FileNotFoundException e) {
throw new EncryptionException("Could not retrieve the PGP Public Key from the file system", e);
}
// Load PGPPublicKey FileInputStream into the PGPPublicKeyRingCollection
PGPPublicKeyRingCollection pgpPub;
try {
pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(pubKey),
new JcaKeyFingerprintCalculator());
} catch (IOException | PGPException e) {
throw new EncryptionException("Could not initialize the PGPPublicKeyRingCollection", e);
}
// Retrieve Public Key and evaluate if for the encryption key
Iterator<PGPPublicKeyRing> keyRingIter = pgpPub.getKeyRings();
while (keyRingIter.hasNext()) {
PGPPublicKeyRing keyRing = keyRingIter.next();
Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
while (keyIter.hasNext()) {
PGPPublicKey key = keyIter.next();
if (key.isEncryptionKey()) {
return key;
}
}
}
throw new EncryptionException("The application PGPPublicKey is not an allowable encryption key");
}
/**
* Helper method for retrieving the signing key {#link PGPSecretKey} from the classpath.
*
* #return Signing key {#link PGPSecretKey}
* #throws EncryptionException is thrown if the Secret Key is not a signing key or if the Secret Key file
* could not be located on the file system
*/
private PGPSecretKey getSecretKey() throws EncryptionException {
// Retrieve the application PGP secret key file from the classpath
File secretKeyFile;
try {
secretKeyFile = new ClassPathResource("keys/yoursecretkey-sec.asc").getFile();
} catch (IOException e) {
throw new EncryptionException("Could not retrieve the PGP Secret Key from the classpath", e);
}
// Read Secret Key file and load it into a PGPPublicKeyRingCollection for evaluation
FileInputStream secKey;
try {
secKey = new FileInputStream(secretKeyFile);
} catch (FileNotFoundException e) {
throw new EncryptionException("Could not retrieve the PGP Secret Key from the file system", e);
}
// Load PGPSecretKey FileInputStream into the PGPSecretKeyRingCollection
PGPSecretKeyRingCollection pgpSec;
try {
pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(secKey), new JcaKeyFingerprintCalculator());
} catch (IOException | PGPException e) {
throw new EncryptionException("Could not initialize the PGPSecretKeyRingCollection", e);
}
// Retrieve signing Secret Key
Iterator<PGPSecretKeyRing> secretKeyRingIterator = pgpSec.getKeyRings();
while (secretKeyRingIterator.hasNext()) {
PGPSecretKeyRing keyRing = secretKeyRingIterator.next();
Iterator<PGPSecretKey> keyIter = keyRing.getSecretKeys();
while (keyIter.hasNext()) {
PGPSecretKey key = keyIter.next();
if (key.isSigningKey()) {
return key;
}
}
}
throw new EncryptionException("The application PGPSecretKey is not a signing key");
}
/**
* Retrieves the {#link PGPPrivateKey} from the provided {#link PGPSecretKey} and its password.
*
* #param secretKey {#link PGPSecretKey}
* #param password {#link String}
* #return PGPPrivateKey
* #throws EncryptionException is thrown in the event that the password for the {#link PGPSecretKey}
* is incorrect
*/
private PGPPrivateKey getPrivateKey(PGPSecretKey secretKey, String password) throws EncryptionException {
PBESecretKeyDecryptor decryptorFactory = new BcPBESecretKeyDecryptorBuilder(
new BcPGPDigestCalculatorProvider()).build(password.toCharArray());
try {
return secretKey.extractPrivateKey(decryptorFactory);
} catch (PGPException e) {
throw new EncryptionException("Could not extract the Private Key from the application Secret Key", e);
}
}
}
I had issues using the implementation recommended by #TomazStoiljkovic,
so I ended up using this one.
I first had some issues when generating the keys with gpg --gen-key.
It is because the default kind of key used by gpg is ECC (sign and encrypt). And this implementation does not support it. Instead, use gpg --full-generate-key. Then when prompted :
Please select what kind of key you want:
(1) RSA and RSA
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(9) ECC (sign and encrypt) *default*
(10) ECC (sign only)
Select (1) or (2) or (4).
Then export to file using:
gpg --output key.pub --armor --export <username>; gpg --output key.prv --armor --export-secret-key <username>;
When I call this, it successfully encrypts my string but the output of the decrypted text is blank. I get no errors and the byteArray of the output string is of the correct lenth (102), however it's just 102 zeros. This is adapted from the KeyBasedFileProcessor example but is attempting to be stream/string based instead of file based.
package com.common.security.pgp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Date;
import java.util.Iterator;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
/**
* A simple utility class that encrypts/decrypts public key based encryption
* files.
* <p>
* To encrypt a file: KeyBasedFileProcessor -e [-a|-ai] fileName publicKeyFile.<br>
* If -a is specified the output file will be "ascii-armored". If -i is
* specified the output file will be have integrity checking added.
* <p>
* To decrypt: KeyBasedFileProcessor -d fileName secretKeyFile passPhrase.
* <p>
* Note 1: this example will silently overwrite files, nor does it pay any
* attention to the specification of "_CONSOLE" in the filename. It also expects
* that a single pass phrase will have been used.
* <p>
* Note 2: if an empty file name has been specified in the literal data object
* contained in the encrypted packet a file with the name filename.out will be
* generated in the current working directory.
*/
public class PgpEncryption3 {
/**
* A simple routine that opens a key ring file and loads the first available
* key suitable for encryption.
*
* #param in
* #return
* #throws IOException
* #throws PGPException
*/
private static PGPPublicKey readPublicKey(InputStream in)
throws IOException, PGPException {
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);
//
// we just loop through the collection till we find a key suitable for
// encryption, in the real
// world you would probably want to be a bit smarter about this.
//
//
// iterate through the key rings.
//
Iterator rIt = pgpPub.getKeyRings();
while (rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (kIt.hasNext()) {
PGPPublicKey k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
return k;
}
}
}
throw new IllegalArgumentException(
"Can't find encryption key in key ring.");
}
/**
* Search a secret key ring collection for a secret key corresponding to
* keyID if it exists.
*
* #param pgpSec
* a secret key ring collection.
* #param keyID
* keyID we want.
* #param pass
* passphrase to decrypt secret key with.
* #return
* #throws PGPException
* #throws NoSuchProviderException
*/
private static PGPPrivateKey findSecretKey(
PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass)
throws PGPException, NoSuchProviderException {
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(pass, "BC");
}
/**
* decrypt the passed in message stream
*/
private static void decryptFile(InputStream in, InputStream keyIn,
char[] passwd, String defaultFileName, OutputStream out) throws Exception {
in = PGPUtil.getDecoderStream(in);
try {
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
//
// the first object might be a PGP marker packet.
//
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
//
// find the secret key
//
Iterator it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(keyIn));
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = findSecretKey(pgpSec, pbe.getKeyID(), passwd);
}
if (sKey == null) {
throw new IllegalArgumentException(
"secret key for message not found.");
}
InputStream clear = pbe.getDataStream(sKey, "BC");
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
PGPObjectFactory pgpFact = new PGPObjectFactory(cData
.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData) {
System.out.println(message);
PGPLiteralData ld = (PGPLiteralData) message;
System.out.println(ld.getFileName());
System.out.println(ld.getDataStream());
InputStream unc = ld.getInputStream();
int ch;
while ((ch = unc.read()) >= 0) {
System.out.println(ch);
out.write(ch);
}
} else if (message instanceof PGPOnePassSignatureList) {
throw new PGPException(
"encrypted message contains a signed message - not literal data.");
} else {
throw new PGPException(
"message is not a simple encrypted file - type unknown.");
}
if (pbe.isIntegrityProtected()) {
if (!pbe.verify()) {
System.err.println("message failed integrity check");
} else {
System.err.println("message integrity check passed");
}
} else {
System.err.println("no message integrity check");
}
} catch (PGPException e) {
System.err.println(e);
if (e.getUnderlyingException() != null) {
e.getUnderlyingException().printStackTrace();
}
}
}
public static void writeStreamToLiteralData(OutputStream out,
char fileType, byte[] data, String fileName, Date modDate) throws IOException {
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(out, fileType, fileName, data.length, modDate);
byte[] buf = new byte[4096];
ByteArrayInputStream in = new ByteArrayInputStream(buf);
int len;
while ((len = in.read(buf)) > 0) {
pOut.write(buf, 0, len);
}
lData.close();
in.close();
}
private static void encryptFile(OutputStream out, byte[] data,String fileName,
PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck, Date modDate)
throws IOException, NoSuchProviderException {
if (armor) {
out = new ArmoredOutputStream(out);
}
try {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
writeStreamToLiteralData(bOut,PGPLiteralData.TEXT, data, fileName,modDate);
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
PGPEncryptedData.CAST5, withIntegrityCheck,
new SecureRandom(), "BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
out.close();
} catch (PGPException e) {
System.err.println(e);
if (e.getUnderlyingException() != null) {
e.getUnderlyingException().printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
Date modDate = new Date();
String dataToEncrypt = "THIS IS SOME TEXTTHIS IS SOME TEXTTHIS IS SOME TEXTTHIS IS SOME TEXTTHIS IS SOME TEXTTHIS IS SOME TEXT";
System.out.println(dataToEncrypt.length());
byte[] data = dataToEncrypt.getBytes();
String fileName = "blah.txt";
ByteArrayOutputStream out = new ByteArrayOutputStream();
FileInputStream pKeyIn = new FileInputStream("/Users/me/blah.pub.key");
encryptFile(out, data, fileName,readPublicKey(pKeyIn), true, false,modDate);
System.out.println(new String(out.toByteArray()));
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
System.out.println(in);
FileInputStream sKeyIn = new FileInputStream("/Users/me/blah.sec.key");
ByteArrayOutputStream decOut = new ByteArrayOutputStream();
decryptFile(in, sKeyIn, "mypass".toCharArray(), "blah.txt", decOut);
System.out.println(decOut.toByteArray().length);
System.out.println(new String(decOut.toByteArray()));
}
}
The full working example
package com.common.security.pgp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Date;
import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
/**
* Simple routine to encrypt and decrypt using a Public and Private key with passphrase. This service
* routine provides the basic PGP services between byte arrays.
*
*/
public class PgpEncryption {
private static PGPPrivateKey findSecretKey(
PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass)
throws PGPException, NoSuchProviderException {
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(pass, "BC");
}
/**
* decrypt the passed in message stream
*
* #param encrypted
* The message to be decrypted.
* #param passPhrase
* Pass phrase (key)
*
* #return Clear text as a byte array. I18N considerations are not handled
* by this routine
* #exception IOException
* #exception PGPException
* #exception NoSuchProviderException
*/
public static byte[] decrypt(byte[] encrypted, InputStream keyIn, char[] password)
throws IOException, PGPException, NoSuchProviderException {
InputStream in = new ByteArrayInputStream(encrypted);
in = PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc = null;
Object o = pgpF.nextObject();
//
// the first object might be a PGP marker packet.
//
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
//
// find the secret key
//
Iterator it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(keyIn));
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = findSecretKey(pgpSec, pbe.getKeyID(), password);
}
if (sKey == null) {
throw new IllegalArgumentException(
"secret key for message not found.");
}
InputStream clear = pbe.getDataStream(sKey, "BC");
PGPObjectFactory pgpFact = new PGPObjectFactory(clear);
PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject();
pgpFact = new PGPObjectFactory(cData.getDataStream());
PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
InputStream unc = ld.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
int ch;
while ((ch = unc.read()) >= 0) {
out.write(ch);
}
byte[] returnBytes = out.toByteArray();
out.close();
return returnBytes;
}
/**
* Simple PGP encryptor between byte[].
*
* #param clearData
* The test to be encrypted
* #param passPhrase
* The pass phrase (key). This method assumes that the key is a
* simple pass phrase, and does not yet support RSA or more
* sophisiticated keying.
* #param fileName
* File name. This is used in the Literal Data Packet (tag 11)
* which is really inly important if the data is to be related to
* a file to be recovered later. Because this routine does not
* know the source of the information, the caller can set
* something here for file name use that will be carried. If this
* routine is being used to encrypt SOAP MIME bodies, for
* example, use the file name from the MIME type, if applicable.
* Or anything else appropriate.
*
* #param armor
*
* #return encrypted data.
* #exception IOException
* #exception PGPException
* #exception NoSuchProviderException
*/
public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey,
String fileName,boolean withIntegrityCheck, boolean armor)
throws IOException, PGPException, NoSuchProviderException {
if (fileName == null) {
fileName = PGPLiteralData.CONSOLE;
}
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
OutputStream out = encOut;
if (armor) {
out = new ArmoredOutputStream(out);
}
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedDataGenerator.ZIP);
OutputStream cos = comData.open(bOut); // open it with the final
// destination
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
// we want to generate compressed data. This might be a user option
// later,
// in which case we would pass in bOut.
OutputStream pOut = lData.open(cos, // the compressed output stream
PGPLiteralData.BINARY, fileName, // "filename" to store
clearData.length, // length of clear data
new Date() // current time
);
pOut.write(clearData);
lData.close();
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(),
"BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes); // obtain the actual bytes from the compressed stream
cOut.close();
out.close();
return encOut.toByteArray();
}
private static PGPPublicKey readPublicKey(InputStream in)
throws IOException, PGPException {
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);
//
// we just loop through the collection till we find a key suitable for
// encryption, in the real
// world you would probably want to be a bit smarter about this.
//
//
// iterate through the key rings.
//
Iterator rIt = pgpPub.getKeyRings();
while (rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (kIt.hasNext()) {
PGPPublicKey k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
return k;
}
}
}
throw new IllegalArgumentException(
"Can't find encryption key in key ring.");
}
public static byte[] getBytesFromFile(File file) throws IOException {
InputStream is = new FileInputStream(file);
// Get the size of the file
long length = file.length();
if (length > Integer.MAX_VALUE) {
// File is too large
}
// Create the byte array to hold the data
byte[] bytes = new byte[(int)length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file "+file.getName());
}
// Close the input stream and return bytes
is.close();
return bytes;
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte[] original = "Hello world".getBytes();
System.out.println("Starting PGP test");
FileInputStream pubKey = new FileInputStream("/Users/me/pub.key");
byte[] encrypted = encrypt(original, readPublicKey(pubKey), null,
true, true);
FileOutputStream dfis = new FileOutputStream("/Users/me/enc.asc");
dfis.write(encrypted);
dfis.close();
byte[] encFromFile = getBytesFromFile(new File("/Users/me/enc.asc"));
FileInputStream secKey = new FileInputStream("/Users/me/sec.key");
System.out.println("\nencrypted data = '" + new String(encrypted) + "'");
byte[] decrypted = decrypt(encFromFile, secKey, "passphrase".toCharArray());
System.out.println("\ndecrypted data = '" + new String(decrypted) + "'");
}
}