- Replaced System.currentTimeMillis() by System.nanoTime() for timing tasks.

- Changed the way how JAXBElements are created due to particularities with the creation of the XML Reference elements for the CertificateInstallationRes and CertificateUpdateRes messages. The JAXBContext is now set exactly for the message or field that is to be marshaled into XML. As a result, the JAXBContext is no more set in the EXI codec, but in the MessageHandler
- SecurityUtils.java has been partly rewritten with regards to the verification of certificate chains. See also verifyCertificateChain(…) method.
- Requirement [V2G2-812] is not implemented.
- When the EVCC sends a message out of the correct order which would induce a FAILED_SequenceError message, the SECC now sends the response message corresponding to the request message sent by the EVCC instead of the response message corresponding to the message the SECC would expect. As a result, ServerState.java has partly been rewritten and all SECC states have been adapted to it.
- Charge parameter provided by ChargeParameterDisoveryReq message are now thoroughly checked by the SECC. See also verifyChargeParameter(…) method.
- Charging profile provided by PowerDeliveryReq message is now thoroughly checked by SECC. See also isChargingProfileValid(…) method.
- New enum class PKI is added to shared.enumerations package. Needed for certificate chain checks in SecurityUtils.java.
- Some bugfixes in V2GTPMessage so that headers are not checked correctly.
This commit is contained in:
Marc Mültin 2017-08-20 23:26:22 +02:00
parent d829d9d47c
commit 788280cd68
50 changed files with 1394 additions and 634 deletions

8
.gitignore vendored
View File

@ -10,6 +10,14 @@
*.class
**/bin
*/bin
*/target
.classpath
.settings
.project
RISE-V2G-Certificates/certs
RISE-V2G-Certificates/csrs
RISE-V2G-Certificates/keystores
RISE-V2G-Certificates/privateKeys
RISE-V2G-Certificates/testing-symposia
/.metadata/
/.recommenders/

View File

@ -0,0 +1,11 @@
# This is a useful small shell script to automatically copy the Java Keystores (.jks files), .p12 containers and the DER encoded Mobility Operator Sub-CA private key to the places in the RISE V2G project where they belong. Execute this script after you executed the generateCertificates.sh script.
cp keystores/evccKeystore.jks ../RISE-V2G-EVCC
cp keystores/evccTruststore.jks ../RISE-V2G-EVCC
cp keystores/seccKeystore.jks ../RISE-V2G-SECC
cp keystores/seccTruststore.jks ../RISE-V2G-SECC
cp certs/cpsCertChain.p12 ../RISE-V2G-SECC
cp certs/moCertChain.p12 ../RISE-V2G-SECC
cp privateKeys/moSubCA2.pkcs8.der ../RISE-V2G-SECC

View File

@ -159,7 +159,7 @@ public class DummyEVController implements IACEVController, IDCEVController {
ProfileEntryType chargingProfileEntry = new ProfileEntryType();
PhysicalValueType maxPower = new PhysicalValueType();
maxPower.setMultiplier(new Byte("0"));
maxPower.setMultiplier(pMaxScheduleEntry.getPMax().getMultiplier());
maxPower.setUnit(UnitSymbolType.W);
maxPower.setValue(pMaxScheduleEntry.getPMax().getValue());

View File

@ -141,7 +141,7 @@ public class V2GCommunicationSessionEVCC extends V2GCommunicationSession impleme
* of a SessionSetupRes (see V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT in TimeRestrictions.java)
* TODO check if this timing requirement is still up to date
*/
setV2gEVCCCommunicationSetupTimer(System.currentTimeMillis());
setV2gEVCCCommunicationSetupTimer(System.nanoTime());
// Set default value for contract certificate status to UNKNOWN
setContractCertStatus(ContractCertificateStatus.UNKNOWN);
@ -378,7 +378,7 @@ public class V2GCommunicationSessionEVCC extends V2GCommunicationSession impleme
public void setSaSchedules(SAScheduleListType saSchedules) {
this.saSchedules = saSchedules;
this.saSchedulesReceived = System.currentTimeMillis();
this.saSchedulesReceived = System.nanoTime();
}

View File

@ -63,7 +63,7 @@ public class WaitForAuthorizationRes extends ClientState {
AuthorizationReqType authorizationReq = getAuthorizationReq(null);
getXMLSignatureRefElements().put(
authorizationReq.getId(),
SecurityUtils.generateDigest(authorizationReq));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(authorizationReq)));
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(

View File

@ -28,11 +28,13 @@ import java.util.HashMap;
import org.v2gclarity.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.PKI;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateInstallationResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignatureType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
@ -53,12 +55,12 @@ public class WaitForCertificateInstallationRes extends ClientState {
return new TerminateSession("Signature verification failed");
}
/**
* Check
* - validity of each certificate in the chain
* - that the signer certificate has a DC (Domain Component) field with the content "CPS" set
*/
if (!SecurityUtils.isCertificateChainValid(certificateInstallationRes.getSAProvisioningCertificateChain(), "CPS")) {
// Check complete CPS certificate chain
ResponseCodeType certChainResponseCode = SecurityUtils.verifyCertificateChain(
certificateInstallationRes.getSAProvisioningCertificateChain(),
GlobalValues.EVCC_TRUSTSTORE_FILEPATH.toString(),
PKI.CPS);
if (!certChainResponseCode.equals(ResponseCodeType.OK)) {
return new TerminateSession("Provisioning certificate chain is not valid");
}
@ -90,19 +92,20 @@ public class WaitForCertificateInstallationRes extends ClientState {
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(
certificateInstallationRes.getContractSignatureCertChain().getId(),
SecurityUtils.generateDigest(certificateInstallationRes.getContractSignatureCertChain()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureCertChain())));
verifyXMLSigRefElements.put(
certificateInstallationRes.getContractSignatureEncryptedPrivateKey().getId(),
SecurityUtils.generateDigest(certificateInstallationRes.getContractSignatureEncryptedPrivateKey()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureEncryptedPrivateKey())));
verifyXMLSigRefElements.put(
certificateInstallationRes.getDHpublickey().getId(),
SecurityUtils.generateDigest(certificateInstallationRes.getDHpublickey()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getDHpublickey())));
verifyXMLSigRefElements.put(
certificateInstallationRes.getEMAID().getId(),
SecurityUtils.generateDigest(certificateInstallationRes.getEMAID()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getEMAID())));
if (!SecurityUtils.verifySignature(
signature,
getMessageHandler().getJaxbElement(signature.getSignedInfo()),
verifyXMLSigRefElements,
certificateInstallationRes.getSAProvisioningCertificateChain().getCertificate())) {
return false;

View File

@ -28,11 +28,13 @@ import java.util.HashMap;
import org.v2gclarity.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.PKI;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateUpdateResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignatureType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
@ -53,12 +55,12 @@ public class WaitForCertificateUpdateRes extends ClientState {
return new TerminateSession("Signature verification failed");
}
/**
* Check
* - validity of each certificate in the chain
* - that the signer certificate has a DC (Domain Component) field with the content "CPS" set
*/
if (!SecurityUtils.isCertificateChainValid(certificateUpdateRes.getSAProvisioningCertificateChain(), "CPS")) {
// Check complete CPS certificate chain
ResponseCodeType certChainResponseCode = SecurityUtils.verifyCertificateChain(
certificateUpdateRes.getSAProvisioningCertificateChain(),
GlobalValues.EVCC_TRUSTSTORE_FILEPATH.toString(),
PKI.CPS);
if (!certChainResponseCode.equals(ResponseCodeType.OK)) {
return new TerminateSession("Provisioning certificate chain is not valid");
}
@ -90,19 +92,20 @@ public class WaitForCertificateUpdateRes extends ClientState {
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(
certificateUpdateRes.getContractSignatureCertChain().getId(),
SecurityUtils.generateDigest(certificateUpdateRes.getContractSignatureCertChain()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureCertChain())));
verifyXMLSigRefElements.put(
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().getId(),
SecurityUtils.generateDigest(certificateUpdateRes.getContractSignatureEncryptedPrivateKey()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureEncryptedPrivateKey())));
verifyXMLSigRefElements.put(
certificateUpdateRes.getDHpublickey().getId(),
SecurityUtils.generateDigest(certificateUpdateRes.getDHpublickey()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getDHpublickey())));
verifyXMLSigRefElements.put(
certificateUpdateRes.getEMAID().getId(),
SecurityUtils.generateDigest(certificateUpdateRes.getEMAID()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getEMAID())));
if (!SecurityUtils.verifySignature(
signature,
getMessageHandler().getJaxbElement(signature.getSignedInfo()),
verifyXMLSigRefElements,
certificateUpdateRes.getSAProvisioningCertificateChain().getCertificate())) {
return false;

View File

@ -177,7 +177,7 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
verifyXMLSigRefElements.put(
saScheduleTuple.getSalesTariff().getId(),
SecurityUtils.generateDigest(saScheduleTuple.getSalesTariff()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(saScheduleTuple.getSalesTariff())));
}
if (salesTariffCounter > 0) {
@ -187,7 +187,11 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
getLogger().error("No MOSubCA2 certificate found, signature of SalesTariff could therefore not be verified");
return false;
} else {
if (!SecurityUtils.verifySignature(signature, verifyXMLSigRefElements, moSubCA2Certificate)) {
if (!SecurityUtils.verifySignature(
signature,
getMessageHandler().getJaxbElement(signature.getSignedInfo()),
verifyXMLSigRefElements,
moSubCA2Certificate)) {
getLogger().warn("Verification of SalesTariff failed using certificate with distinguished name '" +
moSubCA2Certificate.getSubjectX500Principal().getName() + "'");
return false;

View File

@ -71,7 +71,7 @@ public class WaitForChargingStatusRes extends ClientState {
// Set xml reference element
getXMLSignatureRefElements().put(
meteringReceiptReq.getId(),
SecurityUtils.generateDigest(meteringReceiptReq));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(meteringReceiptReq)));
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(

View File

@ -64,7 +64,9 @@ public class WaitForCurrentDemandRes extends ClientState {
meteringReceiptReq.setSessionID(getCommSessionContext().getSessionID());
// Set xml reference element
getXMLSignatureRefElements().put(meteringReceiptReq.getId(), SecurityUtils.generateDigest(meteringReceiptReq));
getXMLSignatureRefElements().put(
meteringReceiptReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(meteringReceiptReq)));
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(

View File

@ -23,8 +23,6 @@
*******************************************************************************/
package org.v2gclarity.risev2g.evcc.states;
import java.util.Base64;
import org.v2gclarity.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
@ -61,7 +59,7 @@ public class WaitForPaymentDetailsRes extends ClientState {
// Set xml reference element
getXMLSignatureRefElements().put(
authorizationReq.getId(),
SecurityUtils.generateDigest(authorizationReq));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(authorizationReq)));
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(

View File

@ -102,7 +102,9 @@ public class WaitForPaymentServiceSelectionRes extends ClientState {
evccKeyStore, GlobalValues.ALIAS_OEM_PROV_CERTIFICATE.toString()).getCertificate());
// Set xml reference element
getXMLSignatureRefElements().put(certInstallationReq.getId(), SecurityUtils.generateDigest(certInstallationReq));
getXMLSignatureRefElements().put(
certInstallationReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certInstallationReq)));
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(
@ -140,7 +142,7 @@ public class WaitForPaymentServiceSelectionRes extends ClientState {
// Set xml reference element
getXMLSignatureRefElements().put(
certificateUpdateReq.getId(),
SecurityUtils.generateDigest(certificateUpdateReq));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateReq)));
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(

View File

@ -23,6 +23,8 @@
*******************************************************************************/
package org.v2gclarity.risev2g.evcc.states;
import java.util.concurrent.TimeUnit;
import org.v2gclarity.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
@ -89,9 +91,12 @@ public class WaitForSupportedAppProtocolRes extends ClientState {
supportedAppProtocolRes.getSchemaID());
}
long elapsedTime = System.nanoTime() - getCommSessionContext().getV2gEVCCCommunicationSetupTimer();
long elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
return getSendMessage(sessionSetupReq, V2GMessages.SESSION_SETUP_RES, (int) Math.min(
TimeRestrictions.getV2G_EVCC_Msg_Timeout(V2GMessages.SESSION_SETUP_RES),
TimeRestrictions.V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT - (System.currentTimeMillis() - getCommSessionContext().getV2gEVCCCommunicationSetupTimer())
TimeRestrictions.V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT - elapsedTimeInMs
));
} else {
return new TerminateSession("Invalid message (" + message.getClass().getSimpleName() +

View File

@ -131,7 +131,7 @@ public class TLSClient extends StatefulTransportLayerClient {
X509Certificate seccLeafCertificate = (X509Certificate) seccCertificates[0];
// Check domain component of SECC certificate
if (!SecurityUtils.isCertificateValid(seccLeafCertificate, "CPO")) {
if (!SecurityUtils.verifyDomainComponent(seccLeafCertificate, "CPO")) {
getLogger().error("TLS client connection failed. \n\t" +
"Reason: Domain component of SECC certificate not valid, expected 'DC=CPO'. \n\t" +
"Distinuished name of SECC certificate: " + seccLeafCertificate.getSubjectX500Principal().getName());
@ -162,7 +162,7 @@ public class TLSClient extends StatefulTransportLayerClient {
@Override
public void run() {
while (!Thread.interrupted()) {
if (getTimeout() > 0) {
if (getTimeout() >= 0) {
try {
getTlsSocketToServer().setSoTimeout(getTimeout());
@ -175,6 +175,9 @@ public class TLSClient extends StatefulTransportLayerClient {
stopAndNotify("An IOException occurred while trying to read message", e2);
break;
}
} else {
stopAndNotify("Timeout value is negative: " + getTimeout(), null);
break;
}
}
}

View File

@ -46,11 +46,6 @@ import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SalesTariffEntryType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SalesTariffType;
import org.v2gclarity.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;
@ -140,7 +135,7 @@ public class DummyBackendInterface implements IBackendInterface {
if (saScheduleTuple.getSalesTariff() != null) {
xmlSignatureRefElements.put(
salesTariff.getId(),
SecurityUtils.generateDigest(salesTariff));
SecurityUtils.generateDigest(getCommSessionContext().getMessageHandler().getJaxbElement(salesTariff)));
}
return saScheduleList;

View File

@ -172,7 +172,7 @@ public class V2GCommunicationSessionHandlerSECC implements Observer {
// The SECCDiscoveryRes must be sent via UDP before the requested TCP/TLS server can be used
UDPServer.getInstance().send(getV2gTpMessage(), (Inet6Address) udpClientPacket.getAddress(), udpClientPacket.getPort());
} else {
getLogger().warn("Incoming DatagramPacket could not be identified as a SECCDiscoveryReq");
getLogger().warn("Incoming DatagramPacket could not be identified as an SECCDiscoveryReq");
}
} catch (NullPointerException e) {
getLogger().error("NullPointerException occurred while processing SECCDiscoveryReq", e);

View File

@ -101,6 +101,7 @@ public class V2GCommunicationSessionSECC extends V2GCommunicationSession impleme
private PaymentOptionType selectedPaymentOption;
private CertificateChainType contractSignatureCertChain;
private MeterInfoType sentMeterInfo;
private boolean chargeProgressStarted; // for checking [V2G2-812]
public V2GCommunicationSessionSECC(ConnectionHandler connectionHandler) {
setConnectionHandler(connectionHandler);
@ -192,7 +193,7 @@ public class V2GCommunicationSessionSECC extends V2GCommunicationSession impleme
if (reactionToIncomingMessage instanceof SendMessage) {
send((SendMessage) reactionToIncomingMessage);
if (isStopV2GCommunicationSession()) {
terminateSession("EVCC indicated request to stop the session", true);
terminateSession("EVCC indicated request to stop the session or a FAILED response code was sent", true);
}
} else if (reactionToIncomingMessage instanceof ChangeProcessingState) {
setCurrentState(((ChangeProcessingState) reactionToIncomingMessage).getNewState());
@ -472,4 +473,14 @@ public class V2GCommunicationSessionSECC extends V2GCommunicationSession impleme
public void setSelectedPaymentOption(PaymentOptionType selectedPaymentOption) {
this.selectedPaymentOption = selectedPaymentOption;
}
public boolean isChargeProgressStarted() {
return chargeProgressStarted;
}
public void setChargeProgressStarted(boolean chargeProgressStarted) {
this.chargeProgressStarted = chargeProgressStarted;
}
}

View File

@ -32,6 +32,8 @@ import org.v2gclarity.risev2g.shared.messageHandling.ChangeProcessingState;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import org.v2gclarity.risev2g.shared.misc.State;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
public class ForkState extends ServerState {
@ -61,21 +63,29 @@ public class ForkState extends ServerState {
return new TerminateSession("No valid V2GMessage received");
}
State newState = getCommSessionContext().getStates().get(incomingMessage);
if (newState == null) {
getLogger().error("Error occurred while switching from ForkState to a new state: new state is null");
return new TerminateSession("Invalid message (" + v2gMessageReq.getBody().getBodyElement().getValue().getClass().getSimpleName() +
") at this state (" + this.getClass().getSimpleName() + "). " +
"Allowed messages are: " + this.getAllowedRequests().toString());
}
if (allowedRequests.contains(incomingMessage)) {
State newState = getCommSessionContext().getStates().get(incomingMessage);
if (newState == null) {
getLogger().error("Error occurred while switching from ForkState to a new state: new state is null");
}
// delete all allowedRequests so that they won't be valid anymore
allowedRequests.clear();
return new ChangeProcessingState(message, newState);
} else {
return new TerminateSession("Invalid message (" + v2gMessageReq.getBody().getBodyElement().getValue().getClass().getSimpleName() +
") at this state (" + this.getClass().getSimpleName() + "). " +
"Allowed messages are: " + this.getAllowedRequests().toString());
getLogger().error("Invalid message (" + v2gMessageReq.getBody().getBodyElement().getValue().getClass().getSimpleName() +
") at this state (" + this.getClass().getSimpleName() + "). " +
"Allowed messages are: " + this.getAllowedRequests().toString());
BodyBaseType responseMessage = getSequenceErrorResMessage(v2gMessageReq);
ServerState newServerState = (ServerState) newState;
return newServerState.getSendMessage(responseMessage, V2GMessages.NONE, ResponseCodeType.FAILED_SEQUENCE_ERROR);
}
}
@ -97,9 +107,10 @@ public class ForkState extends ServerState {
return allowedRequests;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
// Nothing to do here
public BodyBaseType getResponseMessage() {
return null;
}
}

View File

@ -23,6 +23,8 @@
*******************************************************************************/
package org.v2gclarity.risev2g.secc.states;
import org.v2gclarity.risev2g.secc.evseController.IACEVSEController;
import org.v2gclarity.risev2g.secc.evseController.IDCEVSEController;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.SendMessage;
@ -32,14 +34,21 @@ import org.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtoco
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.AuthorizationResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CableCheckResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateInstallationResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateUpdateResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeParameterDiscoveryResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargingStatusResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ContractSignatureEncryptedPrivateKeyType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CurrentDemandResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DiffieHellmanPublickeyType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EMAIDType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSEProcessingType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.MeteringReceiptResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentDetailsResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentServiceSelectionResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PhysicalValueType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PowerDeliveryResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PreChargeResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
@ -47,13 +56,12 @@ import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ServiceDetailResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ServiceDiscoveryResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SessionSetupResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SessionStopResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.UnitSymbolType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.WeldingDetectionResType;
public abstract class ServerState extends State {
private ResponseCodeType responseCode;
public ServerState(V2GCommunicationSessionSECC commSessionContext) {
super(commSessionContext);
}
@ -148,52 +156,337 @@ public abstract class ServerState extends State {
}
if (responseCode.toString().startsWith("OK")) return true;
else {
getLogger().error("Response code '" + responseCode.toString() + "' will be sent");
return false;
}
else return false;
}
public SendMessage getSendMessage(
BodyBaseType message,
V2GMessages nextExpectedMessage,
ResponseCodeType responseCode) {
int timeout = getTimeout(message, nextExpectedMessage);
if (!responseCode.value().startsWith("OK")) {
getLogger().error("Response code '" + responseCode.value() + "' will be sent.");
getCommSessionContext().setStopV2GCommunicationSession(true);
}
return getSendMessage(message, nextExpectedMessage, "", timeout);
}
protected SendMessage getSendMessage(
SupportedAppProtocolRes message,
V2GMessages nextExpectedMessage) {
V2GMessages nextExpectedMessage,
org.v2gclarity.risev2g.shared.v2gMessages.appProtocol.ResponseCodeType responseCode) {
String messageName = message.getClass().getSimpleName();
if (!responseCode.value().substring(0, 2).toUpperCase().equals("OK")) {
getLogger().error("Response code '" + responseCode.value() + "' will be sent.");
getCommSessionContext().setStopV2GCommunicationSession(true);
}
getLogger().debug("Preparing to send " + messageName);
return new SendMessage(message, getCommSessionContext().getStates().get(nextExpectedMessage), TimeRestrictions.V2G_SECC_SEQUENCE_TIMEOUT);
}
public ResponseCodeType getResponseCode() {
return responseCode;
}
/**
* Provides additional information about the kind of response code
* @param responseCode The response code to be sent
* @return True if the response code is positive (i.e. containing or starting with "OK"), false otherwise
*/
public boolean setResponseCode(ResponseCodeType responseCode) {
// Only log a negative response code
if (!responseCode.value().substring(0, 2).toUpperCase().equals("OK")) {
getLogger().error("Response code '" + responseCode.value() + "' will be sent.");
getCommSessionContext().setStopV2GCommunicationSession(true);
return false;
}
this.responseCode = responseCode;
return true;
}
/**
* In case a FAILED response code is sent, the mandatory fields still need to be set with minimum required values,
* otherwise the EVCC's EXI decoder will raise an error.
*
* @param response The respective response message whose mandatory fields are to be set
*/
protected abstract void setMandatoryFieldsForFailedRes();
protected void setMandatoryFieldsForFailedRes(BodyBaseType responseMessage, ResponseCodeType responseCode) {
switch (responseMessage.getClass().getSimpleName()) {
case "SessionSetupResType":
SessionSetupResType sessionSetupRes = (SessionSetupResType) responseMessage;
sessionSetupRes.setEVSEID(getCommSessionContext().getEvseController().getEvseID());
sessionSetupRes.setResponseCode(responseCode);
break;
case "ServiceDiscoveryResType":
ServiceDiscoveryResType serviceDiscoveryRes = (ServiceDiscoveryResType) responseMessage;
serviceDiscoveryRes.setChargeService((new WaitForServiceDiscoveryReq(getCommSessionContext())).getChargeService());
serviceDiscoveryRes.setPaymentOptionList(getCommSessionContext().getPaymentOptions());
serviceDiscoveryRes.setResponseCode(responseCode);
break;
case "ServiceDetailResType":
ServiceDetailResType serviceDetailRes = (ServiceDetailResType) responseMessage;
serviceDetailRes.setServiceID(1);
serviceDetailRes.setResponseCode(responseCode);
break;
case "PaymentServiceSelectionResType":
PaymentServiceSelectionResType paymentServiceSelectionRes = (PaymentServiceSelectionResType) responseMessage;
paymentServiceSelectionRes.setResponseCode(responseCode);
break;
case "PaymentDetailsResType":
PaymentDetailsResType paymentDetailsRes = (PaymentDetailsResType) responseMessage;
paymentDetailsRes.setEVSETimeStamp(0L);
paymentDetailsRes.setGenChallenge(new byte[1]);
paymentDetailsRes.setResponseCode(responseCode);
break;
case "CertificateInstallationResType":
CertificateInstallationResType certificateInstallationRes = (CertificateInstallationResType) responseMessage;
CertificateChainType saProvisioningCertificateChain = new CertificateChainType();
saProvisioningCertificateChain.setCertificate(new byte[1]);
certificateInstallationRes.setSAProvisioningCertificateChain(saProvisioningCertificateChain);
CertificateChainType contractSignatureCertChain = new CertificateChainType();
contractSignatureCertChain.setCertificate(new byte[1]);
contractSignatureCertChain.setId("ID1");
certificateInstallationRes.setContractSignatureCertChain(contractSignatureCertChain);
ContractSignatureEncryptedPrivateKeyType contractSignatureEncryptedPrivateKey = new ContractSignatureEncryptedPrivateKeyType();
contractSignatureEncryptedPrivateKey.setValue(new byte[1]);
contractSignatureEncryptedPrivateKey.setId("ID2");
certificateInstallationRes.setContractSignatureEncryptedPrivateKey(contractSignatureEncryptedPrivateKey);
DiffieHellmanPublickeyType dhPublicKeyType = new DiffieHellmanPublickeyType();
dhPublicKeyType.setValue(new byte[1]);
dhPublicKeyType.setId("ID3");
certificateInstallationRes.setDHpublickey(dhPublicKeyType);
EMAIDType emaid = new EMAIDType();
emaid.setValue("DEV2G1234512345");
emaid.setId("ID4");
certificateInstallationRes.setEMAID(emaid);
certificateInstallationRes.setResponseCode(responseCode);
break;
case "CertificateUpdateResType":
CertificateUpdateResType certificateUpdateRes = (CertificateUpdateResType) responseMessage;
CertificateChainType saProvisioningCertificateChain2 = new CertificateChainType();
saProvisioningCertificateChain2.setCertificate(new byte[1]);
certificateUpdateRes.setSAProvisioningCertificateChain(saProvisioningCertificateChain2);
CertificateChainType contractSignatureCertChain2 = new CertificateChainType();
contractSignatureCertChain2.setCertificate(new byte[1]);
contractSignatureCertChain2.setId("ID1");
certificateUpdateRes.setContractSignatureCertChain(contractSignatureCertChain2);
ContractSignatureEncryptedPrivateKeyType contractSignatureEncryptedPrivateKey2 = new ContractSignatureEncryptedPrivateKeyType();
contractSignatureEncryptedPrivateKey2.setValue(new byte[1]);
contractSignatureEncryptedPrivateKey2.setId("ID2");
certificateUpdateRes.setContractSignatureEncryptedPrivateKey(contractSignatureEncryptedPrivateKey2);
DiffieHellmanPublickeyType dhPublicKeyType2 = new DiffieHellmanPublickeyType();
dhPublicKeyType2.setValue(new byte[1]);
dhPublicKeyType2.setId("ID3");
certificateUpdateRes.setDHpublickey(dhPublicKeyType2);
EMAIDType emaid2 = new EMAIDType();
emaid2.setValue("DEV2G1234512345");
emaid2.setId("ID4");
certificateUpdateRes.setEMAID(emaid2);
certificateUpdateRes.setRetryCounter((short) 0); // according to [V2G2-696] and [V2G2-928]
certificateUpdateRes.setResponseCode(responseCode);
break;
case "AuthorizationResType":
AuthorizationResType authorizationRes = (AuthorizationResType) responseMessage;
authorizationRes.setEVSEProcessing(EVSEProcessingType.FINISHED);
authorizationRes.setResponseCode(responseCode);
break;
case "ChargeParameterDiscoveryResType":
ChargeParameterDiscoveryResType chargeParameterDiscoveryRes = (ChargeParameterDiscoveryResType) responseMessage;
chargeParameterDiscoveryRes.setEVSEProcessing(EVSEProcessingType.FINISHED);
chargeParameterDiscoveryRes.setEVSEChargeParameter(
((IACEVSEController) getCommSessionContext().getACEvseController()).getACEVSEChargeParameter());
chargeParameterDiscoveryRes.setResponseCode(responseCode);
break;
case "CableCheckResType":
CableCheckResType cableCheckRes = (CableCheckResType) responseMessage;
cableCheckRes.setEVSEProcessing(EVSEProcessingType.FINISHED);
cableCheckRes.setDCEVSEStatus(
((IDCEVSEController) getCommSessionContext().getDCEvseController()).getDCEVSEStatus(EVSENotificationType.NONE)
);
cableCheckRes.setResponseCode(responseCode);
break;
case "PreChargeResType":
PreChargeResType preChargeRes = (PreChargeResType) responseMessage;
IDCEVSEController evseController = (IDCEVSEController) getCommSessionContext().getDCEvseController();
preChargeRes.setDCEVSEStatus(evseController.getDCEVSEStatus(EVSENotificationType.NONE));
preChargeRes.setEVSEPresentVoltage(evseController.getPresentVoltage());
preChargeRes.setResponseCode(responseCode);
break;
case "PowerDeliveryResType":
PowerDeliveryResType powerDeliveryRes = (PowerDeliveryResType) responseMessage;
(new WaitForPowerDeliveryReq(getCommSessionContext())).setEVSEStatus(powerDeliveryRes);
powerDeliveryRes.setResponseCode(responseCode);
break;
case "ChargingStatusResType":
ChargingStatusResType chargingStatusRes = (ChargingStatusResType) responseMessage;
chargingStatusRes.setEVSEID(getCommSessionContext().getACEvseController().getEvseID());
chargingStatusRes.setSAScheduleTupleID((short) 1);
chargingStatusRes.setACEVSEStatus(((IACEVSEController) getCommSessionContext().getACEvseController())
.getACEVSEStatus(EVSENotificationType.NONE)
);
chargingStatusRes.setResponseCode(responseCode);
break;
case "CurrentDemandResType":
CurrentDemandResType currentDemandRes = (CurrentDemandResType) responseMessage;
IDCEVSEController evseController2 = (IDCEVSEController) getCommSessionContext().getDCEvseController();
PhysicalValueType physicalValueType = new PhysicalValueType();
physicalValueType.setMultiplier(new Byte("0"));
physicalValueType.setUnit(UnitSymbolType.V); // does not matter which unit symbol if FAILED response is sent
physicalValueType.setValue((short) 1);
currentDemandRes.setDCEVSEStatus(evseController2.getDCEVSEStatus(EVSENotificationType.NONE));
currentDemandRes.setEVSEPresentVoltage(physicalValueType);
currentDemandRes.setEVSEPresentCurrent(physicalValueType);
currentDemandRes.setEVSECurrentLimitAchieved(false);
currentDemandRes.setEVSEVoltageLimitAchieved(false);
currentDemandRes.setEVSEPowerLimitAchieved(false);
currentDemandRes.setEVSEID(evseController2.getEvseID());
currentDemandRes.setSAScheduleTupleID((short) 1);
currentDemandRes.setResponseCode(responseCode);
break;
case "MeteringReceiptResType":
MeteringReceiptResType meteringReceiptRes = (MeteringReceiptResType) responseMessage;
(new WaitForMeteringReceiptReq(getCommSessionContext())).setEVSEStatus(meteringReceiptRes);
meteringReceiptRes.setResponseCode(responseCode);
break;
case "WeldingDetectionResType":
WeldingDetectionResType weldingDetectionRes = (WeldingDetectionResType) responseMessage;
IDCEVSEController evseController3 = (IDCEVSEController) getCommSessionContext().getDCEvseController();
weldingDetectionRes.setDCEVSEStatus(evseController3.getDCEVSEStatus(EVSENotificationType.NONE));
weldingDetectionRes.setEVSEPresentVoltage(evseController3.getPresentVoltage());
weldingDetectionRes.setResponseCode(responseCode);
break;
case "SessionStopResType":
SessionStopResType sessionStopRes = (SessionStopResType) responseMessage;
sessionStopRes.setResponseCode(responseCode);
break;
default:
getLogger().error("Response message could not be identified");
}
}
protected BodyBaseType getSequenceErrorResMessage(Object incomingMessage) {
if (incomingMessage instanceof V2GMessage) {
V2GMessage v2gMessage = (V2GMessage) incomingMessage;
String className = v2gMessage.getBody().getBodyElement().getValue().getClass().getSimpleName();
BodyBaseType responseMessage = null;
switch (className) {
case "SessionSetupReqType":
SessionSetupResType sessionSetupRes = new SessionSetupResType();
sessionSetupRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = sessionSetupRes;
break;
case "ServiceDiscoveryReqType":
ServiceDiscoveryResType serviceDiscoveryRes = new ServiceDiscoveryResType();
serviceDiscoveryRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = serviceDiscoveryRes;
break;
case "ServiceDetailReqType":
ServiceDetailResType serviceDetailRes = new ServiceDetailResType();
serviceDetailRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = serviceDetailRes;
break;
case "PaymentServiceSelectionReqType":
PaymentServiceSelectionResType paymentServiceSelectionRes = new PaymentServiceSelectionResType();
paymentServiceSelectionRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = paymentServiceSelectionRes;
break;
case "PaymentDetailsReqType":
PaymentDetailsResType paymentDetailsRes = new PaymentDetailsResType();
paymentDetailsRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = paymentDetailsRes;
break;
case "CertificateInstallationReqType":
CertificateInstallationResType certificateInstallationRes = new CertificateInstallationResType();
certificateInstallationRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = certificateInstallationRes;
break;
case "CertificateUpdateReqType":
CertificateUpdateResType certificateUpdateRes = new CertificateUpdateResType();
certificateUpdateRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = certificateUpdateRes;
break;
case "AuthorizationReqType":
AuthorizationResType authorizationRes = new AuthorizationResType();
authorizationRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = authorizationRes;
break;
case "ChargeParameterDiscoveryReqType":
ChargeParameterDiscoveryResType chargeParameterDiscoveryRes = new ChargeParameterDiscoveryResType();
chargeParameterDiscoveryRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = chargeParameterDiscoveryRes;
break;
case "CableCheckReqType":
CableCheckResType cableCheckRes = new CableCheckResType();
cableCheckRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = cableCheckRes;
break;
case "PreChargeReqType":
PreChargeResType preChargeRes = new PreChargeResType();
preChargeRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = preChargeRes;
break;
case "PowerDeliveryReqType":
PowerDeliveryResType powerDeliveryResType = new PowerDeliveryResType();
powerDeliveryResType.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = powerDeliveryResType;
break;
case "ChargingStatusReqType":
ChargingStatusResType chargingStatusRes = new ChargingStatusResType();
chargingStatusRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = chargingStatusRes;
break;
case "CurrentDemandReqType":
CurrentDemandResType currentDemandRes = new CurrentDemandResType();
currentDemandRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = currentDemandRes;
break;
case "MeteringReceiptReqType":
MeteringReceiptResType meteringReceiptRes = new MeteringReceiptResType();
meteringReceiptRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = meteringReceiptRes;
break;
case "WeldingDetectionReqType":
WeldingDetectionResType weldingDetectionRes = new WeldingDetectionResType();
weldingDetectionRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = weldingDetectionRes;
break;
case "SessionStopReqType":
SessionStopResType sessionStopRes = new SessionStopResType();
sessionStopRes.setResponseCode(ResponseCodeType.FAILED_SEQUENCE_ERROR);
responseMessage = sessionStopRes;
break;
default:
getLogger().error("Response message could not be identified");
}
setMandatoryFieldsForFailedRes(responseMessage, ResponseCodeType.FAILED_SEQUENCE_ERROR);
return responseMessage;
} else {
return null;
}
}
protected BodyBaseType getSequenceErrorResMessage(
BodyBaseType currentStateRes,
Object incomingMessage) {
BodyBaseType responseMessage = getSequenceErrorResMessage(incomingMessage);
// Check in case the switch statement did not match a proper ISO 15118 request message
if (responseMessage != null) {
return responseMessage;
} else {
setMandatoryFieldsForFailedRes(currentStateRes, ResponseCodeType.FAILED_SEQUENCE_ERROR);
return currentStateRes;
}
}
/**
* Needed for the ForkState to get the respective response message which can be used to instantiate a
* SendMessage() object in case of a sequence error
*/
public abstract BodyBaseType getResponseMessage();
}

View File

@ -32,6 +32,7 @@ import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.AuthorizationReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.AuthorizationResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSEProcessingType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentOptionType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
@ -70,14 +71,19 @@ public class WaitForAuthorizationReq extends ServerState {
return getSendMessage(authorizationRes, V2GMessages.AUTHORIZATION_REQ);
}
} else {
getLogger().error("Response code '" + authorizationRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(authorizationRes, authorizationRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (authorizationRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new AuthorizationReqType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, authorizationRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(authorizationRes, authorizationRes.getResponseCode());
}
}
return getSendMessage(authorizationRes, V2GMessages.NONE);
return getSendMessage(authorizationRes, V2GMessages.NONE, authorizationRes.getResponseCode());
}
@ -104,10 +110,13 @@ public class WaitForAuthorizationReq extends ServerState {
getCommSessionContext().getSelectedPaymentOption().equals(PaymentOptionType.CONTRACT)) {
// Verify signature
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(authorizationReq.getId(), SecurityUtils.generateDigest(authorizationReq));
verifyXMLSigRefElements.put(
authorizationReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(authorizationReq)));
if (!SecurityUtils.verifySignature(
signature,
getMessageHandler().getJaxbElement(signature.getSignedInfo()),
verifyXMLSigRefElements,
getCommSessionContext().getContractSignatureCertChain().getCertificate())) {
authorizationRes.setResponseCode(ResponseCodeType.FAILED_SIGNATURE_ERROR);
@ -128,8 +137,8 @@ public class WaitForAuthorizationReq extends ServerState {
@Override
protected void setMandatoryFieldsForFailedRes() {
authorizationRes.setEVSEProcessing(EVSEProcessingType.FINISHED);
public BodyBaseType getResponseMessage() {
return authorizationRes;
}
}

View File

@ -27,10 +27,12 @@ import org.v2gclarity.risev2g.secc.evseController.IDCEVSEController;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CableCheckReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CableCheckResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSEProcessingType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
public class WaitForCableCheckReq extends ServerState {
@ -71,10 +73,16 @@ public class WaitForCableCheckReq extends ServerState {
return getSendMessage(cableCheckRes, V2GMessages.CABLE_CHECK_REQ);
}
} else {
setMandatoryFieldsForFailedRes();
if (cableCheckRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new CableCheckResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, cableCheckRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(cableCheckRes, cableCheckRes.getResponseCode());
}
}
return getSendMessage(cableCheckRes, V2GMessages.NONE);
return getSendMessage(cableCheckRes, V2GMessages.NONE, cableCheckRes.getResponseCode());
}
@ -86,13 +94,9 @@ public class WaitForCableCheckReq extends ServerState {
this.evseProcessingFinished = evseProcessingFinished;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
cableCheckRes.setEVSEProcessing(EVSEProcessingType.FINISHED);
cableCheckRes.setDCEVSEStatus(
((IDCEVSEController) getCommSessionContext().getDCEvseController()).getDCEVSEStatus(EVSENotificationType.NONE)
);
public BodyBaseType getResponseMessage() {
return cableCheckRes;
}
}

View File

@ -31,12 +31,11 @@ import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateInstallationReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateInstallationResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ContractSignatureEncryptedPrivateKeyType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DiffieHellmanPublickeyType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EMAIDType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignatureType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
@ -95,29 +94,34 @@ public class WaitForCertificateInstallationReq extends ServerState {
// Set xml reference elements
getXMLSignatureRefElements().put(
certificateInstallationRes.getContractSignatureCertChain().getId(),
SecurityUtils.generateDigest(certificateInstallationRes.getContractSignatureCertChain()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureCertChain())));
getXMLSignatureRefElements().put(
certificateInstallationRes.getContractSignatureEncryptedPrivateKey().getId(),
SecurityUtils.generateDigest(certificateInstallationRes.getContractSignatureEncryptedPrivateKey()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureEncryptedPrivateKey())));
getXMLSignatureRefElements().put(
certificateInstallationRes.getDHpublickey().getId(),
SecurityUtils.generateDigest(certificateInstallationRes.getDHpublickey()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getDHpublickey())));
getXMLSignatureRefElements().put(
certificateInstallationRes.getEMAID().getId(),
SecurityUtils.generateDigest(certificateInstallationRes.getEMAID()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getEMAID())));
// Set signing private key
setSignaturePrivateKey(getCommSessionContext().getBackendInterface().getCPSLeafPrivateKey());
} else {
getLogger().error("Response code '" + certificateInstallationRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(certificateInstallationRes, certificateInstallationRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (certificateInstallationRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new CertificateInstallationResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, certificateInstallationRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(certificateInstallationRes, certificateInstallationRes.getResponseCode());
}
}
return getSendMessage(certificateInstallationRes, V2GMessages.PAYMENT_DETAILS_REQ);
return getSendMessage(certificateInstallationRes, V2GMessages.PAYMENT_DETAILS_REQ, certificateInstallationRes.getResponseCode());
}
private boolean isResponseCodeOK(
@ -130,13 +134,14 @@ public class WaitForCertificateInstallationReq extends ServerState {
return false;
}
/*
* Check for FAILED_CertificateExpired
* There is no negative response code for a certificate which is neither yet valid nor expired.
* It is thus implicitly expected that a secondary actor would only send already valid certificates.
*/
if (!SecurityUtils.isCertificateChainValid(saContractCertificateChain)) {
certificateInstallationRes.setResponseCode(ResponseCodeType.FAILED_CERTIFICATE_EXPIRED);
// Check for FAILED_CertificateExpired
ResponseCodeType validityResponseCode = SecurityUtils.verifyValidityPeriod(
SecurityUtils.getCertificate(
certificateInstallationReq.getOEMProvisioningCert()
)
);
if (!validityResponseCode.equals(ResponseCodeType.OK)) {
certificateInstallationRes.setResponseCode(validityResponseCode);
return false;
}
@ -145,10 +150,13 @@ public class WaitForCertificateInstallationReq extends ServerState {
// Verify signature
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(certificateInstallationReq.getId(), SecurityUtils.generateDigest(certificateInstallationReq));
verifyXMLSigRefElements.put(
certificateInstallationReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationReq)));
if (!SecurityUtils.verifySignature(
signature,
getMessageHandler().getJaxbElement(signature.getSignedInfo()),
verifyXMLSigRefElements,
certificateInstallationReq.getOEMProvisioningCert())) {
certificateInstallationRes.setResponseCode(ResponseCodeType.FAILED_SIGNATURE_ERROR);
@ -157,32 +165,9 @@ public class WaitForCertificateInstallationReq extends ServerState {
return true;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
CertificateChainType saProvisioningCertificateChain = new CertificateChainType();
saProvisioningCertificateChain.setCertificate(new byte[1]);
certificateInstallationRes.setSAProvisioningCertificateChain(saProvisioningCertificateChain);
CertificateChainType contractSignatureCertChain = new CertificateChainType();
contractSignatureCertChain.setCertificate(new byte[1]);
contractSignatureCertChain.setId("ID1");
certificateInstallationRes.setContractSignatureCertChain(contractSignatureCertChain);
ContractSignatureEncryptedPrivateKeyType contractSignatureEncryptedPrivateKey = new ContractSignatureEncryptedPrivateKeyType();
contractSignatureEncryptedPrivateKey.setValue(new byte[1]);
contractSignatureEncryptedPrivateKey.setId("ID2");
certificateInstallationRes.setContractSignatureEncryptedPrivateKey(contractSignatureEncryptedPrivateKey);
DiffieHellmanPublickeyType dhPublicKeyType = new DiffieHellmanPublickeyType();
dhPublicKeyType.setValue(new byte[1]);
dhPublicKeyType.setId("ID3");
certificateInstallationRes.setDHpublickey(dhPublicKeyType);
EMAIDType emaid = new EMAIDType();
emaid.setValue("DEV2G1234512345");
emaid.setId("ID4");
certificateInstallationRes.setEMAID(emaid);
public BodyBaseType getResponseMessage() {
return certificateInstallationRes;
}
}

View File

@ -26,18 +26,17 @@ package org.v2gclarity.risev2g.secc.states;
import java.security.KeyPair;
import java.security.interfaces.ECPublicKey;
import java.util.HashMap;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.PKI;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateUpdateReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateUpdateResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ContractSignatureEncryptedPrivateKeyType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DiffieHellmanPublickeyType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EMAIDType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignatureType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
@ -89,7 +88,7 @@ public class WaitForCertificateUpdateReq extends ServerState {
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().setId("id2"); // contractSignatureEncryptedPrivateKey
certificateUpdateRes.setDHpublickey(SecurityUtils.getDHPublicKey(ecdhKeyPair));
certificateUpdateRes.getDHpublickey().setId("id3"); // dhPublicKey
certificateUpdateRes.setEMAID(SecurityUtils.getEMAID(contractCertificateChain));
certificateUpdateRes.setEMAID(SecurityUtils.getEMAID(certificateUpdateReq.getContractSignatureCertChain()));
certificateUpdateRes.getEMAID().setId("id4"); // emaid
certificateUpdateRes.setSAProvisioningCertificateChain(getCommSessionContext().getBackendInterface().getCPSCertificateChain());
@ -100,30 +99,36 @@ public class WaitForCertificateUpdateReq extends ServerState {
// Set xml reference elements
getXMLSignatureRefElements().put(
certificateUpdateRes.getContractSignatureCertChain().getId(),
SecurityUtils.generateDigest(certificateUpdateRes.getContractSignatureCertChain()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureCertChain())));
getXMLSignatureRefElements().put(
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().getId(),
SecurityUtils.generateDigest(certificateUpdateRes.getContractSignatureEncryptedPrivateKey()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureEncryptedPrivateKey())));
getXMLSignatureRefElements().put(
certificateUpdateRes.getDHpublickey().getId(),
SecurityUtils.generateDigest(certificateUpdateRes.getDHpublickey()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getDHpublickey())));
getXMLSignatureRefElements().put(
certificateUpdateRes.getEMAID().getId(),
SecurityUtils.generateDigest(certificateUpdateRes.getEMAID()));
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getEMAID())));
// Set signing private key
setSignaturePrivateKey(getCommSessionContext().getBackendInterface().getCPSLeafPrivateKey());
} else {
getLogger().error("Response code '" + certificateUpdateRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(certificateUpdateRes, certificateUpdateRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (certificateUpdateRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new CertificateUpdateResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, certificateUpdateRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(certificateUpdateRes, certificateUpdateRes.getResponseCode());
}
}
return getSendMessage(certificateUpdateRes,
(certificateUpdateRes.getResponseCode().toString().startsWith("OK") ?
V2GMessages.PAYMENT_DETAILS_REQ : V2GMessages.NONE)
V2GMessages.PAYMENT_DETAILS_REQ : V2GMessages.NONE),
certificateUpdateRes.getResponseCode()
);
}
@ -138,21 +143,13 @@ public class WaitForCertificateUpdateReq extends ServerState {
return false;
}
/*
* Check for FAILED_CertificateExpired
* There is no negative response code for a certificate which is neither yet valid nor expired.
* It is thus implicitly expected that a secondary actor would only send already valid certificates.
*/
if (!SecurityUtils.isCertificateChainValid(certificateUpdateReq.getContractSignatureCertChain())) {
certificateUpdateRes.setResponseCode(ResponseCodeType.FAILED_CERTIFICATE_EXPIRED);
return false;
}
// Check for FAILED_CertChainError
if (!SecurityUtils.isCertificateChainVerified(
GlobalValues.SECC_TRUSTSTORE_FILEPATH.toString(),
certificateUpdateReq.getContractSignatureCertChain())) {
certificateUpdateRes.setResponseCode(ResponseCodeType.FAILED_CERT_CHAIN_ERROR);
// Check complete contract certificate chain
ResponseCodeType certChainResponseCode = SecurityUtils.verifyCertificateChain(
certificateUpdateReq.getContractSignatureCertChain(),
GlobalValues.SECC_TRUSTSTORE_FILEPATH.toString(),
PKI.MO);
if (!certChainResponseCode.equals(ResponseCodeType.OK)) {
certificateUpdateRes.setResponseCode(certChainResponseCode);
return false;
}
@ -164,10 +161,13 @@ public class WaitForCertificateUpdateReq extends ServerState {
// Verify signature
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(certificateUpdateReq.getId(), SecurityUtils.generateDigest(certificateUpdateReq));
verifyXMLSigRefElements.put(
certificateUpdateReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateReq)));
if (!SecurityUtils.verifySignature(
signature,
getMessageHandler().getJaxbElement(signature.getSignedInfo()),
verifyXMLSigRefElements,
certificateUpdateReq.getContractSignatureCertChain().getCertificate())) {
certificateUpdateRes.setResponseCode(ResponseCodeType.FAILED_SIGNATURE_ERROR);
@ -177,31 +177,8 @@ public class WaitForCertificateUpdateReq extends ServerState {
return true;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
CertificateChainType saProvisioningCertificateChain = new CertificateChainType();
saProvisioningCertificateChain.setCertificate(new byte[1]);
certificateUpdateRes.setSAProvisioningCertificateChain(saProvisioningCertificateChain);
CertificateChainType contractSignatureCertChain = new CertificateChainType();
contractSignatureCertChain.setCertificate(new byte[1]);
contractSignatureCertChain.setId("ID1");
certificateUpdateRes.setContractSignatureCertChain(contractSignatureCertChain);
ContractSignatureEncryptedPrivateKeyType contractSignatureEncryptedPrivateKey = new ContractSignatureEncryptedPrivateKeyType();
contractSignatureEncryptedPrivateKey.setValue(new byte[1]);
contractSignatureEncryptedPrivateKey.setId("ID2");
certificateUpdateRes.setContractSignatureEncryptedPrivateKey(contractSignatureEncryptedPrivateKey);
DiffieHellmanPublickeyType dhPublicKeyType = new DiffieHellmanPublickeyType();
dhPublicKeyType.setValue(new byte[1]);
dhPublicKeyType.setId("ID3");
certificateUpdateRes.setDHpublickey(dhPublicKeyType);
EMAIDType emaid = new EMAIDType();
emaid.setValue("DEV2G1234512345");
emaid.setId("ID4");
certificateUpdateRes.setEMAID(emaid);
public BodyBaseType getResponseMessage() {
return certificateUpdateRes;
}
}

View File

@ -36,6 +36,7 @@ import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import org.v2gclarity.risev2g.shared.misc.TimeRestrictions;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ACEVChargeParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeParameterDiscoveryReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeParameterDiscoveryResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVChargeParameterType;
@ -137,14 +138,19 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
return getSendMessage(chargeParameterDiscoveryRes, V2GMessages.CABLE_CHECK_REQ);
}
} else {
getLogger().error("Response code '" + chargeParameterDiscoveryRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(chargeParameterDiscoveryRes, chargeParameterDiscoveryRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (chargeParameterDiscoveryRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new ChargeParameterDiscoveryResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, chargeParameterDiscoveryRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(chargeParameterDiscoveryRes, chargeParameterDiscoveryRes.getResponseCode());
}
}
return getSendMessage(chargeParameterDiscoveryRes, V2GMessages.NONE);
return getSendMessage(chargeParameterDiscoveryRes, V2GMessages.NONE, chargeParameterDiscoveryRes.getResponseCode());
}
@ -169,21 +175,7 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
return false;
}
if (chargeParameterDiscoveryReq.getEVChargeParameter() == null ||
(chargeParameterDiscoveryReq.getEVChargeParameter().getValue() instanceof ACEVChargeParameterType &&
(((ACEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue()).getEAmount() == null ||
((ACEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue()).getEVMaxVoltage() == null ||
((ACEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue()).getEVMaxCurrent() == null ||
((ACEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue()).getEVMinCurrent() == null
)
) ||
(chargeParameterDiscoveryReq.getEVChargeParameter().getValue() instanceof DCEVChargeParameterType &&
(((DCEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue()).getDCEVStatus() == null ||
((DCEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue()).getEVMaximumCurrentLimit() == null ||
((DCEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue()).getEVMaximumVoltageLimit() == null
)
)
) {
if (!verifyChargeParameter(chargeParameterDiscoveryReq)) {
chargeParameterDiscoveryRes.setResponseCode(ResponseCodeType.FAILED_WRONG_CHARGE_PARAMETER);
return false;
}
@ -192,6 +184,93 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
}
private boolean verifyChargeParameter(ChargeParameterDiscoveryReqType chargeParameterDiscoveryReq) {
if (chargeParameterDiscoveryReq.getEVChargeParameter() == null) {
getLogger().error("EVChargeParameter is empty (null)");
return false;
}
if (chargeParameterDiscoveryReq.getEVChargeParameter().getValue() instanceof ACEVChargeParameterType) {
ACEVChargeParameterType acEVChargeParameter = (ACEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue();
if ( // Check if mandatory charge parameters are null
(acEVChargeParameter.getEAmount() == null ||
acEVChargeParameter.getEVMaxVoltage() == null ||
acEVChargeParameter.getEVMaxCurrent() == null ||
acEVChargeParameter.getEVMinCurrent() == null
) ||
// Check if charge parameters are out of range
( acEVChargeParameter.getEAmount().getValue() < 0 ||
acEVChargeParameter.getEAmount().getValue() * Math.pow(10, acEVChargeParameter.getEAmount().getMultiplier()) > 200000 ||
acEVChargeParameter.getEVMaxVoltage().getValue() < 0 ||
acEVChargeParameter.getEVMaxVoltage().getValue() * Math.pow(10, acEVChargeParameter.getEVMaxVoltage().getMultiplier()) > 1000 ||
acEVChargeParameter.getEVMaxCurrent().getValue() < 0 ||
acEVChargeParameter.getEVMaxCurrent().getValue() * Math.pow(10, acEVChargeParameter.getEVMaxCurrent().getMultiplier()) > 400 ||
acEVChargeParameter.getEVMinCurrent().getValue() < 0 ||
acEVChargeParameter.getEVMinCurrent().getValue() * Math.pow(10, acEVChargeParameter.getEVMinCurrent().getMultiplier()) > 400
)
) {
getLogger().error("One of the AC_EVChargeParameter elements is either null or out of range");
return false;
}
}
if (chargeParameterDiscoveryReq.getEVChargeParameter().getValue() instanceof DCEVChargeParameterType) {
DCEVChargeParameterType dcEVChargeParameter = (DCEVChargeParameterType) chargeParameterDiscoveryReq.getEVChargeParameter().getValue();
if ( // Check if mandatory charge parameters are null
(dcEVChargeParameter.getDCEVStatus() == null ||
dcEVChargeParameter.getEVMaximumCurrentLimit() == null ||
dcEVChargeParameter.getEVMaximumVoltageLimit() == null
) ||
// Check if charge parameters are out of range
( dcEVChargeParameter.getDCEVStatus().getEVRESSSOC() < 0 ||
dcEVChargeParameter.getDCEVStatus().getEVRESSSOC() > 100 ||
dcEVChargeParameter.getEVMaximumCurrentLimit().getValue() < 0 ||
dcEVChargeParameter.getEVMaximumCurrentLimit().getValue() * Math.pow(10, dcEVChargeParameter.getEVMaximumCurrentLimit().getMultiplier()) > 400 ||
dcEVChargeParameter.getEVMaximumVoltageLimit().getValue() < 0 ||
dcEVChargeParameter.getEVMaximumVoltageLimit().getValue() * Math.pow(10, dcEVChargeParameter.getEVMaximumVoltageLimit().getMultiplier()) > 1000 ||
( // EVMaximumPowerLimit is optional
dcEVChargeParameter.getEVMaximumPowerLimit() != null && (
dcEVChargeParameter.getEVMaximumPowerLimit().getValue() < 0 ||
dcEVChargeParameter.getEVMaximumPowerLimit().getValue() * Math.pow(10, dcEVChargeParameter.getEVMaximumPowerLimit().getMultiplier()) > 200000
)
) ||
( // EVEnergyCapacity is optional
dcEVChargeParameter.getEVEnergyCapacity() != null && (
dcEVChargeParameter.getEVEnergyCapacity().getValue() < 0 ||
dcEVChargeParameter.getEVEnergyCapacity().getValue() * Math.pow(10, dcEVChargeParameter.getEVEnergyCapacity().getMultiplier()) > 200000
)
) ||
( // EVEnergyRequest is optional
dcEVChargeParameter.getEVEnergyRequest() != null && (
dcEVChargeParameter.getEVEnergyRequest().getValue() < 0 ||
dcEVChargeParameter.getEVEnergyRequest().getValue() * Math.pow(10, dcEVChargeParameter.getEVEnergyRequest().getMultiplier()) > 200000
)
) ||
( // FullSOC is optional
dcEVChargeParameter.getFullSOC() != null && (
dcEVChargeParameter.getFullSOC() < 0 ||
dcEVChargeParameter.getFullSOC() > 100
)
) ||
( // BulkSOC is optional
dcEVChargeParameter.getBulkSOC() != null && (
dcEVChargeParameter.getBulkSOC() < 0 ||
dcEVChargeParameter.getBulkSOC() > 100
)
)
)
) {
getLogger().error("One of the DC_EVChargeParameter elements is either null or out of range");
return false;
}
}
return true;
}
private JAXBElement<SAScheduleListType> getSASchedulesAsJAXBElement(SAScheduleListType saScheduleList) {
return new JAXBElement<SAScheduleListType>(
new QName("urn:iso:15118:2:2013:MsgDataTypes", "SAScheduleList"),
@ -207,12 +286,9 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
this.waitingForSchedule = waitingForSchedule;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
chargeParameterDiscoveryRes.setEVSEProcessing(EVSEProcessingType.FINISHED);
chargeParameterDiscoveryRes.setEVSEChargeParameter(
((IACEVSEController) getCommSessionContext().getACEvseController()).getACEVSEChargeParameter());
public BodyBaseType getResponseMessage() {
return chargeParameterDiscoveryRes;
}
}

View File

@ -27,10 +27,12 @@ import org.v2gclarity.risev2g.secc.evseController.IACEVSEController;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargingStatusReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargingStatusResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.MeterInfoType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
public class WaitForChargingStatusReq extends ServerState {
@ -84,20 +86,22 @@ public class WaitForChargingStatusReq extends ServerState {
return getSendMessage(chargingStatusRes, V2GMessages.FORK);
}
} else {
setMandatoryFieldsForFailedRes();
if (chargingStatusRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new ChargingStatusResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, chargingStatusRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(chargingStatusRes, chargingStatusRes.getResponseCode());
}
}
return getSendMessage(chargingStatusRes, V2GMessages.NONE);
return getSendMessage(chargingStatusRes, V2GMessages.NONE, chargingStatusRes.getResponseCode());
}
@Override
protected void setMandatoryFieldsForFailedRes() {
chargingStatusRes.setEVSEID(getCommSessionContext().getACEvseController().getEvseID());
chargingStatusRes.setSAScheduleTupleID((short) 1);
chargingStatusRes.setACEVSEStatus(((IACEVSEController) getCommSessionContext().getACEvseController())
.getACEVSEStatus(EVSENotificationType.NONE)
);
public BodyBaseType getResponseMessage() {
return chargingStatusRes;
}
}

View File

@ -27,11 +27,11 @@ import org.v2gclarity.risev2g.secc.evseController.IDCEVSEController;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CurrentDemandReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CurrentDemandResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PhysicalValueType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.UnitSymbolType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
public class WaitForCurrentDemandReq extends ServerState {
@ -94,29 +94,21 @@ public class WaitForCurrentDemandReq extends ServerState {
return getSendMessage(currentDemandRes, V2GMessages.FORK);
}
} else {
setMandatoryFieldsForFailedRes();
if (currentDemandRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new CurrentDemandResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, currentDemandRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(currentDemandRes, currentDemandRes.getResponseCode());
}
}
return getSendMessage(currentDemandRes, V2GMessages.NONE);
return getSendMessage(currentDemandRes, V2GMessages.NONE, currentDemandRes.getResponseCode());
}
@Override
protected void setMandatoryFieldsForFailedRes() {
IDCEVSEController evseController = (IDCEVSEController) getCommSessionContext().getDCEvseController();
PhysicalValueType physicalValueType = new PhysicalValueType();
physicalValueType.setMultiplier(new Byte("0"));
physicalValueType.setUnit(UnitSymbolType.V); // does not matter which unit symbol if FAILED response is sent
physicalValueType.setValue((short) 1);
currentDemandRes.setDCEVSEStatus(evseController.getDCEVSEStatus(EVSENotificationType.NONE));
currentDemandRes.setEVSEPresentVoltage(physicalValueType);
currentDemandRes.setEVSEPresentCurrent(physicalValueType);
currentDemandRes.setEVSECurrentLimitAchieved(false);
currentDemandRes.setEVSEVoltageLimitAchieved(false);
currentDemandRes.setEVSEPowerLimitAchieved(false);
currentDemandRes.setEVSEID(evseController.getEvseID());
currentDemandRes.setSAScheduleTupleID((short) 1);
public BodyBaseType getResponseMessage() {
return currentDemandRes;
}
}

View File

@ -34,6 +34,7 @@ import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ACEVSEStatusType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVSEStatusType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.MeterInfoType;
@ -72,14 +73,19 @@ public class WaitForMeteringReceiptReq extends ServerState {
return getSendMessage(meteringReceiptRes, V2GMessages.FORK);
} else {
getLogger().error("Response code '" + meteringReceiptRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(meteringReceiptRes, meteringReceiptRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (meteringReceiptRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new MeteringReceiptResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, meteringReceiptRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(meteringReceiptRes, meteringReceiptRes.getResponseCode());
}
}
return getSendMessage(meteringReceiptRes, V2GMessages.NONE);
return getSendMessage(meteringReceiptRes, V2GMessages.NONE, meteringReceiptRes.getResponseCode());
}
@ -97,10 +103,13 @@ public class WaitForMeteringReceiptReq extends ServerState {
// Verify signature
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(meteringReceiptReq.getId(), SecurityUtils.generateDigest(meteringReceiptReq));
verifyXMLSigRefElements.put(
meteringReceiptReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(meteringReceiptReq)));
if (!SecurityUtils.verifySignature(
signature,
getMessageHandler().getJaxbElement(signature.getSignedInfo()),
verifyXMLSigRefElements,
getCommSessionContext().getContractSignatureCertChain().getCertificate())) {
meteringReceiptRes.setResponseCode(ResponseCodeType.FAILED_METERING_SIGNATURE_NOT_VALID);
@ -131,10 +140,10 @@ public class WaitForMeteringReceiptReq extends ServerState {
}
private void setEVSEStatus(MeteringReceiptResType meteringReceiptRes) {
protected void setEVSEStatus(MeteringReceiptResType meteringReceiptRes) {
if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("AC")) {
/*
* The MiscUtils method getJAXBElement() cannot be used here because of the difference in the
* The MessageHandler method getJAXBElement() cannot be used here because of the difference in the
* class name (ACEVSEStatus) and the name in the XSD (AC_EVSEStatus)
*/
JAXBElement jaxbEVSEStatus = new JAXBElement(new QName("urn:iso:15118:2:2013:MsgDataTypes", "AC_EVSEStatus"),
@ -143,7 +152,7 @@ public class WaitForMeteringReceiptReq extends ServerState {
meteringReceiptRes.setEVSEStatus(jaxbEVSEStatus);
} else if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("DC")) {
/*
* The MiscUtils method getJAXBElement() cannot be used here because of the difference in the
* The MessageHandler method getJAXBElement() cannot be used here because of the difference in the
* class name (DCEVSEStatus) and the name in the XSD (DC_EVSEStatus)
*/
JAXBElement jaxbACEVSEStatus = new JAXBElement(new QName("urn:iso:15118:2:2013:MsgDataTypes", "DC_EVSEStatus"),
@ -156,9 +165,8 @@ public class WaitForMeteringReceiptReq extends ServerState {
}
}
@Override
protected void setMandatoryFieldsForFailedRes() {
setEVSEStatus(meteringReceiptRes);
public BodyBaseType getResponseMessage() {
return meteringReceiptRes;
}
}

View File

@ -23,14 +23,13 @@
*******************************************************************************/
package org.v2gclarity.risev2g.secc.states;
import java.security.cert.X509Certificate;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.PKI;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.utils.ByteUtils;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentDetailsReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentDetailsResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
@ -56,21 +55,27 @@ public class WaitForPaymentDetailsReq extends ServerState {
// Save contract certificate chain for certificate and signature verification/validation
getCommSessionContext().setContractSignatureCertChain(paymentDetailsReq.getContractSignatureCertChain());
paymentDetailsRes.setEVSETimeStamp(System.currentTimeMillis());
paymentDetailsRes.setEVSETimeStamp(System.currentTimeMillis() / 1000L);
byte[] genChallenge = SecurityUtils.generateRandomNumber(16);
getCommSessionContext().setGenChallenge(genChallenge);
paymentDetailsRes.setGenChallenge(genChallenge);
} else {
getLogger().error("Response code '" + paymentDetailsRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(paymentDetailsRes, paymentDetailsRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (paymentDetailsRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new PaymentDetailsResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, paymentDetailsRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(paymentDetailsRes, paymentDetailsRes.getResponseCode());
}
}
return getSendMessage(paymentDetailsRes,
(paymentDetailsRes.getResponseCode().toString().startsWith("OK") ?
V2GMessages.AUTHORIZATION_REQ : V2GMessages.NONE)
V2GMessages.AUTHORIZATION_REQ : V2GMessages.NONE),
paymentDetailsRes.getResponseCode()
);
}
@ -84,17 +89,13 @@ public class WaitForPaymentDetailsReq extends ServerState {
return false;
}
if (!SecurityUtils.isCertificateChainValid(paymentDetailsReq.getContractSignatureCertChain())) {
getLogger().error("Contract certificate chain is not valid");
paymentDetailsRes.setResponseCode(ResponseCodeType.FAILED_CERTIFICATE_EXPIRED);
return false;
}
if (!SecurityUtils.isCertificateChainVerified(
GlobalValues.SECC_TRUSTSTORE_FILEPATH.toString(),
paymentDetailsReq.getContractSignatureCertChain())) {
getLogger().error("Contract certificate chain could not be verified");
paymentDetailsRes.setResponseCode(ResponseCodeType.FAILED_CERT_CHAIN_ERROR);
// Check complete contract certificate chain
ResponseCodeType certChainResponseCode = SecurityUtils.verifyCertificateChain(
paymentDetailsReq.getContractSignatureCertChain(),
GlobalValues.SECC_TRUSTSTORE_FILEPATH.toString(),
PKI.MO);
if (!certChainResponseCode.equals(ResponseCodeType.OK)) {
paymentDetailsRes.setResponseCode(certChainResponseCode);
return false;
}
@ -106,13 +107,19 @@ public class WaitForPaymentDetailsReq extends ServerState {
paymentDetailsRes.setResponseCode(ResponseCodeType.OK_CERTIFICATE_EXPIRES_SOON);
}
// Check for FAILED_ContractCancelled
// TODO how to check if the EMAID provided by EVCC is not accepted by secondary actor?
if (!SecurityUtils.isEMAIDSynstaxValid(paymentDetailsReq.getContractSignatureCertChain())) {
// There is no good FAILED response code for this situation, but ContractCanceled is still better than FAILED
paymentDetailsRes.setResponseCode(ResponseCodeType.FAILED_CONTRACT_CANCELED);
return false;
}
return true;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
paymentDetailsRes.setEVSETimeStamp(0L);
paymentDetailsRes.setGenChallenge(new byte[1]);
public BodyBaseType getResponseMessage() {
return paymentDetailsRes;
}
}

View File

@ -26,6 +26,7 @@ package org.v2gclarity.risev2g.secc.states;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentOptionType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentServiceSelectionReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentServiceSelectionResType;
@ -69,14 +70,19 @@ public class WaitForPaymentServiceSelectionReq extends ServerState {
return getSendMessage(paymentServiceSelectionRes, V2GMessages.AUTHORIZATION_REQ);
}
} else {
getLogger().error("Response code '" + paymentServiceSelectionRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(paymentServiceSelectionRes, paymentServiceSelectionRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (paymentServiceSelectionRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new PaymentServiceSelectionResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, paymentServiceSelectionRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(paymentServiceSelectionRes, paymentServiceSelectionRes.getResponseCode());
}
}
return getSendMessage(paymentServiceSelectionRes, V2GMessages.NONE);
return getSendMessage(paymentServiceSelectionRes, V2GMessages.NONE, paymentServiceSelectionRes.getResponseCode());
}
@ -125,10 +131,10 @@ public class WaitForPaymentServiceSelectionReq extends ServerState {
return true;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
// No other mandatory fields to be set besides response code
public BodyBaseType getResponseMessage() {
return paymentServiceSelectionRes;
}
}

View File

@ -23,20 +23,24 @@
*******************************************************************************/
package org.v2gclarity.risev2g.secc.states;
import java.util.ArrayList;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ACEVSEStatusType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeProgressType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargingProfileType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVSEStatusCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVSEStatusType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PMaxScheduleEntryType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PowerDeliveryReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PowerDeliveryResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ProfileEntryType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.RelativeTimeIntervalType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SAScheduleTupleType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
@ -69,6 +73,8 @@ public class WaitForPowerDeliveryReq extends ServerState {
setEVSEStatus(powerDeliveryRes);
if (powerDeliveryReq.getChargeProgress().equals(ChargeProgressType.START)) {
getCommSessionContext().setChargeProgressStarted(true); // see [V2G2-812]
if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("AC"))
return getSendMessage(powerDeliveryRes, V2GMessages.CHARGING_STATUS_REQ);
else
@ -88,14 +94,19 @@ public class WaitForPowerDeliveryReq extends ServerState {
return getSendMessage(powerDeliveryRes, V2GMessages.CHARGE_PARAMETER_DISCOVERY_REQ);
}
} else {
getLogger().error("Response code '" + powerDeliveryRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(powerDeliveryRes, powerDeliveryRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (powerDeliveryRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new PowerDeliveryResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, powerDeliveryRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(powerDeliveryRes, powerDeliveryRes.getResponseCode());
}
}
return getSendMessage(powerDeliveryRes, V2GMessages.NONE);
return getSendMessage(powerDeliveryRes, V2GMessages.NONE, powerDeliveryRes.getResponseCode());
}
@ -109,7 +120,9 @@ public class WaitForPowerDeliveryReq extends ServerState {
}
// Important to call this AFTER checking for valid tariff selection because of possible null-value!
if (!isChargingProfileValid(chosenSASchedule, powerDeliveryReq.getChargingProfile())) {
// Check ChargingProfile only if EV does not want to stop the charging process
if (!powerDeliveryReq.getChargeProgress().equals(ChargeProgressType.STOP) &&
!isChargingProfileValid(chosenSASchedule, powerDeliveryReq.getChargingProfile())) {
powerDeliveryRes.setResponseCode(ResponseCodeType.FAILED_CHARGING_PROFILE_INVALID);
return false;
}
@ -144,11 +157,19 @@ public class WaitForPowerDeliveryReq extends ServerState {
return false;
}
if (powerDeliveryReq.getChargeProgress().equals(ChargeProgressType.RENEGOTIATE) &&
!getCommSessionContext().isChargeProgressStarted()) {
getLogger().error("EVCC wants to renegotiate, but charge progress has not started yet (no "
+ "PowerDeliveryReq with ChargeProgress=START has been received before)");
powerDeliveryRes.setResponseCode(ResponseCodeType.FAILED);
return false;
}
return true;
}
private void setEVSEStatus(PowerDeliveryResType powerDeliveryRes) {
protected void setEVSEStatus(PowerDeliveryResType powerDeliveryRes) {
if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("AC")) {
/*
* The MiscUtils method getJAXBElement() cannot be used here because of the difference in the
@ -185,14 +206,62 @@ public class WaitForPowerDeliveryReq extends ServerState {
private boolean isChargingProfileValid(
SAScheduleTupleType chosenSAScheduleTuple,
ChargingProfileType chargingProfile) {
// TODO check for validity of charging profile
long profileEntryStart = 0;
double profileEntryPower = 0;
long pMaxScheduleIntervalStart = 0;
long pMaxScheduleIntervalEnd = 0;
double pMaxScheduleIntervalPMax = 0;
ArrayList<PMaxScheduleEntryType> limit = (ArrayList<PMaxScheduleEntryType>) chosenSAScheduleTuple.getPMaxSchedule().getPMaxScheduleEntry();
if (chargingProfile == null) {
getLogger().error("ChargingProfile is empty (null)");
return false;
}
for (ProfileEntryType profileEntry : chargingProfile.getProfileEntry()) {
if (profileEntry.getChargingProfileEntryMaxNumberOfPhasesInUse() == 2) {
getLogger().error("Parameter MaxNumberOfPhasesInUse of one ChargingProfile entry element is 2 which is not allowed. Only 1 or 3 are valid values.");
return false;
}
profileEntryStart = profileEntry.getChargingProfileEntryStart();
profileEntryPower = profileEntry.getChargingProfileEntryMaxPower().getValue() *
Math.pow(10, profileEntry.getChargingProfileEntryMaxPower().getMultiplier());
for (int i=0; i < limit.size(); i++) {
pMaxScheduleIntervalStart = ((RelativeTimeIntervalType) limit.get(i).getTimeInterval().getValue()).getStart();
try {
pMaxScheduleIntervalEnd = ((RelativeTimeIntervalType) limit.get(i+1).getTimeInterval().getValue()).getStart();
} catch (IndexOutOfBoundsException e) {
if ( ((RelativeTimeIntervalType) limit.get(i).getTimeInterval().getValue()).getDuration() != 0)
pMaxScheduleIntervalEnd = pMaxScheduleIntervalStart + ((RelativeTimeIntervalType) limit.get(i).getTimeInterval().getValue()).getDuration();
else
pMaxScheduleIntervalEnd = Long.MAX_VALUE;
}
pMaxScheduleIntervalPMax = limit.get(i).getPMax().getValue() * Math.pow(10, limit.get(i).getPMax().getMultiplier());
// TODO Find out how to deal with grace time period defined by [V2G2-833] and [V2G2-834] that contradicts [V2G2-777]
if (profileEntryStart >= pMaxScheduleIntervalStart && profileEntryStart < pMaxScheduleIntervalEnd) {
if (profileEntryPower > pMaxScheduleIntervalPMax) {
getLogger().error("ChargingProfile entry element starting at " + profileEntryStart +
"s exceeds power limit. Limit is " + pMaxScheduleIntervalPMax +
" W, ChargingProfile entry's max power value is " + profileEntryPower + " W" );
return false;
} else
break;
} else
continue;
}
}
return true;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
setEVSEStatus(powerDeliveryRes);
public BodyBaseType getResponseMessage() {
return powerDeliveryRes;
}
}

View File

@ -27,9 +27,11 @@ import org.v2gclarity.risev2g.secc.evseController.IDCEVSEController;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PreChargeReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PreChargeResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
public class WaitForPreChargeReq extends ServerState {
@ -63,21 +65,25 @@ public class WaitForPreChargeReq extends ServerState {
((ForkState) getCommSessionContext().getStates().get(V2GMessages.FORK))
.getAllowedRequests().add(V2GMessages.POWER_DELIVERY_REQ);
} else {
setMandatoryFieldsForFailedRes();
if (preChargeRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new PreChargeResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, preChargeRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(preChargeRes, preChargeRes.getResponseCode());
}
}
return getSendMessage(preChargeRes,
(preChargeRes.getResponseCode().toString().startsWith("OK") ?
V2GMessages.FORK : V2GMessages.NONE)
V2GMessages.FORK : V2GMessages.NONE),
preChargeRes.getResponseCode()
);
}
@Override
protected void setMandatoryFieldsForFailedRes() {
IDCEVSEController evseController = (IDCEVSEController) getCommSessionContext().getDCEvseController();
preChargeRes.setDCEVSEStatus(evseController.getDCEVSEStatus(EVSENotificationType.NONE));
preChargeRes.setEVSEPresentVoltage(evseController.getPresentVoltage());
public BodyBaseType getResponseMessage() {
return preChargeRes;
}
}

View File

@ -26,6 +26,7 @@ package org.v2gclarity.risev2g.secc.states;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ParameterSetType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
@ -81,14 +82,19 @@ public class WaitForServiceDetailReq extends ServerState {
return getSendMessage(serviceDetailRes, V2GMessages.FORK);
} else {
getLogger().error("Response code '" + serviceDetailRes.getResponseCode() + "' will be sent");
setMandatoryFieldsForFailedRes();
setMandatoryFieldsForFailedRes(serviceDetailRes, serviceDetailRes.getResponseCode());
}
} else {
setMandatoryFieldsForFailedRes();
if (serviceDetailRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new ServiceDetailResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, serviceDetailRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(serviceDetailRes, serviceDetailRes.getResponseCode());
}
}
return getSendMessage(serviceDetailRes, V2GMessages.NONE);
return getSendMessage(serviceDetailRes, V2GMessages.NONE, serviceDetailRes.getResponseCode());
}
@ -190,10 +196,9 @@ public class WaitForServiceDetailReq extends ServerState {
return parameterSet;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
serviceDetailRes.setServiceID(1);
public BodyBaseType getResponseMessage() {
return serviceDetailRes;
}
}

View File

@ -26,7 +26,10 @@ package org.v2gclarity.risev2g.secc.states;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.utils.MiscUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeServiceType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ServiceCategoryType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ServiceDiscoveryReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ServiceDiscoveryResType;
@ -74,17 +77,24 @@ public class WaitForServiceDiscoveryReq extends ServerState {
((ForkState) getCommSessionContext().getStates().get(V2GMessages.FORK))
.getAllowedRequests().add(V2GMessages.PAYMENT_SERVICE_SELECTION_REQ);
} else {
setMandatoryFieldsForFailedRes();
if (serviceDiscoveryRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new ServiceDiscoveryResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, serviceDiscoveryRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(serviceDiscoveryRes, serviceDiscoveryRes.getResponseCode());
}
}
return getSendMessage(serviceDiscoveryRes,
(serviceDiscoveryRes.getResponseCode().toString().startsWith("OK") ?
V2GMessages.FORK : V2GMessages.NONE)
V2GMessages.FORK : V2GMessages.NONE),
serviceDiscoveryRes.getResponseCode()
);
}
private ChargeServiceType getChargeService() {
public ChargeServiceType getChargeService() {
SupportedEnergyTransferModeType supportedEnergyTransferModes = new SupportedEnergyTransferModeType();
supportedEnergyTransferModes.getEnergyTransferMode().addAll(
getCommSessionContext().getSupportedEnergyTransferModes());
@ -106,7 +116,8 @@ public class WaitForServiceDiscoveryReq extends ServerState {
*/
chargeService.setServiceScope("chargingServiceScope");
chargeService.setFreeService(false); // it is supposed that charging is by default not for free
boolean isChargingForFree = ((boolean) MiscUtils.getPropertyValue("ChargingForFree"));
chargeService.setFreeService(isChargingForFree);
return chargeService;
}
@ -116,7 +127,7 @@ public class WaitForServiceDiscoveryReq extends ServerState {
ServiceListType serviceList = new ServiceListType();
if (serviceCategoryFilter != null)
getLogger().debug("EVCC filters offered services by category: " + serviceScopeFilter.toString());
getLogger().debug("EVCC filters offered services by category: " + serviceCategoryFilter.toString());
// Currently no filter based on service scope is applied since its string value is not standardized somehow
if (getCommSessionContext().isTlsConnection() && (
@ -150,10 +161,9 @@ public class WaitForServiceDiscoveryReq extends ServerState {
return certificateService;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
serviceDiscoveryRes.setChargeService(getChargeService());
serviceDiscoveryRes.setPaymentOptionList(getCommSessionContext().getPaymentOptions());
public BodyBaseType getResponseMessage() {
return serviceDiscoveryRes;
}
}

View File

@ -26,6 +26,8 @@ package org.v2gclarity.risev2g.secc.states;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SessionSetupReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SessionSetupResType;
@ -46,19 +48,26 @@ public class WaitForSessionSetupReq extends ServerState {
// Unix time stamp is needed (seconds instead of milliseconds)
sessionSetupRes.setEVSETimeStamp(System.currentTimeMillis() / 1000L);
} else {
setMandatoryFieldsForFailedRes();
if (sessionSetupRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new SessionSetupResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, sessionSetupRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(sessionSetupRes, sessionSetupRes.getResponseCode());
}
}
return getSendMessage(sessionSetupRes,
(sessionSetupRes.getResponseCode().toString().startsWith("OK") ?
V2GMessages.SERVICE_DISCOVERY_REQ : V2GMessages.NONE)
V2GMessages.SERVICE_DISCOVERY_REQ : V2GMessages.NONE),
sessionSetupRes.getResponseCode()
);
}
@Override
protected void setMandatoryFieldsForFailedRes() {
sessionSetupRes.setEVSEID(getCommSessionContext().getEvseController().getEvseID());
public BodyBaseType getResponseMessage() {
return sessionSetupRes;
}
}

View File

@ -26,6 +26,8 @@ package org.v2gclarity.risev2g.secc.states;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SessionStopReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SessionStopResType;
@ -43,16 +45,22 @@ public class WaitForSessionStopReq extends ServerState {
if (isIncomingMessageValid(message, SessionStopReqType.class, sessionStopRes)) {
getCommSessionContext().setStopV2GCommunicationSession(true);
} else {
setMandatoryFieldsForFailedRes();
if (sessionStopRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new SessionStopResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, sessionStopRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(sessionStopRes, sessionStopRes.getResponseCode());
}
}
return getSendMessage(sessionStopRes, V2GMessages.NONE);
return getSendMessage(sessionStopRes, V2GMessages.NONE, sessionStopRes.getResponseCode());
}
@Override
protected void setMandatoryFieldsForFailedRes() {
// No other fields need to be set besides response code
public BodyBaseType getResponseMessage() {
return sessionStopRes;
}
}

View File

@ -105,12 +105,10 @@ public class WaitForSupportedAppProtocolReq extends ServerState {
supportedAppProtocolRes.setResponseCode(ResponseCodeType.FAILED_NO_NEGOTIATION);
}
if (supportedAppProtocolRes.getResponseCode().equals(ResponseCodeType.FAILED_NO_NEGOTIATION))
getLogger().error("Response code '" + supportedAppProtocolRes.getResponseCode() + "' will be sent");
return getSendMessage(supportedAppProtocolRes,
(supportedAppProtocolRes.getResponseCode().toString().startsWith("OK") ?
V2GMessages.SESSION_SETUP_REQ : V2GMessages.NONE)
V2GMessages.SESSION_SETUP_REQ : V2GMessages.NONE),
supportedAppProtocolRes.getResponseCode()
);
}
@ -135,11 +133,11 @@ public class WaitForSupportedAppProtocolReq extends ServerState {
return supportedAppProtocols;
}
@Override
protected void setMandatoryFieldsForFailedRes() {
// No additional mandatory fields besides response code
public BodyBaseType getResponseMessage() {
return null;
}
}

View File

@ -27,7 +27,9 @@ import org.v2gclarity.risev2g.secc.evseController.IDCEVSEController;
import org.v2gclarity.risev2g.secc.session.V2GCommunicationSessionSECC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.WeldingDetectionReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.WeldingDetectionResType;
@ -60,7 +62,13 @@ public class WaitForWeldingDetectionReq extends ServerState {
((ForkState) getCommSessionContext().getStates().get(V2GMessages.FORK))
.getAllowedRequests().add(V2GMessages.SESSION_STOP_REQ);
} else {
setMandatoryFieldsForFailedRes();
if (weldingDetectionRes.getResponseCode().equals(ResponseCodeType.FAILED_SEQUENCE_ERROR)) {
BodyBaseType responseMessage = getSequenceErrorResMessage(new WeldingDetectionResType(), message);
return getSendMessage(responseMessage, V2GMessages.NONE, weldingDetectionRes.getResponseCode());
} else {
setMandatoryFieldsForFailedRes(weldingDetectionRes, weldingDetectionRes.getResponseCode());
}
}
return getSendMessage(weldingDetectionRes,
@ -69,12 +77,9 @@ public class WaitForWeldingDetectionReq extends ServerState {
);
}
@Override
protected void setMandatoryFieldsForFailedRes() {
IDCEVSEController evseController = (IDCEVSEController) getCommSessionContext().getDCEvseController();
weldingDetectionRes.setDCEVSEStatus(evseController.getDCEVSEStatus(EVSENotificationType.NONE));
weldingDetectionRes.setEVSEPresentVoltage(evseController.getPresentVoltage());
public BodyBaseType getResponseMessage() {
return weldingDetectionRes;
}
}

View File

@ -169,6 +169,7 @@ public class ConnectionHandler extends Observable implements Runnable {
public void stop() {
if (!isStopAlreadyInitiated()) {
getLogger().debug("Closing connection to client ...");
setStopAlreadyInitiated(true);
try {

View File

@ -143,6 +143,7 @@ public enum GlobalValues {
return shortValue;
}
@Override
public String toString() {
switch (this) {
case EVCC_CONFIG_PROPERTIES_PATH:

View File

@ -36,17 +36,20 @@ public class EXISchemaFactoryExceptionHandler implements EXISchemaFactoryErrorHa
super();
}
public void warning(EXISchemaFactoryException eXISchemaFactoryException) throws EXISchemaFactoryException {
@Override
public void warning(EXISchemaFactoryException eXISchemaFactoryException) throws EXISchemaFactoryException {
logger.warn("WARN:");
eXISchemaFactoryException.printStackTrace();
}
public void error(EXISchemaFactoryException eXISchemaFactoryException) throws EXISchemaFactoryException {
@Override
public void error(EXISchemaFactoryException eXISchemaFactoryException) throws EXISchemaFactoryException {
logger.warn("ERROR:");
eXISchemaFactoryException.printStackTrace();
}
public void fatalError(EXISchemaFactoryException eXISchemaFactoryException) throws EXISchemaFactoryException {
@Override
public void fatalError(EXISchemaFactoryException eXISchemaFactoryException) throws EXISchemaFactoryException {
logger.warn("FATAL:");
eXISchemaFactoryException.getStackTrace();
}

View File

@ -102,10 +102,9 @@ public final class EXIficientCodec extends ExiCodec {
return instance;
}
// -- END: SINGLETON DEFINITION --
@Override
public synchronized byte[] encodeEXI(Object jaxbObject, String xsdSchemaPath) {
Grammars grammar = null;
@ -121,7 +120,6 @@ public final class EXIficientCodec extends ExiCodec {
}
InputStream inStream = marshalToInputStream(jaxbObject);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos = ((ByteArrayOutputStream) encode(inStream, grammar));

View File

@ -29,13 +29,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
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;
@ -43,7 +40,6 @@ import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.utils.MiscUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolReq;
import org.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolRes;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignedInfoType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
public abstract class ExiCodec {
@ -51,38 +47,17 @@ public abstract class ExiCodec {
private Logger logger = LogManager.getLogger(this.getClass().getSimpleName());
private Marshaller marshaller;
private Unmarshaller unmarshaller;
private JAXBContext jaxbContext;
private InputStream inStream;
private Object decodedMessage;
private String decodedExi;
private boolean xmlRepresentation;
public ExiCodec() {
try {
setJaxbContext(JAXBContext.newInstance(SupportedAppProtocolReq.class, SupportedAppProtocolRes.class, V2GMessage.class));
setUnmarshaller(getJaxbContext().createUnmarshaller());
setMarshaller(getJaxbContext().createMarshaller());
/*
* JAXB by default silently ignores errors. Adding this code to throw an exception if
* something goes wrong.
*/
getUnmarshaller().setEventHandler(
new ValidationEventHandler() {
public boolean handleEvent(ValidationEvent event ) {
throw new RuntimeException(event.getMessage(),
event.getLinkedException());
}
});
// Check if XML representation of sent messages is to be shown (for debug purposes)
if ((boolean) MiscUtils.getPropertyValue("XMLRepresentationOfMessages"))
setXMLRepresentation(true);
else
setXMLRepresentation(false);
} catch (JAXBException e) {
getLogger().error("A JAXBException occurred while trying to instantiate " + this.getClass().getSimpleName(), e);
}
// Check if XML representation of sent messages is to be shown (for debug purposes)
if ((boolean) MiscUtils.getPropertyValue("XMLRepresentationOfMessages"))
setXMLRepresentation(true);
else
setXMLRepresentation(false);
}
@ -156,16 +131,16 @@ public abstract class ExiCodec {
* Provides the EXI encoding of the header's SignedInfo element. The resulting byte array can then be used to
* verify a signature.
*
* @param signedInfo The SignedInfo element of the V2GMessage header
* @param jaxbSignedInfo The SignedInfo element of the V2GMessage header, given as a JAXB element
* @return The EXI encoding of the SignedInfo element given as a byte array
*/
public byte[] getExiEncodedSignedInfo(SignedInfoType signedInfo) {
public byte[] getExiEncodedSignedInfo(JAXBElement jaxbSignedInfo) {
// The schema-informed fragment grammar option needs to be used for EXI encodings in the header's signature
setFragment(true);
// The SignedInfo element must be encoded
byte[] encodedSignedInfo = encodeEXI(
MiscUtils.getJaxbElement(signedInfo),
jaxbSignedInfo,
GlobalValues.SCHEMA_PATH_XMLDSIG.toString()
);
@ -190,14 +165,6 @@ public abstract class ExiCodec {
this.marshaller = marshaller;
}
public JAXBContext getJaxbContext() {
return jaxbContext;
}
public void setJaxbContext(JAXBContext jaxbContext) {
this.jaxbContext = jaxbContext;
}
public Unmarshaller getUnmarshaller() {
return unmarshaller;
}

View File

@ -31,6 +31,7 @@ import java.io.StringWriter;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
@ -89,7 +90,7 @@ public final class OpenEXICodec extends ExiCodec {
// getTransmogrifier().setDivertBuiltinGrammarToAnyType(true); // enable V2G's built-in grammar usage
// Standard SAX methods parse content and lexical values
setSaxTransformerFactory((SAXTransformerFactory) SAXTransformerFactory.newInstance());
setSaxTransformerFactory((SAXTransformerFactory) TransformerFactory.newInstance());
setSaxParserFactory(SAXParserFactory.newInstance());
getSaxParserFactory().setNamespaceAware(true);

View File

@ -89,6 +89,7 @@ public class XSDResolver implements XMLEntityResolver {
}
@Override
public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier)
throws XNIException, IOException {
String literalSystemId = resourceIdentifier.getLiteralSystemId();

View File

@ -27,19 +27,24 @@ import java.security.interfaces.ECPrivateKey;
import java.util.Arrays;
import java.util.HashMap;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.namespace.QName;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.exiCodec.EXIficientCodec;
import org.v2gclarity.risev2g.shared.exiCodec.ExiCodec;
import org.v2gclarity.risev2g.shared.exiCodec.OpenEXICodec;
import org.v2gclarity.risev2g.shared.misc.V2GCommunicationSession;
import org.v2gclarity.risev2g.shared.misc.V2GTPMessage;
import org.v2gclarity.risev2g.shared.utils.ByteUtils;
import org.v2gclarity.risev2g.shared.utils.MiscUtils;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolReq;
import org.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolRes;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.MessageHeaderType;
@ -55,6 +60,14 @@ public class MessageHandler {
private Logger logger = LogManager.getLogger(this.getClass().getSimpleName());
private ExiCodec exiCodec;
private V2GCommunicationSession commSessionContext;
private JAXBContext jaxbContext;
private enum jaxbContextEnum {
SUPPORTED_APP_PROTOCOL_REQ,
SUPPORTED_APP_PROTOCOL_RES,
V2G_MESSAGE,
OTHER // includes the jaxbContext needed for the parameters of CertificateInstallationRes/CertificateUpdateRes
}
private jaxbContextEnum currentJaxbContext;
/**
* This constructor is used by V2GCommunicationSessionEVCC and -SECC
@ -64,6 +77,7 @@ public class MessageHandler {
public MessageHandler(V2GCommunicationSession commSessionContext) {
this();
setCommSessionContext(commSessionContext);
setCurrentJaxbContext(jaxbContextEnum.SUPPORTED_APP_PROTOCOL_REQ);
}
/**
@ -73,28 +87,33 @@ public class MessageHandler {
// Choose which implementation of an EXI codec to use
setExiCodec(EXIficientCodec.getInstance());
// setExiCodec(OpenEXICodec.getInstance());
setJaxbContext(SupportedAppProtocolReq.class, SupportedAppProtocolRes.class, V2GMessage.class);
setCurrentJaxbContext(jaxbContextEnum.SUPPORTED_APP_PROTOCOL_REQ);
}
public boolean isV2GTPMessageValid(V2GTPMessage v2gTpMessage) {
if (isVersionAndInversionFieldCorrect(v2gTpMessage) &&
isPayloadTypeCorrect(v2gTpMessage) &&
checkPayloadLength(v2gTpMessage))
isPayloadLengthCorrect(v2gTpMessage))
return true;
return false;
}
public boolean isVersionAndInversionFieldCorrect(V2GTPMessage v2gTpMessage) {
/*
* The inversion field is set by a private method in V2GTPMessage.java and cannot be set from the outside
* Therefore an additional check for the inversion field is not necessary.
*/
if (v2gTpMessage.getProtocolVersion() == GlobalValues.V2GTP_VERSION_1_IS.getByteValue()) return true;
if (v2gTpMessage.getProtocolVersion() != GlobalValues.V2GTP_VERSION_1_IS.getByteValue()) {
getLogger().error("Protocol version (" + ByteUtils.toStringFromByte(v2gTpMessage.getProtocolVersion()) +
") is not supported!");
return false;
}
getLogger().error("Protocol version or inverse protocol version of '" +
ByteUtils.toStringFromByte(v2gTpMessage.getProtocolVersion()) +
"' is not supported!");
if (v2gTpMessage.getInverseProtocolVersion() != (byte) (v2gTpMessage.getProtocolVersion() ^ 0xFF)) {
getLogger().error("Inverse protocol version (" + ByteUtils.toStringFromByte(v2gTpMessage.getInverseProtocolVersion()) +
") does not match the inverse value of the protocol version (" + v2gTpMessage.getProtocolVersion() + ")!");
return false;
}
return false;
return true;
}
public boolean isPayloadTypeCorrect(V2GTPMessage v2gTpMessage) {
@ -109,14 +128,23 @@ public class MessageHandler {
return false;
}
public boolean checkPayloadLength(V2GTPMessage v2gTpMessage) {
if (ByteUtils.toLongFromByteArray(v2gTpMessage.getPayloadLength()) <=
GlobalValues.V2GTP_HEADER_MAX_PAYLOAD_LENGTH.getLongValue()) return true;
getLogger().error("Payload length not supported! Payload length: " +
ByteUtils.toLongFromByteArray(v2gTpMessage.getPayloadLength()) + " bytes");
public boolean isPayloadLengthCorrect(V2GTPMessage v2gTpMessage) {
if (ByteUtils.toLongFromByteArray(v2gTpMessage.getPayloadLength()) > GlobalValues.V2GTP_HEADER_MAX_PAYLOAD_LENGTH.getLongValue() ||
ByteUtils.toLongFromByteArray(v2gTpMessage.getPayloadLength()) < 0L) {
getLogger().error("Payload length (" + ByteUtils.toLongFromByteArray(v2gTpMessage.getPayloadLength()) +
" bytes) not supported! Must be between 0 and " +
GlobalValues.V2GTP_HEADER_MAX_PAYLOAD_LENGTH.getLongValue() + " bytes");
return false;
}
return false;
int payLoadLengthField = ByteUtils.toIntFromByteArray(v2gTpMessage.getPayloadLength());
if (v2gTpMessage.getPayload().length != payLoadLengthField) {
getLogger().error("Length of payload (" + v2gTpMessage.getPayload().length + " bytes) does not match value of " +
"field payloadLength (" + payLoadLengthField + " bytes)");
return false;
}
return true;
}
@ -185,7 +213,7 @@ public class MessageHandler {
SignedInfoType signedInfo = SecurityUtils.getSignedInfo(xmlSignatureRefElements);
byte[] signature = SecurityUtils.signSignedInfoElement(
getExiCodec().getExiEncodedSignedInfo(signedInfo),
getExiCodec().getExiEncodedSignedInfo(getJaxbElement(signedInfo)),
signaturePrivateKey
);
@ -203,6 +231,97 @@ public class MessageHandler {
}
/**
* Creates an XML element from the given object which may be a complete message or just a field of a
* message. In case of XML signature generation, for some messages certain fields need to be signed
* instead of the complete message.
*
* Setting the JAXBContext is a little time consuming. Thus, this method checks which JAXBContext is currently set and does
* only set it anew if needed. For example, if the JAXBContext is already set for V2GMessage.class, then it will not be set anew
* if the JAXBElement for a message derived from V2GMessage is to be returned.
* The JAXBContext for the XML reference elements of CertificateInstallationRes/CertificateUpdateRes should be minimal and not
* comprise the complete V2GMessage.class.
*
* Suppressed unchecked warning, previously used a type-safe version such as new
* JAXBElement<SessionStopReqType>(new QName ... ) but this seems to work as well
* (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 The JAXBElement of the provided message or field
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public JAXBElement getJaxbElement(Object messageOrField) {
String messageName = messageOrField.getClass().getSimpleName().replace("Type", "");
String namespace = "";
JAXBElement jaxbElement = null;
if (messageName.equals("EMAID") ||
messageName.equals("CertificateChain") ||
messageName.equals("DiffieHellmanPublickey") ||
messageName.equals("ContractSignatureEncryptedPrivateKey")) {
/*
* If this branch is entered, we always need to set the JAXBContext anew because those elements don't repeat
* (like the jaxbContext for V2GMessage.class)
*/
setJaxbContext(messageOrField.getClass());
setCurrentJaxbContext(jaxbContextEnum.OTHER);
} else if (messageOrField instanceof SupportedAppProtocolReq &&
!getCurrentJaxbContext().equals(jaxbContextEnum.SUPPORTED_APP_PROTOCOL_REQ)) {
setJaxbContext(SupportedAppProtocolReq.class);
setCurrentJaxbContext(jaxbContextEnum.SUPPORTED_APP_PROTOCOL_REQ);
} else if (messageOrField instanceof SupportedAppProtocolRes &&
!getCurrentJaxbContext().equals(jaxbContextEnum.SUPPORTED_APP_PROTOCOL_RES)) {
setJaxbContext(SupportedAppProtocolRes.class);
setCurrentJaxbContext(jaxbContextEnum.SUPPORTED_APP_PROTOCOL_RES);
} else if (!getCurrentJaxbContext().equals(jaxbContextEnum.V2G_MESSAGE)) {
setJaxbContext(V2GMessage.class);
setCurrentJaxbContext(jaxbContextEnum.V2G_MESSAGE);
} else {
// nothing to do here
}
if (messageOrField instanceof SignedInfoType) {
namespace = GlobalValues.V2G_CI_XMLDSIG_NAMESPACE.toString();
} else {
namespace = GlobalValues.V2G_CI_MSG_BODY_NAMESPACE.toString();
/*
* We need to set the localPart of the QName object for the CertificateInstallationRes/CertificateUpdateRes parameters
* correctly. The messageOrField object's class name cannot be taken directly as it differs from what should be the
* XML element name.
*/
switch (messageName) {
case "CertificateChain":
messageName = "ContractSignatureCertChain";
namespace = "";
break;
case "DiffieHellmanPublickey":
messageName = "DHpublickey";
namespace = "";
break;
case "EMAID":
messageName = "eMAID";
namespace = "";
break;
case "ContractSignatureEncryptedPrivateKey":
messageName = "ContractSignatureEncryptedPrivateKey";
namespace = "";
break;
default:
break;
}
}
jaxbElement = new JAXBElement(new QName(namespace, messageName),
messageOrField.getClass(),
messageOrField);
return jaxbElement;
}
public Logger getLogger() {
return logger;
}
@ -223,4 +342,45 @@ public class MessageHandler {
public void setCommSessionContext(V2GCommunicationSession commSessionContext) {
this.commSessionContext = commSessionContext;
}
public JAXBContext getJaxbContext() {
return jaxbContext;
}
private void setJaxbContext(JAXBContext jaxbContext) {
this.jaxbContext = jaxbContext;
}
public void setJaxbContext(Class... classesToBeBound) {
try {
setJaxbContext(JAXBContext.newInstance(classesToBeBound));
// Every time we set the JAXBContext, we need to also set the marshaller and unmarshaller for EXICodec
getExiCodec().setUnmarshaller(getJaxbContext().createUnmarshaller());
getExiCodec().setMarshaller(getJaxbContext().createMarshaller());
/*
* JAXB by default silently ignores errors. Adding this code to throw an exception if
* something goes wrong.
*/
getExiCodec().getUnmarshaller().setEventHandler(
new ValidationEventHandler() {
@Override
public boolean handleEvent(ValidationEvent event ) {
throw new RuntimeException(event.getMessage(),
event.getLinkedException());
}
});
} catch (JAXBException e) {
getLogger().error("A JAXBException occurred while trying to set JAXB context", e);
}
}
public jaxbContextEnum getCurrentJaxbContext() {
return currentJaxbContext;
}
public void setCurrentJaxbContext(jaxbContextEnum currentJaxbContext) {
this.currentJaxbContext = currentJaxbContext;
}
}

View File

@ -32,7 +32,6 @@ import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.MessageHandler;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.SendMessage;
import org.v2gclarity.risev2g.shared.utils.MiscUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
@ -53,8 +52,7 @@ public abstract class State {
public abstract ReactionToIncomingMessage processIncomingMessage(Object message);
protected SendMessage getSendMessage(
public SendMessage getSendMessage(
BodyBaseType message,
V2GMessages nextExpectedMessage) {
int timeout = getTimeout(message, nextExpectedMessage);
@ -62,7 +60,7 @@ public abstract class State {
}
protected SendMessage getSendMessage(
public SendMessage getSendMessage(
BodyBaseType message,
V2GMessages nextExpectedMessage,
String optionalLoggerInfo) {
@ -71,7 +69,7 @@ public abstract class State {
}
private int getTimeout(BodyBaseType message, V2GMessages nextExpectedMessage) {
public int getTimeout(BodyBaseType message, V2GMessages nextExpectedMessage) {
String messageName = message.getClass().getSimpleName().replace("Type", "");
// If the sent message is a response message, 60s sequence timeout is used
@ -97,7 +95,10 @@ public abstract class State {
@SuppressWarnings({"unchecked"})
V2GMessage v2gMessage = getMessageHandler().getV2GMessage(
getXMLSignatureRefElements(), getSignaturePrivateKey(), MiscUtils.getJaxbElement(message));
getXMLSignatureRefElements(),
getSignaturePrivateKey(),
getCommSessionContext().getMessageHandler().getJaxbElement(message)
);
getLogger().debug("Preparing to send " + messageName + " " + optionalLoggerInfo);

View File

@ -25,8 +25,10 @@ package org.v2gclarity.risev2g.shared.misc;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.v2gclarity.risev2g.shared.utils.ByteUtils;
public class V2GTPMessage {
@ -47,9 +49,26 @@ public class V2GTPMessage {
* @param payloadType The type of the payload (EXI encoded message, SDP request or response)
* @param payload The payload of the message to be sent
*/
public V2GTPMessage(byte protocolVersion, byte[] payloadType, byte[] payloadLength, byte[] payload) {
setProtocolVersion(protocolVersion);
setInverseProtocolVersion((byte) (protocolVersion ^ 0xFF));
setPayloadType(payloadType);
setPayloadLength(payloadLength);
setPayload(payload);
}
/**
*
*
* @param protocolVersion
* @param payloadType
* @param payload
*/
public V2GTPMessage(byte protocolVersion, byte[] payloadType, byte[] payload) {
setProtocolVersion(protocolVersion);
setInverseProtocolVersion((byte) (protocolVersion ^ 0xFF));
setPayloadType(payloadType);
setPayloadLength(ByteUtils.toByteArrayFromInt(payload.length, false));
setPayload(payload);
}
@ -64,7 +83,9 @@ public class V2GTPMessage {
// Check if this could be a real V2GTPMessage which has 8 bytes of header
if (byteArray != null && byteArray.length >= 8) {
setProtocolVersion(Arrays.copyOfRange(byteArray, 0, 1)[0]);
setInverseProtocolVersion(Arrays.copyOfRange(byteArray, 1, 2)[0]);
setPayloadType(Arrays.copyOfRange(byteArray, 2, 4));
setPayloadLength(Arrays.copyOfRange(byteArray, 4, 8));
// TODO make sure the byteArray is not too long to not generate a Java heap space OutOfMemoryError
setPayload(Arrays.copyOfRange(byteArray, 8, byteArray.length));
} else {
@ -86,8 +107,8 @@ public class V2GTPMessage {
return inverseProtocolVersion;
}
private void setInverseProtocolVersion(byte protocolVersion) {
this.inverseProtocolVersion = (byte) (protocolVersion ^ 0xFF);
private void setInverseProtocolVersion(byte inverseProtocolVersion) {
this.inverseProtocolVersion = inverseProtocolVersion;
}
@ -97,9 +118,6 @@ public class V2GTPMessage {
public void setPayload(byte[] payload) {
this.payload = payload;
// Byte array reflecting the number of bytes of the payload
setPayloadLength(ByteBuffer.allocate(4).putInt(payload.length).array());
}
public byte[] getPayloadType() {

View File

@ -34,17 +34,12 @@ import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Properties;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EnergyTransferModeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentOptionType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignedInfoType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SupportedEnergyTransferModeType;
@ -270,30 +265,4 @@ public final class MiscUtils {
return false;
}
}
/**
* Creates an XML element from the given object which may be a complete message or just a field of a
* message. In case of XML signature generation, for some messages certain fields need to be signed
* instead of the complete message.
*
* Suppressed unchecked warning, previously used a type-safe version such as new
* JAXBElement<SessionStopReqType>(new QName ... ) but this seems to work as well
* (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 The JAXBElement of the provided message or field
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static JAXBElement getJaxbElement(Object messageOrField) {
String messageName = messageOrField.getClass().getSimpleName().replace("Type", "");
String namespace = "";
if (messageOrField instanceof SignedInfoType) namespace = GlobalValues.V2G_CI_XMLDSIG_NAMESPACE.toString();
else namespace = GlobalValues.V2G_CI_MSG_BODY_NAMESPACE.toString();
return new JAXBElement(new QName(namespace, messageName),
messageOrField.getClass(),
messageOrField);
}
}

View File

@ -102,6 +102,7 @@ import javax.xml.bind.JAXBElement;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.PKI;
import org.v2gclarity.risev2g.shared.exiCodec.ExiCodec;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CanonicalizationMethodType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
@ -111,6 +112,7 @@ import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DigestMethodType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EMAIDType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ListOfRootCertificateIDsType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ReferenceType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignatureMethodType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignatureType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignedInfoType;
@ -229,67 +231,60 @@ public final class SecurityUtils {
/**
* Checks whether the given certificate is currently valid.
* Checks whether the given certificate is currently valid with regards to date and time.
*
* @param certificate The X509Certificiate to be checked for validity
* @return True, if the current date lies within the notBefore and notAfter attribute of the
* certificate, false otherwise
* @return ResponseCode FAILED_CertificateExpired, if the certificate is expired. FAILED, if the certificate is
* not yet valid, since there is no other proper response code available. OK, otherwise.
*/
public static boolean isCertificateValid(X509Certificate certificate) {
public static ResponseCodeType verifyValidityPeriod(X509Certificate certificate) {
try {
certificate.checkValidity();
return true;
return ResponseCodeType.OK;
} catch (CertificateExpiredException e) {
X500Principal subject = certificate.getSubjectX500Principal();
getLogger().warn("Certificate with distinguished name '" + subject.getName().toString() +
"' already expired (not after " + certificate.getNotAfter().toString() + ")");
getLogger().warn("Certificate with distinguished name '" + subject.getName() +
"' already expired (not after " + certificate.getNotAfter() + ")");
return ResponseCodeType.FAILED_CERTIFICATE_EXPIRED;
} catch (CertificateNotYetValidException e) {
X500Principal subject = certificate.getSubjectX500Principal();
getLogger().warn("Certificate with distinguished name '" + subject.getName().toString() +
"' not yet valid (not before " + certificate.getNotBefore().toString() + ")");
getLogger().warn("Certificate with distinguished name '" + subject.getName() +
"' not yet valid (not before " + certificate.getNotBefore() + ")");
return ResponseCodeType.FAILED;
}
return false;
}
/**
*
* [V2G2-925] states:
* A leaf certificate shall be treated as invalid, if the trust anchor at the end of the chain does not
* match the specific root certificate required for a certain use, or if the required Domain
* Component value is not present.
*
* Domain Component restrictions:
* - SECC certificate: "CPO" (verification by EVCC)
* - provisioning certificate (signer certificate of a contract certificate: "CPS" (verification by EVCC)
* - OEM Provisioning Certificate: "OEM" (verification by provisioning service (not EVCC or SECC))
* Domain Component restrictions: <br/>
* - SECC certificate: "CPO" (verification by EVCC) <br/>
* - CPS leaf certificate: "CPS" (verification by EVCC) <br/>
* - OEM Provisioning Certificate: "OEM" (verification by provisioning service (neither EVCC nor SECC))
*
* @param certificate The X509Certificiate to be checked for validity
* @param domainComponent The domain component to be checked for in the distinguished name of the certificate
* @return True, if the current date lies within the notBefore and notAfter attribute of the
* certificate and the given domain component is present in the distinguished name, false otherwise
* @return True, if the given domain component is present in the distinguished name, false otherwise
*/
public static boolean isCertificateValid(X509Certificate certificate, String domainComponent) {
if (isCertificateValid(certificate)) {
String dn = certificate.getSubjectX500Principal().getName();
LdapName ln;
public static boolean verifyDomainComponent(X509Certificate certificate, String domainComponent) {
String dn = certificate.getSubjectX500Principal().getName();
LdapName ln;
try {
ln = new LdapName(dn);
try {
ln = new LdapName(dn);
for (Rdn rdn : ln.getRdns()) {
if (rdn.getType().equalsIgnoreCase("DC") && rdn.getValue().equals(domainComponent)) {
return true;
}
}
} catch (InvalidNameException e) {
getLogger().warn("InvalidNameException occurred while trying to check domain component of certificate", e);
for (Rdn rdn : ln.getRdns()) {
if (rdn.getType().equalsIgnoreCase("DC") && rdn.getValue().equals(domainComponent)) {
return true;
}
}
return false;
} else return false;
} catch (InvalidNameException e) {
getLogger().error("InvalidNameException occurred while trying to check domain component of certificate", e);
}
getLogger().error("Expected domain component (DC) '" + domainComponent + "' not found in certificate "
+ "with distinguished name '" + dn + "'");
return false;
}
@ -308,47 +303,163 @@ public final class SecurityUtils {
return (short) TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
}
/**
* Checks whether each certificate in the given certificate chain is currently valid.
* @param certChain The certificate chain to iterate over to check for validity
* @return True, if the current date lies within the notBefore and notAfter attribute of each
* certificate contained in the provided certificate chain, false otherwise
*/
public static boolean isCertificateChainValid(CertificateChainType certChain) {
if (certChain == null) {
getLogger().error("Certificate chain is NULL");
return false;
}
if (!isCertificateValid(getCertificate(certChain.getCertificate())))
return false;
SubCertificatesType subCertificates = certChain.getSubCertificates();
for (byte[] cert : subCertificates.getCertificate()) {
if (!isCertificateValid(getCertificate(cert))) return false;
}
return true;
}
/**
* Checks whether each certificate in the given certificate chain is currently valid and if a given
* domain component (DC) in the distinguished name is set.
*
* Executes the following validity checks:
* <br/><br/>
* 1. Verifies the signature for each certificate in the given certificate chain all the way up to the trust
* anchor. Certificates in certificate chain must be in the right order (leaf -> Sub-CA2 -> Sub-CA1) <br/>
* 2. Verifies whether the given certificate is currently valid with regards to date and time.<br/>
* 3. Verifies that certificate attributes are set correctly, depending on the PKI the certificate chain belongs to
*
* @param certChain The certificate chain to iterate over to check for validity
* @param domainComponent The domain component
* @return True, if the domain component is correctly set and if the current date lies within the notBefore
* and notAfter attribute of each certificate contained in the provided certificate chain,
* false otherwise
* @param trustStoreFileName The relative path and file name of the truststore
* @param pki The Public Key Infrastructure to which the certChain belongs (a PKI enumeration value)
* @return ResponseCode applicable to the verification steps
*/
public static boolean isCertificateChainValid(CertificateChainType certChain, String domainComponent) {
if (isCertificateChainValid(certChain)) {
if (isCertificateValid(getCertificate(certChain.getCertificate()), domainComponent)) return true;
else return false;
} else return false;
public static ResponseCodeType verifyCertificateChain(
CertificateChainType certChain,
String trustStoreFileName,
PKI pki) {
X509Certificate leafCertificate = null;
X509Certificate subCA1Certificate = null;
X509Certificate subCA2Certificate = null;
// Get leaf certificate
if (certChain != null) {
leafCertificate = getCertificate(certChain.getCertificate());
} else {
getLogger().error("Signature verification failed because provided certificate chain is empty (null)");
return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
}
// Get Sub-CA certificates
if (leafCertificate != null) {
SubCertificatesType subCertificates = certChain.getSubCertificates();
if (subCertificates != null && subCertificates.getCertificate().size() != 0) {
subCA2Certificate = getCertificate(subCertificates.getCertificate().get(0));
if (subCertificates.getCertificate().size() == 2)
subCA1Certificate = getCertificate(subCertificates.getCertificate().get(1));
} else {
getLogger().error("Signature verification failed because no Sub-CA certificates available in provided "
+ "certificate chain");
return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
}
} else {
getLogger().error("Signature verification failed because no leaf certificate available in provided "
+ "certificate chain");
return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
}
/*
* ****************
* SIGNATURE CHECKS
* ****************
*/
// Check signature of leaf certificate
if (!verifySignature(leafCertificate, subCA2Certificate)) return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
// Check signature of Sub-CA 2 and optionally, if present, Sub-CA 2 certificate
if (subCA1Certificate != null) {
if (!verifySignature(subCA2Certificate, subCA1Certificate)) return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
if (!verifySignature(subCA1Certificate, trustStoreFileName)) return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
} else {
// In case there is only one intermediate certificate (profile of Sub-CA 2)
if (!verifySignature(subCA2Certificate, trustStoreFileName)) return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
}
/*
* **********************
* VALIDITY PERIOD CHECKS
* **********************
*/
ResponseCodeType validityResponseCode = null;
// Check validity of leaf certificate
validityResponseCode = verifyValidityPeriod(leafCertificate);
if (!validityResponseCode.equals(ResponseCodeType.OK)) return validityResponseCode;
// Check validity of Sub-CA2 certificate
validityResponseCode = verifyValidityPeriod(subCA2Certificate);
if (!validityResponseCode.equals(ResponseCodeType.OK)) return validityResponseCode;
// Check validity of Sub-CA1 certificate, if present
if (subCA1Certificate != null) {
validityResponseCode = verifyValidityPeriod(subCA1Certificate);
if (!validityResponseCode.equals(ResponseCodeType.OK)) return validityResponseCode;
}
/*
* ***********************************
* COMMON CERTIFICATE ATTRIBUTES CHECK
* ***********************************
*/
// Check pathLenContraint (maximum number of non-self-issued intermediate certificates that may follow this certificate)
if (subCA2Certificate.getBasicConstraints() != 0) {
getLogger().error("Sub-CA 2 certificate with distinguished name '" +
subCA2Certificate.getSubjectX500Principal().getName() + "' has incorrect value for " +
"pathLenConstraint. Should be 0 instead of " + subCA2Certificate.getBasicConstraints());
return ResponseCodeType.FAILED_CERTIFICATE_EXPIRED;
}
if (subCA1Certificate != null && subCA1Certificate.getBasicConstraints() != 1) {
getLogger().error("Sub-CA 1 certificate with distinguished name '" +
subCA1Certificate.getSubjectX500Principal().getName() + "' has incorrect value for " +
"pathLenConstraint. Should be 1 instead of " + subCA2Certificate.getBasicConstraints());
return ResponseCodeType.FAILED_CERTIFICATE_EXPIRED;
}
/*
* *****************************************
* PKI SPECIFIC CERTIFICATE ATTRIBUTES CHECK
* *****************************************
*/
switch (pki) {
case CPO:
if (!verifyDomainComponent(leafCertificate, "CPO")) {
getLogger().error("SECC leaf certificate with distinguished name '" +
leafCertificate.getSubjectX500Principal().getName() + "' has incorrect value for " +
"domain component. Should be 'CPO'");
return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
}
break;
case CPS:
if (!verifyDomainComponent(leafCertificate, "CPS")) {
getLogger().error("CPS leaf certificate with distinguished name '" +
leafCertificate.getSubjectX500Principal().getName() + "' has incorrect value for " +
"domain component. Should be 'CPS'");
return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
}
break;
case MO:
if (!isEMAIDSynstaxValid(certChain)) {
return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
}
break;
case OEM:
if (!verifyDomainComponent(leafCertificate, "OEM")) {
getLogger().error("OEM provisioning certificate with distinguished name '" +
leafCertificate.getSubjectX500Principal().getName() + "' has incorrect value for " +
"domain component. Should be 'OEM'");
return ResponseCodeType.FAILED_CERT_CHAIN_ERROR;
}
break;
default:
break;
}
return ResponseCodeType.OK;
}
@ -361,7 +472,7 @@ public final class SecurityUtils {
* key with which the given certificate should have been signed
* @return True, if the verification was successful, false otherwise
*/
public static boolean isCertificateVerified(X509Certificate certificate, X509Certificate issuingCertificate) {
public static boolean verifySignature(X509Certificate certificate, X509Certificate issuingCertificate) {
X500Principal subject = certificate.getSubjectX500Principal();
X500Principal expectedIssuerSubject = certificate.getIssuerX500Principal();
X500Principal issuerSubject = issuingCertificate.getSubjectX500Principal();
@ -386,68 +497,14 @@ public final class SecurityUtils {
/**
* Verifies for each certificate in the given certificate chain that it was signed using the private key
* that corresponds to the public key of a certificate contained in the certificate chain or the truststore.
*
* @param trustStoreFileName The relative path and file name of the truststore
* @param certChain The certificate chain holding the leaf certificate and zero or more intermediate
* certificates (sub CAs)
* @return True, if the verification was successful, false otherwise
*/
public static boolean isCertificateChainVerified(String trustStoreFileName, CertificateChainType certChain) {
X509Certificate issuingCertificate = null;
if (certChain != null) {
X509Certificate leafCertificate = getCertificate(certChain.getCertificate());
if (leafCertificate != null) {
SubCertificatesType subCertificates = certChain.getSubCertificates();
if (subCertificates != null) {
// Sub certificates must be in the right order (leaf -> SubCA2 -> SubCA1 -> RootCA)
issuingCertificate = getCertificate(subCertificates.getCertificate().get(0));
if (!isCertificateVerified(leafCertificate, issuingCertificate)) return false;
for (int i=0; i < subCertificates.getCertificate().size(); i++) {
if ((i+1) < subCertificates.getCertificate().size()) {
issuingCertificate = getCertificate(subCertificates.getCertificate().get(i+1));
if (!isCertificateVerified(getCertificate(subCertificates.getCertificate().get(i)), issuingCertificate))
return false;
} else {
if (isCertificateTrusted(trustStoreFileName, getCertificate(subCertificates.getCertificate().get(i)))) return true;
else return false;
}
}
} else {
if (!isCertificateTrusted(trustStoreFileName, leafCertificate)) return false;
}
} else {
getLogger().error("No leaf certificate available in provided certificate chain, " +
"therefore no verification possible");
return false;
}
} else {
getLogger().error("Provided certificate chain is null, could therefore not be verified");
return false;
}
return false;
}
/**
* Iterates over the certificates stored in the truststore to check if one of the respective public
* keys of the certificates is the corresponding key to the private key with which the provided
* certificate has been signed.
* Iterates over the certificates stored in the truststore to verify the signature of the provided certificate
*
* @param trustStoreFilename The relative path and file name of the truststore
* @param certificate The certificate whose signature needs to be signed
* @return True, if the provided certificate has been signed by one of the certificates in the
* truststore, false otherwise
*/
public static boolean isCertificateTrusted(String trustStoreFilename, X509Certificate certificate) {
/*
* Use one of the root certificates in the truststore to verify the signature of the
* last certificate in the chain
*/
public static boolean verifySignature(X509Certificate certificate, String trustStoreFilename) {
KeyStore trustStore = SecurityUtils.getTrustStore(trustStoreFilename, GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString());
X500Principal expectedIssuer = certificate.getIssuerX500Principal();
@ -456,7 +513,7 @@ public final class SecurityUtils {
while (aliases.hasMoreElements()) {
X509Certificate rootCA = (X509Certificate) trustStore.getCertificate(aliases.nextElement());
if (rootCA.getSubjectX500Principal().getName().equals(expectedIssuer.getName()) &&
isCertificateVerified(certificate, rootCA)) return true;
verifySignature(certificate, rootCA)) return true;
}
} catch (KeyStoreException | NullPointerException e) {
getLogger().error(e.getClass().getSimpleName() + " occurred while trying to verify trust " +
@ -1012,9 +1069,7 @@ public final class SecurityUtils {
if (contractCert == null) {
getLogger().info("No contract certificate stored");
return true;
} else if (contractCert != null && !isCertificateValid(contractCert)) {
getLogger().info("Stored contract certificate with distinguished name '" +
contractCert.getSubjectX500Principal().getName() + "' is not valid");
} else if (!verifyValidityPeriod(contractCert).equals(ResponseCodeType.OK)) {
return true;
} else return false;
}
@ -1064,9 +1119,7 @@ public final class SecurityUtils {
if (contractCert == null) {
getLogger().info("No contract certificate stored");
return ContractCertificateStatus.INSTALLATION_NEEDED;
} else if (contractCert != null && !isCertificateValid(contractCert)) {
getLogger().info("Stored contract certificate with distinguished name '" +
contractCert.getSubjectX500Principal().getName() + "' is not valid");
} else if (contractCert != null && !verifyValidityPeriod(contractCert).equals(ResponseCodeType.OK)) {
return ContractCertificateStatus.INSTALLATION_NEEDED;
} else {
short validityOfContractCert = getValidityPeriod(contractCert);
@ -1400,7 +1453,7 @@ public final class SecurityUtils {
byte[] contractSignatureEncryptedPrivateKey,
ECPrivateKey certificateECPrivateKey) {
// Generate shared secret
ECPublicKey publicKey = (ECPublicKey) getPublicKey(dhPublicKey);
ECPublicKey publicKey = getPublicKey(dhPublicKey);
byte[] sharedSecret = generateSharedSecret(certificateECPrivateKey, publicKey);
if (sharedSecret == null) {
getLogger().error("Shared secret could not be generated");
@ -1619,12 +1672,11 @@ public final class SecurityUtils {
* the second parameter is to be set to true, for all other messages or fields the second parameter
* needs to be set to false.
*
* @param messageOrField The message or field for which a digest is to be generated
* @param jaxbMessageOrField The message or field for which a digest is to be generated, given as a JAXB element
* @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) {
JAXBElement jaxbElement = MiscUtils.getJaxbElement(messageOrField);
public static byte[] generateDigest(JAXBElement jaxbMessageOrField) {
byte[] encoded;
// The schema-informed fragment grammar option needs to be used for EXI encodings in the header's signature
@ -1634,8 +1686,9 @@ public final class SecurityUtils {
* 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 (messageOrField instanceof SignedInfoType) encoded = getExiCodec().encodeEXI(jaxbElement, GlobalValues.SCHEMA_PATH_XMLDSIG.toString());
else encoded = getExiCodec().encodeEXI(jaxbElement, GlobalValues.SCHEMA_PATH_MSG_DEF.toString());
if (jaxbMessageOrField.getValue() instanceof SignedInfoType) {
encoded = getExiCodec().encodeEXI(jaxbMessageOrField, GlobalValues.SCHEMA_PATH_XMLDSIG.toString());
} else encoded = getExiCodec().encodeEXI(jaxbMessageOrField, GlobalValues.SCHEMA_PATH_MSG_DEF.toString());
// Do not use the schema-informed fragment grammar option for other EXI encodings (message bodies)
getExiCodec().setFragment(false);
@ -1655,9 +1708,9 @@ public final class SecurityUtils {
* Show Base64 encoding of digests only for reference elements, not for the SignedInfo element.
* The hashed SignedInfo element is input for ECDSA before the final signature value gets Base64 encoded.
*/
if ( !(messageOrField instanceof SignedInfoType) ) {
if ( !(jaxbMessageOrField.getValue() instanceof SignedInfoType) ) {
getLogger().debug("\n"
+ "\tDigest generated for reference element " + messageOrField.getClass().getSimpleName() + ": " + ByteUtils.toHexString(digest) + "\n"
+ "\tDigest generated for reference element " + jaxbMessageOrField.getClass().getSimpleName() + ": " + ByteUtils.toHexString(digest) + "\n"
+ "\tBase64 encoding of digest: " + Base64.getEncoder().encodeToString(digest));
}
}
@ -1700,6 +1753,7 @@ public final class SecurityUtils {
* Verifies the signature given in the received header of an EVCC or SECC message
*
* @param signature The received header's signature
* @param jaxbSignature The received header's signature, given as a JAXB element (needed for EXI operations)
* @param verifyXMLSigRefElements The HashMap of signature IDs and digest values of the message body
* or fields respectively of the received message (to cross-check against the XML reference
* elements contained in the received message header)
@ -1711,16 +1765,18 @@ public final class SecurityUtils {
*/
public static boolean verifySignature(
SignatureType signature,
JAXBElement jaxbSignature,
HashMap<String, byte[]> verifyXMLSigRefElements,
byte[] verifyCert) {
X509Certificate x509VerifyCert = getCertificate(verifyCert);
return verifySignature(signature, verifyXMLSigRefElements, x509VerifyCert);
return verifySignature(signature, jaxbSignature, verifyXMLSigRefElements, x509VerifyCert);
}
/**
* Verifies the signature given in the received header of an EVCC or SECC message
*
* @param signature The received header's signature
* @param jaxbSignature The received header's signature, given as a JAXB element (needed for EXI operations)
* @param verifyXMLSigRefElements The HashMap of signature IDs and digest values of the message body
* or fields respectively of the received message (to cross-check against the XML reference
* elements contained in the received message header)
@ -1729,7 +1785,8 @@ public final class SecurityUtils {
* successful, false otherwise
*/
public static boolean verifySignature(
SignatureType signature,
SignatureType signature,
JAXBElement jaxbSignedInfo,
HashMap<String, byte[]> verifyXMLSigRefElements,
X509Certificate verifyCert) {
byte[] calculatedReferenceDigest;
@ -1791,14 +1848,14 @@ public final class SecurityUtils {
// Check if signature verification logging is to be shown (for debug purposes)
if (showSignatureVerificationLog) showSignatureVerificationLog(verifyCert, signature, ecPublicKey);
if (showSignatureVerificationLog) showSignatureVerificationLog(verifyCert, signature, jaxbSignedInfo, ecPublicKey);
ecdsa = Signature.getInstance("SHA256withECDSA");
// The Signature object needs to be initialized by setting it into the VERIFY state with the public key
ecdsa.initVerify(ecPublicKey);
// The data to be signed needs to be supplied to the Signature object
byte[] exiEncodedSignedInfo = getExiCodec().getExiEncodedSignedInfo(signature.getSignedInfo());
byte[] exiEncodedSignedInfo = getExiCodec().getExiEncodedSignedInfo(jaxbSignedInfo);
ecdsa.update(exiEncodedSignedInfo);
// Java operates on DER encoded signature values, but the sent signature consists of the raw r and s value
@ -1823,8 +1880,12 @@ public final class SecurityUtils {
* @param signature The signature contained in the header of the V2GMessage
* @param ecPublicKey The public key used to verify the signature
*/
private static void showSignatureVerificationLog(X509Certificate verifyCert, SignatureType signature, ECPublicKey ecPublicKey) {
byte[] computedSignedInfoDigest = generateDigest(signature.getSignedInfo());
private static void showSignatureVerificationLog(
X509Certificate verifyCert,
SignatureType signature,
JAXBElement jaxbSignedInfo,
ECPublicKey ecPublicKey) {
byte[] computedSignedInfoDigest = generateDigest(jaxbSignedInfo);
byte[] receivedSignatureValue = signature.getSignatureValue().getValue();
getLogger().debug("\n"
@ -1866,7 +1927,7 @@ public final class SecurityUtils {
/**
* An ECDSA signature consists of two integers s and r, each of the bit length equal to the curve size.
* An ECDSA signature consists of two integers r and s, each of the bit length equal to the curve size.
* When Java is creating an ECDSA signature, it is encoding it in the DER (Distinguished Encoding Rules) format.
* But in ISO 15118, we do not expect DER encoded signatures. Thus, this function takes the DER encoded signature
* as input and returns the raw r and s integer values of the signature.
@ -1880,17 +1941,21 @@ public final class SecurityUtils {
byte[] s = new byte[32];
// Length of r is encoded in the fourth byte (either 32 (hex: 0x20) or 33 (hex: 0x21))
int lengthOfR = (int) derEncodedSignature[3];
int lengthOfR = derEncodedSignature[3];
// Length of r is encoded in the second byte AFTER r (either 32 (hex: 0x20) or 33 (hex: 0x21))
int lengthOfS = (int) derEncodedSignature[lengthOfR + 5];
int lengthOfS = derEncodedSignature[lengthOfR + 5];
// If r is made up of 33 bytes, then we need to skip the first fill byte (0x00) of r
if (lengthOfR == 33) System.arraycopy(derEncodedSignature, 5, r, 0, 32);
else System.arraycopy(derEncodedSignature, 4, r, 0, 32);
// If r is made up of 33 bytes (hex value 0x21), then we need to skip the first fill byte (0x00) or r
if (lengthOfS == 33) System.arraycopy(derEncodedSignature, lengthOfR + 7, s, 0, 32);
else System.arraycopy(derEncodedSignature, lengthOfR + 6, s, 0, 32);
try {
// If r is made up of 33 bytes, then we need to skip the first fill byte (0x00) of r
if (lengthOfR == 33) System.arraycopy(derEncodedSignature, 5, r, 0, 32);
else System.arraycopy(derEncodedSignature, 4, r, 0, 32);
// If r is made up of 33 bytes (hex value 0x21), then we need to skip the first fill byte (0x00) or r
if (lengthOfS == 33) System.arraycopy(derEncodedSignature, lengthOfR + 7, s, 0, 32);
else System.arraycopy(derEncodedSignature, lengthOfR + 6, s, 0, 32);
} catch (ArrayIndexOutOfBoundsException e) {
getLogger().warn("ArrayIndexOutOfBoundsException occurred while trying to get raw signature from DER encoded signature", e);
}
try {
baos.write(r);
@ -2036,6 +2101,54 @@ public final class SecurityUtils {
}
}
/**
* Checks the syntax of the EMAID according to Annex H.1 of ISO 15118-2
*
* @param certChain The contract certificate chain. The EMAID is read from the contract certificate's common name
* @return True, if the syntax is valid, false otherwise
*/
public static boolean isEMAIDSynstaxValid(CertificateChainType certChain) {
String emaid = getEMAID(certChain).getValue().toUpperCase();
if (emaid.length() < 14 || emaid.length() > 18) {
getLogger().error("EMAID is invalid. Its length (" + emaid.length() + ") mus be between "
+ "14 (min, excluding separators) and 18 (max, including separators)");
return false;
}
String emaidWithoutSeparator = emaid.replace("-", "");
// Check country code
if (Character.isDigit(emaidWithoutSeparator.charAt(0)) || Character.isDigit(emaidWithoutSeparator.charAt(1))) {
getLogger().error("EMAID (" + emaid + ") is invalid, the first two characters must not be a digit");
return false;
}
// Check provider ID
if (! (Character.isLetterOrDigit(emaidWithoutSeparator.charAt(2)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(3)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(4))) ) {
getLogger().error("EMAID (" + emaid + ") is invalid, the provider ID must be alpha-numerical");
return false;
}
// Check emaInstance
if (! (Character.isLetterOrDigit(emaidWithoutSeparator.charAt(5)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(6)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(7)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(8)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(9)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(10)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(11)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(12)) &&
Character.isLetterOrDigit(emaidWithoutSeparator.charAt(13))) ) {
getLogger().error("EMAID (" + emaid + ") is invalid, the eMA instance must be alpha-numerical");
return false;
}
return true;
}
public static void setExiCodec(ExiCodec exiCodecChoice) {
exiCodec = exiCodecChoice;
}

View File

@ -1582,7 +1582,7 @@ public class ObjectFactory {
*/
@XmlElementDecl(namespace = "http://www.w3.org/2000/09/xmldsig#", name = "DigestValue")
public JAXBElement<byte[]> createDigestValue(byte[] value) {
return new JAXBElement<byte[]>(_DigestValue_QNAME, byte[].class, null, ((byte[]) value));
return new JAXBElement<byte[]>(_DigestValue_QNAME, byte[].class, null, (value));
}
/**
@ -1654,7 +1654,7 @@ public class ObjectFactory {
*/
@XmlElementDecl(namespace = "http://www.w3.org/2000/09/xmldsig#", name = "SPKISexp", scope = SPKIDataType.class)
public JAXBElement<byte[]> createSPKIDataTypeSPKISexp(byte[] value) {
return new JAXBElement<byte[]>(_SPKIDataTypeSPKISexp_QNAME, byte[].class, SPKIDataType.class, ((byte[]) value));
return new JAXBElement<byte[]>(_SPKIDataTypeSPKISexp_QNAME, byte[].class, SPKIDataType.class, (value));
}
/**
@ -1681,7 +1681,7 @@ public class ObjectFactory {
*/
@XmlElementDecl(namespace = "http://www.w3.org/2000/09/xmldsig#", name = "X509CRL", scope = X509DataType.class)
public JAXBElement<byte[]> createX509DataTypeX509CRL(byte[] value) {
return new JAXBElement<byte[]>(_X509DataTypeX509CRL_QNAME, byte[].class, X509DataType.class, ((byte[]) value));
return new JAXBElement<byte[]>(_X509DataTypeX509CRL_QNAME, byte[].class, X509DataType.class, (value));
}
/**
@ -1699,7 +1699,7 @@ public class ObjectFactory {
*/
@XmlElementDecl(namespace = "http://www.w3.org/2000/09/xmldsig#", name = "X509SKI", scope = X509DataType.class)
public JAXBElement<byte[]> createX509DataTypeX509SKI(byte[] value) {
return new JAXBElement<byte[]>(_X509DataTypeX509SKI_QNAME, byte[].class, X509DataType.class, ((byte[]) value));
return new JAXBElement<byte[]>(_X509DataTypeX509SKI_QNAME, byte[].class, X509DataType.class, (value));
}
/**
@ -1708,7 +1708,7 @@ public class ObjectFactory {
*/
@XmlElementDecl(namespace = "http://www.w3.org/2000/09/xmldsig#", name = "X509Certificate", scope = X509DataType.class)
public JAXBElement<byte[]> createX509DataTypeX509Certificate(byte[] value) {
return new JAXBElement<byte[]>(_X509DataTypeX509Certificate_QNAME, byte[].class, X509DataType.class, ((byte[]) value));
return new JAXBElement<byte[]>(_X509DataTypeX509Certificate_QNAME, byte[].class, X509DataType.class, (value));
}
/**
@ -1717,7 +1717,7 @@ public class ObjectFactory {
*/
@XmlElementDecl(namespace = "http://www.w3.org/2000/09/xmldsig#", name = "PGPKeyID", scope = PGPDataType.class)
public JAXBElement<byte[]> createPGPDataTypePGPKeyID(byte[] value) {
return new JAXBElement<byte[]>(_PGPDataTypePGPKeyID_QNAME, byte[].class, PGPDataType.class, ((byte[]) value));
return new JAXBElement<byte[]>(_PGPDataTypePGPKeyID_QNAME, byte[].class, PGPDataType.class, (value));
}
/**
@ -1726,7 +1726,7 @@ public class ObjectFactory {
*/
@XmlElementDecl(namespace = "http://www.w3.org/2000/09/xmldsig#", name = "PGPKeyPacket", scope = PGPDataType.class)
public JAXBElement<byte[]> createPGPDataTypePGPKeyPacket(byte[] value) {
return new JAXBElement<byte[]>(_PGPDataTypePGPKeyPacket_QNAME, byte[].class, PGPDataType.class, ((byte[]) value));
return new JAXBElement<byte[]>(_PGPDataTypePGPKeyPacket_QNAME, byte[].class, PGPDataType.class, (value));
}
}