How to associate certificate information to already signed data? - java

I am trying to add digital signature to pdf document using pdf-box library (v2.0.8). I am receiving already signed content from a webservice (signed with only private key). Now I would need to associate certificate information to this signed data so that it can be added to PDF document. How can we add certificate to already signed content, preferably using bouncy castle api ?
// here content is data which has to be signed
public byte[] sign(InputStream content) throws IOException {
try {
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
List<Certificate> certList = new ArrayList<Certificate>();
certList.add(certificate);
Store certs = new JcaCertStore(certList);
gen.addCertificates(certs);
CMSProcessableInputStream msg = new CMSProcessableInputStream(signPrivate(content));
CMSSignedData signedData = gen.generate(msg, false);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DEROutputStream dos = new DEROutputStream(baos);
dos.writeObject(signedData.toASN1Structure());
return baos.toByteArray();
} catch (Exception e) {
throw new IOException(e);
}
}
Here, I am able to generate digital signature, but it does not contain any certificate information. I already checked this and this question but they donot take the case where content is already signed using private key seperatly and only certificate needs to be associated.

(The code you posted refers to CMS signature containers, so I assume we are talking about adbe.pkcs7.detached or ETSI.CAdES.detached PDF signatures.)
When creating a signature in a CMS signature container, one has the choice whether the signature value really only signs the (hash of the) document data or whether it signs a collection of so-called signed attributes (signedAttrs in the SignerInfo specification) and the hash of the document data is but a value of one of those attributes.
SignerInfo ::= SEQUENCE {
version CMSVersion,
sid SignerIdentifier,
digestAlgorithm DigestAlgorithmIdentifier,
signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature SignatureValue,
unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
(RFC 5652 section 5.3. SignerInfo Type)
All profiles hereof to be taken seriously, though, require that you use signed attributes, in particular they require you to use an ESS signing-certificate (RFC 2634 section 5.4) or ESS signing-certificate-v2 (RFC 5035 section 3) signed attribute to reference the signer certificate.
In these attributes, therefore, the association of the signature with its signing certificate is fixed before the signature value is generated.
Thus, you cannot freely associate a signing certificate to an already generated signature.

Related

Extract info about signatures - Cades & Pades (Java)

I have a document (pdf) signed with 1 Pades signature and 2 Cades signatures and I need to extract the information about the signer of each signature.
I'm using CMSSignedData (bouncy castle library), but when I try to get the info, I only get the information about the last signature.
byte[] buffer = new byte[(int) tmpFile.length()];
DataInputStream dataIn = new DataInputStream(new FileInputStream(tmpFile));
dataIn.readFully(buffer);
dataIn.close();
CMSSignedData signature = new CMSSignedData(buffer);
Store cs = signature.getCertificates();
SignerInformationStore signers = signature.getSignerInfos();
String hashOriginalFile = DigestUtils.sha256Hex(
(byte[])signature.getSignedContent().getContent());
List<SignInfo> certificatesInfo = signers.getSigners().stream()
.map(si -> cs.getMatches(si.getSID()))
.flatMap(Collection::stream)
.map(o -> (X509CertificateHolder) o)
.map(cert -> new SignInfo(hashOriginalFile, getCommonName(cert.getSubject()), cert.getIssuer().toString(), null, null))
.collect(Collectors.toList());
There is a way to get all the signatures information? even using another library.
Thank you!
I would say it depends on how the document was signed:
If signatures are added in a parallel way (3 signatures covering the same document hash), you should be able to see them using your code;
If signatures are added sequentially (original document signed first time, then the signed document is signed again, etc.), you should implement a recursive approach.
I'd suggest having a look at the SD-DSS library, which offers this functionality out of the box - see https://github.com/esig/dss . You will probably need to validate the signature in order to retrieve this info.

Is there a way to decrypt S/MIME public key data?

I would like to get the email address and expire date to a S/MIME certificate based on it's public key. Is this aproach even possible? Or am I totally wrong? Can I decrypt the public key to get these kind of data via java?
I searched in google, read the wiki pages and read about an oracle s/mime project. But it doesn't seam like its possible. Are those data only availabe in the csr??
Thanks in advance
I'm amazed this isn't a dupe, but I couldn't find a good one.
Although Bouncy is fine and has many features if you want to use it, core Java can handle X.509 certificates since forever. For a cert in a file (or anything that can be accessed as a Stream) in either PEM or DER format (although the javadoc isn't clear on that) all you need is CertificateFactory:
CertificateFactory fact = CertificateFactory.getInstance("X.509");
// from a real file
InputStream is = new FileInputStream ("filename");
Certificate cert = fact.generateCertificate(is);
is.close(); // or use try-resources to do automatically
// from an alternate/custom filesystem, such as a ZIP
Path p = Paths.get("somespecification"); // or any other creation of a Path
InputStream is = Files.newInputStream(p); // add open options if needed
// same as before
// from the classpath (usually a JAR)
InputStream is = ClassLoader /*or any Class<?> object*/ .getResourceAsStream("name");
// same as before
// from a byte[] in memory
InputStream is = new ByteArrayInputStream (bytearray);
// same as before, except don't really need to close
// you get the idea
Although JCA APIs like this one are defined to allow a lot of extension, reading an X.509 cert will actually give you not just Certificate but subclass X509Certificate from which .getNotAfter() gives the expiration date-time directly. The email address if present (which isn't required by X.509 certs in general, but should always be the case in a cert used for S/MIME) will usually be an attribute in the subject name, which actually has internal structure that Java doesn't let you get at directly so you need to:
String x500name = ((X509Certificate)cert).getSubjectX500Principal()) .toString();
// simple case: no multivalue RDN, no reserved chars ,+="<>\;# or extra spaces
for( String attr : x500name.split(", ") )
if( attr.startsWith("EMAILADDRESS=") )
... use attr.substring(13) ...
// other cases require slightly more complicated parsing
Note there is no encryption at all in X.509, and thus no actual decryption, although many people use 'decrypt' to describe anything unfamiliar not an actual cipher.
File file = new File(fileName);
FileReader fileReader = new FileReader(file);
PEMParser pemParser = new PEMParser(fileReader);
X509CertificateHolder caCertificate = (X509CertificateHolder) pemParser.readObject();

How to convert java KeyStore to PKCS7 .p7b file?

Convert X509 to PKCS7
Create PKCS7 from keystore
I have tried both the answers above but I feel these do not suit my need since Based on the below link I can summarize that PKCS7 is used for two purposes,
Creating signatures, digest etc CMS(Crytographic message syntax)
A container for certificates
Based on this I summarized
My need is more of point no.2. I just want to create a .p7b file using all the certificates that I have in a KeyStore object. Since PKCS7 cannot contain private key. The above two answers generate a signature and what not. Am I missing something? is that the way to go ahead or is there another way?
I can extract certs from a .p7b file using
FileInputStream is = new FileInputStream( "cert.pkcs7" );
CertificateFactory cf = CertificateFactory.getInstance( "X.509" );
Iterator i = cf.generateCertificates( is ).iterator();
while ( i.hasNext() )
{
Certificate c = (Certificate)i.next();
System.out.println(Base64.getEncoder.encodeToString(c.getEncoded());
}
I am asking how to do the reverse, i.e create a .p7b file from Certificate[] or Java KeyStore
Okay I found the solution:
Solution In this we can create what I exactly asked for but I still get the signed data which is generated. I don't want that. A simple .p7b package which I already have has no signerInfo will the .p7b created by this solution have it?
Is this the right way to do it?
Found the solution in this link:
code:
//Export a certificate list to PKCS#7
public static byte[] exportCertificatesAsPkcs7(X509Certificate certs[]) throws Exception {
List certList = new ArrayList();
for (X509Certificate certificate: certs){
certList.add(new X509CertificateHolder(certificate.getEncoded()));
}
Store certStore = new JcaCertStore(certList);
CMSProcessableByteArray msg = new CMSProcessableByteArray("Hello World".getBytes());
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addCertificates(certStore);
CMSSignedData data = gen.generate(msg, "BC");
return data.getEncoded();
}
Useful links related to PKCS7:
Convert X509 to PKCS7
Create PKCS7 from keystore

Java RSA signature mechanism equivalent to emsa-pkcs1-v1_5 with SHA-256

I need to sign data using Java with the SHA-256 and EMSA-PKCS1-V1_5 encoding and RSA private key.
Also I need to use padding type 1 in EMSA-PKCS1-V1_5 which means that the data before signing should be appended by zeros (not random data, as in padding type 2).
The output of the signature on the same data should be the same.
What is equivalent in Java to this mechanism?
I was trying to sign using SHA256withRSA but the output on the same data is always different.
EDIT:
I am using the following code (data, sigAlg, and cert is always the same, so I assume that the signature should be always the same):
CMSSignedDataStreamGenerator generator = new CMSSignedDataStreamGenerator();
String sigAlg = "SHA256withRSA";
ContentSigner contentSigner = new JcaContentSignerBuilder(sigAlg).setProvider("BC").build(getPrivateKey());
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(contentSigner, cert));
generator.addCertificates(new JcaCertStore(certs));
OutputStream out = generator.open(responseOutputStream);
out.write(data);
out.close();

Wrong vertificate signature algorithm in X509Certificate for SHA256withDSA using Java

I am having certificate with key type DSA, bit length 1024, Signature algorithm SHA256:
I am converting it to X509Certificate in java. When I am trying to get signature algorithm from X509Certificate I am getting something like 2.16.840.1.101.3.4.3.2.
CertificateFactory factory=CertificateFactory.getInstance("X.509");
X509Certificate cert=(X509Certificate) factory.generateCertificate(inputStream);
System.out.println(cert.getSigAlgName());
Above method working for all other type (getting name correctly as SHA256withRSA). Not working for SHA256withDSA (getting 2.16.840.1.101.3.4.3.2 Expecting SHA256withDSA). How can I get correct signature algorithm from certificate? Is there any other way to do it?
According X.509 specification Section 4.1.2.3
This field contains the algorithm identifier for the algorithm used by the CA to sign the certificate.
This field MUST contain the same algorithm identifier as the signatureAlgorithm field in the sequence Certificate (Section 4.1.1.2). The contents of the optional parameters field will vary according to the algorithm identified. [RFC3279], [RFC4055], and [RFC4491] list supported signature algorithms, but other signature algorithms MAY also be supported.
It's means X509Certificate#getSigAlgName returned algorithm used by the CA to sign the certificate, not algorithm used by end user (from current certificate) to sign data/document.
If you need take end-user algorithm, you must using another way.
Eventually you can use one certificate for differents compatible signature algorithms. Example: RSA certificate for SHA1withRSA and SHA256withRSA
Here is the code I tried in Eclipse:
InputStream inStream = null;
try {
inStream = new FileInputStream("<cert-file-name-with-path>");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
System.out.println("##"+cert.getSigAlgName()+"##"+cert.getSigAlgOID()+"##"+cert.getType());
} finally {
if (inStream != null) {
inStream.close();
}
}
Output:
SHA256withDSA##2.16.840.1.101.3.4.3.2##X.509

Categories