luisgoncalves / xades4j

A Java library for XAdES signature services
GNU Lesser General Public License v3.0
111 stars 66 forks source link

FileSystemKeyStoreKeyingDataProvider pass certificate as String/Bytes #235

Closed kurbhatt closed 3 years ago

kurbhatt commented 3 years ago

Hello, I have a working code for XAdES for signing XML. There're mainly 2 questions I have:

  1. Is there any way to pass the certificate as String or Bytes rather than passing the file path? (It might be possible that there can be multiple certificates so I may store certificate string and password in the database)
  2. Is there any way to get transformed StremResult directly in code rather than passing resulted data into the file system?

Following is the code:

public class Xades {
    private static final String CERT_FOLDER = "/Users/tnt/Documents/apps/cert/new1/";
    private static final String CERT        = "myp.pfx";
    private static final String PASS        = "12345678";
    private static final String SIGNED      = "/Users/tnt/Documents/apps/cert/signed.xml";

    public static void main(String[] args) {
        try {
            System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
            System.out.println("\tSign");
            System.out.println("______________________");
            signBes();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static void signBes() throws Exception {
        String xml = "UBL XML";
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        InputSource is = new InputSource(new StringReader(xml));
        Document doc = db.parse(is);

        Element elem = doc.getDocumentElement();
        DOMHelper.useIdAsXmlId(elem);

        KeyingDataProvider kdp = new FileSystemKeyStoreKeyingDataProvider(
          "pkcs12", CERT_FOLDER + CERT, new FirstCertificateSelector(), new DirectPasswordProvider(PASS),
          new DirectPasswordProvider(PASS), true); // want to pass certificate string directly rather than file
        DataObjectDesc obj = new DataObjectReference("").withTransform(new EnvelopedSignatureTransform());
        SignedDataObjects dataObjs = new SignedDataObjects().withSignedDataObject(obj);

        XadesSigner signer = new XadesBesSigningProfile(kdp).newSigner();
        signer.sign(dataObjs, elem);

        TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer = tFactory.newTransformer();
        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(new File(SIGNED));
        transformer.transform(source, result); // need transformed result in code rather than write it into the file system
    }
}

If there's any possibility to handle the above-mentioned cases, please guide me for that.

luisgoncalves commented 3 years ago

Hi

  1. Currently there's no way to do that using the FileSystemKeyStoreKeyingDataProvider / KeyStoreKeyingDataProvider classes.
    • I don't remember why the code doesn't use KeyStore.load() which takes an InputStream (and instead uses the builder), but I think it had something to do with the timing of the loading.
    • A possible workaround is to implement your own KeyingDataProvider, which takes the stream and loads the keystore. Should be simple enough.
  2. This is not related to xades4j itself. Note that StreamResult has a constructor that takes an OutputStream. You can pass whatever you want there, namely a ByteArrayOutputStream if you want the bytes.

Let me know if this helps.

kurbhatt commented 3 years ago

Hello. Thanks for the quick guidance. Created Custom KeyingDataProvider implementation that takes KeyStore(passed InputStream and password in .load() method)as a parameter(prepared it before provider call) and ByteArrayOutputStream as a StreamResult constructor parameter and it's working as expected.

Your suggestion works very well. There is one caveat, this I have done first through file read. Now I have a String variable(fetched from database) that will have PKCS12 certificate string but while converting that String to InputStream(using ByteArrayInputStream) it's not working(in .load() method). Any suggestion on this?

luisgoncalves commented 3 years ago

I don't know what the encoding of that string is. Is it base64? Did you decode that?

Anyway, that doesn't seem related to xades4j at all. You seem to be on the right track with the other issues. I'm closing this now.

kurbhatt commented 3 years ago

@luisgoncalves What is the reason for adding an invalid label?

luisgoncalves commented 3 years ago

Nothing in the ticket highlighted an issue in xades4j. This way it's easier to lookup such issues.