package de.persosim.simulator.protocols; import java.security.Key; import java.security.PublicKey; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.TimeZone; import org.globaltester.logging.BasicLogger; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; import de.persosim.simulator.Activator; import de.persosim.simulator.crypto.DomainParameterSet; import de.persosim.simulator.crypto.certificates.CvPublicKey; import de.persosim.simulator.exception.CertificateNotParseableException; import de.persosim.simulator.exception.NotParseableException; import de.persosim.simulator.tlv.ConstructedTlvDataObject; import de.persosim.simulator.tlv.PrimitiveTlvDataObject; import de.persosim.simulator.tlv.TlvConstants; import de.persosim.simulator.tlv.TlvDataObjectContainer; import de.persosim.simulator.tlv.TlvTag; import de.persosim.simulator.utils.HexString; /** * XXX MBK replace TaOid with OID according to our own OID class hierarchy * * This class contains methods unique to the TR-03110 specification. * @author mboonk * */ public class Tr03110Utils implements TlvConstants { public static final int ACCESS_RIGHTS_AT_CAN_ALLOWED_BIT = 4; static private List<Tr03110UtilsProvider> providers = new ArrayList<>(); static private ServiceTracker<Tr03110UtilsProvider, Tr03110UtilsProvider> serviceTracker; static { if (Activator.getContext() != null){ ServiceTrackerCustomizer<Tr03110UtilsProvider, Tr03110UtilsProvider> customizer = new ServiceTrackerCustomizer<Tr03110UtilsProvider, Tr03110UtilsProvider>() { @Override public void removedService( ServiceReference<Tr03110UtilsProvider> reference, Tr03110UtilsProvider service) { providers.remove(service); } @Override public void modifiedService( ServiceReference<Tr03110UtilsProvider> reference, Tr03110UtilsProvider service) { //Nothing to be done } @Override public Tr03110UtilsProvider addingService( ServiceReference<Tr03110UtilsProvider> reference) { Tr03110UtilsProvider provider = Activator.getContext().getService(reference); providers.add(provider); return provider; } }; serviceTracker = new ServiceTracker<Tr03110UtilsProvider, Tr03110UtilsProvider>(Activator.getContext(), Tr03110UtilsProvider.class, customizer); serviceTracker.open(); } else { BasicLogger.log(Tr03110Utils.class, "No OSGi context is available, no additional TR03110 functionalities are supported", BasicLogger.INFO); } providers.add(new Tr03110UtilsDefaultProvider()); } /** * This method parses a public key encoded within a CV certificate * @param publicKeyData the encoding of a public key * @return a key matching the encoding from a CV certificate */ public static CvPublicKey parseCvPublicKey(ConstructedTlvDataObject publicKeyData) { for (Tr03110UtilsProvider provider : providers) { try { CvPublicKey key = provider.parseCvPublicKey(publicKeyData); if (key != null) { return key; } } catch (Exception e) { BasicLogger.logException(Tr03110Utils.class, e, BasicLogger.WARN); } } BasicLogger.log(Tr03110Utils.class, "Public Key data could not be parsed.", BasicLogger.INFO); return null; } /** * This method constructs the input data used to compute the authentication token needed e.g. by Pace's Mutual Authenticate or CA's General Authenticate. * @param publicKey the ephemeral public key to be inserted * @param domParamSet the domain parameters matching the provided public key * @return the authentication token input data */ public static TlvDataObjectContainer buildAuthenticationTokenInput(PublicKey publicKey, DomainParameterSet domParamSet, Oid oidInput) { /* construct authentication token object based on OID and public key */ byte[] ephemeralPublicKeyByteArray = domParamSet.encodePublicKey(publicKey); TlvTag pubKeyTag = domParamSet.getAuthenticationTokenPublicKeyTag(); PrimitiveTlvDataObject primitive06 = new PrimitiveTlvDataObject(TAG_06, oidInput.toByteArray()); PrimitiveTlvDataObject primitive84 = new PrimitiveTlvDataObject(pubKeyTag, ephemeralPublicKeyByteArray); ConstructedTlvDataObject constructed7F49 = new ConstructedTlvDataObject(TAG_7F49); constructed7F49.addTlvDataObject(primitive06); constructed7F49.addTlvDataObject(primitive84); TlvDataObjectContainer authenticationTokenInput = new TlvDataObjectContainer(); authenticationTokenInput.addTlvDataObject(constructed7F49); return authenticationTokenInput; } /** * This method extracts the domain parameter information from DH and EC public and private keys. * @param key a DH/EC public/private key * @return the extracted domain parameter information */ public static DomainParameterSet getDomainParameterSetFromKey(Key key) { for(Tr03110UtilsProvider provider : providers){ DomainParameterSet domainParameters = provider.getDomainParameterSetFromKey(key); if (domainParameters != null){ return domainParameters; } } throw new IllegalArgumentException("unexpected key format"); } /** * Reads the a date encoded in 6 bytes as described in TR-03110 v2.10 D.2.1.3. * @param dateData as described in TR-03110 V2.10 part 3, D * @return a {@link Date} object containing the encoded date * @throws CertificateNotParseableException */ // TODO check possible code duplication/overlap with Utils.getDate method public static Date parseDate(byte [] dateData) throws NotParseableException { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.MILLISECOND, 0); calendar.setTimeZone(TimeZone.getTimeZone("GMT")); if (dateData.length == 6){ for(byte currentByte : dateData){ if (currentByte < 0 || currentByte > 9){ throw new NotParseableException("The date could not be parsed, it contains illegal digit " + currentByte); } } calendar.set(dateData[0] * 10 + dateData[1] + 2000, dateData[2] * 10 + dateData[3] - 1, dateData[4] * 10 + dateData[5], 0, 0, 0); } else { throw new NotParseableException("The date could not be parsed, its length was incorrect"); } return calendar.getTime(); } /** * Encodes a date as described in TR-03110 v2.10 D.2.1.3. * * @param date * the date to encode, only the year, month and day components * are used * @return the 6 byte long BCD encoding */ // TODO check possible code duplication/overlap with Utils.encodeDate method public static byte[] encodeDate(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); DecimalFormat formatter = new DecimalFormat("00"); String tempDate = "" + formatter.format((calendar.get(Calendar.YEAR) - 2000) / 10) + formatter.format((calendar.get(Calendar.YEAR) - 2000) % 10) + formatter.format((calendar.get(Calendar.MONTH) + 1) / 10) + formatter.format((calendar.get(Calendar.MONTH) + 1) % 10) + formatter.format(calendar.get(Calendar.DAY_OF_MONTH) / 10) + formatter.format(calendar.get(Calendar.DAY_OF_MONTH) % 10); return HexString.toByteArray(tempDate); } }