/** 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. @author Bayu Anggorojati [ba@es.aau.dk] Center for TeleInFrastruktur, Aalborg University, Denmark */ package eu.betaas.taas.securitymanager.authentication.service.impl; import java.math.BigInteger; 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.operator.DefaultDigestAlgorithmIdentifierFinder; import org.osgi.framework.BundleContext; //import org.osgi.util.tracker.ServiceTracker; import eu.betaas.taas.securitymanager.authentication.catalog.ExpireKeyGwCatalog; import eu.betaas.taas.securitymanager.authentication.catalog.KeyGwCatalog; //import eu.betaas.taas.securitymanager.authentication.activator.ExtAuthenticationActivator; import eu.betaas.taas.securitymanager.authentication.service.IGatewayEcmqvIntService; 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.ECMQVUtils; import eu.betaas.taas.securitymanager.common.mqv.EcmqvMessage; /** * Implementation class of the IGatewayEcmqvIntService interface * * @author Bayu Anggorojati [ba@es.aau.dk] * Center for TeleInFrastruktur, Aalborg University, Denmark * */ public class GWEcmqvIntService implements IGatewayEcmqvIntService { /** Logger */ private Logger log = Logger.getLogger("betaas.taas.securitymanager"); /** Reference to GWCertificateService from blueprint */ private IGatewayCertificateService gwCertificateService; /** 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); } public GWEcmqvIntService(){ // this.authActivator = activator; } private boolean validateCert(X509CertificateHolder cert) throws Exception{ // 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 2 which is received after invoking the initEcmqv * @param mac2: the received MAC 2 * @param ufnA: the user friendly name of the other GW * @param ufnB: the user friendly name of this GW * @param ephPubA: the ephemeral public key of the other GW * @param ephPubB: the ephemeral public key of this GW * @param k1: one of the calculated shared key * @return true if the calculated MAC 2 matches with the received MAC 2 */ private boolean verifyMac2(byte[] mac2, String ufnA, String ufnB, ECPublicKeyParameters ephPubA, ECPublicKeyParameters ephPubB, byte[] k1){ byte[] calcMac2 = ECMQVUtils.computeMAC("2", ufnA, ufnB, ephPubA.getQ().getEncoded(), ephPubB.getQ().getEncoded(), k1); String calcMac2Str = ""; for(byte b : calcMac2) calcMac2Str = calcMac2Str + Integer.toHexString(0xFF & b); log.debug("The calculated MAC (2):\n"+calcMac2Str); String mac2Str = ""; for(byte b : mac2) mac2Str = mac2Str + Integer.toHexString(0xFF & b); if(!mac2Str.equals(calcMac2Str)) return false; return true; } public AsymmetricCipherKeyPair generateEphemeralKeyPair() throws Exception { AsymmetricCipherKeyPair myEphKp = ECKeyPairGen.generateECKeyPair192(); myEphPub = (ECPublicKeyParameters)myEphKp.getPublic(); myEphPriv = (ECPrivateKeyParameters)myEphKp.getPrivate(); return myEphKp; } public byte[] responseEcmqv(EcmqvMessage eMsg) throws Exception{ // decode the certificate X509CertificateHolder cert = new X509CertificateHolder(eMsg.getMyCertificate()); // decode the ECPublicKey ECPublicKeyParameters ephPub = ECKeyPairGen.generateECPublicKey192( new BigInteger(eMsg.getEphemeralPublicX()), new BigInteger(eMsg.getEphemeralPublicY())); // get the MAC 2 byte[] mac2 = eMsg.getMyMac(); // validate the certificate boolean isCertValid = false; isCertValid = validateCert(cert); 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"); // 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"); // set the ephPub with this received ephPub this.ephPub = ephPub; // now, no need to generate my own ephemeral key here, because it is done // compute the implicit signature BigInteger implSig = ECMQVUtils.computeImplicitSig(myEphPub, myEphPriv, statPriv); // calculates the shared key K ECPublicKeyParameters statPub = (ECPublicKeyParameters) PublicKeyFactory.createKey(cert.getSubjectPublicKeyInfo()); org.bouncycastle.math.ec.ECPoint K = ECMQVUtils.calculateSharedKey( this.ephPub, statPub, this.ephPub.getParameters().getH(), implSig); // 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(); } } // validate MAC 2, which is received from other GW boolean isMac2Valid = verifyMac2(mac2,ufn,myUFN,this.ephPub,myEphPub,k1); // compute the MAC to be sent to the other gateway if(!isMac2Valid){ log.error("Fails to verify the received MAC (2)!!"); bus.sendData("Fails to verify MAC (2)", "warning", "SecM"); return null; } log.debug("Successfully verifies the received MAC (2)!!"); bus.sendData("Successfully verifies MAC (2)", "debug", "SecM"); byte[] mac3 = ECMQVUtils.computeMAC("3", myUFN, ufn, myEphPub.getQ().getEncoded(), ephPub.getQ().getEncoded(), k1); return mac3; } /** * blueprint set reference to GWCertificateService * @param gwCertificateService */ public void setGwCertificateService( IGatewayCertificateService gwCertificateService) { this.gwCertificateService = gwCertificateService; log.debug("Got GWCertificateService from blueprint..."); } public long getExpireTime(String gwId) { ExpireKeyGwCatalog expireCat = ExpireKeyGwCatalog.instance(); return expireCat.getExpireKeyGw(gwId); } public void setKeyAndExpireTime(String gwId, long time) { // put the k2 into the catalog KeyGwCatalog keyCat = KeyGwCatalog.instance(); keyCat.addKeyGw(gwId, k2); // set the expire time ExpireKeyGwCatalog expireCat = ExpireKeyGwCatalog.instance(); expireCat.addExpireKeyGw(gwId, time); } public byte[] getK2(String gwId) { // TODO Auto-generated method stub log.debug("Retreiving k2 of GW: "+gwId); KeyGwCatalog keyCat = KeyGwCatalog.instance(); return keyCat.getKeyGw(gwId); } /** * Blueprint set reference to BundleContext * @param context BundleContext */ public void setContext(BundleContext context) { this.context = context; log.debug("Got BundleContext from the blueprint..."); } }