How can i sign hash of xml file and integrate signature in original file

落爺英雄遲暮 提交于 2020-08-10 18:58:33

问题


Is there a way to sign the hash of an xml file and then integrate this signed hash into the original file.

For the PDF signature I use iText and it works very well.

UPDATE 1 : Sign Original XML FILE

public class SignXML {

static String fileToSign = "B:/tmp/client/032936.xml";
static String signedFile = "B:/tmp/client/Signed-032936.xml";
static String certificate = "C:/lp7command/tools/certificate.p12";
static String password = "123456";

public static void main(String[] args) throws Exception{
    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("#evidence", 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("PKCS12");
        ks.load(new FileInputStream(certificate), password.toCharArray());
        KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry ("mykey", 
                new KeyStore.PasswordProtection(password.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(fileToSign));
        XMLStructure content = new DOMStructure(doc.getDocumentElement());
        
        XMLObject obj = fac.newXMLObject(Collections.singletonList(content), "evidence", null, null);

        DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc);
        XMLSignature signature = fac.newXMLSignature(si, ki, Collections.singletonList(obj), null, null);
        
        signature.sign(dsc);

        OutputStream os = new FileOutputStream(signedFile);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer();
        trans.transform(new DOMSource(doc), new StreamResult(os));
}
}

what I want to do is calculate the hash of the original XML file and send it for signature to another server (B) which will sign the hash and return signed hash which I will then integrate into the original file which is in server A.


回答1:


Yes there is a way to do that. Have a look at for example https://www.w3.org/TR/xmldsig-core/ at (one) standard way to that. Depending on your technology there are several libraries that support you with that. You mentioned iText, so for Java it is integrated in the JRE for a while now. See https://docs.oracle.com/en/java/javase/13/docs/api/java.xml.crypto/javax/xml/crypto/dsig/package-summary.html as a starting point.

You are looking for an enveloped XML-Signature, where the Signature is not included in hash calculation, so that it can be included in the original document.

UPDATE 1: So you are looking for including a remote signing service into xml documents. That is also possible, did that on a previous job, though a bit more complicated.

In general you have to plug in where the signature is created, get the hash, send it to server get the signature value and include it into the respective XML node.

Then and there we provided our own Java Crypto Provider, (https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html) which would override the Private Key Object to do the remote signing magic. In retrospect I would rather recommend looking into Apache Santuario http://santuario.apache.org/, which might be a bit more flexible, when extending or the EU-DSS library https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_other_implementations.




回答2:


XML digital signature is the standard Java API which is different from the classic method of digesting | encrypting | decrypting hash value. The gist is first to canonicalize | reference(URI) | embed the signature algorithm and then receiver verifies both reference and signature.

  1. Is the KeyValue successfully created from the Array in your codes?
        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));

The KeyValue is the equivalent of a Public Key. The code is as simple as:

            KeyInfoFactory kif = fac.getKeyInfoFactory();
            KeyValue kv = kif.newKeyValue(kp.getPublic());

            // wrap the value in the KeyInfo
            KeyInfo ki = kif.newKeyInfo(List.of(kv));

Upon a valid XML digital signature created, it looks like this:

<national-treasure>
.......
    </Banff-highlights>
      <Banff-revenue>CAD$6 billion per annum
    </Banff-revenue>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
        <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
        <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
        <Reference URI="">
            <Transforms>
                <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
            <DigestValue>3tiwE7fdQxZ.....lU99kmkHAg/Bi6fESM=</DigestValue>
        </Reference>
    </SignedInfo>
    <SignatureValue>AZMlxFBjoZxmy8.....YOO3qINqG/wHoJPg==
    </SignatureValue>
    <KeyInfo>
        <KeyValue>
            <RSAKeyValue>
                <Modulus>sLQ+W9fT18Okqf.....tQ==
                </Modulus>
                <Exponent>AQAB</Exponent>
            </RSAKeyValue>
        </KeyValue>
    </KeyInfo>
</Signature>
</national-treasure>
  1. A pristine XML signature shall meet below requirements :
  • Signed element has not been manipulated
  • Digest method | value | algorithm shall match among canonicalized, encrypt and decrypt stages

Put that into context, the method of direct decoding key bytes and sending hash over the App servers quite defeats the purpose of secure signature receipt. There are options to buttress DS validation, mitigate risks(alter document construct), and prevent signature repudiation through Java API:

  • Enable secure Signature process which can restrict XSLT transforms document | cross reference URI in App server.
  • Aggregate security property to enforce restrictions. e.g. increased XML signature key length for validation.

In the realm of NoSQL/document store, like MarkLogic, there are sophisticated security features to explore and to protect PII data: element security, SSL signed certificates, document redaction.

Unless the original document, public key and signature are ALL compromised the same time, Java crypto API shall detect the tampering. Core signature (header |value | key value) or reference element can be effortlessly unmarshalled then validated:

KeyValue is altered:

Signature Key validation failure: Signature is faulty.

Signature value | algorithm | method doesn’t match:

Signature Value validation failure: Decode last unit does not contain valid bits.

Reference signature header | data element is altered:

Signature validation status: false
Kernel Signature validation failed!


来源:https://stackoverflow.com/questions/63228884/how-can-i-sign-hash-of-xml-file-and-integrate-signature-in-original-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!