From 54759ee2491b91dd777a5146cccd078754a63a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20M=C3=BCltin?= Date: Fri, 11 Nov 2016 16:53:46 +0900 Subject: [PATCH] - Bugfix: corrected the use of XSD schema files when applying EXI encoding for signature creation. Distinguishes now between EXI encoding for the SignedInfoElement of the header's signature (where XMLdsig schema needs to be used) and EXI encoding for the reference elements of the header's signature. - Bugfix: corrected the use of EXI encoding option when encoding signature header: Here the schema-informed fragment grammar option needs to be used. For EXI encoding of message bodies, this option is not to be used. --- .../shared/exiCodec/EXIficientCodec.java | 52 +++++++++++++++---- .../risev2g/shared/exiCodec/ExiCodec.java | 7 ++- .../risev2g/shared/exiCodec/OpenEXICodec.java | 29 +++++++++-- .../messageHandling/MessageHandler.java | 4 +- .../risev2g/shared/utils/SecurityUtils.java | 23 +++++--- 5 files changed, 87 insertions(+), 28 deletions(-) diff --git a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/EXIficientCodec.java b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/EXIficientCodec.java index e4c099b..74d39e6 100644 --- a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/EXIficientCodec.java +++ b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/EXIficientCodec.java @@ -34,6 +34,7 @@ import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; import com.siemens.ct.exi.EXIFactory; +import com.siemens.ct.exi.EncodingOptions; import com.siemens.ct.exi.GrammarFactory; import com.siemens.ct.exi.api.sax.EXIResult; import com.siemens.ct.exi.api.sax.EXISource; @@ -54,6 +55,7 @@ public final class EXIficientCodec extends ExiCodec { private GrammarFactory grammarFactory; private Grammars grammarAppProtocol; private Grammars grammarMsgDef; + private Grammars grammarXMLDSig; private OutputStream encodeOS; private EXIficientCodec() { @@ -61,6 +63,7 @@ public final class EXIficientCodec extends ExiCodec { setExiFactory(DefaultEXIFactory.newInstance()); getExiFactory().setValuePartitionCapacity(0); + setFragment(false); // needs to be set to true when encoding signatures setGrammarFactory(GrammarFactory.newInstance()); /* @@ -73,6 +76,9 @@ public final class EXIficientCodec extends ExiCodec { setGrammarMsgDef(getGrammarFactory().createGrammars( getClass().getResourceAsStream(GlobalValues.SCHEMA_PATH_MSG_DEF.toString()), XSDResolver.getInstance())); + setGrammarXMLDSig(getGrammarFactory().createGrammars( + getClass().getResourceAsStream(GlobalValues.SCHEMA_PATH_XMLDSIG.toString()), + XSDResolver.getInstance())); } catch (EXIException e) { getLogger().error("Error occurred while trying to initialize EXIficientCodec (EXIException)!", e); } @@ -86,21 +92,30 @@ public final class EXIficientCodec extends ExiCodec { - public synchronized byte[] encodeEXI(Object jaxbObject, boolean supportedAppProtocolHandshake) { + public synchronized byte[] encodeEXI(Object jaxbObject, String xsdSchemaPath) { + Grammars grammar = null; + + if (xsdSchemaPath.equals(GlobalValues.SCHEMA_PATH_APP_PROTOCOL.toString())) + grammar = getGrammarAppProtocol(); + else if (xsdSchemaPath.equals(GlobalValues.SCHEMA_PATH_MSG_DEF.toString())) + grammar = getGrammarMsgDef(); + else if (xsdSchemaPath.equals(GlobalValues.SCHEMA_PATH_XMLDSIG.toString())) + grammar = getGrammarXMLDSig(); + else { + getLogger().error("False schema path provided for encoding jaxbObject into EXI"); + return null; + } + InputStream inStream = marshalToInputStream(jaxbObject); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - baos = ((ByteArrayOutputStream) encode(inStream, supportedAppProtocolHandshake)); + baos = ((ByteArrayOutputStream) encode(inStream, grammar)); + + // If needed for debugging +// getLogger().debug("Encoded EXI byte stream to be sent: " + ByteUtils.toHexString(baos.toByteArray())); return baos.toByteArray(); } - - private synchronized OutputStream encode(InputStream jaxbXML, boolean supportedAppProtocolHandshake) { - if (supportedAppProtocolHandshake) return encode(jaxbXML, getGrammarAppProtocol()); - else return encode(jaxbXML, getGrammarMsgDef()); - } - - private synchronized OutputStream encode(InputStream jaxbXML, Grammars grammar) { EXIResult exiResult = null; @@ -126,6 +141,9 @@ public final class EXIficientCodec extends ExiCodec { @Override public synchronized Object decodeEXI(byte[] exiEncodedMessage, boolean supportedAppProtocolHandshake) { + // If needed for debugging +// getLogger().debug("Decoded incoming EXI stream: " + ByteUtils.toHexString(exiEncodedMessage)); + ByteArrayInputStream bais = new ByteArrayInputStream(exiEncodedMessage); setDecodedExi(decode(bais, supportedAppProtocolHandshake)); @@ -177,8 +195,17 @@ public final class EXIficientCodec extends ExiCodec { private void setGrammarMsgDef(Grammars grammarMsgDef) { this.grammarMsgDef = grammarMsgDef; } + - private EXIFactory getExiFactory() { + public Grammars getGrammarXMLDSig() { + return grammarXMLDSig; + } + + public void setGrammarXMLDSig(Grammars grammarXMLDSig) { + this.grammarXMLDSig = grammarXMLDSig; + } + + public EXIFactory getExiFactory() { return exiFactory; } @@ -193,4 +220,9 @@ public final class EXIficientCodec extends ExiCodec { private void setGrammarFactory(GrammarFactory grammarFactory) { this.grammarFactory = grammarFactory; } + + @Override + public void setFragment(boolean useFragmentGrammar) { + getExiFactory().setFragment(useFragmentGrammar); + } } diff --git a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/ExiCodec.java b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/ExiCodec.java index 308484a..6161e30 100644 --- a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/ExiCodec.java +++ b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/ExiCodec.java @@ -15,14 +15,12 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; - import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.ValidationEvent; import javax.xml.bind.ValidationEventHandler; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.risev2g.shared.utils.MiscUtils; @@ -112,17 +110,18 @@ public abstract class ExiCodec { if (getInStream() != null) getInStream().reset(); setInStream(new ByteArrayInputStream(decodedExiString.getBytes())); return getUnmarshaller().unmarshal(getInStream()); - } catch (IOException | JAXBException e) { + } catch (IOException | JAXBException | RuntimeException e) { getLogger().error(e.getClass().getSimpleName() + " occurred while trying to unmarshall decoded message", e); return null; } } - public abstract byte[] encodeEXI(Object jaxbXML, boolean supportedAppProtocolHandshake); + public abstract byte[] encodeEXI(Object jaxbXML, String xsdSchemaPath); public abstract Object decodeEXI(byte[] exiEncodedMessage, boolean supportedAppProtocolHandshake); + public abstract void setFragment(boolean useFragmentGrammar); public Marshaller getMarshaller() { return marshaller; diff --git a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/OpenEXICodec.java b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/OpenEXICodec.java index bae035e..2ac84b5 100644 --- a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/OpenEXICodec.java +++ b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/exiCodec/OpenEXICodec.java @@ -49,6 +49,7 @@ public final class OpenEXICodec extends ExiCodec { private InputStream schemaAppProtocolIS; private EXISchema exiSchemaAppProtocol; private EXISchema exiSchemaMsgDef; + private EXISchema exiSchemaXMLDSig; private short options; private SAXTransformerFactory saxTransformerFactory; private SAXParserFactory saxParserFactory; @@ -64,7 +65,8 @@ public final class OpenEXICodec extends ExiCodec { // The Transmogrifier performs the translation from XML to EXI format setTransmogrifier(new Transmogrifier()); getTransmogrifier().setValuePartitionCapacity(0); -// getTransmogrifier().setDivertBuiltinGrammarToAnyType(true); // enable V2G's built-in grammar usage + getTransmogrifier().setFragment(false); +// getTransmogrifier().setDivertBuiltinGrammarToAnyType(true); // enable V2G's built-in grammar usage // Standard SAX methods parse content and lexical values setSaxTransformerFactory((SAXTransformerFactory) SAXTransformerFactory.newInstance()); @@ -109,7 +111,7 @@ public final class OpenEXICodec extends ExiCodec { @Override - public byte[] encodeEXI(Object jaxbObject, boolean supportedAppProtocolHandshake) { + public byte[] encodeEXI(Object jaxbObject, String xsdSchemaPath) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { @@ -118,10 +120,16 @@ public final class OpenEXICodec extends ExiCodec { * The Grammar Cache stores the XML schema and options used to encode an EXI file. * The settings must match when encoding and subsequently decoding a data set. */ - if (supportedAppProtocolHandshake) + if (xsdSchemaPath.equals(GlobalValues.SCHEMA_PATH_APP_PROTOCOL.toString())) setGrammarCache(new GrammarCache(getExiSchemaAppProtocol(), getOptions())); - else + else if (xsdSchemaPath.equals(GlobalValues.SCHEMA_PATH_MSG_DEF.toString())) setGrammarCache(new GrammarCache(getExiSchemaMsgDef(), getOptions())); + else if (xsdSchemaPath.equals(GlobalValues.SCHEMA_PATH_XMLDSIG.toString())) + setGrammarCache(new GrammarCache(getExiSchemaXMLDSig(), getOptions())); + else { + getLogger().error("False schema path provided for encoding jaxbObject into EXI"); + return null; + } // Set the configuration options in the Transmogrifier getTransmogrifier().setGrammarCache(getGrammarCache()); @@ -276,4 +284,17 @@ public final class OpenEXICodec extends ExiCodec { public void setExiSchemaMsgDef(EXISchema exiSchemaMsgDef) { this.exiSchemaMsgDef = exiSchemaMsgDef; } + + public EXISchema getExiSchemaXMLDSig() { + return exiSchemaXMLDSig; + } + + public void setExiSchemaXMLDSig(EXISchema exiSchemaXMLDSig) { + this.exiSchemaXMLDSig = exiSchemaXMLDSig; + } + + @Override + public void setFragment(boolean useFragmentGrammar) { + getTransmogrifier().setFragment(useFragmentGrammar); + } } diff --git a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/messageHandling/MessageHandler.java b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/messageHandling/MessageHandler.java index 6a5f014..9d4d74c 100644 --- a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/messageHandling/MessageHandler.java +++ b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/messageHandling/MessageHandler.java @@ -109,12 +109,12 @@ public class MessageHandler { public synchronized Object suppAppProtocolMsgToExi(Object suppAppProtocolObject) { - return getExiCodec().encodeEXI(suppAppProtocolObject, true); + return getExiCodec().encodeEXI(suppAppProtocolObject, GlobalValues.SCHEMA_PATH_APP_PROTOCOL.toString()); } public synchronized Object v2gMsgToExi(Object jaxbObject) { - byte[] encodedEXI = getExiCodec().encodeEXI(jaxbObject, false); + byte[] encodedEXI = getExiCodec().encodeEXI(jaxbObject, GlobalValues.SCHEMA_PATH_MSG_DEF.toString()); // For test purposes you can log the byte array // getLogger().debug("Encoded EXI byte array: " + ByteUtils.toHexString(encodedEXI)); diff --git a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/utils/SecurityUtils.java b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/utils/SecurityUtils.java index 0b52738..bb02f23 100644 --- a/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/utils/SecurityUtils.java +++ b/RISE-V2G-Shared/src/main/java/org/eclipse/risev2g/shared/utils/SecurityUtils.java @@ -60,7 +60,6 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; - import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; @@ -79,7 +78,6 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.security.auth.x500.X500Principal; import javax.xml.bind.JAXBElement; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.risev2g.shared.enumerations.GlobalValues; @@ -1567,17 +1565,26 @@ public final class SecurityUtils { * needs to be set to false. * * @param messageOrField The message or field for which a digest is to be generated - * @param signature True if a digest for a signature is to be generated, false otherwise + * @param digestForSignedInfoElement True if a digest for the SignedInfoElement of the header's signature is to be generated, false otherwise * @return The SHA-256 digest for message or field */ - public static byte[] generateDigest(Object messageOrField, boolean signature) { + public static byte[] generateDigest(Object messageOrField, boolean digestForSignedInfoElement) { JAXBElement jaxbElement = MiscUtils.getJaxbElement(messageOrField); byte[] encoded; - // TODO what was again the difference? - if (signature) encoded = getExiCodec().encodeEXI(jaxbElement, false); - else encoded = getExiCodec().encodeEXI(jaxbElement, false); - + // The schema-informed fragment grammar option needs to be used for EXI encodings in the header's signature + getExiCodec().setFragment(true); + + /* + * When creating the signature value for the SignedInfoElement, we need to use the XMLdsig schema, + * whereas for creating the reference elements of the signature, we need to use the V2G_CI_MsgDef schema. + */ + if (digestForSignedInfoElement) encoded = getExiCodec().encodeEXI(jaxbElement, GlobalValues.SCHEMA_PATH_XMLDSIG.toString()); + else encoded = getExiCodec().encodeEXI(jaxbElement, GlobalValues.SCHEMA_PATH_MSG_DEF.toString()); + + // Do not use the schema-informed fragment grammar option for other EXI encodings (message bodies) + getExiCodec().setFragment(false); + try { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(encoded);