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();
Related
Right, so I am completely stumped. Usually when I sign certificates I use an ASN.1 like below to specify specific rules, that can be used for IoT for example.
1.2.3.4=ASN1:SEQUENCE:seq_sect
[seq_sect]
one=SEQUENCE:do_one
two=SEQUENCE:do_two
[do_one]
field.1 = UTF8:field1/*
field.2 = UTF8:field2/*
[do_two]
field.1 = UTF8:field1/*
field.2 = UTF8:field2/*
I usually use a program called xca, but in this case I have been coding with bouncycastle and have been really struggling in importing this into the X509v3CertificateBuilder. I know I should be using .addExtension, but I'm not particularly certain if I should store the rules in a .txt file or if I should store it in an array and somehow parse through it?
Actually you're using OpenSSL, which xca wraps. Only OpenSSL uses this particular text-based 'config' format for ASN.1. For BouncyCastle you'll have to write code instead, something like (not tested):
ASN1EncodableVector one = new ASN1EncodableVector();
one.add(new DERUTF8String("field1/*"));
one.add(new DERUTF8String("field2/*"));
// if two is really identical to one, just reuse it; otherwise do something different
ASN1EncodableVector outer = new ASN1EncodableVector();
outer.add(new DERSequence(one));
outer.add(new DERSequence(two)); // or one again
builder.addExtension (oid, critical, new DERSequence(outer));
Similar How to add PrivateKeyUsage extension to a certificate using bouncycastle in java? and Creating Custom X509 v3 Extensions in Java with Bouncy Castle
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
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
How to display details of the subject in a certificate (DER format ie .cer file) in java ? eg: email, country, Name or seperate OIDs etc.
Reading a certificate could be done from the above code. Will work in Android as well. Thank you GreyBeardedGeek. But to more elaborate on it (As curious requested), if you display the subject details by using methods getSubjectDN() or cert.getSubjectX500Principal().getname() it will display the whole details. Some may be encoded. I believe it ASN1.(not sure). What I need is to extract only the information I need. For an example please refer the output of the code for a sample certificate I had created. using method - cert.getSubjectX500Principal().getname() Subject Name 2.5.4.5=#130d4e2d61626c652f49542f303130,2.5.4.72=#1308456e67696e656572,2.5.4.41=#13104e7577616e20446520416c6d65696461,1.2.840.113549.1.9.1=#16106e7577616e406e2d61626c652e62697a,OU=Information Technology,O=N-able Pvt Ltd\ ,ST=Western,C=LK
2.5.4.5 is an OID (Object Identifier) which is encoded.
Using method - getSubjectDN()
OID.2.5.4.5=N-able/IT/010, OID.2.5.4.72=Engineer, OID.2.5.4.41=Nuwan De Almeida, OID.1.2.840.113549.1.9.1=#16106E7577616E406E2D61626C652E62697A, OU=Information Technology, O="N-able Pvt Ltd ", ST=Western, C=LK
Here also some information is encoded eg: email address.
So coming back to my question , how can we extract information(not encoded) separately based on the OID. Further if you install the certificate in windows OS you could view the subject information correctly.What I need is a code to get the OID value information passing the OID in java, to extract subject details separately.
Thank you again in advance.
Look into the Bouncy Castle ASN.1 parsing libraries and especially X500Name. It can parse a distinguished name (DN) into its parts (CN, O, etc.).
http://www.bouncycastle.org/docs/docs1.5on/index.html
The following code (no error handling included) will produce an instance of X509Certificate from the .cer file. You can then use that object's methods to inspect the properties of the certificate. The code is generic java, but should work in Android.
X509Certificate cert = null;
FileInputStream fis = null;
ByteArrayInputStream bais = null;
String source = "certificate.cer";
String certType = "X.509"
fis = new FileInputStream(source);
byte[] value = new byte[fis.available()];
fis.read(value);
bais = new ByteArrayInputStream(value);
java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance(certType);
cert = (X509Certificate)cf.generateCertificate(bais);
My app needs only to read private keys(with associated public cert), no writing to KeyStore, no password changing, no changes at all - just reading.
Does anybody know for sure that for reading I can use this code:
///doing some actions
KeyStore store = KeyStore.getInstance("foo", "bar");
store.load(iaminputstream, iampwd); // I'M JUST LOADING, I'M NOT GONNA STORE IT!
PrivateKey pk = (PrivateKey) store.getKey(iamalias, iamkeypass);
Certificate cert = store.getCertificate(iamalias);
///contnuing some actions
instead of this:
///doing some actions
KeyStore store = KeyStore.getInstance("foo", "bar");
try{
store.load(iaminputstream, iampwd); //I'VE LOADED
PrivateKey pk = (PrivateKey) store.getKey(iamalias, iamkeypass);
Certificate cert = store.getCertificate(iamalias);
} finally {
store.store(iamoutputstream, iampassword); //AND I'VE SAVED!
}
///contnuing some actions
Prove link is highly appreciated!
You don't need to save it of course. Just don't forget to handle exceptions.
Take a look at javadoc - there is nothing about required saving after load() call.
If you are not storing anything, you don't have to call store.store()
But do remember to close the inputstream and handle exceptions.