portals-jetspeed-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rwat...@apache.org
Subject svn commit: r929218 [2/3] - in /portals/jetspeed-2/portal/trunk/openid-step2: ./ common/ common/src/ common/src/main/ common/src/main/java/ common/src/main/java/com/ common/src/main/java/com/google/ common/src/main/java/com/google/step2/ common/src/mai...
Date Tue, 30 Mar 2010 18:41:49 GMT
Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/RelType.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/RelType.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/RelType.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/RelType.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.discovery;
+
+import java.net.URI;
+
+/**
+ * Class that represents rel-types for links.
+ */
+public class RelType {
+
+  private static final URI BASE_URI =
+      URI.create("http://www.iana.org/assignments/relation/");
+
+  private final URI uri;
+
+  public RelType(URI uri) {
+    this.uri = BASE_URI.resolve(uri);
+  }
+
+  public RelType(String uri) {
+    this(URI.create(uri));
+  }
+
+  public String getRelationshipType() {
+    return uri.toASCIIString();
+  }
+
+  @Override
+  public String toString() {
+    return "[rel: " + getRelationshipType() + "]";
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((uri == null) ? 0 : uri.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    RelType other = (RelType) obj;
+    if (uri == null) {
+      if (other.uri != null) return false;
+    } else if (!uri.equals(other.uri)) return false;
+    return true;
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/RelTypes.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/RelTypes.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/RelTypes.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/RelTypes.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.discovery;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Class that represents a collection of rel-types. (A link can have
+ * multiple rel-types attached to it.)
+ */
+public class RelTypes {
+
+  private final Set<RelType> types;
+
+  public static RelTypes setOf(RelType...types) {
+    return setOf(Arrays.asList(types));
+  }
+
+  public static RelTypes setOf(Collection<RelType> types) {
+    return new RelTypes(types);
+  }
+
+  private RelTypes(Collection<RelType> types) {
+    this.types = new HashSet<RelType>(types);
+  }
+
+  public boolean contains(RelType type) {
+    return types.contains(type);
+  }
+
+  public boolean containsAll(RelTypes other) {
+    return types.containsAll(other.types);
+  }
+
+  public int size() {
+    return types.size();
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((types == null) ? 0 : types.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    RelTypes other = (RelTypes) obj;
+    if (types == null) {
+      if (other.types != null) return false;
+    } else if (!types.equals(other.types)) return false;
+    return true;
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/SecureDiscoveryInformation.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/SecureDiscoveryInformation.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/SecureDiscoveryInformation.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/SecureDiscoveryInformation.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.discovery;
+
+import org.openid4java.discovery.DiscoveryException;
+import org.openid4java.discovery.DiscoveryInformation;
+import org.openid4java.discovery.Identifier;
+
+import java.net.URL;
+
+/**
+ * A subclass of {@link DiscoveryInformation} that can keep track of whether
+ * discovery information was obtained securely or not. Discovery information
+ * is considered to have been obtained "securely" if the chain of XRD documents
+ * that led to the discovery information were all properly signed and delegated
+ * to the next XRD document in the chain.
+ */
+public class SecureDiscoveryInformation extends DiscoveryInformation {
+
+  private boolean secure = false;
+
+  public SecureDiscoveryInformation(URL opEndpoint,
+      Identifier claimedIdentifier, String delegate, String version)
+      throws DiscoveryException {
+    super(opEndpoint, claimedIdentifier, delegate, version);
+  }
+
+  public SecureDiscoveryInformation(URL opEndpoint) throws DiscoveryException {
+    super(opEndpoint);
+  }
+
+  public SecureDiscoveryInformation(DiscoveryInformation info)
+      throws DiscoveryException {
+    this(info.getOPEndpoint(),
+         info.getClaimedIdentifier(),
+         info.getDelegateIdentifier(),
+         info.getVersion());
+    if (info instanceof SecureDiscoveryInformation) {
+      this.setSecure(((SecureDiscoveryInformation) info).isSecure());
+    } else {
+      this.setSecure(false);
+    }
+  }
+
+  public boolean isSecure() {
+    return secure;
+  }
+
+  public void setSecure(boolean secure) {
+    this.secure = secure;
+  }
+
+  // we need this for the unit tests
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    SecureDiscoveryInformation other = (SecureDiscoveryInformation) obj;
+    if (secure != other.secure) return false;
+    if (!areEqual(this.getClaimedIdentifier(), other.getClaimedIdentifier())) {
+      return false;
+    }
+    if (!areEqual(this.getDelegateIdentifier(), other.getDelegateIdentifier())) {
+      return false;
+    }
+    if (!areEqual(this.getOPEndpoint(), other.getOPEndpoint())) {
+      return false;
+    }
+    if (!areEqual(this.getVersion(), other.getVersion())) {
+      return false;
+    }
+    return true;
+  }
+
+  private static <T> boolean areEqual(T o1, T o2) {
+    return (o1 == null) ? o2 == null : o1.equals(o2);
+  }
+}
\ No newline at end of file

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/SecureUrlIdentifier.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/SecureUrlIdentifier.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/SecureUrlIdentifier.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/SecureUrlIdentifier.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.discovery;
+
+import org.openid4java.discovery.DiscoveryException;
+import org.openid4java.discovery.Identifier;
+import org.openid4java.discovery.UrlIdentifier;
+
+/**
+ * A {@link UrlIdentifier} that signifies that discovery on this identifier
+ * was performed securely. The getUrl method returns the same URL as a normal
+ * {@link UrlIdentifier} would, but the getIdentifier method returns a different
+ * String, ensuring that a securely discovered identifier and an insecurely
+ * discovered identifier with the same URL don't get treated as the same user
+ * (assuming that an RP would use the String returned by getIdentifier() to
+ * identify users in their system).
+ */
+public class SecureUrlIdentifier extends UrlIdentifier {
+
+  public SecureUrlIdentifier(Identifier id) throws DiscoveryException {
+    super(id.getIdentifier());
+  }
+
+  @Override
+  public String getIdentifier() {
+    return "secure:" + super.getIdentifier();
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/UriTemplate.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/UriTemplate.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/UriTemplate.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/UriTemplate.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.discovery;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.util.regex.Pattern;
+
+/**
+ * Class that represents a URITemplate. URI templates can occur in two different
+ * places: (1) in host-meta documents within a Link-Pattern, and (2) in
+ * a site's XRD(S) document. In either case, their purpose is to generate
+ * a URI that will point to a user's XRD(S) document. This URI is generated by
+ * applying the user's OpenID URL to the template (usually, by replacing the
+ * template's '{uri}' placeholder with the OpenID URL.
+ */
+public class UriTemplate {
+
+  // this pattern should be replaced by a URI without escaping that URI first.
+  private static final Pattern URI_NO_ESCAPE =
+    Pattern.compile("\\{uri\\}", Pattern.CASE_INSENSITIVE);
+
+  // this pattern should be replaced by the escaped form of a URI.
+  private static final Pattern URI_ESCAPE =
+    Pattern.compile("\\{%uri\\}", Pattern.CASE_INSENSITIVE);
+
+  // the template (e.g. "http://www.foo.com/openid?uri={%uri}")
+  private final String template;
+
+  public UriTemplate(String template) {
+    this.template = template.trim();
+  }
+
+  public URI map(URI uri) {
+    String encodedUri;
+    try {
+      encodedUri = URLEncoder.encode(uri.toString(), "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      throw new IllegalArgumentException("could not encode URI "
+          + uri.toASCIIString(), e);
+    }
+
+    String uriMap = template;
+    uriMap = URI_ESCAPE.matcher(uriMap).replaceAll(encodedUri);
+    uriMap = URI_NO_ESCAPE.matcher(uriMap).replaceAll(uri.toString());
+
+    return URI.create(uriMap);
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/UrlHostMetaFetcher.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/UrlHostMetaFetcher.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/UrlHostMetaFetcher.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/UrlHostMetaFetcher.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,82 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.discovery;
+
+import com.google.step2.http.FetchException;
+import com.google.step2.http.FetchRequest;
+import com.google.step2.http.FetchResponse;
+import com.google.step2.http.HttpFetcher;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpResponseException;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Super-class for implementations of HostMetaFetcher that fetch a host-meta
+ * file from a certain URL. A subclass of this class can merely indicate which
+ * URL the host-meta should be fetched from, and this class will then take
+ * care of the actual fetching of the bits, and the parsing of the file.
+ */
+public abstract class UrlHostMetaFetcher implements HostMetaFetcher {
+
+  private final HttpFetcher fetcher;
+
+  /**
+   * Constructor. An UrlHostMetaFetcher is a HostMetaFetcher that needs
+   * an HttpFetcher to do its job.
+   */
+  protected UrlHostMetaFetcher(HttpFetcher fetcher) {
+    this.fetcher = fetcher;
+  }
+
+  public HostMeta getHostMeta(String host) throws HostMetaException {
+    try {
+      URI uri = getHostMetaUriForHost(host);
+      FetchRequest request = FetchRequest.createGetRequest(uri);
+
+      FetchResponse response = fetcher.fetch(request);
+
+      int status = response.getStatusCode();
+
+      if (status != HttpStatus.SC_OK) {
+        throw new HttpResponseException(status, "fetching host-meta from " +
+            host + " return status " + status);
+      }
+
+      return HostMeta.parseFromStream(response.getContentAsStream());
+
+    } catch (FetchException e) {
+      throw new HostMetaException(e);
+    } catch (URISyntaxException e) {
+      throw new HostMetaException(e);
+    } catch (HttpResponseException e) {
+      throw new HostMetaException(e);
+    } catch (IOException e) {
+      throw new HostMetaException(e);
+    }
+  }
+
+  /**
+   * Given a host, returns the URL from which to download the host-meta for
+   * that host.
+   */
+  protected abstract URI getHostMetaUriForHost(String host)
+      throws URISyntaxException;
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/XrdDiscoveryResolver.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/XrdDiscoveryResolver.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/XrdDiscoveryResolver.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/XrdDiscoveryResolver.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,88 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.discovery;
+
+import org.openid4java.discovery.DiscoveryException;
+import org.openid4java.discovery.UrlIdentifier;
+
+import java.net.URI;
+import java.util.List;
+
+/**
+ * Interface describing an XRD discovery resolver. Since we anticipate that the
+ * format of OpenID-meta data might change in the future, we provide this
+ * interface here that can be implemented by different classes. For example,
+ * one implementation might use old-style XRDS syntax to look for OpenID
+ * meta-data, while another implementation might use new XRD syntax to do the
+ * same job.
+ */
+public interface XrdDiscoveryResolver {
+
+  /**
+   * Returns the mime-type of the document that the implementation knows how
+   * to parse. This is given as a hint to the host-meta parser as it looks for
+   * Link: entries in the host-meta document that point to documents that can
+   * be parsed by an implementing class.
+   *
+   * The legacy implementation {@link LegacyXrdsResolver} returns
+   * application/xrds+xml here, while newer implementations might return
+   * application/xrd+xml here.
+   */
+  public String getDiscoveryDocumentType();
+
+  /**
+   * Finds OP endpoints for a site in XRD(S) documents.
+   * @param site the site identifier on which we're performing discovery
+   * @param xrdUri the URL of the site's XRD(S) document that has OpenID
+   *   metadata in it.
+   * @return a list of discovery info objects. A discovery info object will
+   *   include the URL of the discovered endpoint.
+   * @throws DiscoveryException
+   */
+  public List<SecureDiscoveryInformation> findOpEndpointsForSite(
+      IdpIdentifier site, URI xrdUri)
+      throws DiscoveryException;
+
+  /**
+   * Finds OP endpoints for a user in XRD(S) documents.
+   * @param claimedId the user's identifier
+   * @param xrdUri the URL of the user's XRD(S) document that has OpenID
+   *   metadata in it.
+   * @return a list of discovery info objects. A discovery info object will
+   *   include the URL of the discovered endpoint, the claimed id, and
+   *   possibly the OP-local id of a user.
+   * @throws DiscoveryException
+   */
+  public List<SecureDiscoveryInformation> findOpEndpointsForUser(
+      UrlIdentifier claimedId, URI xrdUri)
+      throws DiscoveryException;
+
+  /**
+   * Finds OP endpoints for a site in XRD(S) documents.
+   * @param claimedId the user's identifier on which we're performing discovery
+   * @param xrdUri the URL of the site's XRD(S) document that has OpenID
+   *   metadata in it. The site in question is the site (host) of the user's
+   *   claimed id.
+   * @return a list of discovery info objects. A discovery info object will
+   *   include the URL of the discovered endpoint, the claimed id, and
+   *   possibly the OP-local id of a user.
+   * @throws DiscoveryException
+   */
+  public List<SecureDiscoveryInformation> findOpEndpointsForUserThroughSiteXrd(
+      UrlIdentifier claimedId, URI xrdUri)
+      throws DiscoveryException;
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/XrdLocationSelector.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/XrdLocationSelector.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/XrdLocationSelector.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/discovery/XrdLocationSelector.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,209 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.discovery;
+
+import static com.google.step2.discovery.RelTypes.setOf;
+
+import org.openid4java.discovery.UrlIdentifier;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Various strategies for finding the URI pointing to a relevant XRD(S) document.
+ *
+ * The goal of XRD location selection is to find a suitable URI from a
+ * host-meta document that is likely to point to an XRD(S) file with
+ * metadata about the (user or IdP) identifier in question.
+ *
+ */
+public class XrdLocationSelector {
+
+  // specifies a link that points to an XRD(S) document that includes meta
+  // data about OpenID OPs. Whether the document is old-style XRDS or new-style
+  // XRD depends on the (MIME) type specified in the Link.
+  public static final RelType REL_OPENID_OP_XRD =
+      new RelType("http://reltype.google.com/openid/xrd-op");
+
+  // specifies a link that points to an XRD(S) document that includes meta
+  // data about OpenID RPs. Whether the document is old-style XRDS or new-style
+  // XRD depends on the (MIME) type specified in the Link.
+  public static final RelType REL_OPENID_RP_XRD =
+      new RelType("http://reltype.google.com/openid/xrd-rp");
+
+  // specifies a link that points to an XRD(S) document that includes meta
+  // data about OpenID. Whether the document is old-style XRDS or new-style
+  // XRD depends on the (MIME) type specified in the Link.
+  public static final RelType REL_OPENID_XRD =
+      new RelType("http://reltype.google.com/openid/xrd");
+
+  // specifies a link that points to an XRD(S) document that includes some
+  // meta-data.
+  public static final RelType REL_DESCRIBED_BY = new RelType("describedby");
+
+  // When looking for an XRD that may have information about the OpenID
+  // OP in it, we first look for a link that has the most specific rel-types,
+  // and if that link doesn't exist, start looking for less specific rel-types.
+  private static final Ordering OP_PREFERENCE_ORDER = new Ordering(
+      setOf(REL_DESCRIBED_BY, REL_OPENID_OP_XRD),
+      setOf(REL_DESCRIBED_BY, REL_OPENID_XRD),
+      setOf(REL_DESCRIBED_BY));
+
+  // When looking for an XRD that may have information about the OpenID
+  // RP in it, we first look for a link that has the most specific rel-types,
+  // and if that link doesn't exist, start looking for less specific rel-types.
+  @SuppressWarnings("unused")
+  private static final Ordering RP_PREFERENCE_ORDER = new Ordering(
+      setOf(REL_DESCRIBED_BY, REL_OPENID_RP_XRD),
+      setOf(REL_DESCRIBED_BY, REL_OPENID_XRD),
+      setOf(REL_DESCRIBED_BY));
+
+  /**
+   * Returns a URI that points directly to the claimed id's XRD(S) document.
+   * The user's XRD(S) document should contain the pointer to the OP.
+   */
+  public URI findUserXrdUriForOp(HostMeta hostMeta, String mimeType,
+      UrlIdentifier claimedId) {
+
+    LinkPattern pattern = getMatchingLink(hostMeta.getLinkPatterns(), mimeType);
+    if (pattern == null) {
+      return null;
+    }
+
+    UriTemplate template = new UriTemplate(pattern.getUriPattern());
+    return template.map(URI.create(claimedId.getIdentifier()));
+  }
+
+  /**
+   * Finds, in /host-meta, a pointer to a site-wide XRD(S) document. Normally,
+   * this would simply be the URI in Link: entry with rel-type "describedby".
+   * But sites can annotate the link with further rel-types, indicating whether
+   * the XRD(S) pointed to is likely to contain OpenID-related information or
+   * even more specifically, OP or RP-related information.
+   *
+   * @param hostMeta the host-meta we're searching through.
+   * @param mimeType the mime-type of the link we're interested in.
+   */
+  public URI findSiteXrdUriForOp(HostMeta hostMeta, String mimeType) {
+    Link link = getMatchingLink(hostMeta.getLinks(), mimeType);
+    return (link == null) ? null : link.getUri();
+  }
+
+  /**
+   * Returns a link or link-pattern (from the collection passed in) that matches
+   * the requirements of OP discovery. That is, it needs to have the the
+   * specified mime-type, and is preferrably rel-typed as (describedby,
+   * http://reltype.google.com/openid/xrd-op) (although less specific rel-types
+   * are also considered if the most specific one cannot be found).
+   */
+  private <T extends LinkBase> T getMatchingLink(Collection<T> links,
+      String mimeType) {
+
+    // bring links into a sortable datatype, and only use those
+    // that seem to point to files of the right MIME type
+    List<T> sortableLinks = filterByMimeType(links, mimeType);
+
+    if (sortableLinks.size() < 1) {
+      return null;
+    }
+
+    // sort according to OpenID discovery preference:
+    // since we're looking for an OP for a site, we'll look for something
+    // labeled REL_OPENID_OP_XRD (and describedby) first, then for something
+    // labeled REL_OPENID_XRD (and describedby), then for something simply
+    // labeled "describedby".
+    Collections.sort(sortableLinks, OP_PREFERENCE_ORDER);
+
+    // make sure that the first link in fact points to something that we think
+    // might have OpenID data in it.
+    T candidate = sortableLinks.get(0);
+    RelTypes candidateRelTypes = candidate.getRelationships();
+
+    for (RelTypes validRelTypes : OP_PREFERENCE_ORDER.getAllRelTypeSets()) {
+      if (candidateRelTypes.containsAll(validRelTypes)) {
+        // yes, the first Link in the list lists a combination of RelTypes
+        // that's acceptable
+        return candidate;
+      }
+    }
+
+    // the first Link in the (sorted) list doesn't contain a combination of
+    // acceptable RelTypes
+    return null;
+  }
+
+  /**
+   * Discards all links from a host-meta that aren't the right MIME type.
+   */
+  private <T extends LinkBase> List<T> filterByMimeType(Collection<T> links,
+      String mimeType) {
+
+    ArrayList<T> result = new ArrayList<T>();
+
+    for (T link : links) {
+      if (mimeType.equals(link.getMimeType())) {
+        result.add(link);
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Helper class that implements an induced order, i.e., can order lists
+   * according to the priority of RelType sets. For example, if the
+   * list of rel-types passed to the constructor is:
+   *
+   * (foo bar), (foo), (bla)
+   *
+   * then this class can order a list of links with associated rel-types such
+   * that links that have both "foo" and "bar" rel-types come first, then
+   * (other) links with rel-type "foo", then links with rel-type "bla", and
+   * then all other links.
+   */
+  private static class Ordering implements Comparator<LinkBase> {
+
+    private final List<RelTypes> rels;
+    private final Integer maxValue;
+
+    public Ordering(RelTypes... rels) {
+      this.rels = Arrays.asList(rels);
+      this.maxValue = rels.length;
+    }
+
+    public int compare(LinkBase o1, LinkBase o2) {
+      return getOrdinal(o1).compareTo(getOrdinal(o2));
+    }
+
+    public List<RelTypes> getAllRelTypeSets() {
+      return rels;
+    }
+
+    private Integer getOrdinal(LinkBase o2) {
+      for(int i = 0; i < rels.size(); i++) {
+        if (o2.getRelationships().containsAll(rels.get(i))) {
+          return i;
+        }
+      }
+      return maxValue;
+    }
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/DefaultHttpFetcher.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/DefaultHttpFetcher.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/DefaultHttpFetcher.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/DefaultHttpFetcher.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,122 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.http;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Default implementations of HttpFetcher and FetchResponse. Based on
+ * Apache http client. We use the {@link ProxySelectorRoutePlanner}, which means
+ * that we pick up the default proxy set in the VM (can be set through
+ * {@link java.net.ProxySelector}.setDefault(), or by specifying system
+ * properties http.proxyHost, http.proxyPort, etc.).
+ */
+public class DefaultHttpFetcher implements HttpFetcher {
+
+  private final DefaultHttpClient httpClient;
+
+  public DefaultHttpFetcher() {
+    // this follows redirects by default
+    this.httpClient = new DefaultHttpClient();
+
+    // this means you can set a proxy through
+    // java.net.ProxySelector.setDefault(), or by simply starting the
+    // jvm with -Dhttp.proxyHost=foo.com -Dhttp.proxyPort=8080
+    httpClient.setRoutePlanner(new ProxySelectorRoutePlanner(
+        httpClient.getConnectionManager().getSchemeRegistry(),
+        null));
+  }
+
+  public FetchResponse fetch(FetchRequest request) throws FetchException {
+
+    HttpUriRequest uriRequest;
+
+    switch (request.getMethod()) {
+      case GET:
+        uriRequest = new HttpGet(request.getUri());
+        break;
+      case POST:
+        uriRequest = new HttpPost(request.getUri());
+        break;
+      case HEAD:
+        uriRequest = new HttpHead(request.getUri());
+        break;
+      default:
+        throw new FetchException("unsupported HTTP method: " +
+            request.getMethod());
+    }
+
+    try {
+      return new DefaultFetchResponse(httpClient.execute(uriRequest));
+    } catch (ClientProtocolException e) {
+      throw new FetchException(request, e);
+    } catch (IOException e) {
+      throw new FetchException(request, e);
+    }
+  }
+
+  private static class DefaultFetchResponse implements FetchResponse {
+
+    private final HttpResponse response;
+
+    public DefaultFetchResponse(HttpResponse response) {
+      this.response = response;
+    }
+
+    public int getStatusCode() {
+      return response.getStatusLine().getStatusCode();
+    }
+
+    public InputStream getContentAsStream() throws FetchException {
+      try {
+        return response.getEntity().getContent();
+      } catch (IllegalStateException e) {
+        throw new FetchException(e);
+      } catch (IOException e) {
+        throw new FetchException(e);
+      }
+    }
+
+    public byte[] getContentAsBytes() throws FetchException {
+      try {
+        return IOUtils.toByteArray(getContentAsStream());
+      } catch (IOException e) {
+        throw new FetchException(e);
+      }
+    }
+
+    public String getFirstHeader(String name) {
+      Header header = response.getFirstHeader(name);
+      if (header == null) {
+        return null;
+      }
+      return header.getValue();
+    }
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchException.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchException.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchException.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchException.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.http;
+
+/**
+ * Thrown when something goes wrong while fetching data over HTTP
+ */
+public class FetchException extends Exception {
+
+  public FetchException() {
+    super();
+  }
+
+  public FetchException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  /**
+   * @param request the FetchRequest we couldn't successfully complete.
+   */
+  public FetchException(FetchRequest request, Throwable cause) {
+    this("Couldn't fetch " + request.getUri().toASCIIString(), cause);
+  }
+
+  public FetchException(String message) {
+    super(message);
+  }
+
+  public FetchException(Throwable cause) {
+    super(cause);
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchRequest.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchRequest.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchRequest.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchRequest.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.http;
+
+import java.net.URI;
+
+/**
+ * Class that represents an HTTP request.
+ */
+public class FetchRequest {
+
+  public enum Method {
+    GET,
+    HEAD,
+    POST
+  }
+
+  private final Method method;
+  private final URI uri;
+
+  public static FetchRequest createGetRequest(URI uri) {
+    return new FetchRequest(Method.GET, uri);
+  }
+
+  public static FetchRequest createHeadRequest(URI uri) {
+    return new FetchRequest(Method.HEAD, uri);
+  }
+
+  public static FetchRequest createPostRequest(URI uri) {
+    return new FetchRequest(Method.POST, uri);
+  }
+
+  public FetchRequest(Method method, URI uri) {
+    this.method = method;
+    this.uri = uri;
+  }
+
+  /**
+   * Returns the method (GET, POST, etc.) of this HTTP request
+   */
+  public Method getMethod() {
+    return method;
+  }
+
+  /**
+   * Returns the URI of this HTTP request.
+   */
+  public URI getUri() {
+    return uri;
+  }
+
+  // implementing hashCode and equals so we can use these in EasyMock-based
+  // test cases like this:
+  //
+  // ...
+  // // the request going out should look like this:
+  // FetchRequest expectedRequest = ...;
+  //
+  // expect(httpFetcher.fetch(expectedRequest));
+  // ...
+  //
+  // which will use .equals() to compare the actual and expected fetch requests.
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((method == null) ? 0 : method.hashCode());
+    result = prime * result + ((uri == null) ? 0 : uri.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    FetchRequest other = (FetchRequest) obj;
+    if (method == null) {
+      if (other.method != null) return false;
+    } else if (!method.equals(other.method)) return false;
+    if (uri == null) {
+      if (other.uri != null) return false;
+    } else if (!uri.equals(other.uri)) return false;
+    return true;
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchResponse.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchResponse.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchResponse.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/FetchResponse.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.http;
+
+import java.io.InputStream;
+
+/**
+ * Interface representing the response from an HTTP request. Implementations
+ * that provide a custom implementation of HttpFetcher also need to provide
+ * an implementation of this interface.
+ */
+public interface FetchResponse {
+
+  /**
+   * Returns the status code of this HTTP response.
+   */
+  public int getStatusCode();
+
+  /**
+   * Returns the contents of this HTTP response, as an InputStream
+   */
+  public InputStream getContentAsStream() throws FetchException;
+
+  /**
+   * Returns the contents of this HTTP response, as a byte array
+   */
+  public byte[] getContentAsBytes() throws FetchException;
+
+  /**
+   * Returns the value of the first header with the given name, null if
+   * no such header was found.
+   */
+  public String getFirstHeader(String name);
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/HttpFetcher.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/HttpFetcher.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/HttpFetcher.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/http/HttpFetcher.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.http;
+
+/**
+ * Simple interface for fetching data over HTTP. This is here simply so that
+ * implementation can easily replace the default (Apache HTTP-client-based)
+ * implementation.
+ */
+public interface HttpFetcher {
+
+  /**
+   * Fetch some data over HTTP. Follow redirects during the fetch.
+   *
+   * @throws FetchException if there is an error during the run of the
+   *   HTTP protocol itself. This does not include HTTP error responses (which
+   *   are returned in the FetchResponse).
+   */
+  FetchResponse fetch(FetchRequest request) throws FetchException;
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/EncodingUtil.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/EncodingUtil.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/EncodingUtil.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/EncodingUtil.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.util;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.ArrayUtils;
+
+/**
+ * Base64 encoding and decoding.
+ */
+public class EncodingUtil {
+
+  public static Charset UTF8 = Charset.forName("UTF-8");
+
+  public static String getUtf8String(byte[] data) {
+    return UTF8.decode(ByteBuffer.wrap(data)).toString();
+  }
+
+  public static byte[] getUtf8Bytes(String s) {
+    if (s == null) {
+      return ArrayUtils.EMPTY_BYTE_ARRAY;
+    }
+    ByteBuffer bb = UTF8.encode(s);
+    return ArrayUtils.subarray(bb.array(), 0, bb.limit());
+  }
+
+  public static String encodeBase64(byte[] bytes) {
+    return EncodingUtil.getUtf8String(Base64.encodeBase64(bytes, false));
+  }
+
+  public static byte[] decodeBase64(String b64) {
+    return Base64.decodeBase64(getUtf8Bytes(b64));
+  }
+
+  public static byte[] decodeBase64(byte[] b64) {
+    return Base64.decodeBase64(b64);
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/ExpiringLruCache.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/ExpiringLruCache.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/ExpiringLruCache.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/ExpiringLruCache.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.util;
+
+
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+/**
+ * Cache supporting both LRU and time-based expiry.
+ *
+ * LRU: once maximum size is reached, the least recently accessed element is discarded.
+ *
+ * Time-based: entries are discarded once they reach a maximum age.
+ */
+public class ExpiringLruCache<K, V> {
+
+  private final LruLinkedHashMap<K, V> map;
+
+  private TimeSource timeSource = new TimeSource();
+
+  public ExpiringLruCache(int capacity) {
+    map = new LruLinkedHashMap<K, V>(capacity);
+  }
+
+  public void setTimeSource(TimeSource timeSource) {
+    this.timeSource = timeSource;
+  }
+
+  public void put(K key, V value, long maxSeconds) {
+    synchronized(map) {
+      long maxAge = timeSource.currentTimeMillis() + maxSeconds * 1000L;
+      map.put(key, new EntryWithAge<V>(value, maxAge));
+    }
+  }
+
+  public V get(K key) {
+    synchronized(map) {
+      EntryWithAge<V> entry = map.get(key);
+      if (entry != null && timeSource.currentTimeMillis() < entry.expireMillis) {
+        return entry.value;
+      }
+      return null;
+    }
+  }
+
+  private static class EntryWithAge<V> {
+    private final V value;
+    private final long expireMillis;
+
+    public EntryWithAge(V value, long expireMillis) {
+      this.value = value;
+      this.expireMillis = expireMillis;
+    }
+  }
+
+  private static class LruLinkedHashMap<K, V> extends LinkedHashMap<K, EntryWithAge<V>> {
+
+    private final int capacity;
+
+    public LruLinkedHashMap(int capacity) {
+      super(capacity, 0.75f, true);
+      this.capacity = capacity;
+    }
+
+    @Override
+    protected boolean removeEldestEntry(Entry<K, EntryWithAge<V>> eldest) {
+      return this.size() > capacity;
+    }
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/TimeSource.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/TimeSource.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/TimeSource.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/TimeSource.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.util;
+
+import java.util.Date;
+
+/**
+ * Simple class for returning the current time, which can be replaced by mocks
+ * during testing.
+ */
+public class TimeSource {
+
+  public Date now() {
+    return new Date(currentTimeMillis());
+  }
+
+  public long currentTimeMillis() {
+    return System.currentTimeMillis();
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/XmlUtil.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/XmlUtil.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/XmlUtil.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/util/XmlUtil.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.util;
+
+import org.jdom.Document;
+import org.jdom.JDOMException;
+import org.jdom.input.DOMBuilder;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+/**
+ * Class for parsing XML documents. It can parse a document either into an
+ * org.w3c.dom.Document or a org.jdom.Document. In either case, it uses a
+ * parser that is configured to not touch the file system or network while
+ * parsing the XML.
+ */
+public class XmlUtil {
+
+  public static org.w3c.dom.Document getDocument(InputStream input)
+      throws SAXException, IOException, ParserConfigurationException {
+
+    DocumentBuilderFactory factory = getSecureDocumentBuilderFactory();
+    DocumentBuilder builder = factory.newDocumentBuilder();
+
+    return builder.parse(input);
+  }
+
+  public static Document getJdomDocument(InputStream input)
+      throws JDOMException, IOException {
+    try {
+      return new DOMBuilder().build(getDocument(input));
+    } catch (SAXException e) {
+      throw new JDOMException("couldn't parse xml", e);
+    } catch (ParserConfigurationException e) {
+      throw new JDOMException("could not configure parser", e);
+    }
+  }
+
+  /* visible for testing */
+  static DocumentBuilderFactory getSecureDocumentBuilderFactory()
+     throws ParserConfigurationException {
+
+    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+
+    factory.setNamespaceAware(true);
+    factory.setFeature(
+      "http://xml.org/sax/features/external-general-entities", false);
+    factory.setFeature(
+      "http://xml.org/sax/features/external-parameter-entities",false);
+    factory.setFeature(
+      "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+
+    return factory;
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CachedCertPathValidator.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CachedCertPathValidator.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CachedCertPathValidator.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CachedCertPathValidator.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,107 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import java.security.GeneralSecurityException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.step2.util.ExpiringLruCache;
+import com.google.step2.util.TimeSource;
+
+/**
+ * Verifies X.509 certificates.
+ *
+ * This makes heavy use of the JRE CertPath libraries.  Documentation may be found at
+ * http://java.sun.com/j2se/1.5.0/docs/guide/security/certpath/CertPathProgGuide.html
+ *
+ * TODO: look for sane PKIXParameter certificate policy configuration.
+ */
+public class CachedCertPathValidator {
+
+  private static final Logger log = Logger.getLogger(CachedCertPathValidator.class.getName());
+  private static final String VALIDATOR_TYPE = "PKIX";
+  private static final String CERTIFICATE_TYPE = "X.509";
+  private static final int VALIDATION_CACHE_SIZE = 1024;
+  private static final long VALIDATION_CACHE_AGE_SECONDS = 10 * 60;
+
+  private final Set<TrustAnchor> trustRoots;
+  private final ExpiringLruCache<List<X509Certificate>, Boolean> validationCache;
+
+  private TimeSource timeSource = new TimeSource();
+
+  public CachedCertPathValidator(TrustRootsProvider trustRoots) {
+    this(trustRoots.getTrustRoots());
+  }
+
+  /* visible for testing */
+  public CachedCertPathValidator(Collection<X509Certificate> trustRoots) {
+    this.trustRoots = createTrustRoots(trustRoots);
+    this.validationCache = new ExpiringLruCache<List<X509Certificate>, Boolean>(
+        VALIDATION_CACHE_SIZE);
+  }
+
+  private Set<TrustAnchor> createTrustRoots(Collection<X509Certificate> trustRoots) {
+    List<TrustAnchor> anchors = new ArrayList<TrustAnchor>();
+    for (X509Certificate c : trustRoots) {
+      anchors.add(new TrustAnchor(c, null));
+    }
+    return new HashSet(anchors);
+  }
+
+  public void setTimeSource(TimeSource timeSource) {
+    this.timeSource = timeSource;
+    validationCache.setTimeSource(timeSource);
+  }
+
+  public void validate(List<X509Certificate> certs) throws CertValidatorException {
+    // If a cert chain validates successfully, we cache it for several minutes.  This improves
+    // performance dramatically (anywhere from 10x to 50x decrease in CPU usage when repeatedly
+    // verifying the same certificate chain.
+    if (validationCache.get(certs) != null) {
+      return;
+    }
+    validateNoCache(certs);
+    validationCache.put(certs, Boolean.TRUE, VALIDATION_CACHE_AGE_SECONDS);
+  }
+
+  private void validateNoCache(List<X509Certificate> certs) throws CertValidatorException {
+    try {
+      CertPathValidator validator = CertPathValidator.getInstance(VALIDATOR_TYPE);
+      PKIXParameters params = new PKIXParameters(trustRoots);
+      params.setDate(timeSource.now());
+      params.setRevocationEnabled(false);
+      CertificateFactory certFactory = CertificateFactory.getInstance(CERTIFICATE_TYPE);
+      CertPath certPath = certFactory.generateCertPath(certs);
+      validator.validate(certPath, params);
+    } catch (GeneralSecurityException e) {
+      log.log(Level.WARNING, "Certificate validation failed, certs were: " + certs, e);
+      throw new CertValidatorException("Certificate validation failure", e);
+    }
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertUtil.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertUtil.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertUtil.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertUtil.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import com.google.step2.util.EncodingUtil;
+import java.io.ByteArrayInputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+/**
+ * Parses byte arrays into certificates or private keys.
+ */
+public class CertUtil {
+  public static X509Certificate getCertFromBase64Bytes(String b64cert)
+      throws GeneralSecurityException {
+    return getCertFromBytes(EncodingUtil.decodeBase64(b64cert));
+  }
+
+  public static X509Certificate getCertFromBytes(byte[] derCert) throws GeneralSecurityException {
+    CertificateFactory fac = CertificateFactory.getInstance("X509");
+    ByteArrayInputStream in = new ByteArrayInputStream(derCert);
+    X509Certificate cert = (X509Certificate)fac.generateCertificate(in);
+    return cert;
+  }
+
+  public static PrivateKey getPrivateKeyFromBytes(byte[] derKey) throws GeneralSecurityException {
+    KeyFactory fac = KeyFactory.getInstance("RSA");
+    EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(derKey);
+    return fac.generatePrivate(privKeySpec);
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertValidator.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertValidator.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertValidator.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertValidator.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * Interface for validating certificates.
+ */
+public interface CertValidator {
+
+  /**
+   * Returns true if the certificate matches the given authority.
+   * @param cert the certificate in question. This is usually a certificate
+   *   that was used to sign an XRD document.
+   * @param authority the authority that is supposed to have signed the XRD
+   *   document. Usually, this will be the CanonicalID or Subject of the XRD
+   *   document, but it might also be an authority explicitly delegated to by
+   *   a previous XRD document. In either case, the authority has to "match" the
+   *   certificate.
+   * @return true, if the authority matches the certificate, false otherwise
+   */
+  boolean matches(X509Certificate cert, String authority);
+
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertValidatorException.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertValidatorException.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertValidatorException.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CertValidatorException.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+/**
+ * Thrown when a certificate chain doesn't verify.
+ */
+public class CertValidatorException extends Exception {
+
+  public CertValidatorException(String msg) {
+    super(msg);
+  }
+
+  public CertValidatorException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+
+  public CertValidatorException(Throwable cause) {
+    super(cause);
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CnConstraintCertValidator.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CnConstraintCertValidator.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CnConstraintCertValidator.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/CnConstraintCertValidator.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import java.security.cert.X509Certificate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A cert validator that will match the given cert if and only if its
+ * subject's Common Name equals a given string.
+ */
+public abstract class CnConstraintCertValidator implements CertValidator {
+
+  private static final Pattern CN_PATTERN = Pattern.compile("CN=([^,]+)");
+
+  public boolean matches(X509Certificate cert, String authority) {
+    String cn = getCnFromDn(cert.getSubjectX500Principal().getName());
+
+    if (cn == null) {
+      return false;
+    }
+
+    return cn.equals(getRequiredCn(authority));
+  }
+
+  /* visible for testing */
+  String getCnFromDn(String dn) {
+    Matcher m = CN_PATTERN.matcher(dn);
+
+    if (m.find()) {
+      return m.group(1);
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * Returns the string that has to equal the cert's subject's CN.
+   * @param authority usually, the canonical ID of the XRD that we're currently
+   *   validating; but could be the name of an explicitly delegated-to
+   *   authority. A subclass could for example implement the following:
+   *   if the authority is a full URL, it returns here only the name of the
+   *   host in the URL, since X.509 certificates aren't usually issued to
+   *   URLs for DNS host names.
+   */
+  protected abstract String getRequiredCn(String authority);
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/Constants.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/Constants.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/Constants.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/Constants.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import org.jdom.Namespace;
+
+/**
+ * Some constants we're using.
+ */
+public class Constants {
+
+  public static final String XML_DSIG_NAMESPACE = "http://www.w3.org/2000/09/xmldsig#";
+  public static final String SIMPLE_SIGN_NAMESPACE = "http://docs.oasis-open.org/xri/xrd/2009/01";
+
+  public static final String SIGNATURE_ELEMENT = "Signature";
+  public static final String SIGNED_INFO_ELEMENT = "SignedInfo";
+  public static final String ALGORITHM_ATTRIBUTE = "Algorithm";
+  public static final String CANONICALIZATION_METHOD_ELEMENT = "CanonicalizationMethod";
+  public static final String SIGNATURE_METHOD_ELEMENT = "SignatureMethod";
+  public static final String SIGNATURE_LOCATION_ELEMENT = "SignatureLocation";
+  public static final String KEY_INFO_ELEMENT = "KeyInfo";
+  public static final String X509_DATA_ELEMENT = "X509Data";
+  public static final String X509_CERTIFICATE = "X509Certificate";
+
+  public static final String RSA_SHA1_ALGORITHM = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
+
+  public static final String CANONICALIZE_RAW_OCTETS =
+      "http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets";
+
+
+
+  static final Namespace XML_DSIG_NS = Namespace.getNamespace("ds", XML_DSIG_NAMESPACE);
+  static final Namespace SIMPLE_SIGN_NS = Namespace.getNamespace("sds", SIMPLE_SIGN_NAMESPACE);
+
+  static final String RSA_SHA1_JCE_ID = "SHA1withRSA";
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/DefaultCertValidator.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/DefaultCertValidator.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/DefaultCertValidator.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/DefaultCertValidator.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+/**
+ * The {@link DefaultCertValidator} ensures that the CN of the subject of the
+ * certificate is equal to the authority given.
+ */
+public class DefaultCertValidator extends CnConstraintCertValidator {
+
+  @Override
+  protected String getRequiredCn(String authority) {
+    return authority;
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/DefaultTrustRootsProvider.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/DefaultTrustRootsProvider.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/DefaultTrustRootsProvider.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/DefaultTrustRootsProvider.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * By default, we use the same CA roots that Java's SSL implementation uses.
+ */
+public class DefaultTrustRootsProvider implements TrustRootsProvider {
+
+  private final Collection<X509Certificate> roots;
+
+  public DefaultTrustRootsProvider() {
+
+    TrustManagerFactory factory;
+    try {
+      factory = TrustManagerFactory.getInstance("X509");
+
+      // null keystore means we load the keystore defined through
+      // -Djavax.net.ssl.trustStore, or if that is not set, a keystore on a
+      // default path
+      factory.init((KeyStore) null);
+    } catch (NoSuchAlgorithmException e) {
+      throw new IllegalStateException(e);
+    } catch (KeyStoreException e) {
+      throw new IllegalStateException(e);
+    }
+    TrustManager[] managers = factory.getTrustManagers();
+
+    // there is only one trust manager for the SSL root certs
+    X509TrustManager manager = (X509TrustManager) managers[0];
+
+    this.roots = Arrays.asList(manager.getAcceptedIssuers());
+  }
+
+  public Collection<X509Certificate> getTrustRoots() {
+    return roots;
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/TrustRootsProvider.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/TrustRootsProvider.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/TrustRootsProvider.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/TrustRootsProvider.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+
+/**
+ * Interface that defines trust roots providers. A trust roots provider
+ * returns the list of certificates we should trust as Certification
+ * Authorities.
+ */
+public interface TrustRootsProvider {
+  /**
+   * Returns the list of certificates that we should trust as Certification
+   * Authorities.
+   */
+  public Collection<X509Certificate> getTrustRoots();
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/VerificationResult.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/VerificationResult.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/VerificationResult.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/VerificationResult.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * Result of a signature verification.
+ */
+public class VerificationResult {
+
+  private List<X509Certificate> certs;
+
+  public VerificationResult(List<X509Certificate> certs) {
+    this.certs = certs;
+  }
+
+  public List<X509Certificate> getCerts() {
+    return certs;
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/Verifier.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/Verifier.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/Verifier.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/Verifier.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,208 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import com.google.step2.http.FetchException;
+import com.google.step2.http.FetchRequest;
+import com.google.step2.http.FetchResponse;
+import com.google.step2.http.HttpFetcher;
+import com.google.step2.util.EncodingUtil;
+import com.google.step2.util.XmlUtil;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.Namespace;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.security.GeneralSecurityException;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Verifies signatures on XML documents.
+ */
+public class Verifier {
+
+  private final CachedCertPathValidator validator;
+  private final HttpFetcher fetcher;
+
+  public Verifier(CachedCertPathValidator validator, HttpFetcher fetcher) {
+    this.validator = validator;
+    this.fetcher = fetcher;
+  }
+
+  /**
+   * Verifies the signature on the given document. If the supplied signature is
+   * not null, then we verify the supplied signature. If the signature is null,
+   * then we fetch the signature from the location specified in the document.
+   *
+   * @param document
+   * @param signature if null, signature is fetched from location specified
+   *   in the document.
+   * @throws XmlSimpleSignException
+   */
+  public VerificationResult verify(byte[] document, String signature)
+      throws XmlSimpleSignException {
+    try {
+      /* xml parsing bits */
+      Document xml = XmlUtil.getJdomDocument(new ByteArrayInputStream(document));
+      Element signatureElement =
+          findDsig(xml.getRootElement(), Constants.SIGNATURE_ELEMENT);
+      parseSignatureInfo(signatureElement);
+
+      List<X509Certificate> docCerts = parseCerts(signatureElement);
+
+      byte[] sig;
+      if (signature == null) {
+        sig = parseSignatureValue(signatureElement);
+      } else {
+        sig = EncodingUtil.decodeBase64(signature);
+      }
+
+      return checkSignature(document, sig, docCerts);
+    } catch (JDOMException e) {
+      throw new XmlSimpleSignException("XML error", e);
+    } catch (IOException e) {
+      throw new XmlSimpleSignException("XML error", e);
+    } catch (GeneralSecurityException e) {
+      throw new XmlSimpleSignException("Signature verification error", e);
+    } catch (CertValidatorException e) {
+      throw new XmlSimpleSignException("Untrusted certificate", e);
+    }
+  }
+
+  private void parseSignatureInfo(Element signature) throws XmlSimpleSignException {
+
+    if (signature == null) {
+      throw new XmlSimpleSignException("no Signature element");
+    }
+
+    Element signedInfo = findDsig(signature, Constants.SIGNED_INFO_ELEMENT);
+    if (signedInfo == null) {
+      throw new XmlSimpleSignException("No SignedInfo element");
+    }
+    Element c14n = findDsig(signedInfo, Constants.CANONICALIZATION_METHOD_ELEMENT);
+    if (c14n == null) {
+      throw new XmlSimpleSignException("No CanonicalizationMethod element");
+    }
+    String c14nAlg = c14n.getAttributeValue(Constants.ALGORITHM_ATTRIBUTE);
+    if (!Constants.CANONICALIZE_RAW_OCTETS.equals(c14nAlg)) {
+      throw new XmlSimpleSignException("Unknown canonicalization algorithm: " + c14nAlg);
+    }
+    Element sigMethod = findDsig(signedInfo, Constants.SIGNATURE_METHOD_ELEMENT);
+    if (sigMethod == null) {
+      throw new XmlSimpleSignException("No SignatureMethod element");
+    }
+    // TODO: add support for alternate signing algorithms
+    String signingAlg = sigMethod.getAttributeValue(Constants.ALGORITHM_ATTRIBUTE);
+    if (!Constants.RSA_SHA1_ALGORITHM.equals(signingAlg)) {
+      throw new XmlSimpleSignException("Unknown signing algorithm: " + signingAlg);
+    }
+  }
+
+  private byte[] parseSignatureValue(Element signature) throws XmlSimpleSignException {
+    Element signatureLocation = findSimpleSig(signature, Constants.SIGNATURE_LOCATION_ELEMENT);
+    if (signatureLocation == null) {
+      throw new XmlSimpleSignException("No SignatureLocation element found");
+    }
+    String signatureHref = signatureLocation.getTextTrim();
+    if (signatureHref == null) {
+      throw new XmlSimpleSignException("No SignatureLocation text found");
+    }
+
+    FetchRequest request = FetchRequest.createGetRequest(URI.create(signatureHref));
+    try {
+      FetchResponse r = fetcher.fetch(request);
+      return EncodingUtil.decodeBase64(r.getContentAsBytes());
+    } catch (FetchException e) {
+      throw new XmlSimpleSignException("couldn't fetch signature from " +
+          signatureHref, e);
+    }
+  }
+
+  private List<X509Certificate> parseCerts(Element signature)
+      throws XmlSimpleSignException, GeneralSecurityException {
+    Element keyInfo = findDsig(signature, Constants.KEY_INFO_ELEMENT);
+    if (keyInfo == null) {
+      throw new XmlSimpleSignException("No KeyInfo element found");
+    }
+    Element x509Data = findDsig(keyInfo, Constants.X509_DATA_ELEMENT);
+    if (x509Data == null) {
+      throw new XmlSimpleSignException("No X509Data element found");
+    }
+    List<Element> certs = findElements(x509Data, Constants.X509_CERTIFICATE);
+    if (certs.isEmpty()) {
+      throw new XmlSimpleSignException("No X509Certificate elements found");
+    }
+    List<X509Certificate> docCerts = new ArrayList<X509Certificate>();
+    for (Element i : certs) {
+      docCerts.add(CertUtil.getCertFromBase64Bytes(i.getTextNormalize()));
+    }
+    return docCerts;
+  }
+
+  private VerificationResult checkSignature(byte[] document, byte[] sig,
+      List<X509Certificate> docCerts)
+      throws GeneralSecurityException, XmlSimpleSignException, CertValidatorException {
+    Signature verifier = Signature.getInstance(Constants.RSA_SHA1_JCE_ID);
+    verifier.initVerify(docCerts.get(0).getPublicKey());
+    verifier.update(document);
+    boolean match = verifier.verify(sig);
+    if (!match) {
+      throw new XmlSimpleSignException("Signature is invalid");
+    }
+    validator.validate(docCerts);
+    return new VerificationResult(docCerts);
+  }
+
+  private List<Element> findElements(Element parent, String name) {
+    List<Element> els = new ArrayList<Element>();
+    for (Element i : getChildren(parent)) {
+      if (name.equals(i.getName()) && Constants.XML_DSIG_NS.equals(i.getNamespace())) {
+        els.add(i);
+      }
+    }
+    return els;
+  }
+
+  private Element findDsig(Element parent, String name) {
+    return find(parent, name, Constants.XML_DSIG_NS);
+  }
+
+  private Element findSimpleSig(Element parent, String name) {
+    return find(parent, name, Constants.SIMPLE_SIGN_NS);
+  }
+
+  private Element find(Element parent, String name, Namespace ns) {
+    for (Element i : getChildren(parent)) {
+      if (name.equals(i.getName()) && ns.equals(i.getNamespace())) {
+        return i;
+      }
+    }
+    return null;
+  }
+
+  @SuppressWarnings("unchecked")
+  private List<Element> getChildren(Element xml) {
+    return xml.getChildren();
+  }
+}

Added: portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/XmlSimpleSignException.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/XmlSimpleSignException.java?rev=929218&view=auto
==============================================================================
--- portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/XmlSimpleSignException.java (added)
+++ portals/jetspeed-2/portal/trunk/openid-step2/common/src/main/java/com/google/step2/xmlsimplesign/XmlSimpleSignException.java Tue Mar 30 18:41:47 2010
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.step2.xmlsimplesign;
+
+import org.jdom.Document;
+import org.jdom.output.XMLOutputter;
+
+public class XmlSimpleSignException extends Exception {
+
+  public XmlSimpleSignException(String msg) {
+    super(msg);
+  }
+
+  public XmlSimpleSignException(Throwable cause) {
+    super(cause);
+  }
+
+  public XmlSimpleSignException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+
+  public XmlSimpleSignException(String msg, Document xml) {
+    super(msg + ": " + new XMLOutputter().outputString(xml));
+  }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: jetspeed-dev-unsubscribe@portals.apache.org
For additional commands, e-mail: jetspeed-dev-help@portals.apache.org


Mime
View raw message