- Added support to send signed sales tariffs (SECC side)

- Corrected the verification process of received (signed) sales tariffs (EVCC side)
- changed default value for XMLRepresentationOfMessages to false (SECC side)
This commit is contained in:
Marc Mültin 2016-04-06 17:09:46 -07:00
parent 4a8a8024f7
commit 458babda11
6 changed files with 166 additions and 34 deletions

View File

@ -138,6 +138,11 @@ public class V2GCommunicationSessionHandlerEVCC implements Observer {
getTransportLayerClient().addObserver(getV2gCommunicationSessionEVCC());
getV2gCommunicationSessionEVCC().addObserver(this);
// Set TLS security flag for communication session
boolean secureConn = (((Byte) getSecurity()).compareTo((Byte) GlobalValues.V2G_SECURITY_WITH_TLS.getByteValue()) == 0) ? true : false;
getV2gCommunicationSessionEVCC().setTlsConnection(secureConn);
sendSupportedAppProtocolReq();
} else {
getLogger().fatal("Maximum number of SECCDiscoveryReq messages reached");

View File

@ -28,6 +28,7 @@ import org.eclipse.risev2g.shared.v2gMessages.msgDef.ChargeProgressType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.DCEVSEChargeParameterType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.EVSEProcessingType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.PaymentOptionType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleListType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleTupleType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SignatureType;
@ -77,9 +78,8 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
SAScheduleListType saSchedules = (SAScheduleListType) chargeParameterDiscoveryRes.getSASchedules().getValue();
// Verify each sales tariff with the mobility operator sub 2 certificate
if (saSchedules != null && !verifySalesTariffs(saSchedules, v2gMessageRes.getHeader().getSignature()))
return new TerminateSession("Verification of sales tariffs failed");
if (saSchedules != null && getCommSessionContext().getSelectedPaymentOption().equals(PaymentOptionType.CONTRACT))
verifySalesTariffs(saSchedules, v2gMessageRes.getHeader().getSignature());
// Save the list of SASchedules (saves the time of reception as well)
getCommSessionContext().setSaSchedules(saSchedules);
@ -111,8 +111,31 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
}
}
/**
* Verifies each sales tariff given with the ChargeParameterDiscoveryRes message with the
* mobility operator sub 2 certificate.
*
* @param saSchedules The SASchedule list which holds all PMaxSchedules and SalesTariffs
* @param signature The signature for the sales tariffs
* @return True, if the verification of the sales tariffs was successful, false otherwise
*/
private boolean verifySalesTariffs(SAScheduleListType saSchedules, SignatureType signature) {
/*
* Some important requirements:
*
* 1. In case of PnC, and if a Tariff Table is used by the secondary actor, the secondary actor SHALL
* sign the field SalesTariff of type SalesTariffType. In case of EIM, the secondary actor MAY sign
* this field.
*
* 2. If the EVCC treats the SalesTariff as invalid, it shall ignore the SalesTariff table, i.e. the
* behaviour of the EVCC shall be the same as if no tariff tables were received. Furthermore, the
* EVCC MAY close the connection. It then may reopen the connection again.
*/
boolean salesTariffSignatureAvailable = (signature == null) ? false : true;
boolean ignoreSalesTariffs = (getCommSessionContext().isTlsConnection() && !salesTariffSignatureAvailable) ? true : false;
short ignoredSalesTariffs = 0;
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
List<SAScheduleTupleType> saScheduleTuples = saSchedules.getSAScheduleTuple();
int salesTariffCounter = 0;
@ -121,6 +144,13 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
// verification regards only sales tariffs, not PMaxSchedules
if (saScheduleTuple.getSalesTariff() == null) continue;
// Check if signature is given during TLS communication. If no signature is given, delete SalesTariff
if (ignoreSalesTariffs) {
ignoredSalesTariffs++;
saScheduleTuple.setSalesTariff(null);
continue;
}
salesTariffCounter++;
verifyXMLSigRefElements.put(
@ -142,6 +172,11 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
}
}
if (ignoredSalesTariffs > 0) {
getLogger().info("Sales tariffs could not be verified because of missing signature and will therefore be ignored");
return false;
}
return true;
}
}

View File

@ -72,5 +72,5 @@ PrivateEnvironment = false
# - false
# If this value is set to 'true', the EXICodec will print each message's XML representation (for debugging purposes)
# If no correct value is provided here, 'false' will be chosen
XMLRepresentationOfMessages = true
XMLRepresentationOfMessages = false

View File

@ -12,6 +12,7 @@ package org.eclipse.risev2g.secc.backend;
import java.security.KeyStore;
import java.security.interfaces.ECPrivateKey;
import java.util.HashMap;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
@ -19,6 +20,7 @@ import javax.xml.namespace.QName;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.eclipse.risev2g.secc.states.ServerState;
import org.eclipse.risev2g.shared.enumerations.GlobalValues;
import org.eclipse.risev2g.shared.utils.SecurityUtils;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
@ -28,6 +30,8 @@ import org.eclipse.risev2g.shared.v2gMessages.msgDef.PhysicalValueType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.RelativeTimeIntervalType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleListType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleTupleType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SalesTariffEntryType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SalesTariffType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.UnitSymbolType;
public class DummyBackendInterface implements IBackendInterface {
@ -40,50 +44,123 @@ public class DummyBackendInterface implements IBackendInterface {
}
@Override
public SAScheduleListType getSAScheduleList() {
public SAScheduleListType getSAScheduleList(int maxEntriesSAScheduleTuple, HashMap<String, byte[]> xmlSignatureRefElements) {
/*
* PMaxSchedule
* Some important requirements:
*
* 1. The sum of the individual time intervals described in the PMaxSchedule and
* SalesTariff provided in the ChargeParameterDiscoveryRes message shall match
* the period of time indicated by the EVCC in the message element DepartureTime of the
* ChargeParameterDiscoveryReq message.
*
* 2. If the EVCC did not provide a DepartureTime Target Setting in the ChargeParameterDiscoveryReq
* message, the sum of the individual time intervals described in the PMaxSchedule and SalesTariff
* provided in the ChargeParameterDiscoveryRes message, shall be greater or equal to 24 hours.
*
* 3. If the number of SalesTariffEntry elements in the SalesTariff or the number of
* PMaxScheduleEntry elements in the PMaxSchedule provided by the secondary actor(s) are not
* covering the entire period of time until DepartureTime, the Target Setting EAmount has not
* been met and the communication session has not been finished, it is the responsibility of
* the EVCC to request a new element of type SAScheduleListType as soon as the last
* SalesTariffEntry element or the last PMaxScheduleEntry element becomes active by sending
* a new ChargeParameterDiscoveryReq message.
*
* 4. In case of PnC, and if a Tariff Table is used by the secondary actor, the secondary actor SHALL
* sign the field SalesTariff of type SalesTariffType. In case of EIM, the secondary actor MAY sign
* this field.
*
* 5. The SECC shall 'copy' (not change!) the signature value received from the SA and transmit this value in the
* header of the ChargeParameterDiscoveryRes message.
*
* 6.
* If the element SalesTariff is signed, it shall be signed by the same private key that was used to
* issue the leaf contract certificate that the EVCC used during this connection for contract
* authentication (PnC).
*
* 7. An EVCC shall support 12 entries for PMaxScheduleEntry and SalesTariffEntry elements inside
* one SAScheduleTuple if MaxEntriesSAScheduleTuple is not transmitted in ChargeParameterDiscoveryReq.
*
* 8. The valid range for the value of EPriceLevel element shall be defined as being between 0 and
* the value of NumEPriceLevels element including the boundary values.
*/
PhysicalValueType pMaxValue = new PhysicalValueType();
pMaxValue.setMultiplier(new Byte("3"));
pMaxValue.setUnit(UnitSymbolType.W);
pMaxValue.setValue((short) 11);
RelativeTimeIntervalType timeInterval = new RelativeTimeIntervalType();
timeInterval.setStart(0);
timeInterval.setDuration(3600L);
PMaxScheduleEntryType pMaxScheduleEntry = new PMaxScheduleEntryType();
pMaxScheduleEntry.setTimeInterval(new JAXBElement<RelativeTimeIntervalType>(
new QName("urn:iso:15118:2:2013:MsgDataTypes", "RelativeTimeInterval"),
RelativeTimeIntervalType.class,
timeInterval));
pMaxScheduleEntry.setPMax(pMaxValue);
// PMaxSchedule
// IMPORTANT: check that you do not add more pMax entries than parameter maxEntriesSAScheduleTuple
PMaxScheduleType pMaxSchedule = new PMaxScheduleType();
pMaxSchedule.getPMaxScheduleEntry().add(pMaxScheduleEntry);
pMaxSchedule.getPMaxScheduleEntry().add(createPMaxScheduleEntry("3", (short) 11, 0, 7200L));
/*
* SalesTariff (add some meaningful things)
* But: If it is instantiated, it must be filled with meaningful data, otherwise there will
* occur an error with the EXIDecoder (at least at Vector)
*
* IMPORTANT: check that you do not add more sales tariff entries than parameter maxEntriesSAScheduleTuple
*/
SalesTariffType salesTariff = new SalesTariffType();
salesTariff.setId("salesTariff");
salesTariff.setSalesTariffID((short) 1);
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(0L, (short) 1));
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(1800L, (short) 4));
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(3600L, (short) 2));
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(5400L, (short) 3));
/*
* Put 'em all together
*/
// Put 'em all together
SAScheduleTupleType saScheduleTuple = new SAScheduleTupleType();
saScheduleTuple.setSAScheduleTupleID((short) 1);
saScheduleTuple.setPMaxSchedule(pMaxSchedule);
// saScheduleTuple.setSalesTariff(salesTariff);
saScheduleTuple.setSalesTariff(salesTariff);
SAScheduleListType saScheduleList = new SAScheduleListType();
saScheduleList.getSAScheduleTuple().add(saScheduleTuple);
// Set xml reference elements (repeat this for every sales tariff)
xmlSignatureRefElements.put(
salesTariff.getId(),
SecurityUtils.generateDigest(salesTariff, false));
return saScheduleList;
}
private SalesTariffEntryType createSalesTariffEntry(long start, short ePriceLevel) {
RelativeTimeIntervalType salesTariffTimeInterval = new RelativeTimeIntervalType();
salesTariffTimeInterval.setStart(start);
SalesTariffEntryType salesTariffEntry = new SalesTariffEntryType();
salesTariffEntry.setTimeInterval(new JAXBElement<RelativeTimeIntervalType>(
new QName("urn:iso:15118:2:2013:MsgDataTypes", "RelativeTimeInterval"),
RelativeTimeIntervalType.class,
salesTariffTimeInterval));
salesTariffEntry.setEPriceLevel(ePriceLevel);
return salesTariffEntry;
}
private PMaxScheduleEntryType createPMaxScheduleEntry(String multiplier, short pMax, long start) {
PhysicalValueType pMaxValue = new PhysicalValueType();
pMaxValue.setMultiplier(new Byte(multiplier));
pMaxValue.setUnit(UnitSymbolType.W);
pMaxValue.setValue(pMax);
RelativeTimeIntervalType pMaxTimeInterval = new RelativeTimeIntervalType();
pMaxTimeInterval.setStart(0);
pMaxTimeInterval.setDuration(7200L); // 2 hours
PMaxScheduleEntryType pMaxScheduleEntry = new PMaxScheduleEntryType();
pMaxScheduleEntry.setTimeInterval(new JAXBElement<RelativeTimeIntervalType>(
new QName("urn:iso:15118:2:2013:MsgDataTypes", "RelativeTimeInterval"),
RelativeTimeIntervalType.class,
pMaxTimeInterval));
pMaxScheduleEntry.setPMax(pMaxValue);
return pMaxScheduleEntry;
}
private PMaxScheduleEntryType createPMaxScheduleEntry(String multiplier, short pMax, long start, long duration) {
PMaxScheduleEntryType pMaxScheduleEntry = createPMaxScheduleEntry(multiplier, pMax, start);
((RelativeTimeIntervalType) pMaxScheduleEntry.getTimeInterval().getValue()).setDuration(duration);
return pMaxScheduleEntry;
}
@Override

View File

@ -11,6 +11,7 @@
package org.eclipse.risev2g.secc.backend;
import java.security.interfaces.ECPrivateKey;
import java.util.HashMap;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SAScheduleListType;
@ -20,9 +21,12 @@ public interface IBackendInterface {
/**
* Provides a list of schedules coming from a secondary actor (SAScheduleList) with pMax values
* and optional tariff incentives which shall influence the charging behaviour of the EV.
*
* @param maxEntriesSAScheduleTuple The maximum number of PMaxEntries and SalesTariff entries allowed by EVCC
* @param xmlSignatureRefElements Signature reference parameter provided to put sales tariff IDs and sales tariffs in
* @return An SASchedulesType element with a list of secondary actor schedules
*/
public SAScheduleListType getSAScheduleList();
public SAScheduleListType getSAScheduleList(int maxEntriesSAScheduleTuple, HashMap<String, byte[]> xmlSignatureRefElements);
/**

View File

@ -55,14 +55,22 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
/*
* Request a new schedule in case of first ChargeParameterDiscoveryReq.
* If EVSEProcessingType.ONGOING was sent in previous ChargeParameterDiscoveryReq
* response message, do not request again.
* If EVSEProcessingType.ONGOING was sent in previous ChargeParameterDiscoveryRes
* message, do not request again.
*/
if (!isWaitingForSchedule()) {
// TODO we need a timeout mechanism here so that a response can be sent within 2s
setWaitingForSchedule(true);
// The max. number of PMaxScheduleEntries and SalesTariffEntries is 1024 if not provided otherwise by EVCC
int maxEntriesSAScheduleTuple = (chargeParameterDiscoveryReq.getMaxEntriesSAScheduleTuple() != null) ?
chargeParameterDiscoveryReq.getMaxEntriesSAScheduleTuple() : 1024;
getCommSessionContext().setSaSchedules(
getCommSessionContext().getBackendInterface().getSAScheduleList());
getCommSessionContext().getBackendInterface().getSAScheduleList(
maxEntriesSAScheduleTuple,
getXMLSignatureRefElements())
);
}
// Wait a bit and check if the schedule has already been provided
@ -98,6 +106,9 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
chargeParameterDiscoveryRes.setSASchedules(
getSASchedulesAsJAXBElement(getCommSessionContext().getSaSchedules()));
// Set signing private key
setSignaturePrivateKey(getCommSessionContext().getBackendInterface().getSAProvisioningCertificatePrivateKey());
if (chargeParameterDiscoveryReq.getRequestedEnergyTransferMode().toString().startsWith("AC"))
return getSendMessage(chargeParameterDiscoveryRes, V2GMessages.POWER_DELIVERY_REQ);
else