Changes/bugfixes due to experience in 4th International Testing Symposium in San Diego, California (April 2016)

- shortend all IDs of reference signature headers to 3 characters like the example given in Annex J (because some embedded systems might restrict the length to 3 characters)
- SecurityUtils: minor additional checks in verifySignature() method
- avoided NullPointerException when incoming message does not have ISO 15118 schema (for example DINSPEC 70121 XSD schema for DC charging)
- made sure optional parameters of ServiceDiscoveryRes are not empty when sent
- added logging message which shows which payment option was chosen by EVCC
- added optional parameter EVSEIsolationStatus in DCEVSEStatus as some EVCC systems tend to need this value according to testival
- bug fix: applied correct private key for signing sales tariff (must be MOSub2CA private key)
- made sure EVCC does not choose contract based payment if SECC offers it although no TLS communication is set up
This commit is contained in:
Marc Mültin 2016-04-11 14:59:47 +02:00
parent 458babda11
commit 6345846e7b
20 changed files with 148 additions and 37 deletions

View File

@ -68,7 +68,7 @@ RequestedPaymentOption =
# - DC_extended
# - DC_combo_core
# - DC_unique
RequestedEnergyTransferMode = AC_three_phase_core
RequestedEnergyTransferMode =
# XML representation of messages

View File

@ -48,10 +48,12 @@ public class DummyEVController implements IACEVController, IDCEVController {
@Override
public PaymentOptionType getPaymentOption(PaymentOptionListType paymentOptionsOffered) {
if (paymentOptionsOffered.getPaymentOption().contains(PaymentOptionType.CONTRACT))
return PaymentOptionType.CONTRACT;
else
return PaymentOptionType.EXTERNAL_PAYMENT;
if (paymentOptionsOffered.getPaymentOption().contains(PaymentOptionType.CONTRACT)) {
if (!getCommSessionContext().isTlsConnection()) {
getLogger().warn("SECC offered CONTRACT based payment although no TLS connectionis used. Choosing EIM instead");
return PaymentOptionType.EXTERNAL_PAYMENT;
} else return PaymentOptionType.CONTRACT;
} else return PaymentOptionType.EXTERNAL_PAYMENT;
}

View File

@ -253,7 +253,13 @@ public abstract class ClientState extends State {
if (genChallenge != null) {
authorizationReq.setGenChallenge(genChallenge);
authorizationReq.setId("authorizationReq");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
authorizationReq.setId("id1");
}
return authorizationReq;

View File

@ -162,7 +162,7 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
X509Certificate moSub2Certificate = SecurityUtils.getMOSub2Certificate(
GlobalValues.EVCC_KEYSTORE_FILEPATH.toString());
if (moSub2Certificate == null) {
getLogger().error("No MOSub2Certificate found");
getLogger().error("No MOSub2Certificate found, signature of sales tariff could therefore not be verified");
return false;
} else {
ECPublicKey ecPublicKey = (ECPublicKey) moSub2Certificate.getPublicKey();

View File

@ -38,7 +38,13 @@ public class WaitForChargingStatusRes extends ClientState {
// ReceiptRequired has higher priority than a possible EVSENotification=Renegotiate
if (chargingStatusRes.isReceiptRequired()) {
MeteringReceiptReqType meteringReceiptReq = new MeteringReceiptReqType();
meteringReceiptReq.setId("meteringReceiptReq");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
meteringReceiptReq.setId("id1");
meteringReceiptReq.setMeterInfo(chargingStatusRes.getMeterInfo());
meteringReceiptReq.setSAScheduleTupleID(chargingStatusRes.getSAScheduleTupleID());
meteringReceiptReq.setSessionID(getCommSessionContext().getSessionID());

View File

@ -37,7 +37,13 @@ public class WaitForCurrentDemandRes extends ClientState {
// ReceiptRequired has higher priority than a possible EVSENotification=Renegotiate
if (currentDemandRes.isReceiptRequired()) {
MeteringReceiptReqType meteringReceiptReq = new MeteringReceiptReqType();
meteringReceiptReq.setId("MeterInfo");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
meteringReceiptReq.setId("id1");
meteringReceiptReq.setMeterInfo(currentDemandRes.getMeterInfo());
meteringReceiptReq.setSAScheduleTupleID(currentDemandRes.getSAScheduleTupleID());
meteringReceiptReq.setSessionID(getCommSessionContext().getSessionID());

View File

@ -43,6 +43,7 @@ public class WaitForPaymentDetailsRes extends ClientState {
} else {
// Set xml reference element
AuthorizationReqType authorizationReq = getAuthorizationReq(paymentDetailsRes.getGenChallenge());
getXMLSignatureRefElements().put(
authorizationReq.getId(),
SecurityUtils.generateDigest(authorizationReq, false));

View File

@ -72,7 +72,13 @@ public class WaitForPaymentServiceSelectionRes extends ClientState {
GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString());
CertificateInstallationReqType certInstallationReq = new CertificateInstallationReqType();
certInstallationReq.setId("certificateInstallationReq");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
certInstallationReq.setId("id1");
certInstallationReq.setListOfRootCertificateIDs(
SecurityUtils.getListOfRootCertificateIDs(
GlobalValues.EVCC_TRUSTSTORE_FILEPATH.toString(),
@ -105,7 +111,13 @@ public class WaitForPaymentServiceSelectionRes extends ClientState {
GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString()),
GlobalValues.ALIAS_CONTRACT_CERTIFICATE.toString()));
certificateUpdateReq.setEMAID(SecurityUtils.getEMAID(GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString()).getValue());
certificateUpdateReq.setId("certificateUpdateReq");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
certificateUpdateReq.setId("id1");
certificateUpdateReq.setListOfRootCertificateIDs(
SecurityUtils.getListOfRootCertificateIDs(
GlobalValues.EVCC_TRUSTSTORE_FILEPATH.toString(),

View File

@ -93,7 +93,8 @@ public class WaitForServiceDiscoveryRes extends ClientState {
* Furthermore, it must be checked if VAS are allowed (-> only if TLS connection is used)
*/
private boolean useVAS(ServiceDiscoveryResType serviceDiscoveryRes) {
if (getCommSessionContext().getTransportLayerClient() instanceof TLSClient) {
if (serviceDiscoveryRes.getServiceList() != null &&
getCommSessionContext().getTransportLayerClient() instanceof TLSClient) {
// Check if certificate service is needed
if (isCertificateServiceOffered(serviceDiscoveryRes.getServiceList())) {
getCommSessionContext().setContractCertStatus(SecurityUtils.getContractCertificateStatus());
@ -128,6 +129,11 @@ public class WaitForServiceDiscoveryRes extends ClientState {
private boolean isCertificateServiceOffered(ServiceListType offeredServiceList) {
if (offeredServiceList == null) {
getLogger().debug("No value added services offered by EVCC");
return false;
}
for (ServiceType service : offeredServiceList.getService()) {
if (service.getServiceCategory().equals(ServiceCategoryType.CONTRACT_CERTIFICATE))
return true;

View File

@ -20,7 +20,6 @@ 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;
@ -34,6 +33,11 @@ import org.eclipse.risev2g.shared.v2gMessages.msgDef.SalesTariffEntryType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SalesTariffType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.UnitSymbolType;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
public class DummyBackendInterface implements IBackendInterface {
private V2GCommunicationSessionSECC commSessionContext;
@ -97,7 +101,13 @@ public class DummyBackendInterface implements IBackendInterface {
* IMPORTANT: check that you do not add more sales tariff entries than parameter maxEntriesSAScheduleTuple
*/
SalesTariffType salesTariff = new SalesTariffType();
salesTariff.setId("salesTariff");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
salesTariff.setId("id1");
salesTariff.setSalesTariffID((short) 1);
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(0L, (short) 1));
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(1800L, (short) 4));
@ -179,12 +189,20 @@ public class DummyBackendInterface implements IBackendInterface {
@Override
public ECPrivateKey getSAProvisioningCertificatePrivateKey() {
KeyStore keyStore = SecurityUtils.getPKCS12KeyStore(
"./provServiceCert.p12",
GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString());
return SecurityUtils.getPrivateKey(keyStore);
Path pathToPrivateKey = FileSystems.getDefault().getPath("./MOSub2_ISO-UG_2016-1_key.bin");
byte[] moSub2PrivateKey = null;
try {
moSub2PrivateKey = Files.readAllBytes(pathToPrivateKey);
} catch (IOException e) {
getLogger().error("IOException occurred while trying to read MOSub2 private key for signing sales tariff");
return null;
}
return SecurityUtils.getPrivateKey(moSub2PrivateKey);
}
@Override
public CertificateChainType getSAProvisioningCertificateChain() {
return SecurityUtils.getCertificateChain("./provServiceCert.p12");

View File

@ -20,6 +20,7 @@ import org.eclipse.risev2g.shared.v2gMessages.msgDef.DCEVSEChargeParameterType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.DCEVSEStatusCodeType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.DCEVSEStatusType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.IsolationLevelType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.MeterInfoType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.PhysicalValueType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.UnitSymbolType;
@ -87,7 +88,7 @@ public class DummyDCEVSEController implements IDCEVSEController {
dcEvseStatus.setNotificationMaxDelay(0);
dcEvseStatus.setEVSENotification((notification != null) ? notification : EVSENotificationType.NONE);
dcEvseStatus.setEVSEStatusCode(DCEVSEStatusCodeType.EVSE_READY);
// dcEvseStatus.setEVSEIsolationStatus(IsolationLevelType.INVALID);
dcEvseStatus.setEVSEIsolationStatus(IsolationLevelType.VALID);
return dcEvseStatus;
}

View File

@ -141,7 +141,7 @@ public class V2GCommunicationSessionHandlerSECC implements Observer {
*/
byte[] seccAddress = (isSecureCommunication()) ? TLSServer.getInstance().getServerAddress().getAddress() : TCPServer.getInstance().getServerAddress().getAddress();
int seccPort = (isSecureCommunication()) ? TLSServer.getInstance().getServerPort() : TCPServer.getInstance().getServerPort();
SECCDiscoveryRes seccDiscoveryRes = new SECCDiscoveryRes(
seccAddress,
ByteUtils.toByteArrayFromInt(seccPort, true),

View File

@ -76,9 +76,11 @@ public class WaitForAuthorizationReq extends ServerState {
// Verify signature
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(authorizationReq.getId(), SecurityUtils.generateDigest(authorizationReq, false));
ECPublicKey ecPublicKey = (ECPublicKey) SecurityUtils.getCertificate(
getCommSessionContext().getContractSignatureCertChain().getCertificate())
.getPublicKey();
if (!SecurityUtils.verifySignature(signature, verifyXMLSigRefElements, ecPublicKey)) {
authorizationRes.setResponseCode(ResponseCodeType.FAILED_SIGNATURE_ERROR);
return false;

View File

@ -61,13 +61,19 @@ public class WaitForCertificateInstallationReq extends ServerState {
getCommSessionContext().getBackendInterface().getContractCertificatePrivateKey());
certificateInstallationRes.setContractSignatureCertChain(saContractCertificateChain);
certificateInstallationRes.getContractSignatureCertChain().setId("contractSignatureCertChain");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
certificateInstallationRes.getContractSignatureCertChain().setId("id1"); // contractSignatureCertChain
certificateInstallationRes.setContractSignatureEncryptedPrivateKey(encryptedContractCertPrivateKey);
certificateInstallationRes.getContractSignatureEncryptedPrivateKey().setId("contractSignatureEncryptedPrivateKey");
certificateInstallationRes.getContractSignatureEncryptedPrivateKey().setId("id2"); // contractSignatureEncryptedPrivateKey
certificateInstallationRes.setDHpublickey(SecurityUtils.getDHPublicKey(ecdhKeyPair));
certificateInstallationRes.getDHpublickey().setId("dhPublicKey");
certificateInstallationRes.getDHpublickey().setId("id3"); // dhPublicKey
certificateInstallationRes.setEMAID(SecurityUtils.getEMAID(saContractCertificateChain));
certificateInstallationRes.getEMAID().setId("emaid");
certificateInstallationRes.getEMAID().setId("id4"); // emaid
certificateInstallationRes.setSAProvisioningCertificateChain(
getCommSessionContext().getBackendInterface().getSAProvisioningCertificateChain());

View File

@ -63,13 +63,19 @@ public class WaitForCertificateUpdateReq extends ServerState {
getCommSessionContext().getBackendInterface().getContractCertificatePrivateKey());
certificateUpdateRes.setContractSignatureCertChain(contractCertificateChain);
certificateUpdateRes.getContractSignatureCertChain().setId("contractSignatureCertChain");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
certificateUpdateRes.getContractSignatureCertChain().setId("id1"); // contractSignatureCertChain
certificateUpdateRes.setContractSignatureEncryptedPrivateKey(encryptedContractCertPrivateKey);
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().setId("contractSignatureEncryptedPrivateKey");
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().setId("id2"); // contractSignatureEncryptedPrivateKey
certificateUpdateRes.setDHpublickey(SecurityUtils.getDHPublicKey(ecdhKeyPair));
certificateUpdateRes.getDHpublickey().setId("dhPublicKey");
certificateUpdateRes.getDHpublickey().setId("id3"); // dhPublicKey
certificateUpdateRes.setEMAID(SecurityUtils.getEMAID(contractCertificateChain));
certificateUpdateRes.getEMAID().setId("emaid");
certificateUpdateRes.getEMAID().setId("id4"); // emaid
certificateUpdateRes.setSAProvisioningCertificateChain(getCommSessionContext().getBackendInterface().getSAProvisioningCertificateChain());
// In case of negative response code, try at next charging (retryCounter = 0)

View File

@ -37,6 +37,9 @@ public class WaitForPaymentServiceSelectionReq extends ServerState {
PaymentServiceSelectionReqType paymentServiceSelectionReq =
(PaymentServiceSelectionReqType) v2gMessageReq.getBody().getBodyElement().getValue();
getLogger().info("Payment option " + paymentServiceSelectionReq.getSelectedPaymentOption().toString() +
" has been chosen by EVCC");
if (isResponseCodeOK(paymentServiceSelectionReq)) {
// see [V2G2-551]
if (paymentServiceSelectionReq.getSelectedPaymentOption().equals(PaymentOptionType.CONTRACT)) {

View File

@ -78,8 +78,19 @@ public class WaitForServiceDiscoveryReq extends ServerState {
chargeService.setSupportedEnergyTransferMode(supportedEnergyTransferModes);
chargeService.setServiceCategory(ServiceCategoryType.EV_CHARGING);
chargeService.setServiceID(1); // according to Table 105 ISO/IEC 15118-2
chargeService.setServiceName("EV charging (AC/DC)"); // optional value
chargeService.setServiceScope(""); // optional value
/*
* Is an optional value, but fill it with a non-empty string if used,
* otherwise an EXI decoding error could occur on the other side!
*/
chargeService.setServiceName("EV charging (AC/DC)");
/*
* Is an optional value, but fill it with a non-empty string if used,
* otherwise an EXI decoding error could occur on the other side!
*/
// chargeService.setServiceScope("");
chargeService.setFreeService(false); // it is supposed that charging is by default not for free
return chargeService;

View File

@ -77,10 +77,18 @@ public class WaitForSupportedAppProtocolReq extends ServerState {
} else if (message instanceof SECCDiscoveryReq) {
getLogger().debug("Another SECCDiscoveryReq was received, changing to state WaitForSECCDiscoveryReq");
return new ChangeProcessingState(message, getCommSessionContext().getStates().get(V2GMessages.SECC_DISCOVERY_REQ));
} else {
} else if (message != null) {
/*
* This check has been introduced to make sure the application can deal with incoming messages which rely
* on the DINSPEC 70121 XSD schema (which is different from the ISO 15118-2 schema. Without this check,
* the message.getClass() would throw a NullPointerException and the application would die.
*/
getLogger().error("Invalid message (" + message.getClass().getSimpleName() +
") at this state (" + this.getClass().getSimpleName() + ")");
supportedAppProtocolRes.setResponseCode(ResponseCodeType.FAILED_NO_NEGOTIATION);
} else {
getLogger().error("Invalid message at this state, message seems to be null. Check if same XSD schema is used on EVCC side.");
supportedAppProtocolRes.setResponseCode(ResponseCodeType.FAILED_NO_NEGOTIATION);
}
return getSendMessage(supportedAppProtocolRes,

View File

@ -264,7 +264,7 @@ public final class MiscUtils {
* (I don't know how to infer the type correctly)
*
* @param messageOrField The message or field for which a digest is to be generated
* @return
* @return The JAXBElement of the provided message or field
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static JAXBElement getJaxbElement(Object messageOrField) {

View File

@ -600,7 +600,13 @@ public final class SecurityUtils {
*/
public static DiffieHellmanPublickeyType getDHPublicKey(KeyPair ecdhKeyPair) {
DiffieHellmanPublickeyType dhPublicKey = new DiffieHellmanPublickeyType();
dhPublicKey.setId("dhPublicKey");
/*
* Experience from the test symposium in San Diego (April 2016):
* The Id element of the signature is not restricted in size by the standard itself. But on embedded
* systems, the memory is very limited which is why we should not use long IDs for the signature reference
* element. A good size would be 3 characters max (like the example in the ISO 15118-2 annex J)
*/
dhPublicKey.setId("id1"); // dhPublicKey
dhPublicKey.setValue(ecdhKeyPair.getPublic().getEncoded());
return dhPublicKey;
@ -1537,7 +1543,7 @@ public final class SecurityUtils {
SignatureType signature,
HashMap<String, byte[]> verifyXMLSigRefElements,
ECPublicKey ecPublicKey) {
byte[] providedDigest;
byte[] calculatedReferenceDigest;
boolean match;
/*
@ -1548,17 +1554,28 @@ public final class SecurityUtils {
for (String id : verifyXMLSigRefElements.keySet()) {
getLogger().debug("Verifying digest for element '" + id + "'");
match = false;
providedDigest = verifyXMLSigRefElements.get(id);
calculatedReferenceDigest = verifyXMLSigRefElements.get(id);
// A bit inefficient, but there are max. 4 elements to iterate over (what would be more efficient?)
for (ReferenceType reference : signature.getSignedInfo().getReference()) {
if (reference.getId().equals(id) && Arrays.equals(reference.getDigestValue(), providedDigest))
if (reference == null) {
getLogger().warn("Reference element to check is null");
continue;
}
// We need to check the URI attribute, the Id attribute is likely to be null
if (reference.getURI() == null) {
getLogger().warn("Reference ID element is null");
continue;
}
if (reference.getURI().equals('#' + id) && Arrays.equals(reference.getDigestValue(), calculatedReferenceDigest))
match = true;
}
if (!match) {
getLogger().error("No matching signature found for ID '" + id + "' and digest value " +
ByteUtils.toHexString(providedDigest));
ByteUtils.toHexString(calculatedReferenceDigest));
return false;
}
}