src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 56541 92cbbfc996f3
child 56543 2352538d2f6e
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
     1 /*
       
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.security.krb5.internal.ssl;
       
    27 
       
    28 import sun.security.ssl.ClientKeyExchange;
       
    29 import sun.security.ssl.Debug;
       
    30 import sun.security.ssl.ClientKeyExchangeService;
       
    31 import sun.security.ssl.HandshakeOutStream;
       
    32 
       
    33 import sun.security.jgss.GSSCaller;
       
    34 import sun.security.jgss.krb5.Krb5Util;
       
    35 import sun.security.jgss.krb5.ServiceCreds;
       
    36 import sun.security.krb5.EncryptedData;
       
    37 import sun.security.krb5.EncryptionKey;
       
    38 import sun.security.krb5.KrbException;
       
    39 import sun.security.krb5.PrincipalName;
       
    40 import sun.security.krb5.internal.EncTicketPart;
       
    41 import sun.security.krb5.internal.Ticket;
       
    42 import sun.security.krb5.internal.crypto.KeyUsage;
       
    43 import sun.security.ssl.ProtocolVersion;
       
    44 
       
    45 import javax.crypto.SecretKey;
       
    46 import javax.crypto.spec.SecretKeySpec;
       
    47 import javax.security.auth.Subject;
       
    48 import javax.security.auth.kerberos.KerberosKey;
       
    49 import javax.security.auth.kerberos.KerberosPrincipal;
       
    50 import javax.security.auth.kerberos.KerberosTicket;
       
    51 import javax.security.auth.kerberos.KeyTab;
       
    52 import javax.security.auth.kerberos.ServicePermission;
       
    53 import java.io.IOException;
       
    54 import java.io.PrintStream;
       
    55 import java.net.InetAddress;
       
    56 import java.security.AccessControlContext;
       
    57 import java.security.AccessController;
       
    58 import java.security.Principal;
       
    59 import java.security.PrivilegedAction;
       
    60 import java.security.PrivilegedActionException;
       
    61 import java.security.PrivilegedExceptionAction;
       
    62 import java.security.SecureRandom;
       
    63 import java.util.Set;
       
    64 
       
    65 /**
       
    66  * The provider for TLS_KRB_ cipher suites.
       
    67  *
       
    68  * @since 9
       
    69  */
       
    70 public class Krb5KeyExchangeService implements ClientKeyExchangeService {
       
    71 
       
    72     public static final Debug debug = Debug.getInstance("ssl");
       
    73 
       
    74     @Override
       
    75     public String[] supported() {
       
    76         return new String[] { "KRB5", "KRB5_EXPORT" };
       
    77     }
       
    78 
       
    79     @Override
       
    80     public Object getServiceCreds(AccessControlContext acc) {
       
    81         try {
       
    82             ServiceCreds serviceCreds = AccessController.doPrivileged(
       
    83                     (PrivilegedExceptionAction<ServiceCreds>)
       
    84                             () -> Krb5Util.getServiceCreds(
       
    85                                     GSSCaller.CALLER_SSL_SERVER, null, acc));
       
    86             if (serviceCreds == null) {
       
    87                 if (debug != null && Debug.isOn("handshake")) {
       
    88                     System.out.println("Kerberos serviceCreds not available");
       
    89                 }
       
    90                 return null;
       
    91             }
       
    92             if (debug != null && Debug.isOn("handshake")) {
       
    93                 System.out.println("Using Kerberos creds");
       
    94             }
       
    95             String serverPrincipal = serviceCreds.getName();
       
    96             if (serverPrincipal != null) {
       
    97                 // When service is bound, we check ASAP. Otherwise,
       
    98                 // will check after client request is received
       
    99                 // in in Kerberos ClientKeyExchange
       
   100                 SecurityManager sm = System.getSecurityManager();
       
   101                 try {
       
   102                     if (sm != null) {
       
   103                         // Eliminate dependency on ServicePermission
       
   104                         sm.checkPermission(new ServicePermission(
       
   105                                 serverPrincipal, "accept"), acc);
       
   106                     }
       
   107                 } catch (SecurityException se) {
       
   108                     if (debug != null && Debug.isOn("handshake")) {
       
   109                         System.out.println("Permission to access Kerberos"
       
   110                                 + " secret key denied");
       
   111                     }
       
   112                     return null;
       
   113                 }
       
   114             }
       
   115             return serviceCreds;
       
   116         } catch (PrivilegedActionException e) {
       
   117             // Likely exception here is LoginException
       
   118             if (debug != null && Debug.isOn("handshake")) {
       
   119                 System.out.println("Attempt to obtain Kerberos key failed: "
       
   120                         + e.toString());
       
   121             }
       
   122             return null;
       
   123         }
       
   124     }
       
   125 
       
   126     @Override
       
   127     public String getServiceHostName(Principal principal) {
       
   128         if (principal == null) {
       
   129             return null;
       
   130         }
       
   131         String hostName = null;
       
   132         try {
       
   133             PrincipalName princName =
       
   134                     new PrincipalName(principal.getName(),
       
   135                             PrincipalName.KRB_NT_SRV_HST);
       
   136             String[] nameParts = princName.getNameStrings();
       
   137             if (nameParts.length >= 2) {
       
   138                 hostName = nameParts[1];
       
   139             }
       
   140         } catch (Exception e) {
       
   141             // ignore
       
   142         }
       
   143         return hostName;
       
   144     }
       
   145 
       
   146 
       
   147     @Override
       
   148     public boolean isRelated(boolean isClient,
       
   149             AccessControlContext acc, Principal p) {
       
   150 
       
   151         if (p == null) return false;
       
   152         try {
       
   153             Subject subject = AccessController.doPrivileged(
       
   154                     (PrivilegedExceptionAction<Subject>)
       
   155                             () -> Krb5Util.getSubject(
       
   156                                     isClient ? GSSCaller.CALLER_SSL_CLIENT
       
   157                                             : GSSCaller.CALLER_SSL_SERVER,
       
   158                                     acc));
       
   159             if (subject == null) {
       
   160                 if (debug != null && Debug.isOn("session")) {
       
   161                     System.out.println("Kerberos credentials are" +
       
   162                             " not present in the current Subject;" +
       
   163                             " check if " +
       
   164                             " javax.security.auth.useSubjectAsCreds" +
       
   165                             " system property has been set to false");
       
   166                 }
       
   167                 return false;
       
   168             }
       
   169             Set<Principal> principals =
       
   170                     subject.getPrincipals(Principal.class);
       
   171             if (principals.contains(p)) {
       
   172                 // bound to this principal
       
   173                 return true;
       
   174             } else {
       
   175                 if (isClient) {
       
   176                     return false;
       
   177                 } else {
       
   178                     for (KeyTab pc : subject.getPrivateCredentials(KeyTab.class)) {
       
   179                         if (!pc.isBound()) {
       
   180                             return true;
       
   181                         }
       
   182                     }
       
   183                     return false;
       
   184                 }
       
   185             }
       
   186         } catch (PrivilegedActionException pae) {
       
   187             if (debug != null && Debug.isOn("session")) {
       
   188                 System.out.println("Attempt to obtain" +
       
   189                         " subject failed! " + pae);
       
   190             }
       
   191             return false;
       
   192         }
       
   193 
       
   194     }
       
   195 
       
   196     public ClientKeyExchange createClientExchange(
       
   197             String serverName, AccessControlContext acc,
       
   198             ProtocolVersion protocolVerson, SecureRandom rand) throws IOException {
       
   199         return new ExchangerImpl(serverName, acc, protocolVerson, rand);
       
   200     }
       
   201 
       
   202     public ClientKeyExchange createServerExchange(
       
   203             ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
       
   204             SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
       
   205             AccessControlContext acc, Object serviceCreds) throws IOException {
       
   206         return new ExchangerImpl(protocolVersion, clientVersion, rand,
       
   207                 encodedTicket, encrypted, acc, serviceCreds);
       
   208     }
       
   209 
       
   210     static class ExchangerImpl extends ClientKeyExchange {
       
   211 
       
   212         final private KerberosPreMasterSecret preMaster;
       
   213         final private byte[] encodedTicket;
       
   214         final private KerberosPrincipal peerPrincipal;
       
   215         final private KerberosPrincipal localPrincipal;
       
   216 
       
   217         @Override
       
   218         public int messageLength() {
       
   219             return encodedTicket.length + preMaster.getEncrypted().length + 6;
       
   220         }
       
   221 
       
   222         @Override
       
   223         public void send(HandshakeOutStream s) throws IOException {
       
   224             s.putBytes16(encodedTicket);
       
   225             s.putBytes16(null);
       
   226             s.putBytes16(preMaster.getEncrypted());
       
   227         }
       
   228 
       
   229         @Override
       
   230         public void print(PrintStream s) throws IOException {
       
   231             s.println("*** ClientKeyExchange, Kerberos");
       
   232 
       
   233             if (debug != null && Debug.isOn("verbose")) {
       
   234                 Debug.println(s, "Kerberos service ticket", encodedTicket);
       
   235                 Debug.println(s, "Random Secret", preMaster.getUnencrypted());
       
   236                 Debug.println(s, "Encrypted random Secret", preMaster.getEncrypted());
       
   237             }
       
   238         }
       
   239 
       
   240         ExchangerImpl(String serverName, AccessControlContext acc,
       
   241                 ProtocolVersion protocolVersion, SecureRandom rand) throws IOException {
       
   242 
       
   243             // Get service ticket
       
   244             KerberosTicket ticket = getServiceTicket(serverName, acc);
       
   245             encodedTicket = ticket.getEncoded();
       
   246 
       
   247             // Record the Kerberos principals
       
   248             peerPrincipal = ticket.getServer();
       
   249             localPrincipal = ticket.getClient();
       
   250 
       
   251             // Optional authenticator, encrypted using session key,
       
   252             // currently ignored
       
   253 
       
   254             // Generate premaster secret and encrypt it using session key
       
   255             EncryptionKey sessionKey = new EncryptionKey(
       
   256                     ticket.getSessionKeyType(),
       
   257                     ticket.getSessionKey().getEncoded());
       
   258 
       
   259             preMaster = new KerberosPreMasterSecret(protocolVersion,
       
   260                     rand, sessionKey);
       
   261         }
       
   262 
       
   263         ExchangerImpl(
       
   264                 ProtocolVersion protocolVersion, ProtocolVersion clientVersion, SecureRandom rand,
       
   265                 byte[] encodedTicket, byte[] encrypted,
       
   266                 AccessControlContext acc, Object serviceCreds) throws IOException {
       
   267 
       
   268             // Read ticket
       
   269             this.encodedTicket = encodedTicket;
       
   270 
       
   271             if (debug != null && Debug.isOn("verbose")) {
       
   272                 Debug.println(System.out,
       
   273                         "encoded Kerberos service ticket", encodedTicket);
       
   274             }
       
   275 
       
   276             EncryptionKey sessionKey = null;
       
   277             KerberosPrincipal tmpPeer = null;
       
   278             KerberosPrincipal tmpLocal = null;
       
   279 
       
   280             try {
       
   281                 Ticket t = new Ticket(encodedTicket);
       
   282 
       
   283                 EncryptedData encPart = t.encPart;
       
   284                 PrincipalName ticketSname = t.sname;
       
   285 
       
   286                 final ServiceCreds creds = (ServiceCreds)serviceCreds;
       
   287                 final KerberosPrincipal princ =
       
   288                         new KerberosPrincipal(ticketSname.toString());
       
   289 
       
   290                 // For bound service, permission already checked at setup
       
   291                 if (creds.getName() == null) {
       
   292                     SecurityManager sm = System.getSecurityManager();
       
   293                     try {
       
   294                         if (sm != null) {
       
   295                             // Eliminate dependency on ServicePermission
       
   296                             sm.checkPermission(new ServicePermission(
       
   297                                     ticketSname.toString(), "accept"), acc);
       
   298                         }
       
   299                     } catch (SecurityException se) {
       
   300                         serviceCreds = null;
       
   301                         // Do not destroy keys. Will affect Subject
       
   302                         if (debug != null && Debug.isOn("handshake")) {
       
   303                             System.out.println("Permission to access Kerberos"
       
   304                                     + " secret key denied");
       
   305                             se.printStackTrace(System.out);
       
   306                         }
       
   307                         throw new IOException("Kerberos service not allowedy");
       
   308                     }
       
   309                 }
       
   310                 KerberosKey[] serverKeys = AccessController.doPrivileged(
       
   311                         new PrivilegedAction<KerberosKey[]>() {
       
   312                             @Override
       
   313                             public KerberosKey[] run() {
       
   314                                 return creds.getKKeys(princ);
       
   315                             }
       
   316                         });
       
   317                 if (serverKeys.length == 0) {
       
   318                     throw new IOException("Found no key for " + princ +
       
   319                             (creds.getName() == null ? "" :
       
   320                                     (", this keytab is for " + creds.getName() + " only")));
       
   321                 }
       
   322 
       
   323                 /*
       
   324                  * permission to access and use the secret key of the Kerberized
       
   325                  * "host" service is done in ServerHandshaker.getKerberosKeys()
       
   326                  * to ensure server has the permission to use the secret key
       
   327                  * before promising the client
       
   328                  */
       
   329 
       
   330                 // See if we have the right key to decrypt the ticket to get
       
   331                 // the session key.
       
   332                 int encPartKeyType = encPart.getEType();
       
   333                 Integer encPartKeyVersion = encPart.getKeyVersionNumber();
       
   334                 KerberosKey dkey = null;
       
   335                 try {
       
   336                     dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
       
   337                 } catch (KrbException ke) { // a kvno mismatch
       
   338                     throw new IOException(
       
   339                             "Cannot find key matching version number", ke);
       
   340                 }
       
   341                 if (dkey == null) {
       
   342                     // %%% Should print string repr of etype
       
   343                     throw new IOException("Cannot find key of appropriate type" +
       
   344                             " to decrypt ticket - need etype " + encPartKeyType);
       
   345                 }
       
   346 
       
   347                 EncryptionKey secretKey = new EncryptionKey(
       
   348                         encPartKeyType,
       
   349                         dkey.getEncoded());
       
   350 
       
   351                 // Decrypt encPart using server's secret key
       
   352                 byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
       
   353 
       
   354                 // Reset data stream after decryption, remove redundant bytes
       
   355                 byte[] temp = encPart.reset(bytes);
       
   356                 EncTicketPart encTicketPart = new EncTicketPart(temp);
       
   357 
       
   358                 // Record the Kerberos Principals
       
   359                 tmpPeer = new KerberosPrincipal(encTicketPart.cname.getName());
       
   360                 tmpLocal = new KerberosPrincipal(ticketSname.getName());
       
   361 
       
   362                 sessionKey = encTicketPart.key;
       
   363 
       
   364                 if (debug != null && Debug.isOn("handshake")) {
       
   365                     System.out.println("server principal: " + ticketSname);
       
   366                     System.out.println("cname: " + encTicketPart.cname.toString());
       
   367                 }
       
   368             } catch (IOException e) {
       
   369                 throw e;
       
   370             } catch (Exception e) {
       
   371                 if (debug != null && Debug.isOn("handshake")) {
       
   372                     System.out.println("KerberosWrapper error getting session key,"
       
   373                             + " generating random secret (" + e.getMessage() + ")");
       
   374                 }
       
   375                 sessionKey = null;
       
   376             }
       
   377 
       
   378             //input.getBytes16();   // XXX Read and ignore authenticator
       
   379 
       
   380             if (sessionKey != null) {
       
   381                 preMaster = new KerberosPreMasterSecret(protocolVersion,
       
   382                         clientVersion, rand, encrypted, sessionKey);
       
   383             } else {
       
   384                 // Generate bogus premaster secret
       
   385                 preMaster = new KerberosPreMasterSecret(clientVersion, rand);
       
   386             }
       
   387 
       
   388             peerPrincipal = tmpPeer;
       
   389             localPrincipal = tmpLocal;
       
   390         }
       
   391 
       
   392         // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
       
   393         private static KerberosTicket getServiceTicket(String serverName,
       
   394                 final AccessControlContext acc) throws IOException {
       
   395 
       
   396             if ("localhost".equals(serverName) ||
       
   397                     "localhost.localdomain".equals(serverName)) {
       
   398 
       
   399                 if (debug != null && Debug.isOn("handshake")) {
       
   400                     System.out.println("Get the local hostname");
       
   401                 }
       
   402                 String localHost = java.security.AccessController.doPrivileged(
       
   403                         new java.security.PrivilegedAction<String>() {
       
   404                             public String run() {
       
   405                                 try {
       
   406                                     return InetAddress.getLocalHost().getHostName();
       
   407                                 } catch (java.net.UnknownHostException e) {
       
   408                                     if (debug != null && Debug.isOn("handshake")) {
       
   409                                         System.out.println("Warning,"
       
   410                                                 + " cannot get the local hostname: "
       
   411                                                 + e.getMessage());
       
   412                                     }
       
   413                                     return null;
       
   414                                 }
       
   415                             }
       
   416                         });
       
   417                 if (localHost != null) {
       
   418                     serverName = localHost;
       
   419                 }
       
   420             }
       
   421 
       
   422             // Resolve serverName (possibly in IP addr form) to Kerberos principal
       
   423             // name for service with hostname
       
   424             String serviceName = "host/" + serverName;
       
   425             PrincipalName principal;
       
   426             try {
       
   427                 principal = new PrincipalName(serviceName,
       
   428                         PrincipalName.KRB_NT_SRV_HST);
       
   429             } catch (SecurityException se) {
       
   430                 throw se;
       
   431             } catch (Exception e) {
       
   432                 IOException ioe = new IOException("Invalid service principal" +
       
   433                         " name: " + serviceName);
       
   434                 ioe.initCause(e);
       
   435                 throw ioe;
       
   436             }
       
   437             String realm = principal.getRealmAsString();
       
   438 
       
   439             final String serverPrincipal = principal.toString();
       
   440             final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
       
   441             final String clientPrincipal = null;  // use default
       
   442 
       
   443 
       
   444             // check permission to obtain a service ticket to initiate a
       
   445             // context with the "host" service
       
   446             SecurityManager sm = System.getSecurityManager();
       
   447             if (sm != null) {
       
   448                 sm.checkPermission(new ServicePermission(serverPrincipal,
       
   449                         "initiate"), acc);
       
   450             }
       
   451 
       
   452             try {
       
   453                 KerberosTicket ticket = AccessController.doPrivileged(
       
   454                         new PrivilegedExceptionAction<KerberosTicket>() {
       
   455                             public KerberosTicket run() throws Exception {
       
   456                                 return Krb5Util.getTicketFromSubjectAndTgs(
       
   457                                         GSSCaller.CALLER_SSL_CLIENT,
       
   458                                         clientPrincipal, serverPrincipal,
       
   459                                         tgsPrincipal, acc);
       
   460                             }});
       
   461 
       
   462                 if (ticket == null) {
       
   463                     throw new IOException("Failed to find any kerberos service" +
       
   464                             " ticket for " + serverPrincipal);
       
   465                 }
       
   466                 return ticket;
       
   467             } catch (PrivilegedActionException e) {
       
   468                 IOException ioe = new IOException(
       
   469                         "Attempt to obtain kerberos service ticket for " +
       
   470                                 serverPrincipal + " failed!");
       
   471                 ioe.initCause(e);
       
   472                 throw ioe;
       
   473             }
       
   474         }
       
   475 
       
   476         @Override
       
   477         public SecretKey clientKeyExchange() {
       
   478             byte[] secretBytes = preMaster.getUnencrypted();
       
   479             return new SecretKeySpec(secretBytes, "TlsPremasterSecret");
       
   480         }
       
   481 
       
   482         @Override
       
   483         public Principal getPeerPrincipal() {
       
   484             return peerPrincipal;
       
   485         }
       
   486 
       
   487         @Override
       
   488         public Principal getLocalPrincipal() {
       
   489             return localPrincipal;
       
   490         }
       
   491 
       
   492         /**
       
   493          * Determines if a kvno matches another kvno. Used in the method
       
   494          * findKey(etype, version, keys). Always returns true if either input
       
   495          * is null or zero, in case any side does not have kvno info available.
       
   496          *
       
   497          * Note: zero is included because N/A is not a legal value for kvno
       
   498          * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
       
   499          * that the kvno is N/A might be lost when converting between
       
   500          * EncryptionKey and KerberosKey.
       
   501          */
       
   502         private static boolean versionMatches(Integer v1, int v2) {
       
   503             if (v1 == null || v1 == 0 || v2 == 0) {
       
   504                 return true;
       
   505             }
       
   506             return v1.equals(v2);
       
   507         }
       
   508 
       
   509         private static KerberosKey findKey(int etype, Integer version,
       
   510                 KerberosKey[] keys) throws KrbException {
       
   511             int ktype;
       
   512             boolean etypeFound = false;
       
   513 
       
   514             // When no matched kvno is found, returns tke key of the same
       
   515             // etype with the highest kvno
       
   516             int kvno_found = 0;
       
   517             KerberosKey key_found = null;
       
   518 
       
   519             for (int i = 0; i < keys.length; i++) {
       
   520                 ktype = keys[i].getKeyType();
       
   521                 if (etype == ktype) {
       
   522                     int kv = keys[i].getVersionNumber();
       
   523                     etypeFound = true;
       
   524                     if (versionMatches(version, kv)) {
       
   525                         return keys[i];
       
   526                     } else if (kv > kvno_found) {
       
   527                         key_found = keys[i];
       
   528                         kvno_found = kv;
       
   529                     }
       
   530                 }
       
   531             }
       
   532             // Key not found.
       
   533             // %%% kludge to allow DES keys to be used for diff etypes
       
   534             if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
       
   535                     etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
       
   536                 for (int i = 0; i < keys.length; i++) {
       
   537                     ktype = keys[i].getKeyType();
       
   538                     if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
       
   539                             ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
       
   540                         int kv = keys[i].getVersionNumber();
       
   541                         etypeFound = true;
       
   542                         if (versionMatches(version, kv)) {
       
   543                             return new KerberosKey(keys[i].getPrincipal(),
       
   544                                     keys[i].getEncoded(),
       
   545                                     etype,
       
   546                                     kv);
       
   547                         } else if (kv > kvno_found) {
       
   548                             key_found = new KerberosKey(keys[i].getPrincipal(),
       
   549                                     keys[i].getEncoded(),
       
   550                                     etype,
       
   551                                     kv);
       
   552                             kvno_found = kv;
       
   553                         }
       
   554                     }
       
   555                 }
       
   556             }
       
   557             if (etypeFound) {
       
   558                 return key_found;
       
   559             }
       
   560             return null;
       
   561         }
       
   562     }
       
   563 }