Enhanced TLS-related features:

- added ResponseCode 'OK_CertificateExpiresSoon' on SECC side when receiving ContractCertificate with PaymentDetailsReq
- modified TLSClient to initiate TLS handshake right when initializing TLSClient (instead of when sending first message), thus enabling the check for the correct domain component "CPO" of the SECC certificate
- validates now provisioning certificate chain and checks for correct domain component "CPS" of provisioning leaf certificate
- added SecurityUtils function to get contract certificate from EVCC keystore
- added SecurityUtils function to get validity period of contract certificate
- added SecurityUtils function to also check for correct domain component when checking validity of certificate
- implemented a correct check if certificate update or installation is needed upon receiving PaymentServiceSelectionRes on EVCC side
This commit is contained in:
Marc Mültin 2016-04-01 21:17:05 +02:00
parent 6329658d4a
commit ac7a9095f0
16 changed files with 234 additions and 43 deletions

8
.gitignore vendored
View File

@ -1,3 +1,11 @@
*.jks
*.p12
*.pem
*.cert
*.cer
*.key
*.pfx
*.bin
*.class
**/bin
*/bin

View File

@ -5,7 +5,7 @@
# This file shall serve you with all information needed to create your own certificate chains.
#
# Helpful information about using OpenSSL is provided by Ivan Ristic's book "Bulletproof SSL and TLS".
# Furthermore, you should have OpenSSL 1.0.2 (or above) installed to comply with all security requirements imposed by ISO/IEC 15118. For example, OpenSSL 0.9.8 does not come with SHA-2 for SHA-256 signature algorithms.
# 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.
#
# Author: Marc Mültin (marc.mueltin@chargepartner.com)
@ -49,7 +49,7 @@ openssl x509 -req -in csrs/cpoSub1CA.csr -extfile configs/cpoSub1CA.cnf -extensi
# 3) Create a second intermediate CPO sub-CA certificate just the way the previous intermedia certificate was created which is directly signed by the CPOSub1CA
# Differences to CPOSub1CA
# - 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)
# - validity is set to 1 year (1 - 2 years are allowed according to ISO/IEC 15118)
# - validity is set to 1 year (1 - 2 years are allowed according to ISO 15118)
openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys/cpoSub2CA.key -aes128 -passout file:passphrase.txt
openssl req -new -key privateKeys/cpoSub2CA.key -passin file:passphrase.txt -config configs/cpoSub2CA.cnf -extensions ext -out csrs/cpoSub2CA.csr
openssl x509 -req -in csrs/cpoSub2CA.csr -extfile configs/cpoSub2CA.cnf -extensions ext -CA certs/cpoSub1CA.pem -CAkey privateKeys/cpoSub1CA.key -set_serial 03 -passin file:passphrase.txt -days 365 -out certs/cpoSub2CA.pem
@ -59,7 +59,7 @@ openssl x509 -req -in csrs/cpoSub2CA.csr -extfile configs/cpoSub2CA.cnf -extensi
# Differences to CPOSub1CA and CPOSub2CA
# - basicConstraints sets CA to false, no pathlen is therefore set
# - keyusage is set to digitalSignature instead of keyCertSign and cRLSign
# - validity is set to 60 days (2 - 3 months are allowed according to ISO/IEC 15118)
# - 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/cpoSub2CA.pem -CAkey privateKeys/cpoSub2CA.key -set_serial 04 -passin file:passphrase.txt -days 60 -out certs/seccCert.pem
@ -98,7 +98,7 @@ cat certs/oemSub2CA.pem certs/oemSub1CA.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/oemProvCert.p12
# 9) Create a self-signed MORootCA (mobility operator) certificate (validity is up to the OEM, this example applies the same validity as the V2GRootCA)
# 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 14600 -sha256 -key privateKeys/moRootCA.key -set_serial 09 -passin file:passphrase.txt -config configs/moRootCA.cnf -extensions ext -out certs/moRootCA.pem
@ -145,7 +145,7 @@ cat certs/provSub2CA.pem certs/provSub1CA.pem > certs/intermediateProvCAs.pem
openssl pkcs12 -export -inkey privateKeys/provServiceCert.key -in certs/provServiceCert.pem -certfile certs/intermediateProvCAs.pem -aes128 -passin file:passphrase.txt -passout file:passphrase2.txt -name prov_service_cert -out certs/provServiceCert.p12
# XX) Finally we need to convert the certificates from PEM format to DER format (PEM is the default format, but ISO/IEC 15118 only allows DER format)
# XX) 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.crt
openssl x509 -inform PEM -in certs/oemRootCA.pem -outform DER -out certs/oemRootCA.crt
openssl x509 -inform PEM -in certs/moRootCA.pem -outform DER -out certs/moRootCA.crt
@ -155,7 +155,7 @@ openssl x509 -inform PEM -in certs/moRootCA.pem -outform DER -out certs/moRootCA
# XX) Create the initial Java truststores and keystores
# XX.1) truststore for the EVCC which needs to hold the V2GRootCA certificate (the EVCC does not verify the received 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.crt -storepass:file passphrase.txt -noprompt
# XX.2) truststore for the SECC which needs to hold the V2GRootCA certificate and the MORootCA which signed the MOSub1CA (needed for verifying the contract certificate signature chain which will be sent from the EVCC to the SECC with PaymentDetailsReq message). According to ISO/IEC 15118-2, MORootCA is not necessarily needed as the MOSub1CA could instead be signed by a V2GRootCA.
# XX.2) truststore for the SECC which needs to hold the V2GRootCA certificate and the MORootCA which signed the MOSub1CA (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 MOSub1CA could instead be signed by a V2GRootCA.
keytool -import -keystore keystores/seccTruststore.jks -alias v2g_root_ca -file certs/v2gRootCA.crt -storepass:file passphrase.txt -noprompt
keytool -import -keystore keystores/seccTruststore.jks -alias mo_root_ca -file certs/moRootCA.crt -storepass:file passphrase.txt -noprompt
# XX.3) keystore for the SECC which needs to hold the CPOSub1CA, CPOSub1CA and SECCCert certificates

View File

@ -17,7 +17,7 @@
#
# The network interface name like en3 or eth1 of the network interface on which to communicate with the SECC via a
# link-local IPv6 address
NetworkInterface = en3
NetworkInterface = en0
# Security
@ -28,7 +28,7 @@ NetworkInterface = en3
# - false
# If this value is set to 'false', TCP will be used on transport layer
# If no correct value is provided here, 'false' will be chosen
TLSSecurity = false
TLSSecurity = true
# Contract certificate update timespan

View File

@ -10,10 +10,12 @@
*******************************************************************************/
package org.eclipse.risev2g.evcc.session;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import org.eclipse.risev2g.evcc.evController.DummyEVController;
import org.eclipse.risev2g.evcc.evController.IEVController;
import org.eclipse.risev2g.evcc.states.WaitForAuthorizationRes;
@ -47,6 +49,7 @@ import org.eclipse.risev2g.shared.messageHandling.SendMessage;
import org.eclipse.risev2g.shared.messageHandling.TerminateSession;
import org.eclipse.risev2g.shared.misc.V2GCommunicationSession;
import org.eclipse.risev2g.shared.misc.V2GTPMessage;
import org.eclipse.risev2g.shared.utils.SecurityUtils;
import org.eclipse.risev2g.shared.v2gMessages.appProtocol.AppProtocolType;
import org.eclipse.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolRes;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.ChargeParameterDiscoveryReqType;
@ -425,26 +428,21 @@ public class V2GCommunicationSessionEVCC extends V2GCommunicationSession impleme
}
public boolean isCertificateInstallationNeeded() {
/**
* Checks if the respective service for installing or updating a certificate is offered by the SECC and
* has been selected by the EVCC.
*
* @param parameterSetID 1 for installing a certificate, 2 for updating a certificate
* @return True, if the respective certificate service is available, false otherwise
*/
public boolean isCertificateServiceAvailable(short parameterSetID) {
for (SelectedServiceType service : getSelectedServices().getSelectedService()) {
if (service.getServiceID() == 2 &&
if (service.getServiceID() == 2 && // ServiceID 2 refers to the 'Certificate' service
service.getParameterSetID() != null &&
service.getParameterSetID() == 1)
service.getParameterSetID() == parameterSetID)
return true;
}
return false;
}
public boolean isCertificateUpdateNeeded() {
for (SelectedServiceType service : getSelectedServices().getSelectedService()) {
if (service.getServiceID() == 2 &&
service.getParameterSetID() != null &&
service.getParameterSetID() == 2)
return true;
}
return false;
}

View File

@ -20,6 +20,7 @@ import org.eclipse.risev2g.shared.enumerations.V2GMessages;
import org.eclipse.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.eclipse.risev2g.shared.messageHandling.TerminateSession;
import org.eclipse.risev2g.shared.utils.SecurityUtils;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.CertificateChainType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.CertificateInstallationResType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.SignatureType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.V2GMessage;
@ -41,6 +42,15 @@ 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")) {
return new TerminateSession("Provisioning certificate chain is not valid");
}
ECPrivateKey oemProvCertPrivateKey = SecurityUtils.getPrivateKey(
SecurityUtils.getKeyStore(
GlobalValues.EVCC_KEYSTORE_FILEPATH.toString(),
@ -89,4 +99,5 @@ public class WaitForCertificateInstallationRes extends ClientState {
return true;
}
}

View File

@ -41,6 +41,15 @@ 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")) {
return new TerminateSession("Provisioning certificate chain is not valid");
}
ECPrivateKey contractCertPrivateKey = SecurityUtils.getPrivateKey(
SecurityUtils.getKeyStore(
GlobalValues.EVCC_KEYSTORE_FILEPATH.toString(),

View File

@ -18,6 +18,7 @@ import org.eclipse.risev2g.shared.messageHandling.TerminateSession;
import org.eclipse.risev2g.shared.utils.SecurityUtils;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.AuthorizationReqType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.PaymentDetailsResType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.ResponseCodeType;
import org.eclipse.risev2g.shared.v2gMessages.msgDef.V2GMessage;
public class WaitForPaymentDetailsRes extends ClientState {
@ -33,9 +34,14 @@ public class WaitForPaymentDetailsRes extends ClientState {
PaymentDetailsResType paymentDetailsRes =
(PaymentDetailsResType) v2gMessageRes.getBody().getBodyElement().getValue();
if (paymentDetailsRes.getGenChallenge() == null)
/*
* A reaction on the response code OK_CERTIFICATE_EXPIRES_SOON is not needed as this check
* is already done by EVCC itself before deciding to send CertificateUpdateReq/CertificateInstallationReq
*/
if (paymentDetailsRes.getGenChallenge() == null) {
return new TerminateSession("GenChallenge not provided in PaymentDetailsRes");
else {
} else {
// Set xml reference element
AuthorizationReqType authorizationReq = getAuthorizationReq(paymentDetailsRes.getGenChallenge());
getXMLSignatureRefElements().put(

View File

@ -11,6 +11,8 @@
package org.eclipse.risev2g.evcc.states;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import org.eclipse.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.eclipse.risev2g.shared.enumerations.GlobalValues;
import org.eclipse.risev2g.shared.enumerations.V2GMessages;
@ -32,12 +34,35 @@ public class WaitForPaymentServiceSelectionRes extends ClientState {
public ReactionToIncomingMessage processIncomingMessage(Object message) {
if (isIncomingMessageValid(message, PaymentServiceSelectionResType.class)) {
if (getCommSessionContext().getSelectedPaymentOption().equals(PaymentOptionType.CONTRACT)) {
if (getCommSessionContext().isCertificateInstallationNeeded())
return getSendMessage(getCertificateInstallationReq(), V2GMessages.CERTIFICATE_INSTALLATION_RES);
else if (getCommSessionContext().isCertificateUpdateNeeded())
return getSendMessage(getCertificateUpdateReq(), V2GMessages.CERTIFICATE_UPDATE_RES);
else
return getSendMessage(getPaymentDetailsReq(), V2GMessages.PAYMENT_DETAILS_RES);
X509Certificate contractCert = SecurityUtils.getContractCertificate();
/*
* 1. Check if certificate installation is needed
* No valid contract certificate means:
* - no contract certificate is stored, or
* - existing contract certificates are expired or revoked
*/
if (contractCert == null || (contractCert != null && !SecurityUtils.isCertificateValid(contractCert))) {
if (getCommSessionContext().isCertificateServiceAvailable((short) 1)) {
if (contractCert == null) getLogger().info("No contract certificate stored, trying to install contract certificate");
else getLogger().info("Stored contract certificate not valid, trying to install new contract certificate");
return getSendMessage(getCertificateInstallationReq(), V2GMessages.CERTIFICATE_INSTALLATION_RES);
} else return new TerminateSession("Certificate installation needed but service is not available");
}
// 2. Check if certificate update is needed (means: certificate is available but expires soon)
short validityOfContractCert = SecurityUtils.getValidityPeriod(contractCert);
if (validityOfContractCert <= GlobalValues.CERTIFICATE_EXPIRES_SOON_PERIOD.getShortValue()) {
if (getCommSessionContext().isCertificateServiceAvailable((short) 2)) {
getLogger().info("Stored contract certificate is about to expire in " + validityOfContractCert +
" days, trying to update contract certificate");
return getSendMessage(getCertificateUpdateReq(), V2GMessages.CERTIFICATE_UPDATE_RES);
} else return new TerminateSession("Certificate update needed but service is not available");
}
return getSendMessage(getPaymentDetailsReq(), V2GMessages.PAYMENT_DETAILS_RES);
} else if (getCommSessionContext().getSelectedPaymentOption().equals(PaymentOptionType.EXTERNAL_PAYMENT)) {
return getSendMessage(getAuthorizationReq(null), V2GMessages.AUTHORIZATION_RES);
} else {

View File

@ -14,7 +14,12 @@ import java.io.IOException;
import java.net.Inet6Address;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
@ -93,19 +98,32 @@ public class TLSClient extends StatefulTransportLayerClient {
// Set the supported TLS protocol
String[] enabledProtocols = {"TLSv1.2"};
getTlsSocketToServer().setEnabledProtocols(enabledProtocols);
getTlsSocketToServer().startHandshake();
Certificate[] seccCertificates = getTlsSocketToServer().getSession().getPeerCertificates();
X509Certificate seccLeafCertificate = (X509Certificate) seccCertificates[0];
// Check domain component of SECC certificate
if (!SecurityUtils.isCertificateValid(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());
return false;
}
getLogger().debug("TLS client connection established \n\t from link-local address " +
getClientAddress() + " and port " + getClientPort() +
"\n\t to host " + host.getHostAddress() + " and port " + port);
return true;
} catch (UnknownHostException e) {
getLogger().error("TCP client connection failed (UnknownHostException)!", e);
getLogger().error("TLS client connection failed (UnknownHostException)!", e);
} catch (SSLHandshakeException e) {
getLogger().error("TLS client connection failed (SSLHandshakeException)", e);
} catch (IOException e) {
getLogger().error("TCP client connection failed (IOException)!", e);
getLogger().error("TLS client connection failed (IOException)!", e);
} catch (NullPointerException e) {
getLogger().fatal("NullPointerException while trying to set keystores, resource path to keystore/truststore might be incorrect");
return false;
}
return false;
@ -142,8 +160,10 @@ public class TLSClient extends StatefulTransportLayerClient {
getOutStream().flush();
getLogger().debug("Message sent");
setTimeout(timeout);
} catch (IOException e) {
getLogger().error("An undefined IOException occurred while trying to send message", e);
} catch (SSLHandshakeException e1) {
stopAndNotify("An SSLHandshakeException occurred", e1);
} catch (IOException e2) {
stopAndNotify("An undefined IOException occurred while trying to send message", e2);
}
}

View File

@ -17,7 +17,7 @@
#
# The network interface name like en3 or eth1 of the network interface on which to communicate with the EVCC via a
# link-local IPv6 address
NetworkInterface = en3
NetworkInterface = en0
# Supported energy transfer modes

Binary file not shown.

View File

@ -146,7 +146,7 @@ public class V2GCommunicationSessionSECC extends V2GCommunicationSession impleme
if (obs instanceof ConnectionHandler && obj instanceof byte[]) {
processIncomingMessage((byte[]) obj);
} else if (obs instanceof ConnectionHandler && obj == null) {
terminateSession("ConnectionHanlder has notified an error", false);
terminateSession("ConnectionHandler has notified an error", false);
}
}

View File

@ -79,6 +79,14 @@ public class WaitForPaymentDetailsReq extends ServerState {
return false;
}
// Check if certificate expires soon (in 21 days or fewer) according to V2G2-690
// A check for general validity has already been done above and does not need to be checked again here
if (SecurityUtils.getValidityPeriod(
SecurityUtils.getCertificate(paymentDetailsReq.getContractSignatureCertChain().getCertificate())
) <= GlobalValues.CERTIFICATE_EXPIRES_SOON_PERIOD.getShortValue()) {
paymentDetailsRes.setResponseCode(ResponseCodeType.OK_CERTIFICATE_EXPIRES_SOON);
}
return true;
}
}

View File

@ -17,7 +17,10 @@ import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.Observable;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.risev2g.shared.misc.TimeRestrictions;
@ -120,8 +123,11 @@ public class ConnectionHandler extends Observable implements Runnable {
} catch (SocketTimeoutException e) {
stopAndNotify("A SocketTimeoutException occurred", null);
break;
} catch (IOException e1) {
stopAndNotify("IOException occurred", e1);
} catch (SSLHandshakeException e1) {
stopAndNotify("An SSLHandshakeException occurred", e1);
break;
} catch (IOException e2) {
stopAndNotify("IOException occurred", e2);
break;
}
}

View File

@ -31,6 +31,9 @@ public enum GlobalValues {
ALIAS_CONTRACT_CERTIFICATE("contract_cert"),
ALIAS_OEM_PROV_CERTIFICATE("oem_prov_cert"),
// Period of time in days in which contract certificate update is recommended
CERTIFICATE_EXPIRES_SOON_PERIOD((short) 21, GlobalTypes.SECURITY),
/*
* Relative file path to the EVCC and SECC keystore and truststore.
* Since at least the evccKeystore needs to be modified upon certificate installation / update, the
@ -186,6 +189,8 @@ public enum GlobalValues {
return "./seccKeystore.jks";
case SECC_TRUSTSTORE_FILEPATH:
return "./seccTruststore.jks";
case CERTIFICATE_EXPIRES_SOON_PERIOD:
return "21 days";
default: return "Invalid GlobalValue type";
}
}

View File

@ -53,9 +53,12 @@ import java.security.spec.InvalidParameterSpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@ -201,21 +204,78 @@ public final class SecurityUtils {
*/
public static boolean isCertificateValid(X509Certificate certificate) {
try {
certificate.checkValidity();
certificate.checkValidity();
return true;
} catch (CertificateExpiredException e) {
X500Principal subject = certificate.getSubjectX500Principal();
getLogger().warn("Certificate with distinguished name '" + subject.getName().toString() +
"' already expired (not after " + certificate.getNotAfter().toString() + ")");
} catch (CertificateNotYetValidException e) {
X500Principal subject = certificate.getSubjectX500Principal();
getLogger().warn("Certificate with distinguished name '" + subject.getName().toString() +
"' not yet valid (not before " + certificate.getNotBefore().toString() + ")");
}
}
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))
*
* @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
*/
public static boolean isCertificateValid(X509Certificate certificate, String domainComponent) {
if (isCertificateValid(certificate)) {
String dn = certificate.getSubjectX500Principal().getName();
LdapName ln;
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);
}
return false;
} else return false;
}
/**
* Checks how many days a given certificate is still valid.
* If the certificate is not valid any more, a negative number will be returned according to the number
* of days the certificate is already expired.
*
* @param certificate The X509Certificiate to be checked for validity period
* @return The number of days the given certificate is still valid, a negative number if already expired.
*/
public static short getValidityPeriod(X509Certificate certificate) {
Date today = Calendar.getInstance().getTime();
Date certificateExpirationDate = certificate.getNotAfter();
long diff = certificateExpirationDate.getTime() - today.getTime();
return (short) TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
}
/**
* Checks whether each certificate in the given certificate chain is currently valid.
@ -241,6 +301,23 @@ public final class SecurityUtils {
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.
*
* @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
*/
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;
}
/**
* Verifies that the given certificate was signed using the private key that corresponds to the
@ -761,6 +838,24 @@ public final class SecurityUtils {
}
public static X509Certificate getContractCertificate() {
X509Certificate contractCertificate = null;
KeyStore evccKeyStore = getKeyStore(
GlobalValues.EVCC_KEYSTORE_FILEPATH.toString(),
GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString()
);
try {
contractCertificate = (X509Certificate) evccKeyStore.getCertificate(GlobalValues.ALIAS_CONTRACT_CERTIFICATE.toString());
} catch (KeyStoreException e) {
getLogger().error("KeyStoreException occurred while trying to get contract certificate from keystore", e);
}
return contractCertificate;
}
/**
* Returns a list of certificates from the given CertificateChainType with the leaf certificate
* being the first element and potential subcertificates (intermediate CA certificatess)