How to do Enveloping digital signature to a xml file in java? - java

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);

Related

How to sign XML with Java in fastest way possible

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.

Adding Signing certificate as signed attributes in CMS

Is this the way to add the signing certificate as signed attribute?
JcaSignerInfoGeneratorBuilder builder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());
Attribute attr = new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString( this.digest(myCert.getEncoded()) )));// using SHA-512 for this.digest()
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attr);
builder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
On verification
signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))
I got exception:
org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value
For digest calculation of the attribute I use SHA-512, for signing SHA512withRSA.
The signing certificate can be added as signed attribute using BouncyCastle as:
final X500Name issuerX500Name = new X509CertificateHolder(mycert.getEncoded()).getIssuer();
final GeneralName generalName = new GeneralName(issuerX500Name);
final GeneralNames generalNames = new GeneralNames(generalName);
final BigInteger serialNumber = mycert.getSerialNumber();
final IssuerSerial issuerSerial = new IssuerSerial(generalNames, serialNumber);
ESSCertIDv2 certid = new ESSCertIDv2(new AlgorithmIdentifier(new ASN1ObjectIdentifier("2.16.840.1.101.3.4.2.3")), this.digest(mycert.getEncoded()), issuerSerial);
SigningCertificateV2 sigcert = new SigningCertificateV2(certid);
final DERSet attrValues = new DERSet(sigcert);
Attribute attr = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, attrValues);
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attr);
JcaSignerInfoGeneratorBuilder builder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());
builder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));

XML Signature Value mismatch issue

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.

Java WebService call with authentication

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");

Digital Signature in SOAP message

I'm trying to create a SOAP message with signature (Russian GOST34.10-2001 algorithm) in Java for some goverment services. The thing is that the service tells me that Signature is invalid and I can't see where I made a mistake. The key container where I read private key and certificate from is valid.
Where things go wrong? I understand that my question is quite specific but I still hope that someone could show me the way.
update1:a little update: body of message has some cyrillic(utf)-8 nodes and values
update2:
So I read through this question and tried to get the PrivateKey in the same way and got this Exception:
java.lang.IllegalArgumentException: private key algorithm does not match algorithm of public key in end entity certificate
Here's some code:
Here I marshal classes into SOAP body:
try {
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage sm = mf.createMessage();
SOAPPart sp = sm.getSOAPPart();
SOAPEnvelope envelope = sp.getEnvelope();
QName bodyId = new QName("http://ws.unisoft/", "SendFullULRequest");
SOAPBody sb = (SOAPBody) envelope.getBody();
SOAPHeader sh = (SOAPHeader) envelope.getHeader();
javax.xml.bind.JAXBContext jaxbCtx = javax.xml.bind.JAXBContext.newInstance(FullULRq.class);
javax.xml.bind.Marshaller marshaller = jaxbCtx.createMarshaller();
marshaller.marshal(new JAXBElement<FullULRq>(bodyId, FullULRq.class, fulr),
sm.getSOAPBody());
sm.saveChanges();
Then I compute Digest Value:
ReferenceType rt2 = new ReferenceType();
rt2.setURI("#body");
////Пункт 7
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Source xmlSource = new DOMSource(sm.getSOAPBody());
Result outputTarget = new StreamResult(outputStream);
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "no");
transformer.transform(xmlSource, outputTarget);
Init.init();
Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
final ByteArrayInputStream is = new ByteArrayInputStream(canon.canonicalize(outputStream.toByteArray()));
rt2.setDigestValue(computeDigestWithStream(is));
} catch (Exception e) {
e.printStackTrace();
}
With this method:
public static byte[] computeDigestWithStream(ByteArrayInputStream stream) throws Exception {
final MessageDigest digest =
MessageDigest.getInstance("GOST3411");
// processing data
final DigestInputStream digestStream =
new DigestInputStream(stream, digest);
while (digestStream.available() != 0) {
digestStream.read();
}
And finally I generate a signature from the block (in which element is present):
ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream();
Source xmlSource2 = new DOMSource(sm.getSOAPBody());
Result outputTarget2 = new StreamResult(outputStream2);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "no");
transformer.transform(xmlSource2, outputTarget2);
Init.init();
Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
InputStream is2 = new ByteArrayInputStream(outputStream2.toByteArray());
byte[] bb = canon.canonicalize(outputStream2.toByteArray());
byte[] signval = sign("CryptoProSignature", key, bb);
sign() method:
public static byte[] sign(String algorithmName, PrivateKey privateKey, byte[] data) throws Exception {
final Signature sig = Signature.getInstance(algorithmName);
sig.initSign(privateKey);
sig.update(data);
return sig.sign();
}
And if it is relevant this is how I read the privateKey from a container:
KeyStore ks = KeyStore.getInstance("FloppyStore");
String passwd = "*";
String kalias = null;
ks.load(null, null);
Enumeration enum1 = ks.aliases();
for (; enum1.hasMoreElements();) {
String tAlias = (String) enum1.nextElement();
if (ks.isKeyEntry(tAlias)) {
kalias = tAlias;
}
System.out.println(tAlias);
}
PrivateKey key = (PrivateKey) ks.getKey(kalias, passwd.toCharArray());
Thanks!

Categories