Changelog commit 15.9.17

- Added copyNewCertsAndKeys.bat and generateCertificates.bat to RISE-V2G-Certificates in order to port the already existing Unix shell scripts to Windows systems
- Corrected an error in V2GCommunicationSessionHandlerEVCC.java which ensures that the SDP procedure continues to work correctly even if there is a false SDP response coming from the SECC
- Changed the signature of the method generateDigest() (in SecurityUtils.java) to have more logging information about the ID of the element for which the digest is generated
- Made sure that the PreCharge timeout and CableCheck timeout are triggered exactly (there was a possible delay of 2s)
- Changed logging level of TCP server and client from debug to info
- Added debugging logging functionality with new EVCC and SECC property HexAndBase64RepresentationOfMessages which will log the hexadecimal and Base64 encoding of each message being sent
- Small change in log4j2.xml files enabling highlighting of logging level (you need to have ANSI Console installed in Eclipse IDE for this change to take effect)
- Shifted the process of reading the MO Sub-CA 2 private key from the keystore from the method getMOSubCA2PrivateKey to the constructor of DummyBackendInterface.java in order to avoid timeouts when processing the ChargeParameterDiscoveryReq on embedded devices
- Updated the EXIficient_V2G_CI_MsgDef.java (serialized version of the XSD grammar) as there was an bug before that caused an incomplete deserialization of an EXI byte stream to an XML message
- Updated log4j from 2.1 to 2.9
This commit is contained in:
Marc Mültin 2017-09-15 16:15:25 +02:00
parent 75cb9ed392
commit d8c9fc7bcd
38 changed files with 3206 additions and 4972 deletions

View File

@ -0,0 +1,12 @@
@echo off
REM 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.
copy keystores\evccKeystore.jks ..\RISE-V2G-EVCC
copy keystores\evccTruststore.jks ..\RISE-V2G-EVCC
copy keystores\seccKeystore.jks ..\RISE-V2G-SECC
copy keystores\seccTruststore.jks ..\RISE-V2G-SECC
copy certs\cpsCertChain.p12 ..\RISE-V2G-SECC
copy certs\moCertChain.p12 ..\RISE-V2G-SECC
copy privateKeys\moSubCA2.pkcs8.der ..\RISE-V2G-SECC

View File

@ -0,0 +1,223 @@
@echo off
REM *******************************************************************************
REM The MIT License (MIT)
REM
REM Copyright (c) 2015-207 V2G Clarity (Dr.-Ing. Marc Mültin)
REM
REM Permission is hereby granted, free of charge, to any person obtaining a copy
REM of this software and associated documentation files (the "Software"), to deal
REM in the Software without restriction, including without limitation the rights
REM to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
REM copies of the Software, and to permit persons to whom the Software is
REM furnished to do so, subject to the following conditions:
REM
REM The above copyright notice and this permission notice shall be included in
REM all copies or substantial portions of the Software.
REM
REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
REM IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
REM FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
REM AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
REM LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
REM OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
REM THE SOFTWARE.
REM *******************************************************************************
REM This shell script can be used to create all necessary certificates and keystores needed in order to
REM - successfully perform a TLS handshake between the EVCC (TLSClient) and the SECC (TLSServer) and
REM - install/update a contract certificate in the EVCC.
REM Previously created certificates should have been provided with the respective release of the RISE V2G project for testing purposes. However, certain certificates might not be valid any more in which case you need to create new certificates.
REM This file shall serve you with all information needed to create your own certificate chains.
REM
REM Helpful information about using openssl is provided by Ivan Ristic's book "Bulletproof SSL and TLS".
REM Furthermore, you should have openssl 1.0.2 (or above) installed to comply with all security requirements imposed by ISO 15118. For example, openssl 0.9.8 does not come with SHA-2 for SHA-256 signature algorithms. Some MacOS X installations unfortunately still use openssl < v1.0.2. You could use Homebrew to install openssl. Be aware that you probably then need to use an absolute path for your openssl commands, such as /usr/local/Cellar/openssl/1.0.2h_1/bin/openssl (for linux based systems).
REM
REM Author: Marc Mültin (marc.mueltin@v2g-clarity.com)
REM Some variables to create different outcomes of the PKI for testing purposes. Change the validity periods (given in number of days) to test
REM - valid certificates (e.g. contract certificate or Sub-CA certificate)
REM - expired certificates (e.g. contract certificate or Sub-CA certificates) -> you need to reset your system time to the past to create expired certificates
REM - a to be updated contract certificate
SET validity_contract_cert=730
SET validity_mo_subca1_cert=1460
SET validity_mo_subca2_cert=1460
SET validity_oem_prov_cert=1460
SET validity_oem_subca1_cert=1460
SET validity_oem_subca2_cert=1460
SET validity_cps_leaf_cert=90
SET validity_cps_subca1_cert=1460
SET validity_cps_subca2_cert=730
SET validity_secc_cert=60
SET validity_cpo_subca1_cert=1460
SET validity_cpo_subca2_cert=365
SET validity_v2g_root_cert=3650
SET validity_oem_root_cert=3650
SET validity_mo_root_cert=3650
REM 0) Create directories if not yet existing
if not exist certs mkdir certs
if not exist csrs mkdir csrs
if not exist keystores mkdir keystores
if not exist privateKeys mkdir privateKeys
REM 1) Create a self-signed V2GRootCA certificate
REM 1.1) Create a
REM - private key -> -genkey
REM - with elliptic curve parameters -> ecparam
REM - for key of length 256 bit to be used for digital signatures -> -name secp256r1
REM - with symmetric encryption AES 128 bit -> -aes128
REM - and the passphrase for the private key provided in a file -> -passout file:passphrase.txt
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\v2gRootCA.key -aes128 -passout file:passphrase.txt
REM 1.2) Create a
REM - new -> -new
REM - self-signed certificate -> -new -x509 (and -out v2gRootCA.pem)
REM - valid for 40 years -> -days 14600
REM - with signature algorithm sha256 -> -sha256
REM - with previously created private key -> -key privateKeys\v2gRootCA.key
REM - and configuration data provided -> -config configs\v2gRootCACert.cnf
REM - with extensions specified in section [ext] -> -extensions ext
openssl req -new -x509 -days %validity_v2g_root_cert% -sha256 -key privateKeys\v2gRootCA.key -set_serial 01 -passin file:passphrase.txt -config configs\v2gRootCACert.cnf -extensions ext -out certs\v2gRootCA.pem
REM 2) Create an intermediate CPO sub-CA certificate which is directly signed by the V2GRootCA certificate
REM 2.1) Create a private key (same procedure as for V2GRootCA certificate)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\cpoSubCA1.key -aes128 -passout file:passphrase.txt
REM 2.2) Create a
REM - new Certificate Signing Request (CSR) -> -new (and -out cpoSubCA1.csr)
REM - with previously created private key -> -key privateKeys\cpoSubCA1.key
REM - and configuration data provided -> -config configs\cpoSubCA1Cert.cnf
REM - with extensions specified in section [ext] -> -extensions ext
openssl req -new -key privateKeys\cpoSubCA1.key -passin file:passphrase.txt -config configs\cpoSubCA1Cert.cnf -extensions ext -out csrs\cpoSubCA1.csr
REM 2.3) Create a
REM - certificate for the CPOSubCA1 -> x509
REM - with the previously created CSR -> -in csrs\cpoSubCA1.csr
REM - signed by the V2GRootCA's private key -> -signkey privateKeys\v2gRootCA.key
REM - with a validity of 4 years -> -days 1460
openssl x509 -req -in csrs\cpoSubCA1.csr -extfile configs\cpoSubCA1Cert.cnf -extensions ext -CA certs\v2gRootCA.pem -CAkey privateKeys\v2gRootCA.key -set_serial 02 -passin file:passphrase.txt -days %validity_cpo_subca1_cert% -out certs\cpoSubCA1.pem
REM 3) Create a second intermediate CPO sub-CA certificate just the way the previous intermedia certificate was created which is directly signed by the CPOSubCA1
REM Differences to CPOSubCA1
REM - basicConstraints in config file sets pathlength to 0 (meaning that no further sub CA's certificate may be signed with this certificate, a leaf certificate must follow this certificate in a certificate chain)
REM - validity is set to 1 year (1 - 2 years are allowed according to ISO 15118)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\cpoSubCA2.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\cpoSubCA2.key -passin file:passphrase.txt -config configs\cpoSubCA2Cert.cnf -extensions ext -out csrs\cpoSubCA2.csr
openssl x509 -req -in csrs\cpoSubCA2.csr -extfile configs\cpoSubCA2Cert.cnf -extensions ext -CA certs\cpoSubCA1.pem -CAkey privateKeys\cpoSubCA1.key -set_serial 03 -passin file:passphrase.txt -days %validity_cpo_subca2_cert% -out certs\cpoSubCA2.pem
REM 4) Create an SECCCert certificate which is the leaf certificate belonging to the charging station which authenticates itself to the EVCC during a TLS handshake, signed by CPOSubCA2 certificate
REM Differences to CPOSubCA1 and CPOSubCA2
REM - basicConstraints sets CA to false, no pathlen is therefore set
REM - keyusage is set to digitalSignature instead of keyCertSign and cRLSign
REM - validity is set to 60 days (2 - 3 months are allowed according to ISO 15118)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\seccCert.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\seccCert.key -passin file:passphrase.txt -config configs\seccCert.cnf -extensions ext -out csrs\seccCert.csr
openssl x509 -req -in csrs\seccCert.csr -extfile configs\seccCert.cnf -extensions ext -CA certs\cpoSubCA2.pem -CAkey privateKeys\cpoSubCA2.key -set_serial 04 -passin file:passphrase.txt -days %validity_secc_cert% -out certs\seccCert.pem
REM Concatenate the intermediate CAs into one file intermediateCAs.pem
REM IMPORTANT: Concatenate in such a way that the chain leads from the leaf certificate to the root (excluding), this means here: first parameter of the type command is the intermediate CA's certificate which signs the leaf certificate (in this case cpoSubCA2.pem). Otherwise the Java method getCertificateChain() which is called on the keystore will only return the leaf certificate!
type certs\cpoSubCA2.pem certs\cpoSubCA1.pem > certs\intermediateCPOCAs.pem
REM Put the seccCertificate, the private key of the seccCertificate as well as the intermediate CAs in a pkcs12 container.
REM IMPORTANT: It is necessary to put all necessary intermediate CAs directly into the PKCS12 container (with the -certfile switch), instead of later on iporting the PKCS12 containter only holding the leaf certificate (seccCert) and its private key and additionally importing the intermediate CAs via the keytool command (TLS handshake will fail).
REM This is the reason why we need two password files (passphrase.txt and passphrase2.txt). Possibly the passphrase.txt file resource is locked before being accessed a second time within the same command? See also http://rt.openssl.org/Ticket/Display.html?id=3168&user=guest&pass=guest
REM The -name switch corresponds to the -alias switch in the keytool command later on
openssl pkcs12 -export -inkey privateKeys\seccCert.key -in certs\seccCert.pem -certfile certs\intermediateCPOCAs.pem -aes128 -passin file:passphrase.txt -passout file:passphrase2.txt -name secc_cert -out certs\cpoCertChain.p12
REM 5) Create a self-signed OEMRootCA certificate (validity is up to the OEM, this example applies the same validity as the V2GRootCA)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\oemRootCA.key -aes128 -passout file:passphrase.txt
openssl req -new -x509 -days %validity_oem_root_cert% -sha256 -key privateKeys\oemRootCA.key -set_serial 05 -passin file:passphrase.txt -config configs\oemRootCACert.cnf -extensions ext -out certs\oemRootCA.pem
REM 6) Create an intermediate OEM sub-CA certificate which is directly signed by the OEMRootCA certificate (validity is up to the OEM, this example applies the same validity as the CPOSubCA1)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\oemSubCA1.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\oemSubCA1.key -passin file:passphrase.txt -config configs\oemSubCA1Cert.cnf -extensions ext -out csrs\oemSubCA1.csr
openssl x509 -req -in csrs\oemSubCA1.csr -extfile configs\oemSubCA1Cert.cnf -extensions ext -CA certs\oemRootCA.pem -CAkey privateKeys\oemRootCA.key -set_serial 06 -passin file:passphrase.txt -days %validity_oem_subca1_cert% -out certs\oemSubCA1.pem
REM 7) Create a second intermediate OEM sub-CA certificate which is directly signed by the OEMSubCA1 certificate (validity is up to the OEM, this example applies the same validity as the CPOSubCA2)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\oemSubCA2.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\oemSubCA2.key -passin file:passphrase.txt -config configs\oemSubCA2Cert.cnf -extensions ext -out csrs\oemSubCA2.csr
openssl x509 -req -in csrs\oemSubCA2.csr -extfile configs\oemSubCA2Cert.cnf -extensions ext -CA certs\oemSubCA1.pem -CAkey privateKeys\oemSubCA1.key -set_serial 07 -passin file:passphrase.txt -days %validity_oem_subca2_cert% -out certs\oemSubCA2.pem
REM 8) Create an OEM provisioning certificate which is the leaf certificate belonging to the OEM certificate chain (used for contract certificate installation)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\oemProvCert.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\oemProvCert.key -passin file:passphrase.txt -config configs\oemProvCert.cnf -extensions ext -out csrs\oemProvCert.csr
openssl x509 -req -in csrs\oemProvCert.csr -extfile configs\oemProvCert.cnf -extensions ext -CA certs\oemSubCA2.pem -CAkey privateKeys\oemSubCA2.key -set_serial 08 -passin file:passphrase.txt -days %validity_oem_prov_cert% -out certs\oemProvCert.pem
type certs\oemSubCA2.pem certs\oemSubCA1.pem > certs\intermediateOEMCAs.pem
openssl pkcs12 -export -inkey privateKeys\oemProvCert.key -in certs\oemProvCert.pem -certfile certs\intermediateOEMCAs.pem -aes128 -passin file:passphrase.txt -passout file:passphrase2.txt -name oem_prov_cert -out certs\oemCertChain.p12
REM 9) Create a self-signed MORootCA (mobility operator) certificate (validity is up to the MO, this example applies the same validity as the V2GRootCA)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\moRootCA.key -aes128 -passout file:passphrase.txt
openssl req -new -x509 -days %validity_mo_root_cert% -sha256 -key privateKeys\moRootCA.key -set_serial 09 -passin file:passphrase.txt -config configs\moRootCACert.cnf -extensions ext -out certs\moRootCA.pem
REM 10) Create an intermediate MO sub-CA certificate which is directly signed by the MORootCA certificate (validity is up to the MO, this example applies the same validity as the CPOSubCA1)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\moSubCA1.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\moSubCA1.key -passin file:passphrase.txt -config configs\moSubCA1Cert.cnf -extensions ext -out csrs\moSubCA1.csr
openssl x509 -req -in csrs\moSubCA1.csr -extfile configs\moSubCA1Cert.cnf -extensions ext -CA certs\moRootCA.pem -CAkey privateKeys\moRootCA.key -set_serial 10 -passin file:passphrase.txt -days %validity_mo_subca1_cert% -out certs\moSubCA1.pem
REM 11) Create a second intermediate MO sub-CA certificate which is directly signed by the MOSubCA1 certificate (validity is up to the MO, this example applies the same validity as the CPOSubCA2)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\moSubCA2.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\moSubCA2.key -passin file:passphrase.txt -config configs\moSubCA2Cert.cnf -extensions ext -out csrs\moSubCA2.csr
openssl x509 -req -in csrs\moSubCA2.csr -extfile configs\moSubCA2Cert.cnf -extensions ext -CA certs\moSubCA1.pem -CAkey privateKeys\moSubCA1.key -set_serial 11 -passin file:passphrase.txt -days %validity_mo_subca2_cert% -out certs\moSubCA2.pem
REM 12) Create a contract certificate which is the leaf certificate belonging to the MO certificate chain (used for contract certificate installation)
REM Validity can be between 4 weeks and 2 years (restricted by the contract lifetime), for testing purposes the validity will be set to 2 years
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\contractCert.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\contractCert.key -passin file:passphrase.txt -config configs\contractCert.cnf -extensions ext -out csrs\contractCert.csr
openssl x509 -req -in csrs\contractCert.csr -extfile configs\contractCert.cnf -extensions ext -CA certs\moSubCA2.pem -CAkey privateKeys\moSubCA2.key -set_serial 12 -passin file:passphrase.txt -days %validity_contract_cert% -out certs\contractCert.pem
type certs\moSubCA2.pem certs\moSubCA1.pem > certs\intermediateMOCAs.pem
openssl pkcs12 -export -inkey privateKeys\contractCert.key -in certs\contractCert.pem -certfile certs\intermediateMOCAs.pem -aes128 -passin file:passphrase.txt -passout file:passphrase2.txt -name contract_cert -out certs\moCertChain.p12
REM 13) Create an intermediate provisioning service sub-CA certificate which is directly signed by the V2GRootCA certificate
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\cpsSubCA1.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\cpsSubCA1.key -passin file:passphrase.txt -config configs\cpsSubCA1Cert.cnf -extensions ext -out csrs\cpsSubCA1.csr
openssl x509 -req -in csrs\cpsSubCA1.csr -extfile configs\cpsSubCA1Cert.cnf -extensions ext -CA certs\v2gRootCA.pem -CAkey privateKeys\v2gRootCA.key -set_serial 13 -passin file:passphrase.txt -days %validity_cps_subca1_cert% -out certs\cpsSubCA1.pem
REM 14) Create a second intermediate provisioning sub-CA certificate which is directly signed by the CPSSubCA1 certificate (validity 1 - 2 years, we make it 2 years)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\cpsSubCA2.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\cpsSubCA2.key -passin file:passphrase.txt -config configs\cpsSubCA2Cert.cnf -extensions ext -out csrs\cpsSubCA2.csr
openssl x509 -req -in csrs\cpsSubCA2.csr -extfile configs\cpsSubCA2Cert.cnf -extensions ext -CA certs\cpsSubCA1.pem -CAkey privateKeys\cpsSubCA1.key -set_serial 14 -passin file:passphrase.txt -days %validity_cps_subca2_cert% -out certs\cpsSubCA2.pem
REM 15) Create a provisioning service certificate which is the leaf certificate belonging to the provisioning certificate chain (used for contract certificate installation)
REM Validity can be between 2 - 3 months, we make it 3 months
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\cpsLeafCert.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys\cpsLeafCert.key -passin file:passphrase.txt -config configs\cpsLeafCert.cnf -extensions ext -out csrs\cpsLeafCert.csr
openssl x509 -req -in csrs\cpsLeafCert.csr -extfile configs\cpsLeafCert.cnf -extensions ext -CA certs\cpsSubCA2.pem -CAkey privateKeys\cpsSubCA2.key -set_serial 15 -passin file:passphrase.txt -days %validity_cps_leaf_cert% -out certs\cpsLeafCert.pem
type certs\cpsSubCA2.pem certs\cpsSubCA1.pem > certs\intermediateCPSCAs.pem
openssl pkcs12 -export -inkey privateKeys\cpsLeafCert.key -in certs\cpsLeafCert.pem -certfile certs\intermediateCPSCAs.pem -aes128 -passin file:passphrase.txt -passout file:passphrase2.txt -name cps_leaf_cert -out certs\cpsCertChain.p12
REM 16) Finally we need to convert the certificates from PEM format to DER format (PEM is the default format, but ISO 15118 only allows DER format)
openssl x509 -inform PEM -in certs\v2gRootCA.pem -outform DER -out certs\v2gRootCA.der
openssl x509 -inform PEM -in certs\cpsSubCA1.pem -outform DER -out certs\cpsSubCA1.der
openssl x509 -inform PEM -in certs\cpsSubCA2.pem -outform DER -out certs\cpsSubCA2.der
openssl x509 -inform PEM -in certs\cpsLeafCert.pem -outform DER -out certs\cpsLeafCert.der
openssl x509 -inform PEM -in certs\cpoSubCA1.pem -outform DER -out certs\cpoSubCA1.der
openssl x509 -inform PEM -in certs\cpoSubCA2.pem -outform DER -out certs\cpoSubCA2.der
openssl x509 -inform PEM -in certs\seccCert.pem -outform DER -out certs\seccCert.der
openssl x509 -inform PEM -in certs\oemRootCA.pem -outform DER -out certs\oemRootCA.der
openssl x509 -inform PEM -in certs\oemSubCA1.pem -outform DER -out certs\oemSubCA1.der
openssl x509 -inform PEM -in certs\oemSubCA2.pem -outform DER -out certs\oemSubCA2.der
openssl x509 -inform PEM -in certs\oemProvCert.pem -outform DER -out certs\oemProvCert.der
openssl x509 -inform PEM -in certs\moRootCA.pem -outform DER -out certs\moRootCA.der
openssl x509 -inform PEM -in certs\moSubCA1.pem -outform DER -out certs\moSubCA1.der
openssl x509 -inform PEM -in certs\moSubCA2.pem -outform DER -out certs\moSubCA2.der
openssl x509 -inform PEM -in certs\contractCert.pem -outform DER -out certs\contractCert.der
REM Since the intermediate certificates need to be in PEM format when putting them in a PKCS12 container and the resulting PKCS12 file is a binary format, it might be sufficient. Otherwise, I have currently no idea how to covert the intermediate certificates in DER without running into problems when creating the PKCS12 container.
REM 17) In case you want the private keys in PKCS#8 file format and DER encoded, use this command. Especially necessary for the private key of MOSubCA2 in RISE V2G
openssl pkcs8 -topk8 -in privateKeys\moSubCA2.key -inform PEM -passin file:passphrase.txt -passout file:passphrase2.txt -outform DER -out privateKeys\moSubCA2.pkcs8.der
REM XX) Create the initial Java truststores and keystores
REM XX.1) truststore for the EVCC which needs to hold the V2GRootCA certificate (the EVCC does not verify the received contract certificate chain, therefore no MORootCA needs to be imported in evccTruststore.jks )
keytool -import -keystore keystores\evccTruststore.jks -alias v2g_root_ca -file certs\v2gRootCA.der -storepass:file passphrase.txt -noprompt
REM XX.2) truststore for the SECC which needs to hold the V2GRootCA certificate and the MORootCA which signed the MOSubCA1 (needed for verifying the contract certificate signature chain which will be sent from the EVCC to the SECC with PaymentDetailsReq message). According to ISO 15118-2, MORootCA is not necessarily needed as the MOSubCA1 could instead be signed by a V2GRootCA.
keytool -import -keystore keystores\seccTruststore.jks -alias v2g_root_ca -file certs\v2gRootCA.der -storepass:file passphrase.txt -noprompt
keytool -import -keystore keystores\seccTruststore.jks -alias mo_root_ca -file certs\moRootCA.der -storepass:file passphrase.txt -noprompt
REM XX.3) keystore for the SECC which needs to hold the CPOSubCA1, CPOSubCA2, and SECCCert certificates
keytool -importkeystore -srckeystore certs\cpoCertChain.p12 -srcstoretype pkcs12 -srcstorepass:file passphrase.txt -srcalias secc_cert -destalias secc_cert -destkeystore keystores\seccKeystore.jks -storepass:file passphrase.txt -noprompt
REM XX.4) keystore for the EVCC which needs to hold the OEMSubCA1, OEMSubCA2, and OEMProvCert certificates
keytool -importkeystore -srckeystore certs\oemCertChain.p12 -srcstoretype pkcs12 -srcstorepass:file passphrase.txt -srcalias oem_prov_cert -destalias oem_prov_cert -destkeystore keystores\evccKeystore.jks -storepass:file passphrase.txt -noprompt
REM Side notes for OCSP stapling in Java: see http://openjdk.java.net/jeps/8046321

View File

@ -95,6 +95,17 @@ RequestedEnergyTransferMode = AC_three_phase_core
XMLRepresentationOfMessages = true
# Hexadecimal and Base64 representation of messages
#--------------------------------------------------
#
# Possible values:
# - true
# - false
# If this value is set to 'true', the EXICodec will print each message's hexadecimal and Base64 representation (for debugging purposes)
# If no correct value is provided here, 'false' will be chosen
HexAndBase64RepresentationOfMessages = true
# Extended logging of signature verification
#-------------------------------------------
#
@ -114,4 +125,4 @@ SignatureVerificationLog = true
# - exificient
# - open_exi
# If no correct value is provided here, 'exificient' will be used
EXICodec = exificient
EXICodec = open_exi

View File

@ -88,8 +88,7 @@ public class V2GCommunicationSessionHandlerEVCC implements Observer {
setSessionRetryCounter(getSeccDiscoveryRequestCounter() + 1);
// Create SECCDiscoveryReq and check response
while (udpResponse == null &&
getSeccDiscoveryRequestCounter() < TimeRestrictions.SDP_REQUEST_MAX_COUNTER) {
while (getSeccDiscoveryRequestCounter() < TimeRestrictions.SDP_REQUEST_MAX_COUNTER) {
udpResponse = sendSECCDiscoveryReq(getSecurity());
if (udpResponse == null) {
@ -99,7 +98,9 @@ public class V2GCommunicationSessionHandlerEVCC implements Observer {
if (getMessageHandler().isV2GTPMessageValid(getV2gTpMessage())) {
seccDiscoveryRes = new SECCDiscoveryRes(getV2gTpMessage().getPayload());
break;
break; // if everything is OK and a valid SDP response is received, the while loop is stopped here
} else {
continue;
}
}
}
@ -307,11 +308,11 @@ public class V2GCommunicationSessionHandlerEVCC implements Observer {
if (isSecureCommunication()) {
TLSClient.getInstance().send(
getV2gTpMessage(),
TimeRestrictions.getV2G_EVCC_Msg_Timeout(V2GMessages.SUPPORTED_APP_PROTOCOL_RES));
TimeRestrictions.getV2gEvccMsgTimeout(V2GMessages.SUPPORTED_APP_PROTOCOL_RES));
} else {
TCPClient.getInstance().send(
getV2gTpMessage(),
TimeRestrictions.getV2G_EVCC_Msg_Timeout(V2GMessages.SUPPORTED_APP_PROTOCOL_RES));
TimeRestrictions.getV2gEvccMsgTimeout(V2GMessages.SUPPORTED_APP_PROTOCOL_RES));
}
}

View File

@ -52,6 +52,9 @@ public class WaitForAuthorizationRes extends ClientState {
AuthorizationResType authorizationRes =
(AuthorizationResType) v2gMessageRes.getBody().getBodyElement().getValue();
if (authorizationRes.getEVSEProcessing() == null)
return new TerminateSession("EVSEProcessing parameter of AuthorizationRes is null. Parameter is mandatory.");
if (authorizationRes.getEVSEProcessing().equals(EVSEProcessingType.FINISHED)) {
getLogger().debug("EVSEProcessing was set to FINISHED");
@ -70,9 +73,11 @@ public class WaitForAuthorizationRes extends ClientState {
} else {
getLogger().debug("EVSEProcessing was set to ONGOING");
long elapsedTimeInMs = 0;
if (getCommSessionContext().isOngoingTimerActive()) {
long elapsedTime = System.nanoTime() - getCommSessionContext().getOngoingTimer();
long elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
if (elapsedTimeInMs > TimeRestrictions.V2G_EVCC_ONGOING_TIMEOUT)
return new TerminateSession("Ongoing timer timed out for AuthorizationReq");
@ -90,7 +95,9 @@ public class WaitForAuthorizationRes extends ClientState {
// Set xml reference element
getXMLSignatureRefElements().put(
authorizationReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(authorizationReq)));
SecurityUtils.generateDigest(
authorizationReq.getId(),
getMessageHandler().getJaxbElement(authorizationReq)));
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(
@ -103,7 +110,7 @@ public class WaitForAuthorizationRes extends ClientState {
authorizationReq = getAuthorizationReq(null);
}
return getSendMessage(authorizationReq, V2GMessages.AUTHORIZATION_RES);
return getSendMessage(authorizationReq, V2GMessages.AUTHORIZATION_RES, Math.min((TimeRestrictions.V2G_EVCC_ONGOING_TIMEOUT - (int) elapsedTimeInMs), TimeRestrictions.getV2gEvccMsgTimeout(V2GMessages.AUTHORIZATION_RES)));
}
} else {
return new TerminateSession("Incoming message raised an error");

View File

@ -66,9 +66,11 @@ public class WaitForCableCheckRes extends ClientState {
} else {
getLogger().debug("EVSEProcessing was set to ONGOING");
long elapsedTimeInMs = 0;
if (getCommSessionContext().isOngoingTimerActive()) {
long elapsedTime = System.nanoTime() - getCommSessionContext().getOngoingTimer();
long elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
if (elapsedTimeInMs > TimeRestrictions.V2G_EVCC_CABLE_CHECK_TIMEOUT)
return new TerminateSession("CableCheck timer timed out for CableCheckReq");
@ -77,7 +79,7 @@ public class WaitForCableCheckRes extends ClientState {
getCommSessionContext().setOngoingTimerActive(true);
}
return getSendMessage(getCableCheckReq(), V2GMessages.CABLE_CHECK_RES);
return getSendMessage(getCableCheckReq(), V2GMessages.CABLE_CHECK_RES, Math.min((TimeRestrictions.V2G_EVCC_CABLE_CHECK_TIMEOUT - (int) elapsedTimeInMs), TimeRestrictions.getV2gEvccMsgTimeout(V2GMessages.CABLE_CHECK_RES)));
}
} else {
return new TerminateSession("Incoming message raised an error");

View File

@ -92,16 +92,24 @@ public class WaitForCertificateInstallationRes extends ClientState {
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(
certificateInstallationRes.getContractSignatureCertChain().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureCertChain())));
SecurityUtils.generateDigest(
certificateInstallationRes.getContractSignatureCertChain().getId(),
getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureCertChain())));
verifyXMLSigRefElements.put(
certificateInstallationRes.getContractSignatureEncryptedPrivateKey().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureEncryptedPrivateKey())));
SecurityUtils.generateDigest(
certificateInstallationRes.getContractSignatureEncryptedPrivateKey().getId(),
getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureEncryptedPrivateKey())));
verifyXMLSigRefElements.put(
certificateInstallationRes.getDHpublickey().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getDHpublickey())));
SecurityUtils.generateDigest(
certificateInstallationRes.getDHpublickey().getId(),
getMessageHandler().getJaxbElement(certificateInstallationRes.getDHpublickey())));
verifyXMLSigRefElements.put(
certificateInstallationRes.getEMAID().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getEMAID())));
SecurityUtils.generateDigest(
certificateInstallationRes.getEMAID().getId(),
getMessageHandler().getJaxbElement(certificateInstallationRes.getEMAID())));
if (!SecurityUtils.verifySignature(
signature,

View File

@ -92,16 +92,24 @@ public class WaitForCertificateUpdateRes extends ClientState {
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(
certificateUpdateRes.getContractSignatureCertChain().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureCertChain())));
SecurityUtils.generateDigest(
certificateUpdateRes.getContractSignatureCertChain().getId(),
getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureCertChain())));
verifyXMLSigRefElements.put(
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureEncryptedPrivateKey())));
SecurityUtils.generateDigest(
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().getId(),
getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureEncryptedPrivateKey())));
verifyXMLSigRefElements.put(
certificateUpdateRes.getDHpublickey().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getDHpublickey())));
SecurityUtils.generateDigest(
certificateUpdateRes.getDHpublickey().getId(),
getMessageHandler().getJaxbElement(certificateUpdateRes.getDHpublickey())));
verifyXMLSigRefElements.put(
certificateUpdateRes.getEMAID().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getEMAID())));
SecurityUtils.generateDigest(
certificateUpdateRes.getEMAID().getId(),
getMessageHandler().getJaxbElement(certificateUpdateRes.getEMAID())));
if (!SecurityUtils.verifySignature(
signature,

View File

@ -60,12 +60,17 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
ChargeParameterDiscoveryResType chargeParameterDiscoveryRes =
(ChargeParameterDiscoveryResType) v2gMessageRes.getBody().getBodyElement().getValue();
if (chargeParameterDiscoveryRes.getEVSEProcessing() == null)
return new TerminateSession("EVSEProcessing parameter of ChargeParameterDiscoveryRes is null. Parameter is mandatory.");
if (chargeParameterDiscoveryRes.getEVSEProcessing().equals(EVSEProcessingType.ONGOING)) {
getLogger().debug("EVSEProcessing was set to ONGOING");
long elapsedTimeInMs = 0;
if (getCommSessionContext().isOngoingTimerActive()) {
long elapsedTime = System.nanoTime() - getCommSessionContext().getOngoingTimer();
long elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
if (elapsedTimeInMs > TimeRestrictions.V2G_EVCC_ONGOING_TIMEOUT)
return new TerminateSession("Ongoing timer timed out for ChargeParameterDiscoveryReq");
@ -74,7 +79,7 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
getCommSessionContext().setOngoingTimerActive(true);
}
return getSendMessage(getCommSessionContext().getChargeParameterDiscoveryReq(), V2GMessages.CHARGE_PARAMETER_DISCOVERY_RES);
return getSendMessage(getCommSessionContext().getChargeParameterDiscoveryReq(), V2GMessages.CHARGE_PARAMETER_DISCOVERY_RES, Math.min((TimeRestrictions.V2G_EVCC_ONGOING_TIMEOUT - (int) elapsedTimeInMs), TimeRestrictions.getV2gEvccMsgTimeout(V2GMessages.CHARGE_PARAMETER_DISCOVERY_RES)));
} else {
getLogger().debug("EVSEProcessing was set to FINISHED");
@ -200,7 +205,9 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
verifyXMLSigRefElements.put(
saScheduleTuple.getSalesTariff().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(saScheduleTuple.getSalesTariff())));
SecurityUtils.generateDigest(
saScheduleTuple.getSalesTariff().getId(),
getMessageHandler().getJaxbElement(saScheduleTuple.getSalesTariff())));
}
if (salesTariffCounter > 0) {

View File

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

View File

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

View File

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

View File

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

View File

@ -72,7 +72,7 @@ public class WaitForPreChargeRes extends ClientState {
preChargeReq.setEVTargetCurrent(dcEvController.getTargetCurrent());
preChargeReq.setEVTargetVoltage(dcEvController.getTargetVoltage());
return getSendMessage(preChargeReq, V2GMessages.PRE_CHARGE_RES);
return getSendMessage(preChargeReq, V2GMessages.PRE_CHARGE_RES, Math.min((TimeRestrictions.V2G_EVCC_PRE_CHARGE_TIMEOUT - (int) elapsedTimeInMs), TimeRestrictions.getV2gEvccMsgTimeout(V2GMessages.PRE_CHARGE_RES)));
}
}
} else {

View File

@ -95,7 +95,7 @@ public class WaitForSupportedAppProtocolRes extends ClientState {
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.getV2gEvccMsgTimeout(V2GMessages.SESSION_SETUP_RES),
TimeRestrictions.V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT - elapsedTimeInMs
));
} else {

View File

@ -77,7 +77,7 @@ public class TCPClient extends StatefulTransportLayerClient {
setInStream(getTcpSocketToServer().getInputStream());
setOutStream(getTcpSocketToServer().getOutputStream());
getLogger().debug("TCP client connection established \n\t from link-local address " +
getLogger().info("TCP client connection established \n\t from link-local address " +
getClientAddress() + " and port " + getClientPort() +
"\n\t to host " + host.getHostAddress() + " and port " + port);

View File

@ -138,7 +138,7 @@ public class TLSClient extends StatefulTransportLayerClient {
return false;
}
getLogger().debug("TLS client connection established \n\t from link-local address " +
getLogger().info("TLS client connection established \n\t from link-local address " +
getClientAddress() + " and port " + getClientPort() +
"\n\t to host " + host.getHostAddress() + " and port " + port);

View File

@ -1,22 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appenders>
<console name="Console" target="SYSTEM_OUT">
<patternLayout pattern="%d{ISO8601} %-5p [%t] %c: %m%n" />
</console>
<!-- <param name="threshold" value="debug" />
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%t] %c: %m%n" />
</layout>-->
</appenders>
<loggers>
<root level="debug">
<appender-ref ref="Console" />
</root>
</loggers>
</log4j:configuration>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{ISO8601} %highlight{%level}{FATAL=bg_red, ERROR=red, WARN=yellow, INFO=green, DEBUG=blue} [%t] %c: %m%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
<!-- see http://logging.apache.org/log4j/2.x/manual/configuration.html -->

View File

@ -88,6 +88,17 @@ PrivateEnvironment = false
XMLRepresentationOfMessages = true
# Hexadecimal and Base64 representation of messages
#--------------------------------------------------
#
# Possible values:
# - true
# - false
# If this value is set to 'true', the EXICodec will print each message's hexadecimal and Base64 representation (for debugging purposes)
# If no correct value is provided here, 'false' will be chosen
HexAndBase64RepresentationOfMessages = true
# Extended logging of signature verification
#-------------------------------------------
#
@ -107,4 +118,4 @@ SignatureVerificationLog = true
# - exificient
# - open_exi
# If no correct value is provided here, 'exificient' will be used
EXICodec = exificient
EXICodec = open_exi

View File

@ -53,9 +53,25 @@ public class DummyBackendInterface implements IBackendInterface {
private V2GCommunicationSessionSECC commSessionContext;
private Logger logger = LogManager.getLogger(this.getClass().getSimpleName());
private ECPrivateKey moSubCA2PrivateKey;
public void setMoSubCA2PrivateKey(ECPrivateKey moSubCA2PrivateKey) {
this.moSubCA2PrivateKey = moSubCA2PrivateKey;
}
public DummyBackendInterface(V2GCommunicationSessionSECC commSessionContext) {
setCommSessionContext(commSessionContext);
/*
* In order to reduce timing problems with handling ChargeParameterDiscoveryReq, reading the private key of the MO Sub-CA2
* from the keystore to sign the SalesTariff will be done during initialization of this class.
*/
ECPrivateKey privateKey = SecurityUtils.getPrivateKey("./moSubCA2.pkcs8.der");
if (privateKey == null)
getLogger().error("No private key available from MO Sub-CA 2 PKCS#8 file");
else
setMoSubCA2PrivateKey(privateKey);
}
@Override
@ -145,7 +161,9 @@ public class DummyBackendInterface implements IBackendInterface {
if (saScheduleTuple.getSalesTariff() != null) {
xmlSignatureRefElements.put(
salesTariff.getId(),
SecurityUtils.generateDigest(getCommSessionContext().getMessageHandler().getJaxbElement(salesTariff)));
SecurityUtils.generateDigest(
salesTariff.getId(),
getCommSessionContext().getMessageHandler().getJaxbElement(salesTariff)));
}
return saScheduleList;
@ -225,8 +243,13 @@ public class DummyBackendInterface implements IBackendInterface {
authorizedEMAID2.setId("id2");
authorizedEMAID2.setValue("DE1ABCD2EF357C");
EMAIDType authorizedEMAID3 = new EMAIDType();
authorizedEMAID3.setId("id2");
authorizedEMAID3.setValue("DE1230000000021");
authorizedEMAIDs.add(authorizedEMAID1);
authorizedEMAIDs.add(authorizedEMAID2);
authorizedEMAIDs.add(authorizedEMAID3);
boolean emaidFound = false;
@ -275,12 +298,7 @@ public class DummyBackendInterface implements IBackendInterface {
@Override
public ECPrivateKey getMOSubCA2PrivateKey() {
ECPrivateKey privateKey = SecurityUtils.getPrivateKey("./moSubCA2.pkcs8.der");
if (privateKey == null)
getLogger().error("No private key available from MO Sub-CA 2 PKCS#8 file");
return privateKey;
return this.moSubCA2PrivateKey;
}

View File

@ -112,7 +112,9 @@ public class WaitForAuthorizationReq extends ServerState {
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(
authorizationReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(authorizationReq)));
SecurityUtils.generateDigest(
authorizationReq.getId(),
getMessageHandler().getJaxbElement(authorizationReq)));
if (!SecurityUtils.verifySignature(
signature,

View File

@ -99,16 +99,24 @@ public class WaitForCertificateInstallationReq extends ServerState {
// Set xml reference elements
getXMLSignatureRefElements().put(
certificateInstallationRes.getContractSignatureCertChain().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureCertChain())));
SecurityUtils.generateDigest(
certificateInstallationRes.getContractSignatureCertChain().getId(),
getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureCertChain())));
getXMLSignatureRefElements().put(
certificateInstallationRes.getContractSignatureEncryptedPrivateKey().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureEncryptedPrivateKey())));
SecurityUtils.generateDigest(
certificateInstallationRes.getContractSignatureEncryptedPrivateKey().getId(),
getMessageHandler().getJaxbElement(certificateInstallationRes.getContractSignatureEncryptedPrivateKey())));
getXMLSignatureRefElements().put(
certificateInstallationRes.getDHpublickey().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getDHpublickey())));
SecurityUtils.generateDigest(
certificateInstallationRes.getDHpublickey().getId(),
getMessageHandler().getJaxbElement(certificateInstallationRes.getDHpublickey())));
getXMLSignatureRefElements().put(
certificateInstallationRes.getEMAID().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationRes.getEMAID())));
SecurityUtils.generateDigest(
certificateInstallationRes.getEMAID().getId(),
getMessageHandler().getJaxbElement(certificateInstallationRes.getEMAID())));
// Set signing private key
setSignaturePrivateKey(getCommSessionContext().getBackendInterface().getCPSLeafPrivateKey());
@ -166,7 +174,9 @@ public class WaitForCertificateInstallationReq extends ServerState {
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(
certificateInstallationReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateInstallationReq)));
SecurityUtils.generateDigest(
certificateInstallationReq.getId(),
getMessageHandler().getJaxbElement(certificateInstallationReq)));
if (!SecurityUtils.verifySignature(
signature,

View File

@ -96,16 +96,24 @@ public class WaitForCertificateUpdateReq extends ServerState {
// Set xml reference elements
getXMLSignatureRefElements().put(
certificateUpdateRes.getContractSignatureCertChain().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureCertChain())));
SecurityUtils.generateDigest(
certificateUpdateRes.getContractSignatureCertChain().getId(),
getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureCertChain())));
getXMLSignatureRefElements().put(
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureEncryptedPrivateKey())));
SecurityUtils.generateDigest(
certificateUpdateRes.getContractSignatureEncryptedPrivateKey().getId(),
getMessageHandler().getJaxbElement(certificateUpdateRes.getContractSignatureEncryptedPrivateKey())));
getXMLSignatureRefElements().put(
certificateUpdateRes.getDHpublickey().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getDHpublickey())));
SecurityUtils.generateDigest(
certificateUpdateRes.getDHpublickey().getId(),
getMessageHandler().getJaxbElement(certificateUpdateRes.getDHpublickey())));
getXMLSignatureRefElements().put(
certificateUpdateRes.getEMAID().getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateRes.getEMAID())));
SecurityUtils.generateDigest(
certificateUpdateRes.getEMAID().getId(),
getMessageHandler().getJaxbElement(certificateUpdateRes.getEMAID())));
// Set signing private key
setSignaturePrivateKey(getCommSessionContext().getBackendInterface().getCPSLeafPrivateKey());
@ -162,7 +170,9 @@ public class WaitForCertificateUpdateReq extends ServerState {
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(
certificateUpdateReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(certificateUpdateReq)));
SecurityUtils.generateDigest(
certificateUpdateReq.getId(),
getMessageHandler().getJaxbElement(certificateUpdateReq)));
if (!SecurityUtils.verifySignature(
signature,

View File

@ -91,9 +91,7 @@ public class WaitForChargeParameterDiscoveryReq extends ServerState {
);
}
// Wait a bit and check if the schedule has already been provided
// TODO is this the best way?
SleepUtils.safeSleep(TimeRestrictions.getV2G_EVCC_Msg_Timeout(V2GMessages.CHARGE_PARAMETER_DISCOVERY_RES)-1000);
// TODO An integration to a backend system which provides the SalesTariff would be needed here
if (chargeParameterDiscoveryReq.getRequestedEnergyTransferMode().toString().startsWith("AC"))
chargeParameterDiscoveryRes.setEVSEChargeParameter(

View File

@ -107,7 +107,7 @@ public class WaitForMeteringReceiptReq extends ServerState {
HashMap<String, byte[]> verifyXMLSigRefElements = new HashMap<String, byte[]>();
verifyXMLSigRefElements.put(
meteringReceiptReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(meteringReceiptReq)));
SecurityUtils.generateDigest(meteringReceiptReq.getId(), getMessageHandler().getJaxbElement(meteringReceiptReq)));
if (!SecurityUtils.verifySignature(
signature,

View File

@ -47,7 +47,7 @@ public final class TCPServer extends StatefulTransportLayerServer {
try {
setTcpServerSocket(new ServerSocket(getServerPort(), 50, getServerAddress()));
getLogger().debug("TCP server initialized at link-local address " +
getLogger().info("TCP server initialized at link-local address " +
getTcpServerSocket().getInetAddress().getHostAddress() +
" and port " + getTcpServerSocket().getLocalPort());
} catch (IOException e) {
@ -62,10 +62,10 @@ public final class TCPServer extends StatefulTransportLayerServer {
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
getLogger().debug("Waiting for new TCP client connection ...");
getLogger().info("Waiting for new TCP client connection ...");
setTcpClientSocket(getTcpServerSocket().accept());
getLogger().debug("TCP client connection with IP address " +
getLogger().info("TCP client connection with IP address " +
getTcpClientSocket().getInetAddress().getHostAddress() + " and port " +
getTcpClientSocket().getPort());

View File

@ -87,7 +87,7 @@ public final class TLSServer extends StatefulTransportLayerServer {
String[] enabledProtocols = {"TLSv1.2"};
getTlsServerSocket().setEnabledProtocols(enabledProtocols);
getLogger().debug("TLS server initialized at link-local address " +
getLogger().info("TLS server initialized at link-local address " +
getTlsServerSocket().getInetAddress().getHostAddress() +
" and port " + getTlsServerSocket().getLocalPort());
} catch (IOException e) {
@ -106,10 +106,10 @@ public final class TLSServer extends StatefulTransportLayerServer {
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
getLogger().debug("Waiting for new TLS client connection ...");
getLogger().info("Waiting for new TLS client connection ...");
setTlsClientSocket((SSLSocket) getTlsServerSocket().accept());
getLogger().debug("TLS client connection with IP address " +
getLogger().info("TLS client connection with IP address " +
getTlsClientSocket().getInetAddress().getHostAddress() + " and port " +
getTlsClientSocket().getPort());

View File

@ -1,22 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appenders>
<console name="Console" target="SYSTEM_OUT">
<patternLayout pattern="%d{ISO8601} %-5p [%t] %c: %m%n" />
</console>
<!-- <param name="threshold" value="debug" />
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%t] %c: %m%n" />
</layout>-->
</appenders>
<loggers>
<root level="debug">
<appender-ref ref="Console" />
</root>
</loggers>
</log4j:configuration>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{ISO8601} %highlight{%level}{FATAL=bg_red, ERROR=red, WARN=yellow, INFO=green, DEBUG=blue} [%t] %c: %m%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
<!-- see http://logging.apache.org/log4j/2.x/manual/configuration.html -->

View File

@ -37,15 +37,15 @@
<version>1.4.01</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.1</version>
</dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>net.sourceforge.openexi</groupId>
<artifactId>nagasena</artifactId>

View File

@ -40,6 +40,7 @@ import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.utils.ByteUtils;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
@ -119,6 +120,9 @@ public final class EXIficientCodec extends ExiCodec {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos = ((ByteArrayOutputStream) encode(inStream, grammar));
if (isHexAndBase64MsgRepresentation())
showHexAndBase64RepresentationOfMessage(jaxbObject, baos.toByteArray());
return baos.toByteArray();
}
@ -147,6 +151,8 @@ public final class EXIficientCodec extends ExiCodec {
@Override
public synchronized Object decodeEXI(byte[] exiEncodedMessage, boolean supportedAppProtocolHandshake) {
getLogger().debug("Received EXI stream: " + ByteUtils.toHexString(exiEncodedMessage));
ByteArrayInputStream bais = new ByteArrayInputStream(exiEncodedMessage);
setDecodedExi(decode(bais, supportedAppProtocolHandshake));

View File

@ -28,6 +28,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Base64;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
@ -37,6 +38,7 @@ import javax.xml.bind.Unmarshaller;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.utils.ByteUtils;
import org.v2gclarity.risev2g.shared.utils.MiscUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolReq;
import org.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolRes;
@ -50,14 +52,21 @@ public abstract class ExiCodec {
private InputStream inStream;
private Object decodedMessage;
private String decodedExi;
private boolean xmlRepresentation;
private boolean xmlMsgRepresentation;
private boolean hexAndBase64MsgRepresentation;
public ExiCodec() {
// Check if XML representation of sent messages is to be shown (for debug purposes)
if ((boolean) MiscUtils.getPropertyValue("XMLRepresentationOfMessages"))
setXMLRepresentation(true);
setXMLMsgRepresentation(true);
else
setXMLRepresentation(false);
setXMLMsgRepresentation(false);
// Check if hexadecimal and Base64 representation of sent messages is to be shown (for debug purposes)
if ((boolean) MiscUtils.getPropertyValue("HexAndBase64RepresentationOfMessages"))
setHexAndBase64MsgRepresentation(true);
else
setHexAndBase64MsgRepresentation(false);
}
@ -71,7 +80,7 @@ public abstract class ExiCodec {
setInStream(new ByteArrayInputStream(baos.toByteArray()));
baos.close();
if (isXMLRepresentation()) showXMLRepresentationOfMessage(jaxbObject);
if (isXMLMsgRepresentation()) showXMLRepresentationOfMessage(jaxbObject);
return getInStream();
} catch (JAXBException | IOException e) {
getLogger().error(e.getClass().getSimpleName() + " occurred while trying to marshal to InputStream from JAXBElement", e);
@ -86,7 +95,7 @@ public abstract class ExiCodec {
setInStream(new ByteArrayInputStream(decodedExiString.getBytes()));
Object unmarhalledObject = getUnmarshaller().unmarshal(getInStream());
if (isXMLRepresentation()) showXMLRepresentationOfMessage(unmarhalledObject);
if (isXMLMsgRepresentation()) showXMLRepresentationOfMessage(unmarhalledObject);
return unmarhalledObject;
} catch (IOException | JAXBException | RuntimeException e) {
getLogger().error(e.getClass().getSimpleName() + " occurred while trying to unmarshall decoded message", e);
@ -127,6 +136,27 @@ public abstract class ExiCodec {
}
@SuppressWarnings("rawtypes")
public void showHexAndBase64RepresentationOfMessage(Object messageOrField, byte[] exiEncodedObject) {
String className = "";
if (messageOrField instanceof V2GMessage) {
className = ((V2GMessage) messageOrField).getBody().getBodyElement().getName().getLocalPart();
} else if (messageOrField instanceof JAXBElement) {
className = ((JAXBElement) messageOrField).getName().getLocalPart();
} else if (messageOrField instanceof SupportedAppProtocolReq) {
className = "SupportedAppProtocolReq";
} else if (messageOrField instanceof SupportedAppProtocolRes) {
className = "SupportedAppProtocolRes";
} else {
className = " JAXBElement";
}
getLogger().debug("EXI encoded " + className + ": " + ByteUtils.toHexString(exiEncodedObject));
getLogger().debug("Base64 encoded " + className + ": " + Base64.getEncoder().encodeToString(exiEncodedObject));
}
/**
* Provides the EXI encoding of the header's SignedInfo element. The resulting byte array can then be used to
* verify a signature.
@ -207,11 +237,21 @@ public abstract class ExiCodec {
}
private void setXMLRepresentation(boolean showXMLRepresentation) {
this.xmlRepresentation = showXMLRepresentation;
private void setXMLMsgRepresentation(boolean xmlMsgRepresentation) {
this.xmlMsgRepresentation = xmlMsgRepresentation;
}
public boolean isXMLRepresentation() {
return xmlRepresentation;
public boolean isXMLMsgRepresentation() {
return xmlMsgRepresentation;
}
public boolean isHexAndBase64MsgRepresentation() {
return hexAndBase64MsgRepresentation;
}
public void setHexAndBase64MsgRepresentation(boolean hexAndBase64MsgRepresentation) {
this.hexAndBase64MsgRepresentation = hexAndBase64MsgRepresentation;
}
}

View File

@ -47,6 +47,7 @@ import org.openexi.scomp.EXISchemaFactory;
import org.openexi.scomp.EXISchemaFactoryException;
import org.openexi.scomp.EXISchemaReader;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.utils.ByteUtils;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@ -166,6 +167,10 @@ public final class OpenEXICodec extends ExiCodec {
getTransmogrifier().encode(new InputSource(marshalToInputStream(jaxbObject)));
byte[] encodedExi = baos.toByteArray();
if (isHexAndBase64MsgRepresentation())
showHexAndBase64RepresentationOfMessage(jaxbObject, encodedExi);
baos.close();
return encodedExi;
@ -178,6 +183,8 @@ public final class OpenEXICodec extends ExiCodec {
@Override
public Object decodeEXI(byte[] exiEncodedMessage, boolean supportedAppProtocolHandshake) {
getLogger().debug("Received EXI stream: " + ByteUtils.toHexString(exiEncodedMessage));
StringWriter stringWriter = new StringWriter();
try {

View File

@ -228,12 +228,6 @@ public class MessageHandler {
* 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)
@ -273,7 +267,6 @@ public class MessageHandler {
default:
break;
}
}
jaxbElement = new JAXBElement(new QName(namespace, messageName),

View File

@ -75,7 +75,7 @@ public abstract class State {
// If the sent message is a response message, 60s sequence timeout is used
if (messageName.endsWith("Res")) return TimeRestrictions.V2G_SECC_SEQUENCE_TIMEOUT;
// otherwise the message specific timeout
else return TimeRestrictions.getV2G_EVCC_Msg_Timeout(nextExpectedMessage);
else return TimeRestrictions.getV2gEvccMsgTimeout(nextExpectedMessage);
}

View File

@ -69,7 +69,7 @@ public class TimeRestrictions {
*/
public static final int STAY_AWAKE_THRESHOLD = 125;
public static int getV2G_EVCC_Msg_Timeout(V2GMessages messageType) {
public static int getV2gEvccMsgTimeout(V2GMessages messageType) {
switch(messageType) {
case SUPPORTED_APP_PROTOCOL_RES : return 2000;
case SESSION_SETUP_RES: return 2000;

View File

@ -233,6 +233,10 @@ public final class MiscUtils {
if (Boolean.parseBoolean(propertyValue)) returnValue = true;
else returnValue = false;
break;
case "HexAndBase64RepresentationOfMessages": // EV + EVSE property
if (Boolean.parseBoolean(propertyValue)) returnValue = true;
else returnValue = false;
break;
case "SignatureVerificationLog": // EV + EVSE property
if (Boolean.parseBoolean(propertyValue)) returnValue = true;
else returnValue = false;

View File

@ -1705,7 +1705,8 @@ public final class SecurityUtils {
* @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(JAXBElement jaxbMessageOrField) {
@SuppressWarnings("rawtypes")
public static byte[] generateDigest(String id, JAXBElement jaxbMessageOrField) {
byte[] encoded;
// The schema-informed fragment grammar option needs to be used for EXI encodings in the header's signature
@ -1719,8 +1720,6 @@ public final class SecurityUtils {
encoded = getExiCodec().encodeEXI(jaxbMessageOrField, GlobalValues.SCHEMA_PATH_XMLDSIG.toString());
} else encoded = getExiCodec().encodeEXI(jaxbMessageOrField, GlobalValues.SCHEMA_PATH_MSG_DEF.toString());
getLogger().debug("EXI encoded " + jaxbMessageOrField.getName().getLocalPart() + ": " + ByteUtils.toHexString(encoded));
// Do not use the schema-informed fragment grammar option for other EXI encodings (message bodies)
getExiCodec().setFragment(false);
@ -1741,7 +1740,7 @@ public final class SecurityUtils {
*/
if ( !(jaxbMessageOrField.getValue() instanceof SignedInfoType) ) {
getLogger().debug("\n"
+ "\tDigest generated for XML reference element " + jaxbMessageOrField.getName().getLocalPart() + ": " + ByteUtils.toHexString(digest) + "\n"
+ "\tDigest generated for XML reference element " + jaxbMessageOrField.getName().getLocalPart() + " with ID '" + id + "': " + ByteUtils.toHexString(digest) + "\n"
+ "\tBase64 encoding of digest: " + Base64.getEncoder().encodeToString(digest));
}
}
@ -1766,19 +1765,25 @@ public final class SecurityUtils {
Signature ecdsa = Signature.getInstance("SHA256withECDSA", "SunEC");
getLogger().debug("EXI encoded SignedInfo: " + ByteUtils.toHexString(signedInfoElementExi));
getLogger().debug("\n\tPrivate key used for creating signature: " + ByteUtils.toHexString(ecPrivateKey.getS().toByteArray()));
ecdsa.initSign(ecPrivateKey);
ecdsa.update(signedInfoElementExi);
byte[] signature = ecdsa.sign();
// Java operates on DER encoded signatures, but we must send the raw r and s values as signature
byte[] rawSignature = getRawSignatureFromDEREncoding(signature);
getLogger().debug("Signature value: " + ByteUtils.toHexString(rawSignature));
return rawSignature;
if (ecPrivateKey != null) {
getLogger().debug("\n\tPrivate key used for creating signature: " + ByteUtils.toHexString(ecPrivateKey.getS().toByteArray()));
ecdsa.initSign(ecPrivateKey);
ecdsa.update(signedInfoElementExi);
byte[] signature = ecdsa.sign();
// Java operates on DER encoded signatures, but we must send the raw r and s values as signature
byte[] rawSignature = getRawSignatureFromDEREncoding(signature);
getLogger().debug("Signature value: " + ByteUtils.toHexString(rawSignature));
return rawSignature;
} else {
getLogger().error("Private key used to sign SignedInfo element is null");
return null;
}
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | NoSuchProviderException e) {
getLogger().error(e.getClass().getSimpleName() + " occurred while trying to create signature", e);
return null;
@ -1921,7 +1926,7 @@ public final class SecurityUtils {
SignatureType signature,
JAXBElement jaxbSignedInfo,
ECPublicKey ecPublicKey) {
byte[] computedSignedInfoDigest = generateDigest(jaxbSignedInfo);
byte[] computedSignedInfoDigest = generateDigest("", jaxbSignedInfo);
byte[] receivedSignatureValue = signature.getSignatureValue().getValue();
getLogger().debug("\n"
@ -2036,8 +2041,14 @@ public final class SecurityUtils {
// First we separate x and y of coordinates into separate variables
byte[] r = new byte[32];
byte[] s = new byte[32];
System.arraycopy(signatureValue, 0, r, 0, 32);
System.arraycopy(signatureValue, 32, s, 0, 32);
try {
System.arraycopy(signatureValue, 0, r, 0, 32);
System.arraycopy(signatureValue, 32, s, 0, 32);
} catch (ArrayIndexOutOfBoundsException e) {
getLogger().error("ArrayIndexOutOfBoundsException occurred while trying to get DER encoded signature", e);
return new byte[0];
}
int neededByteLength = signatureValue.length + 6; // 6 bytes for the header
boolean isFillByteForR = false;