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
Related
The plain text is signed using java.security.Signature. Below is the code used to sign the plain text
public String getSignature(String plainText) throws Exception
{
KeyStore keyStore = loadKeyStore(); // A local method to read the keystore file from file system.
PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD.toCharArray());
Signature privateSignature = Signature.getInstance(SIGNATUREALGO);
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes("UTF-8"));
byte[] signature = privateSignature.sign();
return String.valueOf(signature);
// KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD and SIGNATUREALGO are all constant Strings
}
Note 1: I found online a way to verify the signature using the public key Java Code Examples for java.security.Signature#verify(). But this is not what I require.
Note 2: I also found a ways to encrypt and decrypt as mentioned here RSA Signing and Encryption in Java. But the use case I have in hand is to get the original plain text from a signed data. Is that possible?
No, you can't retrieve the original content from just the signature.
The signature alone does not contain enough information to restore the original clear text, no matter what keys you have access to.
The basic idea of a signature is to send it together with the clear text. That means the clear text will be visible, but the signature can be used to verify that the message was written (or at least signed) by who claims to have done so and has not been tampered with since then.
Signing something is different from encrypting it. The two often uses the same or related technologies and both fall under cryptography.
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 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.
My colleagues have problems implementing the above in Java. They wasted some days already.
What does the command do? Create a digest and then sign the digest using the keyfile?
Instructions to create the digest are here: How can I create an SHA512 digest string in Java using bouncy castle?
How can I sign in Java?
Which algorithm does the -sign use? Does it depend on the key I am using?
My keyfile is in p12 format. Is this correct?
Depending on the type of your key and the digest algorithm, openssl will determine the signature algorithm. See OpenSSL documentation,
When signing a file, dgst will automatically determine the algorithm (RSA, ECC, etc) to use for signing based on the private key's ASN.1 info.
To do the same process in Java, you have to load the keys of PKCS12 keystore and do the signature with the private key. Bouncycastle it is not needed
//Read private key identified by keyAlias from keystore
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(Files.readAllBytes(Paths.get("keyfile.p12")), password);
PrivateKey key = (PrivateKey)keystore.getKey(keyAlias, password);
//Use SHA512withRSA with RSA keys, SHA512withDSA for DSA keys,...
//See supported algorithm here: https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature
String signatureAlgorithm = "SHA512withRSA";
//Digital Signature
Signature sig = Signature.getInstance(signatureAlgorithm);
sig.initSign(privateKey);
sig.update(Files.readAllBytes(Paths.get(file)));
byte[] signature = sig.sign();
OpenSSL generate HEX output by default. Use -binary to get the binary data or convert Java output to HEX
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