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
Related
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();
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.
I have a short question: What does this call return exactely?
context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0].toByteArray();
I know it returns the first app certificate for the app which is the CERT.RSA in the META-INF folder, but what exately does it return? Just a byte-array which represents the whole certificate as the file or some other byte-array? I don't really know much about the structure of certificates and the data they contain so I really don't have any clue.
The best answer would be an instruction for openssl with that I get the returned value from the above code line.
I finally tested it myself on an android simulator and got the final answer. It's actually not hard to understand once I realized that PKCS7 is just a storage-form or rather a container for various signature-types.
Within the app
The call returns the first signature within the CERT.RSA file. It's a PKCS7 file which embeds the X.509-certificate and from what I've read it's always just one signature for android apps.
Signature sig = context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0];
This Signature obtained from above can be directly used to generate a working X.509-certificate like this (taken from here):
byte[] rawCert = sig.toByteArray();
InputStream certStream = new ByteArrayInputStream(rawCert);
CertificateFactory certFactory;
X509Certificate x509Cert;
try {
certFactory = CertificateFactory.getInstance("X509");
x509Cert = (X509Certificate) certFactory.generateCertificate(certStream);
//do stuff with your certificate
} catch(Exception ex) {
//handle exception
}
Anywhere else
If you have the certificate outside of your own android app and want the same byte-stream, that is provided by the function above you can do the same with a simple Java-program like this:
FileInputStream is = new FileInputStream("CERT.RSA");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate c = (X509Certificate) cf.generateCertificates(is).toArray()[0];
byte[] rawCert = c.getEncoded();
This code first reads the file, creates the CertificateFactory and then the important step it to isolate the first certificate in the PKCS7-container. And then c.getEncoded() finally gives you the exact same representation as the method above.
openssl
And last but not least the openssl-command for it(taken from here):
openssl pkcs7 -inform DER -in CERT.RSA -print_certs -text
It will give you a pretty overview of the information contained and at the end the
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
block. It contains the same data as above. If you parse the contents of this block and decode it with base64 it will give you the exact same byte array as in the upper two examples.
context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0]
Would return the representation of the signing certificate associated with an application package. It would be an instance of a Signature class as defined here in the documentation. (The name 'Signature' is slightly misleading and even mentioned in the documentation itself).
context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0].toByteArray();
Would return its byte Array representation. ie: The certificate file's byte array representation.
Adding to that, a certificate is nothing new than a text file, but the data is structured into a common format and encoded. X.509 is one of the most widely used formats. You can find it here. The RFC for a X.509 certificate is here. Certificates are structured such that they can be easily checked if altered by a 3rd party.
There isn't any openssl command which directly returns a byte[]. The closest which I could think of is the openssl command which you can use to get the textual representation of a certificate.
$ openssl x509 -in <your-certificate> -noout -text
The signatures member of PackageInfo is simply an array of all signatures read from the package file.
public Signature[] signatures;
The class android.content.pm.Signature stores a signature in the form
private final byte[] mSignature;
This is what toByteArray() does:
/**
* #return the contents of this signature as a byte array.
*/
public byte[] toByteArray() {
byte[] bytes = new byte[mSignature.length];
System.arraycopy(mSignature, 0, bytes, 0, mSignature.length);
return bytes;
}
So essentially this code simply provides you the byte array of a signature used to sign the package.
Assuming CERT.RSA is the RSA file in META-INF folder of the apk, you can get the package MD5, SHA1, and SHA256 signatures through:
keytool -printcert -file CERT.RSA
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
I was advised to look here: http://exampledepot.com/egs/javax.crypto/DesFile.html for the source code of encryption/decryption using import javax.crypto. I have generated my key via keytool and now I don't know how to pass my generated keys into that application for encryption and decryption.
My situation is, that I have a XML file stored online (it stores configuration details) and before I parse it with a XML parser I have to decrypt it. First of all I should of course encrypt it, before it goes online.
Question is: How to pass my generated keys into code visible in link in first row?
Thanks
How to load a KeyStore is documented in the JavaDoc of the KeyStore class:
import java.io.FileInputStream;
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// get user password and file input stream
char[] password = getPassword();
try (FileInputStream fis = new FileInputStream("C:/mykeystore.jks")) {
ks.load(fis, password);
}
Once you have loaded the key store you can load the key:
Key myKey = ks.getKey("mykeyalias", password);
The key alias is the one you have specified using keytool.
Using the myKey you can initialize the Cipher instance or use for example a CipherOutputStream / CipherInputStream