I'm trying to verify a certificate , that was given to me using Java, in the following manner
try{
FileInputStream fr = new FileInputStream(pathtoCertificate);
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate c = (X509Certificate)
cf.generateCertificate(fr);
System.out.println("++++Certificate Verification++++++++");
PublicKey pk = c.getPublicKey();
c.verify(pk);....//63
System.out.println("++This certificate is VALID++");
.....
}
catch(CertificateException e){
e.printStackTrace();
System.out.println("Certificate is Invalid");
}
However, I keep getting the following error
java.security.SignatureException: Signature does not match.
at sun.security.x509.X509CertImpl.verify(Unknown Source)
at sun.security.x509.X509CertImpl.verify(Unknown Source)
at Main.printCertificate(Main.java:63)
at Main.main(Main.java:41)
I've only been provided with a certificate file(which I need to verify), a private key(corresponding to the above file ) and a certificate from a CA.
I'm quite new to Java Security. So any thoughts on how I could verify the certificate file would be great.
Well I finally figured it out. For folks who may reach here in the future.
The reason the error was being thrown is that the certificate in my case was not self signed . Therefore I merely had to use the certificate issued by the CA(as mentioned in my Q) to verify the public key.
The rest remained the same.
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 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.
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.
I have an XML document that is digitally signed. I use the XML Digital Signature API to verify the signature. But this document is a SAML 2.0 Assertion that will be used for single sign-on into our web application. As such I need to establish trust in the X.509 certificate used to sign the XML document.
The code I am using to try and establish this trust is:
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
PKIXParameters params = new PKIXParameters(keystore);
params.setRevocationEnabled(false);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
CertPath certPath = certFactory.generateCertPath(Arrays.asList(signatureCertificate));
CertPathValidator certPathValidator = CertPathValidator.getInstance(CertPathValidator.getDefaultType());
CertPathValidatorResult result = certPathValidator.validate(certPath, params);
PKIXCertPathValidatorResult pkixResult = (PKIXCertPathValidatorResult) result;
TrustAnchor ta = pkixResult.getTrustAnchor();
X509Certificate cert = ta.getTrustedCert();
When run the call to certPathValidator.validate() throws a CertPathValidatorException with the message Path does not chain with any of the trust anchors.
Examining the certificate, it says it has been issued by OU=www.verisign.com/CPS Incorp.by Ref. LIABILITY LTD.(c)97 VeriSign,OU=VeriSign International Server CA - Class 3,OU=VeriSign\, Inc.,O=VeriSign Trust Network.
This is not one of the trust anchors in the JDK's cacerts key store.
However, using IE to inspect a similar certificate's trust chain I see that www.verisign.com/CPS Incorp.'s was issued by VeriSign Class 3 Public Primary CA, which does appear to be one of the trust anchors in the JDK's cacerts key store.
My question: how can I get Java to validate this certificate?
Insert the public cert of the issuing CA into the cacert keystore.
edit: You can use keytool or one of the other tools out there. Article describes keytool use: keytool-Key and Certificate Management Tool
Exactly what xelco said - add the intermediate CA:
OU=www.verisign.com/CPS Incorp.by Ref. LIABILITY LTD.(c)97 VeriSign,OU=VeriSign International Server CA - Class 3,OU=VeriSign\, Inc.,O=VeriSign Trust Network
To the JDK's key store. You can use keytool to do it.
Reason: Generally when an XML message is signed, the signature includes only the signing cert. X509 Certificates are like singly linked lists. The End Entity points to its issuer. The issuer points to its issuer until you get to a self-signed Root CA, which points to itself. To verify a certificate according to PKIX, the validator needs to be able to build the whole CA chain from end entity to self signed root, so every part of the chain (except for the end entity) must be in your certificate store.
I've got better solution. I've found java service that can be run and do everything for us.
Java:
http://code.google.com/p/java-use-examples/source/browse/trunk/src/com/aw/ad/util/InstallCert.java