ExtendedGSSContext
6710360: export Kerberos session key to applications
Reviewed-by: nobody
diff --git a/src/share/classes/com/sun/security/jgss/ExtendedGSSContext.java b/src/share/classes/com/sun/security/jgss/ExtendedGSSContext.java
new file mode 100644
--- /dev/null
+++ b/src/share/classes/com/sun/security/jgss/ExtendedGSSContext.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.security.jgss;
+
+import org.ietf.jgss.*;
+
+/**
+ * The extended GSSContext interface for supporting additional
+ * functionalities not defined by {@code org.ietf.jgss.GSSContext},
+ * such as querying context-specific attributes.
+ */
+public interface ExtendedGSSContext extends GSSContext {
+ /**
+ * Return the mechanism-specific attribute associated with {@code type}.
+ * <br><br>
+ * For each supported attribute type, the type for the output are
+ * defined below.
+ * <ol>
+ * <li>{@code KRB5_GET_SESSION_KEY}:
+ * the returned object is an instance of {@link java.security.Key},
+ * which has the following properties:
+ * <ul>
+ * <li>Algorithm: enctype as a string, where
+ * enctype is defined in RFC 3961, section 8.
+ * <li>Format: "RAW"
+ * <li>Encoded form: the raw key bytes, not in any ASN.1 encoding
+ * </ul>
+ * </ol>
+ *
+ * If there is a security manager, an {@link InquireSecContextPermission}
+ * with the name {@code type.mech} must be granted. Otherwise, this could
+ * result in a {@link SecurityException}.<p>
+ *
+ * Example:
+ * <pre>
+ * GSSContext ctxt = m.createContext(...)
+ * // Establishing the context
+ * if (ctxt instanceof ExtendedGSSContext) {
+ * ExtendedGSSContext ex = (ExtendedGSSContext)ctxt;
+ * try {
+ * Key key = (key)ex.inquireSecContext(
+ * InquireType.KRB5_GET_SESSION_KEY);
+ * // read key info
+ * } catch (GSSException gsse) {
+ * // deal with exception
+ * }
+ * }
+ * </pre>
+ * @param type the type of the attribute requested
+ * @return the attribute, see the method documentation for details.
+ * @throws GSSException containing the following
+ * major error codes:
+ * {@link GSSException#BAD_MECH GSSException.BAD_MECH} if the mechanism
+ * does not support this method,
+ * {@link GSSException#UNAVAILABLE GSSException.UNAVAILABLE} if the
+ * type specified is not supported,
+ * {@link GSSException#NO_CONTEXT GSSException.NO_CONTEXT} if the
+ * security context is invalid,
+ * {@link GSSException#FAILURE GSSException.FAILURE} for other
+ * unspecified failures.
+ * @throws SecurityException if a security manager exists and a proper
+ * {@link InquireSecContextPermission} is not granted.
+ * @see InquireSecContextPermission
+ */
+ public Object inquireSecContext(InquireType type)
+ throws GSSException;
+}
diff --git a/src/share/classes/com/sun/security/jgss/InquireSecContextPermission.java b/src/share/classes/com/sun/security/jgss/InquireSecContextPermission.java
new file mode 100644
--- /dev/null
+++ b/src/share/classes/com/sun/security/jgss/InquireSecContextPermission.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.security.jgss;
+
+import java.security.BasicPermission;
+import java.security.Permission;
+
+/**
+ * This class is used to protect various attributes of an established
+ * GSS security context that can be accessed using the
+ * {@link com.sun.security.jgss.ExtendedGSSContext#inquireSecContext}
+ * method.
+ */
+final public class InquireSecContextPermission extends Permission {
+
+ /**
+ * Constructs an InquireSecContextPermission object.
+ * @param name name of the permission, which has the form
+ * {@code type.mech}. {@code type} is the {@link InquireType}
+ * inquired, and {@code mech} is the Oid specifying the
+ * mechanism of the current security context. Both value
+ * can take the form of "*" which matches any value given.
+ */
+ public InquireSecContextPermission(String name) {
+ super(name);
+ init(name);
+ }
+
+ /**
+ * Checks if the specified permission is "implied" by
+ * this object.
+ * <P>
+ * More specifically, this method returns true if:<p>
+ * <ul>
+ * <li> <i>p</i>'s class is the same as this object's class, and<p>
+ * <li> <i>p</i>'s name equals or (in the case of wildcards)
+ * is implied by this object's
+ * name. For example, each of "a.*", "*.b", "*.*" implies "a.b".
+ * </ul>
+ *
+ * @param p the permission to check against.
+ *
+ * @return true if the passed permission is equal to or
+ * implied by this permission, false otherwise.
+ */
+ @Override
+ public boolean implies(Permission p) {
+ if ((p == null) || (p.getClass() != getClass()))
+ return false;
+
+ InquireSecContextPermission that = (InquireSecContextPermission) p;
+
+ boolean typeOK = false;
+ boolean mechOK = false;
+
+ if (type.equals("*") || type.equals(that.type)) {
+ typeOK = true;
+ }
+ if (mech.equals("*") || mech.equals(that.mech)) {
+ mechOK = true;
+ }
+
+ return typeOK && mechOK;
+ }
+
+ private String type = null;
+ private String mech = null;
+
+ private void init(String name) {
+ int dot = name.indexOf('.');
+ if (dot < 0) {
+ throw new IllegalArgumentException(
+ "Name must have the form of type.mech");
+ }
+ type = name.substring(0, dot);
+ mech = name.substring(dot+1);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+
+ if ((obj == null) || (obj.getClass() != getClass()))
+ return false;
+
+ Permission bp = (Permission) obj;
+
+ return getName().equals(bp.getName());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ /**
+ * Returns the canonical string representation of the actions. For
+ * an InquireSecContextPermission, it's always "".
+ *
+ * @return the empty string "".
+ */
+ @Override
+ public String getActions() {
+ return "";
+ }
+}
diff --git a/src/share/classes/com/sun/security/jgss/InquireType.java b/src/share/classes/com/sun/security/jgss/InquireType.java
new file mode 100644
--- /dev/null
+++ b/src/share/classes/com/sun/security/jgss/InquireType.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.security.jgss;
+
+/**
+ * Attribute types that can be accepted by
+ * {@link com.sun.security.jgss.ExtendedGSSContext#inquireSecContext}
+ */
+public enum InquireType {
+ /**
+ * Attribute type for the session key of an established security context.
+ */
+ KRB5_GET_SESSION_KEY
+}
diff --git a/src/share/classes/sun/security/jgss/GSSContextImpl.java b/src/share/classes/sun/security/jgss/GSSContextImpl.java
--- a/src/share/classes/sun/security/jgss/GSSContextImpl.java
+++ b/src/share/classes/sun/security/jgss/GSSContextImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,14 +27,13 @@
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
-import sun.security.jgss.*;
import sun.security.util.ObjectIdentifier;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-
+import com.sun.security.jgss.*;
/**
* This class represents the JGSS security context and its associated
@@ -88,7 +87,7 @@
* per-message operations are returned in an instance of the MessageProp
* class, which is used as an argument in these calls.</dl>
*/
-class GSSContextImpl implements GSSContext {
+class GSSContextImpl implements ExtendedGSSContext {
private GSSManagerImpl gssManager = null;
@@ -630,4 +629,18 @@
srcName = null;
targName = null;
}
+
+ @Override
+ public Object inquireSecContext(InquireType type) throws GSSException {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ String oidStr = "*";
+ if (getMech() != null) oidStr = getMech().toString();
+ security.checkPermission(new InquireSecContextPermission(type.toString() + "." + oidStr));
+ }
+ if (mechCtxt == null) {
+ throw new GSSException(GSSException.NO_CONTEXT);
+ }
+ return mechCtxt.inquireSecContext(type);
+ }
}
diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java
--- a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,13 +37,13 @@
import java.security.Provider;
import java.security.AccessController;
import java.security.AccessControlContext;
-import java.security.GeneralSecurityException;
-import java.security.PrivilegedAction;
+import java.security.Key;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import javax.crypto.Cipher;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.*;
+import com.sun.security.jgss.*;
/**
* Implements the mechanism specific context class for the Kerberos v5
@@ -1284,4 +1284,54 @@
// Currently used by InitialToken only
return caller;
}
+
+ /**
+ * The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY)
+ */
+ static class KerberosSessionKey implements Key {
+ private EncryptionKey key;
+
+ KerberosSessionKey(EncryptionKey key) {
+ this.key = key;
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return Integer.toString(key.getEType());
+ }
+
+ @Override
+ public String getFormat() {
+ return "RAW";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return key.getBytes().clone();
+ }
+
+ @Override
+ public String toString() {
+ return "Kerberos session key: etype: " + key.getEType() + "\n" +
+ new sun.misc.HexDumpEncoder().encodeBuffer(key.getBytes());
+ }
+ }
+
+ /**
+ * Return the mechanism-specific attribute associated with {@code type}.
+ * Only KRB5_GET_SESSION_KEY is supported now.
+ */
+ public Object inquireSecContext(InquireType type)
+ throws GSSException {
+ if (type == InquireType.KRB5_GET_SESSION_KEY) {
+ if (key == null) {
+ throw new GSSException(GSSException.NO_CONTEXT, -1,
+ "Session key not established.");
+ } else {
+ return new KerberosSessionKey(key);
+ }
+ }
+ throw new GSSException(GSSException.UNAVAILABLE, -1,
+ "Inquire type not supported.");
+ }
}
diff --git a/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java b/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java
--- a/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java
+++ b/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java
@@ -1,5 +1,5 @@
/*
- * Portions Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,10 +42,12 @@
package sun.security.jgss.spi;
+import com.sun.xml.internal.ws.pept.ept.MessageInfo;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Provider;
+import com.sun.security.jgss.*;
/**
* This interface is implemented by a mechanism specific instance of a GSS
@@ -403,4 +405,14 @@
* @exception GSSException may be thrown
*/
public void dispose() throws GSSException;
+
+ /**
+ * Return the mechanism-specific attribute associated with (@code type}.
+ *
+ * @param type the type of the attribute requested
+ * @return the attribute
+ * @throws GSSException may be thrown
+ */
+ public Object inquireSecContext(InquireType type)
+ throws GSSException;
}
diff --git a/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java b/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java
--- a/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java
+++ b/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java
@@ -25,10 +25,10 @@
package sun.security.jgss.spnego;
+import com.sun.security.jgss.ExtendedGSSContext;
+import com.sun.security.jgss.InquireType;
import java.io.*;
import java.security.Provider;
-import java.util.List;
-import java.util.ArrayList;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.jgss.spi.*;
@@ -1185,4 +1185,22 @@
return ("Unknown state " + state);
}
}
+
+ /**
+ * Retrieve attribute of the context for {@code type}.
+ */
+ public Object inquireSecContext(InquireType type)
+ throws GSSException {
+ if (mechContext == null) {
+ throw new GSSException(GSSException.NO_CONTEXT, -1,
+ "Underlying mech not established.");
+ }
+ if (mechContext instanceof ExtendedGSSContext) {
+ return ((ExtendedGSSContext)mechContext).inquireSecContext(type);
+ } else {
+ throw new GSSException(GSSException.BAD_MECH, -1,
+ "inquireSecContext not supported by underlying mech.");
+ }
+ }
+
}
diff --git a/src/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java b/src/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java
--- a/src/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java
+++ b/src/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,6 +36,7 @@
import sun.security.jgss.spnego.NegTokenInit;
import sun.security.jgss.spnego.NegTokenTarg;
import javax.security.auth.kerberos.DelegationPermission;
+import com.sun.security.jgss.InquireType;
import java.io.*;
@@ -615,4 +616,10 @@
protected void finalize() throws Throwable {
dispose();
}
+
+ public Object inquireSecContext(InquireType type)
+ throws GSSException {
+ throw new GSSException(GSSException.UNAVAILABLE, -1,
+ "Inquire type not supported.");
+ }
}
diff --git a/src/share/classes/sun/security/tools/PolicyTool.java b/src/share/classes/sun/security/tools/PolicyTool.java
--- a/src/share/classes/sun/security/tools/PolicyTool.java
+++ b/src/share/classes/sun/security/tools/PolicyTool.java
@@ -1459,6 +1459,7 @@
PERM_ARRAY.add(new AWTPerm());
PERM_ARRAY.add(new DelegationPerm());
PERM_ARRAY.add(new FilePerm());
+ PERM_ARRAY.add(new InqPerm());
PERM_ARRAY.add(new LogPerm());
PERM_ARRAY.add(new MgmtPerm());
PERM_ARRAY.add(new MBeanPerm());
@@ -3961,6 +3962,18 @@
}
}
+class InqPerm extends Perm {
+ public InqPerm() {
+ super("InquireSecContextPermission",
+ "com.sun.security.jgss.InquireSecContextPermission",
+ new String[] {
+ "INQ_SSPI_SESSION_KEY.1.2.840.113554.1.2.2",
+ "INQ_SSPI_SESSION_KEY.1.3.6.1.5.5.2"
+ },
+ null);
+ }
+}
+
class LogPerm extends Perm {
public LogPerm() {
super("LoggingPermission",
diff --git a/test/com/sun/security/jgss/InquireSecContextPermissionCheck.java b/test/com/sun/security/jgss/InquireSecContextPermissionCheck.java
new file mode 100644
--- /dev/null
+++ b/test/com/sun/security/jgss/InquireSecContextPermissionCheck.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * @test
+ * @bug 6710360
+ * @summary export Kerberos session key to applications
+ */
+
+import com.sun.security.jgss.InquireSecContextPermission;
+
+public class InquireSecContextPermissionCheck {
+
+ public static void main(String[] args) throws Exception {
+
+ InquireSecContextPermission p0, p1, p2, p3, p4;
+ p0 = new InquireSecContextPermission(
+ "KRB5_GET_SESSION_KEY.1.3.6.1.5.5.2");
+ p1 = new InquireSecContextPermission(
+ "KRB5_GET_SESSION_KEY.1.3.6.1.5.5.2");
+ p2 = new InquireSecContextPermission("KRB5_GET_SESSION_KEY.*");
+ p3 = new InquireSecContextPermission("*.1.3.6.1.5.5.2");
+ p4 = new InquireSecContextPermission("*.*");
+
+ if (!p1.implies(p0) || !p2.implies(p0)
+ || !p3.implies(p0) || !p4.implies(p0)
+ || !p4.implies(p2) || !p4.implies(p3)) {
+ throw new Exception("Check failed");
+ }
+ }
+}
+
diff --git a/test/sun/security/krb5/auto/Context.java b/test/sun/security/krb5/auto/Context.java
--- a/test/sun/security/krb5/auto/Context.java
+++ b/test/sun/security/krb5/auto/Context.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
*/
import com.sun.security.auth.module.Krb5LoginModule;
+import java.security.Key;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
@@ -38,6 +39,8 @@
import org.ietf.jgss.GSSName;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.Oid;
+import com.sun.security.jgss.ExtendedGSSContext;
+import com.sun.security.jgss.InquireType;
/**
* Context of a JGSS subject, encapsulating Subject and GSSContext.
@@ -276,6 +279,14 @@
}
}
}
+ if (x != null && x instanceof ExtendedGSSContext) {
+ if (x.isEstablished()) {
+ ExtendedGSSContext ex = (ExtendedGSSContext)x;
+ Key k = (Key)ex.inquireSecContext(
+ InquireType.KRB5_GET_SESSION_KEY);
+ System.out.println("Session key is: " + k);
+ }
+ }
}
/**