I have a digitally signed pdf file and I am trying to verify the signature in that. But Signature is not getting verified. Below is the code
public static void main(String[] args) throws Exception {
HashMap<String, Object> keyPair = new HashMap<>();
String password = "123";
KeyStore keyStore = KeyStore.getInstance("pkcs12"); //, "BC");
keyStore.load(new FileInputStream("/Users/shivam/Documents/Projects/label-processor/src/main/resources/DigitalSignatureFile.pfx"), password.toCharArray());
Enumeration<String> keyStoreAliasEnum = keyStore.aliases();
String alias = null;
PublicKey publicKey = null;
PrivateKey privateKey = null;
byte[] signature = null;
while ( keyStoreAliasEnum.hasMoreElements() ) {
alias = keyStoreAliasEnum.nextElement();
if (password != null) {
privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
X509Certificate x509Certificate = (X509Certificate) keyStore.getCertificate(alias);
publicKey = x509Certificate.getPublicKey();
signature = x509Certificate.getSignature();
keyPair.put("Alias", alias);
keyPair.put("PublicKey", publicKey);
keyPair.put("PrivateKey", privateKey);
keyPair.put("X509Certificate", x509Certificate);
}
}
verify(publicKey, signature);
}
public static void verify(PublicKey publicKey, byte[] sigBytes) throws Exception {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(publicKey);
FileInputStream datafis = new FileInputStream("/Users/shivam/Desktop/DigitallySignedFile.pdf");
byte[] data = IOUtils.toByteArray(datafis);
sig.update(data);
boolean verifies = sig.verify(sigBytes);
System.out.println("signature verifies: " + verifies);
FileUtils.writeByteArrayToFile(
new File("/Users/shivam/Documents/Projects/label-processor/src/main/resources/VerifiedFile.pdf"),
data
);
}
Above program is giving "Signature verifies: false". And In VerifiedFile.pdf I am still seeing signature not verified. But when I am trying to verify in Adobe Reader it is verifying correctly.
I tried verifying with the help of itext library also. It is verifying signature But In pdf it still shows signature not verified.
public static void main(String[] args) throws Exception {
byte[] pdfBytes = Files.readAllBytes(
Paths.get("/Users/shivam/Desktop/DigitallySignedFile.pdf")
);
PdfReader pdfReader = new PdfReader(pdfBytes);
AcroFields acroFields = pdfReader.getAcroFields();
List<String> signatures = acroFields.getSignatureNames();
boolean valid = false;
if (!signatures.isEmpty()) {
for (String signature : signatures) {
if (acroFields.signatureCoversWholeDocument(signature)) {
PdfPKCS7 pkcs7 = acroFields.verifySignature(signature);
valid = pkcs7.verify();
String reason = pkcs7.getReason();
Calendar signedAt = pkcs7.getSignDate();
X509Certificate x509Certificate = pkcs7.getSigningCertificate();
Principal issuerDN = x509Certificate.getIssuerDN();
Principal subjectDN = x509Certificate.getSubjectDN();
logger.info("valid = {}, date = {}, reason = '{}', issuer = '{}', subject = '{}'",
valid, signedAt.getTime(), reason, issuerDN, subjectDN);
break;
}
}
}
FileUtils.writeByteArrayToFile(
new File("/Users/shivam/Documents/Projects/label-processor/src/main/resources/VerifiedFile.pdf"),
pdfBytes
);
}
I appreciate any help.
Related
I have successfully generate pkcs7 signature ECDSAwithSHA256 using C# , but then i failed to verify signature using java
Here is sample class
public class TestVerify {
public static void main(String[] args) {
String Signature = "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";
String SigDateTime = "14-10-2020 10:58:22";
String certtype = "token";
String TimestampToken = "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";
String pdfhash = "faadTnFU4cOBsl+sW98ie7KInSbbw0HDbgFeOcsRCAQ=";
CMSSignedData signedDataTSToken = null;
TimeStampToken tstoken = null;
byte[] sigDataBytes = null;
Date sigDate = null;
CMSSignerHelper cmsHelper = new CMSSignerHelper();
CMSSignedData cmsData;
try {
sigDataBytes = Base64.getDecoder().decode(Signature);
cmsData = new CMSSignedData(sigDataBytes);
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
sigDate = formatter.parse(SigDateTime);
byte[] tsTokenDataBytes = Base64.getDecoder().decode(TimestampToken);
signedDataTSToken = new CMSSignedData(tsTokenDataBytes);
tstoken = new TimeStampToken(signedDataTSToken);
verifyCMSSignatureMTID(sigDataBytes, sigDate, TimestampToken, pdfhash);
} catch (Exception e) {
System.out.println("Exception : " + e);
}
}
public static void verifyCMSSignatureMTID(byte[] signature, Date signing_date, String encodedTimestampToken, String PdfHash) throws OperatorCreationException, CMSException, CertificateException, NoSuchAlgorithmException, ParseException, TSPException, IOException, Exception {
String fName="[verifyCMSSignatureMTID] ";
VSInfo vsi=new VSInfo();
String initErr="CMS";
//******************** VERIFY SGNATURE VS SIGNER PUBLICKEY **********************
// Verify signature
byte[] HashByte = Base64.getDecoder().decode(PdfHash);
CMSProcessableByteArray processable = new CMSProcessableByteArray(HashByte);
CMSSignedData cmsData = new CMSSignedData(signature);
X509CertificateHolder certHolder_v=null;
Security.addProvider(new BouncyCastleProvider());
try {
Store store = cmsData.getCertificates();
ByteArrayInputStream stream = new ByteArrayInputStream(signature);
CMSSignedData cms = new CMSSignedData(processable, stream);
SignerInformationStore signers = cms.getSignerInfos();
Collection c_v = signers.getSigners();
Iterator it_v = c_v.iterator();
while (it_v.hasNext()) {
SignerInformation signer_v = (SignerInformation) it_v.next();
Collection certCollection_v = store.getMatches(signer_v.getSID());
Iterator certIt_v = certCollection_v.iterator();
certHolder_v = (X509CertificateHolder) certIt_v.next();
X509Certificate certFromSignedData_v = new JcaX509CertificateConverter().getCertificate(certHolder_v);
if (signer_v.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(certFromSignedData_v))) {
System.out.println(fName + "Signature verified");
} else {
System.out.println(fName + "Error CMS200 Signature is invalid");
System.out.println(initErr+"200");
System.out.println("Signature is invalid");
return;
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(fName + "Error CMS200 Signature is invalid : " + e);
System.out.println("200");
System.out.println("Signature is invalid");
return;
}
System.out.println("000");
System.out.println("Signature is valid");
return;
}
}
Error
org.bouncycastle.operator.RuntimeOperatorException: exception
obtaining signature: error decoding signature bytes.
However, i have succesfully verify RSAwithSHA256 signature using this method. Can someone help me on this and kindly explain what that error is about. The signature length for those signature is 256byte for RSA and 64byte for ECDSA . is it correct?
While rebuilding JarSigner in order to work with PQC Signature Schemes such as XMSS provided by the JCA/JCE Provider BouncyCastle I came across a problem: It seems that XMSS PrivateKeys cannot be used to sign stuff anymore after being stored and retrieved from disk. However XMSSMT and SPHINCS PrivateKeys can. Why ?
The Source Code for you to test it yourself is at the bottom.
You will need 2 external libraries for it to work:
the Bouncy Castle Provider
a Bouncy Castle utility library to generate X.509 Certificates
N.B.: The Source Code is about saving and retrieving the Key in/from a KeyStore. But I also tried saving it simply to a file which does not work either.
The Error I get:
java.security.SignatureException: java.lang.NullPointerException
at org.bouncycastle.pqc.jcajce.provider.xmss.XMSSSignatureSpi.engineSign(Unknown Source)
at java.base/java.security.Signature.sign(Signature.java:598)
at Main.sign(Main.java:91)
at Main.run(Main.java:71)
at Main.main(Main.java:22)
[Main]
public class Main {
public static String keyStorePath = "myKeyStore.keystore";
public static void main(String[] args) throws InterruptedException {
String[] algs = {"XMSS", "XMSS", "XMSSMT", "XMSSMT", "SPHINCS256"},
digests = {"SHA256", "SHA512", "SHA256", "SHA512", "SHA512"};
for (int i = 0; i < algs.length; i++){
try {
run(algs[i], digests[i]);
}catch (Exception ignore){
ignore.printStackTrace();
Thread.sleep(60); // Wait for print
}
}
}
public static void run(String alg, String digest) throws Exception{
String sigAlg = digest + "with" + alg,
provider = "BCPQC",
keyStoreProvider = "BC",
keyStoreAlias = sigAlg, // for readability
keyStorePassword = "password";
System.out.println("Running " + sigAlg + ".");
// Add providers
addProvider(new String[] {provider, keyStoreProvider});
// Generate KeyPairs
KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg, provider);
initialize(kpg, alg, digest);
KeyPair myKp = kpg.generateKeyPair();
// Sign
sign(myKp.getPrivate(), sigAlg, provider, "Hello World!");
// Generate a self-signed certificate
X509Certificate cert = BCCertGen.generate(myKp.getPrivate(), myKp.getPublic(), 365, sigAlg, true);
// Load a KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12", keyStoreProvider);
try {
keyStore.load(new FileInputStream(keyStorePath), keyStorePassword.toCharArray());
}catch (Exception ingore){
// If there is no KeyStore at #keyStorePath
// create an empty one
keyStore.load(null, keyStorePassword.toCharArray());
}
// Store the generated KeyPair with the Certificate in the KeyStore
keyStore.setKeyEntry(keyStoreAlias, myKp.getPrivate(), keyStorePassword.toCharArray(), new X509Certificate[] {cert});
keyStore.store(new FileOutputStream(keyStorePath), keyStorePassword.toCharArray());
// Load the stored PrivateKey from the KeyStore
keyStore.load(new FileInputStream(keyStorePath), keyStorePassword.toCharArray());
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyStoreAlias, keyStorePassword.toCharArray());
// Sign again
sign(privateKey, sigAlg, provider, "Hello World!");
}
public static void addProvider(String[] providers){
for (String provider : providers) {
switch (provider) {
case "BCPQC":
Security.addProvider(new BouncyCastlePQCProvider());
break;
case "BC":
Security.addProvider(new BouncyCastleProvider());
break;
}
}
}
public static void sign(PrivateKey pk, String sigAlg, String provider , String payload) throws Exception{
Signature signer = Signature.getInstance(sigAlg, provider);
signer.initSign(pk);
signer.update(payload.getBytes());
signer.sign();
System.out.println("Successfully signed");
}
public static void initialize(KeyPairGenerator kpg, String alg, String digest) throws Exception {
switch (alg) {
case "XMSS":
kpg.initialize(new XMSSParameterSpec(4, digest));
break;
case "XMSSMT":
kpg.initialize(new XMSSMTParameterSpec(4, 2, digest));
break;
case "SPHINCS256":
kpg.initialize(new SPHINCS256KeyGenParameterSpec());
break;
case "RSA":
kpg.initialize(new RSAKeyGenParameterSpec(2048, new BigInteger("5")));
break;
}
}
}
[BCCertGen]
public class BCCertGen {
public static String _country = "Westeros",
_organisation = "Targaryen",
_location = "Valyria",
_state = "Essos",
_issuer = "Some Trusted CA";
public BCCertGen(String country, String organisation, String location, String state, String issuer){
_country = country;
_organisation = organisation;
_location = location;
_state = state;
_issuer = issuer;
}
public static X509Certificate generate(PrivateKey privKey, PublicKey pubKey, int duration, String signAlg, boolean isSelfSigned) throws Exception{
Provider BC = new BouncyCastleProvider();
// distinguished name table.
X500NameBuilder builder = createStdBuilder();
// create the certificate
ContentSigner sigGen = new JcaContentSignerBuilder(signAlg).build(privKey);
X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
new X500Name("cn="+_issuer), //Issuer
BigInteger.valueOf(1), //Serial
new Date(System.currentTimeMillis() - 50000), //Valid from
new Date((long)(System.currentTimeMillis() + duration*8.65*Math.pow(10,7))), //Valid to
builder.build(), //Subject
pubKey //Publickey to be associated with the certificate
);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
cert.checkValidity(new Date());
if (isSelfSigned) {
// check verifies in general
cert.verify(pubKey);
// check verifies with contained key
cert.verify(cert.getPublicKey());
}
ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
return (X509Certificate) fact.generateCertificate(bIn);
}
private static X500NameBuilder createStdBuilder() {
X500NameBuilder builder = new X500NameBuilder(RFC4519Style.INSTANCE);
builder.addRDN(RFC4519Style.c, _country);
builder.addRDN(RFC4519Style.o, _organisation);
builder.addRDN(RFC4519Style.l, _location);
builder.addRDN(RFC4519Style.st, _state);
return builder;
}
}
This is a known issue: https://github.com/bcgit/bc-java/issues/380
Just use a beta version higher than 161b01 as suggested.
We are trying to make the signed signature LTV enabled. I am using the below code to add verification. When signature.isTsp() is false, the PDF says Signature is not LTV enabled, though in the other case (signature.isTsp() is true) it shows as valid.
When we open the PDF and try to manually add verification info by right clicking on the signature it enables LTV without any issue. Not sure what we are missing here.
Any input will be highly helpful.
// Adds LTV-enabled information to the PDF document.
private ByteArrayOutputStream addLtv(final IOcspClient ocspClient,
final ByteArrayOutputStream docStream)
throws IOException, GeneralSecurityException {
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final InputStream signedStream = new ByteArrayInputStream(docStream.toByteArray());
final PdfReader reader = new PdfReader(signedStream);
final PdfDocument document =
new PdfDocument(reader, new PdfWriter(outputStream), new StampingProperties().useAppendMode());
final LtvVerification verification = new LtvVerification(document);
final SignatureUtil signatureUtil = new SignatureUtil(document);
final List<String> signatureNames = signatureUtil.getSignatureNames();
final String sigName = signatureNames.get(signatureNames.size() - 1);
final PdfPKCS7 signature = signatureUtil.verifySignature(sigName);
final CrlClientOnline crl = new CrlClientOnline();
if (!signature.isTsp()) {
for (final String name: signatureNames) {
addVerificationInfo(ocspClient, verification, crl, name);
}
} else {
addVerificationInfo(ocspClient, verification, crl, sigName);
}
document.close();
return outputStream;
}
private void addVerificationInfo(final IOcspClient ocspClient, final LtvVerification verification,
final CrlClientOnline crl,
final String name) throws IOException, GeneralSecurityException {
verification.addVerification(
name, ocspClient, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
The main reason why your code does not always LTV-enable PDFs is that it does not add validation information related to OCSP response signatures.
It doesn't add validation information for CRL signatures, either. As CRLs usually are signed by the issuer certificate of the signer certificate, though, and as validation information for that issuer certificate have already been added in the context of the main signature, LTV-enabling usually does not fail because of missing CRL signature validation information. So if you can use CRLs only, chances are that your code indeed already does LTV-enable PDFs.
In the context of this answer (in particular its section "An approach using an own utility class") I created an utility class AdobeLtvEnabling for iText 5 allowing to LTV-enable PDFs, mostly using bits and pieces found in iText 5 itself. In contrast to your code it does add validation information for OCSP response signatures (and also for CRL signatures).
Here you can find the port of that class to iText 7.
The utility class AdobeLtvEnabling
This utility class bundles the code required for LTV enabling the signatures in a signed PDF document. The code pieces mostly have been taken from existing iText code. The main reason why this class has not been designed to derive from LtvVerification is that required variables and methods from that class are private. As the class originally has been written for iText 5, some iText-5-isms probably can be found in it...
public class AdobeLtvEnabling {
/**
* Use this constructor with a {#link PdfDocument} in append mode. Otherwise
* the existing signatures will be damaged.
*/
public AdobeLtvEnabling(PdfDocument pdfDocument) {
this.pdfDocument = pdfDocument;
}
/**
* Call this method to have LTV information added to the {#link PdfDocument}
* given in the constructor.
*/
public void enable(IOcspClient ocspClient, ICrlClient crlClient) throws OperatorException, GeneralSecurityException, IOException, StreamParsingException, OCSPException {
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
List<String> names = signatureUtil.getSignatureNames();
for (String name : names) {
PdfPKCS7 pdfPKCS7 = signatureUtil.verifySignature(name, BouncyCastleProvider.PROVIDER_NAME);
PdfSignature sig = signatureUtil.getSignature(name);
List<X509Certificate> certificatesToCheck = new ArrayList<>();
certificatesToCheck.add(pdfPKCS7.getSigningCertificate());
while (!certificatesToCheck.isEmpty()) {
X509Certificate certificate = certificatesToCheck.remove(0);
addLtvForChain(certificate, ocspClient, crlClient, getSignatureHashKey(sig));
}
}
outputDss();
}
//
// the actual LTV enabling methods
//
void addLtvForChain(X509Certificate certificate, IOcspClient ocspClient, ICrlClient crlClient, PdfName key) throws GeneralSecurityException, IOException, StreamParsingException, OperatorCreationException, OCSPException {
ValidationData validationData = new ValidationData();
while (certificate != null) {
System.out.println(certificate.getSubjectX500Principal().getName());
X509Certificate issuer = getIssuerCertificate(certificate);
validationData.certs.add(certificate.getEncoded());
byte[] ocspResponse = ocspClient.getEncoded(certificate, issuer, null);
if (ocspResponse != null) {
System.out.println(" with OCSP response");
validationData.ocsps.add(ocspResponse);
X509Certificate ocspSigner = getOcspSignerCertificate(ocspResponse);
if (ocspSigner != null) {
System.out.printf(" signed by %s\n", ocspSigner.getSubjectX500Principal().getName());
}
addLtvForChain(ocspSigner, ocspClient, crlClient, getOcspHashKey(ocspResponse));
} else {
Collection<byte[]> crl = crlClient.getEncoded(certificate, null);
if (crl != null && !crl.isEmpty()) {
System.out.printf(" with %s CRLs\n", crl.size());
validationData.crls.addAll(crl);
for (byte[] crlBytes : crl) {
addLtvForChain(null, ocspClient, crlClient, getCrlHashKey(crlBytes));
}
}
}
certificate = issuer;
}
validated.put(key, validationData);
}
void outputDss() throws IOException {
PdfDictionary dss = new PdfDictionary();
PdfDictionary vrim = new PdfDictionary();
PdfArray ocsps = new PdfArray();
PdfArray crls = new PdfArray();
PdfArray certs = new PdfArray();
PdfCatalog catalog = pdfDocument.getCatalog();
if (pdfDocument.getPdfVersion().compareTo(PdfVersion.PDF_2_0) < 0) {
catalog.addDeveloperExtension(PdfDeveloperExtension.ESIC_1_7_EXTENSIONLEVEL5);
catalog.addDeveloperExtension(new PdfDeveloperExtension(PdfName.ADBE, new PdfName("1.7"), 8));
}
for (PdfName vkey : validated.keySet()) {
PdfArray ocsp = new PdfArray();
PdfArray crl = new PdfArray();
PdfArray cert = new PdfArray();
PdfDictionary vri = new PdfDictionary();
for (byte[] b : validated.get(vkey).crls) {
PdfStream ps = new PdfStream(b);
ps.setCompressionLevel(CompressionConstants.DEFAULT_COMPRESSION);
ps.makeIndirect(pdfDocument);
crl.add(ps);
crls.add(ps);
crls.setModified();
}
for (byte[] b : validated.get(vkey).ocsps) {
b = buildOCSPResponse(b);
PdfStream ps = new PdfStream(b);
ps.setCompressionLevel(CompressionConstants.DEFAULT_COMPRESSION);
ps.makeIndirect(pdfDocument);
ocsp.add(ps);
ocsps.add(ps);
ocsps.setModified();
}
for (byte[] b : validated.get(vkey).certs) {
PdfStream ps = new PdfStream(b);
ps.setCompressionLevel(CompressionConstants.DEFAULT_COMPRESSION);
ps.makeIndirect(pdfDocument);
cert.add(ps);
certs.add(ps);
certs.setModified();
}
if (ocsp.size() > 0) {
ocsp.makeIndirect(pdfDocument);
vri.put(PdfName.OCSP, ocsp);
}
if (crl.size() > 0) {
crl.makeIndirect(pdfDocument);
vri.put(PdfName.CRL, crl);
}
if (cert.size() > 0) {
cert.makeIndirect(pdfDocument);
vri.put(PdfName.Cert, cert);
}
vri.put(PdfName.TU, new PdfDate().getPdfObject());
vri.makeIndirect(pdfDocument);
vrim.put(vkey, vri);
}
vrim.makeIndirect(pdfDocument);
vrim.setModified();
dss.put(PdfName.VRI, vrim);
if (ocsps.size() > 0) {
ocsps.makeIndirect(pdfDocument);
dss.put(PdfName.OCSPs, ocsps);
}
if (crls.size() > 0) {
crls.makeIndirect(pdfDocument);
dss.put(PdfName.CRLs, crls);
}
if (certs.size() > 0) {
certs.makeIndirect(pdfDocument);
dss.put(PdfName.Certs, certs);
}
dss.makeIndirect(pdfDocument);
dss.setModified();
catalog.put(PdfName.DSS, dss);
}
//
// VRI signature hash key calculation
//
static PdfName getCrlHashKey(byte[] crlBytes) throws NoSuchAlgorithmException, IOException, CRLException, CertificateException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509CRL crl = (X509CRL)cf.generateCRL(new ByteArrayInputStream(crlBytes));
byte[] signatureBytes = crl.getSignature();
DEROctetString octetString = new DEROctetString(signatureBytes);
byte[] octetBytes = octetString.getEncoded();
byte[] octetHash = hashBytesSha1(octetBytes);
PdfName octetName = new PdfName(convertToHex(octetHash));
return octetName;
}
static PdfName getOcspHashKey(byte[] basicResponseBytes) throws NoSuchAlgorithmException, IOException {
BasicOCSPResponse basicResponse = BasicOCSPResponse.getInstance(basicResponseBytes);
byte[] signatureBytes = basicResponse.getSignature().getBytes();
DEROctetString octetString = new DEROctetString(signatureBytes);
byte[] octetBytes = octetString.getEncoded();
byte[] octetHash = hashBytesSha1(octetBytes);
PdfName octetName = new PdfName(convertToHex(octetHash));
return octetName;
}
static PdfName getSignatureHashKey(PdfSignature sig) throws NoSuchAlgorithmException, IOException {
PdfString contents = sig.getContents();
byte[] bc = PdfEncodings.convertToBytes(contents.getValue(), null);
if (PdfName.ETSI_RFC3161.equals(sig.getSubFilter())) {
try ( ASN1InputStream din = new ASN1InputStream(new ByteArrayInputStream(bc)) ) {
ASN1Primitive pkcs = din.readObject();
bc = pkcs.getEncoded();
}
}
byte[] bt = hashBytesSha1(bc);
return new PdfName(convertToHex(bt));
}
static byte[] hashBytesSha1(byte[] b) throws NoSuchAlgorithmException {
MessageDigest sh = MessageDigest.getInstance("SHA1");
return sh.digest(b);
}
static String convertToHex(byte[] bytes) {
ByteBuffer buf = new ByteBuffer();
for (byte b : bytes) {
buf.appendHex(b);
}
return PdfEncodings.convertToString(buf.toByteArray(), null).toUpperCase();
}
//
// OCSP response helpers
//
static X509Certificate getOcspSignerCertificate(byte[] basicResponseBytes) throws CertificateException, OCSPException, OperatorCreationException {
JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME);
BasicOCSPResponse borRaw = BasicOCSPResponse.getInstance(basicResponseBytes);
BasicOCSPResp bor = new BasicOCSPResp(borRaw);
for (final X509CertificateHolder x509CertificateHolder : bor.getCerts()) {
X509Certificate x509Certificate = converter.getCertificate(x509CertificateHolder);
JcaContentVerifierProviderBuilder jcaContentVerifierProviderBuilder = new JcaContentVerifierProviderBuilder();
jcaContentVerifierProviderBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
final PublicKey publicKey = x509Certificate.getPublicKey();
ContentVerifierProvider contentVerifierProvider = jcaContentVerifierProviderBuilder.build(publicKey);
if (bor.isSignatureValid(contentVerifierProvider))
return x509Certificate;
}
return null;
}
static byte[] buildOCSPResponse(byte[] BasicOCSPResponse) throws IOException {
DEROctetString doctet = new DEROctetString(BasicOCSPResponse);
ASN1EncodableVector v2 = new ASN1EncodableVector();
v2.add(OCSPObjectIdentifiers.id_pkix_ocsp_basic);
v2.add(doctet);
ASN1Enumerated den = new ASN1Enumerated(0);
ASN1EncodableVector v3 = new ASN1EncodableVector();
v3.add(den);
v3.add(new DERTaggedObject(true, 0, new DERSequence(v2)));
DERSequence seq = new DERSequence(v3);
return seq.getEncoded();
}
//
// X509 certificate related helpers
//
static X509Certificate getIssuerCertificate(X509Certificate certificate) throws IOException, StreamParsingException {
String url = getCACURL(certificate);
if (url != null && url.length() > 0) {
HttpURLConnection con = (HttpURLConnection)new URL(url).openConnection();
if (con.getResponseCode() / 100 != 2) {
throw new PdfException(PdfException.InvalidHttpResponse1).setMessageParams(con.getResponseCode());
}
InputStream inp = (InputStream) con.getContent();
X509CertParser parser = new X509CertParser();
parser.engineInit(new ByteArrayInputStream(StreamUtil.inputStreamToArray(inp)));
return (X509Certificate) parser.engineRead();
}
return null;
}
static String getCACURL(X509Certificate certificate) {
ASN1Primitive obj;
try {
obj = getExtensionValue(certificate, Extension.authorityInfoAccess.getId());
if (obj == null) {
return null;
}
ASN1Sequence AccessDescriptions = (ASN1Sequence) obj;
for (int i = 0; i < AccessDescriptions.size(); i++) {
ASN1Sequence AccessDescription = (ASN1Sequence) AccessDescriptions.getObjectAt(i);
if ( AccessDescription.size() != 2 ) {
continue;
}
else if (AccessDescription.getObjectAt(0) instanceof ASN1ObjectIdentifier) {
ASN1ObjectIdentifier id = (ASN1ObjectIdentifier)AccessDescription.getObjectAt(0);
if ("1.3.6.1.5.5.7.48.2".equals(id.getId())) {
ASN1Primitive description = (ASN1Primitive)AccessDescription.getObjectAt(1);
String AccessLocation = getStringFromGeneralName(description);
if (AccessLocation == null) {
return "" ;
}
else {
return AccessLocation ;
}
}
}
}
} catch (IOException e) {
return null;
}
return null;
}
static ASN1Primitive getExtensionValue(X509Certificate certificate, String oid) throws IOException {
byte[] bytes = certificate.getExtensionValue(oid);
if (bytes == null) {
return null;
}
ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bytes));
ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets()));
return aIn.readObject();
}
static String getStringFromGeneralName(ASN1Primitive names) throws IOException {
ASN1TaggedObject taggedObject = (ASN1TaggedObject) names ;
return new String(ASN1OctetString.getInstance(taggedObject, false).getOctets(), "ISO-8859-1");
}
//
// inner class
//
static class ValidationData {
final List<byte[]> crls = new ArrayList<byte[]>();
final List<byte[]> ocsps = new ArrayList<byte[]>();
final List<byte[]> certs = new ArrayList<byte[]>();
}
//
// member variables
//
final PdfDocument pdfDocument;
final Map<PdfName,ValidationData> validated = new HashMap<PdfName,ValidationData>();
}
(AdobeLtvEnabling.java)
Example use
You can use the AdobeLtvEnabling class like this:
try ( PdfReader pdfReader = new PdfReader(SOURCE);
PdfWriter pdfWriter = new PdfWriter(TARGET);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter,
new StampingProperties().preserveEncryption().useAppendMode())) {
AdobeLtvEnabling adobeLtvEnabling = new AdobeLtvEnabling(pdfDocument);
IOcspClient ocsp = new OcspClientBouncyCastle(null);
ICrlClient crl = new CrlClientOnline();
adobeLtvEnabling.enable(ocsp, crl);
}
(MakeLtvEnabled test testLtvEnableSignWithoutLtv)
Limitations
As this code essentially is ported from the iText 5 code from the referenced answer, it also inherits the limitations listed in that answer:
The code works under some simplifying restrictions, in particular:
signature time stamps are ignored,
retrieved CRLs are assumed to be direct and complete,
the complete certificate chains are assumed to be buildable using AIA entries.
You can improve the code accordingly if these restrictions are not acceptable for you.
I have written the below code to verify the signature of a file using a certificate that is there in my certificate store. But when I try to get its signature and pass it to the SignedData method, I am getting the below exception.
org.bouncycastle.cms.CMSException: Malformed content.
at org.bouncycastle.cms.CMSUtils.readContentInfo(Unknown Source)
at org.bouncycastle.cms.CMSUtils.readContentInfo(Unknown Source)
at org.bouncycastle.cms.CMSSignedData.<init>(Unknown Source)
at VerifyFinal.main(VerifyFinal.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.IllegalArgumentException: unknown object in getInstance: org.bouncycastle.asn1.DERApplicationSpecific
at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source)
at org.bouncycastle.asn1.cms.ContentInfo.getInstance(Unknown Source)
... 9 more
Below is the code I used to verify the signature of the file.
Security.addProvider(new BouncyCastleProvider());
KeyStore msCertStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
msCertStore.load(null, null);
X509Certificate cer = ((X509Certificate) msCertStore.getCertificate("Software View Certificate Authority"));
PublicKey pubKey = cer.getPublicKey();
byte[] sigToVerify = cer.getSignature();
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initVerify(pubKey);
CMSSignedData cms = new CMSSignedData(cer.getSignature());
Store store = cms.getCertificates();
SignerInformationStore signers = cms.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = store.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
System.out.println("verified");
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
In case you need, below is how I signed the file.
File file = new File("G:\\Projects\\test.zip");
fin = new FileInputStream(file);
byte fileContent[] = new byte[(int) file.length()];
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray());
//Sign
PrivateKey privKey = (PrivateKey) key;
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privKey);
signature.update(fileContent);
//Build CMS
X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
List certList = new ArrayList();
CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
certList.add(cert);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, cert));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, true);
BASE64Encoder encoder = new BASE64Encoder();
String signedContent = encoder.encode((byte[]) sigData.getSignedContent().getContent());
System.out.println("Signed content: " + signedContent + "\n");
String envelopedData = encoder.encode(sigData.getEncoded());
System.out.println("Enveloped data: " + envelopedData);
AFTER COMMENTS FROM VOLKERK :
How I generate the signature+data file :
public static void main(String[] args) throws Exception {
// String text = "This is a message";
// File file = new
// File("C:\\Users\\mayooranM\\Desktop\\SignatureVerificationTest\\ProcessExplorer.zip");
// fin = new FileInputStream(file);
// byte fileContent[] = new byte[(int) file.length()];
Path filepath = Paths.get("G:\\IntelliJTestProjects\\googleplaces.zip");
byte[] fileContent = Files.readAllBytes(filepath);
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray());
// Sign
PrivateKey privKey = (PrivateKey) key;
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privKey);
signature.update(fileContent);
// Build CMS
X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
List certList = new ArrayList();
CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
certList.add(cert);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(sha1Signer, cert));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, true);
BASE64Encoder encoder = new BASE64Encoder();
String signedContent = encoder.encode((byte[]) sigData.getSignedContent().getContent());
System.out.println("Signed content: " + signedContent + "\n");
String envelopedData = encoder.encode(sigData.getEncoded());
System.out.println("Enveloped data: " + envelopedData);
FileOutputStream fos = new FileOutputStream(
"G:\\IntelliJTestProjects\\SignedZip.zip");
fos.write(envelopedData.getBytes());
fos.close();
}
How I verify data:
public static void main(String[] args) {
try {
Security.addProvider(new BouncyCastleProvider());
Path path = Paths
.get("G:\\IntelliJTestProjects\\SignedZip.zip");
byte[] signedContent = Files.readAllBytes(path);
String output = new String(signedContent);
System.out.println("output: " + output);
CMSSignedData cms = new CMSSignedData(Base64.decode(signedContent));
Store store = cms.getCertificates();
SignerInformationStore signers = cms.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = store.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
System.out.println("verified");
}
}
CMSProcessable origData = cms.getSignedContent() ;
byte[] originalContent = (byte[]) origData.getContent();
ZipInputStream zipStream = new ZipInputStream(new ByteArrayInputStream(originalContent));
ZipEntry entry = null;
while ((entry = zipStream.getNextEntry()) != null) {
String entryName = entry.getName();
FileOutputStream out = new FileOutputStream(entryName);
byte[] byteBuff = new byte[4096];
int bytesRead = 0;
while ((bytesRead = zipStream.read(byteBuff)) != -1)
{
out.write(byteBuff, 0, bytesRead);
}
out.close();
zipStream.closeEntry();
}
zipStream.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
Part 2
Ok, now that you've got a file containing the pkcs7 signed data, let's try to retrieve the contents and verify the integrity ...and validity.
The goal is again not to load the whole thing into memory. Looks like CMSSignedDataParser can do that.
Since the documentation says
Note: that because we are in a streaming mode [...] it is important that the methods on the parser are called in the appropriate order.
So, let's first look at what we actual got so far. For that I used a text file containing the line Mary had a little lamb as the input file, instead of the .m4v (or the .zip in your case) and passed the result to http://lapo.it/asn1js/ (you got to love this tool....)
So, the actual contents comes before the signature data and we have to read the entries in the order they appear in the file. Would be easier the other way round, but ...
The idea is to write the contents to the target file regardless of whether it checks out or not. If it doesn't just delete file. (Drawback: If it contains e.g. a virus a virus scanner might be triggered ...too bad. I leave handling that up to you.)
public class SignedDataTest {
... see Part 1
private static void verify(Path signedFile, Path extractToFile) throws Exception {
FileInputStream fis = new FileInputStream(signedFile.toFile());
DigestCalculatorProvider build = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
CMSSignedDataParser sp = new CMSSignedDataParser(build, fis);
// we have to read the whole stream sp.getSignedContent().getContentStream()
// just copy it to the target file
Files.copy(sp.getSignedContent().getContentStream(), extractToFile, StandardCopyOption.REPLACE_EXISTING);
// now we can go on with the other stuff.....
Store certStore = sp.getCertificates();
// the examples create a new instance of this for each certificate.
// I don't think that's necessary, but you might want to look into that...
JcaSimpleSignerInfoVerifierBuilder verifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC");
for (Object objSigner : sp.getSignerInfos().getSigners()) {
SignerInformation signer = (SignerInformation) objSigner;
// as I understand it, there should be only one match ....but anyways....
for (Object objMatch : certStore.getMatches(signer.getSID())) {
X509CertificateHolder certHolder = (X509CertificateHolder) objMatch;
System.out.print("verifying against " + certHolder.getSubject().toString());
if (signer.verify(verifier.build(certHolder))) {
System.out.println(": verified");
} else {
System.out.println(": no match");
}
}
}
}
}
So, what does this actually do/test for? It fetches the signer info from the pkcs7 signedData and then checks the hash and the signature againt the certificates that are contained in the signedData. Not good enough, I and any other attacker can put any certificate in there; so I create a new KeyPiar generate a selfsigned certificate for that key pair and put just any zip file in there I like, preferably a nasty phishing tool.
That's most likely the reason why you've used KeyStore.getInstance("Windows-MY", "SunMSCAPI") in your code; a KeyStore which you implictly trust. So, let's do just that.
Instead of building the SignerInformationVerifier from the data in the signedData file, we pass a ready-made verfier to the method. And this verifier is primed with the certificate from the windows "KeyStore". Btw: you cannot mix the BC and the SunMSCAPI providers arbitrarily; but you can mix them this way, i.e. have BC check the data integrity and SunMSCAPI check whether the hash has been signed by something considered trustworthy.
(sorry, got to go. I'll post just the complete sample class; there's a lot to say about it though ....actually one could write books about that ...actually actually books have been written about that topic ;-) )
public class SignedDataTest {
private static final File KEYSTORE_FILE = new File("c:\\temp\\Software_View_Certificate_Authority.p12");
private static final String KEYSTORE_TYPE = "pkcs12";
private static final char[] KEYSTORE_PWD = "foobar".toCharArray();
private static final String KEYSTORE_ALIAS = "Software View Certificate Authority";
private static final Path CONTENT_SRC_PATH = Paths.get("c:\\temp\\test.txt");
private static final Path CONTENT_TARGET_PATH = Paths.get("c:\\temp\\test-retrieved.txt");
private static final Path SIGNEDDATA_TARGET_PATH = Paths.get("c:\\temp\\test.txt.signed.pkcs7");
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
doForth();
andBack();
}
private static void doForth() throws Exception {
KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE, "BC");
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD);
X500PrivateCredential creds = new X500PrivateCredential(
(X509Certificate) ks.getCertificate(KEYSTORE_ALIAS),
(PrivateKey) ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD)
);
createSignature(CONTENT_SRC_PATH, creds, new FileOutputStream(SIGNEDDATA_TARGET_PATH.toFile()));
}
private static void andBack() throws Exception {
KeyStore msCertStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
msCertStore.load(null, null);
SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider("SunMSCAPI")
.build(((X509Certificate) msCertStore.getCertificate("Software View Certificate Authority")));
verify(SIGNEDDATA_TARGET_PATH, CONTENT_TARGET_PATH, verifier);
}
private static void verify(Path signedFile, Path extractToFile, SignerInformationVerifier verifier) throws Exception {
FileInputStream fis = new FileInputStream(signedFile.toFile());
DigestCalculatorProvider build = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
CMSSignedDataParser sp = new CMSSignedDataParser(build, fis);
// we have to read the whole stream sp.getSignedContent().getContentStream()
// just copy it to the target file
Files.copy(sp.getSignedContent().getContentStream(), extractToFile, StandardCopyOption.REPLACE_EXISTING);
// now we can go on with the other stuff.....
Store certStore = sp.getCertificates();
// the examples create a new instance of this for each certificate.
// I don't think that's necessary, but you might want to look into that...
for (Object objSigner : sp.getSignerInfos().getSigners()) {
SignerInformation signer = (SignerInformation) objSigner;
if (signer.verify(verifier)) {
System.out.println("verified");
// now(!) you want to keep the target content file
} else {
// actually a "org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value"
// exception will be thrown in case the contents has been altered
// So, you will need a try-catch(-finally?) construct to delete the target contents file in such cases....
System.out.println("no match");
}
}
}
private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator() {
{
addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
).build(
new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(creds.getPrivateKey()),
creds.getCertificate()
)
);
addCertificates(new JcaCertStore(new ArrayList<X509Certificate>() {
{
add(creds.getCertificate());
}
}));
}
};
try (OutputStream sigOut = gen.open(target, true)) {
Files.copy(srcfile, sigOut);
}
}
}
Part 1
Let's start with some transformations of the code. (I wonder how long an answer can be....)
Step 1: Not much going on here; just setting up the "framework" - and as you can see: yes, I'm really running/testing the code ;-)
public class SignedDataTest {
private static final File KEYSTORE_FILE = new File("c:\\temp\\Software_View_Certificate_Authority.p12");
private static final String KEYSTORE_TYPE = "pkcs12";
private static final char[] KEYSTORE_PWD = "foobar".toCharArray();
private static final String KEYSTORE_ALIAS = "Software View Certificate Authority";
private static final Path CONTENT_SRC_PATH = Paths.get("c:\\temp\\Londo Buttons are melting.m4v");
private static final Path CONTENT_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v");
private static final Path SIGNEDDATA_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v.signed.pkcs7");
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
createSignature();
}
private static void createSignature() throws Exception {
byte[] fileContent = Files.readAllBytes(CONTENT_SRC_PATH);
KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE, "BC");
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD);
Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD);
// Sign
PrivateKey privKey = (PrivateKey)key;
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privKey);
signature.update(fileContent);
// Build CMS
X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
List certList = new ArrayList();
CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
certList.add(cert);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(sha1Signer, cert));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, true);
BASE64Encoder encoder = new BASE64Encoder();
String signedContent = encoder.encode((byte[]) sigData.getSignedContent().getContent());
System.out.println("Signed content: " + signedContent + "\n");
String envelopedData = encoder.encode(sigData.getEncoded());
System.out.println("Enveloped data: " + envelopedData);
FileOutputStream fos = new FileOutputStream(SIGNEDDATA_TARGET_PATH.toString());
fos.write(envelopedData.getBytes());
fos.close();
}
}
Step 2: This is probably the hardest; the transformation that makes the code look unlike your code the most. Take your time to understand what I'm doing here. I want to get rid of some of the unecessary stuff (e.g. the Base64 encoder) and get a bit more condensed code. This makes debugging a bit harder, since I removed most of the temporary variables, "hiding" them in the initializer block - doh, what's the proper name of that feature?)
public class SignedDataTest {
private static final File KEYSTORE_FILE = new File("c:\\temp\\Software_View_Certificate_Authority.p12");
private static final String KEYSTORE_TYPE = "pkcs12";
private static final char[] KEYSTORE_PWD = "foobar".toCharArray();
private static final String KEYSTORE_ALIAS = "Software View Certificate Authority";
private static final Path CONTENT_SRC_PATH = Paths.get("c:\\temp\\Londo Buttons are melting.m4v");
private static final Path CONTENT_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v");
private static final Path SIGNEDDATA_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v.signed.pkcs7");
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
doForth();
// doBack();
}
private static void doForth() throws Exception {
KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE, "BC");
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD);
X500PrivateCredential creds = new X500PrivateCredential(
(X509Certificate) ks.getCertificate(KEYSTORE_ALIAS),
(PrivateKey) ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD)
);
createSignature(CONTENT_SRC_PATH, creds, new FileOutputStream(SIGNEDDATA_TARGET_PATH.toFile()));
}
private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
byte[] fileContent = Files.readAllBytes(CONTENT_SRC_PATH);
// Sign
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(creds.getPrivateKey());
signature.update(fileContent);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator() {
{
addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
).build(
new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(creds.getPrivateKey()),
creds.getCertificate()
)
);
addCertificates(new JcaCertStore(new ArrayList<X509Certificate>() {
{
add(creds.getCertificate());
}
}));
}
};
// Build CMS
CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
CMSSignedData sigData = gen.generate(msg, true);
// write raw data instead of base64
target.write(sigData.getEncoded());
}
}
Step 3: This is probably the most important step: It changes the code from "cannot work as intended on a fundamental level" to "in principle this might work". You're creating a signature manually and then pass that signature to the CMSSignedDataGenerator as the message. In effect you're creating a signature of a signature; the "real" contents is lost. What you actually want to do is create a signature of the (file) contents:
private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
byte[] fileContent = Files.readAllBytes(CONTENT_SRC_PATH);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator() {
...
};
// Build CMS
CMSTypedData msg = new CMSProcessableByteArray(fileContent);
CMSSignedData sigData = gen.generate(msg, true);
// write raw data instead of base64
target.write(sigData.getEncoded());
}
Step 4: You read the complete contents into memory. That might not be a good idea for a large input file.
private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
CMSSignedDataGenerator gen = new CMSSignedDataGenerator() {
...
};
// see https://www.bouncycastle.org/docs/pkixdocs1.4/org/bouncycastle/cms/CMSProcessableFile.html
CMSProcessableFile msg = new CMSProcessableFile(srcfile.toFile());
CMSSignedData sigData = gen.generate(msg, true);
// write raw data instead of base64
target.write(sigData.getEncoded());
}
Step 5: Again memory usage: gen.generate(msg, true): The true parameter means that the complete msg is included in the asn1-structure. When you call .getEncoded() you get a byte array of the complete asn1-structure, i.e. you have the complete file in memory again. RAM is cheap, but let's try to avoid that anyway. There's another generator called CMSSignedDataStreamGenerator which seems to offer what we need. Instead of working on byte arrays, you give it an OutputStream it can write the result to, and you get OutputStream you can write the contents to:
private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator() {
{
addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
).build(
new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(creds.getPrivateKey()),
creds.getCertificate()
)
);
addCertificates(new JcaCertStore(new ArrayList<X509Certificate>() {
{
add(creds.getCertificate());
}
}));
}
};
try (OutputStream sigOut = gen.open(target, true)) {
Files.copy(srcfile, sigOut);
}
}
So much for creating the signed message. I will post the verify-part in another answer - but have to work for real/for a living for a while.....
edit: There's probably still room to post the complete/final sample class
public class SignedDataTest {
private static final File KEYSTORE_FILE = new File("c:\\temp\\Software_View_Certificate_Authority.p12");
private static final String KEYSTORE_TYPE = "pkcs12";
private static final char[] KEYSTORE_PWD = "foobar".toCharArray();
private static final String KEYSTORE_ALIAS = "Software View Certificate Authority";
private static final Path CONTENT_SRC_PATH = Paths.get("c:\\temp\\Londo Buttons are melting.m4v");
private static final Path CONTENT_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v");
private static final Path SIGNEDDATA_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v.signed.pkcs7");
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
doForth();
//doBack();
}
private static void doForth() throws Exception {
KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE, "BC");
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD);
X500PrivateCredential creds = new X500PrivateCredential(
(X509Certificate) ks.getCertificate(KEYSTORE_ALIAS),
(PrivateKey) ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD)
);
createSignature(CONTENT_SRC_PATH, creds, new FileOutputStream(SIGNEDDATA_TARGET_PATH.toFile()));
}
private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator() {
{
addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
).build(
new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(creds.getPrivateKey()),
creds.getCertificate()
)
);
addCertificates(new JcaCertStore(new ArrayList<X509Certificate>() {
{
add(creds.getCertificate());
}
}));
}
};
try (OutputStream sigOut = gen.open(target, true)) {
Files.copy(srcfile, sigOut);
}
}
}
Data is encrypted using OpenSSL in PHP and i would like to decrypt java but getting error in java
Code for Encryption in PHP-
public function getEncryptedString($cardNumber,$key_id){
$encryptedCardNumber = '';
$key_name = "key_{$key_id}";
$pub_key_path =$key_name.".public";
$fp=fopen ($pub_key_path,"r"); //Open the public key (key_8.public)
$pub_key = fread($fp,8192); //Read public key key (key_8.public) into
fclose($fp);
openssl_public_encrypt($cardNumber,$encryptedCardNumber,$pub_key);
if($key_id > 4) return rawurlencode(base64_encode($encryptedCardNumber));
else return addslashes($encryptedCardNumber);
}
Code for Decryption in JAVA-
public static String getDecryptedValue(int keyId,String encryptedCCNumber ,String passPhrase){
String result="";
String privateKeyFileName="key_8.private";
String privateKeyLocation= PropertiesUtil.getProperty("PUBLIC_PRIVATE_KEY_LOCATION");
String privateKeyFileNameLocation=privateKeyLocation+privateKeyFileName;
String decryptedValue= getDecryptedMessage(privateKeyFileNameLocation,encryptedCCNumber,passPhrase);
return result;
}
public static String getDecryptedMessage(String privateKeyFileNameLocation, String encryptedCCNumber,String passPhrase)
{
byte[] decodedBytesCCNumber= Base64.decodeBase64(encryptedCCNumber.getBytes());
byte[] decryptedMessage=null;
try {
Cipher cipher = Cipher.getInstance("RSA");
PrivateKey privateKey = getPrivateKey(privateKeyFileNameLocation,passPhrase);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
decryptedMessage = cipher.doFinal(decodedBytesCCNumber);
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("new String(decryptedMessage)"+new String(decryptedMessage));
return new String(decryptedMessage);
}
private static PrivateKey getPrivateKey(String privateKeyFileNameLocation,String passPhrase) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(privateKeyFileNameLocation), passPhrase.toCharArray());
String alias = (String) ks.aliases().nextElement();
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(passPhrase.toCharArray()));
return keyEntry.getPrivateKey();
}
Java code is giving below error.
java.io.IOException: toDerInputStream rejects tag type 45
at sun.security.util.DerValue.toDerInputStream(DerValue.java:847)
at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1221)
at java.security.KeyStore.load(KeyStore.java:1214)
You are URL-encoding a Base64-encoding of the ciphertext, but you are only deciphering a base64-decoding of it. Either lose the URL-encoding or decode it at the receiver.