I am receiving the following exception regarding PKCS12 certificate
1. unwrapping private key
2. illegal key size
The following is my actual exception which I received.
Exception in thread "main" java.io.IOException: exception unwrapping private key - java.security.InvalidKeyException: Illegal key size
at org.bouncycastle.jce.provider.JDKPKCS12KeyStore.unwrapKey(Unknown Source)
at org.bouncycastle.jce.provider.JDKPKCS12KeyStore.engineLoad(Unknown Source)
at java.security.KeyStore.load(Unknown Source)
at Signatures.signPdfFirstTime(Signatures.java:103)
at Signatures.main(Signatures.java:229)
This is my actual code on which I am working
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://Proj1//hello.pdf";
/** The resulting PDF */
public static String SIGNED1 = "C://Proj1//signature_1.pdf";
/** The resulting PDF */
public static String SIGNED2 = "C://Proj1//signature_2.pdf";
/** Info after verification of a signed PDF */
public static String VERIFICATION = "results/part3/chapter12/verify.txt";
/** The resulting PDF */
public static String REVISION = "results/part3/chapter12/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://Proj1//keystore.properties";
/** Some properties used when signing. */
public static Properties properties = new Properties();
/** One of the resources. */
public static final String RESOURCE = "C://Proj1//logo.jpeg";
/**
* 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");
char[] str="mysecret".toCharArray();
String path="C://Proj1//keystore.p12";
//String keystore_password = properties.getProperty("PASSWORD");
String key_password="C://Proj1//keystore.propertise";
// String key_password = properties.getProperty("PASSWORD");
KeyStore ks = KeyStore.getInstance("PKCS12","BC");
ks.load(new FileInputStream(path), str);
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 = "C://Proj1//.keystore";
String keystore_password = "ram007";
String key_password = "ram0075";
String alias = "agreeya";
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());
properties.load(new FileInputStream(PATH));
Signatures signatures = new Signatures();
signatures.createPdf(ORIGINAL);
signatures.signPdfFirstTime(ORIGINAL, SIGNED1);
signatures.signPdfSecondTime(SIGNED1, SIGNED2);
// signatures.verifySignatures();
// signatures.extractFirstRevision();
}
}
Did you read Digital signatures for PDF documents?
I quote:
Don’t panic if you get an InvalidKeyException
saying that the key size is invalid. Due to import control restrictions by the governments of a few
countries, the encryption libraries shipped by default with the Java SDK restrict the length, and as a
result the strength, of encryption keys.
If you want to avoid this problem, you need to replace the default security JARs in your Java
installation with the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files.
These JARs are available for download from http://java.oracle.com/ in eligible countries.
Did you install the Java Cryptography Extension?
Related
I am using iText 5 Java for external signing. First I create signature appearance, calculate the hash for signed attributes and leave empty place for the signature. Later when I get the signed hash from client, I insert it the PDF via MakeSignature.signDeferred.
But PDF reader is showing the signature as invalid. Complaining the PDF has been modified.
Here is the code used for signing. I have removed a lot of functioning code to keep the code at bare essentials.
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import java.io.*;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class MklTest {
static String thisHash;
static class MyExternalSignatureContainer implements ExternalSignatureContainer {
protected byte[] sig;
public MyExternalSignatureContainer(byte[] sig) {
this.sig = sig;
}
public byte[] sign(InputStream is) {
return sig;
}
#Override
public void modifySigningDictionary(PdfDictionary signDic) {
}
}
static class EmptyContainer implements ExternalSignatureContainer {
public EmptyContainer() {
}
public byte[] sign(InputStream is) {
ExternalDigest digest = hashAlgorithm1 -> DigestAlgorithms.getMessageDigest(hashAlgorithm1, null);
try {
byte[] hash = DigestAlgorithms.digest(is, digest.getMessageDigest("SHA256"));
thisHash = Hex.encodeHexString(hash);
return new byte[0];
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
#Override
public void modifySigningDictionary(PdfDictionary pdfDictionary) {
pdfDictionary.put(PdfName.FILTER, PdfName.ADOBE_PPKMS);
pdfDictionary.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
}
}
public static String emptySignature(String src, String dest, String fieldname, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
Calendar cal = GregorianCalendar.getInstance();
cal.add(Calendar.MINUTE, 10);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, fieldname);
appearance.setCertificate(chain[0]);
appearance.setReason("Nice");
appearance.setLocation("Delhi");
appearance.setSignDate(cal);
ExternalSignatureContainer external = new EmptyContainer();
MakeSignature.signExternalContainer(appearance, external, 8192);
os.close();
reader.close();
return thisHash;
}
public static Certificate getCert() throws CertificateException {
String cert = ""; // the cert we get from client
ByteArrayInputStream userCertificate = new ByteArrayInputStream(Base64.decodeBase64(cert));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return cf.generateCertificate(userCertificate);
}
private static ExternalDigest getDigest() {
return new ExternalDigest() {
public MessageDigest getMessageDigest(String hashAlgorithm)
throws GeneralSecurityException {
return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
}
};
}
public static void createSignature(String src, String dest, String fieldname, byte[] signature) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
ExternalSignatureContainer external = new MyExternalSignatureContainer(signature);
MakeSignature.signDeferred(reader, fieldname, os, external);
reader.close();
os.close();
}
public static void main(String[] args) throws Exception {
Certificate cert = getCert();
Certificate[] chain = {cert};
String src = "/home/spooderman/Downloads/sample.pdf";
String between = "/tmp/sample_out_between.pdf";
String dest = "/tmp/sample_out.pdf";
String fieldName = "sign";
String hash = emptySignature(src, between, fieldName, chain);
String signature = ""; // signed hash signature we get from client
byte[] signatureBytes = Hex.decodeHex(signature.toCharArray());
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, getDigest(), false);
sgn.setExternalDigest(signatureBytes, null, "RSA");
byte[] data = sgn.getEncodedPKCS7(Hex.decodeHex(hash.toCharArray()),null, null, null, MakeSignature.CryptoStandard.CMS);
createSignature(between, dest, fieldName, data);
}
}
This is the original PDF.
This is the PDF with empty signature.
This is the PDF with final signature.
Calculated hash for the PDF.
954927c9286320e904920b0bf12f7cad387c1a9afd5a92314960a1083593f7dc
This is the signed hash signature I received from the client.
6c14b965c7e90c3134653a9261b0666dce7a7e28cb605fc3152ad111fa7915a77396799357daf1d37c52163ce6d34bfd96ee743e721b45e929f6d8aced144f094d03dce00f25c6c1fc5aa63c92322780f7de675c194ef17303a643055dbbedfec9d5200994fcdfc3ad9488d568ad3f6cd2d262e360a79ad90b5ffb188723de559f3696dcb223930f842172e4838f7d5e6a44494ced54bca54ed12133ea189d616a10039a222ce61885ad98b8ba0bd83d63b887e2c188ca10bd2f53f92f08c5585b9826553280c19976a0ba29f7789ad6a80010b4a6431d3b6bb8f27999b23d3739de03db6db8ab46acaf38b33bd37a74465744c3f95a093deff26cb44b45e27e
I have tried a lot of things found on stackoverflow but the problem remains the same. Any help in the right direction would be really appreciated.
For the client part I am using Fortify which makes local USB tokens and HSM modules available to JS client.
The problem was that EmptyContainer.sign method just gives out PDF bytes and not the authenticated attributes. The authenticated attributes are actually which needs to be signed. Thanks mkl for pointing in the right direction.
I modified EmptyContainer.sign method to create a PdfPKCS7 object and call PdfPKCS7.getAuthenticatedAttributeBytes with PDF hash as one of the arguments.
After signing the bytes returned by getAuthenticatedAttributeBytes() method and creating CMS container of the signed bytes and the original hash, I was able to successfully sign the PDF.
Here is the code if someone needs it. It is cluttered all over but you will get the essence.
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import javax.annotation.Nullable;
import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class MklTest {
static Certificate[] chain;
static byte[] toSign;
static byte[] hash;
static class MyExternalSignatureContainer implements ExternalSignatureContainer {
protected byte[] sig;
public MyExternalSignatureContainer(byte[] sig) {
this.sig = sig;
}
public byte[] sign(InputStream is) {
return sig;
}
#Override
public void modifySigningDictionary(PdfDictionary signDic) {
}
}
static class EmptyContainer implements ExternalSignatureContainer {
public EmptyContainer() {
}
public byte[] sign(InputStream is) {
try {
ExternalDigest digest = getDigest();
String hashAlgorithm = getHashAlgorithm();
Certificate[] certs = {null};
hash = DigestAlgorithms.digest(is, digest.getMessageDigest(hashAlgorithm));
PdfPKCS7 sgn = getPkcs(certs);
toSign = sgn.getAuthenticatedAttributeBytes(hash, getOscp(), null,
MakeSignature.CryptoStandard.CMS);
return new byte[0];
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
#Override
public void modifySigningDictionary(PdfDictionary pdfDictionary) {
pdfDictionary.put(PdfName.FILTER, PdfName.ADOBE_PPKMS);
pdfDictionary.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
}
}
public static String getHashAlgorithm() {
return "SHA256";
}
public static byte[] getOscp() {
byte[] ocsp = null;
OcspClient ocspClient = new OcspClientBouncyCastle(new OCSPVerifier(null, null));
if (chain.length >= 2) {
ocsp = ocspClient.getEncoded((X509Certificate)chain[0], (X509Certificate)chain[1], null);
}
return ocsp;
}
public static PdfPKCS7 getPkcs() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
return new PdfPKCS7(null, chain, getHashAlgorithm(), null, getDigest(), false);
}
public static PdfPKCS7 getPkcs(#Nullable Certificate[] certChain) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
//noinspection ConstantConditions
return new PdfPKCS7(null, certChain, getHashAlgorithm(), null, getDigest(), false);
}
public static void emptySignature(String src, String dest, String fieldname) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
Calendar cal = GregorianCalendar.getInstance();
cal.add(Calendar.MINUTE, 10);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, fieldname);
appearance.setReason("Nice");
appearance.setLocation("Delhi");
appearance.setSignDate(cal);
ExternalSignatureContainer external = new EmptyContainer();
MakeSignature.signExternalContainer(appearance, external, 8192);
os.close();
reader.close();
}
public static void setChain() throws CertificateException {
String cert = ""; // the cert we get from client
ByteArrayInputStream userCertificate = new ByteArrayInputStream(Base64.decodeBase64(cert));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
chain = new Certificate[]{cf.generateCertificate(userCertificate)};
}
private static ExternalDigest getDigest() {
return new ExternalDigest() {
public MessageDigest getMessageDigest(String hashAlgorithm)
throws GeneralSecurityException {
return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
}
};
}
public static TSAClient getTsa() {
return new TSAClientBouncyCastle("http://timestamp.digicert.com", null, null, 4096, "SHA-512");
}
public static void createSignature(String src, String dest, String fieldname, byte[] hash, byte[] signature) throws IOException, DocumentException, GeneralSecurityException {
PdfPKCS7 sgn = getPkcs();
sgn.setExternalDigest(signature, null, "RSA");
byte[] encodedSig = sgn.getEncodedPKCS7(hash, getTsa(), getOscp(), null,
MakeSignature.CryptoStandard.CMS);
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
ExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSig);
MakeSignature.signDeferred(reader, fieldname, os, external);
reader.close();
os.close();
}
public static void main(String[] args) throws Exception {
setChain();
String src = "/home/spooderman/Downloads/sample.pdf";
String between = "/tmp/sample_out_between.pdf";
String dest = "/tmp/sample_out.pdf";
String fieldName = "sign";
emptySignature(src, between, fieldName);
System.out.println(Hex.encodeHexString(toSign));
String signature = ""; // signed hash signature we get from client
byte[] signatureBytes = Hex.decodeHex(signature.toCharArray());
createSignature(between, dest, fieldName, hash, signatureBytes);
}
}
I'm trying to sign a PDF using PDFBox, and it does sign but when I open the document in adobe reader I get the following message "Document has been altered or corrupted since it was signed" can someone please help me find the problem.
The keystore was created with "keytool -genkeypair -storepass 123456 -storetype pkcs12 -alias test -validity 365 -v -keyalg RSA -keystore keystore.p12"
Using pdfbox-1.8.9 and bcpkix-jdk15on-1.52
Here is my code:
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.exceptions.SignatureException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import java.io.*;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.Calendar;
import java.util.Collections;
import java.util.Enumeration;
public class CreateSignature implements SignatureInterface {
private static PrivateKey privateKey;
private static Certificate certificate;
boolean signPdf(File pdfFile, File signedPdfFile) {
try (
FileInputStream fis1 = new FileInputStream(pdfFile);
FileInputStream fis = new FileInputStream(pdfFile);
FileOutputStream fos = new FileOutputStream(signedPdfFile);
PDDocument doc = PDDocument.load(pdfFile)) {
int readCount;
byte[] buffer = new byte[8 * 1024];
while ((readCount = fis1.read(buffer)) != -1) {
fos.write(buffer, 0, readCount);
}
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("NAME");
signature.setLocation("LOCATION");
signature.setReason("REASON");
signature.setSignDate(Calendar.getInstance());
doc.addSignature(signature, this);
doc.saveIncremental(fis, fos);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
#Override
public byte[] sign(InputStream is) throws SignatureException, IOException {
try {
BouncyCastleProvider BC = new BouncyCastleProvider();
Store certStore = new JcaCertStore(Collections.singletonList(certificate));
CMSTypedDataInputStream input = new CMSTypedDataInputStream(is);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha512Signer = new JcaContentSignerBuilder("SHA256WithRSA").setProvider(BC).build(privateKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider(BC).build()).build(sha512Signer, new X509CertificateHolder(certificate.getEncoded())
));
gen.addCertificates(certStore);
CMSSignedData signedData = gen.generate(input, false);
return signedData.getEncoded();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) throws IOException, GeneralSecurityException, SignatureException, COSVisitorException {
char[] password = "123456".toCharArray();
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream("/home/user/Desktop/keystore.p12"), password);
Enumeration<String> aliases = keystore.aliases();
String alias;
if (aliases.hasMoreElements()) {
alias = aliases.nextElement();
} else {
throw new KeyStoreException("Keystore is empty");
}
privateKey = (PrivateKey) keystore.getKey(alias, password);
Certificate[] certificateChain = keystore.getCertificateChain(alias);
certificate = certificateChain[0];
File inFile = new File("/home/user/Desktop/sign.pdf");
File outFile = new File("/home/user/Desktop/sign_signed.pdf");
new CreateSignature().signPdf(inFile, outFile);
}
}
class CMSTypedDataInputStream implements CMSTypedData {
InputStream in;
public CMSTypedDataInputStream(InputStream is) {
in = is;
}
#Override
public ASN1ObjectIdentifier getContentType() {
return PKCSObjectIdentifiers.data;
}
#Override
public Object getContent() {
return in;
}
#Override
public void write(OutputStream out) throws IOException,
CMSException {
byte[] buffer = new byte[8 * 1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
}
}
Fixing "Document has been altered or corrupted"
The mistake is that you call PDDocument.saveIncremental with an InputStream merely covering the original PDF:
FileInputStream fis1 = new FileInputStream(pdfFile);
FileInputStream fis = new FileInputStream(pdfFile);
FileOutputStream fos = new FileOutputStream(signedPdfFile);
...
doc.saveIncremental(fis, fos);
But the method expects the InputStream to cover the original file plus the changes made to prepare for signing.
Thus, fis also needs to point to signedPdfFile, and as that file might not exist before, the order of creating fis and fos must be switched>
FileInputStream fis1 = new FileInputStream(pdfFile);
FileOutputStream fos = new FileOutputStream(signedPdfFile);
FileInputStream fis = new FileInputStream(signedPdfFile);
...
doc.saveIncremental(fis, fos);
Unfortunately the JavaDocs don't Point this out.
Another issue
There is another issue with the generated signature. If you look at the ASN.1 dump of a sample result, you'll see something starting like this:
<30 80>
0 NDEF: SEQUENCE {
<06 09>
2 9: OBJECT IDENTIFIER signedData (1 2 840 113549 1 7 2)
: (PKCS #7)
<A0 80>
13 NDEF: [0] {
<30 80>
15 NDEF: SEQUENCE {
<02 01>
17 1: INTEGER 1
<31 0F>
20 15: SET {
The NDEF length indications show that the indefinite-length method is used for encoding these outer layers of the signature container. Use of this method is allowed in the Basic Encoding Rules (BER) but not in the more strict Distinguished Encoding Rules (DER). While using BER for the outer layers is allowed for generic PKCS#7/CMS signatures, the PDF specification clearly requires:
When PKCS#7 signatures are used, the value of Contents shall be a DER-encoded PKCS#7 binary data object containing the signature.
(section 12.8.3.3.1 "PKCS#7 Signatures as used in ISO 32000" / "General" in ISO 32000-1)
Thus, strictly speaking your signature is even structurally invalid. Usually, though, this is not detected by PDF signature verification services because most of them use standard PKCS#7/CMS libraries or methods for verifying the signature containers.
If you want to make sure that your signatures are truly valid PDF signatures, you can achieve this by replacing
return signedData.getEncoded();
by something like
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DEROutputStream dos = new DEROutputStream(baos);
dos.writeObject(signedData.toASN1Structure());
return baos.toByteArray();
Now the whole signature object is DER-encoded.
(You can find a test creating signatures both with your original and the fixed code either with or without improved encoding here: SignLikeLoneWolf.java)
When running below program I am getting this exception. When I make
byte key_bytes[] = new byte[X]; with X<16, I get same error but with
Invalid AES key length: X bytes
If I write this with X>=16 I get the error that I wrote in the title.
Here is the exception i get and after that my code
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 444 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:495)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1062)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1020)
at javax.crypto.Cipher.init(Cipher.java:1225)
at javax.crypto.Cipher.init(Cipher.java:1166)
at Main.main(Main.java:87)
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.*;
import java.security.cert.CertificateException;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class Main
{
public static void main(String[] args) throws IOException, UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException, CertificateException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, SignatureException{
//open the file containing keys
File file = new File("keys/ks_file.jks");
//cipher object that will hold the information
Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
//create keystore object from stored keys inside the file
KeyStore keystore = loadKeyStore(file, "sergey", "JKS");
//messageDigest instance
MessageDigest md = MessageDigest.getInstance("SHA1");
//singanture instance
Signature dsa = Signature.getInstance("SHA1withDSA");
//params for getting keys
String allias = "enc_key", password = "sergey";
SecureRandom s_random = SecureRandom.getInstance("SHA1PRNG");
//create random bytes for semtric key
byte key_bytes[] = new byte[16];
s_random.setSeed(711);
s_random.nextBytes(key_bytes);
Key key = new SecretKeySpec(key_bytes, "AES");
Key key_enc = keystore.getKey(allias, password.toCharArray());
KeyPair enc_key = null;
if (key_enc instanceof PrivateKey) {
// Get certificate of public key
java.security.cert.Certificate cert = keystore.getCertificate(allias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
enc_key = new KeyPair(publicKey, (PrivateKey) key_enc);
}
//cipher the file
aes.init(Cipher.ENCRYPT_MODE, key);
FileInputStream fis;
FileOutputStream fos;
CipherInputStream cis;
fis = new FileInputStream("tmp/a.txt");
cis = new CipherInputStream(fis, aes);
fos = new FileOutputStream("tmp/b.txt");
byte[] b = new byte[8];
int i = cis.read(b);
byte[] bytes = ByteBuffer.allocate(4).putInt(i).array();
//update message digest for signature
md.update(bytes);
while (i != -1) {
fos.write(b, 0, i);
i = cis.read(b);
bytes = ByteBuffer.allocate(4).putInt(i).array();
md.update(bytes);
}
fis.close();
cis.close();
fos.close();
//encode the secret key
aes.init(Cipher.ENCRYPT_MODE, (Key)enc_key.getPublic());
byte[] cipherKey = aes.doFinal(key.toString().getBytes());
//we save the final digest
byte[] hash = md.digest();
//init singature with private key
dsa.initSign(enc_key.getPrivate());
//update the signature with the hash aster digest
dsa.update(hash);
//final signature
byte[] sig = dsa.sign();
//creating config xml
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
// root elements
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement("config");
doc.appendChild(rootElement);
// signature elements
Element sig_xml = doc.createElement("sig");
rootElement.appendChild(sig_xml);
sig_xml.setAttribute("value", sig.toString());
// key element
Element key_xml = doc.createElement("key");
rootElement.appendChild(key_xml);
key_xml.setAttribute("value", cipherKey.toString());
// write the content into xml file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File("./config.xml"));
transformer.transform(source, result);
System.out.println("File saved!");
} catch (ParserConfigurationException pce) {
pce.printStackTrace();
} catch (TransformerException tfe) {
tfe.printStackTrace();
}
}
/**
* Reads a Java keystore from a file.
*
* #param keystoreFile
* keystore file to read
* #param password
* password for the keystore file
* #param keyStoreType
* type of keystore, e.g., JKS or PKCS12
* #return the keystore object
* #throws KeyStoreException
* if the type of KeyStore could not be created
* #throws IOException
* if the keystore could not be loaded
* #throws NoSuchAlgorithmException
* if the algorithm used to check the integrity of the keystore
* cannot be found
* #throws CertificateException
* if any of the certificates in the keystore could not be loaded
*/
public static KeyStore loadKeyStore(final File keystoreFile,
final String password, final String keyStoreType)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException {
if (null == keystoreFile) {
throw new IllegalArgumentException("Keystore url may not be null");
}
final URI keystoreUri = keystoreFile.toURI();
final URL keystoreUrl = keystoreUri.toURL();
final KeyStore keystore = KeyStore.getInstance(keyStoreType);
InputStream is = null;
try {
is = keystoreUrl.openStream();
keystore.load(is, null == password ? null : password.toCharArray());
} finally {
if (null != is) {
is.close();
}
}
return keystore;
}
}
Try installing the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files this is one of the main issues behind the invalid key length.
It's available at Oracle http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
Download the local_policy and US_export_policy and put them in your
Java\jdk1.6.0_45\jre\lib\security
I'm writing a program that decrypt a file that was encrypted.
i recive the folowing output:
==============================
== Receiver\Decryptor side ==
==============================
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
Base64 cannot be resolved
Base64 cannot be resolved
Base64 cannot be resolved
at Decryptor.DecryptFileAndValidateSignature(Decryptor.java:97)
at Decryptor.main(Decryptor.java:65)
the error happens somwhere at these 3 lines -
ks.load(ksStream, receiverConfigurations.get("keyStorePass").toCharArray());
/* Loads private key from keyStore, needed to decrypt the symmetric key from configuration file */
PrivateKey receiverPrivateKey = (PrivateKey) ks.getKey(receiverConfigurations.get("receiverAlias"), receiverConfigurations.get("receiverKeyPass").toCharArray()); //private key of receiver
i guess something is wring with my imports..
as you can see i tried a lot. any help will be aprricated tnx
here is my code -
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.HashMap;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.security.KeyStore;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Base64InputStream;
public class Decryptor {
public static void main(String args[]) throws Exception
{
if (args.length!=2)
{
System.out.println("Wrong number of parameters, please insert the path of encryptedTextFile.txt, cipherConfigurations.xml and the path for keystore file.\n" +
"Make sure that path is ended with '\\\\'. Example: c:\\\\test\\\\crypto\\\\keyStore.jks c:\\\\test\\\\crypto\\\\"
+ "\nAborting...\n");
return;
}
if (!Files.exists(Paths.get(args[0])))
{
System.out.println("Path "+args[0]+" doesn't exist.\nAborting...\n");
return;
}
String basePath = args[0];
String keyStore = args[1];
basePath = "c:\\test\\crypto\\";
System.out.println("-------------------------------------------------------------");
System.out.println("==============================");
System.out.println("== Receiver\\Decryptor side ==");
System.out.println("==============================");
/* Configurations that are known to the receiver */
HashMap<String,String> receiverConfigurations = new HashMap<>();
receiverConfigurations.put("encryptedTextFile" ,basePath+"encryptedTextFile.txt");
receiverConfigurations.put("configurationFile",basePath+"cipherConfigurations.xml");
receiverConfigurations.put("keyStorePass","gilperryB");
receiverConfigurations.put("receiverAlias","b_side");
receiverConfigurations.put("receiverKeyPass","gilperryB");
receiverConfigurations.put("decryptedTextFile",basePath+"decryptedText.txt");
receiverConfigurations.put("keyStorePath",keyStore);
receiverConfigurations.put("senderAlias","a_side");
DecryptFileAndValidateSignature(receiverConfigurations);
System.out.println("-------------------------------------------------------------");
}
/*
* Decrypts and validates a signature of an encrypted file that was sent between a sender and a receiver, in the following way:
* 1. Uses private key to encrypt a symmetric key from a configuration file.
* 2. Uses the symmetric key to decrypt the message sent in a different file
* 3. Calculates digital signature over the file, and compares it with the one received in the configuration file.
* 4. If the signatures match - returns true, else - returns false
* */
static public boolean DecryptFileAndValidateSignature(HashMap<String,String> receiverConfigurations) throws Exception
{
/* Load data from keyStore .jks file */
KeyStore ks = KeyStore.getInstance("jks"); // Load public key from keyStore
FileInputStream ksStream = new FileInputStream(receiverConfigurations.get("keyStorePath"));
ks.load(ksStream, receiverConfigurations.get("keyStorePass").toCharArray());
/* Loads private key from keyStore, needed to decrypt the symmetric key from configuration file */
PrivateKey receiverPrivateKey = (PrivateKey) ks.getKey(receiverConfigurations.get("receiverAlias"), receiverConfigurations.get("receiverKeyPass").toCharArray()); //private key of receiver
/* Load data received by the cipher configurations XML sent by sender */
HashMap<String,String> cipherConfigurations = ReadConfigurationXML(receiverConfigurations.get("configurationFile"));
if (cipherConfigurations == null)
{
System.out.println("Error reading cipher configurations XML.\nAborting...");
}
System.out.println("Read data Cipher configurations XML.");
/* Initialize the encryptor */
Cipher encryptor = Cipher.getInstance(cipherConfigurations.get("encryptionAlgoForSymmetricKey"), cipherConfigurations.get("encryptionAlgoForSymmetricKeyProvider"));
/* Get data from cipher configurations XML*/
byte[] symetricKeyEncrypted = Base64.decodeBase64(cipherConfigurations.get("symetricKeyEncrypted"));
/* Initialize the symmetric key encryptor */
Cipher rsaEncryptor = Cipher.getInstance(cipherConfigurations.get("encryptionAlgoForSendingSharedKey"), cipherConfigurations.get("encryptionAlgoForSendingSharedKeyProvider")); // encryptor for the secret key
byte[] symetricKeyDecrypted = DecryptText(symetricKeyEncrypted,rsaEncryptor,receiverPrivateKey, null);
byte[] ivConfig =Base64.decodeBase64(cipherConfigurations.get("ivspec"));
byte[] iv = DecryptText(ivConfig, rsaEncryptor, receiverPrivateKey, null);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
byte[] digitalSignature = Base64.decodeBase64(cipherConfigurations.get("digitalSignature"));
Key symmetricKeyAfterDecription = new SecretKeySpec(symetricKeyDecrypted, cipherConfigurations.get("encryptionAlgoForKeyGeneration")); //build a new secret key from text
System.out.println("Decrypted symmetric key using his own private key");
/* Decrypt file into decryptedFile */
DecryptFile(receiverConfigurations.get("encryptedTextFile"), receiverConfigurations.get("decryptedTextFile"), encryptor, symmetricKeyAfterDecription,ivSpec);
System.out.println("Decrypted text file "+receiverConfigurations.get("encryptedTextFile")+" into "+receiverConfigurations.get("decryptedTextFile"));
/* Verify digital signature */
PublicKey senderPublicKey = ks.getCertificate(receiverConfigurations.get("senderAlias")).getPublicKey(); //publicKey holds the public key for sender
boolean signatureValidated = ValidateDigitalSignature(receiverConfigurations.get("decryptedTextFile"),cipherConfigurations.get("digitalSignatureAlgorithm"),senderPublicKey,digitalSignature);
if (!signatureValidated)
{
System.out.println("Error decrypting text or validating digital signature.\nAborting...");
return false;
}
else
{
System.out.println("File was successfully decrypted, digital signature was successfully validated.\n");
return true;
}
}
/*
* Simulates the process where the receiver is calculating the signature of a message he received
* and compares it to the signature sent to him by sender.
* Calculates the digital signature over the decrypted file, using the digital signature algorithm in digitalSignatureAlgorithm,
* and public key in senderPublicKey.
* returns true iff the signatures match.
* */
private static boolean ValidateDigitalSignature(String decryptedFile,
String digitalSignatureAlgorithm, PublicKey senderPublicKey, byte[] signatureToVerify) throws Exception {
Signature dsa = Signature.getInstance(digitalSignatureAlgorithm); /* Initializing the object with the digital signature algorithm */
dsa.initVerify(senderPublicKey);
/* Update and sign the data */
FileInputStream fis = new FileInputStream(decryptedFile);
byte[] block = new byte[8];
int i;
while ((i = fis.read(block)) != -1) { //read all blocks in file
dsa.update(block); // update digital signature after each block
}
fis.close();
return dsa.verify(signatureToVerify);
}
/*
* Reads an encrypted text file and decrypts it using a Cipher object (encryptor).
* The decrypted file will be the returned value.
* Decryption process contains also Base64 encoding of the text.
*/
private static void DecryptFile(String inputFile,String outputFile, Cipher encryptor, Key key, IvParameterSpec ivspec) throws Exception
{
assert (CreateFileIfNecessery(outputFile) == true); //creates output file if necessery
FileInputStream fis = new FileInputStream(inputFile);
Base64InputStream b64os = new Base64InputStream(fis);
CipherInputStream cis = new CipherInputStream(b64os, encryptor);
FileOutputStream fos = new FileOutputStream(outputFile);
encryptor.init(Cipher.DECRYPT_MODE, key, ivspec); //initilize cipher in decryption mode with IV
byte[] block = new byte[8];
int i;
while ((i = cis.read(block)) != -1) { //read all blocks in file
{
fos.write(block,0,i); // write each block encrypted to the output file
}
}
b64os.close();
fos.close(); // close output file
cis.close(); // close input file
}
/*
* Reads the configuration XML from file in 'path'.
* Retuns a HashMap containing the entries and their value.
* if not possible - returns null.
*/
public static HashMap<String,String> ReadConfigurationXML(String path) throws Exception
{
HashMap<String,String> cipherConfigurations = new HashMap<>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc;
try{
doc = builder.parse(path);
}catch (Exception e)
{
System.out.println("Error reading configurations file "+path+"\nAborting...");
return null;
}
NodeList mainNode = null;
try
{
mainNode = doc.getElementsByTagName("CipherConfigurations");
}
catch (Exception e)
{
System.out.println("Could not find element EncryptionConfigurations in the configurations file.\nAborting...");
return null;
}
if (mainNode.getLength()!=1)
{
System.out.println("Wrong structure of cipher configutarion element.\nAborting...");
return null;
}
NodeList cipherConfigurationsRoot = (NodeList) mainNode.item(0); // get the root element of the configurations
for (int i = 0; i < cipherConfigurationsRoot.getLength(); ++i)
{
Element elem = (Element) cipherConfigurationsRoot.item(i);
String paramName = elem.getNodeName();
String innerText = elem.getTextContent();
cipherConfigurations.put(paramName, innerText);
}
return cipherConfigurations;
}
/*
* Reads an encrypted text and decrypts it using a Cipher object (encryptor).
* The decrypted text will be the returned value.
* Decryption process contains also Base64 encoding of the text.
*/
public static byte[] DecryptText(byte[] text, Cipher encryptor, Key key, IvParameterSpec ivspec) throws Exception
{
OutputStream os = new ByteArrayOutputStream();
InputStream is = new ByteArrayInputStream(text);
Base64InputStream b64is = new Base64InputStream(is);
CipherInputStream cis = new CipherInputStream(b64is, encryptor);
encryptor.init(Cipher.DECRYPT_MODE, key, ivspec); //initilize cipher in decryption mode with IV
byte[] block = new byte[8];
int i;
while ((i = cis.read(block)) != -1) { //read all blocks in file
{
os.write(block,0, i); // write each block encrypted to the output file
}
}
b64is.close();
os.close(); // close output file
is.close(); // close input file
cis.close();
return ((ByteArrayOutputStream) os).toByteArray();
}
/*
* Creates file and its paths if not exist.
* Example: path = c:\\test\\test1\\foo.txt
* Method will check if this path and file exists, if not - will create full hierarchy.
*/
private static boolean CreateFileIfNecessery(String path) throws Exception
{
File f = new File(path);
if (!f.mkdirs()) return false; //creates the directories for the file
if (!f.createNewFile()) return false; // creates the output file
return true;
}
}
You're missing Apache's commons-codec-1.10.jar
or
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
if you're using Maven
I've generated a private key with:
openssl genrsa [-out file] –des3
After this I've generated a public key with:
openssl rsa –pubout -in private.key [-out file]
I want to sign some messages with my private key, and verify some other messages with my public key, using code like this:
public String sign(String message) throws SignatureException{
try {
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initSign(privateKey);
sign.update(message.getBytes("UTF-8"));
return new String(Base64.encodeBase64(sign.sign()),"UTF-8");
} catch (Exception ex) {
throw new SignatureException(ex);
}
}
public boolean verify(String message, String signature) throws SignatureException{
try {
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initVerify(publicKey);
sign.update(message.getBytes("UTF-8"));
return sign.verify(Base64.decodeBase64(signature.getBytes("UTF-8")));
} catch (Exception ex) {
throw new SignatureException(ex);
}
}
I found a solution to convert my private key to PKCS8 format and load it. It works with some code like this:
public PrivateKey getPrivateKey(String filename) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf =
KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
And finally my question is: How do I load my RSA Public Key from a file?
I think maybe I need to convert my public key file to x509 format, and use X509EncodedKeySpec. But how can I do this?
Below is the relevant information from the link which Zaki provided.
Generate a 2048-bit RSA private key
$ openssl genrsa -out private_key.pem 2048
Convert private Key to PKCS#8 format (so Java can read it)
$ openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
Output public key portion in DER format (so Java can read it)
$ openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
Private key
import java.nio.file.*;
import java.security.*;
import java.security.spec.*;
public class PrivateKeyReader {
public static PrivateKey get(String filename)
throws Exception {
byte[] keyBytes = Files.readAllBytes(Paths.get(filename));
PKCS8EncodedKeySpec spec =
new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
}
Public key
import java.nio.file.*;
import java.security.*;
import java.security.spec.*;
public class PublicKeyReader {
public static PublicKey get(String filename)
throws Exception {
byte[] keyBytes = Files.readAllBytes(Paths.get(filename));
X509EncodedKeySpec spec =
new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
}
This program is doing almost everything with Public and private keys.
The der format can be obtained but saving raw data ( without encoding base64).
I hope this helps programmers.
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import sun.security.pkcs.PKCS8Key;
import sun.security.pkcs10.PKCS10;
import sun.security.x509.X500Name;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* #author Desphilboy
* DorOd bar shomA barobach
*
*/
public class csrgenerator {
private static PublicKey publickey= null;
private static PrivateKey privateKey=null;
//private static PKCS8Key privateKey=null;
private static KeyPairGenerator kpg= null;
private static ByteArrayOutputStream bs =null;
private static csrgenerator thisinstance;
private KeyPair keypair;
private static PKCS10 pkcs10;
private String signaturealgorithm= "MD5WithRSA";
public String getSignaturealgorithm() {
return signaturealgorithm;
}
public void setSignaturealgorithm(String signaturealgorithm) {
this.signaturealgorithm = signaturealgorithm;
}
private csrgenerator() {
try {
kpg = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
System.out.print("No such algorithm RSA in constructor csrgenerator\n");
}
kpg.initialize(2048);
keypair = kpg.generateKeyPair();
publickey = keypair.getPublic();
privateKey = keypair.getPrivate();
}
/** Generates a new key pair
*
* #param int bits
* this is the number of bits in modulus must be 512, 1024, 2048 or so on
*/
public KeyPair generateRSAkys(int bits)
{
kpg.initialize(bits);
keypair = kpg.generateKeyPair();
publickey = keypair.getPublic();
privateKey = keypair.getPrivate();
KeyPair dup= keypair;
return dup;
}
public static csrgenerator getInstance() {
if (thisinstance == null)
thisinstance = new csrgenerator();
return thisinstance;
}
/**
* Returns a CSR as string
* #param cn Common Name
* #param OU Organizational Unit
* #param Org Organization
* #param LocName Location name
* #param Statename State/Territory/Province/Region
* #param Country Country
* #return returns csr as string.
* #throws Exception
*/
public String getCSR(String commonname, String organizationunit, String organization,String localname, String statename, String country ) throws Exception {
byte[] csr = generatePKCS10(commonname, organizationunit, organization, localname, statename, country,signaturealgorithm);
return new String(csr);
}
/** This function generates a new Certificate
* Signing Request.
*
* #param CN
* Common Name, is X.509 speak for the name that distinguishes
* the Certificate best, and ties it to your Organization
* #param OU
* Organizational unit
* #param O
* Organization NAME
* #param L
* Location
* #param S
* State
* #param C
* Country
* #return byte stream of generated request
* #throws Exception
*/
private static byte[] generatePKCS10(String CN, String OU, String O,String L, String S, String C,String sigAlg) throws Exception {
// generate PKCS10 certificate request
pkcs10 = new PKCS10(publickey);
Signature signature = Signature.getInstance(sigAlg);
signature.initSign(privateKey);
// common, orgUnit, org, locality, state, country
//X500Name(String commonName, String organizationUnit,String organizationName,Local,State, String country)
X500Name x500Name = new X500Name(CN, OU, O, L, S, C);
pkcs10.encodeAndSign(x500Name,signature);
bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs);
pkcs10.print(ps);
byte[] c = bs.toByteArray();
try {
if (ps != null)
ps.close();
if (bs != null)
bs.close();
} catch (Throwable th) {
}
return c;
}
public PublicKey getPublicKey() {
return publickey;
}
/**
* #return
*/
public PrivateKey getPrivateKey() {
return privateKey;
}
/**
* saves private key to a file
* #param filename
*/
public void SavePrivateKey(String filename)
{
PKCS8EncodedKeySpec pemcontents=null;
pemcontents= new PKCS8EncodedKeySpec( privateKey.getEncoded());
PKCS8Key pemprivatekey= new PKCS8Key( );
try {
pemprivatekey.decode(pemcontents.getEncoded());
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
File file=new File(filename);
try {
file.createNewFile();
FileOutputStream fos=new FileOutputStream(file);
fos.write(pemprivatekey.getEncoded());
fos.flush();
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Saves Certificate Signing Request to a file;
* #param filename is a String containing full path to the file which will be created containing the CSR.
*/
public void SaveCSR(String filename)
{
FileOutputStream fos=null;
PrintStream ps=null;
File file;
try {
file = new File(filename);
file.createNewFile();
fos = new FileOutputStream(file);
ps= new PrintStream(fos);
}catch (IOException e)
{
System.out.print("\n could not open the file "+ filename);
}
try {
try {
pkcs10.print(ps);
} catch (SignatureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ps.flush();
ps.close();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.print("\n cannot write to the file "+ filename);
e.printStackTrace();
}
}
/**
* Saves both public key and private key to file names specified
* #param fnpub file name of public key
* #param fnpri file name of private key
* #throws IOException
*/
public static void SaveKeyPair(String fnpub,String fnpri) throws IOException {
// Store Public Key.
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
publickey.getEncoded());
FileOutputStream fos = new FileOutputStream(fnpub);
fos.write(x509EncodedKeySpec.getEncoded());
fos.close();
// Store Private Key.
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
fos = new FileOutputStream(fnpri);
fos.write(pkcs8EncodedKeySpec.getEncoded());
fos.close();
}
/**
* Reads a Private Key from a pem base64 encoded file.
* #param filename name of the file to read.
* #param algorithm Algorithm is usually "RSA"
* #return returns the privatekey which is read from the file;
* #throws Exception
*/
public PrivateKey getPemPrivateKey(String filename, String algorithm) throws Exception {
File f = new File(filename);
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 privKeyPEM = temp.replace("-----BEGIN PRIVATE KEY-----", "");
privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", "");
//System.out.println("Private key\n"+privKeyPEM);
BASE64Decoder b64=new BASE64Decoder();
byte[] decoded = b64.decodeBuffer(privKeyPEM);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePrivate(spec);
}
/**
* Saves the private key to a pem file.
* #param filename name of the file to write the key into
* #param key the Private key to save.
* #return String representation of the pkcs8 object.
* #throws Exception
*/
public String SavePemPrivateKey(String filename) throws Exception {
PrivateKey key=this.privateKey;
File f = new File(filename);
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos = new DataOutputStream(fos);
byte[] keyBytes = key.getEncoded();
PKCS8Key pkcs8= new PKCS8Key();
pkcs8.decode(keyBytes);
byte[] b=pkcs8.encode();
BASE64Encoder b64=new BASE64Encoder();
String encoded = b64.encodeBuffer(b);
encoded= "-----BEGIN PRIVATE KEY-----\r\n" + encoded + "-----END PRIVATE KEY-----";
dos.writeBytes(encoded);
dos.flush();
dos.close();
//System.out.println("Private key\n"+privKeyPEM);
return pkcs8.toString();
}
/**
* Saves a public key to a base64 encoded pem file
* #param filename name of the file
* #param key public key to be saved
* #return string representation of the pkcs8 object.
* #throws Exception
*/
public String SavePemPublicKey(String filename) throws Exception {
PublicKey key=this.publickey;
File f = new File(filename);
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos = new DataOutputStream(fos);
byte[] keyBytes = key.getEncoded();
BASE64Encoder b64=new BASE64Encoder();
String encoded = b64.encodeBuffer(keyBytes);
encoded= "-----BEGIN PUBLIC KEY-----\r\n" + encoded + "-----END PUBLIC KEY-----";
dos.writeBytes(encoded);
dos.flush();
dos.close();
//System.out.println("Private key\n"+privKeyPEM);
return encoded.toString();
}
/**
* reads a public key from a file
* #param filename name of the file to read
* #param algorithm is usually RSA
* #return the read public key
* #throws Exception
*/
public PublicKey getPemPublicKey(String filename, String algorithm) throws Exception {
File f = new File(filename);
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-----", "");
BASE64Decoder b64=new BASE64Decoder();
byte[] decoded = b64.decodeBuffer(publicKeyPEM);
X509EncodedKeySpec spec =
new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePublic(spec);
}
public static void main(String[] args) throws Exception {
csrgenerator gcsr = csrgenerator.getInstance();
gcsr.setSignaturealgorithm("SHA512WithRSA");
System.out.println("Public Key:\n"+gcsr.getPublicKey().toString());
System.out.println("Private Key:\nAlgorithm: "+gcsr.getPrivateKey().getAlgorithm().toString());
System.out.println("Format:"+gcsr.getPrivateKey().getFormat().toString());
System.out.println("To String :"+gcsr.getPrivateKey().toString());
System.out.println("GetEncoded :"+gcsr.getPrivateKey().getEncoded().toString());
BASE64Encoder encoder= new BASE64Encoder();
String s=encoder.encodeBuffer(gcsr.getPrivateKey().getEncoded());
System.out.println("Base64:"+s+"\n");
String csr = gcsr.getCSR( "desphilboy#yahoo.com","baxshi az xodam", "Xodam","PointCook","VIC" ,"AU");
System.out.println("CSR Request Generated!!");
System.out.println(csr);
gcsr.SaveCSR("c:\\testdir\\javacsr.csr");
String p=gcsr.SavePemPrivateKey("c:\\testdir\\java_private.pem");
System.out.print(p);
p=gcsr.SavePemPublicKey("c:\\testdir\\java_public.pem");
privateKey= gcsr.getPemPrivateKey("c:\\testdir\\java_private.pem", "RSA");
BASE64Encoder encoder1= new BASE64Encoder();
String s1=encoder1.encodeBuffer(gcsr.getPrivateKey().getEncoded());
System.out.println("Private Key in Base64:"+s1+"\n");
System.out.print(p);
}
}
Once you have your key stored in a PEM file, you can read it back easily using PemObject and PemReader classes provided by BouncyCastle, as shown in this this tutorial.
Create a PemFile class that encapsulates file handling:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
public class PemFile {
private PemObject pemObject;
public PemFile(String filename) throws FileNotFoundException, IOException {
PemReader pemReader = new PemReader(new InputStreamReader(
new FileInputStream(filename)));
try {
this.pemObject = pemReader.readPemObject();
} finally {
pemReader.close();
}
}
public PemObject getPemObject() {
return pemObject;
}
}
Then instantiate private and public keys as usual:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.apache.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class Main {
protected final static Logger LOGGER = Logger.getLogger(Main.class);
public final static String RESOURCES_DIR = "src/main/resources/rsa-sample/";
public static void main(String[] args) throws FileNotFoundException,
IOException, NoSuchAlgorithmException, NoSuchProviderException {
Security.addProvider(new BouncyCastleProvider());
LOGGER.info("BouncyCastle provider added.");
KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
try {
PrivateKey priv = generatePrivateKey(factory, RESOURCES_DIR
+ "id_rsa");
LOGGER.info(String.format("Instantiated private key: %s", priv));
PublicKey pub = generatePublicKey(factory, RESOURCES_DIR
+ "id_rsa.pub");
LOGGER.info(String.format("Instantiated public key: %s", pub));
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
private static PrivateKey generatePrivateKey(KeyFactory factory,
String filename) throws InvalidKeySpecException,
FileNotFoundException, IOException {
PemFile pemFile = new PemFile(filename);
byte[] content = pemFile.getPemObject().getContent();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
return factory.generatePrivate(privKeySpec);
}
private static PublicKey generatePublicKey(KeyFactory factory,
String filename) throws InvalidKeySpecException,
FileNotFoundException, IOException {
PemFile pemFile = new PemFile(filename);
byte[] content = pemFile.getPemObject().getContent();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
return factory.generatePublic(pubKeySpec);
}
}
Hope this helps.
#Value("${spring.security.oauth2.resourceserver.jwt.key-value}")
RSAPublicKey key;
key-value can be uri (i.e. "classpath:keys/pub.pcks8.pem") or pem content.
you must include the following deps:
compile project(':spring-security-config')
compile project(':spring-security-oauth2-jose')
compile project(':spring-security-oauth2-resource-server')
Below code works absolutely fine to me and working. This code will read RSA private and public key though java code. You can refer to http://snipplr.com/view/18368/
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class Demo {
public static final String PRIVATE_KEY="/home/user/private.der";
public static final String PUBLIC_KEY="/home/user/public.der";
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
//get the private key
File file = new File(PRIVATE_KEY);
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) file.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(spec);
System.out.println("Exponent :" + privKey.getPrivateExponent());
System.out.println("Modulus" + privKey.getModulus());
//get the public key
File file1 = new File(PUBLIC_KEY);
FileInputStream fis1 = new FileInputStream(file1);
DataInputStream dis1 = new DataInputStream(fis1);
byte[] keyBytes1 = new byte[(int) file1.length()];
dis1.readFully(keyBytes1);
dis1.close();
X509EncodedKeySpec spec1 = new X509EncodedKeySpec(keyBytes1);
KeyFactory kf1 = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) kf1.generatePublic(spec1);
System.out.println("Exponent :" + pubKey.getPublicExponent());
System.out.println("Modulus" + pubKey.getModulus());
}
}