I have PEM format file, How can verify the signature in Java, as I followed http://download.oracle.com/javase/tutorial/security/apisign/versig.html but found that Java doesnt support PEM
You can read a certificate in a PEM file using BouncyCastle's PEMReader. If the content is an X.509 certificate, you should get an instance of X509Certificate and verify it as you want from there.
EDIT: Here is what the code should look like (not tried):
// The key with which you want to verify the cert.
// This is probably a CA certificate's public key.
PublicKey publicKey = ...;
PEMReader reader = new PEMReader(new FileReader("/path/to/file.pem"));
Object pemObject = reader.readObject();
if (pemObject instanceof X509Certificate) {
X509Certificate cert = (X509Certificate)pemObject;
cert.checkValidity(); // to check it's valid in time
cert.verify(publicKey); // verify the sig. using the issuer's public key
}
(Of course, as with any I/O operations, you'll need to close the reader perhaps with try/finally.)
Note that checkValidity and verify don't return anything: instead, they throw exceptions if when they fail.
Related
I am receiving the following String from a certificate stored in Azure Key Vault. I am using the Secret API in order to retrieve both the certificate and the private key related to this cert.
Initially the certificate was uploaded using a .pfx file to Azure Key vault. Now I need to create a Certificate and a PrivateKey to allow client authentication to a 3rd party system and I am using the given String retrieved from the API, however I am note sure how to get around that in Java.
I took some hints from this link in C# however I am pretty certain that this method doesn't work like that in Java. In particular an X509Certificate or a Certificate in general doesn't hold any information about the PrivateKey in Java, unlike C#, and I am not sure how to extract that information from given String in Java.
This works as expected to retrieve the certificate from the String retrieved from the API
String secret = azureSecret.getValue();
byte[] certkey = Base64.getDecoder().decode(secret);
ByteArrayInputStream inputStream = new ByteArrayInputStream(certkey);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(inputStream);
The azureSecret.getValue() format is like the following however I am not sure how to get PrivateKey out of the given String
MIIKvgIBaaZd6Euf3EYwYdHrIIKYzCC...
YES, Java X509Certificate and Certificate is only the certificate. Instead use KeyStore which can contain multiple entries each of which is either a 'trusted' certificate (for someone else), or a privatekey plus certificate plus other chain cert(s) (if applicable) for yourself, or (not relevant here) a 'secret' (symmetric) key. PKCS12 is supported as one type of KeyStore along with others not relevant here, so after the base64-decoding you already have do something like:
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(inputstreamfromvaultvalue, password);
// then
PrivateKey pkey = (PrivateKey) ks.getKey(alias, password);
// and
Certificate cert = ks.getCertificate(alias); // if you only need the leaf cert
// or
Certificate[] chain = ks.getCertificateChain(alias); // usually
But if you want to do client authentication in TLS/SSL (including HTTPS), you give the JSSE KeyManager the whole keystore object not the individual pieces (privatekey and certificates). Similarly to verify the peer in TLS/SSL, you give TrustManager a keystore containing trusted certificates, usually root CAs and often defaulted to a built-in set of public root CAs.
I am digitally signing a PDF with iText7 and GlobalSign DSS. I implemented the GlobalSing DSS API calls into the necessary iText classes. I get the proper server responses and I am able to call the pdfSigner.signDetached() method with all the needed arguments. Signing with the pdfSigner also succeeds and I get a signed PDF that looks good at first sight. But when I open the signed pdf in Adobe Reader it tells me that the trust chain of the signing certificate is broken and that it can not trace it back to the CA root. Which is strange because it is an AATL certificate and the AATL list of the Adobe Reader is up to date.
And I do not understand why this is happening.
This is what I do :
call DSS for an identity : returns an id string, the signing certificate
and an ocsp response
call DSS for the trustchain : returns the chain of certificates used to
sign the signing certicate, up to the GlobalSign root, together with
their oscp responses (except for the root)
I create an array of X509Certificate objects containing the signing
certificate, 2 intermediates and the GlobalSign root certificate (in
that order)
I implement an IOcspClient that uses the ocsp response from the DSS call
for the identity
I implement an ITsaClient that calls the DSS API /timestamp/{digest}
and finally I execute : pdfSigner.signDetached(externalDigest, externalSignature, chain.toArray(new X509Certificate[]{}), null, dssOcspClient, dssTSAClient, 0, PdfSigner.CryptoStandard.CMS);
in which the externalSignature (an implementation of
IExternalSignature) will call the DSS identity/{id}/sign/{digest} API
While debugging into the signDetached method and deeper into the pdfSigner code, I clearly see that all certificates are in the chain in the right order. I see them being processed in the PdfPKCS7 class (however I don't know/understand exactly what is going on there). I see the signing taking place, no exceptions are thrown and at the end the produced PDF looks like it is correctly signed. Which Adobe says is not.
What am I missing here ?
The trustchain response from de DSS API not only returns the certificates from the chain of trust of the signing certificate, but also the ocsp responses for the two intermediates between the signing certificate and the GlobalSign root. These are never used. And in fact I don't know what to do with them either. Could these be the missing pieces for AdobeReader to reconstruct the trust chain up to the GlobalSign root ?And if so : how do I put them into that PDF ? And if not : then what am I doing wrong that breaks that trustchain ?
An answer to these questions would save my day :-)
Here is the link to a PDF that will show the problem :
test pdf signed with DSS
(after accepting the answer, I removed the example pdf on my client's request)
Below are some pieces of the code.
The center piece that gathers the DSS info and calls the signDetached method
private InputStream sign(byte[] unsignedDocument) throws IOException, DssServiceException, GeneralSecurityException {
SigningIdentity signingIdentity = signingIdentityService.getValidSigningIdentity();
DssOcspClient dssOcspClient = new DssOcspClient(signingIdentity);
TrustChainResponse trustChainResponse = digitalSigningService.getTrustChain();
List<X509Certificate> chain = new ArrayList<>();
chain.add(signingIdentity.getCertificate());
chain.addAll(trustChainResponse.getTrustChain());
IExternalDigest externalDigest = new ProviderDigest(BC_SECURITY_PROVIDER);
IExternalSignature externalSignature = new DssExternalSignature(signingIdentity.getIdentity(), digitalSigningService);
ByteArrayOutputStream signedPdfOut = new ByteArrayOutputStream();
PdfSigner pdfSigner = createPdfSigner(new ByteArrayInputStream(unsignedDocument), signedPdfOut);
pdfSigner.signDetached(externalDigest, externalSignature, chain.toArray(new X509Certificate[]{}), null, dssOcspClient, dssTSAClient, 0, PdfSigner.CryptoStandard.CADES);
return new ByteArrayInputStream(signedPdfOut.toByteArray());
}
The IExternalSignature implementation
#Override
public byte[] sign(byte[] message) throws GeneralSecurityException {
MessageDigest messageDigest = new BouncyCastleDigest().getMessageDigest(DEFAULT_DIGEST_ALGORITHM);
byte[] documentHash = messageDigest.digest(message);
try {
return digitalSigningService.getSignature(signingIdentity, documentHash);
}
catch (DssServiceException e) {
LOGGER.error("error getting signature", e);
throw new GeneralSecurityException(e);
}
}
The IOcspClient implementation
#Override
public byte[] getEncoded(X509Certificate checkCert, X509Certificate issuerCert, String url) {
try {
if(Objects.equals(signingIdentity.getCertificate(), checkCert)) {
OCSPResp response = new OCSPResp(signingIdentity.getOcsp());
BasicOCSPResp basicResponse = (BasicOCSPResp)response.getResponseObject();
return basicResponse.getEncoded();
}
}
catch (CertificateException | IOException | OCSPException e) {
LOGGER.warn("OCSP validatie gefaald!", e.getMessage());
}
return null;
}
The ITSAClient implementation
#Override
public byte[] getTimeStampToken(byte[] imprint) throws Exception {
String digestAlgorithmOID = DigestAlgorithms.getAllowedDigest(DEFAULT_DIGEST_ALGORITHM);
ASN1ObjectIdentifier digestAlgOID = new ASN1ObjectIdentifier(digestAlgorithmOID);
AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DERNull.INSTANCE);
MessageImprint messageImprint = new MessageImprint(algID, imprint);
byte[] hash = messageImprint.getHashedMessage();
return digitalSigningService.getTimeStamp(hash);
}
In short
Your signer certificate is invalid.
In detail
Your signer certificate and its certificate chain (according to issuer/subject match) are embedded in the signature, in particular your certificate with subject
cn=Homologatie Voertuigen,
ou=Departement Mobiliteit en Openbare Werken,
ou=Vlaams Huis voor de Verkeersveiligheid,
o=Ministeries van de Vlaamse Gemeenschap,
l=Brussel,
st=Brussel,
c=BE
and its claimed issuer
cn=GlobalSign CA 5 for AATL,
o=GlobalSign nv-sa,
c=BE
Thus, one can check the signature with which your certificate is signed. And while doing so one sees that the TBSCertificate part of your signer certificate (the to-be-signed part) has this digest value
C8751FDC7F679CB627F61028ACDD0D09613AFA782412ACFC7E189EA5DA625831
but the signature actually signs this digest value
16090737B41E6E0466E7EB7A7EBD79F5494E438C11D0FB408BCA663A5923AD03
Thus, your signer certificate is not correctly signed.
What does this mean
In a comment you ask
But I am a little confused about what it means exactly. Are we actually doing something wrong during signing, sending the wrong document hash to the signing server ? Or do you mean there is something wrong with the server side signing certificate issued by GlobalSign that they use to sign that document hash?
You're not doing anything wrong during signing, at least I don't think so. The broken signature is not the signature signing the document but its the signature signing your certificate by your CA.
I see essentially three possible reasons for that:
The certificate signature simply is broken and doesn't match your certificate anywhere, anyhow.
This would surprise me.
The certificate signature has been calculated not for the DER encoded form of your to-be-signed certificate part but some other form.
This is not unheard of, if your certificate originally was not in DER form but the certificate signing process assumed it to be, a non-DER form may have been signed (even though according to specification the DER form has to be signed). If some validator then checked the signature which also does not ensure DER form but takes the TBSCertificate as is, that validator would even have indicated that the signature was valid.
In the certificate as embedded in the PDF signature the to-be-signed part is DER encoded but this may have been enforced at some step after the initial certificate generation.
Some minute change may have happened to your certificate after creation.
This also is possible.
You could try and receive a copy of your certificate from your CA in a form that is as original as possible and compare to the certificate that is embedded in your signature. If you find differences, then analyzing the differences most likely will further illuminate the cause of the problem.
I was trying to extract RES public key from the file below
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
Here is the code i did..
public static PublicKey loadPublicKeyFromFile(File publicKeyFile) throws Exception {
FileReader file = new FileReader(publicKeyFile);
PemReader reader = new PemReader(file);
X509EncodedKeySpec caKeySpec = new X509EncodedKeySpec(reader.readPemObject().getContent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey caKey = kf.generatePublic(caKeySpec);
return caKey;
}
But It throws out
java.security.InvalidKeyException: IOException: ObjectIdentifier() -- data isn't an object ID
What's the appropriate way to extract RES Public key from a file..
An X.509 certificate and an X509EncodedKeySpec are quite different structures, and trying to parse a cert as a key won't work.
Java's X509EncodedKeySpec is actually SubjectPublicKeyInfo from X.509 or equivalent and more convenient PKIX also linked from Key, which is only a small part of a certificate.
What you need to do is read and parse the cert and then extract the pubkey from the cert.
Standard SunJCE CertificateFactory can do it
(and can read either PEM or DER to boot) like this:
CertificateFactory fact = CertificateFactory.getInstance("X.509");
FileInputStream is = new FileInputStream (args[0]);
X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
PublicKey key = cer.getPublicKey();
is.close();
// add error handling as appropriate, try-with-resources is often good
If you have BouncyCastle you can use its provider the same way (just add a second argument to .getInstance or set the default provider list order), or you can use PEMParser with JcaX509CertificateConverter -- which effectively does the same thing, internally running the data through a CertificateFactory.
With Sun JVM it is also possible with
import java.security.cert.X509Certificate;
import sun.security.x509.X509CertImpl;
InputStream is = ...
X509Certificate crt = new X509CertImpl(is);
, although I'd prefer the accepted answer as JVM implementation-independent one.
Under the hood, in Sun JVM, CertificateFactory(more precisely, X509Factory) does instantiate X509CertImpl , but there is very subtle difference between the two approaches: CertificateFactory caches X509 Certificate instances by binary content of the input stream, the one that can be retrieved by cer.getEncoded().
The cache can be cleared by
fact.generateCertificate(null);
Load a certificate and keys from keystore which is password protected and then use it for cert verification and digital signing
To read the certificate is really trivial.
CertificateFactory factory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) factory.generateCertificate(new FileInputStream("file.pem"));
This is with standard APIs (in try/catch) etc and you have loaded your certificate.
Now the toString method of certificate is not suitable for you since it just captures the "user's" view of the certificate e.g. you would use it for println for instance
Can't you send the certificate object itself?
Not sure what your server expects so you can look into the various methods of certificate
X509Certificate
I use this code
PEMReader pr=new PEMReader(new StringReader(trust_certs));
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
Object o;
int i=0;
while((o=pr.readObject())!=null){
if(o instanceof X509Certificate){
i++;
X509Certificate c=(X509Certificate)o;
trustStore.setCertificateEntry(Integer.toString(i), c);
}
}
http://www.bouncycastle.org/docs/docs1.6/org/bouncycastle/openssl/PEMReader.html
A pem file is read as any other text file. Read the Java tutorial about IO (and concentrate on character streams, since a pem file contains text, and on File IO, since this is what you want to do)
I use a third party tool to verify signature and to get certificate detail(like serial number, CA etc..) from signature. The problem with this utility is it is licensed and works on certain machines only.
Can i validate the signature against the data using simple java or .net code?(instead of using paid application). I dont have private key to extract certificate information from signed data.
Or if someone can suggest sample code in java or .net to extract certificate detail if i have pfx file. Of from signed data.
Data is signed with asymmetric encryption.
To extract detail from certificate:
Make a string which keeps certificate data. Just ensure it has -----BEGIN CERTIFICATE----- in starting and -----END CERTIFICATE----- in end.
Now use the following code in Java to extract certificate detail.
InputStream inStream = new ByteArrayInputStream(certString.toString().getBytes("UTF-8"));
BufferedInputStream bis = new BufferedInputStream(inStream);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(bis);
X509Certificate xCert = (X509Certificate)cert;
System.out.println("Certificate Type: "+cert.getType());
System.out.println("Public Key: \n"+cert.getPublicKey());
try{
System.out.println("Signature Algorithm"+xCert.getSigAlgName());
System.out.println("IssuerDN : "+xCert.getIssuerDN());
System.out.println("Serial Number : "+xCert.getSerialNumber());
System.out.println("SubjectDN : "+xCert.getSubjectDN());
}catch(Exception exp){
:
}
If you are having the PFX file, then that may contain the public key certificate which will be required to verify the signature.
Alternatively, if your signature is a PKCS#7 signature, then the signature itself will hold the data, signature and the certificate. Assuming PKCS#7 is not detached.
You need to ask your signer, how is he transferring his certificate for validation.