feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,181 @@
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.util.*;
import java.security.Principal;
import java.security.acl.*;
/**
* This is a class that describes one entry that associates users
* or groups with permissions in the ACL.
* The entry may be used as a way of granting or denying permissions.
* @author Satish Dharmaraj
*/
public class AclEntryImpl implements AclEntry {
private Principal user = null;
private Vector<Permission> permissionSet = new Vector<>(10, 10);
private boolean negative = false;
/**
* Construct an ACL entry that associates a user with permissions
* in the ACL.
* @param user The user that is associated with this entry.
*/
public AclEntryImpl(Principal user) {
this.user = user;
}
/**
* Construct a null ACL entry
*/
public AclEntryImpl() {
}
/**
* Sets the principal in the entity. If a group or a
* principal had already been set, a false value is
* returned, otherwise a true value is returned.
* @param user The user that is associated with this entry.
* @return true if the principal is set, false if there is
* one already.
*/
public boolean setPrincipal(Principal user) {
if (this.user != null)
return false;
this.user = user;
return true;
}
/**
* This method sets the ACL to have negative permissions.
* That is the user or group is denied the permission set
* specified in the entry.
*/
public void setNegativePermissions() {
negative = true;
}
/**
* Returns true if this is a negative ACL.
*/
public boolean isNegative() {
return negative;
}
/**
* A principal or a group can be associated with multiple
* permissions. This method adds a permission to the ACL entry.
* @param permission The permission to be associated with
* the principal or the group in the entry.
* @return true if the permission was added, false if the
* permission was already part of the permission set.
*/
public boolean addPermission(Permission permission) {
if (permissionSet.contains(permission))
return false;
permissionSet.addElement(permission);
return true;
}
/**
* The method disassociates the permission from the Principal
* or the Group in this ACL entry.
* @param permission The permission to be disassociated with
* the principal or the group in the entry.
* @return true if the permission is removed, false if the
* permission is not part of the permission set.
*/
public boolean removePermission(Permission permission) {
return permissionSet.removeElement(permission);
}
/**
* Checks if the passed permission is part of the allowed
* permission set in this entry.
* @param permission The permission that has to be part of
* the permission set in the entry.
* @return true if the permission passed is part of the
* permission set in the entry, false otherwise.
*/
public boolean checkPermission(Permission permission) {
return permissionSet.contains(permission);
}
/**
* return an enumeration of the permissions in this ACL entry.
*/
public Enumeration<Permission> permissions() {
return permissionSet.elements();
}
/**
* Return a string representation of the contents of the ACL entry.
*/
public String toString() {
StringBuffer s = new StringBuffer();
if (negative)
s.append("-");
else
s.append("+");
if (user instanceof Group)
s.append("Group.");
else
s.append("User.");
s.append(user + "=");
Enumeration<Permission> e = permissions();
while(e.hasMoreElements()) {
Permission p = e.nextElement();
s.append(p);
if (e.hasMoreElements())
s.append(",");
}
return new String(s);
}
/**
* Clones an AclEntry.
*/
@SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly
public synchronized Object clone() {
AclEntryImpl cloned;
cloned = new AclEntryImpl(user);
cloned.permissionSet = (Vector<Permission>) permissionSet.clone();
cloned.negative = negative;
return cloned;
}
/**
* Return the Principal associated in this ACL entry.
* The method returns null if the entry uses a group
* instead of a principal.
*/
public Principal getPrincipal() {
return user;
}
}

View File

@@ -0,0 +1,408 @@
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.io.*;
import java.util.*;
import java.security.Principal;
import java.security.acl.*;
/**
* An Access Control List (ACL) is encapsulated by this class.
* @author Satish Dharmaraj
*/
public class AclImpl extends OwnerImpl implements Acl {
//
// Maintain four tables. one each for positive and negative
// ACLs. One each depending on whether the entity is a group
// or principal.
//
private Hashtable<Principal, AclEntry> allowedUsersTable =
new Hashtable<>(23);
private Hashtable<Principal, AclEntry> allowedGroupsTable =
new Hashtable<>(23);
private Hashtable<Principal, AclEntry> deniedUsersTable =
new Hashtable<>(23);
private Hashtable<Principal, AclEntry> deniedGroupsTable =
new Hashtable<>(23);
private String aclName = null;
private Vector<Permission> zeroSet = new Vector<>(1,1);
/**
* Constructor for creating an empty ACL.
*/
public AclImpl(Principal owner, String name) {
super(owner);
try {
setName(owner, name);
} catch (Exception e) {}
}
/**
* Sets the name of the ACL.
* @param caller the principal who is invoking this method.
* @param name the name of the ACL.
* @exception NotOwnerException if the caller principal is
* not on the owners list of the Acl.
*/
public void setName(Principal caller, String name)
throws NotOwnerException
{
if (!isOwner(caller))
throw new NotOwnerException();
aclName = name;
}
/**
* Returns the name of the ACL.
* @return the name of the ACL.
*/
public String getName() {
return aclName;
}
/**
* Adds an ACL entry to this ACL. An entry associates a
* group or a principal with a set of permissions. Each
* user or group can have one positive ACL entry and one
* negative ACL entry. If there is one of the type (negative
* or positive) already in the table, a false value is returned.
* The caller principal must be a part of the owners list of
* the ACL in order to invoke this method.
* @param caller the principal who is invoking this method.
* @param entry the ACL entry that must be added to the ACL.
* @return true on success, false if the entry is already present.
* @exception NotOwnerException if the caller principal
* is not on the owners list of the Acl.
*/
public synchronized boolean addEntry(Principal caller, AclEntry entry)
throws NotOwnerException
{
if (!isOwner(caller))
throw new NotOwnerException();
Hashtable<Principal, AclEntry> aclTable = findTable(entry);
Principal key = entry.getPrincipal();
if (aclTable.get(key) != null)
return false;
aclTable.put(key, entry);
return true;
}
/**
* Removes an ACL entry from this ACL.
* The caller principal must be a part of the owners list of the ACL
* in order to invoke this method.
* @param caller the principal who is invoking this method.
* @param entry the ACL entry that must be removed from the ACL.
* @return true on success, false if the entry is not part of the ACL.
* @exception NotOwnerException if the caller principal is not
* the owners list of the Acl.
*/
public synchronized boolean removeEntry(Principal caller, AclEntry entry)
throws NotOwnerException
{
if (!isOwner(caller))
throw new NotOwnerException();
Hashtable<Principal, AclEntry> aclTable = findTable(entry);
Principal key = entry.getPrincipal();
AclEntry o = aclTable.remove(key);
return (o != null);
}
/**
* This method returns the set of allowed permissions for the
* specified principal. This set of allowed permissions is calculated
* as follows:
*
* If there is no entry for a group or a principal an empty permission
* set is assumed.
*
* The group positive permission set is the union of all
* the positive permissions of each group that the individual belongs to.
* The group negative permission set is the union of all
* the negative permissions of each group that the individual belongs to.
* If there is a specific permission that occurs in both
* the postive permission set and the negative permission set,
* it is removed from both. The group positive and negatoive permission
* sets are calculated.
*
* The individial positive permission set and the individual negative
* permission set is then calculated. Again abscence of an entry means
* the empty set.
*
* The set of permissions granted to the principal is then calculated using
* the simple rule: Individual permissions always override the Group permissions.
* Specifically, individual negative permission set (specific
* denial of permissions) overrides the group positive permission set.
* And the individual positive permission set override the group negative
* permission set.
*
* @param user the principal for which the ACL entry is returned.
* @return The resulting permission set that the principal is allowed.
*/
public synchronized Enumeration<Permission> getPermissions(Principal user) {
Enumeration<Permission> individualPositive;
Enumeration<Permission> individualNegative;
Enumeration<Permission> groupPositive;
Enumeration<Permission> groupNegative;
//
// canonicalize the sets. That is remove common permissions from
// positive and negative sets.
//
groupPositive =
subtract(getGroupPositive(user), getGroupNegative(user));
groupNegative =
subtract(getGroupNegative(user), getGroupPositive(user));
individualPositive =
subtract(getIndividualPositive(user), getIndividualNegative(user));
individualNegative =
subtract(getIndividualNegative(user), getIndividualPositive(user));
//
// net positive permissions is individual positive permissions
// plus (group positive - individual negative).
//
Enumeration<Permission> temp1 =
subtract(groupPositive, individualNegative);
Enumeration<Permission> netPositive =
union(individualPositive, temp1);
// recalculate the enumeration since we lost it in performing the
// subtraction
//
individualPositive =
subtract(getIndividualPositive(user), getIndividualNegative(user));
individualNegative =
subtract(getIndividualNegative(user), getIndividualPositive(user));
//
// net negative permissions is individual negative permissions
// plus (group negative - individual positive).
//
temp1 = subtract(groupNegative, individualPositive);
Enumeration<Permission> netNegative = union(individualNegative, temp1);
return subtract(netPositive, netNegative);
}
/**
* This method checks whether or not the specified principal
* has the required permission. If permission is denied
* permission false is returned, a true value is returned otherwise.
* This method does not authenticate the principal. It presumes that
* the principal is a valid authenticated principal.
* @param principal the name of the authenticated principal
* @param permission the permission that the principal must have.
* @return true of the principal has the permission desired, false
* otherwise.
*/
public boolean checkPermission(Principal principal, Permission permission)
{
Enumeration<Permission> permSet = getPermissions(principal);
while (permSet.hasMoreElements()) {
Permission p = permSet.nextElement();
if (p.equals(permission))
return true;
}
return false;
}
/**
* returns an enumeration of the entries in this ACL.
*/
public synchronized Enumeration<AclEntry> entries() {
return new AclEnumerator(this,
allowedUsersTable, allowedGroupsTable,
deniedUsersTable, deniedGroupsTable);
}
/**
* return a stringified version of the
* ACL.
*/
public String toString() {
StringBuffer sb = new StringBuffer();
Enumeration<AclEntry> entries = entries();
while (entries.hasMoreElements()) {
AclEntry entry = entries.nextElement();
sb.append(entry.toString().trim());
sb.append("\n");
}
return sb.toString();
}
//
// Find the table that this entry belongs to. There are 4
// tables that are maintained. One each for postive and
// negative ACLs and one each for groups and users.
// This method figures out which
// table is the one that this AclEntry belongs to.
//
private Hashtable<Principal, AclEntry> findTable(AclEntry entry) {
Hashtable<Principal, AclEntry> aclTable = null;
Principal p = entry.getPrincipal();
if (p instanceof Group) {
if (entry.isNegative())
aclTable = deniedGroupsTable;
else
aclTable = allowedGroupsTable;
} else {
if (entry.isNegative())
aclTable = deniedUsersTable;
else
aclTable = allowedUsersTable;
}
return aclTable;
}
//
// returns the set e1 U e2.
//
private static Enumeration<Permission> union(Enumeration<Permission> e1,
Enumeration<Permission> e2) {
Vector<Permission> v = new Vector<>(20, 20);
while (e1.hasMoreElements())
v.addElement(e1.nextElement());
while (e2.hasMoreElements()) {
Permission o = e2.nextElement();
if (!v.contains(o))
v.addElement(o);
}
return v.elements();
}
//
// returns the set e1 - e2.
//
private Enumeration<Permission> subtract(Enumeration<Permission> e1,
Enumeration<Permission> e2) {
Vector<Permission> v = new Vector<>(20, 20);
while (e1.hasMoreElements())
v.addElement(e1.nextElement());
while (e2.hasMoreElements()) {
Permission o = e2.nextElement();
if (v.contains(o))
v.removeElement(o);
}
return v.elements();
}
private Enumeration<Permission> getGroupPositive(Principal user) {
Enumeration<Permission> groupPositive = zeroSet.elements();
Enumeration<Principal> e = allowedGroupsTable.keys();
while (e.hasMoreElements()) {
Group g = (Group)e.nextElement();
if (g.isMember(user)) {
AclEntry ae = allowedGroupsTable.get(g);
groupPositive = union(ae.permissions(), groupPositive);
}
}
return groupPositive;
}
private Enumeration<Permission> getGroupNegative(Principal user) {
Enumeration<Permission> groupNegative = zeroSet.elements();
Enumeration<Principal> e = deniedGroupsTable.keys();
while (e.hasMoreElements()) {
Group g = (Group)e.nextElement();
if (g.isMember(user)) {
AclEntry ae = deniedGroupsTable.get(g);
groupNegative = union(ae.permissions(), groupNegative);
}
}
return groupNegative;
}
private Enumeration<Permission> getIndividualPositive(Principal user) {
Enumeration<Permission> individualPositive = zeroSet.elements();
AclEntry ae = allowedUsersTable.get(user);
if (ae != null)
individualPositive = ae.permissions();
return individualPositive;
}
private Enumeration<Permission> getIndividualNegative(Principal user) {
Enumeration<Permission> individualNegative = zeroSet.elements();
AclEntry ae = deniedUsersTable.get(user);
if (ae != null)
individualNegative = ae.permissions();
return individualNegative;
}
}
final class AclEnumerator implements Enumeration<AclEntry> {
Acl acl;
Enumeration<AclEntry> u1, u2, g1, g2;
AclEnumerator(Acl acl, Hashtable<?,AclEntry> u1, Hashtable<?,AclEntry> g1,
Hashtable<?,AclEntry> u2, Hashtable<?,AclEntry> g2) {
this.acl = acl;
this.u1 = u1.elements();
this.u2 = u2.elements();
this.g1 = g1.elements();
this.g2 = g2.elements();
}
public boolean hasMoreElements() {
return (u1.hasMoreElements() ||
u2.hasMoreElements() ||
g1.hasMoreElements() ||
g2.hasMoreElements());
}
public AclEntry nextElement()
{
AclEntry o;
synchronized (acl) {
if (u1.hasMoreElements())
return u1.nextElement();
if (u2.hasMoreElements())
return u2.nextElement();
if (g1.hasMoreElements())
return g1.nextElement();
if (g2.hasMoreElements())
return g2.nextElement();
}
throw new NoSuchElementException("Acl Enumerator");
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 1996, 1997, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.security.Principal;
import java.security.acl.*;
/**
* This class implements the principal interface for the set of all permissions.
* @author Satish Dharmaraj
*/
public class AllPermissionsImpl extends PermissionImpl {
public AllPermissionsImpl(String s) {
super(s);
}
/**
* This function returns true if the permission passed matches the permission represented in
* this interface.
* @param another The Permission object to compare with.
* @returns true always
*/
public boolean equals(Permission another) {
return true;
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.util.*;
import java.security.*;
import java.security.acl.*;
/**
* This class implements a group of principals.
* @author Satish Dharmaraj
*/
public class GroupImpl implements Group {
private Vector<Principal> groupMembers = new Vector<>(50, 100);
private String group;
/**
* Constructs a Group object with no members.
* @param groupName the name of the group
*/
public GroupImpl(String groupName) {
this.group = groupName;
}
/**
* adds the specified member to the group.
* @param user The principal to add to the group.
* @return true if the member was added - false if the
* member could not be added.
*/
public boolean addMember(Principal user) {
if (groupMembers.contains(user))
return false;
// do not allow groups to be added to itself.
if (group.equals(user.toString()))
throw new IllegalArgumentException();
groupMembers.addElement(user);
return true;
}
/**
* removes the specified member from the group.
* @param user The principal to remove from the group.
* @param true if the principal was removed false if
* the principal was not a member
*/
public boolean removeMember(Principal user) {
return groupMembers.removeElement(user);
}
/**
* returns the enumeration of the members in the group.
*/
public Enumeration<? extends Principal> members() {
return groupMembers.elements();
}
/**
* This function returns true if the group passed matches
* the group represented in this interface.
* @param another The group to compare this group to.
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Group == false) {
return false;
}
Group another = (Group)obj;
return group.equals(another.toString());
}
// equals(Group) for compatibility
public boolean equals(Group another) {
return equals((Object)another);
}
/**
* Prints a stringified version of the group.
*/
public String toString() {
return group;
}
/**
* return a hashcode for the principal.
*/
public int hashCode() {
return group.hashCode();
}
/**
* returns true if the passed principal is a member of the group.
* @param member The principal whose membership must be checked for.
* @return true if the principal is a member of this group,
* false otherwise
*/
public boolean isMember(Principal member) {
//
// if the member is part of the group (common case), return true.
// if not, recursively search depth first in the group looking for the
// principal.
//
if (groupMembers.contains(member)) {
return true;
} else {
Vector<Group> alreadySeen = new Vector<>(10);
return isMemberRecurse(member, alreadySeen);
}
}
/**
* return the name of the principal.
*/
public String getName() {
return group;
}
//
// This function is the recursive search of groups for this
// implementation of the Group. The search proceeds building up
// a vector of already seen groups. Only new groups are considered,
// thereby avoiding loops.
//
boolean isMemberRecurse(Principal member, Vector<Group> alreadySeen) {
Enumeration<? extends Principal> e = members();
while (e.hasMoreElements()) {
boolean mem = false;
Principal p = (Principal) e.nextElement();
// if the member is in this collection, return true
if (p.equals(member)) {
return true;
} else if (p instanceof GroupImpl) {
//
// if not recurse if the group has not been checked already.
// Can call method in this package only if the object is an
// instance of this class. Otherwise call the method defined
// in the interface. (This can lead to a loop if a mixture of
// implementations form a loop, but we live with this improbable
// case rather than clutter the interface by forcing the
// implementation of this method.)
//
GroupImpl g = (GroupImpl) p;
alreadySeen.addElement(this);
if (!alreadySeen.contains(g))
mem = g.isMemberRecurse(member, alreadySeen);
} else if (p instanceof Group) {
Group g = (Group) p;
if (!alreadySeen.contains(g))
mem = g.isMember(member);
}
if (mem)
return mem;
}
return false;
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 1996, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.util.*;
import java.security.*;
import java.security.acl.*;
/**
* Class implementing the Owner interface. The
* initial owner principal is configured as
* part of the constructor.
* @author Satish Dharmaraj
*/
public class OwnerImpl implements Owner {
private Group ownerGroup;
public OwnerImpl(Principal owner) {
ownerGroup = new GroupImpl("AclOwners");
ownerGroup.addMember(owner);
}
/**
* Adds an owner. Owners can modify ACL contents and can disassociate
* ACLs from the objects they protect in the AclConfig interface.
* The caller principal must be a part of the owners list of the ACL in
* order to invoke this method. The initial owner is configured
* at ACL construction time.
* @param caller the principal who is invoking this method.
* @param owner The owner that should be added to the owners list.
* @return true if success, false if already an owner.
* @exception NotOwnerException if the caller principal is not on
* the owners list of the Acl.
*/
public synchronized boolean addOwner(Principal caller, Principal owner)
throws NotOwnerException
{
if (!isOwner(caller))
throw new NotOwnerException();
ownerGroup.addMember(owner);
return false;
}
/**
* Delete owner. If this is the last owner in the ACL, an exception is
* raised.
* The caller principal must be a part of the owners list of the ACL in
* order to invoke this method.
* @param caller the principal who is invoking this method.
* @param owner The owner to be removed from the owners list.
* @return true if the owner is removed, false if the owner is not part
* of the owners list.
* @exception NotOwnerException if the caller principal is not on
* the owners list of the Acl.
* @exception LastOwnerException if there is only one owner left in the group, then
* deleteOwner would leave the ACL owner-less. This exception is raised in such a case.
*/
public synchronized boolean deleteOwner(Principal caller, Principal owner)
throws NotOwnerException, LastOwnerException
{
if (!isOwner(caller))
throw new NotOwnerException();
Enumeration<? extends Principal> e = ownerGroup.members();
//
// check if there is atleast 2 members left.
//
Object o = e.nextElement();
if (e.hasMoreElements())
return ownerGroup.removeMember(owner);
else
throw new LastOwnerException();
}
/**
* returns if the given principal belongs to the owner list.
* @param owner The owner to check if part of the owners list
* @return true if the passed principal is in the owner list, false if not.
*/
public synchronized boolean isOwner(Principal owner) {
return ownerGroup.isMember(owner);
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 1996, 1999, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.security.Principal;
import java.security.acl.*;
/**
* The PermissionImpl class implements the permission
* interface for permissions that are strings.
* @author Satish Dharmaraj
*/
public class PermissionImpl implements Permission {
private String permission;
/**
* Construct a permission object using a string.
* @param permission the stringified version of the permission.
*/
public PermissionImpl(String permission) {
this.permission = permission;
}
/**
* This function returns true if the object passed matches the permission
* represented in this interface.
* @param another The Permission object to compare with.
* @return true if the Permission objects are equal, false otherwise
*/
public boolean equals(Object another) {
if (another instanceof Permission) {
Permission p = (Permission) another;
return permission.equals(p.toString());
} else {
return false;
}
}
/**
* Prints a stringified version of the permission.
* @return the string representation of the Permission.
*/
public String toString() {
return permission;
}
/**
* Returns a hashcode for this PermissionImpl.
*
* @return a hashcode for this PermissionImpl.
*/
public int hashCode() {
return toString().hashCode();
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 1996, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.security.*;
/**
* This class implements the principal interface.
*
* @author Satish Dharmaraj
*/
public class PrincipalImpl implements Principal {
private String user;
/**
* Construct a principal from a string user name.
* @param user The string form of the principal name.
*/
public PrincipalImpl(String user) {
this.user = user;
}
/**
* This function returns true if the object passed matches
* the principal represented in this implementation
* @param another the Principal to compare with.
* @return true if the Principal passed is the same as that
* encapsulated in this object, false otherwise
*/
public boolean equals(Object another) {
if (another instanceof PrincipalImpl) {
PrincipalImpl p = (PrincipalImpl) another;
return user.equals(p.toString());
} else
return false;
}
/**
* Prints a stringified version of the principal.
*/
public String toString() {
return user;
}
/**
* return a hashcode for the principal.
*/
public int hashCode() {
return user.hashCode();
}
/**
* return the name of the principal.
*/
public String getName() {
return user;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 1996, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.security.*;
/**
* This class implements a group of principals.
* @author Satish Dharmaraj
*/
public class WorldGroupImpl extends GroupImpl {
public WorldGroupImpl(String s) {
super(s);
}
/**
* returns true for all passed principals
* @param member The principal whose membership must be checked in this Group.
* @return true always since this is the "world" group.
*/
public boolean isMember(Principal member) {
return true;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.action;
/**
* A convenience class for retrieving the boolean value of a system property
* as a privileged action.
*
* <p>An instance of this class can be used as the argument of
* <code>AccessController.doPrivileged</code>.
*
* <p>The following code retrieves the boolean value of the system
* property named <code>"prop"</code> as a privileged action: <p>
*
* <pre>
* boolean b = java.security.AccessController.doPrivileged
* (new GetBooleanAction("prop")).booleanValue();
* </pre>
*
* @author Roland Schemers
* @see java.security.PrivilegedAction
* @see java.security.AccessController
* @since 1.2
*/
public class GetBooleanAction
implements java.security.PrivilegedAction<Boolean> {
private String theProp;
/**
* Constructor that takes the name of the system property whose boolean
* value needs to be determined.
*
* @param theProp the name of the system property.
*/
public GetBooleanAction(String theProp) {
this.theProp = theProp;
}
/**
* Determines the boolean value of the system property whose name was
* specified in the constructor.
*
* @return the <code>Boolean</code> value of the system property.
*/
public Boolean run() {
return Boolean.getBoolean(theProp);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.action;
import java.security.Security;
/**
* A convenience class for retrieving the boolean value of a security property
* as a privileged action.
*
* <p>An instance of this class can be used as the argument of
* <code>AccessController.doPrivileged</code>.
*
* <p>The following code retrieves the boolean value of the security
* property named <code>"prop"</code> as a privileged action: <p>
*
* <pre>
* boolean b = java.security.AccessController.doPrivileged
* (new GetBooleanSecurityPropertyAction("prop")).booleanValue();
* </pre>
*
*/
public class GetBooleanSecurityPropertyAction
implements java.security.PrivilegedAction<Boolean> {
private String theProp;
/**
* Constructor that takes the name of the security property whose boolean
* value needs to be determined.
*
* @param theProp the name of the security property
*/
public GetBooleanSecurityPropertyAction(String theProp) {
this.theProp = theProp;
}
/**
* Determines the boolean value of the security property whose name was
* specified in the constructor.
*
* @return the <code>Boolean</code> value of the security property.
*/
public Boolean run() {
boolean b = false;
try {
String value = Security.getProperty(theProp);
b = (value != null) && value.equalsIgnoreCase("true");
} catch (NullPointerException e) {}
return b;
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.action;
/**
* A convenience class for retrieving the integer value of a system property
* as a privileged action.
*
* <p>An instance of this class can be used as the argument of
* <code>AccessController.doPrivileged</code>.
*
* <p>The following code retrieves the integer value of the system
* property named <code>"prop"</code> as a privileged action. Since it does
* not pass a default value to be used in case the property
* <code>"prop"</code> is not defined, it has to check the result for
* <code>null</code>: <p>
*
* <pre>
* Integer tmp = java.security.AccessController.doPrivileged
* (new sun.security.action.GetIntegerAction("prop"));
* int i;
* if (tmp != null) {
* i = tmp.intValue();
* }
* </pre>
*
* <p>The following code retrieves the integer value of the system
* property named <code>"prop"</code> as a privileged action, and also passes
* a default value to be used in case the property <code>"prop"</code> is not
* defined: <p>
*
* <pre>
* int i = ((Integer)java.security.AccessController.doPrivileged(
* new GetIntegerAction("prop", 3))).intValue();
* </pre>
*
* @author Roland Schemers
* @see java.security.PrivilegedAction
* @see java.security.AccessController
* @since 1.2
*/
public class GetIntegerAction
implements java.security.PrivilegedAction<Integer> {
private String theProp;
private int defaultVal;
private boolean defaultSet = false;
/**
* Constructor that takes the name of the system property whose integer
* value needs to be determined.
*
* @param theProp the name of the system property.
*/
public GetIntegerAction(String theProp) {
this.theProp = theProp;
}
/**
* Constructor that takes the name of the system property and the default
* value of that property.
*
* @param theProp the name of the system property.
* @param defaulVal the default value.
*/
public GetIntegerAction(String theProp, int defaultVal) {
this.theProp = theProp;
this.defaultVal = defaultVal;
this.defaultSet = true;
}
/**
* Determines the integer value of the system property whose name was
* specified in the constructor.
*
* <p>If there is no property of the specified name, or if the property
* does not have the correct numeric format, then an <code>Integer</code>
* object representing the default value that was specified in the
* constructor is returned, or <code>null</code> if no default value was
* specified.
*
* @return the <code>Integer</code> value of the property.
*/
public Integer run() {
Integer value = Integer.getInteger(theProp);
if ((value == null) && defaultSet)
return new Integer(defaultVal);
return value;
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.action;
/**
* A convenience class for retrieving the <code>Long</code> value of a system
* property as a privileged action.
*
* <p>An instance of this class can be used as the argument of
* <code>AccessController.doPrivileged</code>.
*
* <p>The following code retrieves the <code>Long</code> value of the system
* property named <code>"prop"</code> as a privileged action. Since it does
* not pass a default value to be used in case the property
* <code>"prop"</code> is not defined, it has to check the result for
* <code>null</code>: <p>
*
* <pre>
* Long tmp = java.security.AccessController.doPrivileged
* (new sun.security.action.GetLongAction("prop"));
* long l;
* if (tmp != null) {
* l = tmp.longValue();
* }
* </pre>
*
* <p>The following code retrieves the <code>Long</code> value of the system
* property named <code>"prop"</code> as a privileged action, and also passes
* a default value to be used in case the property <code>"prop"</code> is not
* defined: <p>
*
* <pre>
* long l = java.security.AccessController.doPrivileged
* (new GetLongAction("prop")).longValue();
* </pre>
*
* @author Roland Schemers
* @see java.security.PrivilegedAction
* @see java.security.AccessController
* @since 1.2
*/
public class GetLongAction implements java.security.PrivilegedAction<Long> {
private String theProp;
private long defaultVal;
private boolean defaultSet = false;
/**
* Constructor that takes the name of the system property whose
* <code>Long</code> value needs to be determined.
*
* @param theProp the name of the system property.
*/
public GetLongAction(String theProp) {
this.theProp = theProp;
}
/**
* Constructor that takes the name of the system property and the default
* value of that property.
*
* @param theProp the name of the system property.
* @param defaulVal the default value.
*/
public GetLongAction(String theProp, long defaultVal) {
this.theProp = theProp;
this.defaultVal = defaultVal;
this.defaultSet = true;
}
/**
* Determines the <code>Long</code> value of the system property whose
* name was specified in the constructor.
*
* <p>If there is no property of the specified name, or if the property
* does not have the correct numeric format, then a <code>Long</code>
* object representing the default value that was specified in the
* constructor is returned, or <code>null</code> if no default value was
* specified.
*
* @return the <code>Long</code> value of the property.
*/
public Long run() {
Long value = Long.getLong(theProp);
if ((value == null) && defaultSet)
return new Long(defaultVal);
return value;
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 1998, 2017, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.action;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* A convenience class for retrieving the string value of a system
* property as a privileged action.
*
* <p>An instance of this class can be used as the argument of
* <code>AccessController.doPrivileged</code>.
*
* <p>The following code retrieves the value of the system
* property named <code>"prop"</code> as a privileged action: <p>
*
* <pre>
* String s = java.security.AccessController.doPrivileged
* (new GetPropertyAction("prop"));
* </pre>
*
* @author Roland Schemers
* @see java.security.PrivilegedAction
* @see java.security.AccessController
* @since 1.2
*/
public class GetPropertyAction implements PrivilegedAction<String> {
private String theProp;
private String defaultVal;
/**
* Constructor that takes the name of the system property whose
* string value needs to be determined.
*
* @param theProp the name of the system property.
*/
public GetPropertyAction(String theProp) {
this.theProp = theProp;
}
/**
* Constructor that takes the name of the system property and the default
* value of that property.
*
* @param theProp the name of the system property.
* @param defaulVal the default value.
*/
public GetPropertyAction(String theProp, String defaultVal) {
this.theProp = theProp;
this.defaultVal = defaultVal;
}
/**
* Determines the string value of the system property whose
* name was specified in the constructor.
*
* @return the string value of the system property,
* or the default value if there is no property with that key.
*/
public String run() {
String value = System.getProperty(theProp);
return (value == null) ? defaultVal : value;
}
/**
* Convenience method to get a property without going through doPrivileged
* if no security manager is present. This is unsafe for inclusion in a
* public API but allowable here since this class is by default restricted
* by the package.access security property.
*
* Note that this method performs a privileged action using caller-provided
* inputs. The caller of this method should take care to ensure that the
* inputs are not tainted and the returned property is not made accessible
* to untrusted code if it contains sensitive information.
*
* @param theProp the name of the system property.
*/
public static String privilegedGetProperty(String theProp) {
if (System.getSecurityManager() == null) {
return System.getProperty(theProp);
} else {
return AccessController.doPrivileged(
new GetPropertyAction(theProp));
}
}
/**
* Convenience method to get a property without going through doPrivileged
* if no security manager is present. This is unsafe for inclusion in a
* public API but allowable here since this class is now encapsulated.
*
* Note that this method performs a privileged action using caller-provided
* inputs. The caller of this method should take care to ensure that the
* inputs are not tainted and the returned property is not made accessible
* to untrusted code if it contains sensitive information.
*
* @param theProp the name of the system property.
* @param defaultVal the default value.
*/
public static String privilegedGetProperty(String theProp,
String defaultVal) {
if (System.getSecurityManager() == null) {
return System.getProperty(theProp, defaultVal);
} else {
return AccessController.doPrivileged(
new GetPropertyAction(theProp, defaultVal));
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2002, 2004, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.action;
import java.io.*;
import java.security.PrivilegedExceptionAction;
/**
* A convenience class for opening a FileInputStream as a privileged action.
*
* @author Andreas Sterbenz
*/
public class OpenFileInputStreamAction
implements PrivilegedExceptionAction<FileInputStream> {
private final File file;
public OpenFileInputStreamAction(File file) {
this.file = file;
}
public OpenFileInputStreamAction(String filename) {
this.file = new File(filename);
}
public FileInputStream run() throws Exception {
return new FileInputStream(file);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.action;
import java.util.Map;
import java.security.Provider;
import java.security.PrivilegedAction;
/**
* A convenience PrivilegedAction class for setting the properties of
* a provider. See the SunRsaSign provider for a usage example.
*
* @see sun.security.rsa.SunRsaSign
* @author Andreas Sterbenz
* @since 1.5
*/
public class PutAllAction implements PrivilegedAction<Void> {
private final Provider provider;
private final Map<?, ?> map;
public PutAllAction(Provider provider, Map<?, ?> map) {
this.provider = provider;
this.map = map;
}
public Void run() {
provider.putAll(map);
return null;
}
}

View File

@@ -0,0 +1,287 @@
/*
* Copyright (c) 2009, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import java.math.*;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import java.util.Optional;
import javax.crypto.*;
import javax.crypto.spec.*;
import sun.security.util.ArrayUtil;
import sun.security.util.ECUtil;
import sun.security.util.math.*;
import sun.security.ec.point.*;
/**
* KeyAgreement implementation for ECDH.
*
* @since 1.7
*/
public final class ECDHKeyAgreement extends KeyAgreementSpi {
// private key, if initialized
private ECPrivateKey privateKey;
// public key, non-null between doPhase() & generateSecret() only
private ECPublicKey publicKey;
// length of the secret to be derived
private int secretLen;
/**
* Constructs a new ECDHKeyAgreement.
*/
public ECDHKeyAgreement() {
}
// see JCE spec
@Override
protected void engineInit(Key key, SecureRandom random)
throws InvalidKeyException {
if (!(key instanceof PrivateKey)) {
throw new InvalidKeyException
("Key must be instance of PrivateKey");
}
privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
publicKey = null;
}
// see JCE spec
@Override
protected void engineInit(Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
}
engineInit(key, random);
}
// see JCE spec
@Override
protected Key engineDoPhase(Key key, boolean lastPhase)
throws InvalidKeyException, IllegalStateException {
if (privateKey == null) {
throw new IllegalStateException("Not initialized");
}
if (publicKey != null) {
throw new IllegalStateException("Phase already executed");
}
if (!lastPhase) {
throw new IllegalStateException
("Only two party agreement supported, lastPhase must be true");
}
if (!(key instanceof ECPublicKey)) {
throw new InvalidKeyException
("Key must be a PublicKey with algorithm EC");
}
this.publicKey = (ECPublicKey) key;
ECParameterSpec params = publicKey.getParams();
int keyLenBits = params.getCurve().getField().getFieldSize();
secretLen = (keyLenBits + 7) >> 3;
return null;
}
private static void validateCoordinate(BigInteger c, BigInteger mod) {
if (c.compareTo(BigInteger.ZERO) < 0) {
throw new ProviderException("invalid coordinate");
}
if (c.compareTo(mod) >= 0) {
throw new ProviderException("invalid coordinate");
}
}
/*
* Check whether a public key is valid. Throw ProviderException
* if it is not valid or could not be validated.
*/
private static void validate(ECOperations ops, ECPublicKey key) {
// ensure that integers are in proper range
BigInteger x = key.getW().getAffineX();
BigInteger y = key.getW().getAffineY();
BigInteger p = ops.getField().getSize();
validateCoordinate(x, p);
validateCoordinate(y, p);
// ensure the point is on the curve
EllipticCurve curve = key.getParams().getCurve();
BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA()
.multiply(x)).add(curve.getB()).mod(p);
BigInteger lhs = y.modPow(BigInteger.valueOf(2), p).mod(p);
if (!rhs.equals(lhs)) {
throw new ProviderException("point is not on curve");
}
// check the order of the point
ImmutableIntegerModuloP xElem = ops.getField().getElement(x);
ImmutableIntegerModuloP yElem = ops.getField().getElement(y);
AffinePoint affP = new AffinePoint(xElem, yElem);
byte[] order = key.getParams().getOrder().toByteArray();
ArrayUtil.reverse(order);
Point product = ops.multiply(affP, order);
if (!ops.isNeutral(product)) {
throw new ProviderException("point has incorrect order");
}
}
// see JCE spec
@Override
protected byte[] engineGenerateSecret() throws IllegalStateException {
if ((privateKey == null) || (publicKey == null)) {
throw new IllegalStateException("Not initialized correctly");
}
Optional<byte[]> resultOpt = deriveKeyImpl(privateKey, publicKey);
return resultOpt.orElseGet(
() -> deriveKeyNative(privateKey, publicKey)
);
}
// see JCE spec
@Override
protected int engineGenerateSecret(byte[] sharedSecret, int
offset) throws IllegalStateException, ShortBufferException {
if (offset + secretLen > sharedSecret.length) {
throw new ShortBufferException("Need " + secretLen
+ " bytes, only " + (sharedSecret.length - offset)
+ " available");
}
byte[] secret = engineGenerateSecret();
System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
return secret.length;
}
// see JCE spec
@Override
protected SecretKey engineGenerateSecret(String algorithm)
throws IllegalStateException, NoSuchAlgorithmException,
InvalidKeyException {
if (algorithm == null) {
throw new NoSuchAlgorithmException("Algorithm must not be null");
}
if (!(algorithm.equals("TlsPremasterSecret"))) {
throw new NoSuchAlgorithmException
("Only supported for algorithm TlsPremasterSecret");
}
return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
}
private static
Optional<byte[]> deriveKeyImpl(ECPrivateKey priv, ECPublicKey pubKey) {
ECParameterSpec ecSpec = priv.getParams();
EllipticCurve curve = ecSpec.getCurve();
Optional<ECOperations> opsOpt = ECOperations.forParameters(ecSpec);
if (!opsOpt.isPresent()) {
return Optional.empty();
}
ECOperations ops = opsOpt.get();
if (! (priv instanceof ECPrivateKeyImpl)) {
return Optional.empty();
}
ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) priv;
byte[] sArr = privImpl.getArrayS();
// to match the native implementation, validate the public key here
// and throw ProviderException if it is invalid
validate(ops, pubKey);
IntegerFieldModuloP field = ops.getField();
// convert s array into field element and multiply by the cofactor
MutableIntegerModuloP scalar = field.getElement(sArr).mutable();
SmallValue cofactor =
field.getSmallValue(priv.getParams().getCofactor());
scalar.setProduct(cofactor);
int keySize = (curve.getField().getFieldSize() + 7) / 8;
byte[] privArr = scalar.asByteArray(keySize);
ImmutableIntegerModuloP x =
field.getElement(pubKey.getW().getAffineX());
ImmutableIntegerModuloP y =
field.getElement(pubKey.getW().getAffineY());
AffinePoint affPub = new AffinePoint(x, y);
Point product = ops.multiply(affPub, privArr);
if (ops.isNeutral(product)) {
throw new ProviderException("Product is zero");
}
AffinePoint affProduct = product.asAffine();
byte[] result = affProduct.getX().asByteArray(keySize);
ArrayUtil.reverse(result);
return Optional.of(result);
}
private static
byte[] deriveKeyNative(ECPrivateKey privateKey, ECPublicKey publicKey) {
ECParameterSpec params = privateKey.getParams();
byte[] s = privateKey.getS().toByteArray();
byte[] encodedParams = // DER OID
ECUtil.encodeECParameterSpec(null, params);
byte[] publicValue;
if (publicKey instanceof ECPublicKeyImpl) {
ECPublicKeyImpl ecPub = (ECPublicKeyImpl) publicKey;
publicValue = ecPub.getEncodedPublicValue();
} else { // instanceof ECPublicKey
publicValue =
ECUtil.encodePoint(publicKey.getW(), params.getCurve());
}
try {
return deriveKey(s, publicValue, encodedParams);
} catch (GeneralSecurityException e) {
throw new ProviderException("Could not derive key", e);
}
}
/**
* Generates a secret key using the public and private keys.
*
* @param s the private key's S value.
* @param w the public key's W point (in uncompressed form).
* @param encodedParams the curve's DER encoded object identifier.
*
* @return byte[] the secret key.
*/
private static native byte[] deriveKey(byte[] s, byte[] w,
byte[] encodedParams) throws GeneralSecurityException;
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import sun.security.ec.point.*;
import sun.security.util.ArrayUtil;
import sun.security.util.math.*;
import static sun.security.ec.ECOperations.IntermediateValueException;
import java.security.ProviderException;
import java.security.spec.*;
import java.util.Optional;
public class ECDSAOperations {
public static class Seed {
private final byte[] seedValue;
public Seed(byte[] seedValue) {
this.seedValue = seedValue;
}
public byte[] getSeedValue() {
return seedValue;
}
}
public static class Nonce {
private final byte[] nonceValue;
public Nonce(byte[] nonceValue) {
this.nonceValue = nonceValue;
}
public byte[] getNonceValue() {
return nonceValue;
}
}
private final ECOperations ecOps;
private final AffinePoint basePoint;
public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {
this.ecOps = ecOps;
this.basePoint = toAffinePoint(basePoint, ecOps.getField());
}
public ECOperations getEcOperations() {
return ecOps;
}
public AffinePoint basePointMultiply(byte[] scalar) {
return ecOps.multiply(basePoint, scalar).asAffine();
}
public static AffinePoint toAffinePoint(ECPoint point,
IntegerFieldModuloP field) {
ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());
ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());
return new AffinePoint(affineX, affineY);
}
public static
Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {
Optional<ECOperations> curveOps =
ECOperations.forParameters(ecParams);
return curveOps.map(
ops -> new ECDSAOperations(ops, ecParams.getGenerator())
);
}
/**
*
* Sign a digest using the provided private key and seed.
* IMPORTANT: The private key is a scalar represented using a
* little-endian byte array. This is backwards from the conventional
* representation in ECDSA. The routines that produce and consume this
* value uses little-endian, so this deviation from convention removes
* the requirement to swap the byte order. The returned signature is in
* the conventional byte order.
*
* @param privateKey the private key scalar as a little-endian byte array
* @param digest the digest to be signed
* @param seed the seed that will be used to produce the nonce. This object
* should contain an array that is at least 64 bits longer than
* the number of bits required to represent the group order.
* @return the ECDSA signature value
* @throws IntermediateValueException if the signature cannot be produced
* due to an unacceptable intermediate or final value. If this
* exception is thrown, then the caller should discard the nonnce and
* try again with an entirely new nonce value.
*/
public byte[] signDigest(byte[] privateKey, byte[] digest, Seed seed)
throws IntermediateValueException {
byte[] nonceArr = ecOps.seedToScalar(seed.getSeedValue());
Nonce nonce = new Nonce(nonceArr);
return signDigest(privateKey, digest, nonce);
}
/**
*
* Sign a digest using the provided private key and nonce.
* IMPORTANT: The private key and nonce are scalars represented by a
* little-endian byte array. This is backwards from the conventional
* representation in ECDSA. The routines that produce and consume these
* values use little-endian, so this deviation from convention removes
* the requirement to swap the byte order. The returned signature is in
* the conventional byte order.
*
* @param privateKey the private key scalar as a little-endian byte array
* @param digest the digest to be signed
* @param nonce the nonce object containing a little-endian scalar value.
* @return the ECDSA signature value
* @throws IntermediateValueException if the signature cannot be produced
* due to an unacceptable intermediate or final value. If this
* exception is thrown, then the caller should discard the nonnce and
* try again with an entirely new nonce value.
*/
public byte[] signDigest(byte[] privateKey, byte[] digest, Nonce nonce)
throws IntermediateValueException {
IntegerFieldModuloP orderField = ecOps.getOrderField();
int orderBits = orderField.getSize().bitLength();
if (orderBits % 8 != 0 && orderBits < digest.length * 8) {
// This implementation does not support truncating digests to
// a length that is not a multiple of 8.
throw new ProviderException("Invalid digest length");
}
byte[] k = nonce.getNonceValue();
// check nonce length
int length = (orderField.getSize().bitLength() + 7) / 8;
if (k.length != length) {
throw new ProviderException("Incorrect nonce length");
}
MutablePoint R = ecOps.multiply(basePoint, k);
IntegerModuloP r = R.asAffine().getX();
// put r into the correct field by fully reducing to an array
byte[] temp = new byte[length];
r.asByteArray(temp);
r = orderField.getElement(temp);
// store r in result
r.asByteArray(temp);
byte[] result = new byte[2 * length];
ArrayUtil.reverse(temp);
System.arraycopy(temp, 0, result, 0, length);
// compare r to 0
if (ECOperations.allZero(temp)) {
throw new IntermediateValueException();
}
IntegerModuloP dU = orderField.getElement(privateKey);
int lengthE = Math.min(length, digest.length);
byte[] E = new byte[lengthE];
System.arraycopy(digest, 0, E, 0, lengthE);
ArrayUtil.reverse(E);
IntegerModuloP e = orderField.getElement(E);
IntegerModuloP kElem = orderField.getElement(k);
IntegerModuloP kInv = kElem.multiplicativeInverse();
MutableIntegerModuloP s = r.mutable();
s.setProduct(dU).setSum(e).setProduct(kInv);
// store s in result
s.asByteArray(temp);
ArrayUtil.reverse(temp);
System.arraycopy(temp, 0, result, length, length);
// compare s to 0
if (ECOperations.allZero(temp)) {
throw new IntermediateValueException();
}
return result;
}
}

View File

@@ -0,0 +1,498 @@
/*
* Copyright (c) 2009, 2020, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import java.nio.ByteBuffer;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import java.util.Optional;
import sun.security.jca.JCAUtil;
import sun.security.util.*;
import static sun.security.ec.ECOperations.IntermediateValueException;
/**
* ECDSA signature implementation. This class currently supports the
* following algorithm names:
*
* . "NONEwithECDSA"
* . "SHA1withECDSA"
* . "SHA224withECDSA"
* . "SHA256withECDSA"
* . "SHA384withECDSA"
* . "SHA512withECDSA"
*
* @since 1.7
*/
abstract class ECDSASignature extends SignatureSpi {
// message digest implementation we use
private final MessageDigest messageDigest;
// supplied entropy
private SecureRandom random;
// flag indicating whether the digest has been reset
private boolean needsReset;
// private key, if initialized for signing
private ECPrivateKey privateKey;
// public key, if initialized for verifying
private ECPublicKey publicKey;
// signature parameters
private ECParameterSpec sigParams = null;
/**
* Constructs a new ECDSASignature. Used by Raw subclass.
*
* @exception ProviderException if the native ECC library is unavailable.
*/
ECDSASignature() {
messageDigest = null;
}
/**
* Constructs a new ECDSASignature. Used by subclasses.
*/
ECDSASignature(String digestName) {
try {
messageDigest = MessageDigest.getInstance(digestName);
} catch (NoSuchAlgorithmException e) {
throw new ProviderException(e);
}
needsReset = false;
}
// Nested class for NONEwithECDSA signatures
public static final class Raw extends ECDSASignature {
// the longest supported digest is 512 bits (SHA-512)
private static final int RAW_ECDSA_MAX = 64;
private final byte[] precomputedDigest;
private int offset = 0;
public Raw() {
precomputedDigest = new byte[RAW_ECDSA_MAX];
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(byte b) throws SignatureException {
if (offset >= precomputedDigest.length) {
offset = RAW_ECDSA_MAX + 1;
return;
}
precomputedDigest[offset++] = b;
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(byte[] b, int off, int len)
throws SignatureException {
if (offset >= precomputedDigest.length) {
offset = RAW_ECDSA_MAX + 1;
return;
}
System.arraycopy(b, off, precomputedDigest, offset, len);
offset += len;
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(ByteBuffer byteBuffer) {
int len = byteBuffer.remaining();
if (len <= 0) {
return;
}
if (offset + len >= precomputedDigest.length) {
offset = RAW_ECDSA_MAX + 1;
return;
}
byteBuffer.get(precomputedDigest, offset, len);
offset += len;
}
@Override
protected void resetDigest() {
offset = 0;
}
// Returns the precomputed message digest value.
@Override
protected byte[] getDigestValue() throws SignatureException {
if (offset > RAW_ECDSA_MAX) {
throw new SignatureException("Message digest is too long");
}
byte[] result = new byte[offset];
System.arraycopy(precomputedDigest, 0, result, 0, offset);
offset = 0;
return result;
}
}
// Nested class for SHA1withECDSA signatures
public static final class SHA1 extends ECDSASignature {
public SHA1() {
super("SHA1");
}
}
// Nested class for SHA224withECDSA signatures
public static final class SHA224 extends ECDSASignature {
public SHA224() {
super("SHA-224");
}
}
// Nested class for SHA256withECDSA signatures
public static final class SHA256 extends ECDSASignature {
public SHA256() {
super("SHA-256");
}
}
// Nested class for SHA384withECDSA signatures
public static final class SHA384 extends ECDSASignature {
public SHA384() {
super("SHA-384");
}
}
// Nested class for SHA512withECDSA signatures
public static final class SHA512 extends ECDSASignature {
public SHA512() {
super("SHA-512");
}
}
// initialize for verification. See JCA doc
@Override
protected void engineInitVerify(PublicKey publicKey)
throws InvalidKeyException {
ECPublicKey key = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
if (!isCompatible(this.sigParams, key.getParams())) {
throw new InvalidKeyException("Key params does not match signature params");
}
// Should check that the supplied key is appropriate for signature
// algorithm (e.g. P-256 for SHA256withECDSA)
this.publicKey = key;
this.privateKey = null;
resetDigest();
}
// initialize for signing. See JCA doc
@Override
protected void engineInitSign(PrivateKey privateKey)
throws InvalidKeyException {
engineInitSign(privateKey, null);
}
// initialize for signing. See JCA doc
@Override
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
throws InvalidKeyException {
ECPrivateKey key = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
if (!isCompatible(this.sigParams, key.getParams())) {
throw new InvalidKeyException("Key params does not match signature params");
}
// Should check that the supplied key is appropriate for signature
// algorithm (e.g. P-256 for SHA256withECDSA)
this.privateKey = key;
this.publicKey = null;
this.random = random;
resetDigest();
}
/**
* Resets the message digest if needed.
*/
protected void resetDigest() {
if (needsReset) {
if (messageDigest != null) {
messageDigest.reset();
}
needsReset = false;
}
}
/**
* Returns the message digest value.
*/
protected byte[] getDigestValue() throws SignatureException {
needsReset = false;
return messageDigest.digest();
}
// update the signature with the plaintext data. See JCA doc
@Override
protected void engineUpdate(byte b) throws SignatureException {
messageDigest.update(b);
needsReset = true;
}
// update the signature with the plaintext data. See JCA doc
@Override
protected void engineUpdate(byte[] b, int off, int len)
throws SignatureException {
messageDigest.update(b, off, len);
needsReset = true;
}
// update the signature with the plaintext data. See JCA doc
@Override
protected void engineUpdate(ByteBuffer byteBuffer) {
int len = byteBuffer.remaining();
if (len <= 0) {
return;
}
messageDigest.update(byteBuffer);
needsReset = true;
}
private static boolean isCompatible(ECParameterSpec sigParams,
ECParameterSpec keyParams) {
if (sigParams == null) {
// no restriction on key param
return true;
}
return ECUtil.equals(sigParams, keyParams);
}
private byte[] signDigestImpl(ECDSAOperations ops, int seedBits,
byte[] digest, ECPrivateKeyImpl privImpl, SecureRandom random)
throws SignatureException {
byte[] seedBytes = new byte[(seedBits + 7) / 8];
byte[] s = privImpl.getArrayS();
// Attempt to create the signature in a loop that uses new random input
// each time. The chance of failure is very small assuming the
// implementation derives the nonce using extra bits
int numAttempts = 128;
for (int i = 0; i < numAttempts; i++) {
random.nextBytes(seedBytes);
ECDSAOperations.Seed seed = new ECDSAOperations.Seed(seedBytes);
try {
return ops.signDigest(s, digest, seed);
} catch (IntermediateValueException ex) {
// try again in the next iteration
}
}
throw new SignatureException("Unable to produce signature after "
+ numAttempts + " attempts");
}
private Optional<byte[]> signDigestImpl(ECPrivateKey privateKey,
byte[] digest, SecureRandom random) throws SignatureException {
if (! (privateKey instanceof ECPrivateKeyImpl)) {
return Optional.empty();
}
ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) privateKey;
ECParameterSpec params = privateKey.getParams();
// seed is the key size + 64 bits
int seedBits = params.getOrder().bitLength() + 64;
Optional<ECDSAOperations> opsOpt =
ECDSAOperations.forParameters(params);
if (!opsOpt.isPresent()) {
return Optional.empty();
} else {
byte[] sig = signDigestImpl(opsOpt.get(), seedBits, digest,
privImpl, random);
return Optional.of(sig);
}
}
private byte[] signDigestNative(ECPrivateKey privateKey, byte[] digest,
SecureRandom random) throws SignatureException {
byte[] s = privateKey.getS().toByteArray();
ECParameterSpec params = privateKey.getParams();
// DER OID
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
int orderLength = params.getOrder().bitLength();
// seed is twice the order length (in bytes) plus 1
byte[] seed = new byte[(((orderLength + 7) >> 3) + 1) * 2];
random.nextBytes(seed);
// random bits needed for timing countermeasures
int timingArgument = random.nextInt();
// values must be non-zero to enable countermeasures
timingArgument |= 1;
try {
return signDigest(digest, s, encodedParams, seed,
timingArgument);
} catch (GeneralSecurityException e) {
throw new SignatureException("Could not sign data", e);
}
}
// sign the data and return the signature. See JCA doc
@Override
protected byte[] engineSign() throws SignatureException {
if (random == null) {
random = JCAUtil.getSecureRandom();
}
byte[] digest = getDigestValue();
Optional<byte[]> sigOpt = signDigestImpl(privateKey, digest, random);
byte[] sig;
if (sigOpt.isPresent()) {
sig = sigOpt.get();
} else {
sig = signDigestNative(privateKey, digest, random);
}
return ECUtil.encodeSignature(sig);
}
// verify the data and return the result. See JCA doc
@Override
protected boolean engineVerify(byte[] signature) throws SignatureException {
byte[] w;
ECParameterSpec params = publicKey.getParams();
// DER OID
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
if (publicKey instanceof ECPublicKeyImpl) {
w = ((ECPublicKeyImpl) publicKey).getEncodedPublicValue();
} else { // instanceof ECPublicKey
w = ECUtil.encodePoint(publicKey.getW(), params.getCurve());
}
try {
return verifySignedDigest(
ECUtil.decodeSignature(signature), getDigestValue(),
w, encodedParams);
} catch (GeneralSecurityException e) {
throw new SignatureException("Could not verify signature", e);
}
}
// set parameter, not supported. See JCA doc
@Override
@Deprecated
protected void engineSetParameter(String param, Object value)
throws InvalidParameterException {
throw new UnsupportedOperationException("setParameter() not supported");
}
@Override
protected void engineSetParameter(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
if (params != null && !(params instanceof ECParameterSpec)) {
throw new InvalidAlgorithmParameterException("No parameter accepted");
}
ECKey key = (this.privateKey == null? this.publicKey : this.privateKey);
if ((key != null) && !isCompatible((ECParameterSpec)params, key.getParams())) {
throw new InvalidAlgorithmParameterException
("Signature params does not match key params");
}
sigParams = (ECParameterSpec) params;
}
// get parameter, not supported. See JCA doc
@Override
@Deprecated
protected Object engineGetParameter(String param)
throws InvalidParameterException {
throw new UnsupportedOperationException("getParameter() not supported");
}
@Override
protected AlgorithmParameters engineGetParameters() {
if (sigParams == null) {
return null;
}
try {
AlgorithmParameters ap = AlgorithmParameters.getInstance("EC");
ap.init(sigParams);
return ap;
} catch (Exception e) {
// should never happen
throw new ProviderException("Error retrieving EC parameters", e);
}
}
/**
* Signs the digest using the private key.
*
* @param digest the digest to be signed.
* @param s the private key's S value.
* @param encodedParams the curve's DER encoded object identifier.
* @param seed the random seed.
* @param timing When non-zero, the implmentation will use timing
* countermeasures to hide secrets from timing channels. The EC
* implementation will disable the countermeasures when this value is
* zero, because the underlying EC functions are shared by several
* crypto operations, some of which do not use the countermeasures.
* The high-order 31 bits must be uniformly random. The entropy from
* these bits is used by the countermeasures.
*
* @return byte[] the signature.
*/
private static native byte[] signDigest(byte[] digest, byte[] s,
byte[] encodedParams, byte[] seed, int timing)
throws GeneralSecurityException;
/**
* Verifies the signed digest using the public key.
*
* @param signature the signature to be verified. It is encoded
* as a concatenation of the key's R and S values.
* @param digest the digest to be used.
* @param w the public key's W point (in uncompressed form).
* @param encodedParams the curve's DER encoded object identifier.
*
* @return boolean true if the signature is successfully verified.
*/
private static native boolean verifySignedDigest(byte[] signature,
byte[] digest, byte[] w, byte[] encodedParams)
throws GeneralSecurityException;
}

View File

@@ -0,0 +1,290 @@
/*
* Copyright (c) 2006, 2021, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
/**
* KeyFactory for EC keys. Keys must be instances of PublicKey or PrivateKey
* and getAlgorithm() must return "EC". For such keys, it supports conversion
* between the following:
*
* For public keys:
* . PublicKey with an X.509 encoding
* . ECPublicKey
* . ECPublicKeySpec
* . X509EncodedKeySpec
*
* For private keys:
* . PrivateKey with a PKCS#8 encoding
* . ECPrivateKey
* . ECPrivateKeySpec
* . PKCS8EncodedKeySpec
*
* @since 1.6
* @author Andreas Sterbenz
*/
public final class ECKeyFactory extends KeyFactorySpi {
// Used by translateKey()
private static KeyFactory instance;
private static KeyFactory getInstance() {
if (instance == null) {
try {
instance = KeyFactory.getInstance("EC", "SunEC");
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
return instance;
}
public ECKeyFactory() {
// empty
}
/**
* Static method to convert Key into a useable instance of
* ECPublicKey or ECPrivateKey. Check the key and convert it
* to a Sun key if necessary. If the key is not an EC key
* or cannot be used, throw an InvalidKeyException.
*
* The difference between this method and engineTranslateKey() is that
* we do not convert keys of other providers that are already an
* instance of ECPublicKey or ECPrivateKey.
*
* To be used by future Java ECDSA and ECDH implementations.
*/
public static ECKey toECKey(Key key) throws InvalidKeyException {
if (key instanceof ECKey) {
ECKey ecKey = (ECKey)key;
checkKey(ecKey);
return ecKey;
} else {
/*
* We don't call the engineTranslateKey method directly
* because KeyFactory.translateKey adds code to loop through
* all key factories.
*/
return (ECKey)getInstance().translateKey(key);
}
}
/**
* Check that the given EC key is valid.
*/
private static void checkKey(ECKey key) throws InvalidKeyException {
// check for subinterfaces, omit additional checks for our keys
if (key instanceof ECPublicKey) {
if (key instanceof ECPublicKeyImpl) {
return;
}
} else if (key instanceof ECPrivateKey) {
if (key instanceof ECPrivateKeyImpl) {
return;
}
} else {
throw new InvalidKeyException("Neither a public nor a private key");
}
// ECKey does not extend Key, so we need to do a cast
String keyAlg = ((Key)key).getAlgorithm();
if (keyAlg.equals("EC") == false) {
throw new InvalidKeyException("Not an EC key: " + keyAlg);
}
// XXX further sanity checks about whether this key uses supported
// fields, point formats, etc. would go here
}
/**
* Translate an EC key into a Sun EC key. If conversion is
* not possible, throw an InvalidKeyException.
* See also JCA doc.
*/
protected Key engineTranslateKey(Key key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Key must not be null");
}
String keyAlg = key.getAlgorithm();
if (keyAlg.equals("EC") == false) {
throw new InvalidKeyException("Not an EC key: " + keyAlg);
}
if (key instanceof PublicKey) {
return implTranslatePublicKey((PublicKey)key);
} else if (key instanceof PrivateKey) {
return implTranslatePrivateKey((PrivateKey)key);
} else {
throw new InvalidKeyException("Neither a public nor a private key");
}
}
// see JCA doc
protected PublicKey engineGeneratePublic(KeySpec keySpec)
throws InvalidKeySpecException {
try {
return implGeneratePublic(keySpec);
} catch (InvalidKeySpecException e) {
throw e;
} catch (GeneralSecurityException e) {
throw new InvalidKeySpecException(e);
}
}
// see JCA doc
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
throws InvalidKeySpecException {
try {
return implGeneratePrivate(keySpec);
} catch (InvalidKeySpecException e) {
throw e;
} catch (GeneralSecurityException e) {
throw new InvalidKeySpecException(e);
}
}
// internal implementation of translateKey() for public keys. See JCA doc
private PublicKey implTranslatePublicKey(PublicKey key)
throws InvalidKeyException {
if (key instanceof ECPublicKey) {
if (key instanceof ECPublicKeyImpl) {
return key;
}
ECPublicKey ecKey = (ECPublicKey)key;
return new ECPublicKeyImpl(
ecKey.getW(),
ecKey.getParams()
);
} else if ("X.509".equals(key.getFormat())) {
byte[] encoded = key.getEncoded();
return new ECPublicKeyImpl(encoded);
} else {
throw new InvalidKeyException("Public keys must be instance "
+ "of ECPublicKey or have X.509 encoding");
}
}
// internal implementation of translateKey() for private keys. See JCA doc
private PrivateKey implTranslatePrivateKey(PrivateKey key)
throws InvalidKeyException {
if (key instanceof ECPrivateKey) {
if (key instanceof ECPrivateKeyImpl) {
return key;
}
ECPrivateKey ecKey = (ECPrivateKey)key;
return new ECPrivateKeyImpl(
ecKey.getS(),
ecKey.getParams()
);
} else if ("PKCS#8".equals(key.getFormat())) {
return new ECPrivateKeyImpl(key.getEncoded());
} else {
throw new InvalidKeyException("Private keys must be instance "
+ "of ECPrivateKey or have PKCS#8 encoding");
}
}
// internal implementation of generatePublic. See JCA doc
private PublicKey implGeneratePublic(KeySpec keySpec)
throws GeneralSecurityException {
if (keySpec instanceof X509EncodedKeySpec) {
X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec;
return new ECPublicKeyImpl(x509Spec.getEncoded());
} else if (keySpec instanceof ECPublicKeySpec) {
ECPublicKeySpec ecSpec = (ECPublicKeySpec)keySpec;
return new ECPublicKeyImpl(
ecSpec.getW(),
ecSpec.getParams()
);
} else {
throw new InvalidKeySpecException("Only ECPublicKeySpec "
+ "and X509EncodedKeySpec supported for EC public keys");
}
}
// internal implementation of generatePrivate. See JCA doc
private PrivateKey implGeneratePrivate(KeySpec keySpec)
throws GeneralSecurityException {
if (keySpec instanceof PKCS8EncodedKeySpec) {
PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec;
return new ECPrivateKeyImpl(pkcsSpec.getEncoded());
} else if (keySpec instanceof ECPrivateKeySpec) {
ECPrivateKeySpec ecSpec = (ECPrivateKeySpec)keySpec;
return new ECPrivateKeyImpl(ecSpec.getS(), ecSpec.getParams());
} else {
throw new InvalidKeySpecException("Only ECPrivateKeySpec "
+ "and PKCS8EncodedKeySpec supported for EC private keys");
}
}
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
throws InvalidKeySpecException {
try {
// convert key to one of our keys
// this also verifies that the key is a valid EC key and ensures
// that the encoding is X.509/PKCS#8 for public/private keys
key = engineTranslateKey(key);
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
if (key instanceof ECPublicKey) {
ECPublicKey ecKey = (ECPublicKey)key;
if (keySpec.isAssignableFrom(ECPublicKeySpec.class)) {
return keySpec.cast(new ECPublicKeySpec(
ecKey.getW(),
ecKey.getParams()
));
} else if (keySpec.isAssignableFrom(X509EncodedKeySpec.class)) {
return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
} else {
throw new InvalidKeySpecException
("KeySpec must be ECPublicKeySpec or "
+ "X509EncodedKeySpec for EC public keys");
}
} else if (key instanceof ECPrivateKey) {
if (keySpec.isAssignableFrom(PKCS8EncodedKeySpec.class)) {
return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded()));
} else if (keySpec.isAssignableFrom(ECPrivateKeySpec.class)) {
ECPrivateKey ecKey = (ECPrivateKey)key;
return keySpec.cast(new ECPrivateKeySpec(
ecKey.getS(),
ecKey.getParams()
));
} else {
throw new InvalidKeySpecException
("KeySpec must be ECPrivateKeySpec or "
+ "PKCS8EncodedKeySpec for EC private keys");
}
} else {
// should not occur, caught in engineTranslateKey()
throw new InvalidKeySpecException("Neither public nor private key");
}
}
}

View File

@@ -0,0 +1,268 @@
/*
* Copyright (c) 2009, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.InvalidParameterSpecException;
import java.util.Optional;
import sun.security.jca.JCAUtil;
import sun.security.util.ECUtil;
import sun.security.util.math.*;
import sun.security.ec.point.*;
import static sun.security.util.SecurityProviderConstants.DEF_EC_KEY_SIZE;
import static sun.security.ec.ECOperations.IntermediateValueException;
/**
* EC keypair generator.
* Standard algorithm, minimum key length is 112 bits, maximum is 571 bits.
*
* @since 1.7
*/
public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
private static final int KEY_SIZE_MIN = 112; // min bits (see ecc_impl.h)
private static final int KEY_SIZE_MAX = 571; // max bits (see ecc_impl.h)
// used to seed the keypair generator
private SecureRandom random;
// size of the key to generate, KEY_SIZE_MIN <= keySize <= KEY_SIZE_MAX
private int keySize;
// parameters specified via init, if any
private AlgorithmParameterSpec params = null;
/**
* Constructs a new ECKeyPairGenerator.
*/
public ECKeyPairGenerator() {
// initialize to default in case the app does not call initialize()
initialize(DEF_EC_KEY_SIZE, null);
}
// initialize the generator. See JCA doc
@Override
public void initialize(int keySize, SecureRandom random) {
checkKeySize(keySize);
this.params = ECUtil.getECParameterSpec(null, keySize);
if (params == null) {
throw new InvalidParameterException(
"No EC parameters available for key size " + keySize + " bits");
}
this.random = random;
}
// second initialize method. See JCA doc
@Override
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
throws InvalidAlgorithmParameterException {
ECParameterSpec ecSpec = null;
if (params instanceof ECParameterSpec) {
ECParameterSpec ecParams = (ECParameterSpec) params;
ecSpec = ECUtil.getECParameterSpec(null, ecParams);
if (ecSpec == null) {
throw new InvalidAlgorithmParameterException(
"Unsupported curve: " + params);
}
} else if (params instanceof ECGenParameterSpec) {
String name = ((ECGenParameterSpec) params).getName();
ecSpec = ECUtil.getECParameterSpec(null, name);
if (ecSpec == null) {
throw new InvalidAlgorithmParameterException(
"Unknown curve name: " + name);
}
} else {
throw new InvalidAlgorithmParameterException(
"ECParameterSpec or ECGenParameterSpec required for EC");
}
// Not all known curves are supported by the native implementation
ensureCurveIsSupported(ecSpec);
this.params = ecSpec;
this.keySize = ecSpec.getCurve().getField().getFieldSize();
this.random = random;
}
private static void ensureCurveIsSupported(ECParameterSpec ecSpec)
throws InvalidAlgorithmParameterException {
AlgorithmParameters ecParams = ECUtil.getECParameters(null);
byte[] encodedParams;
try {
ecParams.init(ecSpec);
encodedParams = ecParams.getEncoded();
} catch (InvalidParameterSpecException ex) {
throw new InvalidAlgorithmParameterException(
"Unsupported curve: " + ecSpec.toString());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
if (!isCurveSupported(encodedParams)) {
throw new InvalidAlgorithmParameterException(
"Unsupported curve: " + ecParams.toString());
}
}
// generate the keypair. See JCA doc
@Override
public KeyPair generateKeyPair() {
if (random == null) {
random = JCAUtil.getSecureRandom();
}
try {
Optional<KeyPair> kp = generateKeyPairImpl(random);
if (kp.isPresent()) {
return kp.get();
}
return generateKeyPairNative(random);
} catch (Exception ex) {
throw new ProviderException(ex);
}
}
private byte[] generatePrivateScalar(SecureRandom random,
ECOperations ecOps, int seedSize) {
// Attempt to create the private scalar in a loop that uses new random
// input each time. The chance of failure is very small assuming the
// implementation derives the nonce using extra bits
int numAttempts = 128;
byte[] seedArr = new byte[seedSize];
for (int i = 0; i < numAttempts; i++) {
random.nextBytes(seedArr);
try {
return ecOps.seedToScalar(seedArr);
} catch (IntermediateValueException ex) {
// try again in the next iteration
}
}
throw new ProviderException("Unable to produce private key after "
+ numAttempts + " attempts");
}
private Optional<KeyPair> generateKeyPairImpl(SecureRandom random)
throws InvalidKeyException {
ECParameterSpec ecParams = (ECParameterSpec) params;
Optional<ECOperations> opsOpt = ECOperations.forParameters(ecParams);
if (!opsOpt.isPresent()) {
return Optional.empty();
}
ECOperations ops = opsOpt.get();
IntegerFieldModuloP field = ops.getField();
int numBits = ecParams.getOrder().bitLength();
int seedBits = numBits + 64;
int seedSize = (seedBits + 7) / 8;
byte[] privArr = generatePrivateScalar(random, ops, seedSize);
ECPoint genPoint = ecParams.getGenerator();
ImmutableIntegerModuloP x = field.getElement(genPoint.getAffineX());
ImmutableIntegerModuloP y = field.getElement(genPoint.getAffineY());
AffinePoint affGen = new AffinePoint(x, y);
Point pub = ops.multiply(affGen, privArr);
AffinePoint affPub = pub.asAffine();
PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams);
ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
affPub.getY().asBigInteger());
PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
return Optional.of(new KeyPair(publicKey, privateKey));
}
private KeyPair generateKeyPairNative(SecureRandom random)
throws Exception {
ECParameterSpec ecParams = (ECParameterSpec) params;
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, ecParams);
// seed is twice the key size (in bytes) plus 1
byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
random.nextBytes(seed);
Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
// The 'params' object supplied above is equivalent to the native
// one so there is no need to fetch it.
// keyBytes[0] is the encoding of the native private key
BigInteger s = new BigInteger(1, (byte[]) keyBytes[0]);
PrivateKey privateKey = new ECPrivateKeyImpl(s, ecParams);
// keyBytes[1] is the encoding of the native public key
byte[] pubKey = (byte[]) keyBytes[1];
ECPoint w = ECUtil.decodePoint(pubKey, ecParams.getCurve());
PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
return new KeyPair(publicKey, privateKey);
}
private void checkKeySize(int keySize) throws InvalidParameterException {
if (keySize < KEY_SIZE_MIN) {
throw new InvalidParameterException
("Key size must be at least " + KEY_SIZE_MIN + " bits");
}
if (keySize > KEY_SIZE_MAX) {
throw new InvalidParameterException
("Key size must be at most " + KEY_SIZE_MAX + " bits");
}
this.keySize = keySize;
}
/**
* Checks whether the curve in the encoded parameters is supported by the
* native implementation. Some curve operations will be performed by the
* Java implementation, but not all of them. So native support is still
* required for all curves.
*
* @param encodedParams encoded parameters in the same form accepted
* by generateECKeyPair
* @return true if and only if generateECKeyPair will succeed for
* the supplied parameters
*/
private static native boolean isCurveSupported(byte[] encodedParams);
/*
* Generates the keypair and returns a 2-element array of encoding bytes.
* The first one is for the private key, the second for the public key.
*/
private static native Object[] generateECKeyPair(int keySize,
byte[] encodedParams, byte[] seed) throws GeneralSecurityException;
}

View File

@@ -0,0 +1,499 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import sun.security.ec.point.*;
import sun.security.util.math.*;
import sun.security.util.math.intpoly.*;
import java.math.BigInteger;
import java.security.ProviderException;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.EllipticCurve;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/*
* Elliptic curve point arithmetic for prime-order curves where a=-3.
* Formulas are derived from "Complete addition formulas for prime order
* elliptic curves" by Renes, Costello, and Batina.
*/
public class ECOperations {
/*
* An exception indicating a problem with an intermediate value produced
* by some part of the computation. For example, the signing operation
* will throw this exception to indicate that the r or s value is 0, and
* that the signing operation should be tried again with a different nonce.
*/
static class IntermediateValueException extends Exception {
private static final long serialVersionUID = 1;
}
static final Map<BigInteger, IntegerFieldModuloP> fields;
static final Map<BigInteger, IntegerFieldModuloP> orderFields;
static {
Map<BigInteger, IntegerFieldModuloP> map = new HashMap<>();
map.put(IntegerPolynomialP256.MODULUS, new IntegerPolynomialP256());
map.put(IntegerPolynomialP384.MODULUS, new IntegerPolynomialP384());
map.put(IntegerPolynomialP521.MODULUS, new IntegerPolynomialP521());
fields = Collections.unmodifiableMap(map);
map = new HashMap<>();
map.put(P256OrderField.MODULUS, new P256OrderField());
map.put(P384OrderField.MODULUS, new P384OrderField());
map.put(P521OrderField.MODULUS, new P521OrderField());
orderFields = Collections.unmodifiableMap(map);
}
public static Optional<ECOperations> forParameters(ECParameterSpec params) {
EllipticCurve curve = params.getCurve();
if (!(curve.getField() instanceof ECFieldFp)) {
return Optional.empty();
}
ECFieldFp primeField = (ECFieldFp) curve.getField();
BigInteger three = BigInteger.valueOf(3);
if (!primeField.getP().subtract(curve.getA()).equals(three)) {
return Optional.empty();
}
IntegerFieldModuloP field = fields.get(primeField.getP());
if (field == null) {
return Optional.empty();
}
IntegerFieldModuloP orderField = orderFields.get(params.getOrder());
if (orderField == null) {
return Optional.empty();
}
ImmutableIntegerModuloP b = field.getElement(curve.getB());
ECOperations ecOps = new ECOperations(b, orderField);
return Optional.of(ecOps);
}
final ImmutableIntegerModuloP b;
final SmallValue one;
final SmallValue two;
final SmallValue three;
final SmallValue four;
final ProjectivePoint.Immutable neutral;
private final IntegerFieldModuloP orderField;
public ECOperations(IntegerModuloP b, IntegerFieldModuloP orderField) {
this.b = b.fixed();
this.orderField = orderField;
this.one = b.getField().getSmallValue(1);
this.two = b.getField().getSmallValue(2);
this.three = b.getField().getSmallValue(3);
this.four = b.getField().getSmallValue(4);
IntegerFieldModuloP field = b.getField();
this.neutral = new ProjectivePoint.Immutable(field.get0(),
field.get1(), field.get0());
}
public IntegerFieldModuloP getField() {
return b.getField();
}
public IntegerFieldModuloP getOrderField() {
return orderField;
}
protected ProjectivePoint.Immutable getNeutral() {
return neutral;
}
public boolean isNeutral(Point p) {
ProjectivePoint<?> pp = (ProjectivePoint<?>) p;
IntegerModuloP z = pp.getZ();
IntegerFieldModuloP field = z.getField();
int byteLength = (field.getSize().bitLength() + 7) / 8;
byte[] zBytes = z.asByteArray(byteLength);
return allZero(zBytes);
}
byte[] seedToScalar(byte[] seedBytes)
throws IntermediateValueException {
// Produce a nonce from the seed using FIPS 186-4,section B.5.1:
// Per-Message Secret Number Generation Using Extra Random Bits
// or
// Produce a scalar from the seed using FIPS 186-4, section B.4.1:
// Key Pair Generation Using Extra Random Bits
// To keep the implementation simple, sample in the range [0,n)
// and throw IntermediateValueException in the (unlikely) event
// that the result is 0.
// Get 64 extra bits and reduce in to the nonce
int seedBits = orderField.getSize().bitLength() + 64;
if (seedBytes.length * 8 < seedBits) {
throw new ProviderException("Incorrect seed length: " +
seedBytes.length * 8 + " < " + seedBits);
}
// input conversion only works on byte boundaries
// clear high-order bits of last byte so they don't influence nonce
int lastByteBits = seedBits % 8;
if (lastByteBits != 0) {
int lastByteIndex = seedBits / 8;
byte mask = (byte) (0xFF >>> (8 - lastByteBits));
seedBytes[lastByteIndex] &= mask;
}
int seedLength = (seedBits + 7) / 8;
IntegerModuloP scalarElem =
orderField.getElement(seedBytes, 0, seedLength, (byte) 0);
int scalarLength = (orderField.getSize().bitLength() + 7) / 8;
byte[] scalarArr = new byte[scalarLength];
scalarElem.asByteArray(scalarArr);
if (ECOperations.allZero(scalarArr)) {
throw new IntermediateValueException();
}
return scalarArr;
}
/*
* Compare all values in the array to 0 without branching on any value
*
*/
public static boolean allZero(byte[] arr) {
byte acc = 0;
for (int i = 0; i < arr.length; i++) {
acc |= arr[i];
}
return acc == 0;
}
/*
* 4-bit branchless array lookup for projective points.
*/
private void lookup4(ProjectivePoint.Immutable[] arr, int index,
ProjectivePoint.Mutable result, IntegerModuloP zero) {
for (int i = 0; i < 16; i++) {
int xor = index ^ i;
int bit3 = (xor & 0x8) >>> 3;
int bit2 = (xor & 0x4) >>> 2;
int bit1 = (xor & 0x2) >>> 1;
int bit0 = (xor & 0x1);
int inverse = bit0 | bit1 | bit2 | bit3;
int set = 1 - inverse;
ProjectivePoint.Immutable pi = arr[i];
result.conditionalSet(pi, set);
}
}
private void double4(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,
MutableIntegerModuloP t1, MutableIntegerModuloP t2,
MutableIntegerModuloP t3, MutableIntegerModuloP t4) {
for (int i = 0; i < 4; i++) {
setDouble(p, t0, t1, t2, t3, t4);
}
}
/**
* Multiply an affine point by a scalar and return the result as a mutable
* point.
*
* @param affineP the point
* @param s the scalar as a little-endian array
* @return the product
*/
public MutablePoint multiply(AffinePoint affineP, byte[] s) {
// 4-bit windowed multiply with branchless lookup.
// The mixed addition is faster, so it is used to construct the array
// at the beginning of the operation.
IntegerFieldModuloP field = affineP.getX().getField();
ImmutableIntegerModuloP zero = field.get0();
// temporaries
MutableIntegerModuloP t0 = zero.mutable();
MutableIntegerModuloP t1 = zero.mutable();
MutableIntegerModuloP t2 = zero.mutable();
MutableIntegerModuloP t3 = zero.mutable();
MutableIntegerModuloP t4 = zero.mutable();
ProjectivePoint.Mutable result = new ProjectivePoint.Mutable(field);
result.getY().setValue(field.get1().mutable());
ProjectivePoint.Immutable[] pointMultiples =
new ProjectivePoint.Immutable[16];
// 0P is neutral---same as initial result value
pointMultiples[0] = result.fixed();
ProjectivePoint.Mutable ps = new ProjectivePoint.Mutable(field);
ps.setValue(affineP);
// 1P = P
pointMultiples[1] = ps.fixed();
// the rest are calculated using mixed point addition
for (int i = 2; i < 16; i++) {
setSum(ps, affineP, t0, t1, t2, t3, t4);
pointMultiples[i] = ps.fixed();
}
ProjectivePoint.Mutable lookupResult = ps.mutable();
for (int i = s.length - 1; i >= 0; i--) {
double4(result, t0, t1, t2, t3, t4);
int high = (0xFF & s[i]) >>> 4;
lookup4(pointMultiples, high, lookupResult, zero);
setSum(result, lookupResult, t0, t1, t2, t3, t4);
double4(result, t0, t1, t2, t3, t4);
int low = 0xF & s[i];
lookup4(pointMultiples, low, lookupResult, zero);
setSum(result, lookupResult, t0, t1, t2, t3, t4);
}
return result;
}
/*
* Point double
*/
private void setDouble(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,
MutableIntegerModuloP t1, MutableIntegerModuloP t2,
MutableIntegerModuloP t3, MutableIntegerModuloP t4) {
t0.setValue(p.getX()).setSquare();
t1.setValue(p.getY()).setSquare();
t2.setValue(p.getZ()).setSquare();
t3.setValue(p.getX()).setProduct(p.getY());
t4.setValue(p.getY()).setProduct(p.getZ());
t3.setSum(t3);
p.getZ().setProduct(p.getX());
p.getZ().setProduct(two);
p.getY().setValue(t2).setProduct(b);
p.getY().setDifference(p.getZ());
p.getX().setValue(p.getY()).setProduct(two);
p.getY().setSum(p.getX());
p.getY().setReduced();
p.getX().setValue(t1).setDifference(p.getY());
p.getY().setSum(t1);
p.getY().setProduct(p.getX());
p.getX().setProduct(t3);
t3.setValue(t2).setProduct(two);
t2.setSum(t3);
p.getZ().setProduct(b);
t2.setReduced();
p.getZ().setDifference(t2);
p.getZ().setDifference(t0);
t3.setValue(p.getZ()).setProduct(two);
p.getZ().setReduced();
p.getZ().setSum(t3);
t0.setProduct(three);
t0.setDifference(t2);
t0.setProduct(p.getZ());
p.getY().setSum(t0);
t4.setSum(t4);
p.getZ().setProduct(t4);
p.getX().setDifference(p.getZ());
p.getZ().setValue(t4).setProduct(t1);
p.getZ().setProduct(four);
}
/*
* Mixed point addition. This method constructs new temporaries each time
* it is called. For better efficiency, the method that reuses temporaries
* should be used if more than one sum will be computed.
*/
public void setSum(MutablePoint p, AffinePoint p2) {
IntegerModuloP zero = p.getField().get0();
MutableIntegerModuloP t0 = zero.mutable();
MutableIntegerModuloP t1 = zero.mutable();
MutableIntegerModuloP t2 = zero.mutable();
MutableIntegerModuloP t3 = zero.mutable();
MutableIntegerModuloP t4 = zero.mutable();
setSum((ProjectivePoint.Mutable) p, p2, t0, t1, t2, t3, t4);
}
/*
* Mixed point addition
*/
private void setSum(ProjectivePoint.Mutable p, AffinePoint p2,
MutableIntegerModuloP t0, MutableIntegerModuloP t1,
MutableIntegerModuloP t2, MutableIntegerModuloP t3,
MutableIntegerModuloP t4) {
t0.setValue(p.getX()).setProduct(p2.getX());
t1.setValue(p.getY()).setProduct(p2.getY());
t3.setValue(p2.getX()).setSum(p2.getY());
t4.setValue(p.getX()).setSum(p.getY());
p.getX().setReduced();
t3.setProduct(t4);
t4.setValue(t0).setSum(t1);
t3.setDifference(t4);
t4.setValue(p2.getY()).setProduct(p.getZ());
t4.setSum(p.getY());
p.getY().setValue(p2.getX()).setProduct(p.getZ());
p.getY().setSum(p.getX());
t2.setValue(p.getZ());
p.getZ().setProduct(b);
p.getX().setValue(p.getY()).setDifference(p.getZ());
p.getX().setReduced();
p.getZ().setValue(p.getX()).setProduct(two);
p.getX().setSum(p.getZ());
p.getZ().setValue(t1).setDifference(p.getX());
p.getX().setSum(t1);
p.getY().setProduct(b);
t1.setValue(t2).setProduct(two);
t2.setSum(t1);
t2.setReduced();
p.getY().setDifference(t2);
p.getY().setDifference(t0);
p.getY().setReduced();
t1.setValue(p.getY()).setProduct(two);
p.getY().setSum(t1);
t1.setValue(t0).setProduct(two);
t0.setSum(t1);
t0.setDifference(t2);
t1.setValue(t4).setProduct(p.getY());
t2.setValue(t0).setProduct(p.getY());
p.getY().setValue(p.getX()).setProduct(p.getZ());
p.getY().setSum(t2);
p.getX().setProduct(t3);
p.getX().setDifference(t1);
p.getZ().setProduct(t4);
t1.setValue(t3).setProduct(t0);
p.getZ().setSum(t1);
}
/*
* Projective point addition
*/
private void setSum(ProjectivePoint.Mutable p, ProjectivePoint.Mutable p2,
MutableIntegerModuloP t0, MutableIntegerModuloP t1,
MutableIntegerModuloP t2, MutableIntegerModuloP t3,
MutableIntegerModuloP t4) {
t0.setValue(p.getX()).setProduct(p2.getX());
t1.setValue(p.getY()).setProduct(p2.getY());
t2.setValue(p.getZ()).setProduct(p2.getZ());
t3.setValue(p.getX()).setSum(p.getY());
t4.setValue(p2.getX()).setSum(p2.getY());
t3.setProduct(t4);
t4.setValue(t0).setSum(t1);
t3.setDifference(t4);
t4.setValue(p.getY()).setSum(p.getZ());
p.getY().setValue(p2.getY()).setSum(p2.getZ());
t4.setProduct(p.getY());
p.getY().setValue(t1).setSum(t2);
t4.setDifference(p.getY());
p.getX().setSum(p.getZ());
p.getY().setValue(p2.getX()).setSum(p2.getZ());
p.getX().setProduct(p.getY());
p.getY().setValue(t0).setSum(t2);
p.getY().setAdditiveInverse().setSum(p.getX());
p.getY().setReduced();
p.getZ().setValue(t2).setProduct(b);
p.getX().setValue(p.getY()).setDifference(p.getZ());
p.getZ().setValue(p.getX()).setProduct(two);
p.getX().setSum(p.getZ());
p.getX().setReduced();
p.getZ().setValue(t1).setDifference(p.getX());
p.getX().setSum(t1);
p.getY().setProduct(b);
t1.setValue(t2).setSum(t2);
t2.setSum(t1);
t2.setReduced();
p.getY().setDifference(t2);
p.getY().setDifference(t0);
p.getY().setReduced();
t1.setValue(p.getY()).setSum(p.getY());
p.getY().setSum(t1);
t1.setValue(t0).setProduct(two);
t0.setSum(t1);
t0.setDifference(t2);
t1.setValue(t4).setProduct(p.getY());
t2.setValue(t0).setProduct(p.getY());
p.getY().setValue(p.getX()).setProduct(p.getZ());
p.getY().setSum(t2);
p.getX().setProduct(t3);
p.getX().setDifference(t1);
p.getZ().setProduct(t4);
t1.setValue(t3).setProduct(t0);
p.getZ().setSum(t1);
}
}

View File

@@ -0,0 +1,233 @@
/*
* Copyright (c) 2006, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import sun.security.util.ArrayUtil;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.util.ECParameters;
import sun.security.util.ECUtil;
import sun.security.x509.AlgorithmId;
import sun.security.pkcs.PKCS8Key;
/**
* Key implementation for EC private keys.
* <p>
* ASN.1 syntax for EC private keys from SEC 1 v1.5 (draft):
*
* <pre>
* EXPLICIT TAGS
*
* ECPrivateKey ::= SEQUENCE {
* version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
* privateKey OCTET STRING,
* parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
* publicKey [1] BIT STRING OPTIONAL
* }
* </pre>
*
* We currently ignore the optional parameters and publicKey fields. We
* require that the parameters are encoded as part of the AlgorithmIdentifier,
* not in the private key structure.
*
* @since 1.6
* @author Andreas Sterbenz
*/
public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
private static final long serialVersionUID = 88695385615075129L;
private BigInteger s; // private value
private byte[] arrayS; // private value as a little-endian array
private ECParameterSpec params;
/**
* Construct a key from its encoding. Called by the ECKeyFactory.
*/
public ECPrivateKeyImpl(byte[] encoded) throws InvalidKeyException {
decode(encoded);
}
/**
* Construct a key from its components. Used by the
* KeyFactory.
*/
public ECPrivateKeyImpl(BigInteger s, ECParameterSpec params)
throws InvalidKeyException {
this.s = s;
this.params = params;
makeEncoding(s);
}
ECPrivateKeyImpl(byte[] s, ECParameterSpec params)
throws InvalidKeyException {
this.arrayS = s.clone();
this.params = params;
makeEncoding(s);
}
private void makeEncoding(byte[] s) throws InvalidKeyException {
algid = new AlgorithmId
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
try {
DerOutputStream out = new DerOutputStream();
out.putInteger(1); // version 1
byte[] privBytes = s.clone();
ArrayUtil.reverse(privBytes);
out.putOctetString(privBytes);
DerValue val =
new DerValue(DerValue.tag_Sequence, out.toByteArray());
key = val.toByteArray();
} catch (IOException exc) {
// should never occur
throw new InvalidKeyException(exc);
}
}
private void makeEncoding(BigInteger s) throws InvalidKeyException {
algid = new AlgorithmId
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
try {
byte[] sArr = s.toByteArray();
// convert to fixed-length array
int numOctets = (params.getOrder().bitLength() + 7) / 8;
byte[] sOctets = new byte[numOctets];
int inPos = Math.max(sArr.length - sOctets.length, 0);
int outPos = Math.max(sOctets.length - sArr.length, 0);
int length = Math.min(sArr.length, sOctets.length);
System.arraycopy(sArr, inPos, sOctets, outPos, length);
DerOutputStream out = new DerOutputStream();
out.putInteger(1); // version 1
out.putOctetString(sOctets);
DerValue val =
new DerValue(DerValue.tag_Sequence, out.toByteArray());
key = val.toByteArray();
} catch (IOException exc) {
// should never occur
throw new InvalidKeyException(exc);
}
}
// see JCA doc
public String getAlgorithm() {
return "EC";
}
// see JCA doc
public BigInteger getS() {
if (s == null) {
byte[] arrCopy = arrayS.clone();
ArrayUtil.reverse(arrCopy);
s = new BigInteger(1, arrCopy);
}
return s;
}
public byte[] getArrayS() {
if (arrayS == null) {
byte[] arr = getS().toByteArray();
ArrayUtil.reverse(arr);
int byteLength = (params.getOrder().bitLength() + 7) / 8;
arrayS = new byte[byteLength];
int length = Math.min(byteLength, arr.length);
System.arraycopy(arr, 0, arrayS, 0, length);
}
return arrayS.clone();
}
// see JCA doc
public ECParameterSpec getParams() {
return params;
}
/**
* Parse the key. Called by PKCS8Key.
*/
protected void parseKeyBits() throws InvalidKeyException {
try {
DerInputStream in = new DerInputStream(key);
DerValue derValue = in.getDerValue();
if (derValue.tag != DerValue.tag_Sequence) {
throw new IOException("Not a SEQUENCE");
}
DerInputStream data = derValue.data;
int version = data.getInteger();
if (version != 1) {
throw new IOException("Version must be 1");
}
byte[] privData = data.getOctetString();
ArrayUtil.reverse(privData);
arrayS = privData;
while (data.available() != 0) {
DerValue value = data.getDerValue();
if (value.isContextSpecific((byte) 0)) {
// ignore for now
} else if (value.isContextSpecific((byte) 1)) {
// ignore for now
} else {
throw new InvalidKeyException("Unexpected value: " + value);
}
}
AlgorithmParameters algParams = this.algid.getParameters();
if (algParams == null) {
throw new InvalidKeyException("EC domain parameters must be "
+ "encoded in the algorithm identifier");
}
params = algParams.getParameterSpec(ECParameterSpec.class);
} catch (IOException e) {
throw new InvalidKeyException("Invalid EC private key", e);
} catch (InvalidParameterSpecException e) {
throw new InvalidKeyException("Invalid EC private key", e);
}
}
/**
* Restores the state of this object from the stream.
* <p>
* Deserialization of this object is not supported.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException(
"ECPrivateKeyImpl keys are not directly deserializable");
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 2006, 2023, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import sun.security.util.ECParameters;
import sun.security.util.ECUtil;
import sun.security.x509.*;
/**
* Key implementation for EC public keys.
*
* @since 1.6
* @author Andreas Sterbenz
*/
public final class ECPublicKeyImpl extends X509Key implements ECPublicKey {
private static final long serialVersionUID = -2462037275160462289L;
private ECPoint w;
private ECParameterSpec params;
/**
* Construct a key from its components. Used by the
* ECKeyFactory.
*/
@SuppressWarnings("deprecation")
public ECPublicKeyImpl(ECPoint w, ECParameterSpec params)
throws InvalidKeyException {
this.w = w;
this.params = params;
// generate the encoding
algid = new AlgorithmId
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
key = ECUtil.encodePoint(w, params.getCurve());
}
/**
* Construct a key from its encoding.
*/
public ECPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
decode(encoded);
}
// see JCA doc
public String getAlgorithm() {
return "EC";
}
// see JCA doc
public ECPoint getW() {
return w;
}
// see JCA doc
public ECParameterSpec getParams() {
return params;
}
// Internal API to get the encoded point. Currently used by SunPKCS11.
// This may change/go away depending on what we do with the public API.
@SuppressWarnings("deprecation")
public byte[] getEncodedPublicValue() {
return key.clone();
}
/**
* Parse the key. Called by X509Key.
*/
@SuppressWarnings("deprecation")
protected void parseKeyBits() throws InvalidKeyException {
AlgorithmParameters algParams = this.algid.getParameters();
if (algParams == null) {
throw new InvalidKeyException("EC domain parameters must be " +
"encoded in the algorithm identifier");
}
try {
params = algParams.getParameterSpec(ECParameterSpec.class);
w = ECUtil.decodePoint(key, params.getCurve());
} catch (IOException e) {
throw new InvalidKeyException("Invalid EC key", e);
} catch (InvalidParameterSpecException e) {
throw new InvalidKeyException("Invalid EC key", e);
}
}
// return a string representation of this key for debugging
public String toString() {
return "Sun EC public key, " + params.getCurve().getField().getFieldSize()
+ " bits\n public x coord: " + w.getAffineX()
+ "\n public y coord: " + w.getAffineY()
+ "\n parameters: " + params;
}
private Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.PUBLIC,
getAlgorithm(),
getFormat(),
getEncoded());
}
/**
* Restores the state of this object from the stream.
* <p>
* Deserialization of this object is not supported.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException(
"ECPublicKeyImpl keys are not directly deserializable");
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import java.util.*;
import java.security.*;
import sun.security.action.PutAllAction;
/**
* Provider class for the Elliptic Curve provider.
* Supports EC keypair and parameter generation, ECDSA signing and
* ECDH key agreement.
*
* IMPLEMENTATION NOTE:
* The Java classes in this provider access a native ECC implementation
* via JNI to a C++ wrapper class which in turn calls C functions.
* The Java classes are packaged into the signed sunec.jar in the JRE
* extensions directory and the C++ and C functions are packaged into
* libsunec.so or sunec.dll in the JRE native libraries directory.
* If the native library is not present then this provider is registered
* with support for fewer ECC algorithms (KeyPairGenerator, Signature and
* KeyAgreement are omitted).
*
* @since 1.7
*/
public final class SunEC extends Provider {
private static final long serialVersionUID = -2279741672933606418L;
// flag indicating whether the full EC implementation is present
// (when native library is absent then fewer EC algorithms are available)
private static boolean useFullImplementation = true;
static {
try {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("sunec"); // check for native library
return null;
}
});
} catch (UnsatisfiedLinkError e) {
useFullImplementation = false;
}
}
public SunEC() {
super("SunEC", 1.8d, "Sun Elliptic Curve provider (EC, ECDSA, ECDH)");
// if there is no security manager installed, put directly into
// the provider. Otherwise, create a temporary map and use a
// doPrivileged() call at the end to transfer the contents
if (System.getSecurityManager() == null) {
SunECEntries.putEntries(this, useFullImplementation);
} else {
Map<Object, Object> map = new HashMap<Object, Object>();
SunECEntries.putEntries(map, useFullImplementation);
AccessController.doPrivileged(new PutAllAction(this, map));
}
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 2009, 2014, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec;
import java.util.Collection;
import java.util.Map;
import java.util.regex.Pattern;
import sun.security.util.CurveDB;
import sun.security.util.NamedCurve;
/**
* Defines the entries of the SunEC provider.
*
* @since 1.7
*/
final class SunECEntries {
private SunECEntries() {
// empty
}
static void putEntries(Map<Object, Object> map,
boolean useFullImplementation) {
/*
* Key Factory engine
*/
map.put("KeyFactory.EC", "sun.security.ec.ECKeyFactory");
map.put("Alg.Alias.KeyFactory.EllipticCurve", "EC");
map.put("KeyFactory.EC ImplementedIn", "Software");
/*
* Algorithm Parameter engine
*/
map.put("AlgorithmParameters.EC", "sun.security.util.ECParameters");
map.put("Alg.Alias.AlgorithmParameters.EllipticCurve", "EC");
map.put("Alg.Alias.AlgorithmParameters.1.2.840.10045.2.1", "EC");
map.put("AlgorithmParameters.EC KeySize", "256");
map.put("AlgorithmParameters.EC ImplementedIn", "Software");
// "AlgorithmParameters.EC SupportedCurves" prop used by unit test
boolean firstCurve = true;
StringBuilder names = new StringBuilder();
Pattern nameSplitPattern = Pattern.compile(CurveDB.SPLIT_PATTERN);
Collection<? extends NamedCurve> supportedCurves =
CurveDB.getSupportedCurves();
for (NamedCurve namedCurve : supportedCurves) {
if (!firstCurve) {
names.append("|");
} else {
firstCurve = false;
}
names.append("[");
String[] commonNames = nameSplitPattern.split(namedCurve.getName());
for (String commonName : commonNames) {
names.append(commonName.trim());
names.append(",");
}
names.append(namedCurve.getObjectId());
names.append("]");
}
map.put("AlgorithmParameters.EC SupportedCurves", names.toString());
/*
* Register the algorithms below only when the full ECC implementation
* is available
*/
if (!useFullImplementation) {
return;
}
/*
* Signature engines
*/
map.put("Signature.NONEwithECDSA",
"sun.security.ec.ECDSASignature$Raw");
map.put("Signature.SHA1withECDSA",
"sun.security.ec.ECDSASignature$SHA1");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.1", "SHA1withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
map.put("Signature.SHA224withECDSA",
"sun.security.ec.ECDSASignature$SHA224");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.1", "SHA224withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA");
map.put("Signature.SHA256withECDSA",
"sun.security.ec.ECDSASignature$SHA256");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.2", "SHA256withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA");
map.put("Signature.SHA384withECDSA",
"sun.security.ec.ECDSASignature$SHA384");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.3", "SHA384withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA");
map.put("Signature.SHA512withECDSA",
"sun.security.ec.ECDSASignature$SHA512");
map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.4", "SHA512withECDSA");
map.put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA");
String ecKeyClasses = "java.security.interfaces.ECPublicKey" +
"|java.security.interfaces.ECPrivateKey";
map.put("Signature.NONEwithECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA1withECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA224withECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA256withECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA384withECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA512withECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA1withECDSA KeySize", "256");
map.put("Signature.NONEwithECDSA ImplementedIn", "Software");
map.put("Signature.SHA1withECDSA ImplementedIn", "Software");
map.put("Signature.SHA224withECDSA ImplementedIn", "Software");
map.put("Signature.SHA256withECDSA ImplementedIn", "Software");
map.put("Signature.SHA384withECDSA ImplementedIn", "Software");
map.put("Signature.SHA512withECDSA ImplementedIn", "Software");
/*
* Key Pair Generator engine
*/
map.put("KeyPairGenerator.EC", "sun.security.ec.ECKeyPairGenerator");
map.put("Alg.Alias.KeyPairGenerator.EllipticCurve", "EC");
map.put("KeyPairGenerator.EC KeySize", "256");
map.put("KeyPairGenerator.EC ImplementedIn", "Software");
/*
* Key Agreement engine
*/
map.put("KeyAgreement.ECDH", "sun.security.ec.ECDHKeyAgreement");
map.put("KeyAgreement.ECDH SupportedKeyClasses", ecKeyClasses);
map.put("KeyAgreement.ECDH ImplementedIn", "Software");
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec.point;
import sun.security.util.math.ImmutableIntegerModuloP;
import java.util.Objects;
/**
* Elliptic curve point represented using affine coordinates (x, y). This class
* is not part of the sun.security.ec.point.Point hierarchy because it is not
* used to hold intermediate values during point arithmetic, and so it does not
* have a mutable form.
*/
public class AffinePoint {
private final ImmutableIntegerModuloP x;
private final ImmutableIntegerModuloP y;
public AffinePoint(ImmutableIntegerModuloP x, ImmutableIntegerModuloP y) {
this.x = x;
this.y = y;
}
public ImmutableIntegerModuloP getX() {
return x;
}
public ImmutableIntegerModuloP getY() {
return y;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof AffinePoint)) {
return false;
}
AffinePoint p = (AffinePoint) obj;
boolean xEquals = x.asBigInteger().equals(p.x.asBigInteger());
boolean yEquals = y.asBigInteger().equals(p.y.asBigInteger());
return xEquals && yEquals;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return "(" + x.asBigInteger().toString() + "," +
y.asBigInteger().toString() + ")";
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec.point;
/**
* An interface for immutable points on an elliptic curve over a finite field.
*/
public interface ImmutablePoint extends Point {
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec.point;
/**
* An interface for mutable points on an elliptic curve over a finite field.
*/
public interface MutablePoint extends Point {
MutablePoint setValue(AffinePoint p);
MutablePoint setValue(Point p);
MutablePoint conditionalSet(Point p, int set);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec.point;
import sun.security.util.math.IntegerFieldModuloP;
/**
* A base interface for points on an elliptic curve over a finite field.
* Implementations may use different representations for points, and this
* interface creates a common API for manipulating points. This API has no
* methods for point arithmetic, which depends on group structure and curve
* parameters in addition to point representation.
*/
public interface Point {
IntegerFieldModuloP getField();
AffinePoint asAffine();
ImmutablePoint fixed();
MutablePoint mutable();
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ec.point;
import sun.security.util.math.*;
/**
* Elliptic curve point in projective coordinates (X, Y, Z) where
* an affine point (x, y) is represented using any (X, Y, Z) s.t.
* x = X/Z and y = Y/Z.
*/
public abstract class ProjectivePoint
<T extends IntegerModuloP> implements Point {
protected final T x;
protected final T y;
protected final T z;
protected ProjectivePoint(T x, T y, T z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public IntegerFieldModuloP getField() {
return this.x.getField();
}
@Override
public Immutable fixed() {
return new Immutable(x.fixed(), y.fixed(), z.fixed());
}
@Override
public Mutable mutable() {
return new Mutable(x.mutable(), y.mutable(), z.mutable());
}
public T getX() {
return x;
}
public T getY() {
return y;
}
public T getZ() {
return z;
}
public AffinePoint asAffine() {
IntegerModuloP zInv = z.multiplicativeInverse();
return new AffinePoint(x.multiply(zInv), y.multiply(zInv));
}
public static class Immutable
extends ProjectivePoint<ImmutableIntegerModuloP>
implements ImmutablePoint {
public Immutable(ImmutableIntegerModuloP x,
ImmutableIntegerModuloP y,
ImmutableIntegerModuloP z) {
super(x, y, z);
}
}
public static class Mutable
extends ProjectivePoint<MutableIntegerModuloP>
implements MutablePoint {
public Mutable(MutableIntegerModuloP x,
MutableIntegerModuloP y,
MutableIntegerModuloP z) {
super(x, y, z);
}
public Mutable(IntegerFieldModuloP field) {
super(field.get0().mutable(),
field.get0().mutable(),
field.get0().mutable());
}
@Override
public Mutable conditionalSet(Point p, int set) {
if (!(p instanceof ProjectivePoint)) {
throw new RuntimeException("Incompatible point");
}
@SuppressWarnings("unchecked")
ProjectivePoint<IntegerModuloP> pp =
(ProjectivePoint<IntegerModuloP>) p;
return conditionalSet(pp, set);
}
private <T extends IntegerModuloP>
Mutable conditionalSet(ProjectivePoint<T> pp, int set) {
x.conditionalSet(pp.x, set);
y.conditionalSet(pp.y, set);
z.conditionalSet(pp.z, set);
return this;
}
@Override
public Mutable setValue(AffinePoint p) {
x.setValue(p.getX());
y.setValue(p.getY());
z.setValue(p.getX().getField().get1());
return this;
}
@Override
public Mutable setValue(Point p) {
if (!(p instanceof ProjectivePoint)) {
throw new RuntimeException("Incompatible point");
}
@SuppressWarnings("unchecked")
ProjectivePoint<IntegerModuloP> pp =
(ProjectivePoint<IntegerModuloP>) p;
return setValue(pp);
}
private <T extends IntegerModuloP>
Mutable setValue(ProjectivePoint<T> pp) {
x.setValue(pp.x);
y.setValue(pp.y);
z.setValue(pp.z);
return this;
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.internal.interfaces;
import javax.crypto.SecretKey;
/**
* An SSL/TLS master secret key. It is a <code>SecretKey</code> that optionally
* contains protocol version information that is used to detect version
* rollback attacks during the SSL/TLS handshake.
*
* <p>Implementation of this interface are returned by the
* <code>generateKey()</code> method of KeyGenerators of the type
* "TlsMasterSecret".
*
* @since 1.6
* @author Andreas Sterbenz
* @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future
* release.
*/
@Deprecated
public interface TlsMasterSecret extends SecretKey {
public static final long serialVersionUID = -461748105810469773L;
/**
* Returns the major version number encapsulated in the premaster secret
* this master secret was derived from, or -1 if it is not available.
*
* <p>This information will only usually only be available when RSA
* was used as the key exchange algorithm.
*
* @return the major version number, or -1 if it is not available
*/
public int getMajorVersion();
/**
* Returns the minor version number encapsulated in the premaster secret
* this master secret was derived from, or -1 if it is not available.
*
* <p>This information will only usually only be available when RSA
* was used as the key exchange algorithm.
*
* @return the major version number, or -1 if it is not available
*/
public int getMinorVersion();
}

View File

@@ -0,0 +1,253 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.internal.spec;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
/**
* Parameters for SSL/TLS key material generation.
* This class is used to initialize KeyGenerator of the type
* "TlsKeyMaterial". The keys returned by such KeyGenerators will be
* instances of {@link TlsKeyMaterialSpec}.
*
* <p>Instances of this class are immutable.
*
* @since 1.6
* @author Andreas Sterbenz
* @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future
* release.
*/
@Deprecated
public class TlsKeyMaterialParameterSpec implements AlgorithmParameterSpec {
private final SecretKey masterSecret;
private final int majorVersion, minorVersion;
private final byte[] clientRandom, serverRandom;
private final String cipherAlgorithm;
private final int cipherKeyLength, ivLength, macKeyLength;
private final int expandedCipherKeyLength; // == 0 for domestic ciphersuites
private final String prfHashAlg;
private final int prfHashLength;
private final int prfBlockSize;
/**
* Constructs a new TlsKeyMaterialParameterSpec.
*
* @param masterSecret the master secret
* @param majorVersion the major number of the protocol version
* @param minorVersion the minor number of the protocol version
* @param clientRandom the client's random value
* @param serverRandom the server's random value
* @param cipherAlgorithm the algorithm name of the cipher keys to
* be generated
* @param cipherKeyLength if 0, no cipher keys will be generated;
* otherwise, the length in bytes of cipher keys to be
* generated for domestic cipher suites; for cipher suites defined as
* exportable, the number of key material bytes to be generated;
* @param expandedCipherKeyLength 0 for domestic cipher suites; for
* exportable cipher suites the length in bytes of the key to be
* generated.
* @param ivLength the length in bytes of the initialization vector
* to be generated, or 0 if no initialization vector is required
* @param macKeyLength the length in bytes of the MAC key to be generated
* @param prfHashAlg the name of the TLS PRF hash algorithm to use.
* Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF.
* @param prfHashLength the output length of the TLS PRF hash algorithm.
* Used only for TLS 1.2+.
* @param prfBlockSize the input block size of the TLS PRF hash algorithm.
* Used only for TLS 1.2+.
*
* @throws NullPointerException if masterSecret, clientRandom,
* serverRandom, or cipherAlgorithm are null
* @throws IllegalArgumentException if the algorithm of masterSecret is
* not TlsMasterSecret, or if majorVersion or minorVersion are
* negative or larger than 255; or if cipherKeyLength, expandedKeyLength,
* ivLength, or macKeyLength are negative
*/
public TlsKeyMaterialParameterSpec(SecretKey masterSecret,
int majorVersion, int minorVersion, byte[] clientRandom,
byte[] serverRandom, String cipherAlgorithm, int cipherKeyLength,
int expandedCipherKeyLength, int ivLength, int macKeyLength,
String prfHashAlg, int prfHashLength, int prfBlockSize) {
if (masterSecret.getAlgorithm().equals("TlsMasterSecret") == false) {
throw new IllegalArgumentException("Not a TLS master secret");
}
if (cipherAlgorithm == null) {
throw new NullPointerException();
}
this.masterSecret = masterSecret;
this.majorVersion =
TlsMasterSecretParameterSpec.checkVersion(majorVersion);
this.minorVersion =
TlsMasterSecretParameterSpec.checkVersion(minorVersion);
this.clientRandom = clientRandom.clone();
this.serverRandom = serverRandom.clone();
this.cipherAlgorithm = cipherAlgorithm;
this.cipherKeyLength = checkSign(cipherKeyLength);
this.expandedCipherKeyLength = checkSign(expandedCipherKeyLength);
this.ivLength = checkSign(ivLength);
this.macKeyLength = checkSign(macKeyLength);
this.prfHashAlg = prfHashAlg;
this.prfHashLength = prfHashLength;
this.prfBlockSize = prfBlockSize;
}
private static int checkSign(int k) {
if (k < 0) {
throw new IllegalArgumentException("Value must not be negative");
}
return k;
}
/**
* Returns the master secret.
*
* @return the master secret.
*/
public SecretKey getMasterSecret() {
return masterSecret;
}
/**
* Returns the major version number.
*
* @return the major version number.
*/
public int getMajorVersion() {
return majorVersion;
}
/**
* Returns the minor version number.
*
* @return the minor version number.
*/
public int getMinorVersion() {
return minorVersion;
}
/**
* Returns a copy of the client's random value.
*
* @return a copy of the client's random value.
*/
public byte[] getClientRandom() {
return clientRandom.clone();
}
/**
* Returns a copy of the server's random value.
*
* @return a copy of the server's random value.
*/
public byte[] getServerRandom() {
return serverRandom.clone();
}
/**
* Returns the cipher algorithm.
*
* @return the cipher algorithm.
*/
public String getCipherAlgorithm() {
return cipherAlgorithm;
}
/**
* Returns the length in bytes of the encryption key to be generated.
*
* @return the length in bytes of the encryption key to be generated.
*/
public int getCipherKeyLength() {
return cipherKeyLength;
}
/**
* Returns the length in bytes of the expanded encryption key to be
* generated. Returns zero if the expanded encryption key is not
* supposed to be generated.
*
* @return the length in bytes of the expanded encryption key to be
* generated.
*/
public int getExpandedCipherKeyLength() {
// TLS v1.1 disables the exportable weak cipher suites.
if (majorVersion >= 0x03 && minorVersion >= 0x02) {
return 0;
}
return expandedCipherKeyLength;
}
/**
* Returns the length in bytes of the initialization vector to be
* generated. Returns zero if the initialization vector is not
* supposed to be generated.
*
* @return the length in bytes of the initialization vector to be
* generated.
*/
public int getIvLength() {
return ivLength;
}
/**
* Returns the length in bytes of the MAC key to be generated.
*
* @return the length in bytes of the MAC key to be generated.
*/
public int getMacKeyLength() {
return macKeyLength;
}
/**
* Obtains the PRF hash algorithm to use in the PRF calculation.
*
* @return the hash algorithm.
*/
public String getPRFHashAlg() {
return prfHashAlg;
}
/**
* Obtains the length of the PRF hash algorithm.
*
* @return the hash algorithm length.
*/
public int getPRFHashLength() {
return prfHashLength;
}
/**
* Obtains the block size of the PRF hash algorithm.
*
* @return the hash algorithm block size
*/
public int getPRFBlockSize() {
return prfBlockSize;
}
}

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.internal.spec;
import java.security.spec.KeySpec;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
/**
* KeySpec class for SSL/TLS key material.
*
* <p>Instances of this class are returned by the <code>generateKey()</code>
* method of KeyGenerators of the type "TlsKeyMaterial".
* Instances of this class are immutable.
*
* @since 1.6
* @author Andreas Sterbenz
* @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future
* release.
*/
@Deprecated
public class TlsKeyMaterialSpec implements KeySpec, SecretKey {
static final long serialVersionUID = 812912859129525028L;
private final SecretKey clientMacKey, serverMacKey;
private final SecretKey clientCipherKey, serverCipherKey;
private final IvParameterSpec clientIv, serverIv;
/**
* Constructs a new TlsKeymaterialSpec from the client and server MAC
* keys.
* This call is equivalent to
* <code>new TlsKeymaterialSpec(clientMacKey, serverMacKey,
* null, null, null, null)</code>.
*
* @param clientMacKey the client MAC key (or null)
* @param serverMacKey the server MAC key (or null)
*/
public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey) {
this(clientMacKey, serverMacKey, null, null, null, null);
}
/**
* Constructs a new TlsKeymaterialSpec from the client and server MAC
* keys and client and server cipher keys.
* This call is equivalent to
* <code>new TlsKeymaterialSpec(clientMacKey, serverMacKey,
* clientCipherKey, serverCipherKey, null, null)</code>.
*
* @param clientMacKey the client MAC key (or null)
* @param serverMacKey the server MAC key (or null)
* @param clientCipherKey the client cipher key (or null)
* @param serverCipherKey the server cipher key (or null)
*/
public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey,
SecretKey clientCipherKey, SecretKey serverCipherKey) {
this(clientMacKey, serverMacKey, clientCipherKey, null,
serverCipherKey, null);
}
/**
* Constructs a new TlsKeymaterialSpec from the client and server MAC
* keys, client and server cipher keys, and client and server
* initialization vectors.
*
* @param clientMacKey the client MAC key (or null)
* @param serverMacKey the server MAC key (or null)
* @param clientCipherKey the client cipher key (or null)
* @param clientIv the client initialization vector (or null)
* @param serverCipherKey the server cipher key (or null)
* @param serverIv the server initialization vector (or null)
*/
public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey,
SecretKey clientCipherKey, IvParameterSpec clientIv,
SecretKey serverCipherKey, IvParameterSpec serverIv) {
this.clientMacKey = clientMacKey;
this.serverMacKey = serverMacKey;
this.clientCipherKey = clientCipherKey;
this.serverCipherKey = serverCipherKey;
this.clientIv = clientIv;
this.serverIv = serverIv;
}
/**
* Returns <code>TlsKeyMaterial</code>.
*
* @return <code>TlsKeyMaterial</code>.
*/
public String getAlgorithm() {
return "TlsKeyMaterial";
}
/**
* Returns <code>null</code> because keys of this type have no encoding.
*
* @return <code>null</code> because keys of this type have no encoding.
*/
public String getFormat() {
return null;
}
/**
* Returns <code>null</code> because keys of this type have no encoding.
*
* @return <code>null</code> because keys of this type have no encoding.
*/
public byte[] getEncoded() {
return null;
}
/**
* Returns the client MAC key.
*
* @return the client MAC key (or null).
*/
public SecretKey getClientMacKey() {
return clientMacKey;
}
/**
* Return the server MAC key.
*
* @return the server MAC key (or null).
*/
public SecretKey getServerMacKey() {
return serverMacKey;
}
/**
* Return the client cipher key (or null).
*
* @return the client cipher key (or null).
*/
public SecretKey getClientCipherKey() {
return clientCipherKey;
}
/**
* Return the client initialization vector (or null).
*
* @return the client initialization vector (or null).
*/
public IvParameterSpec getClientIv() {
return clientIv;
}
/**
* Return the server cipher key (or null).
*
* @return the server cipher key (or null).
*/
public SecretKey getServerCipherKey() {
return serverCipherKey;
}
/**
* Return the server initialization vector (or null).
*
* @return the server initialization vector (or null).
*/
public IvParameterSpec getServerIv() {
return serverIv;
}
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.internal.spec;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
/**
* Parameters for SSL/TLS master secret generation.
* This class encapsulates the information necessary to calculate a SSL/TLS
* master secret from the premaster secret and other parameters.
* It is used to initialize KeyGenerators of the type "TlsMasterSecret".
*
* <p>Instances of this class are immutable.
*
* @since 1.6
* @author Andreas Sterbenz
* @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future
* release.
*/
@Deprecated
public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec {
private final SecretKey premasterSecret;
private final int majorVersion, minorVersion;
private final byte[] clientRandom, serverRandom;
private final byte[] extendedMasterSecretSessionHash;
private final String prfHashAlg;
private final int prfHashLength;
private final int prfBlockSize;
/**
* Constructs a new TlsMasterSecretParameterSpec.
*
* <p>The <code>getAlgorithm()</code> method of <code>premasterSecret</code>
* should return <code>"TlsRsaPremasterSecret"</code> if the key exchange
* algorithm was RSA and <code>"TlsPremasterSecret"</code> otherwise.
*
* @param premasterSecret the premaster secret
* @param majorVersion the major number of the protocol version
* @param minorVersion the minor number of the protocol version
* @param clientRandom the client's random value
* @param serverRandom the server's random value
* @param prfHashAlg the name of the TLS PRF hash algorithm to use.
* Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF.
* @param prfHashLength the output length of the TLS PRF hash algorithm.
* Used only for TLS 1.2+.
* @param prfBlockSize the input block size of the TLS PRF hash algorithm.
* Used only for TLS 1.2+.
*
* @throws NullPointerException if premasterSecret, clientRandom,
* or serverRandom are null
* @throws IllegalArgumentException if minorVersion or majorVersion are
* negative or larger than 255
*/
public TlsMasterSecretParameterSpec(SecretKey premasterSecret,
int majorVersion, int minorVersion,
byte[] clientRandom, byte[] serverRandom,
String prfHashAlg, int prfHashLength, int prfBlockSize) {
this(premasterSecret, majorVersion, minorVersion,
clientRandom, serverRandom,
new byte[0],
prfHashAlg, prfHashLength, prfBlockSize);
}
/**
* Constructs a new TlsMasterSecretParameterSpec.
*
* <p>The <code>getAlgorithm()</code> method of <code>premasterSecret</code>
* should return <code>"TlsRsaPremasterSecret"</code> if the key exchange
* algorithm was RSA and <code>"TlsPremasterSecret"</code> otherwise.
*
* @param premasterSecret the premaster secret
* @param majorVersion the major number of the protocol version
* @param minorVersion the minor number of the protocol version
* @param extendedMasterSecretSessionHash the session hash for
* Extended Master Secret
* @param prfHashAlg the name of the TLS PRF hash algorithm to use.
* Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF.
* @param prfHashLength the output length of the TLS PRF hash algorithm.
* Used only for TLS 1.2+.
* @param prfBlockSize the input block size of the TLS PRF hash algorithm.
* Used only for TLS 1.2+.
*
* @throws NullPointerException if premasterSecret is null
* @throws IllegalArgumentException if minorVersion or majorVersion are
* negative or larger than 255
*/
public TlsMasterSecretParameterSpec(SecretKey premasterSecret,
int majorVersion, int minorVersion,
byte[] extendedMasterSecretSessionHash,
String prfHashAlg, int prfHashLength, int prfBlockSize) {
this(premasterSecret, majorVersion, minorVersion,
new byte[0], new byte[0],
extendedMasterSecretSessionHash,
prfHashAlg, prfHashLength, prfBlockSize);
}
private TlsMasterSecretParameterSpec(SecretKey premasterSecret,
int majorVersion, int minorVersion,
byte[] clientRandom, byte[] serverRandom,
byte[] extendedMasterSecretSessionHash,
String prfHashAlg, int prfHashLength, int prfBlockSize) {
if (premasterSecret == null) {
throw new NullPointerException("premasterSecret must not be null");
}
this.premasterSecret = premasterSecret;
this.majorVersion = checkVersion(majorVersion);
this.minorVersion = checkVersion(minorVersion);
this.clientRandom = clientRandom.clone();
this.serverRandom = serverRandom.clone();
this.extendedMasterSecretSessionHash =
(extendedMasterSecretSessionHash != null ?
extendedMasterSecretSessionHash.clone() : new byte[0]);
this.prfHashAlg = prfHashAlg;
this.prfHashLength = prfHashLength;
this.prfBlockSize = prfBlockSize;
}
static int checkVersion(int version) {
if ((version < 0) || (version > 255)) {
throw new IllegalArgumentException(
"Version must be between 0 and 255");
}
return version;
}
/**
* Returns the premaster secret.
*
* @return the premaster secret.
*/
public SecretKey getPremasterSecret() {
return premasterSecret;
}
/**
* Returns the major version number.
*
* @return the major version number.
*/
public int getMajorVersion() {
return majorVersion;
}
/**
* Returns the minor version number.
*
* @return the minor version number.
*/
public int getMinorVersion() {
return minorVersion;
}
/**
* Returns a copy of the client's random value.
*
* @return a copy of the client's random value.
*/
public byte[] getClientRandom() {
return clientRandom.clone();
}
/**
* Returns a copy of the server's random value.
*
* @return a copy of the server's random value.
*/
public byte[] getServerRandom() {
return serverRandom.clone();
}
/**
* Returns a copy of the Extended Master Secret session hash.
*
* @return a copy of the Extended Master Secret session hash, or an empty
* array if no extended master secret session hash was provided
* at instantiation time
*/
public byte[] getExtendedMasterSecretSessionHash() {
return extendedMasterSecretSessionHash.clone();
}
/**
* Obtains the PRF hash algorithm to use in the PRF calculation.
*
* @return the hash algorithm.
*/
public String getPRFHashAlg() {
return prfHashAlg;
}
/**
* Obtains the length of the PRF hash algorithm.
*
* @return the hash algorithm length.
*/
public int getPRFHashLength() {
return prfHashLength;
}
/**
* Obtains the block size of the PRF hash algorithm.
*
* @return the hash algorithm block size.
*/
public int getPRFBlockSize() {
return prfBlockSize;
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.internal.spec;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
/**
* Parameters for the TLS PRF (pseudo-random function). The PRF function
* is defined in RFC 2246.
* This class is used to initialize KeyGenerators of the type "TlsPrf".
*
* <p>Instances of this class are immutable.
*
* @since 1.6
* @author Andreas Sterbenz
* @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future
* release.
*/
@Deprecated
public class TlsPrfParameterSpec implements AlgorithmParameterSpec {
private final SecretKey secret;
private final String label;
private final byte[] seed;
private final int outputLength;
private final String prfHashAlg;
private final int prfHashLength;
private final int prfBlockSize;
/**
* Constructs a new TlsPrfParameterSpec.
*
* @param secret the secret to use in the calculation (or null)
* @param label the label to use in the calculation
* @param seed the random seed to use in the calculation
* @param outputLength the length in bytes of the output key to be produced
* @param prfHashAlg the name of the TLS PRF hash algorithm to use.
* Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF.
* @param prfHashLength the output length of the TLS PRF hash algorithm.
* Used only for TLS 1.2+.
* @param prfBlockSize the input block size of the TLS PRF hash algorithm.
* Used only for TLS 1.2+.
*
* @throws NullPointerException if label or seed is null
* @throws IllegalArgumentException if outputLength is negative
*/
public TlsPrfParameterSpec(SecretKey secret, String label,
byte[] seed, int outputLength,
String prfHashAlg, int prfHashLength, int prfBlockSize) {
if ((label == null) || (seed == null)) {
throw new NullPointerException("label and seed must not be null");
}
if (outputLength <= 0) {
throw new IllegalArgumentException("outputLength must be positive");
}
this.secret = secret;
this.label = label;
this.seed = seed.clone();
this.outputLength = outputLength;
this.prfHashAlg = prfHashAlg;
this.prfHashLength = prfHashLength;
this.prfBlockSize = prfBlockSize;
}
/**
* Returns the secret to use in the PRF calculation, or null if there is no
* secret.
*
* @return the secret to use in the PRF calculation, or null if there is no
* secret.
*/
public SecretKey getSecret() {
return secret;
}
/**
* Returns the label to use in the PRF calcuation.
*
* @return the label to use in the PRF calcuation.
*/
public String getLabel() {
return label;
}
/**
* Returns a copy of the seed to use in the PRF calcuation.
*
* @return a copy of the seed to use in the PRF calcuation.
*/
public byte[] getSeed() {
return seed.clone();
}
/**
* Returns the length in bytes of the output key to be produced.
*
* @return the length in bytes of the output key to be produced.
*/
public int getOutputLength() {
return outputLength;
}
/**
* Obtains the PRF hash algorithm to use in the PRF calculation.
*
* @return the hash algorithm, or null if no algorithm was specified.
*/
public String getPRFHashAlg() {
return prfHashAlg;
}
/**
* Obtains the length of PRF hash algorithm.
*
* It would have been preferred to use MessageDigest.getDigestLength(),
* but the API does not require implementations to support the method.
*
* @return the hash algorithm length.
*/
public int getPRFHashLength() {
return prfHashLength;
}
/**
* Obtains the length of PRF hash algorithm.
*
* @return the hash algorithm length.
*/
public int getPRFBlockSize() {
return prfBlockSize;
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.internal.spec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* Parameters for SSL/TLS RSA premaster secret.
*
* <p>Instances of this class are immutable.
*
* @since 1.6
* @author Andreas Sterbenz
* @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future
* release.
*/
@Deprecated
public class TlsRsaPremasterSecretParameterSpec
implements AlgorithmParameterSpec {
private final byte[] encodedSecret;
/*
* The TLS spec says that the version in the RSA premaster secret must
* be the maximum version supported by the client (i.e. the version it
* requested in its client hello version). However, we (and other
* implementations) used to send the active negotiated version. The
* system property below allows to toggle the behavior.
*/
private final static String PROP_NAME =
"com.sun.net.ssl.rsaPreMasterSecretFix";
/*
* Default is "false" (old behavior) for compatibility reasons in
* SSLv3/TLSv1. Later protocols (TLSv1.1+) do not use this property.
*/
private final static boolean rsaPreMasterSecretFix =
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
String value = System.getProperty(PROP_NAME);
if (value != null && value.equalsIgnoreCase("true")) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
});
private final int clientVersion;
private final int serverVersion;
/**
* Constructs a new TlsRsaPremasterSecretParameterSpec.
*
* @param clientVersion the version of the TLS protocol by which the
* client wishes to communicate during this session
* @param serverVersion the negotiated version of the TLS protocol which
* contains the lower of that suggested by the client in the client
* hello and the highest supported by the server.
*
* @throws IllegalArgumentException if clientVersion or serverVersion are
* negative or larger than (2^16 - 1)
*/
public TlsRsaPremasterSecretParameterSpec(
int clientVersion, int serverVersion) {
this.clientVersion = checkVersion(clientVersion);
this.serverVersion = checkVersion(serverVersion);
this.encodedSecret = null;
}
/**
* Constructs a new TlsRsaPremasterSecretParameterSpec.
*
* @param clientVersion the version of the TLS protocol by which the
* client wishes to communicate during this session
* @param serverVersion the negotiated version of the TLS protocol which
* contains the lower of that suggested by the client in the client
* hello and the highest supported by the server.
* @param encodedSecret the encoded secret key
*
* @throws IllegalArgumentException if clientVersion or serverVersion are
* negative or larger than (2^16 - 1) or if encodedSecret is not
* exactly 48 bytes
*/
public TlsRsaPremasterSecretParameterSpec(
int clientVersion, int serverVersion, byte[] encodedSecret) {
this.clientVersion = checkVersion(clientVersion);
this.serverVersion = checkVersion(serverVersion);
if (encodedSecret == null || encodedSecret.length != 48) {
throw new IllegalArgumentException(
"Encoded secret is not exactly 48 bytes");
}
this.encodedSecret = encodedSecret.clone();
}
/**
* Returns the version of the TLS protocol by which the client wishes to
* communicate during this session.
*
* @return the version of the TLS protocol in ClientHello message
*/
public int getClientVersion() {
return clientVersion;
}
/**
* Returns the negotiated version of the TLS protocol which contains the
* lower of that suggested by the client in the client hello and the
* highest supported by the server.
*
* @return the negotiated version of the TLS protocol in ServerHello message
*/
public int getServerVersion() {
return serverVersion;
}
/**
* Returns the major version used in RSA premaster secret.
*
* @return the major version used in RSA premaster secret.
*/
public int getMajorVersion() {
if (rsaPreMasterSecretFix || clientVersion >= 0x0302) {
// 0x0302: TLSv1.1
return (clientVersion >>> 8) & 0xFF;
}
return (serverVersion >>> 8) & 0xFF;
}
/**
* Returns the minor version used in RSA premaster secret.
*
* @return the minor version used in RSA premaster secret.
*/
public int getMinorVersion() {
if (rsaPreMasterSecretFix || clientVersion >= 0x0302) {
// 0x0302: TLSv1.1
return clientVersion & 0xFF;
}
return serverVersion & 0xFF;
}
private int checkVersion(int version) {
if ((version < 0) || (version > 0xFFFF)) {
throw new IllegalArgumentException(
"Version must be between 0 and 65,535");
}
return version;
}
/**
* Returns the encoded secret.
*
* @return the encoded secret, may be null if no encoded secret.
*/
public byte[] getEncodedSecret() {
return encodedSecret == null ? null : encodedSecret.clone();
}
}

View File

@@ -0,0 +1,264 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jca;
import java.util.*;
import java.security.*;
import java.security.Provider.Service;
/**
* Collection of utility methods to facilitate implementing getInstance()
* methods in the JCA/JCE/JSSE/... framework.
*
* @author Andreas Sterbenz
* @since 1.5
*/
public class GetInstance {
private GetInstance() {
// empty
}
/**
* Static inner class representing a newly created instance.
*/
public static final class Instance {
// public final fields, access directly without accessors
public final Provider provider;
public final Object impl;
private Instance(Provider provider, Object impl) {
this.provider = provider;
this.impl = impl;
}
// Return Provider and implementation as an array as used in the
// old Security.getImpl() methods.
public Object[] toArray() {
return new Object[] {impl, provider};
}
}
public static Service getService(String type, String algorithm)
throws NoSuchAlgorithmException {
ProviderList list = Providers.getProviderList();
Service s = list.getService(type, algorithm);
if (s == null) {
throw new NoSuchAlgorithmException
(algorithm + " " + type + " not available");
}
return s;
}
public static Service getService(String type, String algorithm,
String provider) throws NoSuchAlgorithmException,
NoSuchProviderException {
if ((provider == null) || (provider.length() == 0)) {
throw new IllegalArgumentException("missing provider");
}
Provider p = Providers.getProviderList().getProvider(provider);
if (p == null) {
throw new NoSuchProviderException("no such provider: " + provider);
}
Service s = p.getService(type, algorithm);
if (s == null) {
throw new NoSuchAlgorithmException("no such algorithm: "
+ algorithm + " for provider " + provider);
}
return s;
}
public static Service getService(String type, String algorithm,
Provider provider) throws NoSuchAlgorithmException {
if (provider == null) {
throw new IllegalArgumentException("missing provider");
}
Service s = provider.getService(type, algorithm);
if (s == null) {
throw new NoSuchAlgorithmException("no such algorithm: "
+ algorithm + " for provider " + provider.getName());
}
return s;
}
/**
* Return a List of all the available Services that implement
* (type, algorithm). Note that the list is initialized lazily
* and Provider loading and lookup is only trigered when
* necessary.
*/
public static List<Service> getServices(String type, String algorithm) {
ProviderList list = Providers.getProviderList();
return list.getServices(type, algorithm);
}
/**
* This method exists for compatibility with JCE only. It will be removed
* once JCE has been changed to use the replacement method.
* @deprecated use getServices(List<ServiceId>) instead
*/
@Deprecated
public static List<Service> getServices(String type,
List<String> algorithms) {
ProviderList list = Providers.getProviderList();
return list.getServices(type, algorithms);
}
/**
* Return a List of all the available Services that implement any of
* the specified algorithms. See getServices(String, String) for detals.
*/
public static List<Service> getServices(List<ServiceId> ids) {
ProviderList list = Providers.getProviderList();
return list.getServices(ids);
}
/*
* For all the getInstance() methods below:
* @param type the type of engine (e.g. MessageDigest)
* @param clazz the Spi class that the implementation must subclass
* (e.g. MessageDigestSpi.class) or null if no superclass check
* is required
* @param algorithm the name of the algorithm (or alias), e.g. MD5
* @param provider the provider (String or Provider object)
* @param param the parameter to pass to the Spi constructor
* (for CertStores)
*
* There are overloaded methods for all the permutations.
*/
public static Instance getInstance(String type, Class<?> clazz,
String algorithm) throws NoSuchAlgorithmException {
// in the almost all cases, the first service will work
// avoid taking long path if so
ProviderList list = Providers.getProviderList();
Service firstService = list.getService(type, algorithm);
if (firstService == null) {
throw new NoSuchAlgorithmException
(algorithm + " " + type + " not available");
}
NoSuchAlgorithmException failure;
try {
return getInstance(firstService, clazz);
} catch (NoSuchAlgorithmException e) {
failure = e;
}
// if we cannot get the service from the preferred provider,
// fail over to the next
for (Service s : list.getServices(type, algorithm)) {
if (s == firstService) {
// do not retry initial failed service
continue;
}
try {
return getInstance(s, clazz);
} catch (NoSuchAlgorithmException e) {
failure = e;
}
}
throw failure;
}
public static Instance getInstance(String type, Class<?> clazz,
String algorithm, Object param) throws NoSuchAlgorithmException {
List<Service> services = getServices(type, algorithm);
NoSuchAlgorithmException failure = null;
for (Service s : services) {
try {
return getInstance(s, clazz, param);
} catch (NoSuchAlgorithmException e) {
failure = e;
}
}
if (failure != null) {
throw failure;
} else {
throw new NoSuchAlgorithmException
(algorithm + " " + type + " not available");
}
}
public static Instance getInstance(String type, Class<?> clazz,
String algorithm, String provider) throws NoSuchAlgorithmException,
NoSuchProviderException {
return getInstance(getService(type, algorithm, provider), clazz);
}
public static Instance getInstance(String type, Class<?> clazz,
String algorithm, Object param, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
return getInstance(getService(type, algorithm, provider), clazz, param);
}
public static Instance getInstance(String type, Class<?> clazz,
String algorithm, Provider provider)
throws NoSuchAlgorithmException {
return getInstance(getService(type, algorithm, provider), clazz);
}
public static Instance getInstance(String type, Class<?> clazz,
String algorithm, Object param, Provider provider)
throws NoSuchAlgorithmException {
return getInstance(getService(type, algorithm, provider), clazz, param);
}
/*
* The two getInstance() methods below take a service. They are
* intended for classes that cannot use the standard methods, e.g.
* because they implement delayed provider selection like the
* Signature class.
*/
public static Instance getInstance(Service s, Class<?> clazz)
throws NoSuchAlgorithmException {
Object instance = s.newInstance(null);
checkSuperClass(s, instance.getClass(), clazz);
return new Instance(s.getProvider(), instance);
}
public static Instance getInstance(Service s, Class<?> clazz,
Object param) throws NoSuchAlgorithmException {
Object instance = s.newInstance(param);
checkSuperClass(s, instance.getClass(), clazz);
return new Instance(s.getProvider(), instance);
}
/**
* Check is subClass is a subclass of superClass. If not,
* throw a NoSuchAlgorithmException.
*/
public static void checkSuperClass(Service s, Class<?> subClass,
Class<?> superClass) throws NoSuchAlgorithmException {
if (superClass == null) {
return;
}
if (superClass.isAssignableFrom(subClass) == false) {
throw new NoSuchAlgorithmException
("class configured for " + s.getType() + ": "
+ s.getClassName() + " not a " + s.getType());
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jca;
import java.lang.ref.*;
import java.security.*;
/**
* Collection of static utility methods used by the security framework.
*
* @author Andreas Sterbenz
* @since 1.5
*/
public final class JCAUtil {
private JCAUtil() {
// no instantiation
}
// size of the temporary arrays we use. Should fit into the CPU's 1st
// level cache and could be adjusted based on the platform
private final static int ARRAY_SIZE = 4096;
/**
* Get the size of a temporary buffer array to use in order to be
* cache efficient. totalSize indicates the total amount of data to
* be buffered. Used by the engineUpdate(ByteBuffer) methods.
*/
public static int getTempArraySize(int totalSize) {
return Math.min(ARRAY_SIZE, totalSize);
}
// cached SecureRandom instance
private static class CachedSecureRandomHolder {
public static SecureRandom instance = new SecureRandom();
}
/**
* Get a SecureRandom instance. This method should be used by JDK
* internal code in favor of calling "new SecureRandom()". That needs to
* iterate through the provider table to find the default SecureRandom
* implementation, which is fairly inefficient.
*/
public static SecureRandom getSecureRandom() {
return CachedSecureRandomHolder.instance;
}
}

View File

@@ -0,0 +1,293 @@
/*
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jca;
import java.io.File;
import java.lang.reflect.*;
import java.security.*;
import sun.security.util.PropertyExpander;
/**
* Class representing a configured provider. Encapsulates configuration
* (className plus optional argument), the provider loading logic, and
* the loaded Provider object itself.
*
* @author Andreas Sterbenz
* @since 1.5
*/
final class ProviderConfig {
private final static sun.security.util.Debug debug =
sun.security.util.Debug.getInstance("jca", "ProviderConfig");
// classname of the SunPKCS11-Solaris provider
private static final String P11_SOL_NAME =
"sun.security.pkcs11.SunPKCS11";
// config file argument of the SunPKCS11-Solaris provider
private static final String P11_SOL_ARG =
"${java.home}/lib/security/sunpkcs11-solaris.cfg";
// maximum number of times to try loading a provider before giving up
private final static int MAX_LOAD_TRIES = 30;
// parameters for the Provider(String) constructor,
// use by doLoadProvider()
private final static Class[] CL_STRING = { String.class };
// name of the provider class
private final String className;
// argument to the provider constructor,
// empty string indicates no-arg constructor
private final String argument;
// number of times we have already tried to load this provider
private int tries;
// Provider object, if loaded
private volatile Provider provider;
// flag indicating if we are currently trying to load the provider
// used to detect recursion
private boolean isLoading;
ProviderConfig(String className, String argument) {
if (className.equals(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) {
checkSunPKCS11Solaris();
}
this.className = className;
this.argument = expand(argument);
}
ProviderConfig(String className) {
this(className, "");
}
ProviderConfig(Provider provider) {
this.className = provider.getClass().getName();
this.argument = "";
this.provider = provider;
}
// check if we should try to load the SunPKCS11-Solaris provider
// avoid if not available (pre Solaris 10) to reduce startup time
// or if disabled via system property
private void checkSunPKCS11Solaris() {
Boolean o = AccessController.doPrivileged(
new PrivilegedAction<Boolean>() {
public Boolean run() {
File file = new File("/usr/lib/libpkcs11.so");
if (file.exists() == false) {
return Boolean.FALSE;
}
if ("false".equalsIgnoreCase(System.getProperty
("sun.security.pkcs11.enable-solaris"))) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
});
if (o == Boolean.FALSE) {
tries = MAX_LOAD_TRIES;
}
}
private boolean hasArgument() {
return argument.length() != 0;
}
// should we try to load this provider?
private boolean shouldLoad() {
return (tries < MAX_LOAD_TRIES);
}
// do not try to load this provider again
private void disableLoad() {
tries = MAX_LOAD_TRIES;
}
boolean isLoaded() {
return (provider != null);
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ProviderConfig == false) {
return false;
}
ProviderConfig other = (ProviderConfig)obj;
return this.className.equals(other.className)
&& this.argument.equals(other.argument);
}
public int hashCode() {
return className.hashCode() + argument.hashCode();
}
public String toString() {
if (hasArgument()) {
return className + "('" + argument + "')";
} else {
return className;
}
}
/**
* Get the provider object. Loads the provider if it is not already loaded.
*/
synchronized Provider getProvider() {
// volatile variable load
Provider p = provider;
if (p != null) {
return p;
}
if (shouldLoad() == false) {
return null;
}
if (isLoading) {
// because this method is synchronized, this can only
// happen if there is recursion.
if (debug != null) {
debug.println("Recursion loading provider: " + this);
new Exception("Call trace").printStackTrace();
}
return null;
}
try {
isLoading = true;
tries++;
p = doLoadProvider();
} finally {
isLoading = false;
}
provider = p;
return p;
}
/**
* Load and instantiate the Provider described by this class.
*
* NOTE use of doPrivileged().
*
* @return null if the Provider could not be loaded
*
* @throws ProviderException if executing the Provider's constructor
* throws a ProviderException. All other Exceptions are ignored.
*/
private Provider doLoadProvider() {
return AccessController.doPrivileged(new PrivilegedAction<Provider>() {
public Provider run() {
if (debug != null) {
debug.println("Loading provider: " + ProviderConfig.this);
}
try {
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class<?> provClass;
if (cl != null) {
provClass = cl.loadClass(className);
} else {
provClass = Class.forName(className);
}
Object obj;
if (hasArgument() == false) {
obj = provClass.newInstance();
} else {
Constructor<?> cons = provClass.getConstructor(CL_STRING);
obj = cons.newInstance(argument);
}
if (obj instanceof Provider) {
if (debug != null) {
debug.println("Loaded provider " + obj);
}
return (Provider)obj;
} else {
if (debug != null) {
debug.println(className + " is not a provider");
}
disableLoad();
return null;
}
} catch (Exception e) {
Throwable t;
if (e instanceof InvocationTargetException) {
t = ((InvocationTargetException)e).getCause();
} else {
t = e;
}
if (debug != null) {
debug.println("Error loading provider " + ProviderConfig.this);
t.printStackTrace();
}
// provider indicates fatal error, pass through exception
if (t instanceof ProviderException) {
throw (ProviderException)t;
}
// provider indicates that loading should not be retried
if (t instanceof UnsupportedOperationException) {
disableLoad();
}
return null;
} catch (ExceptionInInitializerError err) {
// unexpected exception thrown from static initialization block in provider
// (ex: insufficient permission to initialize provider class)
if (debug != null) {
debug.println("Error loading provider " + ProviderConfig.this);
err.printStackTrace();
}
disableLoad();
return null;
}
}
});
}
/**
* Perform property expansion of the provider value.
*
* NOTE use of doPrivileged().
*/
private static String expand(final String value) {
// shortcut if value does not contain any properties
if (value.contains("${") == false) {
return value;
}
return AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
try {
return PropertyExpander.expand(value);
} catch (GeneralSecurityException e) {
throw new ProviderException(e);
}
}
});
}
}

View File

@@ -0,0 +1,505 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jca;
import java.util.*;
import java.security.*;
import java.security.Provider.Service;
/**
* List of Providers. Used to represent the provider preferences.
*
* The system starts out with a ProviderList that only has the classNames
* of the Providers. Providers are loaded on demand only when needed.
*
* For compatibility reasons, Providers that could not be loaded are ignored
* and internally presented as the instance EMPTY_PROVIDER. However, those
* objects cannot be presented to applications. Call the convert() method
* to force all Providers to be loaded and to obtain a ProviderList with
* invalid entries removed. All this is handled by the Security class.
*
* Note that all indices used by this class are 0-based per general Java
* convention. These must be converted to the 1-based indices used by the
* Security class externally when needed.
*
* Instances of this class are immutable. This eliminates the need for
* cloning and synchronization in consumers. The add() and remove() style
* methods are static in order to avoid confusion about the immutability.
*
* @author Andreas Sterbenz
* @since 1.5
*/
public final class ProviderList {
final static sun.security.util.Debug debug =
sun.security.util.Debug.getInstance("jca", "ProviderList");
private final static ProviderConfig[] PC0 = new ProviderConfig[0];
private final static Provider[] P0 = new Provider[0];
// constant for an ProviderList with no elements
static final ProviderList EMPTY = new ProviderList(PC0, true);
// dummy provider object to use during initialization
// used to avoid explicit null checks in various places
private static final Provider EMPTY_PROVIDER =
new Provider("##Empty##", 1.0d, "initialization in progress") {
private static final long serialVersionUID = 1151354171352296389L;
// override getService() to return null slightly faster
public Service getService(String type, String algorithm) {
return null;
}
};
// construct a ProviderList from the security properties
// (static provider configuration in the java.security file)
static ProviderList fromSecurityProperties() {
// doPrivileged() because of Security.getProperty()
return AccessController.doPrivileged(
new PrivilegedAction<ProviderList>() {
public ProviderList run() {
return new ProviderList();
}
});
}
public static ProviderList add(ProviderList providerList, Provider p) {
return insertAt(providerList, p, -1);
}
public static ProviderList insertAt(ProviderList providerList, Provider p,
int position) {
if (providerList.getProvider(p.getName()) != null) {
return providerList;
}
List<ProviderConfig> list = new ArrayList<>
(Arrays.asList(providerList.configs));
int n = list.size();
if ((position < 0) || (position > n)) {
position = n;
}
list.add(position, new ProviderConfig(p));
return new ProviderList(list.toArray(PC0), true);
}
public static ProviderList remove(ProviderList providerList, String name) {
// make sure provider exists
if (providerList.getProvider(name) == null) {
return providerList;
}
// copy all except matching to new list
ProviderConfig[] configs = new ProviderConfig[providerList.size() - 1];
int j = 0;
for (ProviderConfig config : providerList.configs) {
if (config.getProvider().getName().equals(name) == false) {
configs[j++] = config;
}
}
return new ProviderList(configs, true);
}
// Create a new ProviderList from the specified Providers.
// This method is for use by SunJSSE.
public static ProviderList newList(Provider ... providers) {
ProviderConfig[] configs = new ProviderConfig[providers.length];
for (int i = 0; i < providers.length; i++) {
configs[i] = new ProviderConfig(providers[i]);
}
return new ProviderList(configs, true);
}
// configuration of the providers
private final ProviderConfig[] configs;
// flag indicating whether all configs have been loaded successfully
private volatile boolean allLoaded;
// List returned by providers()
private final List<Provider> userList = new AbstractList<Provider>() {
public int size() {
return configs.length;
}
public Provider get(int index) {
return getProvider(index);
}
};
/**
* Create a new ProviderList from an array of configs
*/
private ProviderList(ProviderConfig[] configs, boolean allLoaded) {
this.configs = configs;
this.allLoaded = allLoaded;
}
/**
* Return a new ProviderList parsed from the java.security Properties.
*/
private ProviderList() {
List<ProviderConfig> configList = new ArrayList<>();
for (int i = 1; true; i++) {
String entry = Security.getProperty("security.provider." + i);
if (entry == null) {
break;
}
entry = entry.trim();
if (entry.length() == 0) {
System.err.println("invalid entry for " +
"security.provider." + i);
break;
}
int k = entry.indexOf(' ');
ProviderConfig config;
if (k == -1) {
config = new ProviderConfig(entry);
} else {
String className = entry.substring(0, k);
String argument = entry.substring(k + 1).trim();
config = new ProviderConfig(className, argument);
}
// Get rid of duplicate providers.
if (configList.contains(config) == false) {
configList.add(config);
}
}
configs = configList.toArray(PC0);
if (debug != null) {
debug.println("provider configuration: " + configList);
}
}
/**
* Construct a special ProviderList for JAR verification. It consists
* of the providers specified via jarClassNames, which must be on the
* bootclasspath and cannot be in signed JAR files. This is to avoid
* possible recursion and deadlock during verification.
*/
ProviderList getJarList(String[] jarClassNames) {
List<ProviderConfig> newConfigs = new ArrayList<>();
for (String className : jarClassNames) {
ProviderConfig newConfig = new ProviderConfig(className);
for (ProviderConfig config : configs) {
// if the equivalent object is present in this provider list,
// use the old object rather than the new object.
// this ensures that when the provider is loaded in the
// new thread local list, it will also become available
// in this provider list
if (config.equals(newConfig)) {
newConfig = config;
break;
}
}
newConfigs.add(newConfig);
}
ProviderConfig[] configArray = newConfigs.toArray(PC0);
return new ProviderList(configArray, false);
}
public int size() {
return configs.length;
}
/**
* Return the Provider at the specified index. Returns EMPTY_PROVIDER
* if the provider could not be loaded at this time.
*/
Provider getProvider(int index) {
Provider p = configs[index].getProvider();
return (p != null) ? p : EMPTY_PROVIDER;
}
/**
* Return an unmodifiable List of all Providers in this List. The
* individual Providers are loaded on demand. Elements that could not
* be initialized are replaced with EMPTY_PROVIDER.
*/
public List<Provider> providers() {
return userList;
}
private ProviderConfig getProviderConfig(String name) {
int index = getIndex(name);
return (index != -1) ? configs[index] : null;
}
// return the Provider with the specified name or null
public Provider getProvider(String name) {
ProviderConfig config = getProviderConfig(name);
return (config == null) ? null : config.getProvider();
}
/**
* Return the index at which the provider with the specified name is
* installed or -1 if it is not present in this ProviderList.
*/
public int getIndex(String name) {
for (int i = 0; i < configs.length; i++) {
Provider p = getProvider(i);
if (p.getName().equals(name)) {
return i;
}
}
return -1;
}
// attempt to load all Providers not already loaded
private int loadAll() {
if (allLoaded) {
return configs.length;
}
if (debug != null) {
debug.println("Loading all providers");
new Exception("Debug Info. Call trace:").printStackTrace();
}
int n = 0;
for (int i = 0; i < configs.length; i++) {
Provider p = configs[i].getProvider();
if (p != null) {
n++;
}
}
if (n == configs.length) {
allLoaded = true;
}
return n;
}
/**
* Try to load all Providers and return the ProviderList. If one or
* more Providers could not be loaded, a new ProviderList with those
* entries removed is returned. Otherwise, the method returns this.
*/
ProviderList removeInvalid() {
int n = loadAll();
if (n == configs.length) {
return this;
}
ProviderConfig[] newConfigs = new ProviderConfig[n];
for (int i = 0, j = 0; i < configs.length; i++) {
ProviderConfig config = configs[i];
if (config.isLoaded()) {
newConfigs[j++] = config;
}
}
return new ProviderList(newConfigs, true);
}
// return the providers as an array
public Provider[] toArray() {
return providers().toArray(P0);
}
// return a String representation of this ProviderList
public String toString() {
return Arrays.asList(configs).toString();
}
/**
* Return a Service describing an implementation of the specified
* algorithm from the Provider with the highest precedence that
* supports that algorithm. Return null if no Provider supports this
* algorithm.
*/
public Service getService(String type, String name) {
for (int i = 0; i < configs.length; i++) {
Provider p = getProvider(i);
Service s = p.getService(type, name);
if (s != null) {
return s;
}
}
return null;
}
/**
* Return a List containing all the Services describing implementations
* of the specified algorithms in precedence order. If no implementation
* exists, this method returns an empty List.
*
* The elements of this list are determined lazily on demand.
*
* The List returned is NOT thread safe.
*/
public List<Service> getServices(String type, String algorithm) {
return new ServiceList(type, algorithm);
}
/**
* This method exists for compatibility with JCE only. It will be removed
* once JCE has been changed to use the replacement method.
* @deprecated use getServices(List<ServiceId>) instead
*/
@Deprecated
public List<Service> getServices(String type, List<String> algorithms) {
List<ServiceId> ids = new ArrayList<>();
for (String alg : algorithms) {
ids.add(new ServiceId(type, alg));
}
return getServices(ids);
}
public List<Service> getServices(List<ServiceId> ids) {
return new ServiceList(ids);
}
/**
* Inner class for a List of Services. Custom List implementation in
* order to delay Provider initialization and lookup.
* Not thread safe.
*/
private final class ServiceList extends AbstractList<Service> {
// type and algorithm for simple lookup
// avoid allocating/traversing the ServiceId list for these lookups
private final String type;
private final String algorithm;
// list of ids for parallel lookup
// if ids is non-null, type and algorithm are null
private final List<ServiceId> ids;
// first service we have found
// it is stored in a separate variable so that we can avoid
// allocating the services list if we do not need the second service.
// this is the case if we don't failover (failovers are typically rare)
private Service firstService;
// list of the services we have found so far
private List<Service> services;
// index into config[] of the next provider we need to query
private int providerIndex;
ServiceList(String type, String algorithm) {
this.type = type;
this.algorithm = algorithm;
this.ids = null;
}
ServiceList(List<ServiceId> ids) {
this.type = null;
this.algorithm = null;
this.ids = ids;
}
private void addService(Service s) {
if (firstService == null) {
firstService = s;
} else {
if (services == null) {
services = new ArrayList<Service>(4);
services.add(firstService);
}
services.add(s);
}
}
private Service tryGet(int index) {
while (true) {
if ((index == 0) && (firstService != null)) {
return firstService;
} else if ((services != null) && (services.size() > index)) {
return services.get(index);
}
if (providerIndex >= configs.length) {
return null;
}
// check all algorithms in this provider before moving on
Provider p = getProvider(providerIndex++);
if (type != null) {
// simple lookup
Service s = p.getService(type, algorithm);
if (s != null) {
addService(s);
}
} else {
// parallel lookup
for (ServiceId id : ids) {
Service s = p.getService(id.type, id.algorithm);
if (s != null) {
addService(s);
}
}
}
}
}
public Service get(int index) {
Service s = tryGet(index);
if (s == null) {
throw new IndexOutOfBoundsException();
}
return s;
}
public int size() {
int n;
if (services != null) {
n = services.size();
} else {
n = (firstService != null) ? 1 : 0;
}
while (tryGet(n) != null) {
n++;
}
return n;
}
// override isEmpty() and iterator() to not call size()
// this avoids loading + checking all Providers
public boolean isEmpty() {
return (tryGet(0) == null);
}
public Iterator<Service> iterator() {
return new Iterator<Service>() {
int index;
public boolean hasNext() {
return tryGet(index) != null;
}
public Service next() {
Service s = tryGet(index);
if (s == null) {
throw new NoSuchElementException();
}
index++;
return s;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
}

View File

@@ -0,0 +1,248 @@
/*
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jca;
import java.security.Provider;
/**
* Collection of methods to get and set provider list. Also includes
* special code for the provider list during JAR verification.
*
* @author Andreas Sterbenz
* @since 1.5
*/
public class Providers {
private static final ThreadLocal<ProviderList> threadLists =
new InheritableThreadLocal<>();
// number of threads currently using thread-local provider lists
// tracked to allow an optimization if == 0
private static volatile int threadListsUsed;
// current system-wide provider list
// Note volatile immutable object, so no synchronization needed.
private static volatile ProviderList providerList;
static {
// set providerList to empty list first in case initialization somehow
// triggers a getInstance() call (although that should not happen)
providerList = ProviderList.EMPTY;
providerList = ProviderList.fromSecurityProperties();
}
private Providers() {
// empty
}
// we need special handling to resolve circularities when loading
// signed JAR files during startup. The code below is part of that.
// Basically, before we load data from a signed JAR file, we parse
// the PKCS#7 file and verify the signature. We need a
// CertificateFactory, Signatures, etc. to do that. We have to make
// sure that we do not try to load the implementation from the JAR
// file we are just verifying.
//
// To avoid that, we use different provider settings during JAR
// verification. However, we do not want those provider settings to
// interfere with other parts of the system. Therefore, we make them local
// to the Thread executing the JAR verification code.
//
// The code here is used by sun.security.util.SignatureFileVerifier.
// See there for details.
private static final String BACKUP_PROVIDER_CLASSNAME =
"sun.security.provider.VerificationProvider";
// Hardcoded classnames of providers to use for JAR verification.
// MUST NOT be on the bootclasspath and not in signed JAR files.
private static final String[] jarVerificationProviders = {
"sun.security.provider.Sun",
"sun.security.rsa.SunRsaSign",
// Note: SunEC *is* in a signed JAR file, but it's not signed
// by EC itself. So it's still safe to be listed here.
"sun.security.ec.SunEC",
"com.sun.crypto.provider.SunJCE",
BACKUP_PROVIDER_CLASSNAME,
};
// Return to Sun provider or its backup.
// This method should only be called by
// sun.security.util.ManifestEntryVerifier and java.security.SecureRandom.
public static Provider getSunProvider() {
try {
Class<?> clazz = Class.forName(jarVerificationProviders[0]);
return (Provider)clazz.newInstance();
} catch (Exception e) {
try {
Class<?> clazz = Class.forName(BACKUP_PROVIDER_CLASSNAME);
return (Provider)clazz.newInstance();
} catch (Exception ee) {
throw new RuntimeException("Sun provider not found", e);
}
}
}
/**
* Start JAR verification. This sets a special provider list for
* the current thread. You MUST save the return value from this
* method and you MUST call stopJarVerification() with that object
* once you are done.
*/
public static Object startJarVerification() {
ProviderList currentList = getProviderList();
ProviderList jarList = currentList.getJarList(jarVerificationProviders);
// return the old thread-local provider list, usually null
return beginThreadProviderList(jarList);
}
/**
* Stop JAR verification. Call once you have completed JAR verification.
*/
public static void stopJarVerification(Object obj) {
// restore old thread-local provider list
endThreadProviderList((ProviderList)obj);
}
/**
* Return the current ProviderList. If the thread-local list is set,
* it is returned. Otherwise, the system wide list is returned.
*/
public static ProviderList getProviderList() {
ProviderList list = getThreadProviderList();
if (list == null) {
list = getSystemProviderList();
}
return list;
}
/**
* Set the current ProviderList. Affects the thread-local list if set,
* otherwise the system wide list.
*/
public static void setProviderList(ProviderList newList) {
if (getThreadProviderList() == null) {
setSystemProviderList(newList);
} else {
changeThreadProviderList(newList);
}
}
/**
* Get the full provider list with invalid providers (those that
* could not be loaded) removed. This is the list we need to
* present to applications.
*/
public static ProviderList getFullProviderList() {
ProviderList list;
synchronized (Providers.class) {
list = getThreadProviderList();
if (list != null) {
ProviderList newList = list.removeInvalid();
if (newList != list) {
changeThreadProviderList(newList);
list = newList;
}
return list;
}
}
list = getSystemProviderList();
ProviderList newList = list.removeInvalid();
if (newList != list) {
setSystemProviderList(newList);
list = newList;
}
return list;
}
private static ProviderList getSystemProviderList() {
return providerList;
}
private static void setSystemProviderList(ProviderList list) {
providerList = list;
}
public static ProviderList getThreadProviderList() {
// avoid accessing the threadlocal if none are currently in use
// (first use of ThreadLocal.get() for a Thread allocates a Map)
if (threadListsUsed == 0) {
return null;
}
return threadLists.get();
}
// Change the thread local provider list. Use only if the current thread
// is already using a thread local list and you want to change it in place.
// In other cases, use the begin/endThreadProviderList() methods.
private static void changeThreadProviderList(ProviderList list) {
threadLists.set(list);
}
/**
* Methods to manipulate the thread local provider list. It is for use by
* JAR verification (see above) and the SunJSSE FIPS mode only.
*
* It should be used as follows:
*
* ProviderList list = ...;
* ProviderList oldList = Providers.beginThreadProviderList(list);
* try {
* // code that needs thread local provider list
* } finally {
* Providers.endThreadProviderList(oldList);
* }
*
*/
public static synchronized ProviderList beginThreadProviderList(ProviderList list) {
if (ProviderList.debug != null) {
ProviderList.debug.println("ThreadLocal providers: " + list);
}
ProviderList oldList = threadLists.get();
threadListsUsed++;
threadLists.set(list);
return oldList;
}
public static synchronized void endThreadProviderList(ProviderList list) {
if (list == null) {
if (ProviderList.debug != null) {
ProviderList.debug.println("Disabling ThreadLocal providers");
}
threadLists.remove();
} else {
if (ProviderList.debug != null) {
ProviderList.debug.println
("Restoring previous ThreadLocal providers: " + list);
}
threadLists.set(list);
}
threadListsUsed--;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jca;
/**
* Simple class encapsulating a service type and algorithm for lookup.
* Put in a separate file rather than nested to allow import via ...jca.*.
*
* @author Andreas Sterbenz
* @since 1.5
*/
public final class ServiceId {
public final String type;
public final String algorithm;
public ServiceId(String type, String algorithm) {
this.type = type;
this.algorithm = algorithm;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
/**
* Denotes what client is calling the JGSS-API. The object can be sent deep
* into the mechanism level so that special actions can be performed for
* different callers.
*/
public class GSSCaller {
public static final GSSCaller CALLER_UNKNOWN = new GSSCaller("UNKNOWN");
public static final GSSCaller CALLER_INITIATE = new GSSCaller("INITIATE");
public static final GSSCaller CALLER_ACCEPT = new GSSCaller("ACCEPT");
public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller("SSL_CLIENT");
public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller("SSL_SERVER");
private String name;
GSSCaller(String s) {
name = s;
}
@Override
public String toString() {
return "GSSCaller{" + name + '}';
}
}

View File

@@ -0,0 +1,661 @@
/*
* Copyright (c) 2000, 2009, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
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
* operations. JGSS security contexts are established between
* peers using locally established credentials. Multiple contexts
* may exist simultaneously between a pair of peers, using the same
* or different set of credentials. The JGSS is independent of
* the underlying transport protocols and depends on its callers to
* transport the tokens between peers.
* <p>
* The context object can be thought of as having 3 implicit states:
* before it is established, during its context establishment, and
* after a fully established context exists.
* <p>
* Before the context establishment phase is initiated, the context
* initiator may request specific characteristics desired of the
* established context. These can be set using the set methods. After the
* context is established, the caller can check the actual characteristic
* and services offered by the context using the query methods.
* <p>
* The context establishment phase begins with the first call to the
* initSecContext method by the context initiator. During this phase the
* initSecContext and acceptSecContext methods will produce GSS-API
* authentication tokens which the calling application needs to send to its
* peer. The initSecContext and acceptSecContext methods may
* return a CONTINUE_NEEDED code which indicates that a token is needed
* from its peer in order to continue the context establishment phase. A
* return code of COMPLETE signals that the local end of the context is
* established. This may still require that a token be sent to the peer,
* depending if one is produced by GSS-API. The isEstablished method can
* also be used to determine if the local end of the context has been
* fully established. During the context establishment phase, the
* isProtReady method may be called to determine if the context can be
* used for the per-message operations. This allows implementation to
* use per-message operations on contexts which aren't fully established.
* <p>
* After the context has been established or the isProtReady method
* returns "true", the query routines can be invoked to determine the actual
* characteristics and services of the established context. The
* application can also start using the per-message methods of wrap and
* getMIC to obtain cryptographic operations on application supplied data.
* <p>
* When the context is no longer needed, the application should call
* dispose to release any system resources the context may be using.
* <DL><DT><B>RFC 2078</b>
* <DD>This class corresponds to the context level calls together with
* the per message calls of RFC 2078. The gss_init_sec_context and
* gss_accept_sec_context calls have been made simpler by only taking
* required parameters. The context can have its properties set before
* the first call to initSecContext. The supplementary status codes for the
* 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 ExtendedGSSContext {
private final GSSManagerImpl gssManager;
private final boolean initiator;
// private flags for the context state
private static final int PRE_INIT = 1;
private static final int IN_PROGRESS = 2;
private static final int READY = 3;
private static final int DELETED = 4;
// instance variables
private int currentState = PRE_INIT;
private GSSContextSpi mechCtxt = null;
private Oid mechOid = null;
private ObjectIdentifier objId = null;
private GSSCredentialImpl myCred = null;
private GSSNameImpl srcName = null;
private GSSNameImpl targName = null;
private int reqLifetime = INDEFINITE_LIFETIME;
private ChannelBinding channelBindings = null;
private boolean reqConfState = true;
private boolean reqIntegState = true;
private boolean reqMutualAuthState = true;
private boolean reqReplayDetState = true;
private boolean reqSequenceDetState = true;
private boolean reqCredDelegState = false;
private boolean reqAnonState = false;
private boolean reqDelegPolicyState = false;
/**
* Creates a GSSContextImp on the context initiator's side.
*/
public GSSContextImpl(GSSManagerImpl gssManager, GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException {
if ((peer == null) || !(peer instanceof GSSNameImpl)) {
throw new GSSException(GSSException.BAD_NAME);
}
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
this.gssManager = gssManager;
this.myCred = (GSSCredentialImpl) myCred; // XXX Check first
reqLifetime = lifetime;
targName = (GSSNameImpl)peer;
this.mechOid = mech;
initiator = true;
}
/**
* Creates a GSSContextImpl on the context acceptor's side.
*/
public GSSContextImpl(GSSManagerImpl gssManager, GSSCredential myCred)
throws GSSException {
this.gssManager = gssManager;
this.myCred = (GSSCredentialImpl) myCred; // XXX Check first
initiator = false;
}
/**
* Creates a GSSContextImpl out of a previously exported
* GSSContext.
*
* @see #isTransferable
*/
public GSSContextImpl(GSSManagerImpl gssManager, byte[] interProcessToken)
throws GSSException {
this.gssManager = gssManager;
mechCtxt = gssManager.getMechanismContext(interProcessToken);
initiator = mechCtxt.isInitiator();
this.mechOid = mechCtxt.getMech();
}
public byte[] initSecContext(byte inputBuf[], int offset, int len)
throws GSSException {
/*
* Size of ByteArrayOutputStream will double each time that extra
* bytes are to be written. Usually, without delegation, a GSS
* initial token containing the Kerberos AP-REQ is between 400 and
* 600 bytes.
*/
ByteArrayOutputStream bos = new ByteArrayOutputStream(600);
ByteArrayInputStream bin =
new ByteArrayInputStream(inputBuf, offset, len);
int size = initSecContext(bin, bos);
return (size == 0? null : bos.toByteArray());
}
public int initSecContext(InputStream inStream,
OutputStream outStream) throws GSSException {
if (mechCtxt != null && currentState != IN_PROGRESS) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Illegal call to initSecContext");
}
GSSHeader gssHeader = null;
int inTokenLen = -1;
GSSCredentialSpi credElement = null;
boolean firstToken = false;
try {
if (mechCtxt == null) {
if (myCred != null) {
try {
credElement = myCred.getElement(mechOid, true);
} catch (GSSException ge) {
if (GSSUtil.isSpNegoMech(mechOid) &&
ge.getMajor() == GSSException.NO_CRED) {
credElement = myCred.getElement
(myCred.getMechs()[0], true);
} else {
throw ge;
}
}
}
GSSNameSpi nameElement = targName.getElement(mechOid);
mechCtxt = gssManager.getMechanismContext(nameElement,
credElement,
reqLifetime,
mechOid);
mechCtxt.requestConf(reqConfState);
mechCtxt.requestInteg(reqIntegState);
mechCtxt.requestCredDeleg(reqCredDelegState);
mechCtxt.requestMutualAuth(reqMutualAuthState);
mechCtxt.requestReplayDet(reqReplayDetState);
mechCtxt.requestSequenceDet(reqSequenceDetState);
mechCtxt.requestAnonymity(reqAnonState);
mechCtxt.setChannelBinding(channelBindings);
mechCtxt.requestDelegPolicy(reqDelegPolicyState);
objId = new ObjectIdentifier(mechOid.toString());
currentState = IN_PROGRESS;
firstToken = true;
} else {
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
GSSUtil.isSpNegoMech(mechOid)) {
// do not parse GSS header for native provider or SPNEGO
// mech
} else {
// parse GSS header
gssHeader = new GSSHeader(inStream);
if (!gssHeader.getOid().equals((Object) objId))
throw new GSSExceptionImpl
(GSSException.DEFECTIVE_TOKEN,
"Mechanism not equal to " +
mechOid.toString() +
" in initSecContext token");
inTokenLen = gssHeader.getMechTokenLength();
}
}
byte[] obuf = mechCtxt.initSecContext(inStream, inTokenLen);
int retVal = 0;
if (obuf != null) {
retVal = obuf.length;
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(!firstToken && GSSUtil.isSpNegoMech(mechOid))) {
// do not add GSS header for native provider or SPNEGO
// except for the first SPNEGO token
} else {
// add GSS header
gssHeader = new GSSHeader(objId, obuf.length);
retVal += gssHeader.encode(outStream);
}
outStream.write(obuf);
}
if (mechCtxt.isEstablished())
currentState = READY;
return retVal;
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN,
e.getMessage());
}
}
public byte[] acceptSecContext(byte inTok[], int offset, int len)
throws GSSException {
/*
* Usually initial GSS token containing a Kerberos AP-REP is less
* than 100 bytes.
*/
ByteArrayOutputStream bos = new ByteArrayOutputStream(100);
acceptSecContext(new ByteArrayInputStream(inTok, offset, len),
bos);
byte[] out = bos.toByteArray();
return (out.length == 0) ? null : out;
}
public void acceptSecContext(InputStream inStream,
OutputStream outStream) throws GSSException {
if (mechCtxt != null && currentState != IN_PROGRESS) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Illegal call to acceptSecContext");
}
GSSHeader gssHeader = null;
int inTokenLen = -1;
GSSCredentialSpi credElement = null;
try {
if (mechCtxt == null) {
// mechOid will be null for an acceptor's context
gssHeader = new GSSHeader(inStream);
inTokenLen = gssHeader.getMechTokenLength();
/*
* Convert ObjectIdentifier to Oid
*/
objId = gssHeader.getOid();
mechOid = new Oid(objId.toString());
// System.out.println("Entered GSSContextImpl.acceptSecContext"
// + " with mechanism = " + mechOid);
if (myCred != null) {
credElement = myCred.getElement(mechOid, false);
}
mechCtxt = gssManager.getMechanismContext(credElement,
mechOid);
mechCtxt.setChannelBinding(channelBindings);
currentState = IN_PROGRESS;
} else {
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(GSSUtil.isSpNegoMech(mechOid))) {
// do not parse GSS header for native provider and SPNEGO
} else {
// parse GSS Header
gssHeader = new GSSHeader(inStream);
if (!gssHeader.getOid().equals((Object) objId))
throw new GSSExceptionImpl
(GSSException.DEFECTIVE_TOKEN,
"Mechanism not equal to " +
mechOid.toString() +
" in acceptSecContext token");
inTokenLen = gssHeader.getMechTokenLength();
}
}
byte[] obuf = mechCtxt.acceptSecContext(inStream, inTokenLen);
if (obuf != null) {
int retVal = obuf.length;
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(GSSUtil.isSpNegoMech(mechOid))) {
// do not add GSS header for native provider and SPNEGO
} else {
// add GSS header
gssHeader = new GSSHeader(objId, obuf.length);
retVal += gssHeader.encode(outStream);
}
outStream.write(obuf);
}
if (mechCtxt.isEstablished()) {
currentState = READY;
}
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN,
e.getMessage());
}
}
public boolean isEstablished() {
if (mechCtxt == null)
return false;
else
return (currentState == READY);
}
public int getWrapSizeLimit(int qop, boolean confReq,
int maxTokenSize) throws GSSException {
if (mechCtxt != null)
return mechCtxt.getWrapSizeLimit(qop, confReq, maxTokenSize);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] wrap(byte inBuf[], int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.wrap(inBuf, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void wrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.wrap(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte [] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.unwrap(inBuf, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void unwrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.unwrap(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] getMIC(byte []inMsg, int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.getMIC(inMsg, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void getMIC(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.getMIC(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
byte[] inMsg, int msgOffset, int msgLen,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.verifyMIC(inTok, tokOffset, tokLen,
inMsg, msgOffset, msgLen, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void verifyMIC(InputStream tokStream, InputStream msgStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.verifyMIC(tokStream, msgStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] export() throws GSSException {
// Defaults to null to match old behavior
byte[] result = null;
// Only allow context export from native provider since JGSS
// still has not defined its own interprocess token format
if (mechCtxt.isTransferable() &&
mechCtxt.getProvider().getName().equals("SunNativeGSS")) {
result = mechCtxt.export();
}
return result;
}
public void requestMutualAuth(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqMutualAuthState = state;
}
public void requestReplayDet(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqReplayDetState = state;
}
public void requestSequenceDet(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqSequenceDetState = state;
}
public void requestCredDeleg(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqCredDelegState = state;
}
public void requestAnonymity(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqAnonState = state;
}
public void requestConf(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqConfState = state;
}
public void requestInteg(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqIntegState = state;
}
public void requestLifetime(int lifetime) throws GSSException {
if (mechCtxt == null && initiator)
reqLifetime = lifetime;
}
public void setChannelBinding(ChannelBinding channelBindings)
throws GSSException {
if (mechCtxt == null)
this.channelBindings = channelBindings;
}
public boolean getCredDelegState() {
if (mechCtxt != null)
return mechCtxt.getCredDelegState();
else
return reqCredDelegState;
}
public boolean getMutualAuthState() {
if (mechCtxt != null)
return mechCtxt.getMutualAuthState();
else
return reqMutualAuthState;
}
public boolean getReplayDetState() {
if (mechCtxt != null)
return mechCtxt.getReplayDetState();
else
return reqReplayDetState;
}
public boolean getSequenceDetState() {
if (mechCtxt != null)
return mechCtxt.getSequenceDetState();
else
return reqSequenceDetState;
}
public boolean getAnonymityState() {
if (mechCtxt != null)
return mechCtxt.getAnonymityState();
else
return reqAnonState;
}
public boolean isTransferable() throws GSSException {
if (mechCtxt != null)
return mechCtxt.isTransferable();
else
return false;
}
public boolean isProtReady() {
if (mechCtxt != null)
return mechCtxt.isProtReady();
else
return false;
}
public boolean getConfState() {
if (mechCtxt != null)
return mechCtxt.getConfState();
else
return reqConfState;
}
public boolean getIntegState() {
if (mechCtxt != null)
return mechCtxt.getIntegState();
else
return reqIntegState;
}
public int getLifetime() {
if (mechCtxt != null)
return mechCtxt.getLifetime();
else
return reqLifetime;
}
public GSSName getSrcName() throws GSSException {
if (srcName == null) {
srcName = GSSNameImpl.wrapElement
(gssManager, mechCtxt.getSrcName());
}
return srcName;
}
public GSSName getTargName() throws GSSException {
if (targName == null) {
targName = GSSNameImpl.wrapElement
(gssManager, mechCtxt.getTargName());
}
return targName;
}
public Oid getMech() throws GSSException {
if (mechCtxt != null) {
return mechCtxt.getMech();
}
return mechOid;
}
public GSSCredential getDelegCred() throws GSSException {
if (mechCtxt == null)
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
GSSCredentialSpi delCredElement = mechCtxt.getDelegCred();
return (delCredElement == null ?
null : new GSSCredentialImpl(gssManager, delCredElement));
}
public boolean isInitiator() throws GSSException {
return initiator;
}
public void dispose() throws GSSException {
currentState = DELETED;
if (mechCtxt != null) {
mechCtxt.dispose();
mechCtxt = null;
}
myCred = null;
srcName = null;
targName = null;
}
// ExtendedGSSContext methods:
@Override
public Object inquireSecContext(InquireType type) throws GSSException {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(new InquireSecContextPermission(type.toString()));
}
if (mechCtxt == null) {
throw new GSSException(GSSException.NO_CONTEXT);
}
return mechCtxt.inquireSecContext(type);
}
@Override
public void requestDelegPolicy(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqDelegPolicyState = state;
}
@Override
public boolean getDelegPolicyState() {
if (mechCtxt != null)
return mechCtxt.getDelegPolicyState();
else
return reqDelegPolicyState;
}
}

View File

@@ -0,0 +1,690 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.*;
import com.sun.security.jgss.*;
import sun.security.jgss.spnego.SpNegoCredElement;
public class GSSCredentialImpl implements ExtendedGSSCredential {
private GSSManagerImpl gssManager = null;
private boolean destroyed = false;
/*
* We store all elements in a hashtable, using <oid, usage> as the
* key. This makes it easy to locate the specific kind of credential we
* need. The implementation needs to be optimized for the case where
* there is just one element (tempCred).
*/
private Hashtable<SearchKey, GSSCredentialSpi> hashtable = null;
// XXX Optimization for single mech usage
private GSSCredentialSpi tempCred = null;
GSSCredentialImpl(GSSManagerImpl gssManager, int usage)
throws GSSException {
this(gssManager, null, GSSCredential.DEFAULT_LIFETIME,
(Oid[]) null, usage);
}
GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
int lifetime, Oid mech, int usage)
throws GSSException {
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
init(gssManager);
add(name, lifetime, lifetime, mech, usage);
}
GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
int lifetime, Oid mechs[], int usage)
throws GSSException {
init(gssManager);
boolean defaultList = false;
if (mechs == null) {
mechs = gssManager.getMechs();
defaultList = true;
}
for (int i = 0; i < mechs.length; i++) {
try {
add(name, lifetime, lifetime, mechs[i], usage);
} catch (GSSException e) {
if (defaultList) {
// Try the next mechanism
GSSUtil.debug("Ignore " + e + " while acquring cred for "
+ mechs[i]);
//e.printStackTrace();
} else throw e; // else try the next mechanism
}
}
if ((hashtable.size() == 0) || (usage != getUsage()))
throw new GSSException(GSSException.NO_CRED);
}
// Wrap a mech cred into a GSS cred
public GSSCredentialImpl(GSSManagerImpl gssManager,
GSSCredentialSpi mechElement) throws GSSException {
init(gssManager);
int usage = GSSCredential.ACCEPT_ONLY;
if (mechElement.isInitiatorCredential()) {
if (mechElement.isAcceptorCredential()) {
usage = GSSCredential.INITIATE_AND_ACCEPT;
} else {
usage = GSSCredential.INITIATE_ONLY;
}
}
SearchKey key = new SearchKey(mechElement.getMechanism(),
usage);
tempCred = mechElement;
hashtable.put(key, tempCred);
// More mechs that can use this cred, say, SPNEGO
if (!GSSUtil.isSpNegoMech(mechElement.getMechanism())) {
key = new SearchKey(GSSUtil.GSS_SPNEGO_MECH_OID, usage);
hashtable.put(key, new SpNegoCredElement(mechElement));
}
}
void init(GSSManagerImpl gssManager) {
this.gssManager = gssManager;
hashtable = new Hashtable<SearchKey, GSSCredentialSpi>(
gssManager.getMechs().length);
}
public void dispose() throws GSSException {
if (!destroyed) {
GSSCredentialSpi element;
Enumeration<GSSCredentialSpi> values = hashtable.elements();
while (values.hasMoreElements()) {
element = values.nextElement();
element.dispose();
}
destroyed = true;
}
}
public GSSCredential impersonate(GSSName name) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
Oid mech = tempCred.getMechanism();
GSSNameSpi nameElement = (name == null ? null :
((GSSNameImpl)name).getElement(mech));
GSSCredentialSpi cred = tempCred.impersonate(nameElement);
return (cred == null ?
null : new GSSCredentialImpl(gssManager, cred));
}
public GSSName getName() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
return GSSNameImpl.wrapElement(gssManager, tempCred.getName());
}
public GSSName getName(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey key = null;
GSSCredentialSpi element = null;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
}
if (element == null) {
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
}
if (element == null) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return GSSNameImpl.wrapElement(gssManager, element.getName());
}
/**
* Returns the remaining lifetime of this credential. The remaining
* lifetime is defined as the minimum lifetime, either for initiate or
* for accept, across all elements contained in it. Not terribly
* useful, but required by GSS-API.
*/
public int getRemainingLifetime() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey tempKey;
GSSCredentialSpi tempCred;
int tempLife = 0, tempInitLife = 0, tempAcceptLife = 0;
int min = INDEFINITE_LIFETIME;
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
tempKey = e.nextElement();
tempCred = hashtable.get(tempKey);
if (tempKey.getUsage() == INITIATE_ONLY)
tempLife = tempCred.getInitLifetime();
else if (tempKey.getUsage() == ACCEPT_ONLY)
tempLife = tempCred.getAcceptLifetime();
else {
tempInitLife = tempCred.getInitLifetime();
tempAcceptLife = tempCred.getAcceptLifetime();
tempLife = (tempInitLife < tempAcceptLife ?
tempInitLife:
tempAcceptLife);
}
if (min > tempLife)
min = tempLife;
}
return min;
}
public int getRemainingInitLifetime(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean found = false;
int max = 0;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getInitLifetime())
max = element.getInitLifetime();
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getInitLifetime())
max = element.getInitLifetime();
}
if (!found) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return max;
}
public int getRemainingAcceptLifetime(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean found = false;
int max = 0;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getAcceptLifetime())
max = element.getAcceptLifetime();
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getAcceptLifetime())
max = element.getAcceptLifetime();
}
if (!found) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return max;
}
/**
* Returns the usage mode for this credential. Returns
* INITIATE_AND_ACCEPT if any one element contained in it supports
* INITIATE_AND_ACCEPT or if two different elements exist where one
* support INITIATE_ONLY and the other supports ACCEPT_ONLY.
*/
public int getUsage() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey tempKey;
boolean initiate = false;
boolean accept = false;
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
tempKey = e.nextElement();
if (tempKey.getUsage() == INITIATE_ONLY)
initiate = true;
else if (tempKey.getUsage() == ACCEPT_ONLY)
accept = true;
else
return INITIATE_AND_ACCEPT;
}
if (initiate) {
if (accept)
return INITIATE_AND_ACCEPT;
else
return INITIATE_ONLY;
} else
return ACCEPT_ONLY;
}
public int getUsage(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean initiate = false;
boolean accept = false;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element != null) {
initiate = true;
}
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
if (element != null) {
accept = true;
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
initiate = true;
accept = true;
}
if (initiate && accept)
return GSSCredential.INITIATE_AND_ACCEPT;
else if (initiate)
return GSSCredential.INITIATE_ONLY;
else if (accept)
return GSSCredential.ACCEPT_ONLY;
else {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
}
public Oid[] getMechs() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
Vector<Oid> result = new Vector<Oid>(hashtable.size());
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
SearchKey tempKey = e.nextElement();
result.addElement(tempKey.getMech());
}
return result.toArray(new Oid[0]);
}
public void add(GSSName name, int initLifetime, int acceptLifetime,
Oid mech, int usage) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
SearchKey key = new SearchKey(mech, usage);
if (hashtable.containsKey(key)) {
throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT,
"Duplicate element found: " +
getElementStr(mech, usage));
}
// XXX If not instance of GSSNameImpl then throw exception
// Application mixing GSS implementations
GSSNameSpi nameElement = (name == null ? null :
((GSSNameImpl)name).getElement(mech));
tempCred = gssManager.getCredentialElement(nameElement,
initLifetime,
acceptLifetime,
mech,
usage);
/*
* Not all mechanisms support the concept of one credential element
* that can be used for both initiating and accepting a context. In
* the event that an application requests usage INITIATE_AND_ACCEPT
* for a credential from such a mechanism, the GSS framework will
* need to obtain two different credential elements from the
* mechanism, one that will have usage INITIATE_ONLY and another
* that will have usage ACCEPT_ONLY. The mechanism will help the
* GSS-API realize this by returning a credential element with
* usage INITIATE_ONLY or ACCEPT_ONLY prompting it to make another
* call to getCredentialElement, this time with the other usage
* mode.
*/
if (tempCred != null) {
if (usage == GSSCredential.INITIATE_AND_ACCEPT &&
(!tempCred.isAcceptorCredential() ||
!tempCred.isInitiatorCredential())) {
int currentUsage;
int desiredUsage;
if (!tempCred.isInitiatorCredential()) {
currentUsage = GSSCredential.ACCEPT_ONLY;
desiredUsage = GSSCredential.INITIATE_ONLY;
} else {
currentUsage = GSSCredential.INITIATE_ONLY;
desiredUsage = GSSCredential.ACCEPT_ONLY;
}
key = new SearchKey(mech, currentUsage);
hashtable.put(key, tempCred);
tempCred = gssManager.getCredentialElement(nameElement,
initLifetime,
acceptLifetime,
mech,
desiredUsage);
key = new SearchKey(mech, desiredUsage);
hashtable.put(key, tempCred);
} else {
hashtable.put(key, tempCred);
}
}
}
public boolean equals(Object another) {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
if (this == another) {
return true;
}
if (!(another instanceof GSSCredentialImpl)) {
return false;
}
// NOTE: The specification does not define the criteria to compare
// credentials.
/*
* XXX
* The RFC says: "Tests if this GSSCredential refers to the same
* entity as the supplied object. The two credentials must be
* acquired over the same mechanisms and must refer to the same
* principal. Returns "true" if the two GSSCredentials refer to
* the same entity; "false" otherwise."
*
* Well, when do two credentials refer to the same principal? Do
* they need to have one GSSName in common for the different
* GSSName's that the credential elements return? Or do all
* GSSName's have to be in common when the names are exported with
* their respective mechanisms for the credential elements?
*/
return false;
}
/**
* Returns a hashcode value for this GSSCredential.
*
* @return a hashCode value
*/
public int hashCode() {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
// NOTE: The specification does not define the criteria to compare
// credentials.
/*
* XXX
* Decide on a criteria for equals first then do this.
*/
return 1;
}
/**
* Returns the specified mechanism's credential-element.
*
* @param mechOid the oid for mechanism to retrieve
* @param initiate boolean indicating if the function is
* to throw exception or return null when element is not
* found.
* @return mechanism credential object
* @exception GSSException of invalid mechanism
*/
public GSSCredentialSpi getElement(Oid mechOid, boolean initiate)
throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey key;
GSSCredentialSpi element;
if (mechOid == null) {
/*
* First see if the default mechanism satisfies the
* desired usage.
*/
mechOid = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mechOid,
initiate? INITIATE_ONLY : ACCEPT_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element == null) {
/*
* Now just return any element that satisfies the
* desired usage.
*/
Object[] elements = hashtable.entrySet().toArray();
for (int i = 0; i < elements.length; i++) {
element = (GSSCredentialSpi)
((Map.Entry)elements[i]).getValue();
if (element.isInitiatorCredential() == initiate)
break;
} // for loop
}
}
} else {
if (initiate)
key = new SearchKey(mechOid, INITIATE_ONLY);
else
key = new SearchKey(mechOid, ACCEPT_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
element = hashtable.get(key);
}
}
if (element == null)
throw new GSSExceptionImpl(GSSException.NO_CRED,
"No credential found for: " +
getElementStr(mechOid,
initiate? INITIATE_ONLY : ACCEPT_ONLY));
return element;
}
Set<GSSCredentialSpi> getElements() {
HashSet<GSSCredentialSpi> retVal =
new HashSet<GSSCredentialSpi>(hashtable.size());
Enumeration<GSSCredentialSpi> values = hashtable.elements();
while (values.hasMoreElements()) {
GSSCredentialSpi o = values.nextElement();
retVal.add(o);
}
return retVal;
}
private static String getElementStr(Oid mechOid, int usage) {
String displayString = mechOid.toString();
if (usage == GSSCredential.INITIATE_ONLY) {
displayString =
displayString.concat(" usage: Initiate");
} else if (usage == GSSCredential.ACCEPT_ONLY) {
displayString =
displayString.concat(" usage: Accept");
} else {
displayString =
displayString.concat(" usage: Initiate and Accept");
}
return displayString;
}
public String toString() {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
StringBuffer buffer = new StringBuffer("[GSSCredential: ");
Object[] elements = hashtable.entrySet().toArray();
for (int i = 0; i < elements.length; i++) {
try {
buffer.append('\n');
element = (GSSCredentialSpi)
((Map.Entry)elements[i]).getValue();
buffer.append(element.getName());
buffer.append(' ');
buffer.append(element.getMechanism());
buffer.append(element.isInitiatorCredential() ?
" Initiate" : "");
buffer.append(element.isAcceptorCredential() ?
" Accept" : "");
buffer.append(" [");
buffer.append(element.getClass());
buffer.append(']');
} catch (GSSException e) {
// skip to next element
}
}
buffer.append(']');
return buffer.toString();
}
static class SearchKey {
private Oid mechOid = null;
private int usage = GSSCredential.INITIATE_AND_ACCEPT;
public SearchKey(Oid mechOid, int usage) {
this.mechOid = mechOid;
this.usage = usage;
}
public Oid getMech() {
return mechOid;
}
public int getUsage() {
return usage;
}
public boolean equals(Object other) {
if (! (other instanceof SearchKey))
return false;
SearchKey that = (SearchKey) other;
return ((this.mechOid.equals(that.mechOid)) &&
(this.usage == that.usage));
}
public int hashCode() {
return mechOid.hashCode();
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
/**
* This class helps overcome a limitation of the org.ietf.jgss.GSSException
* class that does not allow the thrower to set a string corresponding to
* the major code.
*/
public class GSSExceptionImpl extends GSSException {
private static final long serialVersionUID = 4251197939069005575L;
private String majorMessage;
/**
* A constructor that takes the majorCode as well as the mech oid that
* will be appended to the standard message defined in its super class.
*/
GSSExceptionImpl(int majorCode, Oid mech) {
super(majorCode);
this.majorMessage = super.getMajorString() + ": " + mech;
}
/**
* A constructor that takes the majorCode as well as the message that
* corresponds to it.
*/
public GSSExceptionImpl(int majorCode, String majorMessage) {
super(majorCode);
this.majorMessage = majorMessage;
}
/**
* A constructor that takes the majorCode and the exception cause.
*/
public GSSExceptionImpl(int majorCode, Exception cause) {
super(majorCode);
initCause(cause);
}
/**
* A constructor that takes the majorCode, the message that
* corresponds to it, and the exception cause.
*/
public GSSExceptionImpl(int majorCode, String majorMessage,
Exception cause) {
this(majorCode, majorMessage);
initCause(cause);
}
/**
* Returns the message that was embedded in this object, otherwise it
* returns the default message that an org.ietf.jgss.GSSException
* generates.
*/
public String getMessage() {
if (majorMessage != null)
return majorMessage;
else
return super.getMessage();
}
}

View File

@@ -0,0 +1,345 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.GSSException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import sun.security.util.*;
/**
* This class represents the mechanism independent part of a GSS-API
* context establishment token. Some mechanisms may choose to encode
* all subsequent tokens as well such that they start with an encoding
* of an instance of this class. e.g., The Kerberos v5 GSS-API Mechanism
* uses this header for all GSS-API tokens.
* <p>
* The format is specified in RFC 2743 section 3.1.
*
* @author Mayank Upadhyay
*/
/*
* The RFC states that implementations should explicitly follow the
* encoding scheme descibed in this section rather than use ASN.1
* compilers. However, we should consider removing duplicate ASN.1
* like code from here and depend on sun.security.util if possible.
*/
public class GSSHeader {
private ObjectIdentifier mechOid = null;
private byte[] mechOidBytes = null;
private int mechTokenLength = 0;
/**
* The tag defined in the GSS-API mechanism independent token
* format.
*/
public static final int TOKEN_ID=0x60;
/**
* Creates a GSSHeader instance whose encoding can be used as the
* prefix for a particular mechanism token.
* @param mechOid the Oid of the mechanism which generated the token
* @param mechTokenLength the length of the subsequent portion that
* the mechanism will be adding.
*/
public GSSHeader(ObjectIdentifier mechOid, int mechTokenLength)
throws IOException {
this.mechOid = mechOid;
DerOutputStream temp = new DerOutputStream();
temp.putOID(mechOid);
mechOidBytes = temp.toByteArray();
this.mechTokenLength = mechTokenLength;
}
/**
* Reads in a GSSHeader from an InputStream. Typically this would be
* used as part of reading the complete token from an InputStream
* that is obtained from a socket.
*/
public GSSHeader(InputStream is)
throws IOException, GSSException {
// debug("Parsing GSS token: ");
int tag = is.read();
// debug("tag=" + tag);
if (tag != TOKEN_ID)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"GSSHeader did not find the right tag");
int length = getLength(is);
DerValue temp = new DerValue(is);
mechOidBytes = temp.toByteArray();
mechOid = temp.getOID();
// debug (" oid=" + mechOid);
// debug (" len starting with oid=" + length);
mechTokenLength = length - mechOidBytes.length;
// debug(" mechToken length=" + mechTokenLength);
}
/**
* Used to obtain the Oid stored in this GSSHeader instance.
* @return the Oid of the mechanism.
*/
public ObjectIdentifier getOid() {
return mechOid;
}
/**
* Used to obtain the length of the mechanism specific token that
* will follow the encoding of this GSSHeader instance.
* @return the length of the mechanism specific token portion that
* will follow this GSSHeader.
*/
public int getMechTokenLength() {
return mechTokenLength;
}
/**
* Used to obtain the length of the encoding of this GSSHeader.
* @return the lenght of the encoding of this GSSHeader instance.
*/
public int getLength() {
int lenField = mechOidBytes.length + mechTokenLength;
return (1 + getLenFieldSize(lenField) + mechOidBytes.length);
}
/**
* Used to determine what the maximum possible mechanism token
* size is if the complete GSSToken returned to the application
* (including a GSSHeader) is not to exceed some pre-determined
* value in size.
* @param mechOid the Oid of the mechanism that will generate
* this GSS-API token
* @param maxTotalSize the pre-determined value that serves as a
* maximum size for the complete GSS-API token (including a
* GSSHeader)
* @return the maximum size of mechanism token that can be used
* so as to not exceed maxTotalSize with the GSS-API token
*/
public static int getMaxMechTokenSize(ObjectIdentifier mechOid,
int maxTotalSize) {
int mechOidBytesSize = 0;
try {
DerOutputStream temp = new DerOutputStream();
temp.putOID(mechOid);
mechOidBytesSize = temp.toByteArray().length;
} catch (IOException e) {
}
// Subtract bytes needed for 0x60 tag and mechOidBytes
maxTotalSize -= (1 + mechOidBytesSize);
// Subtract maximum len bytes
maxTotalSize -= 5;
return maxTotalSize;
/*
* Len field and mechanism token must fit in remaining
* space. The range of the len field that we allow is
* 1 through 5.
*
int mechTokenSize = 0;
for (int lenFieldSize = 1; lenFieldSize <= 5;
lenFieldSize++) {
mechTokenSize = maxTotalSize - lenFieldSize;
if (getLenFieldSize(mechTokenSize + mechOidBytesSize +
lenFieldSize) <= lenFieldSize)
break;
}
return mechTokenSize;
*/
}
/**
* Used to determine the number of bytes that will be need to encode
* the length field of the GSSHeader.
*/
private int getLenFieldSize(int len) {
int retVal = 1;
if (len < 128) {
retVal=1;
} else if (len < (1 << 8)) {
retVal=2;
} else if (len < (1 << 16)) {
retVal=3;
} else if (len < (1 << 24)) {
retVal=4;
} else {
retVal=5; // See getMaxMechTokenSize
}
return retVal;
}
/**
* Encodes this GSSHeader instance onto the provided OutputStream.
* @param os the OutputStream to which the token should be written.
* @return the number of bytes that are output as a result of this
* encoding
*/
public int encode(OutputStream os) throws IOException {
int retVal = 1 + mechOidBytes.length;
os.write(TOKEN_ID);
int length = mechOidBytes.length + mechTokenLength;
retVal += putLength(length, os);
os.write(mechOidBytes);
return retVal;
}
/**
* Get a length from the input stream, allowing for at most 32 bits of
* encoding to be used. (Not the same as getting a tagged integer!)
*
* @return the length or -1 if indefinite length found.
* @exception IOException on parsing error or unsupported lengths.
*/
// shameless lifted from sun.security.util.DerInputStream.
private int getLength(InputStream in) throws IOException {
return getLength(in.read(), in);
}
/**
* Get a length from the input stream, allowing for at most 32 bits of
* encoding to be used. (Not the same as getting a tagged integer!)
*
* @return the length or -1 if indefinite length found.
* @exception IOException on parsing error or unsupported lengths.
*/
// shameless lifted from sun.security.util.DerInputStream.
private int getLength(int lenByte, InputStream in) throws IOException {
int value, tmp;
tmp = lenByte;
if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum
value = tmp;
} else { // long form or indefinite
tmp &= 0x07f;
/*
* NOTE: tmp == 0 indicates indefinite length encoded data.
* tmp > 4 indicates more than 4Gb of data.
*/
if (tmp == 0)
return -1;
if (tmp < 0 || tmp > 4)
throw new IOException("DerInputStream.getLength(): lengthTag="
+ tmp + ", "
+ ((tmp < 0) ? "incorrect DER encoding." : "too big."));
for (value = 0; tmp > 0; tmp --) {
value <<= 8;
value += 0x0ff & in.read();
}
if (value < 0) {
throw new IOException("Invalid length bytes");
}
}
return value;
}
/**
* Put the encoding of the length in the specified stream.
*
* @params len the length of the attribute.
* @param out the outputstream to write the length to
* @return the number of bytes written
* @exception IOException on writing errors.
*/
// Shameless lifted from sun.security.util.DerOutputStream.
private int putLength(int len, OutputStream out) throws IOException {
int retVal = 0;
if (len < 128) {
out.write((byte)len);
retVal=1;
} else if (len < (1 << 8)) {
out.write((byte)0x081);
out.write((byte)len);
retVal=2;
} else if (len < (1 << 16)) {
out.write((byte)0x082);
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=3;
} else if (len < (1 << 24)) {
out.write((byte)0x083);
out.write((byte)(len >> 16));
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=4;
} else {
out.write((byte)0x084);
out.write((byte)(len >> 24));
out.write((byte)(len >> 16));
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=5;
}
return retVal;
}
// XXX Call these two in some central class
private void debug(String str) {
System.err.print(str);
}
private String getHexBytes(byte[] bytes, int len)
throws IOException {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i++) {
int b1 = (bytes[i]>>4) & 0x0f;
int b2 = bytes[i] & 0x0f;
sb.append(Integer.toHexString(b1));
sb.append(Integer.toHexString(b2));
sb.append(' ');
}
return sb.toString();
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.security.Provider;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* This class provides the default implementation of the GSSManager
* interface.
*/
public class GSSManagerImpl extends GSSManager {
// Undocumented property
private static final String USE_NATIVE_PROP =
"sun.security.jgss.native";
private static final Boolean USE_NATIVE;
static {
USE_NATIVE =
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
return Boolean.valueOf(System.getProperty
(USE_NATIVE_PROP));
}
});
}
private ProviderList list;
// Used by java SPNEGO impl to make sure native is disabled
public GSSManagerImpl(GSSCaller caller, boolean useNative) {
list = new ProviderList(caller, useNative);
}
// Used by HTTP/SPNEGO NegotiatorImpl
public GSSManagerImpl(GSSCaller caller) {
list = new ProviderList(caller, USE_NATIVE);
}
public GSSManagerImpl() {
list = new ProviderList(GSSCaller.CALLER_UNKNOWN, USE_NATIVE);
}
public Oid[] getMechs(){
return list.getMechs();
}
public Oid[] getNamesForMech(Oid mech)
throws GSSException {
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameTypes().clone();
}
public Oid[] getMechsForName(Oid nameType){
Oid[] mechs = list.getMechs();
Oid[] retVal = new Oid[mechs.length];
int pos = 0;
// Compatibility with RFC 2853 old NT_HOSTBASED_SERVICE value.
if (nameType.equals(GSSNameImpl.oldHostbasedServiceName)) {
nameType = GSSName.NT_HOSTBASED_SERVICE;
}
// Iterate thru all mechs in GSS
for (int i = 0; i < mechs.length; i++) {
// what nametypes does this mech support?
Oid mech = mechs[i];
try {
Oid[] namesForMech = getNamesForMech(mech);
// Is the desired Oid present in that list?
if (nameType.containedIn(namesForMech)) {
retVal[pos++] = mech;
}
} catch (GSSException e) {
// Squelch it and just skip over this mechanism
GSSUtil.debug("Skip " + mech +
": error retrieving supported name types");
}
}
// Trim the list if needed
if (pos < retVal.length) {
Oid[] temp = new Oid[pos];
for (int i = 0; i < pos; i++)
temp[i] = retVal[i];
retVal = temp;
}
return retVal;
}
public GSSName createName(String nameStr, Oid nameType)
throws GSSException {
return new GSSNameImpl(this, nameStr, nameType);
}
public GSSName createName(byte name[], Oid nameType)
throws GSSException {
return new GSSNameImpl(this, name, nameType);
}
public GSSName createName(String nameStr, Oid nameType,
Oid mech) throws GSSException {
return new GSSNameImpl(this, nameStr, nameType, mech);
}
public GSSName createName(byte name[], Oid nameType, Oid mech)
throws GSSException {
return new GSSNameImpl(this, name, nameType, mech);
}
public GSSCredential createCredential(int usage)
throws GSSException {
return new GSSCredentialImpl(this, usage);
}
public GSSCredential createCredential(GSSName aName,
int lifetime, Oid mech, int usage)
throws GSSException {
return new GSSCredentialImpl(this, aName, lifetime, mech, usage);
}
public GSSCredential createCredential(GSSName aName,
int lifetime, Oid mechs[], int usage)
throws GSSException {
return new GSSCredentialImpl(this, aName, lifetime, mechs, usage);
}
public GSSContext createContext(GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException {
return new GSSContextImpl(this, peer, mech, myCred, lifetime);
}
public GSSContext createContext(GSSCredential myCred)
throws GSSException {
return new GSSContextImpl(this, myCred);
}
public GSSContext createContext(byte[] interProcessToken)
throws GSSException {
return new GSSContextImpl(this, interProcessToken);
}
public void addProviderAtFront(Provider p, Oid mech)
throws GSSException {
list.addProviderAtFront(p, mech);
}
public void addProviderAtEnd(Provider p, Oid mech)
throws GSSException {
list.addProviderAtEnd(p, mech);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name, int initLifetime,
int acceptLifetime, Oid mech, int usage)
throws GSSException {
MechanismFactory factory = list.getMechFactory(mech);
return factory.getCredentialElement(name, initLifetime,
acceptLifetime, usage);
}
// Used by java SPNEGO impl
public GSSNameSpi getNameElement(String name, Oid nameType, Oid mech)
throws GSSException {
// Just use the most preferred MF impl assuming GSSNameSpi
// objects are interoperable among providers
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameElement(name, nameType);
}
// Used by java SPNEGO impl
public GSSNameSpi getNameElement(byte[] name, Oid nameType, Oid mech)
throws GSSException {
// Just use the most preferred MF impl assuming GSSNameSpi
// objects are interoperable among providers
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameElement(name, nameType);
}
GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred,
int lifetime, Oid mech)
throws GSSException {
Provider p = null;
if (myInitiatorCred != null) {
p = myInitiatorCred.getProvider();
}
MechanismFactory factory = list.getMechFactory(mech, p);
return factory.getMechanismContext(peer, myInitiatorCred, lifetime);
}
GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred,
Oid mech)
throws GSSException {
Provider p = null;
if (myAcceptorCred != null) {
p = myAcceptorCred.getProvider();
}
MechanismFactory factory = list.getMechFactory(mech, p);
return factory.getMechanismContext(myAcceptorCred);
}
GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
if ((exportedContext == null) || (exportedContext.length == 0)) {
throw new GSSException(GSSException.NO_CONTEXT);
}
GSSContextSpi result = null;
// Only allow context import with native provider since JGSS
// still has not defined its own interprocess token format
Oid[] mechs = list.getMechs();
for (int i = 0; i < mechs.length; i++) {
MechanismFactory factory = list.getMechFactory(mechs[i]);
if (factory.getProvider().getName().equals("SunNativeGSS")) {
result = factory.getMechanismContext(exportedContext);
if (result != null) break;
}
}
if (result == null) {
throw new GSSException(GSSException.UNAVAILABLE);
}
return result;
}
}

View File

@@ -0,0 +1,507 @@
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Arrays;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import sun.security.util.ObjectIdentifier;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
/**
* This is the implementation class for GSSName. Conceptually the
* GSSName is a container with mechanism specific name elements. Each
* name element is a representation of how that particular mechanism
* would canonicalize this principal.
*
* Generally a GSSName is created by an application when it supplies
* a sequence of bytes and a nametype that helps each mechanism
* decide how to interpret those bytes.
*
* It is not necessary to create name elements for each available
* mechanism at the time the application creates the GSSName. This
* implementation does this lazily, as and when name elements for
* mechanisms are required to be handed out. (Generally, other GSS
* classes like GSSContext and GSSCredential request specific
* elements depending on the mechanisms that they are dealing with.)
* Assume that getting a mechanism to parse the applciation specified
* bytes is an expensive call.
*
* When a GSSName is canonicalized wrt some mechanism, it is supposed
* to discard all elements of other mechanisms and retain only the
* element for this mechanism. In GSS terminology this is called a
* Mechanism Name or MN. This implementation tries to retain the
* application provided bytes and name type just in case the MN is
* asked to produce an element for a mechanism that is different.
*
* When a GSSName is to be exported, the name element for the desired
* mechanism is converted to a byte representation and written
* out. It might happen that a name element for that mechanism cannot
* be obtained. This happens when the mechanism is just not supported
* in this GSS-API or when the mechanism is supported but bytes
* corresponding to the nametypes that it understands are not
* available in this GSSName.
*
* This class is safe for sharing. Each retrieval of a name element
* from getElement() might potentially add a new element to the
* hashmap of elements, but getElement() is synchronized.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class GSSNameImpl implements GSSName {
/**
* The old Oid used in RFC 2853. Now supported as
* input parameters in:
*
* 1. The four overloaded GSSManager.createName(*) methods
* 2. GSSManager.getMechsForName(Oid)
*
* Note that even if a GSSName is created with this old Oid,
* its internal name type and getStringNameType() output are
* always the new value.
*/
final static Oid oldHostbasedServiceName;
static {
Oid tmp = null;
try {
tmp = new Oid("1.3.6.1.5.6.2");
} catch (Exception e) {
// should never happen
}
oldHostbasedServiceName = tmp;
}
private GSSManagerImpl gssManager = null;
/*
* Store whatever the application passed in. We will use this to
* get individual mechanisms to create name elements as and when
* needed.
* Store both the String and the byte[]. Leave I18N to the
* mechanism by allowing it to extract bytes from the String!
*/
private String appNameStr = null;
private byte[] appNameBytes = null;
private Oid appNameType = null;
/*
* When we figure out what the printable name would be, we store
* both the name and its type.
*/
private String printableName = null;
private Oid printableNameType = null;
private HashMap<Oid, GSSNameSpi> elements = null;
private GSSNameSpi mechElement = null;
static GSSNameImpl wrapElement(GSSManagerImpl gssManager,
GSSNameSpi mechElement) throws GSSException {
return (mechElement == null ?
null : new GSSNameImpl(gssManager, mechElement));
}
GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) {
this.gssManager = gssManager;
appNameStr = printableName = mechElement.toString();
appNameType = printableNameType = mechElement.getStringNameType();
this.mechElement = mechElement;
elements = new HashMap<Oid, GSSNameSpi>(1);
elements.put(mechElement.getMechanism(), this.mechElement);
}
GSSNameImpl(GSSManagerImpl gssManager,
Object appName,
Oid appNameType)
throws GSSException {
this(gssManager, appName, appNameType, null);
}
GSSNameImpl(GSSManagerImpl gssManager,
Object appName,
Oid appNameType,
Oid mech)
throws GSSException {
if (oldHostbasedServiceName.equals(appNameType)) {
appNameType = GSSName.NT_HOSTBASED_SERVICE;
}
if (appName == null)
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Cannot import null name");
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
if (NT_EXPORT_NAME.equals(appNameType)) {
importName(gssManager, appName);
} else {
init(gssManager, appName, appNameType, mech);
}
}
private void init(GSSManagerImpl gssManager,
Object appName, Oid appNameType,
Oid mech)
throws GSSException {
this.gssManager = gssManager;
this.elements =
new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length);
if (appName instanceof String) {
this.appNameStr = (String) appName;
/*
* If appNameType is null, then the nametype for this printable
* string is determined only by interrogating the
* mechanism. Thus, defer the setting of printableName and
* printableNameType till later.
*/
if (appNameType != null) {
printableName = appNameStr;
printableNameType = appNameType;
}
} else {
this.appNameBytes = (byte[]) appName;
}
this.appNameType = appNameType;
mechElement = getElement(mech);
/*
* printableName will be null if appName was in a byte[] or if
* appName was in a String but appNameType was null.
*/
if (printableName == null) {
printableName = mechElement.toString();
printableNameType = mechElement.getStringNameType();
}
/*
* At this point the GSSNameImpl has the following set:
* appNameStr or appNameBytes
* appNameType (could be null)
* printableName
* printableNameType
* mechElement (which also exists in the hashmap of elements)
*/
}
private void importName(GSSManagerImpl gssManager,
Object appName)
throws GSSException {
int pos = 0;
byte[] bytes = null;
if (appName instanceof String) {
try {
bytes = ((String) appName).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// Won't happen
}
} else
bytes = (byte[]) appName;
if ((bytes[pos++] != 0x04) ||
(bytes[pos++] != 0x01))
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name token id is corrupted!");
int oidLen = (((0xFF & bytes[pos++]) << 8) |
(0xFF & bytes[pos++]));
ObjectIdentifier temp = null;
try {
DerInputStream din = new DerInputStream(bytes, pos,
oidLen);
temp = new ObjectIdentifier(din);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name Object identifier is corrupted!");
}
Oid oid = new Oid(temp.toString());
pos += oidLen;
int mechPortionLen = (((0xFF & bytes[pos++]) << 24) |
((0xFF & bytes[pos++]) << 16) |
((0xFF & bytes[pos++]) << 8) |
(0xFF & bytes[pos++]));
if (mechPortionLen < 0 || pos > bytes.length - mechPortionLen) {
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name mech name is corrupted!");
}
byte[] mechPortion = new byte[mechPortionLen];
System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen);
init(gssManager, mechPortion, NT_EXPORT_NAME, oid);
}
public GSSName canonicalize(Oid mech) throws GSSException {
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
return wrapElement(gssManager, getElement(mech));
}
/**
* This method may return false negatives. But if it says two
* names are equals, then there is some mechanism that
* authenticates them as the same principal.
*/
public boolean equals(GSSName other) throws GSSException {
if (this.isAnonymous() || other.isAnonymous())
return false;
if (other == this)
return true;
if (! (other instanceof GSSNameImpl))
return equals(gssManager.createName(other.toString(),
other.getStringNameType()));
/*
* XXX Do a comparison of the appNameStr/appNameBytes if
* available. If that fails, then proceed with this test.
*/
GSSNameImpl that = (GSSNameImpl) other;
GSSNameSpi myElement = this.mechElement;
GSSNameSpi element = that.mechElement;
/*
* XXX If they are not of the same mechanism type, convert both to
* Kerberos since it is guaranteed to be present.
*/
if ((myElement == null) && (element != null)) {
myElement = this.getElement(element.getMechanism());
} else if ((myElement != null) && (element == null)) {
element = that.getElement(myElement.getMechanism());
}
if (myElement != null && element != null) {
return myElement.equals(element);
}
if ((this.appNameType != null) &&
(that.appNameType != null)) {
if (!this.appNameType.equals(that.appNameType)) {
return false;
}
byte[] myBytes = null;
byte[] bytes = null;
try {
myBytes =
(this.appNameStr != null ?
this.appNameStr.getBytes("UTF-8") :
this.appNameBytes);
bytes =
(that.appNameStr != null ?
that.appNameStr.getBytes("UTF-8") :
that.appNameBytes);
} catch (UnsupportedEncodingException e) {
// Won't happen
}
return Arrays.equals(myBytes, bytes);
}
return false;
}
/**
* Returns a hashcode value for this GSSName.
*
* @return a hashCode value
*/
public int hashCode() {
/*
* XXX
* In order to get this to work reliably and properly(!), obtain a
* Kerberos name element for the name and then call hashCode on its
* string representation. But this cannot be done if the nametype
* is not one of those supported by the Kerberos provider and hence
* this name cannot be imported by Kerberos. In that case return a
* constant value!
*/
return 1;
}
public boolean equals(Object another) {
try {
// XXX This can lead to an infinite loop. Extract info
// and create a GSSNameImpl with it.
if (another instanceof GSSName)
return equals((GSSName) another);
} catch (GSSException e) {
// Squelch it and return false
}
return false;
}
/**
* Returns a flat name representation for this object. The name
* format is defined in RFC 2743:
*<pre>
* Length Name Description
* 2 TOK_ID Token Identifier
* For exported name objects, this
* must be hex 04 01.
* 2 MECH_OID_LEN Length of the Mechanism OID
* MECH_OID_LEN MECH_OID Mechanism OID, in DER
* 4 NAME_LEN Length of name
* NAME_LEN NAME Exported name; format defined in
* applicable mechanism draft.
*</pre>
*
* Note that it is not required to canonicalize a name before
* calling export(). i.e., the name need not be an MN. If it is
* not an MN, an implementation defined algorithm can be used for
* choosing the mechanism which should export this name.
*
* @return the flat name representation for this object
* @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
* BAD_NAME, FAILURE.
*/
public byte[] export() throws GSSException {
if (mechElement == null) {
/* Use default mech */
mechElement = getElement(ProviderList.DEFAULT_MECH_OID);
}
byte[] mechPortion = mechElement.export();
byte[] oidBytes = null;
ObjectIdentifier oid = null;
try {
oid = new ObjectIdentifier
(mechElement.getMechanism().toString());
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Invalid OID String ");
}
DerOutputStream dout = new DerOutputStream();
try {
dout.putOID(oid);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Could not ASN.1 Encode "
+ oid.toString());
}
oidBytes = dout.toByteArray();
byte[] retVal = new byte[2
+ 2 + oidBytes.length
+ 4 + mechPortion.length];
int pos = 0;
retVal[pos++] = 0x04;
retVal[pos++] = 0x01;
retVal[pos++] = (byte) (oidBytes.length>>>8);
retVal[pos++] = (byte) oidBytes.length;
System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length);
pos += oidBytes.length;
retVal[pos++] = (byte) (mechPortion.length>>>24);
retVal[pos++] = (byte) (mechPortion.length>>>16);
retVal[pos++] = (byte) (mechPortion.length>>>8);
retVal[pos++] = (byte) mechPortion.length;
System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length);
return retVal;
}
public String toString() {
return printableName;
}
public Oid getStringNameType() throws GSSException {
return printableNameType;
}
public boolean isAnonymous() {
if (printableNameType == null) {
return false;
} else {
return GSSName.NT_ANONYMOUS.equals(printableNameType);
}
}
public boolean isMN() {
return true; // Since always canonicalized for some mech
}
public synchronized GSSNameSpi getElement(Oid mechOid)
throws GSSException {
GSSNameSpi retVal = elements.get(mechOid);
if (retVal == null) {
if (appNameStr != null) {
retVal = gssManager.getNameElement
(appNameStr, appNameType, mechOid);
} else {
retVal = gssManager.getNameElement
(appNameBytes, appNameType, mechOid);
}
elements.put(mechOid, retVal);
}
return retVal;
}
Set<GSSNameSpi> getElements() {
return new HashSet<GSSNameSpi>(elements.values());
}
private static String getNameTypeStr(Oid nameTypeOid) {
if (nameTypeOid == null)
return "(NT is null)";
if (nameTypeOid.equals(NT_USER_NAME))
return "NT_USER_NAME";
if (nameTypeOid.equals(NT_HOSTBASED_SERVICE))
return "NT_HOSTBASED_SERVICE";
if (nameTypeOid.equals(NT_EXPORT_NAME))
return "NT_EXPORT_NAME";
if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
return "NT_GSS_KRB5_PRINCIPAL";
else
return "Unknown";
}
}

View File

@@ -0,0 +1,231 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.EOFException;
import sun.security.util.*;
/**
* Utilities for processing GSS Tokens.
*
*/
public abstract class GSSToken {
/**
* Copies an integer value to a byte array in little endian form.
* @param value the integer value to write
* @param array the byte array into which the integer must be copied. It
* is assumed that the array will be large enough to hold the 4 bytes of
* the integer.
*/
public static final void writeLittleEndian(int value, byte[] array) {
writeLittleEndian(value, array, 0);
}
/**
* Copies an integer value to a byte array in little endian form.
* @param value the integer value to write
* @param array the byte array into which the integer must be copied. It
* is assumed that the array will be large enough to hold the 4 bytes of
* the integer.
* @param pos the position at which to start writing
*/
public static final void writeLittleEndian(int value, byte[] array,
int pos) {
array[pos++] = (byte)(value);
array[pos++] = (byte)((value>>>8));
array[pos++] = (byte)((value>>>16));
array[pos++] = (byte)((value>>>24));
}
public static final void writeBigEndian(int value, byte[] array) {
writeBigEndian(value, array, 0);
}
public static final void writeBigEndian(int value, byte[] array,
int pos) {
array[pos++] = (byte)((value>>>24));
array[pos++] = (byte)((value>>>16));
array[pos++] = (byte)((value>>>8));
array[pos++] = (byte)(value);
}
/**
* Reads an integer value from a byte array in little endian form. This
* method allows the reading of two byte values as well as four bytes
* values both of which are needed in the Kerberos v5 GSS-API mechanism.
*
* @param data the array containing the bytes of the integer value
* @param pos the offset in the array
* @param size the number of bytes to read from the array.
* @return the integer value
*/
public static final int readLittleEndian(byte[] data, int pos, int size) {
int retVal = 0;
int shifter = 0;
while (size > 0) {
retVal += (data[pos] & 0xff) << shifter;
shifter += 8;
pos++;
size--;
}
return retVal;
}
public static final int readBigEndian(byte[] data, int pos, int size) {
int retVal = 0;
int shifter = (size-1)*8;
while (size > 0) {
retVal += (data[pos] & 0xff) << shifter;
shifter -= 8;
pos++;
size--;
}
return retVal;
}
/**
* Writes a two byte integer value to a OutputStream.
*
* @param val the integer value. It will lose the high-order two bytes.
* @param os the OutputStream to write to
* @throws IOException if an error occurs while writing to the OutputStream
*/
public static final void writeInt(int val, OutputStream os)
throws IOException {
os.write(val>>>8);
os.write(val);
}
/**
* Writes a two byte integer value to a byte array.
*
* @param val the integer value. It will lose the high-order two bytes.
* @param dest the byte array to write to
* @param pos the offset to start writing to
*/
public static final int writeInt(int val, byte[] dest, int pos) {
dest[pos++] = (byte)(val>>>8);
dest[pos++] = (byte)val;
return pos;
}
/**
* Reads a two byte integer value from an InputStream.
*
* @param is the InputStream to read from
* @return the integer value
* @throws IOException if some errors occurs while reading the integer
* bytes.
*/
public static final int readInt(InputStream is) throws IOException {
return (((0xFF & is.read()) << 8)
| (0xFF & is.read()));
}
/**
* Reads a two byte integer value from a byte array.
*
* @param src the byte arra to read from
* @param pos the offset to start reading from
* @return the integer value
*/
public static final int readInt(byte[] src, int pos) {
return ((0xFF & src[pos])<<8 | (0xFF & src[pos+1]));
}
/**
* Blocks till the required number of bytes have been read from the
* input stream.
*
* @param is the InputStream to read from
* @param buffer the buffer to store the bytes into
* @throws EOFException if EOF is reached before all bytes are
* read.
* @throws IOException is an error occurs while reading
*/
public static final void readFully(InputStream is, byte[] buffer)
throws IOException {
readFully(is, buffer, 0, buffer.length);
}
/**
* Blocks till the required number of bytes have been read from the
* input stream.
*
* @param is the InputStream to read from
* @param buffer the buffer to store the bytes into
* @param offset the offset to start storing at
* @param len the number of bytes to read
* @throws EOFException if EOF is reached before all bytes are
* read.
* @throws IOException is an error occurs while reading
*/
public static final void readFully(InputStream is,
byte[] buffer, int offset, int len)
throws IOException {
int temp;
while (len > 0) {
temp = is.read(buffer, offset, len);
if (temp == -1)
throw new EOFException("Cannot read all "
+ len
+ " bytes needed to form this token!");
offset += temp;
len -= temp;
}
}
public static final void debug(String str) {
System.err.print(str);
}
public static final String getHexBytes(byte[] bytes) {
return getHexBytes(bytes, 0, bytes.length);
}
public static final String getHexBytes(byte[] bytes, int len) {
return getHexBytes(bytes, 0, len);
}
public static final String getHexBytes(byte[] bytes, int pos, int len) {
StringBuffer sb = new StringBuffer();
for (int i = pos; i < (pos+len); i++) {
int b1 = (bytes[i]>>4) & 0x0f;
int b2 = bytes[i] & 0x0f;
sb.append(Integer.toHexString(b1));
sb.append(Integer.toHexString(b2));
sb.append(' ');
}
return sb.toString();
}
}

View File

@@ -0,0 +1,366 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import com.sun.security.auth.callback.TextCallbackHandler;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import org.ietf.jgss.*;
import sun.security.jgss.spi.GSSNameSpi;
import sun.security.jgss.spi.GSSCredentialSpi;
import sun.security.action.GetPropertyAction;
import sun.security.jgss.krb5.Krb5NameElement;
import sun.security.jgss.spnego.SpNegoCredElement;
import java.util.Set;
import java.util.HashSet;
import java.util.Vector;
import java.util.Iterator;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import sun.security.action.GetBooleanAction;
/**
* The GSSUtilImplementation that knows how to work with the internals of
* the GSS-API.
*/
public class GSSUtil {
public static final Oid GSS_KRB5_MECH_OID =
GSSUtil.createOid("1.2.840.113554.1.2.2");
public static final Oid GSS_KRB5_MECH_OID2 =
GSSUtil.createOid("1.3.5.1.5.2");
public static final Oid GSS_KRB5_MECH_OID_MS =
GSSUtil.createOid("1.2.840.48018.1.2.2");
public static final Oid GSS_SPNEGO_MECH_OID =
GSSUtil.createOid("1.3.6.1.5.5.2");
public static final Oid NT_GSS_KRB5_PRINCIPAL =
GSSUtil.createOid("1.2.840.113554.1.2.2.1");
private static final String DEFAULT_HANDLER =
"auth.login.defaultCallbackHandler";
static final boolean DEBUG;
static {
DEBUG = (AccessController.doPrivileged
(new GetBooleanAction("sun.security.jgss.debug"))).
booleanValue();
}
static void debug(String message) {
if (DEBUG) {
assert(message != null);
System.out.println(message);
}
}
// NOTE: this method is only for creating Oid objects with
// known to be valid <code>oidStr</code> given it ignores
// the GSSException
public static Oid createOid(String oidStr) {
try {
return new Oid(oidStr);
} catch (GSSException e) {
debug("Ignored invalid OID: " + oidStr);
return null;
}
}
public static boolean isSpNegoMech(Oid oid) {
return (GSS_SPNEGO_MECH_OID.equals(oid));
}
public static boolean isKerberosMech(Oid oid) {
return (GSS_KRB5_MECH_OID.equals(oid) ||
GSS_KRB5_MECH_OID2.equals(oid) ||
GSS_KRB5_MECH_OID_MS.equals(oid));
}
public static String getMechStr(Oid oid) {
if (isSpNegoMech(oid)) {
return "SPNEGO";
} else if (isKerberosMech(oid)) {
return "Kerberos V5";
} else {
return oid.toString();
}
}
/**
* Note: The current impl only works with Sun's impl of
* GSSName and GSSCredential since it depends on package
* private APIs.
*/
public static Subject getSubject(GSSName name,
GSSCredential creds) {
HashSet<Object> privCredentials = null;
HashSet<Object> pubCredentials = new HashSet<Object>(); // empty Set
Set<GSSCredentialSpi> gssCredentials = null;
Set<KerberosPrincipal> krb5Principals =
new HashSet<KerberosPrincipal>();
if (name instanceof GSSNameImpl) {
try {
GSSNameSpi ne = ((GSSNameImpl) name).getElement
(GSS_KRB5_MECH_OID);
String krbName = ne.toString();
if (ne instanceof Krb5NameElement) {
krbName =
((Krb5NameElement) ne).getKrb5PrincipalName().getName();
}
KerberosPrincipal krbPrinc = new KerberosPrincipal(krbName);
krb5Principals.add(krbPrinc);
} catch (GSSException ge) {
debug("Skipped name " + name + " due to " + ge);
}
}
if (creds instanceof GSSCredentialImpl) {
gssCredentials = ((GSSCredentialImpl) creds).getElements();
privCredentials = new HashSet<Object>(gssCredentials.size());
populateCredentials(privCredentials, gssCredentials);
} else {
privCredentials = new HashSet<Object>(); // empty Set
}
debug("Created Subject with the following");
debug("principals=" + krb5Principals);
debug("public creds=" + pubCredentials);
debug("private creds=" + privCredentials);
return new Subject(false, krb5Principals, pubCredentials,
privCredentials);
}
/**
* Populates the set credentials with elements from gssCredentials. At
* the same time, it converts any subclasses of KerberosTicket
* into KerberosTicket instances and any subclasses of KerberosKey into
* KerberosKey instances. (It is not desirable to expose the customer
* to sun.security.jgss.krb5.Krb5InitCredential which extends
* KerberosTicket and sun.security.jgss.krb5.Kbr5AcceptCredential which
* extends KerberosKey.)
*/
private static void populateCredentials(Set<Object> credentials,
Set<?> gssCredentials) {
Object cred;
Iterator<?> elements = gssCredentials.iterator();
while (elements.hasNext()) {
cred = elements.next();
// Retrieve the internal cred out of SpNegoCredElement
if (cred instanceof SpNegoCredElement) {
cred = ((SpNegoCredElement) cred).getInternalCred();
}
if (cred instanceof KerberosTicket) {
if (!cred.getClass().getName().equals
("javax.security.auth.kerberos.KerberosTicket")) {
KerberosTicket tempTkt = (KerberosTicket) cred;
cred = new KerberosTicket(tempTkt.getEncoded(),
tempTkt.getClient(),
tempTkt.getServer(),
tempTkt.getSessionKey().getEncoded(),
tempTkt.getSessionKeyType(),
tempTkt.getFlags(),
tempTkt.getAuthTime(),
tempTkt.getStartTime(),
tempTkt.getEndTime(),
tempTkt.getRenewTill(),
tempTkt.getClientAddresses());
}
credentials.add(cred);
} else if (cred instanceof KerberosKey) {
if (!cred.getClass().getName().equals
("javax.security.auth.kerberos.KerberosKey")) {
KerberosKey tempKey = (KerberosKey) cred;
cred = new KerberosKey(tempKey.getPrincipal(),
tempKey.getEncoded(),
tempKey.getKeyType(),
tempKey.getVersionNumber());
}
credentials.add(cred);
} else {
// Ignore non-KerberosTicket and non-KerberosKey elements
debug("Skipped cred element: " + cred);
}
}
}
/**
* Authenticate using the login module from the specified
* configuration entry.
*
* @param caller the caller of JAAS Login
* @param mech the mech to be used
* @return the authenticated subject
*/
public static Subject login(GSSCaller caller, Oid mech) throws LoginException {
CallbackHandler cb = null;
if (caller instanceof HttpCaller) {
cb = new sun.net.www.protocol.http.spnego.NegotiateCallbackHandler(
((HttpCaller)caller).info());
} else {
String defaultHandler =
java.security.Security.getProperty(DEFAULT_HANDLER);
// get the default callback handler
if ((defaultHandler != null) && (defaultHandler.length() != 0)) {
cb = null;
} else {
cb = new TextCallbackHandler();
}
}
// New instance of LoginConfigImpl must be created for each login,
// since the entry name is not passed as the first argument, but
// generated with caller and mech inside LoginConfigImpl
LoginContext lc = new LoginContext("", null, cb,
new LoginConfigImpl(caller, mech));
lc.login();
return lc.getSubject();
}
/**
* Determines if the application doesn't mind if the mechanism obtains
* the required credentials from outside of the current Subject. Our
* Kerberos v5 mechanism would do a JAAS login on behalf of the
* application if this were the case.
*
* The application indicates this by explicitly setting the system
* property javax.security.auth.useSubjectCredsOnly to false.
*/
public static boolean useSubjectCredsOnly(GSSCaller caller) {
String propValue = GetPropertyAction.privilegedGetProperty(
"javax.security.auth.useSubjectCredsOnly");
// Invalid values should be ignored and the default assumed.
if (caller instanceof HttpCaller) {
// Default for HTTP/SPNEGO is false.
return "true".equalsIgnoreCase(propValue);
} else {
// Default for JGSS is true.
return !("false".equalsIgnoreCase(propValue));
}
}
/**
* Determines the SPNEGO interoperability mode with Microsoft;
* by default it is set to true.
*
* To disable it, the application indicates this by explicitly setting
* the system property sun.security.spnego.interop to false.
*/
public static boolean useMSInterop() {
/*
* Don't use GetBooleanAction because the default value in the JRE
* (when this is unset) has to treated as true.
*/
String propValue = AccessController.doPrivileged(
new GetPropertyAction("sun.security.spnego.msinterop",
"true"));
/*
* This property has to be explicitly set to "false". Invalid
* values should be ignored and the default "true" assumed.
*/
return (!propValue.equalsIgnoreCase("false"));
}
/**
* Searches the private credentials of current Subject with the
* specified criteria and returns the matching GSSCredentialSpi
* object out of Sun's impl of GSSCredential. Returns null if
* no Subject present or a Vector which contains 0 or more
* matching GSSCredentialSpi objects.
*/
public static <T extends GSSCredentialSpi> Vector<T>
searchSubject(final GSSNameSpi name,
final Oid mech,
final boolean initiate,
final Class<? extends T> credCls) {
debug("Search Subject for " + getMechStr(mech) +
(initiate? " INIT" : " ACCEPT") + " cred (" +
(name == null? "<<DEF>>" : name.toString()) + ", " +
credCls.getName() + ")");
final AccessControlContext acc = AccessController.getContext();
try {
Vector<T> creds =
AccessController.doPrivileged
(new PrivilegedExceptionAction<Vector<T>>() {
public Vector<T> run() throws Exception {
Subject accSubj = Subject.getSubject(acc);
Vector<T> result = null;
if (accSubj != null) {
result = new Vector<T>();
Iterator<GSSCredentialImpl> iterator =
accSubj.getPrivateCredentials
(GSSCredentialImpl.class).iterator();
while (iterator.hasNext()) {
GSSCredentialImpl cred = iterator.next();
debug("...Found cred" + cred);
try {
GSSCredentialSpi ce =
cred.getElement(mech, initiate);
debug("......Found element: " + ce);
if (ce.getClass().equals(credCls) &&
(name == null ||
name.equals((Object) ce.getName()))) {
result.add(credCls.cast(ce));
} else {
debug("......Discard element");
}
} catch (GSSException ge) {
debug("...Discard cred (" + ge + ")");
}
}
} else debug("No Subject");
return result;
}
});
return creds;
} catch (PrivilegedActionException pae) {
debug("Unexpected exception when searching Subject:");
if (DEBUG) pae.printStackTrace();
return null;
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import sun.net.www.protocol.http.HttpCallerInfo;
/**
* A special kind of GSSCaller, which origins from HTTP/Negotiate and contains
* info about what triggers the JGSS calls.
*/
public class HttpCaller extends GSSCaller {
final private HttpCallerInfo hci;
public HttpCaller(HttpCallerInfo hci) {
super("HTTP_CLIENT");
this.hci = hci;
}
public HttpCallerInfo info() {
return hci;
}
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import org.ietf.jgss.Oid;
import sun.security.action.GetPropertyAction;
/**
* A Configuration implementation especially designed for JGSS.
*
* @author weijun.wang
* @since 1.6
*/
public class LoginConfigImpl extends Configuration {
private final Configuration config;
private final GSSCaller caller;
private final String mechName;
private static final sun.security.util.Debug debug =
sun.security.util.Debug.getInstance("gssloginconfig", "\t[GSS LoginConfigImpl]");
public static final boolean HTTP_USE_GLOBAL_CREDS;
static {
String prop = GetPropertyAction
.privilegedGetProperty("http.use.global.creds");
//HTTP_USE_GLOBAL_CREDS = "true".equalsIgnoreCase(prop); // default false
HTTP_USE_GLOBAL_CREDS = !"false".equalsIgnoreCase(prop); // default true
}
/**
* A new instance of LoginConfigImpl must be created for each login request
* since it's only used by a single (caller, mech) pair
* @param caller defined in GSSUtil as CALLER_XXX final fields
* @param mech defined in GSSUtil as XXX_MECH_OID final fields
*/
public LoginConfigImpl(GSSCaller caller, Oid mech) {
this.caller = caller;
if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID)) {
mechName = "krb5";
} else {
throw new IllegalArgumentException(mech.toString() + " not supported");
}
config = java.security.AccessController.doPrivileged
(new java.security.PrivilegedAction <Configuration> () {
public Configuration run() {
return Configuration.getConfiguration();
}
});
}
/**
* @param name Almost useless, since the (caller, mech) is already passed
* into constructor. The only use will be detecting OTHER which
* is called in LoginContext
*/
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
AppConfigurationEntry[] entries = null;
// This is the second call from LoginContext, which we will just ignore
if ("OTHER".equalsIgnoreCase(name)) {
return null;
}
String[] alts = null;
// Compatibility:
// For the 4 old callers, old entry names will be used if the new
// entry name is not provided.
if ("krb5".equals(mechName)) {
if (caller == GSSCaller.CALLER_INITIATE) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
"com.sun.security.jgss.initiate",
};
} else if (caller == GSSCaller.CALLER_ACCEPT) {
alts = new String[] {
"com.sun.security.jgss.krb5.accept",
"com.sun.security.jgss.accept",
};
} else if (caller == GSSCaller.CALLER_SSL_CLIENT) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
"com.sun.net.ssl.client",
};
} else if (caller == GSSCaller.CALLER_SSL_SERVER) {
alts = new String[] {
"com.sun.security.jgss.krb5.accept",
"com.sun.net.ssl.server",
};
} else if (caller instanceof HttpCaller) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
};
} else if (caller == GSSCaller.CALLER_UNKNOWN) {
throw new AssertionError("caller not defined");
}
} else {
throw new IllegalArgumentException(mechName + " not supported");
// No other mech at the moment, maybe --
/*
switch (caller) {
case GSSUtil.CALLER_INITIATE:
case GSSUtil.CALLER_SSL_CLIENT:
case GSSUtil.CALLER_HTTP_NEGOTIATE:
alts = new String[] {
"com.sun.security.jgss." + mechName + ".initiate",
};
break;
case GSSUtil.CALLER_ACCEPT:
case GSSUtil.CALLER_SSL_SERVER:
alts = new String[] {
"com.sun.security.jgss." + mechName + ".accept",
};
break;
case GSSUtil.CALLER_UNKNOWN:
// should never use
throw new AssertionError("caller cannot be unknown");
default:
throw new AssertionError("caller not defined");
}
*/
}
for (String alt: alts) {
entries = config.getAppConfigurationEntry(alt);
if (debug != null) {
debug.println("Trying " + alt +
((entries == null)?": does not exist.":": Found!"));
}
if (entries != null) {
break;
}
}
if (entries == null) {
if (debug != null) {
debug.println("Cannot read JGSS entry, use default values instead.");
}
entries = getDefaultConfigurationEntry();
}
return entries;
}
/**
* Default value for a caller-mech pair when no entry is defined in
* the system-wide Configuration object.
*/
private AppConfigurationEntry[] getDefaultConfigurationEntry() {
HashMap <String, String> options = new HashMap <String, String> (2);
if (mechName == null || mechName.equals("krb5")) {
if (isServerSide(caller)) {
// Assuming the keytab file can be found through
// krb5 config file or under user home directory
options.put("useKeyTab", "true");
options.put("storeKey", "true");
options.put("doNotPrompt", "true");
options.put("principal", "*");
options.put("isInitiator", "false");
} else {
if (caller instanceof HttpCaller && !HTTP_USE_GLOBAL_CREDS) {
options.put("useTicketCache", "false");
} else {
options.put("useTicketCache", "true");
}
options.put("doNotPrompt", "false");
}
return new AppConfigurationEntry[] {
new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options)
};
}
return null;
}
private static boolean isServerSide (GSSCaller caller) {
return GSSCaller.CALLER_ACCEPT == caller ||
GSSCaller.CALLER_SSL_SERVER == caller;
}
}

View File

@@ -0,0 +1,547 @@
/*
* Copyright (c) 2000, 2009, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import java.lang.reflect.InvocationTargetException;
import org.ietf.jgss.*;
import java.security.AccessController;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Enumeration;
import java.util.Iterator;
import sun.security.jgss.spi.*;
import sun.security.jgss.wrapper.NativeGSSFactory;
import sun.security.jgss.wrapper.SunNativeProvider;
import sun.security.action.GetPropertyAction;
/**
* This class stores the list of providers that this
* GSS-Implementation is configured to use. The GSSManagerImpl class
* queries this class whenever it needs a mechanism's factory.<p>
*
* This class stores an ordered list of pairs of the form
* {@code <provider, oid>}. When it attempts to instantiate a mechanism
* defined by oid o, it steps through the list looking for an entry
* with oid=o, or with oid=null. (An entry with oid=null matches all
* mechanisms.) When it finds such an entry, the corresponding
* provider is approached for the mechanism's factory class.
* At instantiation time this list in initialized to contain those
* system wide providers that contain a property of the form
* "GssApiMechanism.x.y.z..." where "x.y.z..." is a numeric object
* identifier with numbers x, y, z, etc. Such a property is defined
* to map to that provider's implementation of the MechanismFactory
* interface for the mechanism x.y.z...
* As and when a MechanismFactory is instantiated, it is
* cached for future use. <p>
*
* An application can cause more providers to be added by means of
* the addProviderAtFront and addProviderAtEnd methods on
* GSSManager which get delegated to this class. The
* addProviderAtFront method can also cause a change in the ordering
* of the providers without adding any new providers, by causing a
* provider to move up in a list. The method addProviderAtEnd can
* only add providers at the end of the list if they are not already
* in the list. The rationale is that an application will call
* addProviderAtFront when it wants a provider to be used in
* preference over the default ones. And it will call
* addProviderAtEnd when it wants a provider to be used in case
* the system ones don't suffice.<p>
*
* If a mechanism's factory is being obtained from a provider as a
* result of encountering a entryof the form {@code <provider, oid>} where
* oid is non-null, then the assumption is that the application added
* this entry and it wants this mechanism to be obtained from this
* provider. Thus is the provider does not actually contain the
* requested mechanism, an exception will be thrown. However, if the
* entry were of the form {@code <provider, null>}, then it is viewed more
* liberally and is simply skipped over if the provider does not claim to
* support the requested mechanism.
*/
public final class ProviderList {
private static final String PROV_PROP_PREFIX = "GssApiMechanism.";
private static final int PROV_PROP_PREFIX_LEN =
PROV_PROP_PREFIX.length();
private static final String SPI_MECH_FACTORY_TYPE
= "sun.security.jgss.spi.MechanismFactory";
// Undocumented property?
private static final String DEFAULT_MECH_PROP =
"sun.security.jgss.mechanism";
public static final Oid DEFAULT_MECH_OID;
static {
/*
* Set the default mechanism. Kerberos v5 is the default
* mechanism unless it is overridden by a system property.
* with a valid OID value
*/
Oid defOid = null;
String defaultOidStr = AccessController.doPrivileged
(new GetPropertyAction(DEFAULT_MECH_PROP));
if (defaultOidStr != null) {
defOid = GSSUtil.createOid(defaultOidStr);
}
DEFAULT_MECH_OID =
(defOid == null ? GSSUtil.GSS_KRB5_MECH_OID : defOid);
}
private ArrayList<PreferencesEntry> preferences =
new ArrayList<PreferencesEntry>(5);
private HashMap<PreferencesEntry, MechanismFactory> factories =
new HashMap<PreferencesEntry, MechanismFactory>(5);
private HashSet<Oid> mechs = new HashSet<Oid>(5);
final private GSSCaller caller;
public ProviderList(GSSCaller caller, boolean useNative) {
this.caller = caller;
Provider[] provList;
if (useNative) {
provList = new Provider[1];
provList[0] = new SunNativeProvider();
} else {
provList = Security.getProviders();
}
for (int i = 0; i < provList.length; i++) {
Provider prov = provList[i];
try {
addProviderAtEnd(prov, null);
} catch (GSSException ge) {
// Move on to the next provider
GSSUtil.debug("Error in adding provider " +
prov.getName() + ": " + ge);
}
} // End of for loop
}
/**
* Determines if the given provider property represents a GSS-API
* Oid to MechanismFactory mapping.
* @return true if this is a GSS-API property, false otherwise.
*/
private boolean isMechFactoryProperty(String prop) {
return (prop.startsWith(PROV_PROP_PREFIX) ||
prop.regionMatches(true, 0, // Try ignoring case
PROV_PROP_PREFIX, 0,
PROV_PROP_PREFIX_LEN));
}
private Oid getOidFromMechFactoryProperty(String prop)
throws GSSException {
String oidPart = prop.substring(PROV_PROP_PREFIX_LEN);
return new Oid(oidPart);
}
// So the existing code do not have to be changed
synchronized public MechanismFactory getMechFactory(Oid mechOid)
throws GSSException {
if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID;
return getMechFactory(mechOid, null);
}
/**
* Obtains a MechanismFactory for a given mechanism. If the
* specified provider is not null, then the impl from the
* provider is used. Otherwise, the most preferred impl based
* on the configured preferences is used.
* @param mechOid the oid of the desired mechanism
* @return a MechanismFactory for the desired mechanism.
* @throws GSSException when the specified provider does not
* support the desired mechanism, or when no provider supports
* the desired mechanism.
*/
synchronized public MechanismFactory getMechFactory(Oid mechOid,
Provider p)
throws GSSException {
if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID;
if (p == null) {
// Iterate thru all preferences to find right provider
String className;
PreferencesEntry entry;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
entry = list.next();
if (entry.impliesMechanism(mechOid)) {
MechanismFactory retVal = getMechFactory(entry, mechOid);
if (retVal != null) return retVal;
}
} // end of while loop
throw new GSSExceptionImpl(GSSException.BAD_MECH, mechOid);
} else {
// Use the impl from the specified provider; return null if the
// the mech is unsupported by the specified provider.
PreferencesEntry entry = new PreferencesEntry(p, mechOid);
return getMechFactory(entry, mechOid);
}
}
/**
* Helper routine that uses a preferences entry to obtain an
* implementation of a MechanismFactory from it.
* @param e the preferences entry that contains the provider and
* either a null of an explicit oid that matched the oid of the
* desired mechanism.
* @param mechOid the oid of the desired mechanism
* @throws GSSException If the application explicitly requested
* this entry's provider to be used for the desired mechanism but
* some problem is encountered
*/
private MechanismFactory getMechFactory(PreferencesEntry e, Oid mechOid)
throws GSSException {
Provider p = e.getProvider();
/*
* See if a MechanismFactory was previously instantiated for
* this provider and mechanism combination.
*/
PreferencesEntry searchEntry = new PreferencesEntry(p, mechOid);
MechanismFactory retVal = factories.get(searchEntry);
if (retVal == null) {
/*
* Apparently not. Now try to instantiate this class from
* the provider.
*/
String prop = PROV_PROP_PREFIX + mechOid.toString();
String className = p.getProperty(prop);
if (className != null) {
retVal = getMechFactoryImpl(p, className, mechOid, caller);
factories.put(searchEntry, retVal);
} else {
/*
* This provider does not support this mechanism.
* If the application explicitly requested that
* this provider be used for this mechanism, then
* throw an exception
*/
if (e.getOid() != null) {
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName() +
" does not support mechanism " + mechOid);
}
}
}
return retVal;
}
/**
* Helper routine to obtain a MechanismFactory implementation
* from the same class loader as the provider of this
* implementation.
* @param p the provider whose classloader must be used for
* instantiating the desired MechanismFactory
* @ param className the name of the MechanismFactory class
* @throws GSSException If some error occurs when trying to
* instantiate this MechanismFactory.
*/
private static MechanismFactory getMechFactoryImpl(Provider p,
String className,
Oid mechOid,
GSSCaller caller)
throws GSSException {
try {
Class<?> baseClass = Class.forName(SPI_MECH_FACTORY_TYPE);
/*
* Load the implementation class with the same class loader
* that was used to load the provider.
* In order to get the class loader of a class, the
* caller's class loader must be the same as or an ancestor of
* the class loader being returned. Otherwise, the caller must
* have "getClassLoader" permission, or a SecurityException
* will be thrown.
*/
ClassLoader cl = p.getClass().getClassLoader();
Class<?> implClass;
if (cl != null) {
implClass = cl.loadClass(className);
} else {
implClass = Class.forName(className);
}
if (baseClass.isAssignableFrom(implClass)) {
java.lang.reflect.Constructor<?> c =
implClass.getConstructor(GSSCaller.class);
MechanismFactory mf = (MechanismFactory) (c.newInstance(caller));
if (mf instanceof NativeGSSFactory) {
((NativeGSSFactory) mf).setMech(mechOid);
}
return mf;
} else {
throw createGSSException(p, className, "is not a " +
SPI_MECH_FACTORY_TYPE, null);
}
} catch (ClassNotFoundException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (NoSuchMethodException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (InvocationTargetException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (InstantiationException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (IllegalAccessException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (SecurityException e) {
throw createGSSException(p, className, "cannot be created", e);
}
}
// Only used by getMechFactoryImpl
private static GSSException createGSSException(Provider p,
String className,
String trailingMsg,
Exception cause) {
String errClassInfo = className + " configured by " +
p.getName() + " for GSS-API Mechanism Factory ";
return new GSSExceptionImpl(GSSException.BAD_MECH,
errClassInfo + trailingMsg,
cause);
}
public Oid[] getMechs() {
return mechs.toArray(new Oid[] {});
}
synchronized public void addProviderAtFront(Provider p, Oid mechOid)
throws GSSException {
PreferencesEntry newEntry = new PreferencesEntry(p, mechOid);
PreferencesEntry oldEntry;
boolean foundSomeMech;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
oldEntry = list.next();
if (newEntry.implies(oldEntry))
list.remove();
}
if (mechOid == null) {
foundSomeMech = addAllMechsFromProvider(p);
} else {
String oidStr = mechOid.toString();
if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null)
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName()
+ " does not support "
+ oidStr);
mechs.add(mechOid);
foundSomeMech = true;
}
if (foundSomeMech) {
preferences.add(0, newEntry);
}
}
synchronized public void addProviderAtEnd(Provider p, Oid mechOid)
throws GSSException {
PreferencesEntry newEntry = new PreferencesEntry(p, mechOid);
PreferencesEntry oldEntry;
boolean foundSomeMech;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
oldEntry = list.next();
if (oldEntry.implies(newEntry))
return;
}
// System.out.println("addProviderAtEnd: No it is not redundant");
if (mechOid == null)
foundSomeMech = addAllMechsFromProvider(p);
else {
String oidStr = mechOid.toString();
if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null)
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName()
+ " does not support "
+ oidStr);
mechs.add(mechOid);
foundSomeMech = true;
}
if (foundSomeMech) {
preferences.add(newEntry);
}
}
/**
* Helper routine to go through all properties contined in a
* provider and add its mechanisms to the list of supported
* mechanisms. If no default mechanism has been assinged so far,
* it sets the default MechanismFactory and Oid as well.
* @param p the provider to query
* @return true if there is at least one mechanism that this
* provider contributed, false otherwise
*/
private boolean addAllMechsFromProvider(Provider p) {
String prop;
boolean retVal = false;
// Get all props for this provider
Enumeration<Object> props = p.keys();
// See if there are any GSS prop's
while (props.hasMoreElements()) {
prop = (String) props.nextElement();
if (isMechFactoryProperty(prop)) {
// Ok! This is a GSS provider!
try {
Oid mechOid = getOidFromMechFactoryProperty(prop);
mechs.add(mechOid);
retVal = true;
} catch (GSSException e) {
// Skip to next property
GSSUtil.debug("Ignore the invalid property " +
prop + " from provider " + p.getName());
}
} // Processed GSS property
} // while loop
return retVal;
}
/**
* Stores a provider and a mechanism oid indicating that the
* provider should be used for the mechanism. If the mechanism
* Oid is null, then it indicates that this preference holds for
* any mechanism.<p>
*
* The ProviderList maintains an ordered list of
* PreferencesEntry's and iterates thru them as it tries to
* instantiate MechanismFactory's.
*/
private static final class PreferencesEntry {
private Provider p;
private Oid oid;
PreferencesEntry(Provider p, Oid oid) {
this.p = p;
this.oid = oid;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PreferencesEntry)) {
return false;
}
PreferencesEntry that = (PreferencesEntry)other;
if (this.p.getName().equals(that.p.getName())) {
if (this.oid != null && that.oid != null) {
return this.oid.equals(that.oid);
} else {
return (this.oid == null && that.oid == null);
}
}
return false;
}
public int hashCode() {
int result = 17;
result = 37 * result + p.getName().hashCode();
if (oid != null) {
result = 37 * result + oid.hashCode();
}
return result;
}
/**
* Determines if a preference implies another. A preference
* implies another if the latter is subsumed by the
* former. e.g., <Provider1, null> implies <Provider1, OidX>
* because the null in the former indicates that it should
* be used for all mechanisms.
*/
boolean implies(Object other) {
if (other instanceof PreferencesEntry) {
PreferencesEntry temp = (PreferencesEntry) other;
return (equals(temp) ||
p.getName().equals(temp.p.getName()) &&
oid == null);
} else {
return false;
}
}
Provider getProvider() {
return p;
}
Oid getOid() {
return oid;
}
/**
* Determines if this entry is applicable to the desired
* mechanism. The entry is applicable to the desired mech if
* it contains the same oid or if it contains a null oid
* indicating that it is applicable to all mechs.
* @param mechOid the desired mechanism
* @return true if the provider in this entry should be
* queried for this mechanism.
*/
boolean impliesMechanism(Oid oid) {
return (this.oid == null || this.oid.equals(oid));
}
// For debugging
public String toString() {
StringBuffer buf = new StringBuffer("<");
buf.append(p.getName());
buf.append(", ");
buf.append(oid);
buf.append(">");
return buf.toString();
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import java.security.Provider;
import java.security.AccessController;
/**
* Defines the Sun JGSS provider.
* Will merger this with the Sun security provider
* sun.security.provider.Sun when the JGSS src is merged with the JDK
* src.
*
* Mechanisms supported are:
*
* - Kerberos v5 as defined in RFC 1964.
* Oid is 1.2.840.113554.1.2.2
*
* - SPNEGO as defined in RFC 2478
* Oid is 1.3.6.1.5.5.2
*
* [Dummy mechanism is no longer compiled:
* - Dummy mechanism. This is primarily useful to test a multi-mech
* environment.
* Oid is 1.3.6.1.4.1.42.2.26.1.2]
*
* @author Mayank Upadhyay
*/
public final class SunProvider extends Provider {
private static final long serialVersionUID = -238911724858694198L;
private static final String INFO = "Sun " +
"(Kerberos v5, SPNEGO)";
// "(Kerberos v5, Dummy GSS-API Mechanism)";
public static final SunProvider INSTANCE = new SunProvider();
public SunProvider() {
/* We are the Sun JGSS provider */
super("SunJGSS", 1.8d, INFO);
AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
put("GssApiMechanism.1.2.840.113554.1.2.2",
"sun.security.jgss.krb5.Krb5MechFactory");
put("GssApiMechanism.1.3.6.1.5.5.2",
"sun.security.jgss.spnego.SpNegoMechFactory");
/*
put("GssApiMechanism.1.3.6.1.4.1.42.2.26.1.2",
"sun.security.jgss.dummy.DummyMechFactory");
*/
return null;
}
});
}
}

View File

@@ -0,0 +1,419 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.MessageProp;
import java.util.LinkedList;
/**
* A utility class that implements a number list that keeps track of which
* tokens have arrived by storing their token numbers in the list. It helps
* detect old tokens, out of sequence tokens, and duplicate tokens.
*
* Each element of the list is an interval [a, b]. Its existence in the
* list implies that all token numbers in the range a, a+1, ..., b-1, b
* have arrived. Gaps in arrived token numbers are represented by the
* numbers that fall in between two elements of the list. eg. {[a,b],
* [c,d]} indicates that the token numbers b+1, ..., c-1 have not arrived
* yet.
*
* The maximum number of intervals that we keep track of is
* MAX_INTERVALS. Thus if there are too many gaps, then some of the older
* sequence numbers are deleted from the list. The earliest sequence number
* that exists in the list is the windowStart. The next expected sequence
* number, or expectedNumber, is one greater than the latest sequence
* number in the list.
*
* The list keeps track the first token number that should have arrived
* (initNumber) so that it is able to detect if certain numbers occur after
* the first valid token number but before windowStart. That would happen
* if the number of elements (intervals) exceeds MAX_INTERVALS and some
* initial elements had to be deleted.
*
* The working of the list is optimized for the normal case where the
* tokens arrive in sequence.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class TokenTracker {
static final int MAX_INTERVALS = 5;
private int initNumber;
private int windowStart;
private int expectedNumber;
private int windowStartIndex = 0;
private LinkedList<Entry> list = new LinkedList<Entry>();
public TokenTracker(int initNumber) {
this.initNumber = initNumber;
this.windowStart = initNumber;
this.expectedNumber = initNumber;
// Make an entry with one less than the expected first token
Entry entry = new Entry(initNumber-1);
list.add(entry);
}
/**
* Returns the index for the entry into which this number will fit. If
* there is none, it returns the index of the last interval
* which precedes this number. It returns -1 if the number needs to be
* a in a new interval ahead of the whole list.
*/
private int getIntervalIndex(int number) {
Entry entry = null;
int i;
// Start from the rear to optimize for the normal case
for (i = list.size() - 1; i >= 0; i--) {
entry = list.get(i);
if (entry.compareTo(number) <= 0)
break;
}
return i;
}
/**
* Sets the sequencing and replay information for the given token
* number.
*
* The following represents the number line with positions of
* initNumber, windowStart, expectedNumber marked on it. Regions in
* between them show the different sequencing and replay state
* possibilites for tokens that fall in there.
*
* (1) windowStart
* initNumber expectedNumber
* | |
* ---|---------------------------|---
* GAP | DUP/UNSEQ | GAP
*
*
* (2) initNumber windowStart expectedNumber
* | | |
* ---|---------------|--------------|---
* GAP | OLD | DUP/UNSEQ | GAP
*
*
* (3) windowStart
* expectedNumber initNumber
* | |
* ---|---------------------------|---
* DUP/UNSEQ | GAP | DUP/UNSEQ
*
*
* (4) expectedNumber initNumber windowStart
* | | |
* ---|---------------|--------------|---
* DUP/UNSEQ | GAP | OLD | DUP/UNSEQ
*
*
*
* (5) windowStart expectedNumber initNumber
* | | |
* ---|---------------|--------------|---
* OLD | DUP/UNSEQ | GAP | OLD
*
*
*
* (This analysis leaves out the possibility that expectedNumber passes
* initNumber after wrapping around. That may be added later.)
*/
synchronized public final void getProps(int number, MessageProp prop) {
boolean gap = false;
boolean old = false;
boolean unsequenced = false;
boolean duplicate = false;
// System.out.println("\n\n==========");
// System.out.println("TokenTracker.getProps(): number=" + number);
// System.out.println(toString());
int pos = getIntervalIndex(number);
Entry entry = null;
if (pos != -1)
entry = list.get(pos);
// Optimize for the expected case:
if (number == expectedNumber) {
expectedNumber++;
} else {
// Next trivial case is to check for duplicate
if (entry != null && entry.contains(number))
duplicate = true;
else {
if (expectedNumber >= initNumber) {
// Cases (1) and (2)
if (number > expectedNumber) {
gap = true;
} else if (number >= windowStart) {
unsequenced = true;
} else if (number >= initNumber) {
old = true;
} else {
gap = true;
}
} else {
// Cases (3), (4) and (5)
if (number > expectedNumber) {
if (number < initNumber) {
gap = true;
} else if (windowStart >= initNumber) {
if (number >= windowStart) {
unsequenced = true;
} else
old = true;
} else {
old = true;
}
} else if (windowStart > expectedNumber) {
unsequenced = true;
} else if (number < windowStart) {
old = true;
} else
unsequenced = true;
}
}
}
if (!duplicate && !old)
add(number, pos);
if (gap)
expectedNumber = number+1;
prop.setSupplementaryStates(duplicate, old, unsequenced, gap,
0, null);
// System.out.println("Leaving with state:");
// System.out.println(toString());
// System.out.println("==========\n");
}
/**
* Adds the number to the list just after the entry that is currently
* at position prevEntryPos. If prevEntryPos is -1, then the number
* will begin a new interval at the front of the list.
*/
private void add(int number, int prevEntryPos) {
Entry entry;
Entry entryBefore = null;
Entry entryAfter = null;
boolean appended = false;
boolean prepended = false;
if (prevEntryPos != -1) {
entryBefore = list.get(prevEntryPos);
// Can this number simply be added to the previous interval?
if (number == (entryBefore.getEnd() + 1)) {
entryBefore.setEnd(number);
appended = true;
}
}
// Now check the interval that follows this number
int nextEntryPos = prevEntryPos + 1;
if ((nextEntryPos) < list.size()) {
entryAfter = list.get(nextEntryPos);
// Can this number simply be added to the next interval?
if (number == (entryAfter.getStart() - 1)) {
if (!appended) {
entryAfter.setStart(number);
} else {
// Merge the two entries
entryAfter.setStart(entryBefore.getStart());
list.remove(prevEntryPos);
// Index of any entry following this gets decremented
if (windowStartIndex > prevEntryPos)
windowStartIndex--;
}
prepended = true;
}
}
if (prepended || appended)
return;
/*
* At this point we know that the number will start a new interval
* which needs to be added to the list. We might have to recyle an
* older entry in the list.
*/
if (list.size() < MAX_INTERVALS) {
entry = new Entry(number);
if (prevEntryPos < windowStartIndex)
windowStartIndex++; // due to the insertion which will happen
} else {
/*
* Delete the entry that marks the start of the current window.
* The marker will automatically point to the next entry in the
* list when this happens. If the current entry is at the end
* of the list then set the marker to the start of the list.
*/
int oldWindowStartIndex = windowStartIndex;
if (windowStartIndex == (list.size() - 1))
windowStartIndex = 0;
entry = list.remove(oldWindowStartIndex);
windowStart = list.get(windowStartIndex).getStart();
entry.setStart(number);
entry.setEnd(number);
if (prevEntryPos >= oldWindowStartIndex) {
prevEntryPos--; // due to the deletion that just happened
} else {
/*
* If the start of the current window just moved from the
* end of the list to the front of the list, and if the new
* entry will be added to the front of the list, then
* the new entry is the actual window start.
* eg, Consider { [-10, -8], ..., [-6, -3], [3, 9]}. In
* this list, suppose the element [3, 9] is the start of
* the window and has to be deleted to make place to add
* [-12, -12]. The resultant list will be
* {[-12, -12], [-10, -8], ..., [-6, -3]} and the new start
* of the window should be the element [-12, -12], not
* [-10, -8] which succeeded [3, 9] in the old list.
*/
if (oldWindowStartIndex != windowStartIndex) {
// windowStartIndex is 0 at this point
if (prevEntryPos == -1)
// The new entry is going to the front
windowStart = number;
} else {
// due to the insertion which will happen:
windowStartIndex++;
}
}
}
// Finally we are ready to actually add to the list at index
// 'prevEntryPos+1'
list.add(prevEntryPos+1, entry);
}
public String toString() {
StringBuffer buf = new StringBuffer("TokenTracker: ");
buf.append(" initNumber=").append(initNumber);
buf.append(" windowStart=").append(windowStart);
buf.append(" expectedNumber=").append(expectedNumber);
buf.append(" windowStartIndex=").append(windowStartIndex);
buf.append("\n\tIntervals are: {");
for (int i = 0; i < list.size(); i++) {
if (i != 0)
buf.append(", ");
buf.append(list.get(i).toString());
}
buf.append('}');
return buf.toString();
}
/**
* An entry in the list that represents the sequence of received
* tokens. Each entry is actaully an interval of numbers, all of which
* have been received.
*/
class Entry {
private int start;
private int end;
Entry(int number) {
start = number;
end = number;
}
/**
* Returns -1 if this interval represented by this entry precedes
* the number, 0 if the the number is contained in the interval,
* and -1 if the interval occurs after the number.
*/
final int compareTo(int number) {
if (start > number)
return 1;
else if (end < number)
return -1;
else
return 0;
}
final boolean contains(int number) {
return (number >= start &&
number <= end);
}
final void append(int number) {
if (number == (end + 1))
end = number;
}
final void setInterval(int start, int end) {
this.start = start;
this.end = end;
}
final void setEnd(int end) {
this.end = end;
}
final void setStart(int start) {
this.start = start;
}
final int getStart() {
return start;
}
final int getEnd() {
return end;
}
public String toString() {
return ("[" + start + ", " + end + "]");
}
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.IOException;
import java.security.AccessController;
import sun.security.action.GetBooleanAction;
import sun.security.krb5.*;
class AcceptSecContextToken extends InitialToken {
private KrbApRep apRep = null;
/**
* Creates an AcceptSecContextToken for the context acceptor to send to
* the context initiator.
*/
public AcceptSecContextToken(Krb5Context context,
KrbApReq apReq)
throws KrbException, IOException, GSSException {
boolean useSubkey = AccessController.doPrivileged(
new GetBooleanAction("sun.security.krb5.acceptor.subkey"));
boolean useSequenceNumber = true;
EncryptionKey subKey = null;
if (useSubkey) {
subKey = new EncryptionKey(apReq.getCreds().getSessionKey());
context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey);
}
apRep = new KrbApRep(apReq, useSequenceNumber, subKey);
context.resetMySequenceNumber(apRep.getSeqNumber().intValue());
/*
* Note: The acceptor side context key was set when the
* InitSecContextToken was received.
*/
}
/**
* Creates an AcceptSecContextToken at the context initiator's side
* using the bytes received from the acceptor.
*/
public AcceptSecContextToken(Krb5Context context,
Credentials serviceCreds, KrbApReq apReq,
InputStream is)
throws IOException, GSSException, KrbException {
int tokenId = ((is.read()<<8) | is.read());
if (tokenId != Krb5Token.AP_REP_ID)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"AP_REP token id does not match!");
byte[] apRepBytes =
new sun.security.util.DerValue(is).toByteArray();
KrbApRep apRep = new KrbApRep(apRepBytes, serviceCreds, apReq);
/*
* Allow the context acceptor to set a subkey if desired, even
* though our context acceptor will not do so.
*/
EncryptionKey subKey = apRep.getSubKey();
if (subKey != null) {
context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey);
/*
System.out.println("\n\nSub-Session key from AP-REP is: " +
getHexBytes(subKey.getBytes()) + "\n");
*/
}
Integer apRepSeqNumber = apRep.getSeqNumber();
int peerSeqNumber = (apRepSeqNumber != null ?
apRepSeqNumber.intValue() :
0);
context.resetPeerSequenceNumber(peerSeqNumber);
}
public final byte[] encode() throws IOException {
byte[] apRepBytes = apRep.getMessage();
byte[] retVal = new byte[2 + apRepBytes.length];
writeInt(Krb5Token.AP_REP_ID, retVal, 0);
System.arraycopy(apRepBytes, 0, retVal, 2, apRepBytes.length);
return retVal;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,215 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import com.sun.security.jgss.AuthorizationDataEntry;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.IOException;
import sun.security.action.GetPropertyAction;
import sun.security.krb5.*;
import java.net.InetAddress;
import sun.security.krb5.internal.AuthorizationData;
import sun.security.krb5.internal.KerberosTime;
class InitSecContextToken extends InitialToken {
// If non-mutual authentication is requested, there is no AP-REP message.
// The acceptor thus has no chance to send the seq-number field to the
// initiator. In this case, the initiator and acceptor should has an
// agreement to derive acceptor's initial seq-number if the acceptor wishes
// to send messages to the initiator.
// If this flag is true, it will the same as the initiator's initial
// seq-number (as MIT krb5 and Windows SSPI do). Otherwise, it will be zero
// (as Heimdal does). The default value is true.
private static final boolean ACCEPTOR_USE_INITIATOR_SEQNUM;
static {
// The ACCEPTOR_USE_INITIATOR_SEQNUM value is determined by the system
// property "sun.security.krb5.acceptor.sequence.number.nonmutual",
// which can be set to "initiator", "zero" or "0".
String propName = "sun.security.krb5.acceptor.sequence.number.nonmutual";
String s = GetPropertyAction.privilegedGetProperty(propName, "initiator");
if (s.equals("initiator")) {
ACCEPTOR_USE_INITIATOR_SEQNUM = true;
} else if (s.equals("zero") || s.equals("0")) {
ACCEPTOR_USE_INITIATOR_SEQNUM = false;
} else {
throw new AssertionError("Unrecognized value for " + propName
+ ": " + s);
}
}
private KrbApReq apReq = null;
/**
* For the context initiator to call. It constructs a new
* InitSecContextToken to send over to the peer containing the desired
* flags and the AP-REQ. It also updates the context with the local
* sequence number and shared context key.
* (When mutual auth is enabled the peer has an opportunity to
* renegotiate the session key in the followup AcceptSecContextToken
* that it sends.)
*/
InitSecContextToken(Krb5Context context,
Credentials tgt,
Credentials serviceTicket)
throws KrbException, IOException, GSSException {
boolean mutualRequired = context.getMutualAuthState();
boolean useSubkey = true; // MIT Impl will crash if this is not set!
boolean useSequenceNumber = true;
OverloadedChecksum gssChecksum =
new OverloadedChecksum(context, tgt, serviceTicket);
Checksum checksum = gssChecksum.getChecksum();
context.setTktFlags(serviceTicket.getFlags());
context.setAuthTime(
new KerberosTime(serviceTicket.getAuthTime()).toString());
apReq = new KrbApReq(serviceTicket,
mutualRequired,
useSubkey,
useSequenceNumber,
checksum);
context.resetMySequenceNumber(apReq.getSeqNumber().intValue());
EncryptionKey subKey = apReq.getSubKey();
if (subKey != null)
context.setKey(Krb5Context.INITIATOR_SUBKEY, subKey);
else
context.setKey(Krb5Context.SESSION_KEY, serviceTicket.getSessionKey());
if (!mutualRequired)
context.resetPeerSequenceNumber(
ACCEPTOR_USE_INITIATOR_SEQNUM
? apReq.getSeqNumber().intValue()
: 0);
}
/**
* For the context acceptor to call. It reads the bytes out of an
* InputStream and constructs an InitSecContextToken with them.
*/
InitSecContextToken(Krb5Context context, Krb5AcceptCredential cred,
InputStream is)
throws IOException, GSSException, KrbException {
int tokenId = ((is.read()<<8) | is.read());
if (tokenId != Krb5Token.AP_REQ_ID)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"AP_REQ token id does not match!");
// XXX Modify KrbApReq cons to take an InputStream
byte[] apReqBytes =
new sun.security.util.DerValue(is).toByteArray();
//debug("=====ApReqBytes: [" + getHexBytes(apReqBytes) + "]\n");
InetAddress addr = null;
if (context.getChannelBinding() != null) {
addr = context.getChannelBinding().getInitiatorAddress();
}
apReq = new KrbApReq(apReqBytes, cred, addr);
//debug("\nReceived AP-REQ and authenticated it.\n");
EncryptionKey sessionKey = apReq.getCreds().getSessionKey();
/*
System.out.println("\n\nSession key from service ticket is: " +
getHexBytes(sessionKey.getBytes()));
*/
EncryptionKey subKey = apReq.getSubKey();
if (subKey != null) {
context.setKey(Krb5Context.INITIATOR_SUBKEY, subKey);
/*
System.out.println("Sub-Session key from authenticator is: " +
getHexBytes(subKey.getBytes()) + "\n");
*/
} else {
context.setKey(Krb5Context.SESSION_KEY, sessionKey);
//System.out.println("Sub-Session Key Missing in Authenticator.\n");
}
OverloadedChecksum gssChecksum = new OverloadedChecksum(
context, apReq.getChecksum(), sessionKey, subKey);
gssChecksum.setContextFlags(context);
Credentials delegCred = gssChecksum.getDelegatedCreds();
if (delegCred != null) {
Krb5CredElement credElement =
Krb5InitCredential.getInstance(
(Krb5NameElement)context.getSrcName(),
delegCred);
context.setDelegCred(credElement);
}
Integer apReqSeqNumber = apReq.getSeqNumber();
int peerSeqNumber = (apReqSeqNumber != null ?
apReqSeqNumber.intValue() :
0);
context.resetPeerSequenceNumber(peerSeqNumber);
if (!context.getMutualAuthState()) {
context.resetMySequenceNumber(
ACCEPTOR_USE_INITIATOR_SEQNUM
? peerSeqNumber
: 0);
}
context.setAuthTime(
new KerberosTime(apReq.getCreds().getAuthTime()).toString());
context.setTktFlags(apReq.getCreds().getFlags());
AuthorizationData ad = apReq.getCreds().getAuthzData();
if (ad == null) {
context.setAuthzData(null);
} else {
AuthorizationDataEntry[] authzData =
new AuthorizationDataEntry[ad.count()];
for (int i=0; i<ad.count(); i++) {
authzData[i] = new AuthorizationDataEntry(
ad.item(i).adType, ad.item(i).adData);
}
context.setAuthzData(authzData);
}
}
public final KrbApReq getKrbApReq() {
return apReq;
}
public final byte[] encode() throws IOException {
byte[] apReqBytes = apReq.getMessage();
byte[] retVal = new byte[2 + apReqBytes.length];
writeInt(Krb5Token.AP_REQ_ID, retVal, 0);
System.arraycopy(apReqBytes, 0, retVal, 2, apReqBytes.length);
// System.out.println("GSS-Token with AP_REQ is:");
// System.out.println(getHexBytes(retVal));
return retVal;
}
}

View File

@@ -0,0 +1,453 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import javax.security.auth.kerberos.DelegationPermission;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import sun.security.krb5.*;
import sun.security.krb5.internal.Krb5;
abstract class InitialToken extends Krb5Token {
private static final int CHECKSUM_TYPE = 0x8003;
private static final int CHECKSUM_LENGTH_SIZE = 4;
private static final int CHECKSUM_BINDINGS_SIZE = 16;
private static final int CHECKSUM_FLAGS_SIZE = 4;
private static final int CHECKSUM_DELEG_OPT_SIZE = 2;
private static final int CHECKSUM_DELEG_LGTH_SIZE = 2;
private static final int CHECKSUM_DELEG_FLAG = 1;
private static final int CHECKSUM_MUTUAL_FLAG = 2;
private static final int CHECKSUM_REPLAY_FLAG = 4;
private static final int CHECKSUM_SEQUENCE_FLAG = 8;
private static final int CHECKSUM_CONF_FLAG = 16;
private static final int CHECKSUM_INTEG_FLAG = 32;
private final byte[] CHECKSUM_FIRST_BYTES =
{(byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00};
private static final int CHANNEL_BINDING_AF_INET = 2;
private static final int CHANNEL_BINDING_AF_INET6 = 24;
private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255;
private static final int Inet4_ADDRSZ = 4;
private static final int Inet6_ADDRSZ = 16;
protected class OverloadedChecksum {
private byte[] checksumBytes = null;
private Credentials delegCreds = null;
private int flags = 0;
/**
* Called on the initiator side when creating the
* InitSecContextToken.
*/
public OverloadedChecksum(Krb5Context context,
Credentials tgt,
Credentials serviceTicket)
throws KrbException, IOException, GSSException {
byte[] krbCredMessage = null;
int pos = 0;
int size = CHECKSUM_LENGTH_SIZE + CHECKSUM_BINDINGS_SIZE +
CHECKSUM_FLAGS_SIZE;
if (!tgt.isForwardable()) {
context.setCredDelegState(false);
context.setDelegPolicyState(false);
} else if (context.getCredDelegState()) {
if (context.getDelegPolicyState()) {
if (!serviceTicket.checkDelegate()) {
// delegation not permitted by server policy, mark it
context.setDelegPolicyState(false);
}
}
} else if (context.getDelegPolicyState()) {
if (serviceTicket.checkDelegate()) {
context.setCredDelegState(true);
} else {
context.setDelegPolicyState(false);
}
}
if (context.getCredDelegState()) {
KrbCred krbCred = null;
CipherHelper cipherHelper =
context.getCipherHelper(serviceTicket.getSessionKey());
if (useNullKey(cipherHelper)) {
krbCred = new KrbCred(tgt, serviceTicket,
EncryptionKey.NULL_KEY);
} else {
krbCred = new KrbCred(tgt, serviceTicket,
serviceTicket.getSessionKey());
}
krbCredMessage = krbCred.getMessage();
size += CHECKSUM_DELEG_OPT_SIZE +
CHECKSUM_DELEG_LGTH_SIZE +
krbCredMessage.length;
}
checksumBytes = new byte[size];
checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[0];
checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[1];
checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[2];
checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[3];
ChannelBinding localBindings = context.getChannelBinding();
if (localBindings != null) {
byte[] localBindingsBytes =
computeChannelBinding(context.getChannelBinding());
System.arraycopy(localBindingsBytes, 0,
checksumBytes, pos, localBindingsBytes.length);
// System.out.println("ChannelBinding hash: "
// + getHexBytes(localBindingsBytes));
}
pos += CHECKSUM_BINDINGS_SIZE;
if (context.getCredDelegState())
flags |= CHECKSUM_DELEG_FLAG;
if (context.getMutualAuthState())
flags |= CHECKSUM_MUTUAL_FLAG;
if (context.getReplayDetState())
flags |= CHECKSUM_REPLAY_FLAG;
if (context.getSequenceDetState())
flags |= CHECKSUM_SEQUENCE_FLAG;
if (context.getIntegState())
flags |= CHECKSUM_INTEG_FLAG;
if (context.getConfState())
flags |= CHECKSUM_CONF_FLAG;
byte[] temp = new byte[4];
writeLittleEndian(flags, temp);
checksumBytes[pos++] = temp[0];
checksumBytes[pos++] = temp[1];
checksumBytes[pos++] = temp[2];
checksumBytes[pos++] = temp[3];
if (context.getCredDelegState()) {
PrincipalName delegateTo =
serviceTicket.getServer();
// Cannot use '\"' instead of "\"" in constructor because
// it is interpreted as suggested length!
StringBuffer buf = new StringBuffer("\"");
buf.append(delegateTo.getName()).append('\"');
String realm = delegateTo.getRealmAsString();
buf.append(" \"krbtgt/").append(realm).append('@');
buf.append(realm).append('\"');
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
DelegationPermission perm =
new DelegationPermission(buf.toString());
sm.checkPermission(perm);
}
/*
* Write 1 in little endian but in two bytes
* for DlgOpt
*/
checksumBytes[pos++] = (byte)0x01;
checksumBytes[pos++] = (byte)0x00;
/*
* Write the length of the delegated credential in little
* endian but in two bytes for Dlgth
*/
if (krbCredMessage.length > 0x0000ffff)
throw new GSSException(GSSException.FAILURE, -1,
"Incorrect message length");
writeLittleEndian(krbCredMessage.length, temp);
checksumBytes[pos++] = temp[0];
checksumBytes[pos++] = temp[1];
System.arraycopy(krbCredMessage, 0,
checksumBytes, pos, krbCredMessage.length);
}
}
/**
* Called on the acceptor side when reading an InitSecContextToken.
*/
// XXX Passing in Checksum is not required. byte[] can
// be passed in if this checksum type denotes a
// raw_checksum. In that case, make Checksum class krb5
// internal.
public OverloadedChecksum(Krb5Context context, Checksum checksum,
EncryptionKey key, EncryptionKey subKey)
throws GSSException, KrbException, IOException {
int pos = 0;
if (checksum == null) {
GSSException ge = new GSSException(GSSException.FAILURE, -1,
"No cksum in AP_REQ's authenticator");
ge.initCause(new KrbException(Krb5.KRB_AP_ERR_INAPP_CKSUM));
throw ge;
}
checksumBytes = checksum.getBytes();
if ((checksumBytes[0] != CHECKSUM_FIRST_BYTES[0]) ||
(checksumBytes[1] != CHECKSUM_FIRST_BYTES[1]) ||
(checksumBytes[2] != CHECKSUM_FIRST_BYTES[2]) ||
(checksumBytes[3] != CHECKSUM_FIRST_BYTES[3])) {
throw new GSSException(GSSException.FAILURE, -1,
"Incorrect checksum");
}
ChannelBinding localBindings = context.getChannelBinding();
// Ignore remote channel binding info when not requested at
// local side (RFC 4121 4.1.1.2: the acceptor MAY ignore...).
//
// All major krb5 implementors implement this "MAY",
// and some applications depend on it as a workaround
// for not having a way to negotiate the use of channel
// binding -- the initiator application always uses CB
// and hopes the acceptor will ignore the CB if the
// acceptor doesn't support CB.
if (localBindings != null) {
byte[] remoteBindingBytes = new byte[CHECKSUM_BINDINGS_SIZE];
System.arraycopy(checksumBytes, 4, remoteBindingBytes, 0,
CHECKSUM_BINDINGS_SIZE);
byte[] noBindings = new byte[CHECKSUM_BINDINGS_SIZE];
if (!Arrays.equals(noBindings, remoteBindingBytes)) {
byte[] localBindingsBytes =
computeChannelBinding(localBindings);
if (!Arrays.equals(localBindingsBytes,
remoteBindingBytes)) {
throw new GSSException(GSSException.BAD_BINDINGS, -1,
"Bytes mismatch!");
}
} else {
throw new GSSException(GSSException.BAD_BINDINGS, -1,
"Token missing ChannelBinding!");
}
}
flags = readLittleEndian(checksumBytes, 20, 4);
if ((flags & CHECKSUM_DELEG_FLAG) > 0) {
/*
* XXX
* if ((checksumBytes[24] != (byte)0x01) &&
* (checksumBytes[25] != (byte)0x00))
*/
int credLen = readLittleEndian(checksumBytes, 26, 2);
byte[] credBytes = new byte[credLen];
System.arraycopy(checksumBytes, 28, credBytes, 0, credLen);
KrbCred cred;
try {
cred = new KrbCred(credBytes, key);
} catch (KrbException ke) {
if (subKey != null) {
cred = new KrbCred(credBytes, subKey);
} else {
throw ke;
}
}
delegCreds = cred.getDelegatedCreds()[0];
}
}
// check if KRB-CRED message should use NULL_KEY for encryption
private boolean useNullKey(CipherHelper ch) {
boolean flag = true;
// for "newer" etypes and RC4-HMAC do not use NULL KEY
if ((ch.getProto() == 1) || ch.isArcFour()) {
flag = false;
}
return flag;
}
public Checksum getChecksum() throws KrbException {
return new Checksum(checksumBytes, CHECKSUM_TYPE);
}
public Credentials getDelegatedCreds() {
return delegCreds;
}
// Only called by acceptor
public void setContextFlags(Krb5Context context) {
// default for cred delegation is false
if ((flags & CHECKSUM_DELEG_FLAG) > 0)
context.setCredDelegState(true);
// default for the following are true
if ((flags & CHECKSUM_MUTUAL_FLAG) == 0) {
context.setMutualAuthState(false);
}
if ((flags & CHECKSUM_REPLAY_FLAG) == 0) {
context.setReplayDetState(false);
}
if ((flags & CHECKSUM_SEQUENCE_FLAG) == 0) {
context.setSequenceDetState(false);
}
if ((flags & CHECKSUM_CONF_FLAG) == 0) {
context.setConfState(false);
}
if ((flags & CHECKSUM_INTEG_FLAG) == 0) {
context.setIntegState(false);
}
}
}
private int getAddrType(InetAddress addr) {
int addressType = CHANNEL_BINDING_AF_NULL_ADDR;
if (addr instanceof Inet4Address)
addressType = CHANNEL_BINDING_AF_INET;
else if (addr instanceof Inet6Address)
addressType = CHANNEL_BINDING_AF_INET6;
return (addressType);
}
private byte[] getAddrBytes(InetAddress addr) throws GSSException {
int addressType = getAddrType(addr);
byte[] addressBytes = addr.getAddress();
if (addressBytes != null) {
switch (addressType) {
case CHANNEL_BINDING_AF_INET:
if (addressBytes.length != Inet4_ADDRSZ) {
throw new GSSException(GSSException.FAILURE, -1,
"Incorrect AF-INET address length in ChannelBinding.");
}
return (addressBytes);
case CHANNEL_BINDING_AF_INET6:
if (addressBytes.length != Inet6_ADDRSZ) {
throw new GSSException(GSSException.FAILURE, -1,
"Incorrect AF-INET6 address length in ChannelBinding.");
}
return (addressBytes);
default:
throw new GSSException(GSSException.FAILURE, -1,
"Cannot handle non AF-INET addresses in ChannelBinding.");
}
}
return null;
}
private byte[] computeChannelBinding(ChannelBinding channelBinding)
throws GSSException {
InetAddress initiatorAddress = channelBinding.getInitiatorAddress();
InetAddress acceptorAddress = channelBinding.getAcceptorAddress();
int size = 5*4;
int initiatorAddressType = getAddrType(initiatorAddress);
int acceptorAddressType = getAddrType(acceptorAddress);
byte[] initiatorAddressBytes = null;
if (initiatorAddress != null) {
initiatorAddressBytes = getAddrBytes(initiatorAddress);
size += initiatorAddressBytes.length;
}
byte[] acceptorAddressBytes = null;
if (acceptorAddress != null) {
acceptorAddressBytes = getAddrBytes(acceptorAddress);
size += acceptorAddressBytes.length;
}
byte[] appDataBytes = channelBinding.getApplicationData();
if (appDataBytes != null) {
size += appDataBytes.length;
}
byte[] data = new byte[size];
int pos = 0;
writeLittleEndian(initiatorAddressType, data, pos);
pos += 4;
if (initiatorAddressBytes != null) {
writeLittleEndian(initiatorAddressBytes.length, data, pos);
pos += 4;
System.arraycopy(initiatorAddressBytes, 0,
data, pos, initiatorAddressBytes.length);
pos += initiatorAddressBytes.length;
} else {
// Write length 0
pos += 4;
}
writeLittleEndian(acceptorAddressType, data, pos);
pos += 4;
if (acceptorAddressBytes != null) {
writeLittleEndian(acceptorAddressBytes.length, data, pos);
pos += 4;
System.arraycopy(acceptorAddressBytes, 0,
data, pos, acceptorAddressBytes.length);
pos += acceptorAddressBytes.length;
} else {
// Write length 0
pos += 4;
}
if (appDataBytes != null) {
writeLittleEndian(appDataBytes.length, data, pos);
pos += 4;
System.arraycopy(appDataBytes, 0, data, pos,
appDataBytes.length);
pos += appDataBytes.length;
} else {
// Write 0
pos += 4;
}
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
return md5.digest(data);
} catch (NoSuchAlgorithmException e) {
throw new GSSException(GSSException.FAILURE, -1,
"Could not get MD5 Message Digest - "
+ e.getMessage());
}
}
public abstract byte[] encode() throws IOException;
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import java.io.IOException;
import org.ietf.jgss.*;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
import sun.security.krb5.*;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.AccessController;
import java.security.AccessControlContext;
import javax.security.auth.DestroyFailedException;
/**
* Implements the krb5 acceptor credential element.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class Krb5AcceptCredential
implements Krb5CredElement {
private final Krb5NameElement name;
private final ServiceCreds screds;
private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) {
/*
* Initialize this instance with the data from the acquired
* KerberosKey. This class needs to be a KerberosKey too
* hence we can't just store a reference.
*/
this.name = name;
this.screds = creds;
}
static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name)
throws GSSException {
final String serverPrinc = (name == null? null:
name.getKrb5PrincipalName().getName());
final AccessControlContext acc = AccessController.getContext();
ServiceCreds creds = null;
try {
creds = AccessController.doPrivileged(
new PrivilegedExceptionAction<ServiceCreds>() {
public ServiceCreds run() throws Exception {
return Krb5Util.getServiceCreds(
caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller,
serverPrinc, acc);
}});
} catch (PrivilegedActionException e) {
GSSException ge =
new GSSException(GSSException.NO_CRED, -1,
"Attempt to obtain new ACCEPT credentials failed!");
ge.initCause(e.getException());
throw ge;
}
if (creds == null)
throw new GSSException(GSSException.NO_CRED, -1,
"Failed to find any Kerberos credentails");
if (name == null) {
String fullName = creds.getName();
if (fullName != null) {
name = Krb5NameElement.getInstance(fullName,
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
}
}
return new Krb5AcceptCredential(name, creds);
}
/**
* Returns the principal name for this credential. The name
* is in mechanism specific format.
*
* @return GSSNameSpi representing principal name of this credential
* @exception GSSException may be thrown
*/
public final GSSNameSpi getName() throws GSSException {
return name;
}
/**
* Returns the init lifetime remaining.
*
* @return the init lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getInitLifetime() throws GSSException {
return 0;
}
/**
* Returns the accept lifetime remaining.
*
* @return the accept lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getAcceptLifetime() throws GSSException {
return GSSCredential.INDEFINITE_LIFETIME;
}
public boolean isInitiatorCredential() throws GSSException {
return false;
}
public boolean isAcceptorCredential() throws GSSException {
return true;
}
/**
* Returns the oid representing the underlying credential
* mechanism oid.
*
* @return the Oid for this credential mechanism
* @exception GSSException may be thrown
*/
public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
public EncryptionKey[] getKrb5EncryptionKeys(PrincipalName princ) {
return screds.getEKeys(princ);
}
/**
* Called to invalidate this credential element.
*/
public void dispose() throws GSSException {
try {
destroy();
} catch (DestroyFailedException e) {
GSSException gssException =
new GSSException(GSSException.FAILURE, -1,
"Could not destroy credentials - " + e.getMessage());
gssException.initCause(e);
}
}
/**
* Destroys the locally cached EncryptionKey value and then calls
* destroy in the base class.
*/
public void destroy() throws DestroyFailedException {
screds.destroy();
}
/**
* Impersonation is only available on the initiator side. The
* service must starts as an initiator to get an initial TGT to complete
* the S4U2self protocol.
*/
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
Credentials cred = screds.getInitCred();
if (cred != null) {
return Krb5InitCredential.getInstance(this.name, cred)
.impersonate(name);
} else {
throw new GSSException(GSSException.FAILURE, -1,
"Only an initiate credentials can impersonate");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2000, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.krb5.*;
import java.security.Provider;
/**
* Provides type safety for Krb5 credential elements.
*
* @author Mayank Upadhyay
* @since 1.4
*/
interface Krb5CredElement
extends GSSCredentialSpi {
}

View File

@@ -0,0 +1,406 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
import sun.security.krb5.*;
import sun.security.krb5.Config;
import javax.security.auth.kerberos.*;
import java.net.InetAddress;
import java.io.IOException;
import java.util.Date;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
/**
* Implements the krb5 initiator credential element.
*
* @author Mayank Upadhyay
* @author Ram Marti
* @since 1.4
*/
public class Krb5InitCredential
extends KerberosTicket
implements Krb5CredElement {
private static final long serialVersionUID = 7723415700837898232L;
private Krb5NameElement name;
private Credentials krb5Credentials;
public KerberosTicket proxyTicket;
private Krb5InitCredential(Krb5NameElement name,
byte[] asn1Encoding,
KerberosPrincipal client,
KerberosPrincipal clientAlias,
KerberosPrincipal server,
KerberosPrincipal serverAlias,
byte[] sessionKey,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] clientAddresses)
throws GSSException {
super(asn1Encoding,
client,
server,
sessionKey,
keyType,
flags,
authTime,
startTime,
endTime,
renewTill,
clientAddresses);
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketSetClientAlias(this, clientAlias);
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketSetServerAlias(this, serverAlias);
this.name = name;
try {
// Cache this for later use by the sun.security.krb5 package.
krb5Credentials = new Credentials(asn1Encoding,
client.getName(),
(clientAlias != null ?
clientAlias.getName() : null),
server.getName(),
(serverAlias != null ?
serverAlias.getName() : null),
sessionKey,
keyType,
flags,
authTime,
startTime,
endTime,
renewTill,
clientAddresses);
} catch (KrbException e) {
throw new GSSException(GSSException.NO_CRED, -1,
e.getMessage());
} catch (IOException e) {
throw new GSSException(GSSException.NO_CRED, -1,
e.getMessage());
}
}
private Krb5InitCredential(Krb5NameElement name,
Credentials delegatedCred,
byte[] asn1Encoding,
KerberosPrincipal client,
KerberosPrincipal clientAlias,
KerberosPrincipal server,
KerberosPrincipal serverAlias,
byte[] sessionKey,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] clientAddresses)
throws GSSException {
super(asn1Encoding,
client,
server,
sessionKey,
keyType,
flags,
authTime,
startTime,
endTime,
renewTill,
clientAddresses);
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketSetClientAlias(this, clientAlias);
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketSetServerAlias(this, serverAlias);
this.name = name;
// A delegated cred does not have all fields set. So do not try to
// creat new Credentials out of the delegatedCred.
this.krb5Credentials = delegatedCred;
}
static Krb5InitCredential getInstance(GSSCaller caller, Krb5NameElement name,
int initLifetime)
throws GSSException {
KerberosTicket tgt = getTgt(caller, name, initLifetime);
if (tgt == null)
throw new GSSException(GSSException.NO_CRED, -1,
"Failed to find any Kerberos tgt");
if (name == null) {
String fullName = tgt.getClient().getName();
name = Krb5NameElement.getInstance(fullName,
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
}
KerberosPrincipal clientAlias = KerberosSecrets
.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketGetClientAlias(tgt);
KerberosPrincipal serverAlias = KerberosSecrets
.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketGetServerAlias(tgt);
Krb5InitCredential result = new Krb5InitCredential(name,
tgt.getEncoded(),
tgt.getClient(),
clientAlias,
tgt.getServer(),
serverAlias,
tgt.getSessionKey().getEncoded(),
tgt.getSessionKeyType(),
tgt.getFlags(),
tgt.getAuthTime(),
tgt.getStartTime(),
tgt.getEndTime(),
tgt.getRenewTill(),
tgt.getClientAddresses());
result.proxyTicket = KerberosSecrets.getJavaxSecurityAuthKerberosAccess().
kerberosTicketGetProxy(tgt);
return result;
}
static Krb5InitCredential getInstance(Krb5NameElement name,
Credentials delegatedCred)
throws GSSException {
EncryptionKey sessionKey = delegatedCred.getSessionKey();
/*
* all of the following data is optional in a KRB-CRED
* messages. This check for each field.
*/
PrincipalName cPrinc = delegatedCred.getClient();
PrincipalName cAPrinc = delegatedCred.getClientAlias();
PrincipalName sPrinc = delegatedCred.getServer();
PrincipalName sAPrinc = delegatedCred.getServerAlias();
KerberosPrincipal client = null;
KerberosPrincipal clientAlias = null;
KerberosPrincipal server = null;
KerberosPrincipal serverAlias = null;
Krb5NameElement credName = null;
if (cPrinc != null) {
String fullName = cPrinc.getName();
credName = Krb5NameElement.getInstance(fullName,
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
client = new KerberosPrincipal(fullName);
}
if (cAPrinc != null) {
clientAlias = new KerberosPrincipal(cAPrinc.getName());
}
// XXX Compare name to credName
if (sPrinc != null) {
server =
new KerberosPrincipal(sPrinc.getName(),
KerberosPrincipal.KRB_NT_SRV_INST);
}
if (sAPrinc != null) {
serverAlias = new KerberosPrincipal(sAPrinc.getName());
}
return new Krb5InitCredential(credName,
delegatedCred,
delegatedCred.getEncoded(),
client,
clientAlias,
server,
serverAlias,
sessionKey.getBytes(),
sessionKey.getEType(),
delegatedCred.getFlags(),
delegatedCred.getAuthTime(),
delegatedCred.getStartTime(),
delegatedCred.getEndTime(),
delegatedCred.getRenewTill(),
delegatedCred.getClientAddresses());
}
/**
* Returns the principal name for this credential. The name
* is in mechanism specific format.
*
* @return GSSNameSpi representing principal name of this credential
* @exception GSSException may be thrown
*/
public final GSSNameSpi getName() throws GSSException {
return name;
}
/**
* Returns the init lifetime remaining.
*
* @return the init lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getInitLifetime() throws GSSException {
Date d = getEndTime();
if (d == null) {
return 0;
}
long retVal = d.getTime() - System.currentTimeMillis();
return (int)(retVal/1000);
}
/**
* Returns the accept lifetime remaining.
*
* @return the accept lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getAcceptLifetime() throws GSSException {
return 0;
}
public boolean isInitiatorCredential() throws GSSException {
return true;
}
public boolean isAcceptorCredential() throws GSSException {
return false;
}
/**
* Returns the oid representing the underlying credential
* mechanism oid.
*
* @return the Oid for this credential mechanism
* @exception GSSException may be thrown
*/
public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
/**
* Returns a sun.security.krb5.Credentials instance so that it maybe
* used in that package for th Kerberos protocol.
*/
Credentials getKrb5Credentials() {
return krb5Credentials;
}
/*
* XXX Call to this.refresh() should refresh the locally cached copy
* of krb5Credentials also.
*/
/**
* Called to invalidate this credential element.
*/
public void dispose() throws GSSException {
try {
destroy();
} catch (javax.security.auth.DestroyFailedException e) {
GSSException gssException =
new GSSException(GSSException.FAILURE, -1,
"Could not destroy credentials - " + e.getMessage());
gssException.initCause(e);
}
}
// XXX call to this.destroy() should destroy the locally cached copy
// of krb5Credentials and then call super.destroy().
private static KerberosTicket getTgt(GSSCaller caller, Krb5NameElement name,
int initLifetime)
throws GSSException {
final String clientPrincipal;
/*
* Find the TGT for the realm that the client is in. If the client
* name is not available, then use the default realm.
*/
if (name != null) {
clientPrincipal = (name.getKrb5PrincipalName()).getName();
} else {
clientPrincipal = null;
}
final AccessControlContext acc = AccessController.getContext();
try {
final GSSCaller realCaller = (caller == GSSCaller.CALLER_UNKNOWN)
? GSSCaller.CALLER_INITIATE
: caller;
return AccessController.doPrivileged(
new PrivilegedExceptionAction<KerberosTicket>() {
public KerberosTicket run() throws Exception {
// It's OK to use null as serverPrincipal. TGT is almost
// the first ticket for a principal and we use list.
return Krb5Util.getInitialTicket(
realCaller,
clientPrincipal, acc);
}});
} catch (PrivilegedActionException e) {
GSSException ge =
new GSSException(GSSException.NO_CRED, -1,
"Attempt to obtain new INITIATE credentials failed!" +
" (" + e.getMessage() + ")");
ge.initCause(e.getException());
throw ge;
}
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
try {
Krb5NameElement kname = (Krb5NameElement)name;
Credentials newCred = Credentials.acquireS4U2selfCreds(
kname.getKrb5PrincipalName(), krb5Credentials);
return new Krb5ProxyCredential(this, kname, newCred.getTicket());
} catch (IOException | KrbException ke) {
GSSException ge =
new GSSException(GSSException.FAILURE, -1,
"Attempt to obtain S4U2self credentials failed!");
ge.initCause(ke);
throw ge;
}
}
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
import javax.security.auth.kerberos.ServicePermission;
import java.security.Provider;
import java.util.Vector;
/**
* Krb5 Mechanism plug in for JGSS
* This is the properties object required by the JGSS framework.
* All mechanism specific information is defined here.
*
* @author Mayank Upadhyay
*/
public final class Krb5MechFactory implements MechanismFactory {
private static final boolean DEBUG = Krb5Util.DEBUG;
static final Provider PROVIDER =
new sun.security.jgss.SunProvider();
static final Oid GSS_KRB5_MECH_OID =
createOid("1.2.840.113554.1.2.2");
static final Oid NT_GSS_KRB5_PRINCIPAL =
createOid("1.2.840.113554.1.2.2.1");
private static Oid[] nameTypes =
new Oid[] { GSSName.NT_USER_NAME,
GSSName.NT_HOSTBASED_SERVICE,
GSSName.NT_EXPORT_NAME,
NT_GSS_KRB5_PRINCIPAL};
final private GSSCaller caller;
private static Krb5CredElement getCredFromSubject(GSSNameSpi name,
boolean initiate)
throws GSSException {
Vector<Krb5CredElement> creds =
GSSUtil.searchSubject(name, GSS_KRB5_MECH_OID, initiate,
(initiate ?
Krb5InitCredential.class :
Krb5AcceptCredential.class));
Krb5CredElement result = ((creds == null || creds.isEmpty()) ?
null : creds.firstElement());
// Force permission check before returning the cred to caller
if (result != null) {
if (initiate) {
checkInitCredPermission((Krb5NameElement) result.getName());
} else {
checkAcceptCredPermission
((Krb5NameElement) result.getName(), name);
}
}
return result;
}
public Krb5MechFactory(GSSCaller caller) {
this.caller = caller;
}
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException {
return Krb5NameElement.getInstance(nameStr, nameType);
}
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException {
// At this point, even an exported name is stripped down to safe
// bytes only
// XXX Use encoding here
return Krb5NameElement.getInstance(new String(name), nameType);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime,
int usage) throws GSSException {
if (name != null && !(name instanceof Krb5NameElement)) {
name = Krb5NameElement.getInstance(name.toString(),
name.getStringNameType());
}
Krb5CredElement credElement = getCredFromSubject
(name, (usage != GSSCredential.ACCEPT_ONLY));
if (credElement == null) {
if (usage == GSSCredential.INITIATE_ONLY ||
usage == GSSCredential.INITIATE_AND_ACCEPT) {
credElement = Krb5InitCredential.getInstance
(caller, (Krb5NameElement) name, initLifetime);
credElement = Krb5ProxyCredential.tryImpersonation(
caller, (Krb5InitCredential)credElement);
checkInitCredPermission
((Krb5NameElement) credElement.getName());
} else if (usage == GSSCredential.ACCEPT_ONLY) {
credElement =
Krb5AcceptCredential.getInstance(caller,
(Krb5NameElement) name);
checkAcceptCredPermission
((Krb5NameElement) credElement.getName(), name);
} else
throw new GSSException(GSSException.FAILURE, -1,
"Unknown usage mode requested");
}
return credElement;
}
public static void checkInitCredPermission(Krb5NameElement name) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
String realm = (name.getKrb5PrincipalName()).getRealmAsString();
String tgsPrincipal =
new String("krbtgt/" + realm + '@' + realm);
ServicePermission perm =
new ServicePermission(tgsPrincipal, "initiate");
try {
sm.checkPermission(perm);
} catch (SecurityException e) {
if (DEBUG) {
System.out.println("Permission to initiate" +
"kerberos init credential" + e.getMessage());
}
throw e;
}
}
}
public static void checkAcceptCredPermission(Krb5NameElement name,
GSSNameSpi originalName) {
SecurityManager sm = System.getSecurityManager();
if (sm != null && name != null) {
ServicePermission perm = new ServicePermission
(name.getKrb5PrincipalName().getName(), "accept");
try {
sm.checkPermission(perm);
} catch (SecurityException e) {
if (originalName == null) {
// Don't disclose the name of the principal
e = new SecurityException("No permission to acquire "
+ "Kerberos accept credential");
// Don't call e.initCause() with caught exception
}
throw e;
}
}
}
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred, int lifetime)
throws GSSException {
if (peer != null && !(peer instanceof Krb5NameElement)) {
peer = Krb5NameElement.getInstance(peer.toString(),
peer.getStringNameType());
}
// XXX Convert myInitiatorCred to Krb5CredElement
if (myInitiatorCred == null) {
myInitiatorCred = getCredentialElement(null, lifetime, 0,
GSSCredential.INITIATE_ONLY);
}
return new Krb5Context(caller, (Krb5NameElement)peer,
(Krb5CredElement)myInitiatorCred, lifetime);
}
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
throws GSSException {
// XXX Convert myAcceptorCred to Krb5CredElement
if (myAcceptorCred == null) {
myAcceptorCred = getCredentialElement(null, 0,
GSSCredential.INDEFINITE_LIFETIME, GSSCredential.ACCEPT_ONLY);
}
return new Krb5Context(caller, (Krb5CredElement)myAcceptorCred);
}
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
return new Krb5Context(caller, exportedContext);
}
public final Oid getMechanismOid() {
return GSS_KRB5_MECH_OID;
}
public Provider getProvider() {
return PROVIDER;
}
public Oid[] getNameTypes() {
// nameTypes is cloned in GSSManager.getNamesForMech
return nameTypes;
}
private static Oid createOid(String oidStr) {
Oid retVal = null;
try {
retVal = new Oid(oidStr);
} catch (GSSException e) {
// Should not happen!
}
return retVal;
}
}

View File

@@ -0,0 +1,348 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.Realm;
import sun.security.krb5.KrbException;
import javax.security.auth.kerberos.ServicePermission;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.Provider;
import java.util.Locale;
/**
* Implements the GSSNameSpi for the krb5 mechanism.
*
* @author Mayank Upadhyay
*/
public class Krb5NameElement
implements GSSNameSpi {
private PrincipalName krb5PrincipalName;
private String gssNameStr = null;
private Oid gssNameType = null;
// XXX Move this concept into PrincipalName's asn1Encode() sometime
private static String CHAR_ENCODING = "UTF-8";
private Krb5NameElement(PrincipalName principalName,
String gssNameStr,
Oid gssNameType) {
this.krb5PrincipalName = principalName;
this.gssNameStr = gssNameStr;
this.gssNameType = gssNameType;
}
/**
* Instantiates a new Krb5NameElement object. Internally it stores the
* information provided by the input parameters so that they may later
* be used for output when a printable representaion of this name is
* needed in GSS-API format rather than in Kerberos format.
*
*/
static Krb5NameElement getInstance(String gssNameStr, Oid gssNameType)
throws GSSException {
/*
* A null gssNameType implies that the mechanism default
* Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL be used.
*/
if (gssNameType == null)
gssNameType = Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL;
else
if (!gssNameType.equals(GSSName.NT_USER_NAME) &&
!gssNameType.equals(GSSName.NT_HOSTBASED_SERVICE) &&
!gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL) &&
!gssNameType.equals(GSSName.NT_EXPORT_NAME))
throw new GSSException(GSSException.BAD_NAMETYPE, -1,
gssNameType.toString()
+" is an unsupported nametype");
PrincipalName principalName;
try {
if (gssNameType.equals(GSSName.NT_EXPORT_NAME) ||
gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL)) {
principalName = new PrincipalName(gssNameStr,
PrincipalName.KRB_NT_PRINCIPAL);
} else {
String[] components = getComponents(gssNameStr);
/*
* We have forms of GSS name strings that can come in:
*
* 1. names of the form "foo" with just one
* component. (This might include a "@" but only in escaped
* form like "\@")
* 2. names of the form "foo@bar" with two components
*
* The nametypes that are accepted are NT_USER_NAME, and
* NT_HOSTBASED_SERVICE.
*/
if (gssNameType.equals(GSSName.NT_USER_NAME))
principalName = new PrincipalName(gssNameStr,
PrincipalName.KRB_NT_PRINCIPAL);
else {
String hostName = null;
String service = components[0];
if (components.length >= 2)
hostName = components[1];
String principal = getHostBasedInstance(service, hostName);
principalName = new PrincipalName(principal,
PrincipalName.KRB_NT_SRV_HST);
}
}
} catch (KrbException e) {
throw new GSSException(GSSException.BAD_NAME, -1, e.getMessage());
}
if (principalName.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkPermission(new ServicePermission(
"@" + principalName.getRealmAsString(), "-"));
} catch (SecurityException se) {
// Do not chain the actual exception to hide info
throw new GSSException(GSSException.FAILURE);
}
}
}
return new Krb5NameElement(principalName, gssNameStr, gssNameType);
}
public static Krb5NameElement getInstance(PrincipalName principalName) {
return new Krb5NameElement(principalName,
principalName.getName(),
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
}
private static String[] getComponents(String gssNameStr)
throws GSSException {
String[] retVal;
// XXX Perhaps provide this parsing code in PrincipalName
// Look for @ as in service@host
// Assumes host name will not have an escaped '@'
int separatorPos = gssNameStr.lastIndexOf('@', gssNameStr.length());
// Not really a separator if it is escaped. Then this is just part
// of the principal name or service name
if ((separatorPos > 0) &&
(gssNameStr.charAt(separatorPos-1) == '\\')) {
// Is the `\` character escaped itself?
if ((separatorPos - 2 < 0) ||
(gssNameStr.charAt(separatorPos-2) != '\\'))
separatorPos = -1;
}
if (separatorPos > 0) {
String serviceName = gssNameStr.substring(0, separatorPos);
String hostName = gssNameStr.substring(separatorPos+1);
retVal = new String[] { serviceName, hostName};
} else {
retVal = new String[] {gssNameStr};
}
return retVal;
}
private static String getHostBasedInstance(String serviceName,
String hostName)
throws GSSException {
StringBuffer temp = new StringBuffer(serviceName);
try {
// A lack of "@" defaults to the service being on the local
// host as per RFC 2743
// XXX Move this part into JGSS framework
if (hostName == null)
hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
// use hostname as it is
}
hostName = hostName.toLowerCase(Locale.ENGLISH);
temp = temp.append('/').append(hostName);
return temp.toString();
}
public final PrincipalName getKrb5PrincipalName() {
return krb5PrincipalName;
}
/**
* Equal method for the GSSNameSpi objects.
* If either name denotes an anonymous principal, the call should
* return false.
*
* @param other to be compared with
* @return true if they both refer to the same entity, else false
* @exception GSSException with major codes of BAD_NAMETYPE,
* BAD_NAME, FAILURE
*/
public boolean equals(GSSNameSpi other) throws GSSException {
if (other == this)
return true;
if (other instanceof Krb5NameElement) {
Krb5NameElement that = (Krb5NameElement) other;
return (this.krb5PrincipalName.getName().equals(
that.krb5PrincipalName.getName()));
}
return false;
}
/**
* Compares this <code>GSSNameSpi</code> object to another Object
* that might be a <code>GSSNameSpi</code>. The behaviour is exactly
* the same as in {@link #equals(GSSNameSpi) equals} except that
* no GSSException is thrown; instead, false will be returned in the
* situation where an error occurs.
*
* @param another the object to be compared to
* @return true if they both refer to the same entity, else false
* @see #equals(GSSNameSpi)
*/
public boolean equals(Object another) {
if (this == another) {
return true;
}
try {
if (another instanceof Krb5NameElement)
return equals((Krb5NameElement) another);
} catch (GSSException e) {
// ignore exception
}
return false;
}
/**
* Returns a hashcode value for this GSSNameSpi.
*
* @return a hashCode value
*/
public int hashCode() {
return 37 * 17 + krb5PrincipalName.getName().hashCode();
}
/**
* Returns the principal name in the form user@REALM or
* host/service@REALM but with the following constraints that are
* imposed by RFC 1964:
* <pre>
* (1) all occurrences of the characters `@`, `/`, and `\` within
* principal components or realm names shall be quoted with an
* immediately-preceding `\`.
*
* (2) all occurrences of the null, backspace, tab, or newline
* characters within principal components or realm names will be
* represented, respectively, with `\0`, `\b`, `\t`, or `\n`.
*
* (3) the `\` quoting character shall not be emitted within an
* exported name except to accommodate cases (1) and (2).
* </pre>
*/
public byte[] export() throws GSSException {
// XXX Apply the above constraints.
byte[] retVal = null;
try {
retVal = krb5PrincipalName.getName().getBytes(CHAR_ENCODING);
} catch (UnsupportedEncodingException e) {
// Can't happen
}
return retVal;
}
/**
* Get the mechanism type that this NameElement corresponds to.
*
* @return the Oid of the mechanism type
*/
public Oid getMechanism() {
return (Krb5MechFactory.GSS_KRB5_MECH_OID);
}
/**
* Returns a string representation for this name. The printed
* name type can be obtained by calling getStringNameType().
*
* @return string form of this name
* @see #getStringNameType()
* @overrides Object#toString
*/
public String toString() {
return (gssNameStr);
// For testing: return (super.toString());
}
/**
* Returns the name type oid.
*/
public Oid getGSSNameType() {
return (gssNameType);
}
/**
* Returns the oid describing the format of the printable name.
*
* @return the Oid for the format of the printed name
*/
public Oid getStringNameType() {
// XXX For NT_EXPORT_NAME return a different name type. Infact,
// don't even store NT_EXPORT_NAME in the cons.
return (gssNameType);
}
/**
* Indicates if this name object represents an Anonymous name.
*/
public boolean isAnonymousName() {
return (gssNameType.equals(GSSName.NT_ANONYMOUS));
}
public Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
import java.io.IOException;
import sun.security.krb5.Credentials;
import sun.security.krb5.KrbException;
import sun.security.krb5.internal.Ticket;
import javax.security.auth.kerberos.KerberosTicket;
/**
* Implements the krb5 proxy credential element used in constrained
* delegation. It is used in both impersonation (where there is no Kerberos 5
* communication between the middle server and the client) and normal
* constrained delegation (where there is, but client has not called
* requestCredDeleg(true)).
* @since 1.8
*/
public class Krb5ProxyCredential
implements Krb5CredElement {
public final Krb5InitCredential self; // the middle server
private final Krb5NameElement client; // the client
// The ticket with cname=client and sname=self. This can be a normal
// service ticket or an S4U2self ticket.
public final Ticket tkt;
Krb5ProxyCredential(Krb5InitCredential self, Krb5NameElement client,
Ticket tkt) {
this.self = self;
this.tkt = tkt;
this.client = client;
}
// The client name behind the proxy
@Override
public final Krb5NameElement getName() throws GSSException {
return client;
}
@Override
public int getInitLifetime() throws GSSException {
// endTime of tkt is not used by KDC, and it's also not
// available in the case of kerberos constr deleg
return self.getInitLifetime();
}
@Override
public int getAcceptLifetime() throws GSSException {
return 0;
}
@Override
public boolean isInitiatorCredential() throws GSSException {
return true;
}
@Override
public boolean isAcceptorCredential() throws GSSException {
return false;
}
@Override
public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
@Override
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
@Override
public void dispose() throws GSSException {
try {
self.destroy();
} catch (javax.security.auth.DestroyFailedException e) {
GSSException gssException =
new GSSException(GSSException.FAILURE, -1,
"Could not destroy credentials - " + e.getMessage());
gssException.initCause(e);
}
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
// Cannot impersonate multiple levels without the impersonatee's TGT.
throw new GSSException(GSSException.FAILURE, -1,
"Only an initiate credentials can impersonate");
}
// Try to see if a default credential should act as an impersonator.
static Krb5CredElement tryImpersonation(GSSCaller caller,
Krb5InitCredential initiator) throws GSSException {
try {
KerberosTicket proxy = initiator.proxyTicket;
if (proxy != null) {
Credentials proxyCreds = Krb5Util.ticketToCreds(proxy);
return new Krb5ProxyCredential(initiator,
Krb5NameElement.getInstance(proxyCreds.getClient()),
proxyCreds.getTicket());
} else {
return initiator;
}
} catch (KrbException | IOException e) {
throw new GSSException(GSSException.DEFECTIVE_CREDENTIAL, -1,
"Cannot create proxy credential");
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import java.io.IOException;
import sun.security.util.*;
import sun.security.jgss.*;
/**
* This class represents a base class for all Kerberos v5 GSS-API
* tokens. It contains commonly used definitions and utilities.
*
* @author Mayank Upadhyay
*/
abstract class Krb5Token extends GSSToken {
/**
* The token id defined for the token emitted by the initSecContext call
* carrying the AP_REQ .
*/
public static final int AP_REQ_ID = 0x0100;
/**
* The token id defined for the token emitted by the acceptSecContext call
* carrying the AP_REP .
*/
public static final int AP_REP_ID = 0x0200;
/**
* The token id defined for any token carrying a KRB-ERR message.
*/
public static final int ERR_ID = 0x0300;
/**
* The token id defined for the token emitted by the getMIC call.
*/
public static final int MIC_ID = 0x0101;
/**
* The token id defined for the token emitted by the wrap call.
*/
public static final int WRAP_ID = 0x0201;
// new token ID draft-ietf-krb-wg-gssapi-cfx-07.txt
public static final int MIC_ID_v2 = 0x0404;
public static final int WRAP_ID_v2 = 0x0504;
/**
* The object identifier corresponding to the Kerberos v5 GSS-API
* mechanism.
*/
public static ObjectIdentifier OID;
static {
try {
OID = new ObjectIdentifier(Krb5MechFactory.
GSS_KRB5_MECH_OID.toString());
} catch (IOException ioe) {
// should not happen
}
}
/**
* Returns a strign representing the token type.
*
* @param tokenId the token id for which a string name is desired
* @return the String name of this token type
*/
public static String getTokenName(int tokenId) {
String retVal = null;
switch (tokenId) {
case AP_REQ_ID:
case AP_REP_ID:
retVal = "Context Establishment Token";
break;
case MIC_ID:
retVal = "MIC Token";
break;
case MIC_ID_v2:
retVal = "MIC Token (new format)";
break;
case WRAP_ID:
retVal = "Wrap Token";
break;
case WRAP_ID_v2:
retVal = "Wrap Token (new format)";
break;
default:
retVal = "Kerberos GSS-API Mechanism Token";
break;
}
return retVal;
}
}

View File

@@ -0,0 +1,319 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
import java.security.AccessControlContext;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSCaller;
import sun.security.krb5.Credentials;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.KrbException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import sun.security.krb5.KerberosSecrets;
import sun.security.krb5.PrincipalName;
/**
* Utilities for obtaining and converting Kerberos tickets.
*
*/
public class Krb5Util {
static final boolean DEBUG =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction
("sun.security.krb5.debug")).booleanValue();
/**
* Default constructor
*/
private Krb5Util() { // Cannot create one of these
}
/**
* Retrieve the service ticket for serverPrincipal from caller's Subject
* or from Subject obtained by logging in, or if not found, via the
* Ticket Granting Service using the TGT obtained from the Subject.
*
* Caller must have permission to:
* - access and update Subject's private credentials
* - create LoginContext
* - read the auth.login.defaultCallbackHandler security property
*
* NOTE: This method is used by JSSE Kerberos Cipher Suites
*/
public static KerberosTicket getTicketFromSubjectAndTgs(GSSCaller caller,
String clientPrincipal, String serverPrincipal, String tgsPrincipal,
AccessControlContext acc)
throws LoginException, KrbException, IOException {
// 1. Try to find service ticket in acc subject
Subject accSubj = Subject.getSubject(acc);
KerberosTicket ticket = SubjectComber.find(accSubj,
serverPrincipal, clientPrincipal, KerberosTicket.class);
if (ticket != null) {
return ticket; // found it
}
Subject loginSubj = null;
if (!GSSUtil.useSubjectCredsOnly(caller)) {
// 2. Try to get ticket from login
try {
loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
ticket = SubjectComber.find(loginSubj,
serverPrincipal, clientPrincipal, KerberosTicket.class);
if (ticket != null) {
return ticket; // found it
}
} catch (LoginException e) {
// No login entry to use
// ignore and continue
}
}
// Service ticket not found in subject or login
// Try to get TGT to acquire service ticket
// 3. Try to get TGT from acc subject
KerberosTicket tgt = SubjectComber.find(accSubj,
tgsPrincipal, clientPrincipal, KerberosTicket.class);
boolean fromAcc;
if (tgt == null && loginSubj != null) {
// 4. Try to get TGT from login subject
tgt = SubjectComber.find(loginSubj,
tgsPrincipal, clientPrincipal, KerberosTicket.class);
fromAcc = false;
} else {
fromAcc = true;
}
// 5. Try to get service ticket using TGT
if (tgt != null) {
Credentials tgtCreds = ticketToCreds(tgt);
Credentials serviceCreds = Credentials.acquireServiceCreds(
serverPrincipal, tgtCreds);
if (serviceCreds != null) {
ticket = credsToTicket(serviceCreds);
// Store service ticket in acc's Subject
if (fromAcc && accSubj != null && !accSubj.isReadOnly()) {
accSubj.getPrivateCredentials().add(ticket);
}
}
}
return ticket;
}
/**
* Retrieves the ticket corresponding to the client/server principal
* pair from the Subject in the specified AccessControlContext.
*/
static KerberosTicket getServiceTicket(GSSCaller caller,
String clientPrincipal, String serverPrincipal,
AccessControlContext acc) throws LoginException {
// Try to get ticket from acc's Subject
Subject accSubj = Subject.getSubject(acc);
KerberosTicket ticket =
SubjectComber.find(accSubj, serverPrincipal, clientPrincipal,
KerberosTicket.class);
return ticket;
}
/**
* Retrieves the initial TGT corresponding to the client principal
* from the Subject in the specified AccessControlContext.
* If the ticket can not be found in the Subject, and if
* useSubjectCredsOnly is false, then obtain ticket from
* a LoginContext.
*/
static KerberosTicket getInitialTicket(GSSCaller caller,
String clientPrincipal,
AccessControlContext acc) throws LoginException {
// Try to get ticket from acc's Subject
Subject accSubj = Subject.getSubject(acc);
KerberosTicket ticket =
SubjectComber.find(accSubj, null, clientPrincipal,
KerberosTicket.class);
// Try to get ticket from Subject obtained from GSSUtil
if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) {
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
ticket = SubjectComber.find(subject,
null, clientPrincipal, KerberosTicket.class);
}
return ticket;
}
/**
* Retrieves the caller's Subject, or Subject obtained by logging in
* via the specified caller.
*
* Caller must have permission to:
* - access the Subject
* - create LoginContext
* - read the auth.login.defaultCallbackHandler security property
*
* NOTE: This method is used by JSSE Kerberos Cipher Suites
*/
public static Subject getSubject(GSSCaller caller,
AccessControlContext acc) throws LoginException {
// Try to get the Subject from acc
Subject subject = Subject.getSubject(acc);
// Try to get Subject obtained from GSSUtil
if (subject == null && !GSSUtil.useSubjectCredsOnly(caller)) {
subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
}
return subject;
}
/**
* Retrieves the ServiceCreds for the specified server principal from
* the Subject in the specified AccessControlContext. If not found, and if
* useSubjectCredsOnly is false, then obtain from a LoginContext.
*
* NOTE: This method is also used by JSSE Kerberos Cipher Suites
*/
public static ServiceCreds getServiceCreds(GSSCaller caller,
String serverPrincipal, AccessControlContext acc)
throws LoginException {
Subject accSubj = Subject.getSubject(acc);
ServiceCreds sc = null;
if (accSubj != null) {
sc = ServiceCreds.getInstance(accSubj, serverPrincipal);
}
if (sc == null && !GSSUtil.useSubjectCredsOnly(caller)) {
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
sc = ServiceCreds.getInstance(subject, serverPrincipal);
}
return sc;
}
public static KerberosTicket credsToTicket(Credentials serviceCreds) {
EncryptionKey sessionKey = serviceCreds.getSessionKey();
KerberosTicket kt = new KerberosTicket(
serviceCreds.getEncoded(),
new KerberosPrincipal(serviceCreds.getClient().getName()),
new KerberosPrincipal(serviceCreds.getServer().getName(),
KerberosPrincipal.KRB_NT_SRV_INST),
sessionKey.getBytes(),
sessionKey.getEType(),
serviceCreds.getFlags(),
serviceCreds.getAuthTime(),
serviceCreds.getStartTime(),
serviceCreds.getEndTime(),
serviceCreds.getRenewTill(),
serviceCreds.getClientAddresses());
PrincipalName clientAlias = serviceCreds.getClientAlias();
PrincipalName serverAlias = serviceCreds.getServerAlias();
if (clientAlias != null) {
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketSetClientAlias(kt, new KerberosPrincipal(
clientAlias.getName(), clientAlias.getNameType()));
}
if (serverAlias != null) {
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketSetServerAlias(kt, new KerberosPrincipal(
serverAlias.getName(), serverAlias.getNameType()));
}
return kt;
};
public static Credentials ticketToCreds(KerberosTicket kerbTicket)
throws KrbException, IOException {
KerberosPrincipal clientAlias = KerberosSecrets
.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketGetClientAlias(kerbTicket);
KerberosPrincipal serverAlias = KerberosSecrets
.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketGetServerAlias(kerbTicket);
return new Credentials(
kerbTicket.getEncoded(),
kerbTicket.getClient().getName(),
(clientAlias != null ? clientAlias.getName() : null),
kerbTicket.getServer().getName(),
(serverAlias != null ? serverAlias.getName() : null),
kerbTicket.getSessionKey().getEncoded(),
kerbTicket.getSessionKeyType(),
kerbTicket.getFlags(),
kerbTicket.getAuthTime(),
kerbTicket.getStartTime(),
kerbTicket.getEndTime(),
kerbTicket.getRenewTill(),
kerbTicket.getClientAddresses());
}
/**
* A helper method to get a sun..KeyTab from a javax..KeyTab
* @param ktab the javax..KeyTab object
* @return the sun..KeyTab object
*/
public static sun.security.krb5.internal.ktab.KeyTab
snapshotFromJavaxKeyTab(KeyTab ktab) {
return KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
.keyTabTakeSnapshot(ktab);
}
/**
* A helper method to get EncryptionKeys from a javax..KeyTab
* @param ktab the javax..KeyTab object
* @param cname the PrincipalName
* @return the EKeys, never null, might be empty
*/
public static EncryptionKey[] keysFromJavaxKeyTab(
KeyTab ktab, PrincipalName cname) {
return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname);
}
public static String keyInfo(byte[] data) {
if (data == null) {
return "null key";
} else if (data.length == 0) {
return "empty key";
} else {
for (byte b : data) {
if (b != 0) {
return data.length + "-byte key";
}
}
return data.length + "-byte zero key";
}
}
}

View File

@@ -0,0 +1,721 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.security.MessageDigest;
/**
* This class is a base class for other token definitions that pertain to
* per-message GSS-API calls. Conceptually GSS-API has two types of
* per-message tokens: WrapToken and MicToken. They differ in the respect
* that a WrapToken carries additional plaintext or ciphertext application
* data besides just the sequence number and checksum. This class
* encapsulates the commonality in the structure of the WrapToken and the
* MicToken. This structure can be represented as:
* <p>
* <pre>
* 0..1 TOK_ID Identification field.
* 01 01 - Mic token
* 02 01 - Wrap token
* 2..3 SGN_ALG Checksum algorithm indicator.
* 00 00 - DES MAC MD5
* 01 00 - MD2.5
* 02 00 - DES MAC
* 04 00 - HMAC SHA1 DES3-KD
* 11 00 - RC4-HMAC
* 4..5 SEAL_ALG ff ff - none
* 00 00 - DES
* 02 00 - DES3-KD
* 10 00 - RC4-HMAC
* 6..7 Filler Contains ff ff
* 8..15 SND_SEQ Encrypted sequence number field.
* 16..s+15 SGN_CKSUM Checksum of plaintext padded data,
* calculated according to algorithm
* specified in SGN_ALG field.
* s+16..last Data encrypted or plaintext padded data
* </pre>
* Where "s" indicates the size of the checksum.
* <p>
* As always, this is preceeded by a GSSHeader.
*
* @author Mayank Upadhyay
* @author Ram Marti
* @see sun.security.jgss.GSSHeader
*/
abstract class MessageToken extends Krb5Token {
/* Fields in header minus checksum size */
private static final int TOKEN_NO_CKSUM_SIZE = 16;
/**
* Filler data as defined in the specification of the Kerberos v5 GSS-API
* Mechanism.
*/
private static final int FILLER = 0xffff;
// Signing algorithm values (for the SNG_ALG field)
// From RFC 1964
/* Use a DES MAC MD5 checksum */
static final int SGN_ALG_DES_MAC_MD5 = 0x0000;
/* Use DES MAC checksum. */
static final int SGN_ALG_DES_MAC = 0x0200;
// From draft-raeburn-cat-gssapi-krb5-3des-00
/* Use a HMAC SHA1 DES3 -KD checksum */
static final int SGN_ALG_HMAC_SHA1_DES3_KD = 0x0400;
// Sealing algorithm values (for the SEAL_ALG field)
// RFC 1964
/**
* A value for the SEAL_ALG field that indicates that no encryption was
* used.
*/
static final int SEAL_ALG_NONE = 0xffff;
/* Use DES CBC encryption algorithm. */
static final int SEAL_ALG_DES = 0x0000;
// From draft-raeburn-cat-gssapi-krb5-3des-00
/**
* Use DES3-KD sealing algorithm. (draft-raeburn-cat-gssapi-krb5-3des-00)
* This algorithm uses triple-DES with key derivation, with a usage
* value KG_USAGE_SEAL. Padding is still to 8-byte multiples, and the
* IV for encrypting application data is zero.
*/
static final int SEAL_ALG_DES3_KD = 0x0200;
// draft draft-brezak-win2k-krb-rc4-hmac-04.txt
static final int SEAL_ALG_ARCFOUR_HMAC = 0x1000;
static final int SGN_ALG_HMAC_MD5_ARCFOUR = 0x1100;
private static final int TOKEN_ID_POS = 0;
private static final int SIGN_ALG_POS = 2;
private static final int SEAL_ALG_POS = 4;
private int seqNumber;
private boolean confState = true;
private boolean initiator = true;
private int tokenId = 0;
private GSSHeader gssHeader = null;
private MessageTokenHeader tokenHeader = null;
private byte[] checksum = null;
private byte[] encSeqNumber = null;
private byte[] seqNumberData = null;
/* cipher instance used by the corresponding GSSContext */
CipherHelper cipherHelper = null;
/**
* Constructs a MessageToken from a byte array. If there are more bytes
* in the array than needed, the extra bytes are simply ignroed.
*
* @param tokenId the token id that should be contained in this token as
* it is read.
* @param context the Kerberos context associated with this token
* @param tokenBytes the byte array containing the token
* @param tokenOffset the offset where the token begins
* @param tokenLen the length of the token
* @param prop the MessageProp structure in which the properties of the
* token should be stored.
* @throws GSSException if there is a problem parsing the token
*/
MessageToken(int tokenId, Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
this(tokenId, context,
new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen),
prop);
}
/**
* Constructs a MessageToken from an InputStream. Bytes will be read on
* demand and the thread might block if there are not enough bytes to
* complete the token.
*
* @param tokenId the token id that should be contained in this token as
* it is read.
* @param context the Kerberos context associated with this token
* @param is the InputStream from which to read
* @param prop the MessageProp structure in which the properties of the
* token should be stored.
* @throws GSSException if there is a problem reading from the
* InputStream or parsing the token
*/
MessageToken(int tokenId, Krb5Context context, InputStream is,
MessageProp prop) throws GSSException {
init(tokenId, context);
try {
gssHeader = new GSSHeader(is);
if (!gssHeader.getOid().equals((Object)OID)) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId));
}
if (!confState) {
prop.setPrivacy(false);
}
tokenHeader = new MessageTokenHeader(is, prop);
encSeqNumber = new byte[8];
readFully(is, encSeqNumber);
// debug("\n\tRead EncSeq#=" +
// getHexBytes(encSeqNumber, encSeqNumber.length));
checksum = new byte[cipherHelper.getChecksumLength()];
readFully(is, checksum);
// debug("\n\tRead checksum=" +
// getHexBytes(checksum, checksum.length));
// debug("\nLeaving MessageToken.Cons\n");
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + e.getMessage());
}
}
/**
* Used to obtain the GSSHeader that was at the start of this
* token.
*/
public final GSSHeader getGSSHeader() {
return gssHeader;
}
/**
* Used to obtain the token id that was contained in this token.
* @return the token id in the token
*/
public final int getTokenId() {
return tokenId;
}
/**
* Used to obtain the encrypted sequence number in this token.
* @return the encrypted sequence number in the token
*/
public final byte[] getEncSeqNumber() {
return encSeqNumber;
}
/**
* Used to obtain the checksum that was contained in this token.
* @return the checksum in the token
*/
public final byte[] getChecksum() {
return checksum;
}
/**
* Used to determine if this token contains any encrypted data.
* @return true if it contains any encrypted data, false if there is only
* plaintext data or if there is no data.
*/
public final boolean getConfState() {
return confState;
}
/**
* Generates the checksum field and the encrypted sequence number
* field. The encrypted sequence number uses the 8 bytes of the checksum
* as an initial vector in a fixed DesCbc algorithm.
*
* @param prop the MessageProp structure that determines what sort of
* checksum and sealing algorithm should be used. The lower byte
* of qop determines the checksum algorithm while the upper byte
* determines the signing algorithm.
* Checksum values are:
* 0 - default (DES_MAC)
* 1 - MD5
* 2 - DES_MD5
* 3 - DES_MAC
* 4 - HMAC_SHA1
* Sealing values are:
* 0 - default (DES)
* 1 - DES
* 2 - DES3-KD
*
* @param optionalHeader an optional header that will be processed first
* during checksum calculation
*
* @param data the application data to checksum
* @param offset the offset where the data starts
* @param len the length of the data
*
* @param optionalTrailer an optional trailer that will be processed
* last during checksum calculation. e.g., padding that should be
* appended to the application data
*
* @throws GSSException if an error occurs in the checksum calculation or
* encryption sequence number calculation.
*/
public void genSignAndSeqNumber(MessageProp prop,
byte[] optionalHeader,
byte[] data, int offset, int len,
byte[] optionalTrailer)
throws GSSException {
// debug("Inside MessageToken.genSignAndSeqNumber:\n");
int qop = prop.getQOP();
if (qop != 0) {
qop = 0;
prop.setQOP(qop);
}
if (!confState) {
prop.setPrivacy(false);
}
// Create a token header with the correct sign and seal algorithm
// values.
tokenHeader =
new MessageTokenHeader(tokenId, prop.getPrivacy(), qop);
// Calculate SGN_CKSUM
checksum =
getChecksum(optionalHeader, data, offset, len, optionalTrailer);
// debug("\n\tCalc checksum=" +
// getHexBytes(checksum, checksum.length));
// Calculate SND_SEQ
seqNumberData = new byte[8];
// When using this RC4 based encryption type, the sequence number is
// always sent in big-endian rather than little-endian order.
if (cipherHelper.isArcFour()) {
writeBigEndian(seqNumber, seqNumberData);
} else {
// for all other etypes
writeLittleEndian(seqNumber, seqNumberData);
}
if (!initiator) {
seqNumberData[4] = (byte)0xff;
seqNumberData[5] = (byte)0xff;
seqNumberData[6] = (byte)0xff;
seqNumberData[7] = (byte)0xff;
}
encSeqNumber = cipherHelper.encryptSeq(checksum, seqNumberData, 0, 8);
// debug("\n\tCalc seqNum=" +
// getHexBytes(seqNumberData, seqNumberData.length));
// debug("\n\tCalc encSeqNum=" +
// getHexBytes(encSeqNumber, encSeqNumber.length));
}
/**
* Verifies that the checksum field and sequence number direction bytes
* are valid and consistent with the application data.
*
* @param optionalHeader an optional header that will be processed first
* during checksum calculation.
*
* @param data the application data
* @param offset the offset where the data begins
* @param len the length of the application data
*
* @param optionalTrailer an optional trailer that will be processed last
* during checksum calculation. e.g., padding that should be appended to
* the application data
*
* @throws GSSException if an error occurs in the checksum calculation or
* encryption sequence number calculation.
*/
public final boolean verifySignAndSeqNumber(byte[] optionalHeader,
byte[] data, int offset, int len,
byte[] optionalTrailer)
throws GSSException {
// debug("\tIn verifySign:\n");
// debug("\t\tchecksum: [" + getHexBytes(checksum) + "]\n");
byte[] myChecksum =
getChecksum(optionalHeader, data, offset, len, optionalTrailer);
// debug("\t\tmychecksum: [" + getHexBytes(myChecksum) +"]\n");
// debug("\t\tchecksum: [" + getHexBytes(checksum) + "]\n");
if (MessageDigest.isEqual(checksum, myChecksum)) {
seqNumberData = cipherHelper.decryptSeq(
checksum, encSeqNumber, 0, 8);
// debug("\t\tencSeqNumber: [" + getHexBytes(encSeqNumber)
// + "]\n");
// debug("\t\tseqNumberData: [" + getHexBytes(seqNumberData)
// + "]\n");
/*
* The token from the initiator has direction bytes 0x00 and
* the token from the acceptor has direction bytes 0xff.
*/
byte directionByte = 0;
if (initiator)
directionByte = (byte) 0xff; // Received token from acceptor
if ((seqNumberData[4] == directionByte) &&
(seqNumberData[5] == directionByte) &&
(seqNumberData[6] == directionByte) &&
(seqNumberData[7] == directionByte))
return true;
}
return false;
}
public final int getSequenceNumber() {
int sequenceNum = 0;
if (cipherHelper.isArcFour()) {
sequenceNum = readBigEndian(seqNumberData, 0, 4);
} else {
sequenceNum = readLittleEndian(seqNumberData, 0, 4);
}
return sequenceNum;
}
/**
* Computes the checksum based on the algorithm stored in the
* tokenHeader.
*
* @param optionalHeader an optional header that will be processed first
* during checksum calculation.
*
* @param data the application data
* @param offset the offset where the data begins
* @param len the length of the application data
*
* @param optionalTrailer an optional trailer that will be processed last
* during checksum calculation. e.g., padding that should be appended to
* the application data
*
* @throws GSSException if an error occurs in the checksum calculation.
*/
private byte[] getChecksum(byte[] optionalHeader,
byte[] data, int offset, int len,
byte[] optionalTrailer)
throws GSSException {
// debug("Will do getChecksum:\n");
/*
* For checksum calculation the token header bytes i.e., the first 8
* bytes following the GSSHeader, are logically prepended to the
* application data to bind the data to this particular token.
*
* Note: There is no such requirement wrt adding padding to the
* application data for checksumming, although the cryptographic
* algorithm used might itself apply some padding.
*/
byte[] tokenHeaderBytes = tokenHeader.getBytes();
byte[] existingHeader = optionalHeader;
byte[] checksumDataHeader = tokenHeaderBytes;
if (existingHeader != null) {
checksumDataHeader = new byte[tokenHeaderBytes.length +
existingHeader.length];
System.arraycopy(tokenHeaderBytes, 0,
checksumDataHeader, 0, tokenHeaderBytes.length);
System.arraycopy(existingHeader, 0,
checksumDataHeader, tokenHeaderBytes.length,
existingHeader.length);
}
return cipherHelper.calculateChecksum(tokenHeader.getSignAlg(),
checksumDataHeader, optionalTrailer, data, offset, len, tokenId);
}
/**
* Constructs an empty MessageToken for the local context to send to
* the peer. It also increments the local sequence number in the
* Krb5Context instance it uses after obtaining the object lock for
* it.
*
* @param tokenId the token id that should be contained in this token
* @param context the Kerberos context associated with this token
*/
MessageToken(int tokenId, Krb5Context context) throws GSSException {
/*
debug("\n============================");
debug("\nMySessionKey=" +
getHexBytes(context.getMySessionKey().getBytes()));
debug("\nPeerSessionKey=" +
getHexBytes(context.getPeerSessionKey().getBytes()));
debug("\n============================\n");
*/
init(tokenId, context);
this.seqNumber = context.incrementMySequenceNumber();
}
private void init(int tokenId, Krb5Context context) throws GSSException {
this.tokenId = tokenId;
// Just for consistency check in Wrap
this.confState = context.getConfState();
this.initiator = context.isInitiator();
this.cipherHelper = context.getCipherHelper(null);
// debug("In MessageToken.Cons");
}
/**
* Encodes a GSSHeader and this token onto an OutputStream.
*
* @param os the OutputStream to which this should be written
* @throws GSSException if an error occurs while writing to the OutputStream
*/
public void encode(OutputStream os) throws IOException, GSSException {
gssHeader = new GSSHeader(OID, getKrb5TokenSize());
gssHeader.encode(os);
tokenHeader.encode(os);
// debug("Writing seqNumber: " + getHexBytes(encSeqNumber));
os.write(encSeqNumber);
// debug("Writing checksum: " + getHexBytes(checksum));
os.write(checksum);
}
/**
* Obtains the size of this token. Note that this excludes the size of
* the GSSHeader.
* @return token size
*/
protected int getKrb5TokenSize() throws GSSException {
return getTokenSize();
}
protected final int getTokenSize() throws GSSException {
return TOKEN_NO_CKSUM_SIZE + cipherHelper.getChecksumLength();
}
protected static final int getTokenSize(CipherHelper ch)
throws GSSException {
return TOKEN_NO_CKSUM_SIZE + ch.getChecksumLength();
}
/**
* Obtains the conext key that is associated with this token.
* @return the context key
*/
/*
public final byte[] getContextKey() {
return contextKey;
}
*/
/**
* Obtains the encryption algorithm that should be used in this token
* given the state of confidentiality the application requested.
* Requested qop must be consistent with negotiated session key.
* @param confRequested true if the application desired confidentiality
* on this token, false otherwise
* @param qop the qop requested by the application
* @throws GSSException if qop is incompatible with the negotiated
* session key
*/
protected abstract int getSealAlg(boolean confRequested, int qop)
throws GSSException;
// ******************************************* //
// I N N E R C L A S S E S F O L L O W
// ******************************************* //
/**
* This inner class represents the initial portion of the message token
* and contains information about the checksum and encryption algorithms
* that are in use. It constitutes the first 8 bytes of the
* message token:
* <pre>
* 0..1 TOK_ID Identification field.
* 01 01 - Mic token
* 02 01 - Wrap token
* 2..3 SGN_ALG Checksum algorithm indicator.
* 00 00 - DES MAC MD5
* 01 00 - MD2.5
* 02 00 - DES MAC
* 04 00 - HMAC SHA1 DES3-KD
* 11 00 - RC4-HMAC
* 4..5 SEAL_ALG ff ff - none
* 00 00 - DES
* 02 00 - DES3-KD
* 10 00 - RC4-HMAC
* 6..7 Filler Contains ff ff
* </pre>
*/
class MessageTokenHeader {
private int tokenId;
private int signAlg;
private int sealAlg;
private byte[] bytes = new byte[8];
/**
* Constructs a MessageTokenHeader for the specified token type with
* appropriate checksum and encryption algorithms fields.
*
* @param tokenId the token id for this message token
* @param conf true if confidentiality will be resuested with this
* message token, false otherwise.
* @param qop the value of the quality of protection that will be
* desired.
*/
public MessageTokenHeader(int tokenId, boolean conf, int qop)
throws GSSException {
this.tokenId = tokenId;
signAlg = MessageToken.this.getSgnAlg(qop);
sealAlg = MessageToken.this.getSealAlg(conf, qop);
bytes[0] = (byte) (tokenId >>> 8);
bytes[1] = (byte) (tokenId);
bytes[2] = (byte) (signAlg >>> 8);
bytes[3] = (byte) (signAlg);
bytes[4] = (byte) (sealAlg >>> 8);
bytes[5] = (byte) (sealAlg);
bytes[6] = (byte) (MessageToken.FILLER >>> 8);
bytes[7] = (byte) (MessageToken.FILLER);
}
/**
* Constructs a MessageTokenHeader by reading it from an InputStream
* and sets the appropriate confidentiality and quality of protection
* values in a MessageProp structure.
*
* @param is the InputStream to read from
* @param prop the MessageProp to populate
* @throws IOException is an error occurs while reading from the
* InputStream
*/
public MessageTokenHeader(InputStream is, MessageProp prop)
throws IOException {
readFully(is, bytes);
tokenId = readInt(bytes, TOKEN_ID_POS);
signAlg = readInt(bytes, SIGN_ALG_POS);
sealAlg = readInt(bytes, SEAL_ALG_POS);
// debug("\nMessageTokenHeader read tokenId=" +
// getHexBytes(bytes) + "\n");
// XXX compare to FILLER
int temp = readInt(bytes, SEAL_ALG_POS + 2);
// debug("SIGN_ALG=" + signAlg);
switch (sealAlg) {
case SEAL_ALG_DES:
case SEAL_ALG_DES3_KD:
case SEAL_ALG_ARCFOUR_HMAC:
prop.setPrivacy(true);
break;
default:
prop.setPrivacy(false);
}
prop.setQOP(0); // default
}
/**
* Encodes this MessageTokenHeader onto an OutputStream
* @param os the OutputStream to write to
* @throws IOException is an error occurs while writing
*/
public final void encode(OutputStream os) throws IOException {
os.write(bytes);
}
/**
* Returns the token id for the message token.
* @return the token id
* @see sun.security.jgss.krb5.Krb5Token#MIC_ID
* @see sun.security.jgss.krb5.Krb5Token#WRAP_ID
*/
public final int getTokenId() {
return tokenId;
}
/**
* Returns the sign algorithm for the message token.
* @return the sign algorithm
* @see sun.security.jgss.krb5.MessageToken#SIGN_DES_MAC
* @see sun.security.jgss.krb5.MessageToken#SIGN_DES_MAC_MD5
*/
public final int getSignAlg() {
return signAlg;
}
/**
* Returns the seal algorithm for the message token.
* @return the seal algorithm
* @see sun.security.jgss.krb5.MessageToken#SEAL_ALG_DES
* @see sun.security.jgss.krb5.MessageToken#SEAL_ALG_NONE
*/
public final int getSealAlg() {
return sealAlg;
}
/**
* Returns the bytes of this header.
* @return 8 bytes that form this header
*/
public final byte[] getBytes() {
return bytes;
}
} // end of class MessageTokenHeader
/**
* Determine signing algorithm based on QOP.
*/
protected int getSgnAlg(int qop) throws GSSException {
// QOP ignored
return cipherHelper.getSgnAlg();
}
}

View File

@@ -0,0 +1,643 @@
/*
* Copyright (c) 2004, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.util.Arrays;
/**
* This class is a base class for new GSS token definitions, as defined
* in RFC 4121, that pertain to per-message GSS-API calls. Conceptually
* GSS-API has two types of per-message tokens: WrapToken and MicToken.
* They differ in the respect that a WrapToken carries additional plaintext
* or ciphertext application data besides just the sequence number and
* checksum. This class encapsulates the commonality in the structure of
* the WrapToken and the MicToken. This structure can be represented as:
* <p>
* <pre>
* Wrap Tokens
*
* Octet no Name Description
* ---------------------------------------------------------------
* 0..1 TOK_ID Identification field. Tokens emitted by
* GSS_Wrap() contain the hex value 05 04
* expressed in big-endian order in this field.
* 2 Flags Attributes field, as described in section
* 4.2.2.
* 3 Filler Contains the hex value FF.
* 4..5 EC Contains the "extra count" field, in big-
* endian order as described in section 4.2.3.
* 6..7 RRC Contains the "right rotation count" in big
* endian order, as described in section 4.2.5.
* 8..15 SND_SEQ Sequence number field in clear text,
* expressed in big-endian order.
* 16..last Data Encrypted data for Wrap tokens with
* confidentiality, or plaintext data followed
* by the checksum for Wrap tokens without
* confidentiality, as described in section
* 4.2.4.
* MIC Tokens
*
* Octet no Name Description
* -----------------------------------------------------------------
* 0..1 TOK_ID Identification field. Tokens emitted by
* GSS_GetMIC() contain the hex value 04 04
* expressed in big-endian order in this field.
* 2 Flags Attributes field, as described in section
* 4.2.2.
* 3..7 Filler Contains five octets of hex value FF.
* 8..15 SND_SEQ Sequence number field in clear text,
* expressed in big-endian order.
* 16..last SGN_CKSUM Checksum of the "to-be-signed" data and
* octet 0..15, as described in section 4.2.4.
*
* </pre>
* <p>
* This class is the super class of WrapToken_v2 and MicToken_v2. The token's
* header (bytes[0..15]) and data (byte[16..]) are saved in tokenHeader and
* tokenData fields. Since there is no easy way to find out the exact length
* of a WrapToken_v2 token from any header info, in the case of reading from
* stream, we read all available() bytes into the token.
* <p>
* All read actions are performed in this super class. On the write part, the
* super class only write the tokenHeader, and the content writing is inside
* child classes.
*
* @author Seema Malkani
*/
abstract class MessageToken_v2 extends Krb5Token {
protected static final int TOKEN_HEADER_SIZE = 16;
private static final int TOKEN_ID_POS = 0;
private static final int TOKEN_FLAG_POS = 2;
private static final int TOKEN_EC_POS = 4;
private static final int TOKEN_RRC_POS = 6;
/**
* The size of the random confounder used in a WrapToken.
*/
protected static final int CONFOUNDER_SIZE = 16;
// RFC 4121, key usage values
static final int KG_USAGE_ACCEPTOR_SEAL = 22;
static final int KG_USAGE_ACCEPTOR_SIGN = 23;
static final int KG_USAGE_INITIATOR_SEAL = 24;
static final int KG_USAGE_INITIATOR_SIGN = 25;
// RFC 4121, Flags Field
private static final int FLAG_SENDER_IS_ACCEPTOR = 1;
private static final int FLAG_WRAP_CONFIDENTIAL = 2;
private static final int FLAG_ACCEPTOR_SUBKEY = 4;
private static final int FILLER = 0xff;
private MessageTokenHeader tokenHeader = null;
// Common field
private int tokenId = 0;
private int seqNumber;
protected byte[] tokenData; // content of token, without the header
protected int tokenDataLen;
// Key usage number for crypto action
private int key_usage = 0;
// EC and RRC fields, WrapToken only
private int ec = 0;
private int rrc = 0;
// Checksum. Always in MicToken, might be in WrapToken
byte[] checksum = null;
// Context properties
private boolean confState = true;
private boolean initiator = true;
private boolean have_acceptor_subkey = false;
/* cipher instance used by the corresponding GSSContext */
CipherHelper cipherHelper = null;
/**
* Constructs a MessageToken from a byte array.
*
* @param tokenId the token id that should be contained in this token as
* it is read.
* @param context the Kerberos context associated with this token
* @param tokenBytes the byte array containing the token
* @param tokenOffset the offset where the token begins
* @param tokenLen the length of the token
* @param prop the MessageProp structure in which the properties of the
* token should be stored.
* @throws GSSException if there is a problem parsing the token
*/
MessageToken_v2(int tokenId, Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
this(tokenId, context,
new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen),
prop);
}
/**
* Constructs a MessageToken from an InputStream. Bytes will be read on
* demand and the thread might block if there are not enough bytes to
* complete the token. Please note there is no accurate way to find out
* the size of a token, but we try our best to make sure there is
* enough bytes to construct one.
*
* @param tokenId the token id that should be contained in this token as
* it is read.
* @param context the Kerberos context associated with this token
* @param is the InputStream from which to read
* @param prop the MessageProp structure in which the properties of the
* token should be stored.
* @throws GSSException if there is a problem reading from the
* InputStream or parsing the token
*/
MessageToken_v2(int tokenId, Krb5Context context, InputStream is,
MessageProp prop) throws GSSException {
init(tokenId, context);
try {
if (!confState) {
prop.setPrivacy(false);
}
tokenHeader = new MessageTokenHeader(is, prop, tokenId);
// set key_usage
if (tokenId == Krb5Token.WRAP_ID_v2) {
key_usage = (!initiator ? KG_USAGE_INITIATOR_SEAL
: KG_USAGE_ACCEPTOR_SEAL);
} else if (tokenId == Krb5Token.MIC_ID_v2) {
key_usage = (!initiator ? KG_USAGE_INITIATOR_SIGN
: KG_USAGE_ACCEPTOR_SIGN);
}
int minSize = 0; // minimal size for token data
if (tokenId == Krb5Token.WRAP_ID_v2 && prop.getPrivacy()) {
minSize = CONFOUNDER_SIZE +
TOKEN_HEADER_SIZE + cipherHelper.getChecksumLength();
} else {
minSize = cipherHelper.getChecksumLength();
}
// Read token data
if (tokenId == Krb5Token.MIC_ID_v2) {
// The only case we can precisely predict the token data length
tokenDataLen = minSize;
tokenData = new byte[minSize];
readFully(is, tokenData);
} else {
tokenDataLen = is.available();
if (tokenDataLen >= minSize) { // read in one shot
tokenData = new byte[tokenDataLen];
readFully(is, tokenData);
} else {
byte[] tmp = new byte[minSize];
readFully(is, tmp);
// Hope while blocked in the read above, more data would
// come and is.available() below contains the whole token.
int more = is.available();
tokenDataLen = minSize + more;
tokenData = Arrays.copyOf(tmp, tokenDataLen);
readFully(is, tokenData, minSize, more);
}
}
if (tokenId == Krb5Token.WRAP_ID_v2) {
rotate();
}
if (tokenId == Krb5Token.MIC_ID_v2 ||
(tokenId == Krb5Token.WRAP_ID_v2 && !prop.getPrivacy())) {
// Read checksum
int chkLen = cipherHelper.getChecksumLength();
checksum = new byte[chkLen];
System.arraycopy(tokenData, tokenDataLen-chkLen,
checksum, 0, chkLen);
// validate EC for Wrap tokens without confidentiality
if (tokenId == Krb5Token.WRAP_ID_v2 && !prop.getPrivacy()) {
if (chkLen != ec) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + "EC incorrect!");
}
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + e.getMessage());
}
}
/**
* Used to obtain the token id that was contained in this token.
* @return the token id in the token
*/
public final int getTokenId() {
return tokenId;
}
/**
* Used to obtain the key_usage type for this token.
* @return the key_usage for the token
*/
public final int getKeyUsage() {
return key_usage;
}
/**
* Used to determine if this token contains any encrypted data.
* @return true if it contains any encrypted data, false if there is only
* plaintext data or if there is no data.
*/
public final boolean getConfState() {
return confState;
}
/**
* Generates the checksum field and the sequence number field.
*
* @param prop the MessageProp structure
* @param data the application data to checksum
* @param offset the offset where the data starts
* @param len the length of the data
*
* @throws GSSException if an error occurs in the checksum calculation or
* sequence number calculation.
*/
public void genSignAndSeqNumber(MessageProp prop,
byte[] data, int offset, int len)
throws GSSException {
// debug("Inside MessageToken.genSignAndSeqNumber:\n");
int qop = prop.getQOP();
if (qop != 0) {
qop = 0;
prop.setQOP(qop);
}
if (!confState) {
prop.setPrivacy(false);
}
// Create a new gss token header as defined in RFC 4121
tokenHeader = new MessageTokenHeader(tokenId, prop.getPrivacy());
// debug("\n\t Message Header = " +
// getHexBytes(tokenHeader.getBytes(), tokenHeader.getBytes().length));
// set key_usage
if (tokenId == Krb5Token.WRAP_ID_v2) {
key_usage = (initiator ? KG_USAGE_INITIATOR_SEAL
: KG_USAGE_ACCEPTOR_SEAL);
} else if (tokenId == Krb5Token.MIC_ID_v2) {
key_usage = (initiator ? KG_USAGE_INITIATOR_SIGN
: KG_USAGE_ACCEPTOR_SIGN);
}
// Calculate SGN_CKSUM
if ((tokenId == MIC_ID_v2) ||
(!prop.getPrivacy() && (tokenId == WRAP_ID_v2))) {
checksum = getChecksum(data, offset, len);
// debug("\n\tCalc checksum=" +
// getHexBytes(checksum, checksum.length));
}
// In Wrap tokens without confidentiality, the EC field SHALL be used
// to encode the number of octets in the trailing checksum
if (!prop.getPrivacy() && (tokenId == WRAP_ID_v2)) {
byte[] tok_header = tokenHeader.getBytes();
tok_header[4] = (byte) (checksum.length >>> 8);
tok_header[5] = (byte) (checksum.length);
}
}
/**
* Verifies the validity of checksum field
*
* @param data the application data
* @param offset the offset where the data begins
* @param len the length of the application data
*
* @throws GSSException if an error occurs in the checksum calculation
*/
public final boolean verifySign(byte[] data, int offset, int len)
throws GSSException {
// debug("\t====In verifySign:====\n");
// debug("\t\t checksum: [" + getHexBytes(checksum) + "]\n");
// debug("\t\t data = [" + getHexBytes(data) + "]\n");
byte[] myChecksum = getChecksum(data, offset, len);
// debug("\t\t mychecksum: [" + getHexBytes(myChecksum) +"]\n");
if (MessageDigest.isEqual(checksum, myChecksum)) {
// debug("\t\t====Checksum PASS:====\n");
return true;
}
return false;
}
/**
* Rotate bytes as per the "RRC" (Right Rotation Count) received.
* Our implementation does not do any rotates when sending, only
* when receiving, we rotate left as per the RRC count, to revert it.
*/
private void rotate() {
if (rrc % tokenDataLen != 0) {
rrc = rrc % tokenDataLen;
byte[] newBytes = new byte[tokenDataLen];
System.arraycopy(tokenData, rrc, newBytes, 0, tokenDataLen-rrc);
System.arraycopy(tokenData, 0, newBytes, tokenDataLen-rrc, rrc);
tokenData = newBytes;
}
}
public final int getSequenceNumber() {
return seqNumber;
}
/**
* Computes the checksum based on the algorithm stored in the
* tokenHeader.
*
* @param data the application data
* @param offset the offset where the data begins
* @param len the length of the application data
*
* @throws GSSException if an error occurs in the checksum calculation.
*/
byte[] getChecksum(byte[] data, int offset, int len)
throws GSSException {
// debug("Will do getChecksum:\n");
/*
* For checksum calculation the token header bytes i.e., the first 16
* bytes following the GSSHeader, are logically prepended to the
* application data to bind the data to this particular token.
*
* Note: There is no such requirement wrt adding padding to the
* application data for checksumming, although the cryptographic
* algorithm used might itself apply some padding.
*/
byte[] tokenHeaderBytes = tokenHeader.getBytes();
// check confidentiality
int conf_flag = tokenHeaderBytes[TOKEN_FLAG_POS] &
FLAG_WRAP_CONFIDENTIAL;
// clear EC and RRC in token header for checksum calculation
if ((conf_flag == 0) && (tokenId == WRAP_ID_v2)) {
tokenHeaderBytes[4] = 0;
tokenHeaderBytes[5] = 0;
tokenHeaderBytes[6] = 0;
tokenHeaderBytes[7] = 0;
}
return cipherHelper.calculateChecksum(tokenHeaderBytes, data,
offset, len, key_usage);
}
/**
* Constructs an empty MessageToken for the local context to send to
* the peer. It also increments the local sequence number in the
* Krb5Context instance it uses after obtaining the object lock for
* it.
*
* @param tokenId the token id that should be contained in this token
* @param context the Kerberos context associated with this token
*/
MessageToken_v2(int tokenId, Krb5Context context) throws GSSException {
/*
debug("\n============================");
debug("\nMySessionKey=" +
getHexBytes(context.getMySessionKey().getBytes()));
debug("\nPeerSessionKey=" +
getHexBytes(context.getPeerSessionKey().getBytes()));
debug("\n============================\n");
*/
init(tokenId, context);
this.seqNumber = context.incrementMySequenceNumber();
}
private void init(int tokenId, Krb5Context context) throws GSSException {
this.tokenId = tokenId;
// Just for consistency check in Wrap
this.confState = context.getConfState();
this.initiator = context.isInitiator();
this.have_acceptor_subkey = context.getKeySrc() == Krb5Context.ACCEPTOR_SUBKEY;
this.cipherHelper = context.getCipherHelper(null);
// debug("In MessageToken.Cons");
}
/**
* Encodes a MessageTokenHeader onto an OutputStream.
*
* @param os the OutputStream to which this should be written
* @throws IOException is an error occurs while writing to the OutputStream
*/
protected void encodeHeader(OutputStream os) throws IOException {
tokenHeader.encode(os);
}
/**
* Encodes a MessageToken_v2 onto an OutputStream.
*
* @param os the OutputStream to which this should be written
* @throws IOException is an error occurs while encoding the token
*/
public abstract void encode(OutputStream os) throws IOException;
protected final byte[] getTokenHeader() {
return (tokenHeader.getBytes());
}
// ******************************************* //
// I N N E R C L A S S E S F O L L O W
// ******************************************* //
/**
* This inner class represents the initial portion of the message token.
* It constitutes the first 16 bytes of the message token.
*/
class MessageTokenHeader {
private int tokenId;
private byte[] bytes = new byte[TOKEN_HEADER_SIZE];
// Writes a new token header
public MessageTokenHeader(int tokenId, boolean conf) throws GSSException {
this.tokenId = tokenId;
bytes[0] = (byte) (tokenId >>> 8);
bytes[1] = (byte) (tokenId);
// Flags (Note: MIT impl requires subkey)
int flags = 0;
flags = (initiator ? 0 : FLAG_SENDER_IS_ACCEPTOR) |
((conf && tokenId != MIC_ID_v2) ?
FLAG_WRAP_CONFIDENTIAL : 0) |
(have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0);
bytes[2] = (byte) flags;
// filler
bytes[3] = (byte) FILLER;
if (tokenId == WRAP_ID_v2) {
// EC field
bytes[4] = (byte) 0;
bytes[5] = (byte) 0;
// RRC field
bytes[6] = (byte) 0;
bytes[7] = (byte) 0;
} else if (tokenId == MIC_ID_v2) {
// more filler for MicToken
for (int i = 4; i < 8; i++) {
bytes[i] = (byte) FILLER;
}
}
// Calculate SND_SEQ, only write 4 bytes from the 12th position
writeBigEndian(seqNumber, bytes, 12);
}
/**
* Reads a MessageTokenHeader from an InputStream and sets the
* appropriate confidentiality and quality of protection
* values in a MessageProp structure.
*
* @param is the InputStream to read from
* @param prop the MessageProp to populate
* @throws IOException is an error occurs while reading from the
* InputStream
*/
public MessageTokenHeader(InputStream is, MessageProp prop, int tokId)
throws IOException, GSSException {
readFully(is, bytes, 0, TOKEN_HEADER_SIZE);
tokenId = readInt(bytes, TOKEN_ID_POS);
// validate Token ID
if (tokenId != tokId) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + "Defective Token ID!");
}
/*
* Validate new GSS TokenHeader
*/
// valid acceptor_flag
// If I am initiator, the received token should have ACCEPTOR on
int acceptor_flag = (initiator ? FLAG_SENDER_IS_ACCEPTOR : 0);
int flag = bytes[TOKEN_FLAG_POS] & FLAG_SENDER_IS_ACCEPTOR;
if (flag != acceptor_flag) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + "Acceptor Flag Error!");
}
// check for confidentiality
int conf_flag = bytes[TOKEN_FLAG_POS] & FLAG_WRAP_CONFIDENTIAL;
if ((conf_flag == FLAG_WRAP_CONFIDENTIAL) &&
(tokenId == WRAP_ID_v2)) {
prop.setPrivacy(true);
} else {
prop.setPrivacy(false);
}
if (tokenId == WRAP_ID_v2) {
// validate filler
if ((bytes[3] & 0xff) != FILLER) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + "Defective Token Filler!");
}
// read EC field
ec = readBigEndian(bytes, TOKEN_EC_POS, 2);
// read RRC field
rrc = readBigEndian(bytes, TOKEN_RRC_POS, 2);
} else if (tokenId == MIC_ID_v2) {
for (int i = 3; i < 8; i++) {
if ((bytes[i] & 0xff) != FILLER) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN,
-1, getTokenName(tokenId) + ":" +
"Defective Token Filler!");
}
}
}
// set default QOP
prop.setQOP(0);
// sequence number
seqNumber = readBigEndian(bytes, 12, 4);
}
/**
* Encodes this MessageTokenHeader onto an OutputStream
* @param os the OutputStream to write to
* @throws IOException is an error occurs while writing
*/
public final void encode(OutputStream os) throws IOException {
os.write(bytes);
}
/**
* Returns the token id for the message token.
* @return the token id
* @see sun.security.jgss.krb5.Krb5Token#MIC_ID_v2
* @see sun.security.jgss.krb5.Krb5Token#WRAP_ID_v2
*/
public final int getTokenId() {
return tokenId;
}
/**
* Returns the bytes of this header.
* @return 8 bytes that form this header
*/
public final byte[] getBytes() {
return bytes;
}
} // end of class MessageTokenHeader
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
class MicToken extends MessageToken {
public MicToken(Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
super(Krb5Token.MIC_ID, context,
tokenBytes, tokenOffset, tokenLen, prop);
}
public MicToken(Krb5Context context,
InputStream is, MessageProp prop)
throws GSSException {
super(Krb5Token.MIC_ID, context, is, prop);
}
public void verify(byte[] data, int offset, int len) throws GSSException {
if (!verifySignAndSeqNumber(null, data, offset, len, null))
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in MIC token");
}
public void verify(InputStream data) throws GSSException {
byte[] dataBytes = null;
try {
dataBytes = new byte[data.available()];
data.read(dataBytes);
} catch (IOException e) {
// Error reading application data
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in MIC token");
}
verify(dataBytes, 0, dataBytes.length);
}
public MicToken(Krb5Context context, MessageProp prop,
byte[] data, int pos, int len)
throws GSSException {
super(Krb5Token.MIC_ID, context);
// debug("Application data to MicToken verify is [" +
// getHexBytes(data, pos, len) + "]\n");
if (prop == null) prop = new MessageProp(0, false);
genSignAndSeqNumber(prop, null, data, pos, len, null);
}
public MicToken(Krb5Context context, MessageProp prop,
InputStream data)
throws GSSException, IOException {
super(Krb5Token.MIC_ID, context);
byte[] dataBytes = new byte[data.available()];
data.read(dataBytes);
//debug("Application data to MicToken cons is [" +
// getHexBytes(dataBytes) + "]\n");
if (prop == null) prop = new MessageProp(0, false);
genSignAndSeqNumber(prop, null, dataBytes, 0, dataBytes.length, null);
}
protected int getSealAlg(boolean confRequested, int qop) {
return (SEAL_ALG_NONE);
}
public int encode(byte[] outToken, int offset)
throws IOException, GSSException {
// Token is small
ByteArrayOutputStream bos = new ByteArrayOutputStream();
super.encode(bos);
byte[] token = bos.toByteArray();
System.arraycopy(token, 0, outToken, offset, token.length);
return token.length;
}
public byte[] encode() throws IOException, GSSException{
// XXX Fine tune this initial size
ByteArrayOutputStream bos = new ByteArrayOutputStream(50);
encode(bos);
return bos.toByteArray();
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
/**
* This class represents the new format of GSS MIC tokens, as specified
* in RFC 4121
*
* MIC tokens = { 16-byte token-header | HMAC }
* where HMAC is on { plaintext | 16-byte token-header }
*
* @author Seema Malkani
*/
class MicToken_v2 extends MessageToken_v2 {
public MicToken_v2(Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
super(Krb5Token.MIC_ID_v2, context,
tokenBytes, tokenOffset, tokenLen, prop);
}
public MicToken_v2(Krb5Context context, InputStream is, MessageProp prop)
throws GSSException {
super(Krb5Token.MIC_ID_v2, context, is, prop);
}
public void verify(byte[] data, int offset, int len) throws GSSException {
if (!verifySign(data, offset, len))
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in MIC token");
}
public void verify(InputStream data) throws GSSException {
byte[] dataBytes = null;
try {
dataBytes = new byte[data.available()];
data.read(dataBytes);
} catch (IOException e) {
// Error reading application data
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in MIC token");
}
verify(dataBytes, 0, dataBytes.length);
}
public MicToken_v2(Krb5Context context, MessageProp prop,
byte[] data, int pos, int len)
throws GSSException {
super(Krb5Token.MIC_ID_v2, context);
// debug("Application data to MicToken verify is [" +
// getHexBytes(data, pos, len) + "]\n");
if (prop == null) prop = new MessageProp(0, false);
genSignAndSeqNumber(prop, data, pos, len);
}
public MicToken_v2(Krb5Context context, MessageProp prop, InputStream data)
throws GSSException, IOException {
super(Krb5Token.MIC_ID_v2, context);
byte[] dataBytes = new byte[data.available()];
data.read(dataBytes);
// debug("Application data to MicToken cons is [" +
// getHexBytes(dataBytes) + "]\n");
if (prop == null) prop = new MessageProp(0, false);
genSignAndSeqNumber(prop, dataBytes, 0, dataBytes.length);
}
public byte[] encode() throws IOException {
// XXX Fine tune this initial size
ByteArrayOutputStream bos = new ByteArrayOutputStream(50);
encode(bos);
return bos.toByteArray();
}
public int encode(byte[] outToken, int offset) throws IOException {
byte[] token = encode();
System.arraycopy(token, 0, outToken, offset, token.length);
return token.length;
}
public void encode(OutputStream os) throws IOException {
encodeHeader(os);
os.write(checksum);
}
}

View File

@@ -0,0 +1,262 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
import javax.security.auth.Subject;
import sun.security.krb5.Credentials;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.KrbException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import sun.security.krb5.*;
import sun.security.krb5.internal.Krb5;
/**
* Credentials of a kerberos acceptor. A KerberosPrincipal object (kp) is
* the principal. It can be specified as the serverPrincipal argument
* in the getInstance() method, or uses only KerberosPrincipal in the subject.
* Otherwise, the creds object is unbound and kp is null.
*
* The class also encapsulates various secrets, which can be:
*
* 1. Some KerberosKeys (generated from password)
* 2. Some KeyTabs (for a typical service based on keytabs)
* 3. A TGT (for S4U2proxy extension or user2user)
*
* Note that some secrets can coexist. For example, a user2user service
* can use its keytab (or keys) if the client can successfully obtain a
* normal service ticket, or it can use the TGT (actually, the session key
* of the TGT) if the client can only acquire a service ticket
* of ENC-TKT-IN-SKEY style.
*
* @since 1.8
*/
public final class ServiceCreds {
// The principal, or null if unbound
private KerberosPrincipal kp;
// All principals in the subject's princ set
private Set<KerberosPrincipal> allPrincs;
// All private credentials that can be used
private List<KeyTab> ktabs;
private List<KerberosKey> kk;
private KerberosTicket tgt;
private boolean destroyed;
private ServiceCreds() {
// Make sure this class cannot be instantiated externally.
}
/**
* Creates a ServiceCreds object based on info in a Subject for
* a given principal name (if specified).
* @return the object, or null if there is no private creds for it
*/
public static ServiceCreds getInstance(
Subject subj, String serverPrincipal) {
ServiceCreds sc = new ServiceCreds();
sc.allPrincs =
subj.getPrincipals(KerberosPrincipal.class);
// Compatibility. A key implies its own principal
for (KerberosKey key: SubjectComber.findMany(
subj, serverPrincipal, null, KerberosKey.class)) {
sc.allPrincs.add(key.getPrincipal());
}
if (serverPrincipal != null) { // A named principal
sc.kp = new KerberosPrincipal(serverPrincipal);
} else {
// For compatibility reason, we set the name of default principal
// to the "only possible" name it can take, which means there is
// only one KerberosPrincipal and there is no unbound keytabs
if (sc.allPrincs.size() == 1) {
boolean hasUnbound = false;
for (KeyTab ktab: SubjectComber.findMany(
subj, null, null, KeyTab.class)) {
if (!ktab.isBound()) {
hasUnbound = true;
break;
}
}
if (!hasUnbound) {
sc.kp = sc.allPrincs.iterator().next();
serverPrincipal = sc.kp.getName();
}
}
}
sc.ktabs = SubjectComber.findMany(
subj, serverPrincipal, null, KeyTab.class);
sc.kk = SubjectComber.findMany(
subj, serverPrincipal, null, KerberosKey.class);
sc.tgt = SubjectComber.find(
subj, null, serverPrincipal, KerberosTicket.class);
if (sc.ktabs.isEmpty() && sc.kk.isEmpty() && sc.tgt == null) {
return null;
}
sc.destroyed = false;
return sc;
}
// can be null
public String getName() {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
return kp == null ? null : kp.getName();
}
/**
* Gets keys for "someone". Used in 2 cases:
* 1. By TLS because it needs to get keys before client comes in.
* 2. As a fallback in getEKeys() below.
* This method can still return an empty array.
*/
public KerberosKey[] getKKeys() {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
KerberosPrincipal one = kp; // named principal
if (one == null && !allPrincs.isEmpty()) { // or, a known principal
one = allPrincs.iterator().next();
}
if (one == null) { // Or, some random one
for (KeyTab ktab: ktabs) {
// Must be unbound keytab, otherwise, allPrincs is not empty
PrincipalName pn =
Krb5Util.snapshotFromJavaxKeyTab(ktab).getOneName();
if (pn != null) {
one = new KerberosPrincipal(pn.getName());
break;
}
}
}
if (one != null) {
return getKKeys(one);
} else {
return new KerberosKey[0];
}
}
/**
* Get kkeys for a principal,
* @param princ the target name initiator requests. Not null.
* @return keys for the princ, never null, might be empty
*/
public KerberosKey[] getKKeys(KerberosPrincipal princ) {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
ArrayList<KerberosKey> keys = new ArrayList<>();
if (kp != null && !princ.equals(kp)) { // named principal
return new KerberosKey[0];
}
for (KerberosKey k: kk) {
if (k.getPrincipal().equals(princ)) {
keys.add(k);
}
}
for (KeyTab ktab: ktabs) {
if (ktab.getPrincipal() == null && ktab.isBound()) {
// legacy bound keytab. although we don't know who
// the bound principal is, it must be in allPrincs
if (!allPrincs.contains(princ)) {
continue; // skip this legacy bound keytab
}
}
for (KerberosKey k: ktab.getKeys(princ)) {
keys.add(k);
}
}
return keys.toArray(new KerberosKey[keys.size()]);
}
/**
* Gets EKeys for a principal.
* @param princ the target name initiator requests. Not null.
* @return keys for the princ, never null, might be empty
*/
public EncryptionKey[] getEKeys(PrincipalName princ) {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
KerberosKey[] kkeys = getKKeys(new KerberosPrincipal(princ.getName()));
if (kkeys.length == 0) {
// Fallback: old JDK does not perform real name checking. If the
// acceptor has host.sun.com but initiator requests for host,
// as long as their keys match (i.e. keys for one can decrypt
// the other's service ticket), the authentication is OK.
// There are real customers depending on this to use different
// names for a single service.
kkeys = getKKeys();
}
EncryptionKey[] ekeys = new EncryptionKey[kkeys.length];
for (int i=0; i<ekeys.length; i++) {
ekeys[i] = new EncryptionKey(
kkeys[i].getEncoded(), kkeys[i].getKeyType(),
new Integer(kkeys[i].getVersionNumber()));
}
return ekeys;
}
public Credentials getInitCred() {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
if (tgt == null) {
return null;
}
try {
return Krb5Util.ticketToCreds(tgt);
} catch (KrbException | IOException e) {
return null;
}
}
public void destroy() {
// Do not wipe out real keys because they are references to the
// priv creds in subject. Just make it useless.
destroyed = true;
kp = null;
ktabs.clear();
kk.clear();
tgt = null;
}
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import sun.security.krb5.JavaxSecurityAuthKerberosAccess;
import sun.security.krb5.KerberosSecrets;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.Subject;
import javax.security.auth.DestroyFailedException;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
/**
* This utility looks through the current Subject and retrieves private
* credentials for the desired client/server principals.
*
* @author Ram Marti
* @since 1.4.2
*/
class SubjectComber {
private static final boolean DEBUG = Krb5Util.DEBUG;
/**
* Default constructor
*/
private SubjectComber() { // Cannot create one of these
}
static <T> T find(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> credClass) {
// findAux returns T if oneOnly.
return credClass.cast(findAux(subject, serverPrincipal,
clientPrincipal, credClass, true));
}
@SuppressWarnings("unchecked") // findAux returns List<T> if !oneOnly.
static <T> List<T> findMany(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> credClass) {
return (List<T>)findAux(subject, serverPrincipal, clientPrincipal,
credClass, false);
}
/**
* Find private credentials for the specified client/server principals
* in the subject. Returns null if the subject is null.
*
* @return the private credentials
*/
// Returns T if oneOnly and List<T> if !oneOnly.
private static <T> Object findAux(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> credClass, boolean oneOnly) {
if (subject == null) {
return null;
} else {
List<T> answer = (oneOnly ? null : new ArrayList<T>());
if (credClass == KeyTab.class) {
Iterator<KeyTab> iterator =
subject.getPrivateCredentials(KeyTab.class).iterator();
while (iterator.hasNext()) {
KeyTab t = iterator.next();
if (serverPrincipal != null && t.isBound()) {
KerberosPrincipal name = t.getPrincipal();
if (name != null) {
if (!serverPrincipal.equals(name.getName())) {
continue;
}
} else {
// legacy bound keytab. although we don't know who
// the bound principal is, it must be in allPrincs
boolean found = false;
for (KerberosPrincipal princ:
subject.getPrincipals(KerberosPrincipal.class)) {
if (princ.getName().equals(serverPrincipal)) {
found = true;
break;
}
}
if (!found) continue;
}
}
// Check passed, we can add now
if (DEBUG) {
System.out.println("Found " + credClass.getSimpleName()
+ " " + t);
}
if (oneOnly) {
return t;
} else {
answer.add(credClass.cast(t));
}
}
} else if (credClass == KerberosKey.class) {
// We are looking for credentials for the serverPrincipal
Iterator<KerberosKey> iterator =
subject.getPrivateCredentials(KerberosKey.class).iterator();
while (iterator.hasNext()) {
KerberosKey t = iterator.next();
String name = t.getPrincipal().getName();
if (serverPrincipal == null || serverPrincipal.equals(name)) {
if (DEBUG) {
System.out.println("Found " +
credClass.getSimpleName() + " for " + name);
}
if (oneOnly) {
return t;
} else {
answer.add(credClass.cast(t));
}
}
}
} else if (credClass == KerberosTicket.class) {
// we are looking for a KerberosTicket credentials
// for client-service principal pair
Set<Object> pcs = subject.getPrivateCredentials();
synchronized (pcs) {
Iterator<Object> iterator = pcs.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
if (!(obj instanceof KerberosTicket)) {
continue;
}
@SuppressWarnings("unchecked")
KerberosTicket ticket = (KerberosTicket)obj;
if (DEBUG) {
System.out.println("Found ticket for "
+ ticket.getClient()
+ " to go to "
+ ticket.getServer()
+ " expiring on "
+ ticket.getEndTime());
}
if (!ticket.isCurrent()) {
// let us remove the ticket from the Subject
// Note that both TGT and service ticket will be
// removed upon expiration
if (!subject.isReadOnly()) {
iterator.remove();
try {
ticket.destroy();
if (DEBUG) {
System.out.println("Removed and destroyed "
+ "the expired Ticket \n"
+ ticket);
}
} catch (DestroyFailedException dfe) {
if (DEBUG) {
System.out.println("Expired ticket not" +
" detroyed successfully. " + dfe);
}
}
}
continue;
}
String serverMatch = findServerMatch(serverPrincipal, ticket);
if (serverMatch != null) {
String clientMatch = findClientMatch(clientPrincipal, ticket);
if (clientMatch != null) {
if (oneOnly) {
return ticket;
} else {
// Record names so that tickets will
// all belong to same principals
if (clientPrincipal == null) {
clientPrincipal = clientMatch;
}
if (serverPrincipal == null) {
serverPrincipal = serverMatch;
}
answer.add(credClass.cast(ticket));
}
}
}
}
}
}
return answer;
}
}
private static String findServerMatch(String input, KerberosTicket ticket) {
KerberosPrincipal serverAlias = KerberosSecrets
.getJavaxSecurityAuthKerberosAccess()
.kerberosTicketGetServerAlias(ticket);
if (input != null) {
return ((serverAlias != null && input.equals(serverAlias.getName())) ||
input.equals(ticket.getServer().getName()))
? input : null;
} else {
return serverAlias != null
? serverAlias.getName()
: ticket.getServer().getName();
}
}
private static String findClientMatch(String input, KerberosTicket ticket) {
JavaxSecurityAuthKerberosAccess access = KerberosSecrets
.getJavaxSecurityAuthKerberosAccess();
KerberosPrincipal clientAlias = access.kerberosTicketGetClientAlias(ticket);
KerberosTicket proxy = access.kerberosTicketGetProxy(ticket);
if (input != null) {
return ((clientAlias != null && input.equals(clientAlias.getName())) ||
(proxy != null && input.equals(proxy.getClient().getName())) ||
(proxy == null && input.equals(ticket.getClient().getName())))
? input : null;
} else {
if (clientAlias != null) {
return clientAlias.getName();
} else if (proxy != null) {
return proxy.getClient().getName();
} else {
return ticket.getClient().getName();
}
}
}
}

View File

@@ -0,0 +1,532 @@
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import sun.security.krb5.Confounder;
/**
* This class represents a token emitted by the GSSContext.wrap()
* call. It is a MessageToken except that it also contains plaintext
* or encrypted data at the end. A wrapToken has certain other rules
* that are peculiar to it and different from a MICToken, which is
* another type of MessageToken. All data in a WrapToken is prepended
* by a random counfounder of 8 bytes. All data in a WrapToken is
* also padded with one to eight bytes where all bytes are equal in
* value to the number of bytes being padded. Thus, all application
* data is replaced by (confounder || data || padding).
*
* @author Mayank Upadhyay
*/
class WrapToken extends MessageToken {
/**
* The size of the random confounder used in a WrapToken.
*/
static final int CONFOUNDER_SIZE = 8;
/*
* The padding used with a WrapToken. All data is padded to the
* next multiple of 8 bytes, even if its length is already
* multiple of 8.
* Use this table as a quick way to obtain padding bytes by
* indexing it with the number of padding bytes required.
*/
static final byte[][] pads = {
null, // No, no one escapes padding
{0x01},
{0x02, 0x02},
{0x03, 0x03, 0x03},
{0x04, 0x04, 0x04, 0x04},
{0x05, 0x05, 0x05, 0x05, 0x05},
{0x06, 0x06, 0x06, 0x06, 0x06, 0x06},
{0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07},
{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}
};
/*
* A token may come in either in an InputStream or as a
* byte[]. Store a reference to it in either case and process
* it's data only later when getData() is called and
* decryption/copying is needed to be done. Note that JCE can
* decrypt both from a byte[] and from an InputStream.
*/
private boolean readTokenFromInputStream = true;
private InputStream is = null;
private byte[] tokenBytes = null;
private int tokenOffset = 0;
private int tokenLen = 0;
/*
* Application data may come from an InputStream or from a
* byte[]. However, it will always be stored and processed as a
* byte[] since
* (a) the MessageDigest class only accepts a byte[] as input and
* (b) It allows writing to an OuputStream via a CipherOutputStream.
*/
private byte[] dataBytes = null;
private int dataOffset = 0;
private int dataLen = 0;
// the len of the token data: (confounder || data || padding)
private int dataSize = 0;
// Accessed by CipherHelper
byte[] confounder = null;
byte[] padding = null;
private boolean privacy = false;
/**
* Constructs a WrapToken from token bytes obtained from the
* peer.
* @param context the mechanism context associated with this
* token
* @param tokenBytes the bytes of the token
* @param tokenOffset the offset of the token
* @param tokenLen the length of the token
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective
*/
public WrapToken(Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
// Just parse the MessageToken part first
super(Krb5Token.WRAP_ID, context,
tokenBytes, tokenOffset, tokenLen, prop);
this.readTokenFromInputStream = false;
// Will need the token bytes again when extracting data
this.tokenBytes = tokenBytes;
this.tokenOffset = tokenOffset;
this.tokenLen = tokenLen;
this.privacy = prop.getPrivacy();
dataSize =
getGSSHeader().getMechTokenLength() - getKrb5TokenSize();
}
/**
* Constructs a WrapToken from token bytes read on the fly from
* an InputStream.
* @param context the mechanism context associated with this
* token
* @param is the InputStream containing the token bytes
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective or if there is
* a problem reading from the InputStream
*/
public WrapToken(Krb5Context context,
InputStream is, MessageProp prop)
throws GSSException {
// Just parse the MessageToken part first
super(Krb5Token.WRAP_ID, context, is, prop);
// Will need the token bytes again when extracting data
this.is = is;
this.privacy = prop.getPrivacy();
/*
debug("WrapToken Cons: gssHeader.getMechTokenLength=" +
getGSSHeader().getMechTokenLength());
debug("\n token size="
+ getTokenSize());
*/
dataSize =
getGSSHeader().getMechTokenLength() - getTokenSize();
// debug("\n dataSize=" + dataSize);
// debug("\n");
}
/**
* Obtains the application data that was transmitted in this
* WrapToken.
* @return a byte array containing the application data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
public byte[] getData() throws GSSException {
byte[] temp = new byte[dataSize];
getData(temp, 0);
// Remove the confounder and the padding
byte[] retVal = new byte[dataSize - confounder.length -
padding.length];
System.arraycopy(temp, 0, retVal, 0, retVal.length);
return retVal;
}
/**
* Obtains the application data that was transmitted in this
* WrapToken, writing it into an application provided output
* array.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @return the size of the data written
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
public int getData(byte[] dataBuf, int dataBufOffset)
throws GSSException {
if (readTokenFromInputStream)
getDataFromStream(dataBuf, dataBufOffset);
else
getDataFromBuffer(dataBuf, dataBufOffset);
return (dataSize - confounder.length - padding.length);
}
/**
* Helper routine to obtain the application data transmitted in
* this WrapToken. It is called if the WrapToken was constructed
* with a byte array as input.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
private void getDataFromBuffer(byte[] dataBuf, int dataBufOffset)
throws GSSException {
GSSHeader gssHeader = getGSSHeader();
int dataPos = tokenOffset +
gssHeader.getLength() + getTokenSize();
if (dataPos + dataSize > tokenOffset + tokenLen)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Insufficient data in "
+ getTokenName(getTokenId()));
// debug("WrapToken cons: data is token is [" +
// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");
confounder = new byte[CONFOUNDER_SIZE];
// Do decryption if this token was privacy protected.
if (privacy) {
cipherHelper.decryptData(this,
tokenBytes, dataPos, dataSize, dataBuf, dataBufOffset);
/*
debug("\t\tDecrypted data is [" +
getHexBytes(confounder) + " " +
getHexBytes(dataBuf, dataBufOffset,
dataSize - CONFOUNDER_SIZE - padding.length) +
getHexBytes(padding) +
"]\n");
*/
} else {
// Token data is in cleartext
// debug("\t\tNo encryption was performed by peer.\n");
System.arraycopy(tokenBytes, dataPos,
confounder, 0, CONFOUNDER_SIZE);
int padSize = tokenBytes[dataPos + dataSize - 1];
if (padSize < 0)
padSize = 0;
if (padSize > 8)
padSize %= 8;
padding = pads[padSize];
// debug("\t\tPadding applied was: " + padSize + "\n");
System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
dataBuf, dataBufOffset, dataSize -
CONFOUNDER_SIZE - padSize);
// byte[] debugbuf = new byte[dataSize - CONFOUNDER_SIZE - padSize];
// System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
// debugbuf, 0, debugbuf.length);
// debug("\t\tData is: " + getHexBytes(debugbuf, debugbuf.length));
}
/*
* Make sure sign and sequence number are not corrupt
*/
if (!verifySignAndSeqNumber(confounder,
dataBuf, dataBufOffset,
dataSize - CONFOUNDER_SIZE
- padding.length,
padding))
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in Wrap token");
}
/**
* Helper routine to obtain the application data transmitted in
* this WrapToken. It is called if the WrapToken was constructed
* with an Inputstream.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
private void getDataFromStream(byte[] dataBuf, int dataBufOffset)
throws GSSException {
GSSHeader gssHeader = getGSSHeader();
// Don't check the token length. Data will be read on demand from
// the InputStream.
// debug("WrapToken cons: data will be read from InputStream.\n");
confounder = new byte[CONFOUNDER_SIZE];
try {
// Do decryption if this token was privacy protected.
if (privacy) {
cipherHelper.decryptData(this, is, dataSize,
dataBuf, dataBufOffset);
// debug("\t\tDecrypted data is [" +
// getHexBytes(confounder) + " " +
// getHexBytes(dataBuf, dataBufOffset,
// dataSize - CONFOUNDER_SIZE - padding.length) +
// getHexBytes(padding) +
// "]\n");
} else {
// Token data is in cleartext
// debug("\t\tNo encryption was performed by peer.\n");
readFully(is, confounder);
if (cipherHelper.isArcFour()) {
padding = pads[1];
readFully(is, dataBuf, dataBufOffset, dataSize-CONFOUNDER_SIZE-1);
} else {
// Data is always a multiple of 8 with this GSS Mech
// Copy all but last block as they are
int numBlocks = (dataSize - CONFOUNDER_SIZE)/8 - 1;
int offset = dataBufOffset;
for (int i = 0; i < numBlocks; i++) {
readFully(is, dataBuf, offset, 8);
offset += 8;
}
byte[] finalBlock = new byte[8];
readFully(is, finalBlock);
int padSize = finalBlock[7];
padding = pads[padSize];
// debug("\t\tPadding applied was: " + padSize + "\n");
System.arraycopy(finalBlock, 0, dataBuf, offset,
finalBlock.length - padSize);
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(getTokenId())
+ ": " + e.getMessage());
}
/*
* Make sure sign and sequence number are not corrupt
*/
if (!verifySignAndSeqNumber(confounder,
dataBuf, dataBufOffset,
dataSize - CONFOUNDER_SIZE
- padding.length,
padding))
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in Wrap token");
}
/**
* Helper routine to pick the right padding for a certain length
* of application data. Every application message has some
* padding between 1 and 8 bytes.
* @param len the length of the application data
* @return the padding to be applied
*/
private byte[] getPadding(int len) {
int padSize = 0;
// For RC4-HMAC, all padding is rounded up to 1 byte.
// One byte is needed to say that there is 1 byte of padding.
if (cipherHelper.isArcFour()) {
padSize = 1;
} else {
padSize = len % 8;
padSize = 8 - padSize;
}
return pads[padSize];
}
public WrapToken(Krb5Context context, MessageProp prop,
byte[] dataBytes, int dataOffset, int dataLen)
throws GSSException {
super(Krb5Token.WRAP_ID, context);
confounder = Confounder.bytes(CONFOUNDER_SIZE);
padding = getPadding(dataLen);
dataSize = confounder.length + dataLen + padding.length;
this.dataBytes = dataBytes;
this.dataOffset = dataOffset;
this.dataLen = dataLen;
/*
debug("\nWrapToken cons: data to wrap is [" +
getHexBytes(confounder) + " " +
getHexBytes(dataBytes, dataOffset, dataLen) + " " +
// padding is never null for Wrap
getHexBytes(padding) + "]\n");
*/
genSignAndSeqNumber(prop,
confounder,
dataBytes, dataOffset, dataLen,
padding);
/*
* If the application decides to ask for privacy when the context
* did not negotiate for it, do not provide it. The peer might not
* have support for it. The app will realize this with a call to
* pop.getPrivacy() after wrap().
*/
if (!context.getConfState())
prop.setPrivacy(false);
privacy = prop.getPrivacy();
}
public void encode(OutputStream os) throws IOException, GSSException {
super.encode(os);
// debug("Writing data: [");
if (!privacy) {
// debug(getHexBytes(confounder, confounder.length));
os.write(confounder);
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
os.write(dataBytes, dataOffset, dataLen);
// debug(" " + getHexBytes(padding, padding.length));
os.write(padding);
} else {
cipherHelper.encryptData(this, confounder,
dataBytes, dataOffset, dataLen, padding, os);
}
// debug("]\n");
}
public byte[] encode() throws IOException, GSSException {
// XXX Fine tune this initial size
ByteArrayOutputStream bos = new ByteArrayOutputStream(dataSize + 50);
encode(bos);
return bos.toByteArray();
}
public int encode(byte[] outToken, int offset)
throws IOException, GSSException {
// Token header is small
ByteArrayOutputStream bos = new ByteArrayOutputStream();
super.encode(bos);
byte[] header = bos.toByteArray();
System.arraycopy(header, 0, outToken, offset, header.length);
offset += header.length;
// debug("WrapToken.encode: Writing data: [");
if (!privacy) {
// debug(getHexBytes(confounder, confounder.length));
System.arraycopy(confounder, 0, outToken, offset,
confounder.length);
offset += confounder.length;
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
System.arraycopy(dataBytes, dataOffset, outToken, offset,
dataLen);
offset += dataLen;
// debug(" " + getHexBytes(padding, padding.length));
System.arraycopy(padding, 0, outToken, offset, padding.length);
} else {
cipherHelper.encryptData(this, confounder, dataBytes,
dataOffset, dataLen, padding, outToken, offset);
// debug(getHexBytes(outToken, offset, dataSize));
}
// debug("]\n");
// %%% assume that plaintext length == ciphertext len
return (header.length + confounder.length + dataLen + padding.length);
}
protected int getKrb5TokenSize() throws GSSException {
return (getTokenSize() + dataSize);
}
protected int getSealAlg(boolean conf, int qop) throws GSSException {
if (!conf) {
return SEAL_ALG_NONE;
}
// ignore QOP
return cipherHelper.getSealAlg();
}
// This implementation is way too conservative. And it certainly
// doesn't return the maximum limit.
static int getSizeLimit(int qop, boolean confReq, int maxTokenSize,
CipherHelper ch) throws GSSException {
return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) -
(getTokenSize(ch) + CONFOUNDER_SIZE) - 8); /* safety */
}
}

View File

@@ -0,0 +1,231 @@
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import sun.security.krb5.Confounder;
/**
* This class represents the new format of GSS tokens, as specified in RFC
* 4121, emitted by the GSSContext.wrap() call. It is a MessageToken except
* that it also contains plaintext or encrypted data at the end. A WrapToken
* has certain other rules that are peculiar to it and different from a
* MICToken, which is another type of MessageToken. All data in a WrapToken is
* prepended by a random confounder of 16 bytes. Thus, all application data
* is replaced by (confounder || data || tokenHeader || checksum).
*
* @author Seema Malkani
*/
class WrapToken_v2 extends MessageToken_v2 {
// Accessed by CipherHelper
byte[] confounder = null;
private final boolean privacy;
/**
* Constructs a WrapToken from token bytes obtained from the
* peer.
* @param context the mechanism context associated with this
* token
* @param tokenBytes the bytes of the token
* @param tokenOffset the offset of the token
* @param tokenLen the length of the token
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective
*/
public WrapToken_v2(Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
super(Krb5Token.WRAP_ID_v2, context,
tokenBytes, tokenOffset, tokenLen, prop);
this.privacy = prop.getPrivacy();
}
/**
* Constructs a WrapToken from token bytes read on the fly from
* an InputStream.
* @param context the mechanism context associated with this
* token
* @param is the InputStream containing the token bytes
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective or if there is
* a problem reading from the InputStream
*/
public WrapToken_v2(Krb5Context context,
InputStream is, MessageProp prop)
throws GSSException {
super(Krb5Token.WRAP_ID_v2, context, is, prop);
this.privacy = prop.getPrivacy();
}
/**
* Obtains the application data that was transmitted in this
* WrapToken.
* @return a byte array containing the application data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
public byte[] getData() throws GSSException {
byte[] temp = new byte[tokenDataLen];
int len = getData(temp, 0);
return Arrays.copyOf(temp, len);
}
/**
* Obtains the application data that was transmitted in this
* WrapToken, writing it into an application provided output
* array.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @return the size of the data written
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
public int getData(byte[] dataBuf, int dataBufOffset)
throws GSSException {
// debug("WrapToken cons: data is token is [" +
// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");
// Do decryption if this token was privacy protected.
if (privacy) {
// decrypt data
cipherHelper.decryptData(this, tokenData, 0, tokenDataLen,
dataBuf, dataBufOffset, getKeyUsage());
return tokenDataLen - CONFOUNDER_SIZE -
TOKEN_HEADER_SIZE - cipherHelper.getChecksumLength();
} else {
// Token data is in cleartext
// debug("\t\tNo encryption was performed by peer.\n");
// data
int data_length = tokenDataLen - cipherHelper.getChecksumLength();
System.arraycopy(tokenData, 0,
dataBuf, dataBufOffset,
data_length);
// debug("\t\tData is: " + getHexBytes(dataBuf, data_length));
/*
* Make sure checksum is not corrupt
*/
if (!verifySign(dataBuf, dataBufOffset, data_length)) {
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum in Wrap token");
}
return data_length;
}
}
/**
* Writes a WrapToken_v2 object
*/
public WrapToken_v2(Krb5Context context, MessageProp prop,
byte[] dataBytes, int dataOffset, int dataLen)
throws GSSException {
super(Krb5Token.WRAP_ID_v2, context);
confounder = Confounder.bytes(CONFOUNDER_SIZE);
// debug("\nWrapToken cons: data to wrap is [" +
// getHexBytes(confounder) + " " +
// getHexBytes(dataBytes, dataOffset, dataLen) + "]\n");
genSignAndSeqNumber(prop, dataBytes, dataOffset, dataLen);
/*
* If the application decides to ask for privacy when the context
* did not negotiate for it, do not provide it. The peer might not
* have support for it. The app will realize this with a call to
* pop.getPrivacy() after wrap().
*/
if (!context.getConfState())
prop.setPrivacy(false);
privacy = prop.getPrivacy();
if (!privacy) {
// Wrap Tokens (without confidentiality) =
// { 16 byte token_header | plaintext | 12-byte HMAC }
// where HMAC is on { plaintext | token_header }
tokenData = new byte[dataLen + checksum.length];
System.arraycopy(dataBytes, dataOffset, tokenData, 0, dataLen);
System.arraycopy(checksum, 0, tokenData, dataLen, checksum.length);
} else {
// Wrap Tokens (with confidentiality) =
// { 16 byte token_header |
// Encrypt(16-byte confounder | plaintext | token_header) |
// 12-byte HMAC }
tokenData = cipherHelper.encryptData(this, confounder, getTokenHeader(),
dataBytes, dataOffset, dataLen, getKeyUsage());
}
}
public void encode(OutputStream os) throws IOException {
encodeHeader(os);
os.write(tokenData);
}
public byte[] encode() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream(
MessageToken_v2.TOKEN_HEADER_SIZE + tokenData.length);
encode(bos);
return bos.toByteArray();
}
public int encode(byte[] outToken, int offset) throws IOException {
byte[] token = encode();
System.arraycopy(token, 0, outToken, offset, token.length);
return token.length;
}
// This implementation is way to conservative. And it certainly
// doesn't return the maximum limit.
static int getSizeLimit(int qop, boolean confReq, int maxTokenSize,
CipherHelper ch) throws GSSException {
return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) -
(TOKEN_HEADER_SIZE + ch.getChecksumLength() + CONFOUNDER_SIZE)
- 8 /* safety */);
}
}

View File

@@ -0,0 +1,408 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.jgss.spi;
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
* security context.
* A GSSContextSpi object can be thought of having 3 states:
* -before initialization
* -during initialization with its peer
* -after it is established
* <p>
* The context options can only be requested in state 1. In state 3,
* the per message operations are available to the callers. The get
* methods for the context options will return the requested options
* while in state 1 and 2, and the established values in state 3.
* Some mechanisms may allow the access to the per-message operations
* and the context flags before the context is fully established. The
* isProtReady method is used to indicate that these services are
* available.
* <p>
* <strong>
* Context establishment tokens are defined in a mechanism independent
* format in section 3.1 of RFC 2743. The GSS-Framework will add
* and remove the mechanism independent header portion of this token format
* depending on whether a token is received or is being sent. The mechanism
* should only generate or expect to read the inner-context token portion.
* <br>
* On the other hands, tokens used for per-message calls are generated
* entirely by the mechanism. It is possible that the mechanism chooses to
* encase inner-level per-message tokens in a header similar to that used
* for initial tokens, however, this is upto the mechanism to do. The token
* to/from the per-message calls are opaque to the GSS-Framework.
* </strong>
* <p>
* An attempt has been made to allow for reading the peer's tokens from an
* InputStream and writing tokens for the peer to an OutputStream. This
* allows applications to pass in streams that are obtained from their network
* connections and thus minimize the buffer copies that will happen. This
* is especially important for tokens generated by wrap() which are
* proportional in size to the length of the application data being
* wrapped, and are probably also the most frequently used type of tokens.
* <p>
* It is anticipated that most applications will want to use wrap() in a
* fashion where they obtain the application bytes to wrap from a byte[]
* but want to output the wrap token straight to an
* OutputStream. Similarly, they will want to use unwrap() where they read
* the token directly form an InputStream but output it to some byte[] for
* the application to process. Unfortunately the high level GSS bindings
* do not contain overloaded forms of wrap() and unwrap() that do just
* this, however we have accomodated those cases here with the expectation
* that this will be rolled into the high level bindings sooner or later.
*
* @author Mayank Upadhyay
*/
public interface GSSContextSpi {
public Provider getProvider();
// The specification for the following methods mirrors the
// specification of the same methods in the GSSContext interface, as
// defined in RFC 2853.
public void requestLifetime(int lifetime) throws GSSException;
public void requestMutualAuth(boolean state) throws GSSException;
public void requestReplayDet(boolean state) throws GSSException;
public void requestSequenceDet(boolean state) throws GSSException;
public void requestCredDeleg(boolean state) throws GSSException;
public void requestAnonymity(boolean state) throws GSSException;
public void requestConf(boolean state) throws GSSException;
public void requestInteg(boolean state) throws GSSException;
public void requestDelegPolicy(boolean state) throws GSSException;
public void setChannelBinding(ChannelBinding cb) throws GSSException;
public boolean getCredDelegState();
public boolean getMutualAuthState();
public boolean getReplayDetState();
public boolean getSequenceDetState();
public boolean getAnonymityState();
public boolean getDelegPolicyState();
public boolean isTransferable() throws GSSException;
public boolean isProtReady();
public boolean isInitiator();
public boolean getConfState();
public boolean getIntegState();
public int getLifetime();
public boolean isEstablished();
public GSSNameSpi getSrcName() throws GSSException;
public GSSNameSpi getTargName() throws GSSException;
public Oid getMech() throws GSSException;
public GSSCredentialSpi getDelegCred() throws GSSException;
/**
* Initiator context establishment call. This method may be
* required to be called several times. A CONTINUE_NEEDED return
* call indicates that more calls are needed after the next token
* is received from the peer.
* <p>
* This method is called by the GSS-Framework when the application
* calls the initSecContext method on the GSSContext implementation
* that it has a reference to.
* <p>
* All overloaded forms of GSSContext.initSecContext() can be handled
* with this mechanism level initSecContext. Since the output token
* from this method is a fixed size, not exeedingly large, and a one
* time deal, an overloaded form that takes an OutputStream has not
* been defined. The GSS-Framwork can write the returned byte[] to any
* application provided OutputStream. Similarly, any application input
* int he form of byte arrays will be wrapped in an input stream by the
* GSS-Framework and then passed here.
* <p>
* <strong>
* The GSS-Framework will strip off the leading mechanism independent
* GSS-API header. In other words, only the mechanism specific
* inner-context token of RFC 2743 section 3.1 will be available on the
* InputStream.
* </strong>
*
* @param is contains the inner context token portion of the GSS token
* received from the peer. On the first call to initSecContext, there
* will be no token hence it will be ignored.
* @param mechTokenSize the size of the inner context token as read by
* the GSS-Framework from the mechanism independent GSS-API level
* header.
* @return any inner-context token required to be sent to the peer as
* part of a GSS token. The mechanism should not add the mechanism
* independent part of the token. The GSS-Framework will add that on
* the way out.
* @exception GSSException may be thrown
*/
public byte[] initSecContext(InputStream is, int mechTokenSize)
throws GSSException;
/**
* Acceptor's context establishment call. This method may be
* required to be called several times. A CONTINUE_NEEDED return
* call indicates that more calls are needed after the next token
* is received from the peer.
* <p>
* This method is called by the GSS-Framework when the application
* calls the acceptSecContext method on the GSSContext implementation
* that it has a reference to.
* <p>
* All overloaded forms of GSSContext.acceptSecContext() can be handled
* with this mechanism level acceptSecContext. Since the output token
* from this method is a fixed size, not exeedingly large, and a one
* time deal, an overloaded form that takes an OutputStream has not
* been defined. The GSS-Framwork can write the returned byte[] to any
* application provided OutputStream. Similarly, any application input
* int he form of byte arrays will be wrapped in an input stream by the
* GSS-Framework and then passed here.
* <p>
* <strong>
* The GSS-Framework will strip off the leading mechanism independent
* GSS-API header. In other words, only the mechanism specific
* inner-context token of RFC 2743 section 3.1 will be available on the
* InputStream.
* </strong>
*
* @param is contains the inner context token portion of the GSS token
* received from the peer.
* @param mechTokenSize the size of the inner context token as read by
* the GSS-Framework from the mechanism independent GSS-API level
* header.
* @return any inner-context token required to be sent to the peer as
* part of a GSS token. The mechanism should not add the mechanism
* independent part of the token. The GSS-Framework will add that on
* the way out.
* @exception GSSException may be thrown
*/
public byte[] acceptSecContext(InputStream is, int mechTokenSize)
throws GSSException;
/**
* Queries the context for largest data size to accommodate
* the specified protection and for the token to remain less then
* maxTokSize.
*
* @param qop the quality of protection that the context will be
* asked to provide.
* @param confReq a flag indicating whether confidentiality will be
* requested or not
* @param maxTokSize the maximum size of the output token
* @return the maximum size for the input message that can be
* provided to the wrap() method in order to guarantee that these
* requirements are met.
* @exception GSSException may be thrown
*/
public int getWrapSizeLimit(int qop, boolean confReq, int maxTokSize)
throws GSSException;
/**
* Provides per-message token encapsulation.
*
* @param is the user-provided message to be protected
* @param os the token to be sent to the peer. It includes
* the message from <i>is</i> with the requested protection.
* @param msgProp on input it contains the requested qop and
* confidentiality state, on output, the applied values
* @exception GSSException may be thrown
* @see unwrap
*/
public void wrap(InputStream is, OutputStream os, MessageProp msgProp)
throws GSSException;
/**
* For apps that want simplicity and don't care about buffer copies.
*/
public byte[] wrap(byte inBuf[], int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* For apps that care about buffer copies but either cannot use streams
* or want to avoid them for whatever reason. (Say, they are using
* block ciphers.)
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int wrap(byte inBuf[], int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* For apps that want to read from a specific application provided
* buffer but want to write directly to the network stream.
*/
/*
* Can be achieved by converting the input buffer to a
* ByteInputStream. Provided to keep the API consistent
* with unwrap.
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public void wrap(byte inBuf[], int offset, int len,
OutputStream os, MessageProp msgProp)
throws GSSException;
*/
/**
* Retrieves the message token previously encapsulated in the wrap
* call.
*
* @param is the token from the peer
* @param os unprotected message data
* @param msgProp will contain the applied qop and confidentiality
* of the input token and any informatory status values
* @exception GSSException may be thrown
* @see wrap
*/
public void unwrap(InputStream is, OutputStream os,
MessageProp msgProp) throws GSSException;
/**
* For apps that want simplicity and don't care about buffer copies.
*/
public byte[] unwrap(byte inBuf[], int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* For apps that care about buffer copies but either cannot use streams
* or want to avoid them for whatever reason. (Say, they are using
* block ciphers.)
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int unwrap(byte inBuf[], int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* For apps that care about buffer copies and want to read
* straight from the network, but also want the output in a specific
* application provided buffer, say to reduce buffer allocation or
* subsequent copy.
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int unwrap(InputStream is,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* Applies per-message integrity services.
*
* @param is the user-provided message
* @param os the token to be sent to the peer along with the
* message token. The message token <b>is not</b> encapsulated.
* @param msgProp on input the desired QOP and output the applied QOP
* @exception GSSException
*/
public void getMIC(InputStream is, OutputStream os,
MessageProp msgProp)
throws GSSException;
public byte[] getMIC(byte []inMsg, int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* Checks the integrity of the supplied tokens.
* This token was previously generated by getMIC.
*
* @param is token generated by getMIC
* @param msgStr the message to check integrity for
* @param mProp will contain the applied QOP and confidentiality
* states of the token as well as any informatory status codes
* @exception GSSException may be thrown
*/
public void verifyMIC(InputStream is, InputStream msgStr,
MessageProp mProp) throws GSSException;
public void verifyMIC(byte []inTok, int tokOffset, int tokLen,
byte[] inMsg, int msgOffset, int msgLen,
MessageProp msgProp) throws GSSException;
/**
* Produces a token representing this context. After this call
* the context will no longer be usable until an import is
* performed on the returned token.
*
* @return exported context token
* @exception GSSException may be thrown
*/
public byte[] export() throws GSSException;
/**
* Releases context resources and terminates the
* context between 2 peer.
*
* @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 see {@link ExtendedGSSContext#inquireSecContext}
* for details
*/
public Object inquireSecContext(InquireType type)
throws GSSException;
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by a mechanism specific credential
* element. A GSSCredential is conceptually a container class of several
* credential elements from different mechanisms.
*
* @author Mayank Upadhyay
*/
public interface GSSCredentialSpi {
public Provider getProvider();
/**
* Called to invalidate this credential element and release
* any system recourses and cryptographic information owned
* by the credential.
*
* @exception GSSException with major codes NO_CRED and FAILURE
*/
public void dispose() throws GSSException;
/**
* Returns the principal name for this credential. The name
* is in mechanism specific format.
*
* @return GSSNameSpi representing principal name of this credential
* @exception GSSException may be thrown
*/
public GSSNameSpi getName() throws GSSException;
/**
* Returns the init lifetime remaining.
*
* @return the init lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getInitLifetime() throws GSSException;
/**
* Returns the accept lifetime remaining.
*
* @return the accept lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getAcceptLifetime() throws GSSException;
/**
* Determines if this credential element can be used by a context
* initiator.
* @return true if it can be used for initiating contexts
*/
public boolean isInitiatorCredential() throws GSSException;
/**
* Determines if this credential element can be used by a context
* acceptor.
* @return true if it can be used for accepting contexts
*/
public boolean isAcceptorCredential() throws GSSException;
/**
* Returns the oid representing the underlying credential
* mechanism oid.
*
* @return the Oid for this credential mechanism
* @exception GSSException may be thrown
*/
public Oid getMechanism();
/**
* Impersonates another client.
*
* @param name the client to impersonate
* @return the new credential
* @exception GSSException may be thrown
*/
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException;
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 2000, 2005, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by a mechanism specific name element. A
* GSSName is conceptually a container class of several name elements from
* different mechanisms.
*
* @author Mayank Upadhyay
*/
public interface GSSNameSpi {
public Provider getProvider();
/**
* Equals method for the GSSNameSpi objects.
* If either name denotes an anonymous principal, the call should
* return false.
*
* @param name to be compared with
* @return true if they both refer to the same entity, else false
* @exception GSSException with major codes of BAD_NAMETYPE,
* BAD_NAME, FAILURE
*/
public boolean equals(GSSNameSpi name) throws GSSException;
/**
* Compares this <code>GSSNameSpi</code> object to another Object
* that might be a <code>GSSNameSpi</code>. The behaviour is exactly
* the same as in {@link #equals(GSSNameSpi) equals} except that
* no GSSException is thrown; instead, false will be returned in the
* situation where an error occurs.
*
* @param another the object to be compared to
* @return true if they both refer to the same entity, else false
* @see #equals(GSSNameSpi)
*/
public boolean equals(Object another);
/**
* Returns a hashcode value for this GSSNameSpi.
*
* @return a hashCode value
*/
public int hashCode();
/**
* Returns a flat name representation for this object. The name
* format is defined in RFC 2078.
*
* @return the flat name representation for this object
* @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
* BAD_NAME, FAILURE.
*/
public byte[] export() throws GSSException;
/**
* Get the mechanism type that this NameElement corresponds to.
*
* @return the Oid of the mechanism type
*/
public Oid getMechanism();
/**
* Returns a string representation for this name. The printed
* name type can be obtained by calling getStringNameType().
*
* @return string form of this name
* @see #getStringNameType()
* @overrides Object#toString
*/
public String toString();
/**
* Returns the oid describing the format of the printable name.
*
* @return the Oid for the format of the printed name
*/
public Oid getStringNameType();
/**
* Indicates if this name object represents an Anonymous name.
*/
public boolean isAnonymousName();
}

View File

@@ -0,0 +1,214 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by the factory class for every
* plugin mechanism. The GSSManager locates an implementation of this
* interface by querying the security providers installed on the
* system. For a provider to support a mechanism defined by Oid x.y.z,
* the provider master file would have to contain a mapping from the
* property "GssApiMechanism.x.y.z" to an implementation class that serves
* as the factory for that mechanism.
* <p>
* e.g., If a provider master file contained the a mapping from the
* property "GssApiMechanism.1.2.840.113554.1.2.2" to the class name
* "com.foo.krb5.Krb5GssFactory", then the GSS-API framework would assume
* that com.foo.krb5.Krb5GssFactory implements the MechanismFactory
* interface and that it can be used to obtain elements required by for
* supporting this mechanism.
*
* @author Mayank Upadhyay
*/
public interface MechanismFactory {
/**
* Returns the Oid of the mechanism that this factory supports.
* @return the Oid
*/
public Oid getMechanismOid();
/**
* Returns the provider that this factory came from.
* @return the provider
*/
public Provider getProvider();
/**
* Returns the GSS-API nametypes that this mechanism can
* support. Having this method helps the GSS-Framework decide quickly
* if a certain mechanism can be skipped when importing a name.
* @return an array of the Oid's corresponding to the different GSS-API
* nametypes supported
* @see org.ietf.jgss.GSSName
*/
public Oid[] getNameTypes() throws GSSException;
/**
* Creates a credential element for this mechanism to be included as
* part of a GSSCredential implementation. A GSSCredential is
* conceptually a container class of several credential elements from
* different mechanisms. A GSS-API credential can be used either for
* initiating GSS security contexts or for accepting them. This method
* also accepts parameters that indicate what usage is expected and how
* long the life of the credential should be. It is not necessary that
* the mechanism honor the request for lifetime. An application will
* always query an acquired GSSCredential to determine what lifetime it
* got back.<p>
*
* <b>Not all mechanisms support the concept of one credential element
* that can be used for both initiating and accepting a context. In the
* event that an application requests usage INITIATE_AND_ACCEPT for a
* credential from such a mechanism, the GSS framework will need to
* obtain two different credential elements from the mechanism, one
* that will have usage INITIATE_ONLY and another that will have usage
* ACCEPT_ONLY. The mechanism will help the GSS-API realize this by
* returning a credential element with usage INITIATE_ONLY or
* ACCEPT_ONLY prompting it to make another call to
* getCredentialElement, this time with the other usage mode. The
* mechanism indicates the missing mode by returning a 0 lifetime for
* it.</b>
*
* @param name the mechanism level name element for the entity whose
* credential is desired. A null value indicates that a mechanism
* dependent default choice is to be made.
* @param initLifetime indicates the lifetime (in seconds) that is
* requested for this credential to be used at the context initiator's
* end. This value should be ignored if the usage is
* ACCEPT_ONLY. Predefined contants are available in the
* org.ietf.jgss.GSSCredential interface.
* @param acceptLifetime indicates the lifetime (in seconds) that is
* requested for this credential to be used at the context acceptor's
* end. This value should be ignored if the usage is
* INITIATE_ONLY. Predefined contants are available in the
* org.ietf.jgss.GSSCredential interface.
* @param usage One of the values GSSCredential.INIATE_ONLY,
* GSSCredential.ACCEPT_ONLY, and GSSCredential.INITIATE_AND_ACCEPT.
* @see org.ietf.jgss.GSSCredential
* @throws GSSException if one of the error situations described in RFC
* 2743 with the GSS_Acquire_Cred or GSS_Add_Cred calls occurs.
*/
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime, int usage) throws GSSException;
/**
* Creates a name element for this mechanism to be included as part of
* a GSSName implementation. A GSSName is conceptually a container
* class of several name elements from different mechanisms. A GSSName
* can be created either with a String or with a sequence of
* bytes. This factory method accepts the name in a String. Such a name
* can generally be assumed to be printable and may be returned from
* the name element's toString() method.
*
* @param nameStr a string containing the characters describing this
* entity to the mechanism
* @param nameType an Oid serving as a clue as to how the mechanism should
* interpret the nameStr
* @throws GSSException if any of the errors described in RFC 2743 for
* the GSS_Import_Name or GSS_Canonicalize_Name calls occur.
*/
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException;
/**
* This is a variation of the factory method that accepts a String for
* the characters that make up the name. Usually the String characters
* are assumed to be printable. The bytes passed in to this method have
* to be converted to characters using some encoding of the mechanism's
* choice. It is recommended that UTF-8 be used. (Note that UTF-8
* preserves the encoding for 7-bit ASCII characters.)
* <p>
* An exported name will generally be passed in using this method.
*
* @param name the bytes describing this entity to the mechanism
* @param nameType an Oid serving as a clue as to how the mechanism should
* interpret the nameStr
* @throws GSSException if any of the errors described in RFC 2743 for
* the GSS_Import_Name or GSS_Canonicalize_Name calls occur.
*/
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException;
/**
* Creates a security context for this mechanism so that it can be used
* on the context initiator's side.
*
* @param peer the name element from this mechanism that represents the
* peer
* @param myInitiatorCred a credential element for the context
* initiator obtained previously from this mechanism. The identity of
* the context initiator can be obtained from this credential. Passing
* a value of null here indicates that a default entity of the
* mechanism's choice should be assumed to be the context initiator and
* that default credentials should be applied.
* @param lifetime the requested lifetime (in seconds) for the security
* context. Predefined contants are available in the
* org.ietf.jgss.GSSContext interface.
* @throws GSSException if any of the errors described in RFC 2743 in
* the GSS_Init_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred,
int lifetime) throws GSSException;
/**
* Creates a security context for this mechanism so thatit can be used
* on the context acceptor's side.
*
* @param myAcceptorCred a credential element for the context acceptor
* obtained previously from this mechanism. The identity of the context
* acceptor cna be obtained from this credential. Passing a value of
* null here indicates that tha default entity of the mechanism's
* choice should be assumed to be the context acceptor and default
* credentials should be applied.
*
* @throws GSSException if any of the errors described in RFC 2743 in
* the GSS_Accept_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
throws GSSException;
/**
* Creates a security context from a previously exported (serialized)
* security context. Note that this is different from Java
* serialization and is defined at a mechanism level to interoperate
* over the wire with non-Java implementations. Either the initiator or
* the acceptor can export and then import a security context.
* Implementations of mechanism contexts are not required to implement
* exporting and importing.
*
* @param exportedContext the bytes representing this security context
* @throws GSSException is any of the errors described in RFC 2743 in
* the GSS_Import_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException;
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import java.util.*;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.util.*;
/**
* Implements the SPNEGO NegTokenInit token
* as specified in RFC 2478
*
* NegTokenInit ::= SEQUENCE {
* mechTypes [0] MechTypeList OPTIONAL,
* reqFlags [1] ContextFlags OPTIONAL,
* mechToken [2] OCTET STRING OPTIONAL,
* mechListMIC [3] OCTET STRING OPTIONAL
* }
*
* MechTypeList ::= SEQUENCE OF MechType
*
* MechType::= OBJECT IDENTIFIER
*
* ContextFlags ::= BIT STRING {
* delegFlag (0),
* mutualFlag (1),
* replayFlag (2),
* sequenceFlag (3),
* anonFlag (4),
* confFlag (5),
* integFlag (6)
* }
*
* @author Seema Malkani
* @since 1.6
*/
public class NegTokenInit extends SpNegoToken {
// DER-encoded mechTypes
private byte[] mechTypes = null;
private Oid[] mechTypeList = null;
private BitArray reqFlags = null;
private byte[] mechToken = null;
private byte[] mechListMIC = null;
NegTokenInit(byte[] mechTypes, BitArray flags,
byte[] token, byte[] mechListMIC)
{
super(NEG_TOKEN_INIT_ID);
this.mechTypes = mechTypes;
this.reqFlags = flags;
this.mechToken = token;
this.mechListMIC = mechListMIC;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to parse SPNEGO tokens
public NegTokenInit(byte[] in) throws GSSException {
super(NEG_TOKEN_INIT_ID);
parseToken(in);
}
final byte[] encode() throws GSSException {
try {
// create negInitToken
DerOutputStream initToken = new DerOutputStream();
// DER-encoded mechTypes with CONTEXT 00
if (mechTypes != null) {
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), mechTypes);
}
// write context flags with CONTEXT 01
if (reqFlags != null) {
DerOutputStream flags = new DerOutputStream();
flags.putUnalignedBitString(reqFlags);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), flags);
}
// mechToken with CONTEXT 02
if (mechToken != null) {
DerOutputStream dataValue = new DerOutputStream();
dataValue.putOctetString(mechToken);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), dataValue);
}
// mechListMIC with CONTEXT 03
if (mechListMIC != null) {
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"sending MechListMIC");
}
DerOutputStream mic = new DerOutputStream();
mic.putOctetString(mechListMIC);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), mic);
}
// insert in a SEQUENCE
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, initToken);
return out.toByteArray();
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenInit token : " + e.getMessage());
}
}
private void parseToken(byte[] in) throws GSSException {
try {
DerValue der = new DerValue(in);
// verify NegotiationToken type token
if (!der.isContextSpecific((byte) NEG_TOKEN_INIT_ID)) {
throw new IOException("SPNEGO NegoTokenInit : " +
"did not have right token type");
}
DerValue tmp1 = der.data.getDerValue();
if (tmp1.tag != DerValue.tag_Sequence) {
throw new IOException("SPNEGO NegoTokenInit : " +
"did not have the Sequence tag");
}
// parse various fields if present
int lastField = -1;
while (tmp1.data.available() > 0) {
DerValue tmp2 = tmp1.data.getDerValue();
if (tmp2.isContextSpecific((byte)0x00)) {
// get the DER-encoded sequence of mechTypes
lastField = checkNextField(lastField, 0);
DerInputStream mValue = tmp2.data;
mechTypes = mValue.toByteArray();
// read all the mechTypes
DerValue[] mList = mValue.getSequence(0);
mechTypeList = new Oid[mList.length];
ObjectIdentifier mech = null;
for (int i = 0; i < mList.length; i++) {
mech = mList[i].getOID();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"reading Mechanism Oid = " + mech);
}
mechTypeList[i] = new Oid(mech.toString());
}
} else if (tmp2.isContextSpecific((byte)0x01)) {
lastField = checkNextField(lastField, 1);
// received reqFlags, skip it
} else if (tmp2.isContextSpecific((byte)0x02)) {
lastField = checkNextField(lastField, 2);
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"reading Mech Token");
}
mechToken = tmp2.data.getOctetString();
} else if (tmp2.isContextSpecific((byte)0x03)) {
lastField = checkNextField(lastField, 3);
if (!GSSUtil.useMSInterop()) {
mechListMIC = tmp2.data.getOctetString();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"MechListMIC Token = " +
getHexBytes(mechListMIC));
}
}
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenInit token : " + e.getMessage());
}
}
byte[] getMechTypes() {
return mechTypes;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to find the mechs in SPNEGO tokens
public Oid[] getMechTypeList() {
return mechTypeList;
}
BitArray getReqFlags() {
return reqFlags;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to access the mech token portion of SPNEGO tokens
public byte[] getMechToken() {
return mechToken;
}
byte[] getMechListMIC() {
return mechListMIC;
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.util.*;
/**
* Implements the SPNEGO NegTokenTarg token
* as specified in RFC 2478
*
* NegTokenTarg ::= SEQUENCE {
* negResult [0] ENUMERATED {
* accept_completed (0),
* accept_incomplete (1),
* reject (2) } OPTIONAL,
* supportedMech [1] MechType OPTIONAL,
* responseToken [2] OCTET STRING OPTIONAL,
* mechListMIC [3] OCTET STRING OPTIONAL
* }
*
* MechType::= OBJECT IDENTIFIER
*
*
* @author Seema Malkani
* @since 1.6
*/
public class NegTokenTarg extends SpNegoToken {
private int negResult = 0;
private Oid supportedMech = null;
private byte[] responseToken = null;
private byte[] mechListMIC = null;
NegTokenTarg(int result, Oid mech, byte[] token, byte[] mechListMIC)
{
super(NEG_TOKEN_TARG_ID);
this.negResult = result;
this.supportedMech = mech;
this.responseToken = token;
this.mechListMIC = mechListMIC;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to parse SPNEGO tokens
public NegTokenTarg(byte[] in) throws GSSException {
super(NEG_TOKEN_TARG_ID);
parseToken(in);
}
final byte[] encode() throws GSSException {
try {
// create negTargToken
DerOutputStream targToken = new DerOutputStream();
// write the negotiated result with CONTEXT 00
DerOutputStream result = new DerOutputStream();
result.putEnumerated(negResult);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), result);
// supportedMech with CONTEXT 01
if (supportedMech != null) {
DerOutputStream mech = new DerOutputStream();
byte[] mechType = supportedMech.getDER();
mech.write(mechType);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), mech);
}
// response Token with CONTEXT 02
if (responseToken != null) {
DerOutputStream rspToken = new DerOutputStream();
rspToken.putOctetString(responseToken);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), rspToken);
}
// mechListMIC with CONTEXT 03
if (mechListMIC != null) {
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"sending MechListMIC");
}
DerOutputStream mic = new DerOutputStream();
mic.putOctetString(mechListMIC);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), mic);
}
// insert in a SEQUENCE
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, targToken);
return out.toByteArray();
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenTarg token : " + e.getMessage());
}
}
private void parseToken(byte[] in) throws GSSException {
try {
DerValue der = new DerValue(in);
// verify NegotiationToken type token
if (!der.isContextSpecific((byte) NEG_TOKEN_TARG_ID)) {
throw new IOException("SPNEGO NegoTokenTarg : " +
"did not have the right token type");
}
DerValue tmp1 = der.data.getDerValue();
if (tmp1.tag != DerValue.tag_Sequence) {
throw new IOException("SPNEGO NegoTokenTarg : " +
"did not have the Sequence tag");
}
// parse various fields if present
int lastField = -1;
while (tmp1.data.available() > 0) {
DerValue tmp2 = tmp1.data.getDerValue();
if (tmp2.isContextSpecific((byte)0x00)) {
lastField = checkNextField(lastField, 0);
negResult = tmp2.data.getEnumerated();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: negotiated" +
" result = " + getNegoResultString(negResult));
}
} else if (tmp2.isContextSpecific((byte)0x01)) {
lastField = checkNextField(lastField, 1);
ObjectIdentifier mech = tmp2.data.getOID();
supportedMech = new Oid(mech.toString());
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"supported mechanism = " + supportedMech);
}
} else if (tmp2.isContextSpecific((byte)0x02)) {
lastField = checkNextField(lastField, 2);
responseToken = tmp2.data.getOctetString();
} else if (tmp2.isContextSpecific((byte)0x03)) {
lastField = checkNextField(lastField, 3);
if (!GSSUtil.useMSInterop()) {
mechListMIC = tmp2.data.getOctetString();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"MechListMIC Token = " +
getHexBytes(mechListMIC));
}
}
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenTarg token : " + e.getMessage());
}
}
int getNegotiatedResult() {
return negResult;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to find the supported mech in SPNEGO tokens
public Oid getSupportedMech() {
return supportedMech;
}
byte[] getResponseToken() {
return responseToken;
}
byte[] getMechListMIC() {
return mechListMIC;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.ProviderList;
import sun.security.jgss.GSSCredentialImpl;
import sun.security.jgss.spi.GSSNameSpi;
import sun.security.jgss.spi.GSSCredentialSpi;
/**
* This class is the cred element implementation for SPNEGO mech.
* NOTE: The current implementation can only support one mechanism.
* This should be changed once multi-mechanism support is needed.
*
* @author Valerie Peng
* @since 1.6
*/
public class SpNegoCredElement implements GSSCredentialSpi {
private GSSCredentialSpi cred = null;
public SpNegoCredElement(GSSCredentialSpi cred) throws GSSException {
this.cred = cred;
}
Oid getInternalMech() {
return cred.getMechanism();
}
// Used by GSSUtil.populateCredentials()
public GSSCredentialSpi getInternalCred() {
return cred;
}
public Provider getProvider() {
return SpNegoMechFactory.PROVIDER;
}
public void dispose() throws GSSException {
cred.dispose();
}
public GSSNameSpi getName() throws GSSException {
return cred.getName();
}
public int getInitLifetime() throws GSSException {
return cred.getInitLifetime();
}
public int getAcceptLifetime() throws GSSException {
return cred.getAcceptLifetime();
}
public boolean isInitiatorCredential() throws GSSException {
return cred.isInitiatorCredential();
}
public boolean isAcceptorCredential() throws GSSException {
return cred.isAcceptorCredential();
}
public Oid getMechanism() {
return GSSUtil.GSS_SPNEGO_MECH_OID;
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
return cred.impersonate(name);
}
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (c) 2005, 2009, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.jgss.krb5.Krb5MechFactory;
import sun.security.jgss.krb5.Krb5InitCredential;
import sun.security.jgss.krb5.Krb5AcceptCredential;
import sun.security.jgss.krb5.Krb5NameElement;
import java.security.Provider;
import java.util.Vector;
/**
* SpNego Mechanism plug in for JGSS
* This is the properties object required by the JGSS framework.
* All mechanism specific information is defined here.
*
* @author Seema Malkani
* @since 1.6
*/
public final class SpNegoMechFactory implements MechanismFactory {
static final Provider PROVIDER =
new sun.security.jgss.SunProvider();
static final Oid GSS_SPNEGO_MECH_OID =
GSSUtil.createOid("1.3.6.1.5.5.2");
private static Oid[] nameTypes =
new Oid[] { GSSName.NT_USER_NAME,
GSSName.NT_HOSTBASED_SERVICE,
GSSName.NT_EXPORT_NAME};
// The default underlying mech of SPNEGO, must not be SPNEGO itself.
private static final Oid DEFAULT_SPNEGO_MECH_OID =
ProviderList.DEFAULT_MECH_OID.equals(GSS_SPNEGO_MECH_OID)?
GSSUtil.GSS_KRB5_MECH_OID:
ProviderList.DEFAULT_MECH_OID;
// Use an instance of a GSSManager whose provider list
// does not include native provider
final GSSManagerImpl manager;
final Oid[] availableMechs;
private static SpNegoCredElement getCredFromSubject(GSSNameSpi name,
boolean initiate)
throws GSSException {
Vector<SpNegoCredElement> creds =
GSSUtil.searchSubject(name, GSS_SPNEGO_MECH_OID,
initiate, SpNegoCredElement.class);
SpNegoCredElement result = ((creds == null || creds.isEmpty()) ?
null : creds.firstElement());
// Force permission check before returning the cred to caller
if (result != null) {
GSSCredentialSpi cred = result.getInternalCred();
if (GSSUtil.isKerberosMech(cred.getMechanism())) {
if (initiate) {
Krb5InitCredential krbCred = (Krb5InitCredential) cred;
Krb5MechFactory.checkInitCredPermission
((Krb5NameElement) krbCred.getName());
} else {
Krb5AcceptCredential krbCred = (Krb5AcceptCredential) cred;
Krb5MechFactory.checkAcceptCredPermission
((Krb5NameElement) krbCred.getName(), name);
}
}
}
return result;
}
public SpNegoMechFactory(GSSCaller caller) {
manager = new GSSManagerImpl(caller, false);
Oid[] mechs = manager.getMechs();
availableMechs = new Oid[mechs.length-1];
for (int i = 0, j = 0; i < mechs.length; i++) {
// Skip SpNego mechanism
if (!mechs[i].equals(GSS_SPNEGO_MECH_OID)) {
availableMechs[j++] = mechs[i];
}
}
// Move the preferred mech to first place
for (int i=0; i<availableMechs.length; i++) {
if (availableMechs[i].equals(DEFAULT_SPNEGO_MECH_OID)) {
if (i != 0) {
availableMechs[i] = availableMechs[0];
availableMechs[0] = DEFAULT_SPNEGO_MECH_OID;
}
break;
}
}
}
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException {
return manager.getNameElement(
nameStr, nameType, DEFAULT_SPNEGO_MECH_OID);
}
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException {
return manager.getNameElement(name, nameType, DEFAULT_SPNEGO_MECH_OID);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime,
int usage) throws GSSException {
SpNegoCredElement credElement = getCredFromSubject
(name, (usage != GSSCredential.ACCEPT_ONLY));
if (credElement == null) {
// get CredElement for the default Mechanism
credElement = new SpNegoCredElement
(manager.getCredentialElement(name, initLifetime,
acceptLifetime, null, usage));
}
return credElement;
}
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred, int lifetime)
throws GSSException {
// get SpNego mechanism context
if (myInitiatorCred == null) {
myInitiatorCred = getCredFromSubject(null, true);
} else if (!(myInitiatorCred instanceof SpNegoCredElement)) {
// convert to SpNegoCredElement
SpNegoCredElement cred = new SpNegoCredElement(myInitiatorCred);
return new SpNegoContext(this, peer, cred, lifetime);
}
return new SpNegoContext(this, peer, myInitiatorCred, lifetime);
}
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
throws GSSException {
// get SpNego mechanism context
if (myAcceptorCred == null) {
myAcceptorCred = getCredFromSubject(null, false);
} else if (!(myAcceptorCred instanceof SpNegoCredElement)) {
// convert to SpNegoCredElement
SpNegoCredElement cred = new SpNegoCredElement(myAcceptorCred);
return new SpNegoContext(this, cred);
}
return new SpNegoContext(this, myAcceptorCred);
}
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
// get SpNego mechanism context
return new SpNegoContext(this, exportedContext);
}
public final Oid getMechanismOid() {
return GSS_SPNEGO_MECH_OID;
}
public Provider getProvider() {
return PROVIDER;
}
public Oid[] getNameTypes() {
// nameTypes is cloned in GSSManager.getNamesForMech
return nameTypes;
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import java.util.*;
import org.ietf.jgss.*;
import sun.security.util.*;
import sun.security.jgss.*;
/**
* Astract class for SPNEGO tokens.
* Implementation is based on RFC 2478
*
* NegotiationToken ::= CHOICE {
* negTokenInit [0] NegTokenInit,
* negTokenTarg [1] NegTokenTarg }
*
*
* @author Seema Malkani
* @since 1.6
*/
abstract class SpNegoToken extends GSSToken {
static final int NEG_TOKEN_INIT_ID = 0x00;
static final int NEG_TOKEN_TARG_ID = 0x01;
static enum NegoResult {
ACCEPT_COMPLETE,
ACCEPT_INCOMPLETE,
REJECT,
};
private int tokenType;
// property
static final boolean DEBUG = SpNegoContext.DEBUG;
/**
* The object identifier corresponding to the SPNEGO GSS-API
* mechanism.
*/
public static ObjectIdentifier OID;
static {
try {
OID = new ObjectIdentifier(SpNegoMechFactory.
GSS_SPNEGO_MECH_OID.toString());
} catch (IOException ioe) {
// should not happen
}
}
/**
* Creates SPNEGO token of the specified type.
*/
protected SpNegoToken(int tokenType) {
this.tokenType = tokenType;
}
/**
* Returns the individual encoded SPNEGO token
*
* @return the encoded token
* @exception GSSException
*/
abstract byte[] encode() throws GSSException;
/**
* Returns the encoded SPNEGO token
* Note: inserts the required CHOICE tags
*
* @return the encoded token
* @exception GSSException
*/
byte[] getEncoded() throws IOException, GSSException {
// get the token encoded value
DerOutputStream token = new DerOutputStream();
token.write(encode());
// now insert the CHOICE
switch (tokenType) {
case NEG_TOKEN_INIT_ID:
// Insert CHOICE of Negotiation Token
DerOutputStream initToken = new DerOutputStream();
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) NEG_TOKEN_INIT_ID), token);
return initToken.toByteArray();
case NEG_TOKEN_TARG_ID:
// Insert CHOICE of Negotiation Token
DerOutputStream targToken = new DerOutputStream();
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) NEG_TOKEN_TARG_ID), token);
return targToken.toByteArray();
default:
return token.toByteArray();
}
}
/**
* Returns the SPNEGO token type
*
* @return the token type
*/
final int getType() {
return tokenType;
}
/**
* Returns a string representing the token type.
*
* @param tokenType the token type for which a string name is desired
* @return the String name of this token type
*/
static String getTokenName(int type) {
switch (type) {
case NEG_TOKEN_INIT_ID:
return "SPNEGO NegTokenInit";
case NEG_TOKEN_TARG_ID:
return "SPNEGO NegTokenTarg";
default:
return "SPNEGO Mechanism Token";
}
}
/**
* Returns the enumerated type of the Negotiation result.
*
* @param result the negotiated result represented by integer
* @return the enumerated type of Negotiated result
*/
static NegoResult getNegoResultType(int result) {
switch (result) {
case 0:
return NegoResult.ACCEPT_COMPLETE;
case 1:
return NegoResult.ACCEPT_INCOMPLETE;
case 2:
return NegoResult.REJECT;
default:
// unknown - return optimistic result
return NegoResult.ACCEPT_COMPLETE;
}
}
/**
* Returns a string representing the negotiation result.
*
* @param result the negotiated result
* @return the String message of this negotiated result
*/
static String getNegoResultString(int result) {
switch (result) {
case 0:
return "Accept Complete";
case 1:
return "Accept InComplete";
case 2:
return "Reject";
default:
return ("Unknown Negotiated Result: " + result);
}
}
/**
* Checks if the context tag in a sequence is in correct order. The "last"
* value must be smaller than "current".
* @param last the last tag seen
* @param current the current tag
* @return the current tag, used as the next value for last
* @throws GSSException if there's a wrong order
*/
static int checkNextField(int last, int current) throws GSSException {
if (last < current) {
return current;
} else {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SpNegoToken token : wrong order");
}
}
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.spi.GSSCredentialSpi;
import sun.security.jgss.spi.GSSNameSpi;
/**
* This class is essentially a wrapper class for the gss_cred_id_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
public class GSSCredElement implements GSSCredentialSpi {
private int usage;
long pCred; // Pointer to the gss_cred_id_t structure
private GSSNameElement name = null;
private GSSLibStub cStub;
// Perform the necessary ServicePermission check on this cred
void doServicePermCheck() throws GSSException {
if (GSSUtil.isKerberosMech(cStub.getMech())) {
if (System.getSecurityManager() != null) {
if (isInitiatorCredential()) {
String tgsName = Krb5Util.getTGSName(name);
Krb5Util.checkServicePermission(tgsName, "initiate");
}
if (isAcceptorCredential() &&
name != GSSNameElement.DEF_ACCEPTOR) {
String krbName = name.getKrbName();
Krb5Util.checkServicePermission(krbName, "accept");
}
}
}
}
// Construct delegation cred using the actual context mech and srcName
GSSCredElement(long pCredentials, GSSNameElement srcName, Oid mech)
throws GSSException {
pCred = pCredentials;
cStub = GSSLibStub.getInstance(mech);
usage = GSSCredential.INITIATE_ONLY;
name = srcName;
}
GSSCredElement(GSSNameElement name, int lifetime, int usage,
GSSLibStub stub) throws GSSException {
cStub = stub;
this.usage = usage;
if (name != null) { // Could be GSSNameElement.DEF_ACCEPTOR
this.name = name;
doServicePermCheck();
pCred = cStub.acquireCred(this.name.pName, lifetime, usage);
} else {
pCred = cStub.acquireCred(0, lifetime, usage);
this.name = new GSSNameElement(cStub.getCredName(pCred), cStub);
doServicePermCheck();
}
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public void dispose() throws GSSException {
name = null;
if (pCred != 0) {
pCred = cStub.releaseCred(pCred);
}
}
public GSSNameElement getName() throws GSSException {
return (name == GSSNameElement.DEF_ACCEPTOR ?
null : name);
}
public int getInitLifetime() throws GSSException {
if (isInitiatorCredential()) {
return cStub.getCredTime(pCred);
} else return 0;
}
public int getAcceptLifetime() throws GSSException {
if (isAcceptorCredential()) {
return cStub.getCredTime(pCred);
} else return 0;
}
public boolean isInitiatorCredential() {
return (usage != GSSCredential.ACCEPT_ONLY);
}
public boolean isAcceptorCredential() {
return (usage != GSSCredential.INITIATE_ONLY);
}
public Oid getMechanism() {
return cStub.getMech();
}
public String toString() {
// No hex bytes available for native impl
return "N/A";
}
protected void finalize() throws Throwable {
dispose();
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
throw new GSSException(GSSException.FAILURE, -1,
"Not supported yet");
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import java.util.Hashtable;
import org.ietf.jgss.Oid;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.ChannelBinding;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.GSSException;
import sun.security.jgss.GSSUtil;
/**
* This class is essentially a JNI calling stub for all wrapper classes.
*
* @author Valerie Peng
* @since 1.6
*/
class GSSLibStub {
private Oid mech;
private long pMech;
/**
* Initialization routine to dynamically load function pointers.
*
* @param lib library name to dlopen
* @param debug set to true for reporting native debugging info
* @return true if succeeded, false otherwise.
*/
static native boolean init(String lib, boolean debug);
private static native long getMechPtr(byte[] oidDerEncoding);
// Miscellaneous routines
static native Oid[] indicateMechs();
native Oid[] inquireNamesForMech() throws GSSException;
// Name related routines
native void releaseName(long pName);
native long importName(byte[] name, Oid type);
native boolean compareName(long pName1, long pName2);
native long canonicalizeName(long pName);
native byte[] exportName(long pName) throws GSSException;
native Object[] displayName(long pName) throws GSSException;
// Credential related routines
native long acquireCred(long pName, int lifetime, int usage)
throws GSSException;
native long releaseCred(long pCred);
native long getCredName(long pCred);
native int getCredTime(long pCred);
native int getCredUsage(long pCred);
// Context related routines
native NativeGSSContext importContext(byte[] interProcToken);
native byte[] initContext(long pCred, long targetName, ChannelBinding cb,
byte[] inToken, NativeGSSContext context);
native byte[] acceptContext(long pCred, ChannelBinding cb,
byte[] inToken, NativeGSSContext context);
native long[] inquireContext(long pContext);
native Oid getContextMech(long pContext);
native long getContextName(long pContext, boolean isSrc);
native int getContextTime(long pContext);
native long deleteContext(long pContext);
native int wrapSizeLimit(long pContext, int flags, int qop, int outSize);
native byte[] exportContext(long pContext);
native byte[] getMic(long pContext, int qop, byte[] msg);
native void verifyMic(long pContext, byte[] token, byte[] msg,
MessageProp prop) ;
native byte[] wrap(long pContext, byte[] msg, MessageProp prop);
native byte[] unwrap(long pContext, byte[] msgToken, MessageProp prop);
private static Hashtable<Oid, GSSLibStub>
table = new Hashtable<Oid, GSSLibStub>(5);
static GSSLibStub getInstance(Oid mech) throws GSSException {
GSSLibStub s = table.get(mech);
if (s == null) {
s = new GSSLibStub(mech);
table.put(mech, s);
}
return s;
}
private GSSLibStub(Oid mech) throws GSSException {
SunNativeProvider.debug("Created GSSLibStub for mech " + mech);
this.mech = mech;
this.pMech = getMechPtr(mech.getDER());
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof GSSLibStub)) {
return false;
}
return (mech.equals(((GSSLibStub) obj).getMech()));
}
public int hashCode() {
return mech.hashCode();
}
Oid getMech() {
return mech;
}
}

View File

@@ -0,0 +1,295 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import java.security.Security;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import sun.security.krb5.Realm;
import sun.security.jgss.GSSUtil;
import sun.security.util.ObjectIdentifier;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.GSSNameSpi;
import javax.security.auth.kerberos.ServicePermission;
/**
* This class is essentially a wrapper class for the gss_name_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
public class GSSNameElement implements GSSNameSpi {
long pName = 0; // Pointer to the gss_name_t structure
private String printableName;
private Oid printableType;
private GSSLibStub cStub;
static final GSSNameElement DEF_ACCEPTOR = new GSSNameElement();
private static Oid getNativeNameType(Oid nameType, GSSLibStub stub) {
if (GSSUtil.NT_GSS_KRB5_PRINCIPAL.equals(nameType)) {
Oid[] supportedNTs = null;
try {
supportedNTs = stub.inquireNamesForMech();
} catch (GSSException ge) {
if (ge.getMajor() == GSSException.BAD_MECH &&
GSSUtil.isSpNegoMech(stub.getMech())) {
// Workaround known Heimdal issue and retry with KRB5
try {
stub = GSSLibStub.getInstance
(GSSUtil.GSS_KRB5_MECH_OID);
supportedNTs = stub.inquireNamesForMech();
} catch (GSSException ge2) {
// Should never happen
SunNativeProvider.debug("Name type list unavailable: " +
ge2.getMajorString());
}
} else {
SunNativeProvider.debug("Name type list unavailable: " +
ge.getMajorString());
}
}
if (supportedNTs != null) {
for (int i = 0; i < supportedNTs.length; i++) {
if (supportedNTs[i].equals(nameType)) return nameType;
}
// Special handling the specified name type
SunNativeProvider.debug("Override " + nameType +
" with mechanism default(null)");
return null; // Use mechanism specific default
}
}
return nameType;
}
private GSSNameElement() {
printableName = "<DEFAULT ACCEPTOR>";
}
GSSNameElement(long pNativeName, GSSLibStub stub) throws GSSException {
assert(stub != null);
if (pNativeName == 0) {
throw new GSSException(GSSException.BAD_NAME);
}
// Note: pNativeName is assumed to be a MN.
pName = pNativeName;
cStub = stub;
setPrintables();
}
GSSNameElement(byte[] nameBytes, Oid nameType, GSSLibStub stub)
throws GSSException {
assert(stub != null);
if (nameBytes == null) {
throw new GSSException(GSSException.BAD_NAME);
}
cStub = stub;
byte[] name = nameBytes;
if (nameType != null) {
// Special handling the specified name type if
// necessary
nameType = getNativeNameType(nameType, stub);
if (GSSName.NT_EXPORT_NAME.equals(nameType)) {
// Need to add back the mech Oid portion (stripped
// off by GSSNameImpl class prior to calling this
// method) for "NT_EXPORT_NAME"
byte[] mechBytes = null;
DerOutputStream dout = new DerOutputStream();
Oid mech = cStub.getMech();
try {
dout.putOID(new ObjectIdentifier(mech.toString()));
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE, e);
}
mechBytes = dout.toByteArray();
name = new byte[2 + 2 + mechBytes.length + 4 + nameBytes.length];
int pos = 0;
name[pos++] = 0x04;
name[pos++] = 0x01;
name[pos++] = (byte) (mechBytes.length>>>8);
name[pos++] = (byte) mechBytes.length;
System.arraycopy(mechBytes, 0, name, pos, mechBytes.length);
pos += mechBytes.length;
name[pos++] = (byte) (nameBytes.length>>>24);
name[pos++] = (byte) (nameBytes.length>>>16);
name[pos++] = (byte) (nameBytes.length>>>8);
name[pos++] = (byte) nameBytes.length;
System.arraycopy(nameBytes, 0, name, pos, nameBytes.length);
}
}
pName = cStub.importName(name, nameType);
setPrintables();
SecurityManager sm = System.getSecurityManager();
if (sm != null && !Realm.AUTODEDUCEREALM) {
String krbName = getKrbName();
int atPos = krbName.lastIndexOf('@');
if (atPos != -1) {
String atRealm = krbName.substring(atPos);
// getNativeNameType() can modify NT_GSS_KRB5_PRINCIPAL to null
if ((nameType == null
|| nameType.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
&& new String(nameBytes).endsWith(atRealm)) {
// Created from Kerberos name with realm, no need to check
} else {
try {
sm.checkPermission(new ServicePermission(atRealm, "-"));
} catch (SecurityException se) {
// Do not chain the actual exception to hide info
throw new GSSException(GSSException.FAILURE);
}
}
}
}
SunNativeProvider.debug("Imported " + printableName + " w/ type " +
printableType);
}
private void setPrintables() throws GSSException {
Object[] printables = null;
printables = cStub.displayName(pName);
assert((printables != null) && (printables.length == 2));
printableName = (String) printables[0];
assert(printableName != null);
printableType = (Oid) printables[1];
if (printableType == null) {
printableType = GSSName.NT_USER_NAME;
}
}
// Need to be public for GSSUtil.getSubject()
public String getKrbName() throws GSSException {
long mName = 0;
GSSLibStub stub = cStub;
if (!GSSUtil.isKerberosMech(cStub.getMech())) {
stub = GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID);
}
mName = stub.canonicalizeName(pName);
Object[] printables2 = stub.displayName(mName);
stub.releaseName(mName);
SunNativeProvider.debug("Got kerberized name: " + printables2[0]);
return (String) printables2[0];
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public boolean equals(GSSNameSpi other) throws GSSException {
if (!(other instanceof GSSNameElement)) {
return false;
}
return cStub.compareName(pName, ((GSSNameElement)other).pName);
}
public boolean equals(Object other) {
if (!(other instanceof GSSNameElement)) {
return false;
}
try {
return equals((GSSNameElement) other);
} catch (GSSException ex) {
return false;
}
}
public int hashCode() {
return new Long(pName).hashCode();
}
public byte[] export() throws GSSException {
byte[] nameVal = cStub.exportName(pName);
// Need to strip off the mech Oid portion of the exported
// bytes since GSSNameImpl class will subsequently add it.
int pos = 0;
if ((nameVal[pos++] != 0x04) ||
(nameVal[pos++] != 0x01))
throw new GSSException(GSSException.BAD_NAME);
int mechOidLen = (((0xFF & nameVal[pos++]) << 8) |
(0xFF & nameVal[pos++]));
ObjectIdentifier temp = null;
try {
DerInputStream din = new DerInputStream(nameVal, pos,
mechOidLen);
temp = new ObjectIdentifier(din);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.BAD_NAME, e);
}
Oid mech2 = new Oid(temp.toString());
assert(mech2.equals(getMechanism()));
pos += mechOidLen;
int mechPortionLen = (((0xFF & nameVal[pos++]) << 24) |
((0xFF & nameVal[pos++]) << 16) |
((0xFF & nameVal[pos++]) << 8) |
(0xFF & nameVal[pos++]));
if (mechPortionLen < 0) {
throw new GSSException(GSSException.BAD_NAME);
}
byte[] mechPortion = new byte[mechPortionLen];
System.arraycopy(nameVal, pos, mechPortion, 0, mechPortionLen);
return mechPortion;
}
public Oid getMechanism() {
return cStub.getMech();
}
public String toString() {
return printableName;
}
public Oid getStringNameType() {
return printableType;
}
public boolean isAnonymousName() {
return (GSSName.NT_ANONYMOUS.equals(printableType));
}
public void dispose() {
if (pName != 0) {
cStub.releaseName(pName);
pName = 0;
}
}
protected void finalize() throws Throwable {
dispose();
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import org.ietf.jgss.*;
import javax.security.auth.kerberos.ServicePermission;
/**
* This class is an utility class for Kerberos related stuff.
* @author Valerie Peng
* @since 1.6
*/
class Krb5Util {
// Return the Kerberos TGS principal name using the domain
// of the specified <code>name</code>
static String getTGSName(GSSNameElement name)
throws GSSException {
String krbPrinc = name.getKrbName();
int atIndex = krbPrinc.indexOf("@");
String realm = krbPrinc.substring(atIndex + 1);
StringBuffer buf = new StringBuffer("krbtgt/");
buf.append(realm).append('@').append(realm);
return buf.toString();
}
// Perform the Service Permission check using the specified
// <code>target</code> and <code>action</code>
static void checkServicePermission(String target, String action) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
SunNativeProvider.debug("Checking ServicePermission(" +
target + ", " + action + ")");
ServicePermission perm =
new ServicePermission(target, action);
sm.checkPermission(perm);
}
}
}

View File

@@ -0,0 +1,631 @@
/*
* Copyright (c) 2005, 2009, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSHeader;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.*;
import sun.security.util.DerValue;
import sun.security.util.ObjectIdentifier;
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.*;
/**
* This class is essentially a wrapper class for the gss_ctx_id_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
class NativeGSSContext implements GSSContextSpi {
private static final int GSS_C_DELEG_FLAG = 1;
private static final int GSS_C_MUTUAL_FLAG = 2;
private static final int GSS_C_REPLAY_FLAG = 4;
private static final int GSS_C_SEQUENCE_FLAG = 8;
private static final int GSS_C_CONF_FLAG = 16;
private static final int GSS_C_INTEG_FLAG = 32;
private static final int GSS_C_ANON_FLAG = 64;
private static final int GSS_C_PROT_READY_FLAG = 128;
private static final int GSS_C_TRANS_FLAG = 256;
private static final int NUM_OF_INQUIRE_VALUES = 6;
private long pContext = 0; // Pointer to the gss_ctx_id_t structure
private GSSNameElement srcName;
private GSSNameElement targetName;
private GSSCredElement cred;
private boolean isInitiator;
private boolean isEstablished;
private Oid actualMech; // Assigned during context establishment
private ChannelBinding cb;
private GSSCredElement delegatedCred;
private int flags;
private int lifetime = GSSCredential.DEFAULT_LIFETIME;
private final GSSLibStub cStub;
private boolean skipDelegPermCheck;
private boolean skipServicePermCheck;
// Retrieve the (preferred) mech out of SPNEGO tokens, i.e.
// NegTokenInit & NegTokenTarg
private static Oid getMechFromSpNegoToken(byte[] token,
boolean isInitiator)
throws GSSException {
Oid mech = null;
if (isInitiator) {
GSSHeader header = null;
try {
header = new GSSHeader(new ByteArrayInputStream(token));
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
int negTokenLen = header.getMechTokenLength();
byte[] negToken = new byte[negTokenLen];
System.arraycopy(token, token.length-negTokenLen,
negToken, 0, negToken.length);
NegTokenInit ntok = new NegTokenInit(negToken);
if (ntok.getMechToken() != null) {
Oid[] mechList = ntok.getMechTypeList();
mech = mechList[0];
}
} else {
NegTokenTarg ntok = new NegTokenTarg(token);
mech = ntok.getSupportedMech();
}
return mech;
}
// Perform the Service permission check
private void doServicePermCheck() throws GSSException {
if (System.getSecurityManager() != null) {
String action = (isInitiator? "initiate" : "accept");
// Need to check Service permission for accessing
// initiator cred for SPNEGO during context establishment
if (GSSUtil.isSpNegoMech(cStub.getMech()) && isInitiator
&& !isEstablished) {
if (srcName == null) {
// Check by creating default initiator KRB5 cred
GSSCredElement tempCred =
new GSSCredElement(null, lifetime,
GSSCredential.INITIATE_ONLY,
GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID));
tempCred.dispose();
} else {
String tgsName = Krb5Util.getTGSName(srcName);
Krb5Util.checkServicePermission(tgsName, action);
}
}
String targetStr = targetName.getKrbName();
Krb5Util.checkServicePermission(targetStr, action);
skipServicePermCheck = true;
}
}
// Perform the Delegation permission check
private void doDelegPermCheck() throws GSSException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
String targetStr = targetName.getKrbName();
String tgsStr = Krb5Util.getTGSName(targetName);
StringBuffer buf = new StringBuffer("\"");
buf.append(targetStr).append("\" \"");
buf.append(tgsStr).append('\"');
String krbPrincPair = buf.toString();
SunNativeProvider.debug("Checking DelegationPermission (" +
krbPrincPair + ")");
DelegationPermission perm =
new DelegationPermission(krbPrincPair);
sm.checkPermission(perm);
skipDelegPermCheck = true;
}
}
private byte[] retrieveToken(InputStream is, int mechTokenLen)
throws GSSException {
try {
byte[] result = null;
if (mechTokenLen != -1) {
// Need to add back the GSS header for a complete GSS token
SunNativeProvider.debug("Precomputed mechToken length: " +
mechTokenLen);
GSSHeader gssHeader = new GSSHeader
(new ObjectIdentifier(cStub.getMech().toString()),
mechTokenLen);
ByteArrayOutputStream baos = new ByteArrayOutputStream(600);
byte[] mechToken = new byte[mechTokenLen];
int len = is.read(mechToken);
assert(mechTokenLen == len);
gssHeader.encode(baos);
baos.write(mechToken);
result = baos.toByteArray();
} else {
// Must be unparsed GSS token or SPNEGO's NegTokenTarg token
assert(mechTokenLen == -1);
DerValue dv = new DerValue(is);
result = dv.toByteArray();
}
SunNativeProvider.debug("Complete Token length: " +
result.length);
return result;
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
// Constructor for context initiator
NativeGSSContext(GSSNameElement peer, GSSCredElement myCred,
int time, GSSLibStub stub) throws GSSException {
if (peer == null) {
throw new GSSException(GSSException.FAILURE, 1, "null peer");
}
cStub = stub;
cred = myCred;
targetName = peer;
isInitiator = true;
lifetime = time;
if (GSSUtil.isKerberosMech(cStub.getMech())) {
doServicePermCheck();
if (cred == null) {
cred = new GSSCredElement(null, lifetime,
GSSCredential.INITIATE_ONLY, cStub);
}
srcName = cred.getName();
}
}
// Constructor for context acceptor
NativeGSSContext(GSSCredElement myCred, GSSLibStub stub)
throws GSSException {
cStub = stub;
cred = myCred;
if (cred != null) targetName = cred.getName();
isInitiator = false;
// Defer Service permission check for default acceptor cred
// to acceptSecContext()
if (GSSUtil.isKerberosMech(cStub.getMech()) && targetName != null) {
doServicePermCheck();
}
// srcName and potentially targetName (when myCred is null)
// will be set in GSSLibStub.acceptContext(...)
}
// Constructor for imported context
NativeGSSContext(long pCtxt, GSSLibStub stub) throws GSSException {
assert(pContext != 0);
pContext = pCtxt;
cStub = stub;
// Set everything except cred, cb, delegatedCred
long[] info = cStub.inquireContext(pContext);
if (info.length != NUM_OF_INQUIRE_VALUES) {
throw new RuntimeException("Bug w/ GSSLibStub.inquireContext()");
}
srcName = new GSSNameElement(info[0], cStub);
targetName = new GSSNameElement(info[1], cStub);
isInitiator = (info[2] != 0);
isEstablished = (info[3] != 0);
flags = (int) info[4];
lifetime = (int) info[5];
// Do Service Permission check when importing SPNEGO context
// just to be safe
Oid mech = cStub.getMech();
if (GSSUtil.isSpNegoMech(mech) || GSSUtil.isKerberosMech(mech)) {
doServicePermCheck();
}
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public byte[] initSecContext(InputStream is, int mechTokenLen)
throws GSSException {
byte[] outToken = null;
if ((!isEstablished) && (isInitiator)) {
byte[] inToken = null;
// Ignore the specified input stream on the first call
if (pContext != 0) {
inToken = retrieveToken(is, mechTokenLen);
SunNativeProvider.debug("initSecContext=> inToken len=" +
inToken.length);
}
if (!getCredDelegState()) skipDelegPermCheck = true;
if (GSSUtil.isKerberosMech(cStub.getMech()) && !skipDelegPermCheck) {
doDelegPermCheck();
}
long pCred = (cred == null? 0 : cred.pCred);
outToken = cStub.initContext(pCred, targetName.pName,
cb, inToken, this);
SunNativeProvider.debug("initSecContext=> outToken len=" +
(outToken == null ? 0 : outToken.length));
// Only inspect the token when the permission check
// has not been performed
if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null) {
// WORKAROUND for SEAM bug#6287358
actualMech = getMechFromSpNegoToken(outToken, true);
if (GSSUtil.isKerberosMech(actualMech)) {
if (!skipServicePermCheck) doServicePermCheck();
if (!skipDelegPermCheck) doDelegPermCheck();
}
}
if (isEstablished) {
if (srcName == null) {
srcName = new GSSNameElement
(cStub.getContextName(pContext, true), cStub);
}
if (cred == null) {
cred = new GSSCredElement(srcName, lifetime,
GSSCredential.INITIATE_ONLY,
cStub);
}
}
}
return outToken;
}
public byte[] acceptSecContext(InputStream is, int mechTokenLen)
throws GSSException {
byte[] outToken = null;
if ((!isEstablished) && (!isInitiator)) {
byte[] inToken = retrieveToken(is, mechTokenLen);
SunNativeProvider.debug("acceptSecContext=> inToken len=" +
inToken.length);
long pCred = (cred == null? 0 : cred.pCred);
outToken = cStub.acceptContext(pCred, cb, inToken, this);
SunNativeProvider.debug("acceptSecContext=> outToken len=" +
(outToken == null? 0 : outToken.length));
if (targetName == null) {
targetName = new GSSNameElement
(cStub.getContextName(pContext, false), cStub);
// Replace the current default acceptor cred now that
// the context acceptor name is available
if (cred != null) cred.dispose();
cred = new GSSCredElement(targetName, lifetime,
GSSCredential.ACCEPT_ONLY, cStub);
}
// Only inspect token when the permission check has not
// been performed
if (GSSUtil.isSpNegoMech(cStub.getMech()) &&
(outToken != null) && !skipServicePermCheck) {
if (GSSUtil.isKerberosMech(getMechFromSpNegoToken
(outToken, false))) {
doServicePermCheck();
}
}
}
return outToken;
}
public boolean isEstablished() {
return isEstablished;
}
public void dispose() throws GSSException {
srcName = null;
targetName = null;
cred = null;
delegatedCred = null;
if (pContext != 0) {
pContext = cStub.deleteContext(pContext);
pContext = 0;
}
}
public int getWrapSizeLimit(int qop, boolean confReq,
int maxTokenSize)
throws GSSException {
return cStub.wrapSizeLimit(pContext, (confReq? 1:0), qop,
maxTokenSize);
}
public byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
byte[] data = inBuf;
if ((offset != 0) || (len != inBuf.length)) {
data = new byte[len];
System.arraycopy(inBuf, offset, data, 0, len);
}
return cStub.wrap(pContext, data, msgProp);
}
public void wrap(byte inBuf[], int offset, int len,
OutputStream os, MessageProp msgProp)
throws GSSException {
try {
byte[] result = wrap(inBuf, offset, len, msgProp);
os.write(result);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public int wrap(byte[] inBuf, int inOffset, int len, byte[] outBuf,
int outOffset, MessageProp msgProp)
throws GSSException {
byte[] result = wrap(inBuf, inOffset, len, msgProp);
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public void wrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
byte[] data = new byte[inStream.available()];
int length = inStream.read(data);
byte[] token = wrap(data, 0, length, msgProp);
outStream.write(token);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public byte[] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp)
throws GSSException {
if ((offset != 0) || (len != inBuf.length)) {
byte[] temp = new byte[len];
System.arraycopy(inBuf, offset, temp, 0, len);
return cStub.unwrap(pContext, temp, msgProp);
} else {
return cStub.unwrap(pContext, inBuf, msgProp);
}
}
public int unwrap(byte[] inBuf, int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException {
byte[] result = null;
if ((inOffset != 0) || (len != inBuf.length)) {
byte[] temp = new byte[len];
System.arraycopy(inBuf, inOffset, temp, 0, len);
result = cStub.unwrap(pContext, temp, msgProp);
} else {
result = cStub.unwrap(pContext, inBuf, msgProp);
}
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public void unwrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
byte[] wrapped = new byte[inStream.available()];
int wLength = inStream.read(wrapped);
byte[] data = unwrap(wrapped, 0, wLength, msgProp);
outStream.write(data);
outStream.flush();
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public int unwrap(InputStream inStream,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException {
byte[] wrapped = null;
int wLength = 0;
try {
wrapped = new byte[inStream.available()];
wLength = inStream.read(wrapped);
byte[] result = unwrap(wrapped, 0, wLength, msgProp);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
byte[] result = unwrap(wrapped, 0, wLength, msgProp);
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public byte[] getMIC(byte[] in, int offset, int len,
MessageProp msgProp) throws GSSException {
int qop = (msgProp == null? 0:msgProp.getQOP());
byte[] inMsg = in;
if ((offset != 0) || (len != in.length)) {
inMsg = new byte[len];
System.arraycopy(in, offset, inMsg, 0, len);
}
return cStub.getMic(pContext, qop, inMsg);
}
public void getMIC(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
int length = 0;
byte[] msg = new byte[inStream.available()];
length = inStream.read(msg);
byte[] msgToken = getMIC(msg, 0, length, msgProp);
if ((msgToken != null) && msgToken.length != 0) {
outStream.write(msgToken);
}
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public void verifyMIC(byte[] inToken, int tOffset, int tLen,
byte[] inMsg, int mOffset, int mLen,
MessageProp msgProp) throws GSSException {
byte[] token = inToken;
byte[] msg = inMsg;
if ((tOffset != 0) || (tLen != inToken.length)) {
token = new byte[tLen];
System.arraycopy(inToken, tOffset, token, 0, tLen);
}
if ((mOffset != 0) || (mLen != inMsg.length)) {
msg = new byte[mLen];
System.arraycopy(inMsg, mOffset, msg, 0, mLen);
}
cStub.verifyMic(pContext, token, msg, msgProp);
}
public void verifyMIC(InputStream tokStream, InputStream msgStream,
MessageProp msgProp) throws GSSException {
try {
byte[] msg = new byte[msgStream.available()];
int mLength = msgStream.read(msg);
byte[] tok = new byte[tokStream.available()];
int tLength = tokStream.read(tok);
verifyMIC(tok, 0, tLength, msg, 0, mLength, msgProp);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public byte[] export() throws GSSException {
byte[] result = cStub.exportContext(pContext);
pContext = 0;
return result;
}
private void changeFlags(int flagMask, boolean isEnable) {
if (isInitiator && pContext == 0) {
if (isEnable) {
flags |= flagMask;
} else {
flags &= ~flagMask;
}
}
}
public void requestMutualAuth(boolean state) throws GSSException {
changeFlags(GSS_C_MUTUAL_FLAG, state);
}
public void requestReplayDet(boolean state) throws GSSException {
changeFlags(GSS_C_REPLAY_FLAG, state);
}
public void requestSequenceDet(boolean state) throws GSSException {
changeFlags(GSS_C_SEQUENCE_FLAG, state);
}
public void requestCredDeleg(boolean state) throws GSSException {
changeFlags(GSS_C_DELEG_FLAG, state);
}
public void requestAnonymity(boolean state) throws GSSException {
changeFlags(GSS_C_ANON_FLAG, state);
}
public void requestConf(boolean state) throws GSSException {
changeFlags(GSS_C_CONF_FLAG, state);
}
public void requestInteg(boolean state) throws GSSException {
changeFlags(GSS_C_INTEG_FLAG, state);
}
public void requestDelegPolicy(boolean state) throws GSSException {
// Not supported, ignore
}
public void requestLifetime(int lifetime) throws GSSException {
if (isInitiator && pContext == 0) {
this.lifetime = lifetime;
}
}
public void setChannelBinding(ChannelBinding cb) throws GSSException {
if (pContext == 0) {
this.cb = cb;
}
}
private boolean checkFlags(int flagMask) {
return ((flags & flagMask) != 0);
}
public boolean getCredDelegState() {
return checkFlags(GSS_C_DELEG_FLAG);
}
public boolean getMutualAuthState() {
return checkFlags(GSS_C_MUTUAL_FLAG);
}
public boolean getReplayDetState() {
return checkFlags(GSS_C_REPLAY_FLAG);
}
public boolean getSequenceDetState() {
return checkFlags(GSS_C_SEQUENCE_FLAG);
}
public boolean getAnonymityState() {
return checkFlags(GSS_C_ANON_FLAG);
}
public boolean isTransferable() throws GSSException {
return checkFlags(GSS_C_TRANS_FLAG);
}
public boolean isProtReady() {
return checkFlags(GSS_C_PROT_READY_FLAG);
}
public boolean getConfState() {
return checkFlags(GSS_C_CONF_FLAG);
}
public boolean getIntegState() {
return checkFlags(GSS_C_INTEG_FLAG);
}
public boolean getDelegPolicyState() {
return false;
}
public int getLifetime() {
return cStub.getContextTime(pContext);
}
public GSSNameSpi getSrcName() throws GSSException {
return srcName;
}
public GSSNameSpi getTargName() throws GSSException {
return targetName;
}
public Oid getMech() throws GSSException {
if (isEstablished && actualMech != null) {
return actualMech;
} else {
return cStub.getMech();
}
}
public GSSCredentialSpi getDelegCred() throws GSSException {
return delegatedCred;
}
public boolean isInitiator() {
return isInitiator;
}
protected void finalize() throws Throwable {
dispose();
}
public Object inquireSecContext(InquireType type)
throws GSSException {
throw new GSSException(GSSException.UNAVAILABLE, -1,
"Inquire type not supported.");
}
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import java.io.UnsupportedEncodingException;
import java.security.Provider;
import java.util.Vector;
import org.ietf.jgss.*;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.*;
/**
* JGSS plugin for generic mechanisms provided through native GSS framework.
*
* @author Valerie Peng
*/
public final class NativeGSSFactory implements MechanismFactory {
GSSLibStub cStub = null;
private final GSSCaller caller;
private GSSCredElement getCredFromSubject(GSSNameElement name,
boolean initiate)
throws GSSException {
Oid mech = cStub.getMech();
Vector<GSSCredElement> creds = GSSUtil.searchSubject
(name, mech, initiate, GSSCredElement.class);
// If Subject is present but no native creds available
if (creds != null && creds.isEmpty()) {
if (GSSUtil.useSubjectCredsOnly(caller)) {
throw new GSSException(GSSException.NO_CRED);
}
}
GSSCredElement result = ((creds == null || creds.isEmpty()) ?
null : creds.firstElement());
// Force permission check before returning the cred to caller
if (result != null) {
result.doServicePermCheck();
}
return result;
}
public NativeGSSFactory(GSSCaller caller) {
this.caller = caller;
// Have to call setMech(Oid) explicitly before calling other
// methods. Otherwise, NPE may be thrown unexpectantly
}
public void setMech(Oid mech) throws GSSException {
cStub = GSSLibStub.getInstance(mech);
}
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException {
try {
byte[] nameBytes =
(nameStr == null ? null : nameStr.getBytes("UTF-8"));
return new GSSNameElement(nameBytes, nameType, cStub);
} catch (UnsupportedEncodingException uee) {
// Shouldn't happen
throw new GSSExceptionImpl(GSSException.FAILURE, uee);
}
}
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException {
return new GSSNameElement(name, nameType, cStub);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime,
int acceptLifetime,
int usage)
throws GSSException {
GSSNameElement nname = null;
if (name != null && !(name instanceof GSSNameElement)) {
nname = (GSSNameElement)
getNameElement(name.toString(), name.getStringNameType());
} else nname = (GSSNameElement) name;
if (usage == GSSCredential.INITIATE_AND_ACCEPT) {
// Force separate acqusition of cred element since
// MIT's impl does not correctly report NO_CRED error.
usage = GSSCredential.INITIATE_ONLY;
}
GSSCredElement credElement =
getCredFromSubject(nname, (usage == GSSCredential.INITIATE_ONLY));
if (credElement == null) {
// No cred in the Subject
if (usage == GSSCredential.INITIATE_ONLY) {
credElement = new GSSCredElement(nname, initLifetime,
usage, cStub);
} else if (usage == GSSCredential.ACCEPT_ONLY) {
if (nname == null) {
nname = GSSNameElement.DEF_ACCEPTOR;
}
credElement = new GSSCredElement(nname, acceptLifetime,
usage, cStub);
} else {
throw new GSSException(GSSException.FAILURE, -1,
"Unknown usage mode requested");
}
}
return credElement;
}
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myCred,
int lifetime)
throws GSSException {
if (peer == null) {
throw new GSSException(GSSException.BAD_NAME);
} else if (!(peer instanceof GSSNameElement)) {
peer = (GSSNameElement)
getNameElement(peer.toString(), peer.getStringNameType());
}
if (myCred == null) {
myCred = getCredFromSubject(null, true);
} else if (!(myCred instanceof GSSCredElement)) {
throw new GSSException(GSSException.NO_CRED);
}
return new NativeGSSContext((GSSNameElement) peer,
(GSSCredElement) myCred,
lifetime, cStub);
}
public GSSContextSpi getMechanismContext(GSSCredentialSpi myCred)
throws GSSException {
if (myCred == null) {
myCred = getCredFromSubject(null, false);
} else if (!(myCred instanceof GSSCredElement)) {
throw new GSSException(GSSException.NO_CRED);
}
return new NativeGSSContext((GSSCredElement) myCred, cStub);
}
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
return cStub.importContext(exportedContext);
}
public final Oid getMechanismOid() {
return cStub.getMech();
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public Oid[] getNameTypes() throws GSSException {
return cStub.inquireNamesForMech();
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import java.util.HashMap;
import java.security.Provider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.ietf.jgss.Oid;
import sun.security.action.PutAllAction;
/**
* Defines the Sun NativeGSS provider for plugging in the
* native GSS mechanisms to Java GSS.
*
* List of supported mechanisms depends on the local
* machine configuration.
*
* @author Yu-Ching Valerie Peng
*/
public final class SunNativeProvider extends Provider {
private static final long serialVersionUID = -238911724858694204L;
private static final String NAME = "SunNativeGSS";
private static final String INFO = "Sun Native GSS provider";
private static final String MF_CLASS =
"sun.security.jgss.wrapper.NativeGSSFactory";
private static final String LIB_PROP = "sun.security.jgss.lib";
private static final String DEBUG_PROP = "sun.security.nativegss.debug";
private static HashMap<String, String> MECH_MAP;
static final Provider INSTANCE = new SunNativeProvider();
static boolean DEBUG;
static void debug(String message) {
if (DEBUG) {
if (message == null) {
throw new NullPointerException();
}
System.out.println(NAME + ": " + message);
}
}
static {
MECH_MAP =
AccessController.doPrivileged(
new PrivilegedAction<HashMap<String, String>>() {
public HashMap<String, String> run() {
DEBUG = Boolean.parseBoolean
(System.getProperty(DEBUG_PROP));
try {
System.loadLibrary("j2gss");
} catch (Error err) {
debug("No j2gss library found!");
if (DEBUG) err.printStackTrace();
return null;
}
String gssLibs[];
String defaultLib = System.getProperty(LIB_PROP);
if (defaultLib == null || defaultLib.trim().equals("")) {
String osname = System.getProperty("os.name");
if (osname.startsWith("SunOS")) {
gssLibs = new String[]{ "libgss.so" };
} else if (osname.startsWith("Linux")) {
gssLibs = new String[]{
"libgssapi.so",
"libgssapi_krb5.so",
"libgssapi_krb5.so.2",
};
} else if (osname.contains("OS X")) {
gssLibs = new String[]{
"libgssapi_krb5.dylib",
"/usr/lib/sasl2/libgssapiv2.2.so",
};
} else if (osname.contains("Windows")) {
// Full path needed, DLL is in jre/bin
gssLibs = new String[]{ System.getProperty("java.home")
+ "\\bin\\sspi_bridge.dll" };
} else {
gssLibs = new String[0];
}
} else {
gssLibs = new String[]{ defaultLib };
}
for (String libName: gssLibs) {
if (GSSLibStub.init(libName, DEBUG)) {
debug("Loaded GSS library: " + libName);
Oid[] mechs = GSSLibStub.indicateMechs();
HashMap<String,String> map = new HashMap<>();
for (int i = 0; i < mechs.length; i++) {
debug("Native MF for " + mechs[i]);
map.put("GssApiMechanism." + mechs[i],
MF_CLASS);
}
return map;
}
}
return null;
}
});
}
public SunNativeProvider() {
/* We are the Sun NativeGSS provider */
super(NAME, 1.8d, INFO);
if (MECH_MAP != null) {
AccessController.doPrivileged(new PutAllAction(this, MECH_MAP));
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
public class Asn1Exception extends KrbException {
private static final long serialVersionUID = 8291288984575084132L;
public Asn1Exception(int i) {
super(i);
}
}

View File

@@ -0,0 +1,340 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import java.util.Arrays;
import sun.security.util.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
import java.math.BigInteger;
/**
* This class encapsulates the concept of a Kerberos checksum.
*/
public class Checksum {
private int cksumType;
private byte[] checksum;
// ----------------------------------------------+-------------+-----------
// Checksum type |sumtype |checksum
// |value | size
// ----------------------------------------------+-------------+-----------
public static final int CKSUMTYPE_NULL = 0; // 0
public static final int CKSUMTYPE_CRC32 = 1; // 4
public static final int CKSUMTYPE_RSA_MD4 = 2; // 16
public static final int CKSUMTYPE_RSA_MD4_DES = 3; // 24
public static final int CKSUMTYPE_DES_MAC = 4; // 16
public static final int CKSUMTYPE_DES_MAC_K = 5; // 8
public static final int CKSUMTYPE_RSA_MD4_DES_K = 6; // 16
public static final int CKSUMTYPE_RSA_MD5 = 7; // 16
public static final int CKSUMTYPE_RSA_MD5_DES = 8; // 24
// draft-ietf-krb-wg-crypto-07.txt
public static final int CKSUMTYPE_HMAC_SHA1_DES3_KD = 12; // 20
// draft-raeburn-krb-rijndael-krb-07.txt
public static final int CKSUMTYPE_HMAC_SHA1_96_AES128 = 15; // 96
public static final int CKSUMTYPE_HMAC_SHA1_96_AES256 = 16; // 96
// draft-brezak-win2k-krb-rc4-hmac-04.txt
public static final int CKSUMTYPE_HMAC_MD5_ARCFOUR = -138;
// default checksum type, -1 if not set
static int CKSUMTYPE_DEFAULT;
static int SAFECKSUMTYPE_DEFAULT;
private static boolean DEBUG = Krb5.DEBUG;
static {
initStatic();
}
public static void initStatic() {
String temp = null;
Config cfg = null;
try {
cfg = Config.getInstance();
temp = cfg.get("libdefaults", "default_checksum");
if (temp != null) {
CKSUMTYPE_DEFAULT = Config.getType(temp);
} else {
CKSUMTYPE_DEFAULT = -1;
}
} catch (Exception exc) {
if (DEBUG) {
System.out.println("Exception in getting default checksum "+
"value from the configuration. " +
"No default checksum set.");
exc.printStackTrace();
}
CKSUMTYPE_DEFAULT = -1;
}
try {
temp = cfg.get("libdefaults", "safe_checksum_type");
if (temp != null)
{
SAFECKSUMTYPE_DEFAULT = Config.getType(temp);
} else {
SAFECKSUMTYPE_DEFAULT = -1;
}
} catch (Exception exc) {
if (DEBUG) {
System.out.println("Exception in getting safe default " +
"checksum value " +
"from the configuration Setting. " +
"No safe default checksum set.");
exc.printStackTrace();
}
SAFECKSUMTYPE_DEFAULT = -1;
}
}
/**
* Constructs a new Checksum using the raw data and type.
*
* This constructor is only used by Authenticator Checksum
* {@link sun.security.jgss.krb5.InitialToken.OverloadedChecksum}
* where the checksum type must be 0x8003
* (see https://tools.ietf.org/html/rfc4121#section-4.1.1)
* and checksum field/value is used to convey service flags,
* channel bindings, and optional delegation information.
* This special type does NOT have a {@link CksumType} and has its
* own calculating and verification rules. It does has the same
* ASN.1 encoding though.
*
* @param data the byte array of checksum.
* @param new_cksumType the type of checksum.
*/
public Checksum(byte[] data, int new_cksumType) {
cksumType = new_cksumType;
checksum = data;
}
/**
* Constructs a new Checksum by calculating over the data using
* the specified checksum type. If the checksum is unkeyed, key
* and usage are ignored.
*
* @param new_cksumType the type of checksum. If set to -1, the
* {@linkplain EType#checksumType() mandatory checksum type}
* for the encryption type of {@code key} will be used
* @param data the data that needs to be performed a checksum calculation on
* @param key the key used by a keyed checksum
* @param usage the usage used by a keyed checksum
*/
public Checksum(int new_cksumType, byte[] data,
EncryptionKey key, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
if (new_cksumType == -1) {
cksumType = EType.getInstance(key.getEType()).checksumType();
} else {
cksumType = new_cksumType;
}
checksum = CksumType.getInstance(cksumType).calculateChecksum(
data, data.length, key.getBytes(), usage);
}
/**
* Verifies the keyed checksum over the data passed in.
*/
public boolean verifyKeyedChecksum(byte[] data, EncryptionKey key, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
CksumType cksumEngine = CksumType.getInstance(cksumType);
if (!cksumEngine.isKeyed()) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
} else {
return cksumEngine.verifyChecksum(
data, data.length, key.getBytes(), checksum, usage);
}
}
/**
* Verifies the checksum over the data passed in. The checksum might
* be a keyed or not.
*
* =============== ATTENTION! Use with care ==================
* According to https://tools.ietf.org/html/rfc3961#section-6.1,
* An unkeyed checksum should only be used "in limited circumstances
* where the lack of a key does not provide a window for an attack,
* preferably as part of an encrypted message".
*/
public boolean verifyAnyChecksum(byte[] data, EncryptionKey key, int usage)
throws KdcErrException, KrbCryptoException {
return CksumType.getInstance(cksumType).verifyChecksum(
data, data.length, key.getBytes(), checksum, usage);
}
boolean isEqual(Checksum cksum) throws KdcErrException {
if (cksumType != cksum.cksumType) {
return false;
}
return CksumType.isChecksumEqual(checksum, cksum.checksum);
}
/**
* Constructs an instance of Checksum from an ASN.1 encoded representation.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*
*/
public Checksum(DerValue encoding) throws Asn1Exception, IOException {
DerValue der;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
cksumType = der.getData().getBigInteger().intValue();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x01) {
checksum = der.getData().getOctetString();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
if (encoding.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes a Checksum object.
* <pre>{@code
* Checksum ::= SEQUENCE {
* cksumtype [0] Int32,
* checksum [1] OCTET STRING
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
* @return byte array of enocded Checksum.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(cksumType));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x00), temp);
temp = new DerOutputStream();
temp.putOctetString(checksum);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x01), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Parse (unmarshal) a checksum object from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
* @param data the Der input stream value, which contains one or more
* marshaled value.
* @param explicitTag tag number.
* @param optional indicates if this data field is optional
* @return an instance of Checksum.
*
*/
public static Checksum parse(DerInputStream data,
byte explicitTag, boolean optional)
throws Asn1Exception, IOException {
if ((optional) &&
(((byte)data.peekByte() & (byte)0x1F) != explicitTag)) {
return null;
}
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new Checksum(subDer);
}
}
/**
* Returns the raw bytes of the checksum, not in ASN.1 encoded form.
*/
public final byte[] getBytes() {
return checksum;
}
public final int getType() {
return cksumType;
}
@Override public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Checksum)) {
return false;
}
try {
return isEqual((Checksum)obj);
} catch (KdcErrException kee) {
return false;
}
}
@Override public int hashCode() {
int result = 17;
result = 37 * result + cksumType;
if (checksum != null) {
result = 37 * result + Arrays.hashCode(checksum);
}
return result;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2001, 2007, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import java.security.SecureRandom;
public final class Confounder {
private static SecureRandom srand = new SecureRandom();
private Confounder() { // not instantiable
}
public static byte[] bytes(int size) {
byte[] data = new byte[size];
srand.nextBytes(data);
return data;
}
public static int intValue() {
return srand.nextInt();
}
public static long longValue() {
return srand.nextLong();
}
}

View File

@@ -0,0 +1,585 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.ccache.CredentialsCache;
import sun.security.krb5.internal.crypto.EType;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import java.net.InetAddress;
/**
* This class encapsulates the concept of a Kerberos service
* credential. That includes a Kerberos ticket and an associated
* session key.
*/
public class Credentials {
Ticket ticket;
PrincipalName client;
PrincipalName clientAlias;
PrincipalName server;
PrincipalName serverAlias;
EncryptionKey key;
TicketFlags flags;
KerberosTime authTime;
KerberosTime startTime;
KerberosTime endTime;
KerberosTime renewTill;
HostAddresses cAddr;
AuthorizationData authzData;
private static boolean DEBUG = Krb5.DEBUG;
private static CredentialsCache cache;
static boolean alreadyLoaded = false;
private static boolean alreadyTried = false;
private Credentials proxy = null;
public Credentials getProxy() {
return proxy;
}
public Credentials setProxy(Credentials proxy) {
this.proxy = proxy;
return this;
}
// Read native ticket with session key type in the given list
private static native Credentials acquireDefaultNativeCreds(int[] eTypes);
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_client_alias,
PrincipalName new_server,
PrincipalName new_server_alias,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
KerberosTime new_startTime,
KerberosTime new_endTime,
KerberosTime renewTill,
HostAddresses cAddr,
AuthorizationData authzData) {
this(new_ticket, new_client, new_client_alias, new_server,
new_server_alias, new_key, new_flags, authTime,
new_startTime, new_endTime, renewTill, cAddr);
this.authzData = authzData;
}
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_client_alias,
PrincipalName new_server,
PrincipalName new_server_alias,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
KerberosTime new_startTime,
KerberosTime new_endTime,
KerberosTime renewTill,
HostAddresses cAddr) {
ticket = new_ticket;
client = new_client;
clientAlias = new_client_alias;
server = new_server;
serverAlias = new_server_alias;
key = new_key;
flags = new_flags;
this.authTime = authTime;
startTime = new_startTime;
endTime = new_endTime;
this.renewTill = renewTill;
this.cAddr = cAddr;
}
public Credentials(byte[] encoding,
String client,
String clientAlias,
String server,
String serverAlias,
byte[] keyBytes,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] cAddrs) throws KrbException, IOException {
this(new Ticket(encoding),
new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL),
(clientAlias == null? null : new PrincipalName(clientAlias,
PrincipalName.KRB_NT_PRINCIPAL)),
new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST),
(serverAlias == null? null : new PrincipalName(serverAlias,
PrincipalName.KRB_NT_SRV_INST)),
new EncryptionKey(keyType, keyBytes),
(flags == null? null: new TicketFlags(flags)),
(authTime == null? null: new KerberosTime(authTime)),
(startTime == null? null: new KerberosTime(startTime)),
(endTime == null? null: new KerberosTime(endTime)),
(renewTill == null? null: new KerberosTime(renewTill)),
null); // caddrs are in the encoding at this point
}
/**
* Acquires a service ticket for the specified service
* principal. If the service ticket is not already available, it
* obtains a new one from the KDC.
*/
/*
public Credentials(Credentials tgt, PrincipalName service)
throws KrbException {
}
*/
public final PrincipalName getClient() {
return client;
}
public final PrincipalName getClientAlias() {
return clientAlias;
}
public final PrincipalName getServer() {
return server;
}
public final PrincipalName getServerAlias() {
return serverAlias;
}
public final EncryptionKey getSessionKey() {
return key;
}
public final Date getAuthTime() {
if (authTime != null) {
return authTime.toDate();
} else {
return null;
}
}
public final Date getStartTime() {
if (startTime != null)
{
return startTime.toDate();
}
return null;
}
public final Date getEndTime() {
if (endTime != null)
{
return endTime.toDate();
}
return null;
}
public final Date getRenewTill() {
if (renewTill != null)
{
return renewTill.toDate();
}
return null;
}
public final boolean[] getFlags() {
if (flags == null) // Can be in a KRB-CRED
return null;
return flags.toBooleanArray();
}
public final InetAddress[] getClientAddresses() {
if (cAddr == null)
return null;
return cAddr.getInetAddresses();
}
public final byte[] getEncoded() {
byte[] retVal = null;
try {
retVal = ticket.asn1Encode();
} catch (Asn1Exception e) {
if (DEBUG)
System.out.println(e);
} catch (IOException ioe) {
if (DEBUG)
System.out.println(ioe);
}
return retVal;
}
public boolean isForwardable() {
return flags.get(Krb5.TKT_OPTS_FORWARDABLE);
}
public boolean isRenewable() {
return flags.get(Krb5.TKT_OPTS_RENEWABLE);
}
public Ticket getTicket() {
return ticket;
}
public TicketFlags getTicketFlags() {
return flags;
}
public AuthorizationData getAuthzData() {
return authzData;
}
/**
* Checks if the service ticket returned by the KDC has the OK-AS-DELEGATE
* flag set
* @return true if OK-AS_DELEGATE flag is set, otherwise, return false.
*/
public boolean checkDelegate() {
return flags.get(Krb5.TKT_OPTS_DELEGATE);
}
/**
* Reset TKT_OPTS_DELEGATE to false, called at credentials acquirement
* when one of the cross-realm TGTs does not have the OK-AS-DELEGATE
* flag set. This info must be preservable and restorable through
* the Krb5Util.credsToTicket/ticketToCreds() methods so that even if
* the service ticket is cached it still remembers the cross-realm
* authentication result.
*/
public void resetDelegate() {
flags.set(Krb5.TKT_OPTS_DELEGATE, false);
}
public Credentials renew() throws KrbException, IOException {
KDCOptions options = new KDCOptions();
options.set(KDCOptions.RENEW, true);
/*
* Added here to pass KrbKdcRep.check:73
*/
options.set(KDCOptions.RENEWABLE, true);
return new KrbTgsReq(options,
this,
server,
serverAlias,
null, // from
null, // till
null, // rtime
null, // eTypes
cAddr,
null,
null,
null).sendAndGetCreds();
}
/**
* Returns a TGT for the given client principal from a ticket cache.
*
* @param princ the client principal. A value of null means that the
* default principal name in the credentials cache will be used.
* @param ticketCache the path to the tickets file. A value
* of null will be accepted to indicate that the default
* path should be searched
* @return the TGT credentials or null if none were found. If the tgt
* expired, it is the responsibility of the caller to determine this.
*/
public static Credentials acquireTGTFromCache(PrincipalName princ,
String ticketCache)
throws KrbException, IOException {
if (ticketCache == null) {
// The default ticket cache on Windows and Mac is not a file.
String os = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("os.name"));
if (os.toUpperCase(Locale.ENGLISH).startsWith("WINDOWS") ||
os.toUpperCase(Locale.ENGLISH).contains("OS X")) {
Credentials creds = acquireDefaultCreds();
if (creds == null) {
if (DEBUG) {
System.out.println(">>> Found no TGT's in LSA");
}
return null;
}
if (princ != null) {
if (creds.getClient().equals(princ)) {
if (DEBUG) {
System.out.println(">>> Obtained TGT from LSA: "
+ creds);
}
return creds;
} else {
if (DEBUG) {
System.out.println(">>> LSA contains TGT for "
+ creds.getClient()
+ " not "
+ princ);
}
return null;
}
} else {
if (DEBUG) {
System.out.println(">>> Obtained TGT from LSA: "
+ creds);
}
return creds;
}
}
}
/*
* Returns the appropriate cache. If ticketCache is null, it is the
* default cache otherwise it is the cache filename contained in it.
*/
CredentialsCache ccache =
CredentialsCache.getInstance(princ, ticketCache);
if (ccache == null) {
return null;
}
Credentials tgtCred = ccache.getInitialCreds();
if (tgtCred == null) {
return null;
}
if (EType.isSupported(tgtCred.key.getEType())) {
return tgtCred;
} else {
if (DEBUG) {
System.out.println(
">>> unsupported key type found the default TGT: " +
tgtCred.key.getEType());
}
return null;
}
}
/**
* Acquires default credentials.
* <br>The possible locations for default credentials cache is searched in
* the following order:
* <ol>
* <li> The directory and cache file name specified by "KRB5CCNAME" system.
* property.
* <li> The directory and cache file name specified by "KRB5CCNAME"
* environment variable.
* <li> A cache file named krb5cc_{user.name} at {user.home} directory.
* </ol>
* @return a <code>KrbCreds</code> object if the credential is found,
* otherwise return null.
*/
// this method is intentionally changed to not check if the caller's
// principal name matches cache file's principal name.
// It assumes that the GSS call has
// the privilege to access the default cache file.
// This method is only called on Windows and Mac OS X, the native
// acquireDefaultNativeCreds is also available on these platforms.
public static synchronized Credentials acquireDefaultCreds() {
Credentials result = null;
if (cache == null) {
cache = CredentialsCache.getInstance();
}
if (cache != null) {
Credentials temp = cache.getInitialCreds();
if (temp != null) {
if (DEBUG) {
System.out.println(">>> KrbCreds found the default ticket"
+ " granting ticket in credential cache.");
}
if (EType.isSupported(temp.key.getEType())) {
result = temp;
} else {
if (DEBUG) {
System.out.println(
">>> unsupported key type found the default TGT: " +
temp.key.getEType());
}
}
}
}
if (result == null) {
// Doesn't seem to be a default cache on this system or
// TGT has unsupported encryption type
if (!alreadyTried) {
// See if there's any native code to load
try {
ensureLoaded();
} catch (Exception e) {
if (DEBUG) {
System.out.println("Can not load credentials cache");
e.printStackTrace();
}
alreadyTried = true;
}
}
if (alreadyLoaded) {
// There is some native code
if (DEBUG) {
System.out.println(">> Acquire default native Credentials");
}
try {
result = acquireDefaultNativeCreds(
EType.getDefaults("default_tkt_enctypes"));
} catch (KrbException ke) {
// when there is no default_tkt_enctypes.
}
}
}
return result;
}
/**
* Acquires credentials for a specified service using initial credential.
* When the service has a different realm
* from the initial credential, we do cross-realm authentication
* - first, we use the current credential to get
* a cross-realm credential from the local KDC, then use that
* cross-realm credential to request service credential
* from the foreigh KDC.
*
* @param service the name of service principal using format
* components@realm
* @param ccreds client's initial credential.
* @exception IOException if an error occurs in reading the credentials
* cache
* @exception KrbException if an error occurs specific to Kerberos
* @return a <code>Credentials</code> object.
*/
public static Credentials acquireServiceCreds(String service,
Credentials ccreds)
throws KrbException, IOException {
return CredentialsUtil.acquireServiceCreds(service, ccreds);
}
public static Credentials acquireS4U2selfCreds(PrincipalName user,
Credentials ccreds) throws KrbException, IOException {
return CredentialsUtil.acquireS4U2selfCreds(user, ccreds);
}
public static Credentials acquireS4U2proxyCreds(String service,
Ticket second, PrincipalName client, Credentials ccreds)
throws KrbException, IOException {
return CredentialsUtil.acquireS4U2proxyCreds(
service, second, client, ccreds);
}
public CredentialsCache getCache() {
return cache;
}
/*
* Prints out debug info.
*/
public static void printDebug(Credentials c) {
System.out.println(">>> DEBUG: ----Credentials----");
System.out.println("\tclient: " + c.client.toString());
if (c.clientAlias != null)
System.out.println("\tclient alias: " + c.clientAlias.toString());
System.out.println("\tserver: " + c.server.toString());
if (c.serverAlias != null)
System.out.println("\tserver alias: " + c.serverAlias.toString());
System.out.println("\tticket: sname: " + c.ticket.sname.toString());
if (c.startTime != null) {
System.out.println("\tstartTime: " + c.startTime.getTime());
}
System.out.println("\tendTime: " + c.endTime.getTime());
System.out.println(" ----Credentials end----");
}
static void ensureLoaded() {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void> () {
public Void run() {
if (System.getProperty("os.name").contains("OS X")) {
System.loadLibrary("osxkrb5");
} else {
System.loadLibrary("w2k_lsa_auth");
}
return null;
}
});
alreadyLoaded = true;
}
public String toString() {
StringBuffer buffer = new StringBuffer("Credentials:");
buffer.append( "\n client=").append(client);
if (clientAlias != null)
buffer.append( "\n clientAlias=").append(clientAlias);
buffer.append( "\n server=").append(server);
if (serverAlias != null)
buffer.append( "\n serverAlias=").append(serverAlias);
if (authTime != null) {
buffer.append("\n authTime=").append(authTime);
}
if (startTime != null) {
buffer.append("\n startTime=").append(startTime);
}
buffer.append( "\n endTime=").append(endTime);
buffer.append( "\n renewTill=").append(renewTill);
buffer.append( "\n flags=").append(flags);
buffer.append( "\nEType (skey)=").append(key.getEType());
buffer.append( "\n (tkt key)=").append(ticket.encPart.eType);
return buffer.toString();
}
public sun.security.krb5.internal.ccache.Credentials toCCacheCreds() {
return new sun.security.krb5.internal.ccache.Credentials(
getClient(), getServer(),
getSessionKey(),
date2kt(getAuthTime()),
date2kt(getStartTime()),
date2kt(getEndTime()),
date2kt(getRenewTill()),
false,
flags,
new HostAddresses(getClientAddresses()),
getAuthzData(),
getTicket(),
null);
}
private static KerberosTime date2kt(Date d) {
return d == null ? null : new KerberosTime(d);
}
}

View File

@@ -0,0 +1,380 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.util.*;
import sun.security.krb5.internal.crypto.*;
import sun.security.krb5.internal.*;
import java.io.IOException;
import java.math.BigInteger;
/**
* This class encapsulates Kerberos encrypted data. It allows
* callers access to both the ASN.1 encoded form of the EncryptedData
* type as well as the raw cipher text.
*/
public class EncryptedData implements Cloneable {
int eType;
Integer kvno; // optional
byte[] cipher;
byte[] plain; // not part of ASN.1 encoding
// ----------------+-----------+----------+----------------+---------------
// Encryption type |etype value|block size|minimum pad size|confounder size
// ----------------+-----------+----------+----------------+---------------
public static final int
ETYPE_NULL = 0; // 1 0 0
public static final int
ETYPE_DES_CBC_CRC = 1; // 8 4 8
public static final int
ETYPE_DES_CBC_MD4 = 2; // 8 0 8
public static final int
ETYPE_DES_CBC_MD5 = 3; // 8 0 8
// draft-brezak-win2k-krb-rc4-hmac-04.txt
public static final int
ETYPE_ARCFOUR_HMAC = 23; // 1
// NOTE: the exportable RC4-HMAC is not supported;
// it is no longer a usable encryption type
public static final int
ETYPE_ARCFOUR_HMAC_EXP = 24; // 1
// draft-ietf-krb-wg-crypto-07.txt
public static final int
ETYPE_DES3_CBC_HMAC_SHA1_KD = 16; // 8 0 8
// draft-raeburn-krb-rijndael-krb-07.txt
public static final int
ETYPE_AES128_CTS_HMAC_SHA1_96 = 17; // 16 0 16
public static final int
ETYPE_AES256_CTS_HMAC_SHA1_96 = 18; // 16 0 16
/* used by self */
private EncryptedData() {
}
public Object clone() {
EncryptedData new_encryptedData = new EncryptedData();
new_encryptedData.eType = eType;
if (kvno != null) {
new_encryptedData.kvno = new Integer(kvno.intValue());
}
if (cipher != null) {
new_encryptedData.cipher = new byte[cipher.length];
System.arraycopy(cipher, 0, new_encryptedData.cipher,
0, cipher.length);
}
return new_encryptedData;
}
// Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret)
public EncryptedData(
int new_eType,
Integer new_kvno,
byte[] new_cipher) {
eType = new_eType;
kvno = new_kvno;
cipher = new_cipher;
}
/*
// Not used.
public EncryptedData(
EncryptionKey key,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes());
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
// used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv
// Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret)
public EncryptedData(
EncryptionKey key,
byte[] plaintext,
int usage)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes(), usage);
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
/*
// Not used.
public EncryptedData(
EncryptionKey key,
byte[] ivec,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes(), ivec);
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
/*
// Not used.
EncryptedData(
StringBuffer password,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EncryptionKey key = new EncryptionKey(password);
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes());
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
public byte[] decrypt(
EncryptionKey key, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
if (eType != key.getEType()) {
throw new KrbCryptoException(
"EncryptedData is encrypted using keytype " +
EType.toString(eType) +
" but decryption key is of type " +
EType.toString(key.getEType()));
}
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes(), usage);
// The service ticket will be used in S4U2proxy request. Therefore
// the raw ticket is still needed.
//cipher = null;
return etypeEngine.decryptedData(plain);
}
/*
// currently destructive on cipher
// Not used.
public byte[] decrypt(
EncryptionKey key,
byte[] ivec, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
// XXX check for matching eType and kvno here
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes(), ivec, usage);
cipher = null;
return etypeEngine.decryptedData(plain);
}
// currently destructive on cipher
// Not used.
byte[] decrypt(StringBuffer password)
throws KdcErrException, KrbApErrException, KrbCryptoException {
EncryptionKey key = new EncryptionKey(password);
// XXX check for matching eType here
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes());
cipher = null;
return etypeEngine.decryptedData(plain);
}
*/
private byte[] decryptedData() throws KdcErrException {
if (plain != null) {
EType etypeEngine = EType.getInstance(eType);
return etypeEngine.decryptedData(plain);
}
return null;
}
/**
* Constructs an instance of EncryptedData type.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
*
*/
/* Used by self */
private EncryptedData(DerValue encoding)
throws Asn1Exception, IOException {
DerValue der = null;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
eType = (der.getData().getBigInteger()).intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if ((encoding.getData().peekByte() & 0x1F) == 1) {
der = encoding.getData().getDerValue();
int i = (der.getData().getBigInteger()).intValue();
kvno = new Integer(i);
} else {
kvno = null;
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x02) {
cipher = der.getData().getOctetString();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if (encoding.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Returns an ASN.1 encoded EncryptedData type.
*
* <pre>{@code
* EncryptedData ::= SEQUENCE {
* etype [0] Int32 -- EncryptionType --,
* kvno [1] UInt32 OPTIONAL,
* cipher [2] OCTET STRING -- ciphertext
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*
* @return byte array of encoded EncryptedData object.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(this.eType));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x00), temp);
temp = new DerOutputStream();
if (kvno != null) {
// encode as an unsigned integer (UInt32)
temp.putInteger(BigInteger.valueOf(this.kvno.longValue()));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x01), temp);
temp = new DerOutputStream();
}
temp.putOctetString(this.cipher);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
(byte)0x02), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Parse (unmarshal) an EncryptedData from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @param data the Der input stream value, which contains one or more
* marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
* @return an instance of EncryptedData.
*
*/
public static EncryptedData parse(DerInputStream data,
byte explicitTag,
boolean optional)
throws Asn1Exception, IOException {
if ((optional) &&
(((byte)data.peekByte() & (byte)0x1F) != explicitTag))
return null;
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new EncryptedData(subDer);
}
}
/**
* Reset asn.1 data stream after decryption, remove redundant bytes.
* @param data the decrypted data from decrypt().
* @return the reset byte array which holds exactly one asn1 datum
* including its tag and length.
*
*/
public byte[] reset(byte[] data) {
byte[] bytes = null;
// for asn.1 encoded data, we use length field to
// determine the data length and remove redundant paddings.
if ((data[1] & 0xFF) < 128) {
bytes = new byte[data[1] + 2];
System.arraycopy(data, 0, bytes, 0, data[1] + 2);
} else {
if ((data[1] & 0xFF) > 128) {
int len = data[1] & (byte)0x7F;
int result = 0;
for (int i = 0; i < len; i++) {
result |= (data[i + 2] & 0xFF) << (8 * (len - i - 1));
}
bytes = new byte[result + len + 2];
System.arraycopy(data, 0, bytes, 0, result + len + 2);
}
}
return bytes;
}
public int getEType() {
return eType;
}
public Integer getKeyVersionNumber() {
return kvno;
}
/**
* Returns the raw cipher text bytes, not in ASN.1 encoding.
*/
public byte[] getBytes() {
return cipher;
}
}

Some files were not shown because too many files have changed in this diff Show More