/** Copyright 2014-2015 Center for TeleInFrastruktur (CTIF), Aalborg University Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package eu.betaas.taas.securitymanager.authentication.service.impl; import java.io.IOException; import java.math.BigInteger; import java.util.Date; import org.apache.log4j.Logger; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; import org.osgi.framework.BundleContext; import eu.betaas.taas.securitymanager.authentication.catalog.ExpireKeyGwCatalog; import eu.betaas.taas.securitymanager.authentication.catalog.KeyGwCatalog; import eu.betaas.taas.securitymanager.authentication.service.IGatewayEcmqvExtService; import eu.betaas.taas.securitymanager.authentication.utils.AuthBetaasBus; import eu.betaas.taas.securitymanager.certificate.service.IGatewayCertificateService; import eu.betaas.taas.securitymanager.common.certificate.utils.PKCS12Utils; import eu.betaas.taas.securitymanager.common.ec.ECKeyPairGen; import eu.betaas.taas.securitymanager.common.ec.operator.BcECDSAContentVerifierProviderBuilder; import eu.betaas.taas.securitymanager.common.model.BcCredential; import eu.betaas.taas.securitymanager.common.mqv.*; /** * Implementation class of the IGatewayEcmqvExtService interface * * @author Bayu Anggorojati [ba@es.aau.dk] * Center for TeleInFrastruktur, Aalborg University, Denmark * */ public class GWEcmqvExtService implements IGatewayEcmqvExtService { /** Logger */ private Logger log = Logger.getLogger("betaas.taas.securitymanager"); private IGatewayCertificateService gwCertificateService; private static final long VALID_PERIOD = 10 * 60 * 1000; // 10 minutes /** My Certificate */ private X509CertificateHolder myCert; // private ECPublicKey statPub; // my static/long term public key /** My static or long term private key */ private ECPrivateKeyParameters statPriv; /** My generated ephemeral public key */ private ECPublicKeyParameters myEphPub; /** my generated ephemeral private key */ private ECPrivateKeyParameters myEphPriv; /** ephemeral public key of the other GW */ private ECPublicKeyParameters ephPub; /** shared key 1 (it is used to encrypt the MAC 2) */ private byte[] k1; /** shared key 2 (the session key) */ private byte[] k2; /** My User Friendly Name, taken from my certificate */ private String myUFN; /** other GW's UFN, derived from the submitted certificate */ private String ufn; /** Class that handles BETaaS BUS in authentication bundle */ private AuthBetaasBus bus; /** Reference to Blueprint BundleContext */ private BundleContext context; /** * Initial setup method to initialize betaas bus service */ public void setup(){ // set the GW ID bus = new AuthBetaasBus(context); } /** Empty Constructor */ public GWEcmqvExtService(){} private boolean validateCert(X509CertificateHolder cert) throws Exception { // ServiceTracker certTracker = authActivator.getCertTracker(); // first load the KeyStore that contains certificates from GW* as well as // my own certificate issued by GW* // refer to GWCertificateService directly from blueprint BcCredential myCredential = gwCertificateService.loadMyCertificate( PKCS12Utils.GW_CERT); // get the public key of intermediate certificate from GW* // AsymmetricKeyParameter verKey = ECKeyPairGen.generateECPublicKey( // myCredential.getCertificateChain()[1].getSubjectPublicKeyInfo()); AsymmetricKeyParameter verKey = PublicKeyFactory.createKey( myCredential.getCertificateChain()[1].getSubjectPublicKeyInfo()); // get my own certificate myCert = (X509CertificateHolder)myCredential.getCertificateChain()[0]; // get the static/long term public and private key // statPub = (ECPublicKey) myCert.getPublicKey(); statPriv = (ECPrivateKeyParameters) myCredential.getPrivateKey(); if(cert.isSignatureValid(new BcECDSAContentVerifierProviderBuilder(new DefaultDigestAlgorithmIdentifierFinder()).build(verKey))) return true; return false; } /** * A method to verify MAC 3 which is received after the responseEcmqv * @param mac3: the received MAC 3 * @param ufn: the user friendly name of the other GW * @return true if the calculated MAC 3 matches with the received MAC 3 */ public boolean verifyMac3(byte[] mac3){ byte[] calcMac3 = ECMQVUtils.computeMAC("3", ufn, myUFN, ephPub.getQ().getEncoded(), myEphPub.getQ().getEncoded(), k1); String calcMac3Str = ""; for(byte b : calcMac3) calcMac3Str = calcMac3Str + Integer.toHexString(0xFF & b); log.debug("The calculated MAC (3):\n"+calcMac3Str); String mac3Str = ""; for(byte b : mac3) mac3Str = mac3Str + Integer.toHexString(0xFF & b); if(!mac3Str.equals(calcMac3Str)){ bus.sendData("MAC3 fails the verification", "warning", "SecM"); return false; } bus.sendData("MAC3 is verified", "debug", "SecM"); return true; } public EcmqvMessage initEcmqv(byte[] ephPubX, byte[] ephPubY, byte[] certByte){ // decode the certificate X509CertificateHolder cert = null; try { cert = new X509CertificateHolder(certByte); } catch (IOException e1) { log.error("Error in decoding the submitted certificate!!"); bus.sendData("Error in decoding the submitted certificate!!", "error", "SecM"); e1.printStackTrace(); } // validate the certificate boolean isCertValid = false; try { isCertValid = validateCert(cert); } catch (Exception e) { log.error("Error in verifying the submitted certificate: "+e.getMessage()); bus.sendData("Error in verifying the submitted certificate!!", "error", "SecM"); e.printStackTrace(); } if(!isCertValid){ log.error("The submitted certificate is not valid!!"); bus.sendData("The submitted certificate is not valid!!", "error", "SecM"); return null; } log.debug("Passed the certificate validation!!"); bus.sendData("Passed the certificate validation!!", "debug", "SecM"); // decode the ephemeral public key try { ephPub = ECKeyPairGen.generateECPublicKey192(new BigInteger(ephPubX), new BigInteger(ephPubY)); } catch (Exception e) { log.error("Error in decoding the submitted ephemeral public key: " +e.getMessage()); bus.sendData("Error in decoding the submitted ephemeral public key", "error", "SecM"); e.printStackTrace(); } // perform embedded public key validation boolean pubValid = ECMQVUtils.validateEmbedPubKey(ephPub); if(!pubValid){ log.error("The submitted ephemeral public key is not valid!!"); bus.sendData("The submitted ephemeral public key is not valid!!", "error", "SecM"); return null; } log.debug("Passed the embedded ephemeral public key validation!!"); bus.sendData("Passed the embedded ephemeral public key validation", "debug", "SecM"); // generates its own ephemeral key pairs, we assume that in this stage the // ephemeral key pairs were not generated AsymmetricCipherKeyPair myEphKp = ECKeyPairGen.generateECKeyPair192(); myEphPub = (ECPublicKeyParameters)myEphKp.getPublic(); myEphPriv = (ECPrivateKeyParameters)myEphKp.getPrivate(); // computes the implicit signature --> the static private key was obtained // when we validate the certificate (upon loading the KeyStore) BigInteger implSig = ECMQVUtils.computeImplicitSig(myEphPub, myEphPriv, statPriv); // calculates the shared key K ECPoint K=null; try { K = ECMQVUtils.calculateSharedKey(ephPub, (ECPublicKeyParameters) PublicKeyFactory.createKey(cert.getSubjectPublicKeyInfo()), ephPub.getParameters().getH(), implSig); } catch (IOException e) { log.error("Error in calculating the shared key K: "+e.getMessage()); bus.sendData("Error in calculating the shared key K", "error", "SecM"); e.printStackTrace(); } // derive 2 symmetric keys from the shared key K byte[] Kx = K.normalize().getXCoord().toBigInteger().toByteArray(); int Lx = K.normalize().getXCoord().toBigInteger().bitLength(); double x = Math.log(Lx)/Math.log(2.0); double L = Math.pow(2, 1+ Math.ceil(x)); byte[] deriveK = ECMQVUtils.deriveKeyHKDF(Kx, (int)L/8); // k1 and k2 split from newKey --> k1: to be MACed, k2: the session key k1 = new byte[deriveK.length/2]; k2 = new byte[deriveK.length/2]; int c = 0; for(byte b : deriveK){ if(c<deriveK.length/2){ k1[c] = b; } else{ k2[c-deriveK.length/2]=b; } c++; } // retrieving my user friendly name from the SubjectAlternativeNames in my // certificate Extensions myExs = myCert.getExtensions(); if(myExs!=null){ GeneralNames gns = GeneralNames.fromExtensions(myExs, Extension.subjectAlternativeName); for(int i=0;i<gns.getNames().length;i++){ myUFN = gns.getNames()[i].getName().toString(); } } // retrieving other GW user friendly name from the SubjectAlternativeNames // in the submitted certificate Extensions oExs = cert.getExtensions(); if(oExs!=null){ GeneralNames gns = GeneralNames.fromExtensions(oExs, Extension.subjectAlternativeName); for(int i=0;i<gns.getNames().length;i++){ ufn = gns.getNames()[i].getName().toString(); } } // compute the MAC to be sent to the other gateway byte[] myMac = ECMQVUtils.computeMAC("2", myUFN, ufn, myEphPub.getQ().getEncoded(), ephPub.getQ().getEncoded(), k1); EcmqvMessage eMsg = new EcmqvMessage(); eMsg.setMyMac(myMac); try { eMsg.setMyCertificate(myCert.getEncoded()); } catch (IOException e) { log.error("Error in encoding the certificate: "+e.getMessage()); bus.sendData("Error in encoding the certificate", "error", "SecM"); e.printStackTrace(); } eMsg.setEphemeralPublicX(myEphPub.getQ().normalize().getXCoord(). toBigInteger().toByteArray()); eMsg.setEphemeralPublicY(myEphPub.getQ().normalize().getYCoord(). toBigInteger().toByteArray()); return eMsg; } public long lastEcmqv(byte[] mac, String gwId) { // TODO Auto-generated method stub boolean isValid = false; // verify the received MAC 3 isValid = verifyMac3(mac); if(isValid){ Date expire = new Date(); long keyExpire = expire.getTime() + VALID_PERIOD; // add the k2 in the catalog KeyGwCatalog keyGwCat = KeyGwCatalog.instance(); keyGwCat.addKeyGw(gwId, k2); // add the expire time of the key in the catalog ExpireKeyGwCatalog expireGwCat = ExpireKeyGwCatalog.instance(); expireGwCat.addExpireKeyGw(gwId, keyExpire); return keyExpire; } else return -1; } // blueprint set reference to the IGatewayCertificateService public void setGwCertificateService( IGatewayCertificateService gwCertificateService) { this.gwCertificateService = gwCertificateService; log.debug("Got IGatewayCertificateService from blueprint..."); } /** * Blueprint set reference to BundleContext * @param context BundleContext */ public void setContext(BundleContext context) { this.context = context; log.debug("Got BundleContext from the blueprint..."); } }