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.
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);
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.
I generate and digitally sign correctly a pdf using iText.
When I try to sign it again (two or three times) the pdf says that the previuos revision is been modified and the first signature is not valid, but I've just signed it on another time without touching nothing else.
This is my code:
public void signPdf(String SRC, String DEST, String SIGN_IMAGE)
throws IOException, DocumentException, GeneralSecurityException {
// Gets the informations stored in the properties file
String path = properties.getProperty("PRIVATE");
String keystore_password = properties.getProperty("PASSWORD");
String key_password = properties.getProperty("PASSWORD");
// Create the keystore
KeyStore ks = KeyStore.getInstance("pkcs12", "BC");
ks.load(new FileInputStream(path), keystore_password.toCharArray());
String alias = (String) ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias,
key_password.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
// reader and stamper
PdfReader reader = new PdfReader(SRC);
FileOutputStream os = new FileOutputStream(DEST);
// Get all the signatures if existing
AcroFields acroFields = reader.getAcroFields();
List<String> signatureNames = acroFields.getSignatureNames();
PdfStamper stamper;
// Choose to append or not the signature
if(signatureNames.isEmpty()){
stamper = PdfStamper.createSignature(reader, os, '\0');
} else{
stamper = PdfStamper.createSignature(reader, os, '\0', null,
true);
}
String sName = "sign_" + (signatureNames.size());
// appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(x_bl, y_bl, x_tr, y_tr),
page, sName);
appearance
.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
appearance.setSignatureGraphic(Image.getInstance(SIGN_IMAGE));
// digital signature
ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", "BC");
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, es, chain, null, null,
null, 0, CryptoStandard.CMS);
}
I'm not getting what's really wrong because when I used to sign it just twice separately like in this example it used to work. What am I doing wrong?
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");
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