I have an issue with XML Signature value mismatch error. Full Scenario
I generated a private 1024 bit key with OpenSSL.
I generated the CSR with that private key and sent it to VISA, they have signed the document with their root CA certificate and sent me a signed certificate in the .pem format.
I use the following java code to sign an XML document.
When i send the data back to VISA they verify the signature value and send an error of mismatch.
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
DigestMethod digestMethod = factory.newDigestMethod(DigestMethod.SHA1, null);
factory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
Reference reference = factory.newReference("#" + paresId, digestMethod, null, null,null);
CanonicalizationMethod canonicalizationMethod = factory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null);
SignatureMethod signatureMethod = factory.newSignatureMethod(SignatureMethod.RSA_SHA1, null);
SignedInfo signedInfo = factory.newSignedInfo(canonicalizationMethod, signatureMethod, Collections.singletonList(reference));
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair();
KeyInfoFactory keyInfoFactory = factory.getKeyInfoFactory();
KeyStore keyStore = KeyStore.getInstance("JKS"); //PKC#7
keyStore.load(new FileInputStream(keystorePath), "dell12345".toCharArray());
KeyStore.PrivateKeyEntry keyEntry =
(KeyStore.PrivateKeyEntry) keyStore.getEntry("deskey", new KeyStore.PasswordProtection("dell12345".toCharArray()));
//PrivateKey m_objRequestSigningKey = (PrivateKey) obj_keyStore.getKey(str_alias, "password".toCharArray());
KeyStore.TrustedCertificateEntry rootEntry = (KeyStore.TrustedCertificateEntry) keyStore.getEntry("root", null);
X509Certificate rootCertificate = (X509Certificate) rootEntry.getTrustedCertificate();
KeyStore.TrustedCertificateEntry intermediateEntry = (KeyStore.TrustedCertificateEntry) keyStore.getEntry("intermediate", null);
X509Certificate intermediateCertificate = (X509Certificate) intermediateEntry.getTrustedCertificate();
KeyStore.TrustedCertificateEntry signEntry = (KeyStore.TrustedCertificateEntry) keyStore.getEntry("sign", null);
X509Certificate sigingCertificate = (X509Certificate) signEntry.getTrustedCertificate();
List<X509Certificate> x509 = new ArrayList<X509Certificate>();
x509.add(rootCertificate );
x509.add(intermediateCertificate );
x509.add(sigingCertificate );
X509Data x509Data = keyInfoFactory.newX509Data(x509);
List<X509Data> items = new ArrayList<X509Data>();
items.add(x509Data);
KeyInfo keyInfo = keyInfoFactory.newKeyInfo(items);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(IOUtils.toInputStream(inputXml));
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getDocumentElement());
XMLSignature signature = factory.newXMLSignature(signedInfo, keyInfo);
signature.sign(dsc);
Sorry, after a long time I am going to answer the question which I have posted 5 years back.
Actually, the problem was basically with the XML formatting, I removed the next line and carriage return characters from XML and send back to Visa and then my signature was match properly.
I don't know why they giving the error even I mentioned the CanonicalizationMethod properly.
Related
I have a requirement to do enveloping digital signature to a xml file using java, currently i can able to sign with enveloped digital signature. Can any one help how to do enveloping signature?? My code for enveloped signature:
//code
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
Transform exc14nTranform = fac.newTransform("http://www.w3.org/TR/2001/REC-xml-c14n-20010315", (TransformParameterSpec) null);
Transform envTransform = fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
List<Transform> transformList = new ArrayList();
transformList.add(exc14nTranform);
transformList.add(envTransform);
Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, null), transformList,null, null);
SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE,(C14NMethodParameterSpec) null),fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("C:\\kstore.jks"), "kspassword".toCharArray());
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry ("aliasname", new KeyStore.PasswordProtection("kspassword".toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
KeyInfoFactory kif = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
X509IssuerSerial issuer = kif.newX509IssuerSerial(cert.getIssuerDN().toString(), cert.getSerialNumber());
System.out.println(cert.getSubjectAlternativeNames().toString());
x509Content.add(cert.getSubjectX500Principal().getName());
x509Content.add(issuer);
x509Content.add(cert);
X509Data xd = kif.newX509Data(x509Content);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream("C:\\myfile.xml"));
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getDocumentElement());
XMLSignature signature = fac.newXMLSignature(si, ki);
signature.sign(dsc);
OutputStream os = new FileOutputStream("C:\\mysignedfile.xml");
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
}
}
i found the answer, i have added/modified below lines
Reference ref = fac.newReference("#object",fac.newDigestMethod(DigestMethod.SHA1, null));
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream("C:\\myfile.xml"));
XMLStructure content = new DOMStructure(doc.getDocumentElement());
XMLObject obj = fac.newXMLObject(Collections.singletonList(content), "object", null, null);
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc);
XMLSignature signature = fac.newXMLSignature(si, ki, Collections.singletonList(obj), null, null);
need some tips/advice about (optimization) and how to sign XML with a certificate in the fastest way possible. On some databases (Oracle) it needs 30 msec to sign a file.. But on another database, it needs 1.2 sec. This function/method is called in a loop. It signs some XML data and returns a signed XML in a String. (Java method is exposed as PL/SQL function).
public static String signXML(String vhodniXml) throws Exception {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
Document doc = dbFactory.newDocumentBuilder().parse(new ByteArrayInputStream(vhodniXml.getBytes()));
NodeList nl = doc.getElementsByTagName("fu:BusinessPremiseRequest");
Node node = nl.item(0);
((Element) node).setIdAttribute("Id", true);
Enumeration e = p12.aliases();
String alias = (String) e.nextElement();
Key privateKey = p12.getKey(alias, geslo.toCharArray());
KeyStore.PrivateKeyEntry keyEntry
= (KeyStore.PrivateKeyEntry) p12.getEntry(alias, new KeyStore.PasswordProtection(geslo.toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
PublicKey publicKey = cert.getPublicKey();
final XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
// Create a Reference to the enveloped document
Reference ref = sigFactory.newReference("#data",
sigFactory.newDigestMethod(DigestMethod.SHA256, null),
Collections.singletonList(sigFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)),
null,
null);
SignedInfo si = sigFactory.newSignedInfo(sigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), sigFactory.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null), Collections.singletonList(ref));
// Create a KeyValue containing the RSA PublicKey
KeyInfoFactory keyInfoFactory = sigFactory.getKeyInfoFactory();
X509IssuerSerial x509IssuerSerial = keyInfoFactory.newX509IssuerSerial(cert.getSubjectX500Principal().getName(), cert.getSerialNumber());
List x509Content = new ArrayList();
x509Content.add(cert.getSubjectX500Principal().getName());
x509Content.add(x509IssuerSerial);
KeyValue keyValue = keyInfoFactory.newKeyValue(publicKey);
X509Data xd = keyInfoFactory.newX509Data(x509Content);
// Create a KeyInfo and add the KeyValue to it
KeyInfo keyInfo = keyInfoFactory.newKeyInfo(Collections.singletonList(xd));
// Create a DOMSignContext and specify the RSA PrivateKey and
// location of the resulting XMLSignature's parent element
DOMSignContext dsc = new DOMSignContext(
privateKey,
node
);
// Create the XMLSignature (but don't sign it yet)
XMLSignature signature = sigFactory.newXMLSignature(si, keyInfo);
// Marshal, generate (and sign) the enveloped signature
signature.sign(dsc);
ByteArrayOutputStream os = new ByteArrayOutputStream();
Transformer trans = TransformerFactory.newInstance()
.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
return new String(os.toByteArray());
}
The first thing to do is to remove some IO operations by storing your key related objects in fields because reading the KeyStore to get keys takes time.
I'm working on webservice client which requires authentication and xml signing.
I've read a lot of articles but it looks like my one looks different.
I need to send a request containing tag with some certificate details.
I received from Service provider few files (certificate.crt , certificate.p12 , certificate.pem)
I managed to attach crt file into request using follString providerName =
System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
Reference ref = fac.newReference("", fac.newDigestMethod(
DigestMethod.SHA1, null), Collections.singletonList(fac
.newTransform(Transform.ENVELOPED, (XMLStructure) null)), null,
null);
// Create the SignedInfo
SignedInfo si = fac.newSignedInfo(
fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS, (C14NMethodParameterSpec) null),
fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair kp = kpg.generateKeyPair();
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(kp.getPublic());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(new FileInputStream(new File("certificate.crt")));
X509Data x509d = kif.newX509Data(Collections.singletonList(cert));
KeyInfo ki = kif.newKeyInfo(Arrays.asList(x509d, kv));
Document doc = (Document) result.getNode();
DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), doc.getDocumentElement());
XMLSignature signature = fac.newXMLSignature(si, ki);
signature.sign(dsc);
But RSA key is generated and different each call. It is obvious because I'm using KeyPairGenerator. Is it possible to import my private key from P12 file instead?
When I run this application on tomcat do I need to configure it somehow to enable the HTTPS calls to the service?
You can use KeySotre
Load the file:
KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream ksin = new FileInputStream("myfile.p12");
ks.load(ksin, "password");
getKey("keyalis", "password");
The question is how to generate certificate chains programmatically in Java. In other words, I would like to perform in java the operations detailed here: http://fusesource.com/docs/broker/5.3/security/i382664.html
Besically, I can create the RSA keys for a new client:
private KeyPair genRSAKeyPair(){
// Get RSA key factory:
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
log.error(e.getMessage());
e.printStackTrace();
return null;
}
// Generate RSA public/private key pair:
kpg.initialize(RSA_KEY_LEN);
KeyPair kp = kpg.genKeyPair();
return kp;
}
and I generate the corresponding certificate:
private X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm)
throws GeneralSecurityException, IOException {
PrivateKey privkey = pair.getPrivate();
X509CertInfo info = new X509CertInfo();
Date from = new Date();
Date to = new Date(from.getTime() + days * 86400000l);
CertificateValidity interval = new CertificateValidity(from, to);
BigInteger sn = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(dn);
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
// Sign the cert to identify the algorithm that's used.
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
// Update the algorith, and resign.
algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
return cert;
}
Then I generate the cert signing request and I save it to csrFile file:
public static void writeCertReq(File csrFile, String alias, String keyPass, KeyStore ks)
throws KeyStoreException,
NoSuchAlgorithmException,
InvalidKeyException,
IOException,
CertificateException,
SignatureException,
UnrecoverableKeyException {
Object objs[] = getPrivateKey(ks, alias, keyPass.toCharArray());
PrivateKey privKey = (PrivateKey) objs[0];
PKCS10 request = null;
Certificate cert = ks.getCertificate(alias);
request = new PKCS10(cert.getPublicKey());
String sigAlgName = "MD5WithRSA";
Signature signature = Signature.getInstance(sigAlgName);
signature.initSign(privKey);
X500Name subject = new X500Name(((X509Certificate) cert).getSubjectDN().toString());
X500Signer signer = new X500Signer(signature, subject);
request.encodeAndSign(signer);
request.print(System.out);
FileOutputStream fos = new FileOutputStream(csrFile);
PrintStream ps = new PrintStream(fos);
request.print(ps);
fos.close();
}
where
private static Object[] getPrivateKey(KeyStore ks, String alias, char keyPass[])
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException {
key = null;
key = ks.getKey(alias, keyPass);
return (new Object[]{ (PrivateKey) key, keyPass });
}
Now I should sign the CSR with the CA private key, but I cannot see how to achive that in java. I have "my own" CA private key in my jks.
Besides, once I manage to sign the CSR I should chain the CA cert with the signed CSR: how that can be done in java?
I would prefer not to use bc or other external libs, just "sun.security" classes.
Thank you.
I believe the code example in the post http://www.pixelstech.net/article/1406726666-Generate-certificate-in-Java----2 will show you how to generate certificate chain with pure Java. It doesn't require you to use Bouncy Castle.
Sorry, but despite your desires, and besides writing all of your crypto code and including it with your project (not recommended), I'd recommend using Bouncy Castle here.
Specifically, please refer to https://stackoverflow.com/a/7366757/751158 - which includes code for exactly what you're looking to do.
I see you've already gone over to the BouncyCastle side of the house but just in case anyone else was wondering; you can add the cert chain to the entry when putting the key into the KeyStore. For example
// build your certs
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load([keystore stream], password.toCharArray());// or null, null if it's a brand new store
X509Certificate[] chain = new X509Certificate[2];
chain[0] = _clientCert;
chain[1] = _caCert;
keyStore.setKeyEntry("Alias", _clientCertKey, password.toCharArray(), chain);
keyStore.store([output stream], password.toCharArray());
// do other stuff
Hi I just have contructed an org.apache.xml.security.signature.XMLSignature from a xml that complies with xmldsig w3c recommendation, and I can see that the xml contains all certificate chain in
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
elements, but using the XMLSignature API I can see that I can only access the user certificate and the issuer one, but not the complete chain, is there an straightforward way to get this done through xmlsec API?
I found a solution for this, not the most clean, but it works:
XMLSignature signature = new XMLSignature(sigElement,
null);
KeyInfo keyInfo = signature.getKeyInfo();
NodeList x509Certificates = keyInfo.getElement().getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "X509Certificate");
ArrayList<X509Certificate> allCertificates = new ArrayList<X509Certificate>();
for (int i = 0; i < x509Certificates.getLength(); i++) {
Node x509CertificateElement = x509Certificates.item(i);
byte[] decodedX509Certificate = Base64.decode(x509CertificateElement.getTextContent());
X509Certificate x509Certificate = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(decodedX509Certificate));
allCertificates.add(x509Certificate);
}
// now you have all certificates in allCertificates