* The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
* All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met: Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the
- * above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other
- * materials provided with the distribution, if any, must include the following acknowledgment: "This product includes
- * software developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2
- * Project. Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
- * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2,
- * nor the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
- * products derived from this software without specific prior written permission. For written permission, please
- * contact shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2,
- * UCAID, or the University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name,
- * without prior written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS
- * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
- * NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS
- * WITH LICENSEE. IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED
- * INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
+ * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
+ * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
+ * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
+ * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
+ * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
+ * products derived from this software without specific prior written permission. For written permission, please contact
+ * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
+ * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
+ * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
+ * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
+ * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
+ * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package edu.internet2.middleware.shibboleth.hs.provider;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutput;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
-import sun.misc.BASE64Decoder;
-import sun.misc.BASE64Encoder;
import edu.internet2.middleware.shibboleth.common.AuthNPrincipal;
import edu.internet2.middleware.shibboleth.common.IdentityProvider;
import edu.internet2.middleware.shibboleth.common.InvalidNameIdentifierException;
import edu.internet2.middleware.shibboleth.common.ServiceProvider;
import edu.internet2.middleware.shibboleth.common.ShibResource;
import edu.internet2.middleware.shibboleth.hs.HSNameIdentifierMapping;
+import edu.internet2.middleware.shibboleth.utils.Base32;
/**
* {@link HSNameIdentifierMapping}implementation that uses symmetric encryption to store principal data inside
* Shibboleth Attribute Query Handles.
*
* @author Walter Hoehn
+ * @author Derek Morr
*/
public class CryptoShibHandle extends AQHNameIdentifierMapping implements HSNameIdentifierMapping {
- private static Logger log = Logger.getLogger(CryptoShibHandle.class.getName());
- protected SecretKey secret;
- private SecureRandom random = new SecureRandom();
+ private static Logger log = Logger.getLogger(CryptoShibHandle.class.getName());
+ protected SecretKey secret;
+ private SecureRandom random = new SecureRandom();
+ private String cipherAlgorithm = "DESede/CBC/PKCS5Padding";
+ private String macAlgorithm = "HmacSHA1";
+ private String storeType = "JCEKS";
public CryptoShibHandle(Element config) throws NameIdentifierMappingException {
+
super(config);
try {
- String keyStorePath = getElementConfigData(config, "KeyStorePath");
- String keyStorePassword = getElementConfigData(config, "KeyStorePassword");
- String keyStoreKeyAlias = getElementConfigData(config, "KeyStoreKeyAlias");
- String keyStoreKeyPassword = getElementConfigData(config, "KeyStoreKeyPassword");
+ String keyStorePath = getElementConfigData(config, "KeyStorePath", true);
+ String keyStorePassword = getElementConfigData(config, "KeyStorePassword", true);
+ String keyStoreKeyAlias = getElementConfigData(config, "KeyStoreKeyAlias", true);
+ String keyStoreKeyPassword = getElementConfigData(config, "KeyStoreKeyPassword", true);
- KeyStore keyStore = KeyStore.getInstance("JCEKS");
+ String rawStoreType = getElementConfigData(config, "KeyStoreType", false);
+ if (rawStoreType != null && !rawStoreType.equals("")) {
+ storeType = rawStoreType;
+ }
+ String rawCipherAlgorithm = getElementConfigData(config, "Cipher", false);
+ if (rawCipherAlgorithm != null && !rawCipherAlgorithm.equals("")) {
+ cipherAlgorithm = rawCipherAlgorithm;
+ }
+ String rawMacAlgorithm = getElementConfigData(config, "MAC", false);
+ if (rawMacAlgorithm != null && !rawMacAlgorithm.equals("")) {
+ macAlgorithm = rawMacAlgorithm;
+ }
+
+ KeyStore keyStore = KeyStore.getInstance(storeType);
keyStore.load(new ShibResource(keyStorePath, this.getClass()).getInputStream(), keyStorePassword
.toCharArray());
secret = (SecretKey) keyStore.getKey(keyStoreKeyAlias, keyStoreKeyPassword.toCharArray());
- //Before we finish initilization, make sure that things are
- // working
+ // Before we finish initilization, make sure that things are working
testEncryption();
if (usingDefaultSecret()) {
- log
- .warn("You are running Crypto AQH Name Mapping with the default secret key. This is UNSAFE! Please change "
- + "this configuration and restart the origin.");
+ log.warn("You are running Crypto AQH Name Mapping with the "
+ + "default secret key. This is UNSAFE! Please change "
+ + "this configuration and restart the origin.");
}
} catch (StreamCorruptedException e) {
if (System.getProperty("java.version").startsWith("1.4.2")) {
- log.error("There is a bug in Java 1.4.2 that prevents JCEKS keystores from being loaded properly. "
+ log.error("There is a bug in some versions of Java 1.4.2.x that "
+ + "prevent JCEKS keystores from being loaded properly. "
+ "You probably need to upgrade or downgrade your JVM in order to make this work.");
}
- log.error("An error occurred while loading the java keystore. Unable to initialize Crypto Name Mapping: "
- + e);
+ log.error("An error occurred while loading the java keystore. Unable to initialize "
+ + "Crypto Name Mapping: " + e);
throw new NameIdentifierMappingException(
- "An error occurred while loading the java keystore. Unable to initialize Crypto Name Mapping.");
+ "An error occurred while loading the java keystore. Unable to initialize Crypto "
+ + "Name Mapping.");
} catch (KeyStoreException e) {
- log.error("An error occurred while loading the java keystore. Unable to initialize Crypto Name Mapping: "
- + e);
+ log.error("An error occurred while loading the java keystore. Unable to initialize Crypto "
+ + "Name Mapping: " + e);
throw new NameIdentifierMappingException(
"An error occurred while loading the java keystore. Unable to initialize Crypto Name Mapping.");
} catch (CertificateException e) {
throw new NameIdentifierMappingException(
"The java keystore contained corrupted data. Unable to initialize Crypto Name Mapping.");
} catch (NoSuchAlgorithmException e) {
- log
- .error("Appropriate JCE provider not found in the java environment. Unable to initialize Crypto Name Mapping: "
- + e);
+ log.error("Appropriate JCE provider not found in the java environment. Unable "
+ + "to initialize Crypto Name Mapping: " + e);
throw new NameIdentifierMappingException(
"Appropriate JCE provider not found in the java environment. Unable to initialize Crypto Name Mapping.");
} catch (IOException e) {
- log.error("An error accessing while loading the java keystore. Unable to initialize Crypto Name Mapping: "
- + e);
+ log.error("An error accessing while loading the java keystore. Unable to initialize Crypto Name "
+ + "Mapping: " + e);
throw new NameIdentifierMappingException(
"An error occurred while accessing the java keystore. Unable to initialize Crypto Name Mapping.");
} catch (UnrecoverableKeyException e) {
- log
- .error("Secret could not be loaded from the java keystore. Verify that the alias and password are correct: "
- + e);
+ log.error("Secret could not be loaded from the java keystore. Verify that the alias and "
+ + "password are correct: " + e);
throw new NameIdentifierMappingException(
"Secret could not be loaded from the java keystore. Verify that the alias and password are correct. ");
}
}
+ /**
+ * Decode an encrypted handle back into a principal
+ */
public AuthNPrincipal getPrincipal(SAMLNameIdentifier nameId, ServiceProvider sProv, IdentityProvider idProv)
throws NameIdentifierMappingException, InvalidNameIdentifierException {
verifyQualifier(nameId, idProv);
-
+
try {
- //Separate the IV and handle
- byte[] in = new BASE64Decoder().decodeBuffer(nameId.getName());
- if (in.length < 9) {
+ byte[] in = Base32.decode(nameId.getName());
+
+ Cipher cipher = Cipher.getInstance(cipherAlgorithm);
+ int ivSize = cipher.getBlockSize();
+ byte[] iv = new byte[ivSize];
+
+ Mac mac = Mac.getInstance(macAlgorithm);
+ mac.init(secret);
+ int macSize = mac.getMacLength();
+
+ if (in.length < ivSize) {
log.debug("Attribute Query Handle is malformed (not enough bytes).");
throw new NameIdentifierMappingException("Attribute Query Handle is malformed (not enough bytes).");
}
- byte[] iv = new byte[8];
- System.arraycopy(in, 0, iv, 0, 8);
- byte[] encryptedHandle = new byte[in.length - iv.length];
- System.arraycopy(in, 8, encryptedHandle, 0, in.length - iv.length);
- Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ // extract the IV, setup the cipher and extract the encrypted handle
+ System.arraycopy(in, 0, iv, 0, ivSize);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secret, ivSpec);
- byte[] objectArray = cipher.doFinal(encryptedHandle);
- GZIPInputStream zipBytesIn = new GZIPInputStream(new ByteArrayInputStream(objectArray));
-
- ObjectInputStream objectStream = new ObjectInputStream(zipBytesIn);
+ byte[] encryptedHandle = new byte[in.length - iv.length];
+ System.arraycopy(in, ivSize, encryptedHandle, 0, in.length - iv.length);
+
+ // decrypt the rest of the data and setup the streams
+ byte[] decryptedBytes = cipher.doFinal(encryptedHandle);
+ ByteArrayInputStream byteStream = new ByteArrayInputStream(decryptedBytes);
+ GZIPInputStream compressedData = new GZIPInputStream(byteStream);
+ DataInputStream dataStream = new DataInputStream(compressedData);
+
+ // extract the components
+ byte[] decodedMac = new byte[macSize];
+ int bytesRead = dataStream.read(decodedMac);
+ if (bytesRead != macSize) {
+ log.error("Error parsing handle: Unable to extract HMAC.");
+ throw new NameIdentifierMappingException("Error parsing handle: Unable to extract HMAC.");
+ }
+ long decodedExpirationTime = dataStream.readLong();
+ String decodedPrincipal = dataStream.readUTF();
- HMACHandleEntry handleEntry = (HMACHandleEntry) objectStream.readObject();
- objectStream.close();
+ HMACHandleEntry macHandleEntry = new HMACHandleEntry(
+ createHandleEntry(new AuthNPrincipal(decodedPrincipal)));
+ macHandleEntry.setExpirationTime(decodedExpirationTime);
+ byte[] generatedMac = macHandleEntry.getMAC(mac);
- if (handleEntry.isExpired()) {
+ if (macHandleEntry.isExpired()) {
log.debug("Attribute Query Handle is expired.");
throw new InvalidNameIdentifierException("Attribute Query Handle is expired.", errorCodes);
}
- Mac mac = Mac.getInstance("HmacSHA1");
- mac.init(secret);
- if (!handleEntry.isValid(mac)) {
+ if (!Arrays.equals(decodedMac, generatedMac)) {
log.warn("Attribute Query Handle failed integrity check.");
throw new NameIdentifierMappingException("Attribute Query Handle failed integrity check.");
}
log.debug("Attribute Query Handle recognized.");
- return handleEntry.principal;
+ return macHandleEntry.principal;
} catch (NoSuchAlgorithmException e) {
log.error("Appropriate JCE provider not found in the java environment. Could not load Algorithm: " + e);
throw new NameIdentifierMappingException(
"Appropriate JCE provider not found in the java environment. Could not load Algorithm.");
} catch (NoSuchPaddingException e) {
- log.error("Appropriate JCE provider not found in the java environment. Could not load Padding method: "
- + e);
+ log.error("Appropriate JCE provider not found in the java environment. Could not load Padding "
+ + "method: " + e);
throw new NameIdentifierMappingException(
"Appropriate JCE provider not found in the java environment. Could not load Padding method.");
} catch (InvalidKeyException e) {
} catch (GeneralSecurityException e) {
log.warn("Unable to decrypt the supplied Attribute Query Handle: " + e);
throw new NameIdentifierMappingException("Unable to decrypt the supplied Attribute Query Handle.");
- } catch (ClassNotFoundException e) {
- log.warn("The supplied Attribute Query Handle does not represent a serialized AuthNPrincipal: " + e);
- throw new NameIdentifierMappingException(
- "The supplied Attribute Query Handle does not represent a serialized AuthNPrincipal.");
} catch (IOException e) {
- log.warn("The AuthNPrincipal could not be de-serialized from the supplied Attribute Query Handle: " + e);
- throw new NameIdentifierMappingException(
- "The AuthNPrincipal could not be de-serialized from the supplied Attribute Query Handle.");
+ log.warn("IO error while decoding handle.");
+ throw new NameIdentifierMappingException("IO error while decoding handle.");
}
}
+ /**
+ * Encodes a principal into a cryptographic handle Format of encoded handle: [IV][HMAC][TTL][principal] where: [IV] =
+ * the Initialization Vector; byte-array [HMAC] = the HMAC; byte array [exprTime] = expiration time of the handle; 8
+ * bytes; Big-endian [principal] = the principal; a UTF-8-encoded string The [HMAC][exprTime][princLen][principal]
+ * byte stream is GZIPped. The IV is pre-pended to this byte stream, and the result is Base32-encoded. We don't need
+ * to encode the IV or MAC's lengths. They can be obtained from Cipher.getBlockSize() and Mac.getMacLength(),
+ * respectively.
+ */
public SAMLNameIdentifier getNameIdentifierName(AuthNPrincipal principal, ServiceProvider sProv,
IdentityProvider idProv) throws NameIdentifierMappingException {
throw new IllegalArgumentException("A principal must be supplied for Attribute Query Handle creation.");
}
- HandleEntry handleEntry = createHandleEntry(principal);
-
- Mac mac = Mac.getInstance("HmacSHA1");
+ Mac mac = Mac.getInstance(macAlgorithm);
mac.init(secret);
- HMACHandleEntry macHandleEntry = new HMACHandleEntry(handleEntry, mac);
-
- ByteArrayOutputStream outStream = new ByteArrayOutputStream();
- ByteArrayOutputStream encStream = new ByteArrayOutputStream();
+ HandleEntry handleEntry = createHandleEntry(principal);
+ HMACHandleEntry macHandleEntry = new HMACHandleEntry(handleEntry);
- Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
- byte[] iv = new byte[8];
+ Cipher cipher = Cipher.getInstance(cipherAlgorithm);
+ byte[] iv = new byte[cipher.getBlockSize()];
random.nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secret, ivSpec);
- //Handle contains 8 byte IV, followed by cipher text
- outStream.write(cipher.getIV());
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ GZIPOutputStream compressedStream = new GZIPOutputStream(byteStream);
+ DataOutputStream dataStream = new DataOutputStream(compressedStream);
- ObjectOutput objectStream = new ObjectOutputStream(new GZIPOutputStream(encStream));
- objectStream.writeObject(macHandleEntry);
- objectStream.close();
+ dataStream.write(macHandleEntry.getMAC(mac));
+ dataStream.writeLong(macHandleEntry.getExpirationTime());
+ dataStream.writeUTF(principal.getName());
- outStream.write(cipher.doFinal(encStream.toByteArray()));
- encStream.close();
+ dataStream.flush();
+ compressedStream.flush();
+ compressedStream.finish();
+ byteStream.flush();
- String handle = new BASE64Encoder().encode(outStream.toByteArray());
- outStream.close();
+ byte[] encryptedData = cipher.doFinal(byteStream.toByteArray());
+
+ byte[] handleBytes = new byte[iv.length + encryptedData.length];
+ System.arraycopy(iv, 0, handleBytes, 0, iv.length);
+ System.arraycopy(encryptedData, 0, handleBytes, iv.length, encryptedData.length);
+
+ String handle = Base32.encode(handleBytes);
try {
return new SAMLNameIdentifier(handle.replaceAll(System.getProperty("line.separator"), ""), idProv
throw new NameIdentifierMappingException(
"Appropriate JCE provider not found in the java environment. Could not load Cipher.");
} catch (IOException e) {
- log.error("Could not serialize Principal for handle creation: " + e);
- throw new NameIdentifierMappingException(
- "Could not serialize Principal for Attribute Query Handle creation.");
+ log.warn("IO error while decoding handle.");
+ throw new NameIdentifierMappingException("IO error while decoding handle.");
}
+
}
- private String getElementConfigData(Element e, String itemName) throws NameIdentifierMappingException {
+ private String getElementConfigData(Element e, String itemName, boolean required)
+ throws NameIdentifierMappingException {
NodeList itemElements = e.getElementsByTagNameNS(NameIdentifierMapping.mappingNamespace, itemName);
if (itemElements.getLength() < 1) {
- log.error(itemName + " not specified.");
- throw new NameIdentifierMappingException("Crypto Name Mapping requires a <" + itemName + "> specification.");
+ if (required) {
+ log.error(itemName + " not specified.");
+ throw new NameIdentifierMappingException("Crypto Name Mapping requires a <" + itemName
+ + "> specification.");
+ } else {
+ return null;
+ }
}
if (itemElements.getLength() > 1) {
}
if (item == null || item.equals("")) {
log.error(itemName + " not specified.");
- throw new NameIdentifierMappingException("Crypto Name Mapping requires a <" + itemName + "> specification.");
+ throw new NameIdentifierMappingException("Crypto Name Mapping requires a valid <" + itemName
+ + "> specification.");
}
return item;
}
String decrypted;
try {
- Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
- cipher.init(Cipher.ENCRYPT_MODE, secret);
+ Cipher cipher = Cipher.getInstance(cipherAlgorithm);
+ byte[] iv = new byte[cipher.getBlockSize()];
+ random.nextBytes(iv);
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ cipher.init(Cipher.ENCRYPT_MODE, secret, ivSpec);
byte[] cipherText = cipher.doFinal("test".getBytes());
- cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
- cipher.init(Cipher.DECRYPT_MODE, secret);
+ cipher = Cipher.getInstance(cipherAlgorithm);
+ cipher.init(Cipher.DECRYPT_MODE, secret, ivSpec);
decrypted = new String(cipher.doFinal(cipherText));
} catch (Exception e) {
log.error("Round trip encryption/decryption test unsuccessful: " + e);
byte[] code;
try {
- Mac mac = Mac.getInstance("HmacSHA1");
+ Mac mac = Mac.getInstance(macAlgorithm);
mac.init(secret);
mac.update("foo".getBytes());
code = mac.doFinal();
}
private boolean usingDefaultSecret() {
+
byte[] defaultKey = new byte[]{(byte) 0xC7, (byte) 0x49, (byte) 0x80, (byte) 0xD3, (byte) 0x02, (byte) 0x4A,
(byte) 0x61, (byte) 0xEF, (byte) 0x25, (byte) 0x5D, (byte) 0xE3, (byte) 0x2F, (byte) 0x57, (byte) 0x51,
(byte) 0x20, (byte) 0x15, (byte) 0xC7, (byte) 0x49, (byte) 0x80, (byte) 0xD3, (byte) 0x02, (byte) 0x4A,
* <code>HandleEntry</code> extension class that performs message authentication.
*/
-class HMACHandleEntry extends HandleEntry implements Serializable {
+class HMACHandleEntry extends HandleEntry {
- static final long serialVersionUID = 1L;
- protected byte[] code;
+ protected HMACHandleEntry(AuthNPrincipal principal, long TTL) {
- protected HMACHandleEntry(AuthNPrincipal principal, long TTL, Mac mac) {
super(principal, TTL);
- mac.update(this.principal.getName().getBytes());
- mac.update(new Long(this.expirationTime).byteValue());
- code = mac.doFinal();
}
- protected HMACHandleEntry(HandleEntry handleEntry, Mac mac) {
+ protected HMACHandleEntry(HandleEntry handleEntry) {
+
super(handleEntry.principal, handleEntry.expirationTime);
- mac.update(this.principal.getName().getBytes());
- mac.update(new Long(this.expirationTime).byteValue());
- code = mac.doFinal();
}
- boolean isValid(Mac mac) {
- mac.update(this.principal.getName().getBytes());
- mac.update(new Long(this.expirationTime).byteValue());
- byte[] validationCode = mac.doFinal();
- return Arrays.equals(code, validationCode);
+ private static byte[] getLongBytes(long longValue) {
+
+ try {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ DataOutputStream dataStream = new DataOutputStream(byteStream);
+
+ dataStream.writeLong(longValue);
+ dataStream.flush();
+ byteStream.flush();
+
+ return byteStream.toByteArray();
+ } catch (IOException ex) {
+ return null;
+ }
}
-}
+
+ public byte[] getMAC(Mac mac) {
+
+ mac.update(principal.getName().getBytes());
+ mac.update(getLongBytes(expirationTime));
+
+ return mac.doFinal();
+ }
+
+ public long getExpirationTime() {
+
+ return expirationTime;
+ }
+
+ public void setExpirationTime(long expr) {
+
+ expirationTime = expr;
+ }
+}
\ No newline at end of file
--- /dev/null
+/* (PD) 2001 The Bitzi Corporation
+ * Please see http://bitzi.com/publicdomain for more info.
+ *
+ * Base32.java
+ *
+ */
+
+package edu.internet2.middleware.shibboleth.utils;
+
+/**
+ * Base32 - encodes and decodes 'Canonical' Base32
+ *
+ * @author Robert Kaye & Gordon Mohr
+ */
+public class Base32 {
+
+ /* lookup table used to encode() groups of 5 bits of data */
+ private static final String base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+ /* lookup table used to decode() characters in Base32 strings */
+ private static final byte[] base32Lookup =
+ { 26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1, // 23456789:;<=>?
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, // @ABCDEFGHIJKLMNO
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, // PQRSTUVWXYZ[\]^_
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, // `abcdefghijklmno
+ 15,16,17,18,19,20,21,22,23,24,25 // pqrstuvwxyz
+ };
+
+ /* Messsages for Illegal Parameter Exceptions in decode() */
+ private static final String errorCanonicalLength = "non canonical Base32 string length";
+ private static final String errorCanonicalEnd = "non canonical bits at end of Base32 string";
+ private static final String errorInvalidChar = "invalid character in Base32 string";
+
+ /**
+ * Encode an array of binary bytes into a Base32 string.
+ * Should not fail (the only possible exception is that the
+ * returned string cannot be allocated in memory)
+ */
+ static public String encode(final byte[] bytes) {
+
+ StringBuffer base32 = new StringBuffer((bytes.length * 8 + 4) / 5);
+ int currByte, digit, i = 0;
+
+ while (i < bytes.length) {
+
+ // INVARIANTS FOR EACH STEP n in [0..5[; digit in [0..31[;
+ // The remaining n bits are already aligned on top positions
+ // of the 5 least bits of digit, the other bits are 0.
+
+ // STEP n = 0; insert new 5 bits, leave 3 bits
+ currByte = bytes[i++] & 255;
+ base32.append(base32Chars.charAt(currByte >> 3));
+ digit = (currByte & 7) << 2;
+ if (i >= bytes.length) { // put the last 3 bits
+ base32.append(base32Chars.charAt(digit));
+ break;
+ }
+
+
+ // STEP n = 3: insert 2 new bits, then 5 bits, leave 1 bit
+ currByte = bytes[i++] & 255;
+ base32.append(base32Chars.charAt(digit | (currByte >> 6)));
+ base32.append(base32Chars.charAt((currByte >> 1) & 31));
+ digit = (currByte & 1) << 4;
+ if (i >= bytes.length) { // put the last 1 bit
+ base32.append(base32Chars.charAt(digit));
+ break;
+ }
+
+ // STEP n = 1: insert 4 new bits, leave 4 bit
+ currByte = bytes[i++] & 255;
+ base32.append(base32Chars.charAt(digit | (currByte >> 4)));
+ digit = (currByte & 15) << 1;
+ if (i >= bytes.length) { // put the last 4 bits
+ base32.append(base32Chars.charAt(digit));
+ break;
+ }
+
+ // STEP n = 4: insert 1 new bit, then 5 bits, leave 2 bits
+ currByte = bytes[i++] & 255;
+ base32.append(base32Chars.charAt(digit | (currByte >> 7)));
+ base32.append(base32Chars.charAt((currByte >> 2) & 31));
+ digit = (currByte & 3) << 3;
+ if (i >= bytes.length) { // put the last 2 bits
+ base32.append(base32Chars.charAt(digit));
+ break;
+ }
+
+ // STEP n = 2: insert 3 new bits, then 5 bits, leave 0 bit
+ currByte = bytes[i++] & 255;
+ base32.append(base32Chars.charAt(digit | (currByte >> 5)));
+ base32.append(base32Chars.charAt(currByte & 31));
+ //// This point is reached for bytes.length multiple of 5
+ }
+
+ return base32.toString();
+ }
+
+
+ /**
+ * Decode a Base32 string into an array of binary bytes.
+ * May fail if the parameter is a non canonical Base32 string
+ * (the only other possible exception is that the
+ * returned array cannot be allocated in memory)
+ */
+ static public byte[] decode(final String base32) throws IllegalArgumentException {
+
+ // Note that the code below detects could detect non canonical
+ // Base32 length within the loop. However canonical Base32 length
+ // can be tested before entering the loop.
+ // A canonical Base32 length modulo 8 cannot be:
+ // 1 (aborts discarding 5 bits at STEP n=0 which produces no byte),
+ // 3 (aborts discarding 7 bits at STEP n=2 which produces no byte),
+ // 6 (aborts discarding 6 bits at STEP n=1 which produces no byte)
+ // So these tests could be avoided within the loop.
+ switch (base32.length() % 8) { // test the length of last subblock
+
+ case 1: // 5 bits in subblock: 0 useful bits but 5 discarded
+ case 3: // 15 bits in subblock: 8 useful bits but 7 discarded
+ case 6: // 30 bits in subblock: 24 useful bits but 6 discarded
+
+ throw new IllegalArgumentException(errorCanonicalLength);
+ }
+
+ byte[] bytes = new byte[base32.length() * 5 / 8];
+ int offset = 0, i = 0, lookup;
+ byte nextByte, digit;
+
+ // Also the code below does test that other discarded bits
+ // (1 to 4 bits at end) are effectively 0.
+ while (i < base32.length()) {
+ // Read the 1st char in a 8-chars subblock
+ // check that chars are not outside the lookup table and valid
+ lookup = base32.charAt(i++) - '2';
+ if (lookup < 0 || lookup >= base32Lookup.length) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+ digit = base32Lookup[lookup];
+ if (digit == -1) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+
+ // STEP n = 0: leave 5 bits
+ nextByte = (byte)(digit << 3);
+ // Assert(i < base32.length) // tested before loop
+ // Read the 2nd char in a 8-chars subblock
+ // Check that chars are not outside the lookup table and valid
+ lookup = base32.charAt(i++) - '2';
+ if (lookup < 0 || lookup >= base32Lookup.length) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+ digit = base32Lookup[lookup];
+ if (digit == -1) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+
+ // STEP n = 5: insert 3 bits, leave 2 bits
+ bytes[offset++] = (byte)(nextByte | (digit >> 2));
+ nextByte = (byte)((digit & 3) << 6);
+ if (i >= base32.length()) {
+ if (nextByte != (byte)0) {
+ throw new IllegalArgumentException(errorCanonicalEnd);
+ }
+ break; // discard the remaining 2 bits
+ }
+
+ // Read the 3rd char in a 8-chars subblock
+ // Check that chars are not outside the lookup table and valid
+ lookup = base32.charAt(i++) - '2';
+ if (lookup < 0 || lookup >= base32Lookup.length) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+ digit = base32Lookup[lookup];
+ if (digit == -1) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+
+ // STEP n = 2: leave 7 bits
+ nextByte |= (byte)(digit << 1);
+ // Assert(i < base32.length) // tested before loop
+ // Read the 4th char in a 8-chars subblock
+ // Check that chars are not outside the lookup table and valid
+ lookup = base32.charAt(i++) - '2';
+ if (lookup < 0 || lookup >= base32Lookup.length) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+ digit = base32Lookup[lookup];
+ if (digit == -1) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+
+ // STEP n = 7: insert 1 bit, leave 4 bits
+ bytes[offset++] = (byte)(nextByte | (digit >> 4));
+ nextByte = (byte)((digit & 15) << 4);
+ if (i >= base32.length()) {
+ if (nextByte != (byte)0) {
+ throw new IllegalArgumentException(errorCanonicalEnd);
+ }
+ break; // discard the remaining 4 bits
+ }
+
+ // Read the 5th char in a 8-chars subblock
+ // Assert that chars are not outside the lookup table and valid
+ lookup = base32.charAt(i++) - '2';
+ if (lookup < 0 || lookup >= base32Lookup.length) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+ digit = base32Lookup[lookup];
+ if (digit == -1) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+
+ // STEP n = 4: insert 4 bits, leave 1 bit
+ bytes[offset++] = (byte)(nextByte | (digit >> 1));
+ nextByte = (byte)((digit & 1) << 7);
+ if (i >= base32.length()) {
+ if (nextByte != (byte)0) {
+ throw new IllegalArgumentException(errorCanonicalEnd);
+ }
+ break; // discard the remaining 1 bit
+ }
+
+ // Read the 6th char in a 8-chars subblock
+ // Check that chars are not outside the lookup table and valid
+ lookup = base32.charAt(i++) - '2';
+ if (lookup < 0 || lookup >= base32Lookup.length) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+ digit = base32Lookup[lookup];
+ if (digit == -1) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+
+ // STEP n = 1: leave 6 bits
+ nextByte |= (byte)(digit << 2);
+ // Assert(i < base32.length) // tested before loop
+ // Read the 7th char in a 8-chars subblock
+ // Check that chars are not outside the lookup table and valid
+ lookup = base32.charAt(i++) - '2';
+ if (lookup < 0 || lookup >= base32Lookup.length) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+ digit = base32Lookup[lookup];
+ if (digit == -1) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+
+ // STEP n = 6: insert 2 bits, leave 3 bits
+ bytes[offset++] = (byte)(nextByte | (digit >> 3));
+ nextByte = (byte)((digit & 7) << 5);
+ if (i >= base32.length()) {
+ if (nextByte != (byte)0) {
+ throw new IllegalArgumentException(errorCanonicalEnd);
+ }
+ break; // discard the remaining 3 bits
+ }
+
+ // Read the 8th char in a 8-chars subblock
+ // Check that chars are not outside the lookup table and valid
+ lookup = base32.charAt(i++) - '2';
+ if (lookup < 0 || lookup >= base32Lookup.length) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+ digit = base32Lookup[lookup];
+ if (digit == -1) {
+ throw new IllegalArgumentException(errorInvalidChar);
+ }
+
+ // STEP n = 3: insert 5 bits, leave 0 bit
+ bytes[offset++] = (byte)(nextByte | digit);
+ // possible end of string here with no trailing bits
+ }
+
+ // On loop exit, discard trialing n bits.
+ return bytes;
+ }
+}
+
/*
- * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation
- * for Advanced Internet Development, Inc. All rights reserved
- *
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution, if any, must include
- * the following acknowledgment: "This product includes software developed by
- * the University Corporation for Advanced Internet Development
- * <http://www.ucaid.edu> Internet2 Project. Alternately, this acknowledegement
- * may appear in the software itself, if and wherever such third-party
- * acknowledgments normally appear.
- *
- * Neither the name of Shibboleth nor the names of its contributors, nor
- * Internet2, nor the University Corporation for Advanced Internet Development,
- * Inc., nor UCAID may be used to endorse or promote products derived from this
- * software without specific prior written permission. For written permission,
- * please contact shibboleth@shibboleth.org
- *
- * Products derived from this software may not be called Shibboleth, Internet2,
- * UCAID, or the University Corporation for Advanced Internet Development, nor
- * may Shibboleth appear in their name, without prior written permission of the
- * University Corporation for Advanced Internet Development.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
+ * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met: Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
+ * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
+ * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
+ * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
+ * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
+ * products derived from this software without specific prior written permission. For written permission, please contact
+ * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
+ * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
+ * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
+ * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
+ * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
+ * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package edu.internet2.middleware.shibboleth.hs.provider;
private DOMParser parser = new DOMParser();
public NameMapperTests(String name) {
+
super(name);
BasicConfigurator.resetConfiguration();
BasicConfigurator.configure();
- Logger.getRootLogger().setLevel(Level.OFF);
+ Logger.getRootLogger().setLevel(Level.DEBUG);
}
public static void main(String[] args) {
+
junit.textui.TestRunner.run(NameMapperTests.class);
BasicConfigurator.configure();
- Logger.getRootLogger().setLevel(Level.OFF);
+ Logger.getRootLogger().setLevel(Level.DEBUG);
}
protected void setUp() throws Exception {
+
super.setUp();
try {
parser.setFeature("http://xml.org/sax/features/validation", true);
parser.setFeature("http://apache.org/xml/features/validation/schema", true);
parser.setEntityResolver(new EntityResolver() {
+
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
if (systemId.endsWith("namemapper.xsd")) {
InputStream stream;
try {
stream = new FileInputStream("src/schemas/namemapper.xsd");
- if (stream != null) {
- return new InputSource(stream);
- }
+ if (stream != null) { return new InputSource(stream); }
throw new SAXException("Could not load entity: Null input stream");
} catch (FileNotFoundException e) {
throw new SAXException("Could not load entity: " + e);
});
parser.setErrorHandler(new ErrorHandler() {
+
public void error(SAXParseException arg0) throws SAXException {
+
throw new SAXException("Error parsing xml file: " + arg0);
}
+
public void fatalError(SAXParseException arg0) throws SAXException {
+
throw new SAXException("Error parsing xml file: " + arg0);
}
+
public void warning(SAXParseException arg0) throws SAXException {
+
throw new SAXException("Error parsing xml file: " + arg0);
}
});
fail("Failed to setup xml parser: " + e);
}
}
+
public void testCryptoMapping() {
try {
File file = new File("data/handle.jks");
- String rawConfig =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
+ " id=\"cryptotest\" format=\"urn:mace:shibboleth:1.0:nameIdentifier\" "
- + " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
- + " <KeyStorePath>"
- + file.toURL().toString()
- + "</KeyStorePath>"
+ + " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
+ + " <KeyStorePath>" + file.toURL().toString() + "</KeyStorePath>"
+ " <KeyStorePassword>shibhs</KeyStorePassword>"
+ " <KeyStoreKeyAlias>handlekey</KeyStoreKeyAlias>"
- + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ " </NameMapping>";
parser.parse(new InputSource(new StringReader(rawConfig)));
nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
- SAMLNameIdentifier nameId =
- nameMapper.getNameIdentifierName(
- "cryptotest",
- new AuthNPrincipal("testprincipal"),
- new BasicServiceProvider(),
+ SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("cryptotest", new AuthNPrincipal(
+ "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
+
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
new BasicIdentityProvider("urn-x:testid"));
+ assertEquals("Round-trip handle validation failed.", principal.getName(), "testprincipal");
+
+ } catch (MalformedURLException e) {
+ fail("Error in test specification: " + e.getMessage());
+ } catch (NameIdentifierMappingException e) {
+ fail("Error exercising NameMaper: " + e.getMessage());
+ } catch (Exception e) {
+ fail("Error exercising NameMaper: " + e.getMessage());
+ }
+
+ }
+
+ public void testCryptoMappingWithOverriddenAlgorithms() {
+
+ try {
+
+ HSNameMapper nameMapper = new HSNameMapper();
- AuthNPrincipal principal =
- nameMapper.getPrincipal(nameId, new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
+ File file = new File("data/handle.jks");
+
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ + " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
+ + " id=\"cryptotest\" format=\"urn:mace:shibboleth:1.0:nameIdentifier\" "
+ + " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
+ + " <KeyStorePath>"+ file.toURL().toString() + "</KeyStorePath>"
+ + " <KeyStorePassword>shibhs</KeyStorePassword>"
+ + " <KeyStoreKeyAlias>handlekey</KeyStoreKeyAlias>"
+ + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ + " <Cipher>DESede/CBC/PKCS5Padding</Cipher>"
+ + " <MAC>HmacSHA1</MAC>"
+ + " <KeyStoreType>JCEKS</KeyStoreType>"
+ + " </NameMapping>";
+
+ parser.parse(new InputSource(new StringReader(rawConfig)));
+ nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
+
+ SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("cryptotest", new AuthNPrincipal(
+ "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
+
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:testid"));
assertEquals("Round-trip handle validation failed.", principal.getName(), "testprincipal");
} catch (MalformedURLException e) {
}
}
+
+ public void testCryptoMappingBadQualifier() {
+
+ try {
+
+ HSNameMapper nameMapper = new HSNameMapper();
+
+ File file = new File("data/handle.jks");
+
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ + " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
+ + " id=\"cryptotest\" format=\"urn:mace:shibboleth:1.0:nameIdentifier\" "
+ + " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
+ + " <KeyStorePath>" + file.toURL().toString() + "</KeyStorePath>"
+ + " <KeyStorePassword>shibhs</KeyStorePassword>"
+ + " <KeyStoreKeyAlias>handlekey</KeyStoreKeyAlias>"
+ + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ + " </NameMapping>";
+
+ parser.parse(new InputSource(new StringReader(rawConfig)));
+ nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
+
+ SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("cryptotest", new AuthNPrincipal(
+ "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:good"));
+
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:bad"));
+
+ fail("Expected failure for bad name qualifier.");
+
+ } catch (NameIdentifierMappingException e) {
+ //This exception should be generated by this test
+
+ } catch (MalformedURLException e) {
+ fail("Error in test specification: " + e.getMessage());
+
+ } catch (Exception e) {
+ fail("Error exercising NameMaper: " + e.getMessage());
+ }
+
+ }
+
public void testDefaultConfig() {
try {
HSNameMapper nameMapper = new HSNameMapper();
- SAMLNameIdentifier nameId =
- nameMapper.getNameIdentifierName(
- null,
- new AuthNPrincipal("testprincipal"),
- new BasicServiceProvider(),
- new BasicIdentityProvider("urn-x:testid"));
+ SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName(null, new AuthNPrincipal("testprincipal"),
+ new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
- AuthNPrincipal principal =
- nameMapper.getPrincipal(nameId, new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:testid"));
assertEquals("Round-trip handle validation failed.", principal.getName(), "testprincipal");
File file = new File("data/handle.jks");
- String rawConfig =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
+ " format=\"urn:mace:shibboleth:1.0:nameIdentifier\""
- + " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
- + " <KeyStorePath>"
- + file.toURL().toString()
- + "</KeyStorePath>"
+ + " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
+ + " <KeyStorePath>" + file.toURL().toString() + "</KeyStorePath>"
+ " <KeyStorePassword>shibhs</KeyStorePassword>"
+ " <KeyStoreKeyAlias>handlekey</KeyStoreKeyAlias>"
- + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ " </NameMapping>";
parser.parse(new InputSource(new StringReader(rawConfig)));
nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
- SAMLNameIdentifier nameId =
- nameMapper.getNameIdentifierName(
- null,
- new AuthNPrincipal("testprincipal"),
- new BasicServiceProvider(),
- new BasicIdentityProvider("urn-x:testid"));
+ SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName(null, new AuthNPrincipal("testprincipal"),
+ new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
- AuthNPrincipal principal =
- nameMapper.getPrincipal(nameId, new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:testid"));
assertEquals("Round-trip handle validation failed.", principal.getName(), "testprincipal");
File file = new File("data/handle.jks");
- String rawConfig =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
+ " format=\"urn:mace:shibboleth:1.0:nameIdentifier\""
- + " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
- + " <KeyStorePath>"
- + file.toURL().toString()
- + "</KeyStorePath>"
+ + " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
+ + " <KeyStorePath>" + file.toURL().toString() + "</KeyStorePath>"
+ " <KeyStorePassword>shibhs</KeyStorePassword>"
+ " <KeyStoreKeyAlias>handlekey</KeyStoreKeyAlias>"
- + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ " </NameMapping>";
parser.parse(new InputSource(new StringReader(rawConfig)));
nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
- String rawConfig2 =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ String rawConfig2 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
- + " format=\"urn-x:testNameIdentifier\""
+ + " format=\"urn-x:testNameIdentifier\""
+ " type=\"CryptoHandleGenerator\" handleTTL=\"1800\">"
- + " <KeyStorePath>"
- + file.toURL().toString()
- + "</KeyStorePath>"
+ + " <KeyStorePath>" + file.toURL().toString() + "</KeyStorePath>"
+ " <KeyStorePassword>shibhs</KeyStorePassword>"
+ " <KeyStoreKeyAlias>handlekey</KeyStoreKeyAlias>"
- + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ + " <KeyStoreKeyPassword>shibhs</KeyStoreKeyPassword>"
+ " </NameMapping>";
parser.parse(new InputSource(new StringReader(rawConfig2)));
nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
- nameMapper.getNameIdentifierName(
- null,
- new AuthNPrincipal("testprincipal"),
- new BasicServiceProvider(),
- new BasicIdentityProvider("urn-x:testid"));
+ nameMapper.getNameIdentifierName(null, new AuthNPrincipal("testprincipal"), new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:testid"));
fail("HSNameMapper defaulted to incorrect name mapping.");
fail("Error exercising NameMaper: " + e.getMessage());
}
}
+
public void testMemoryMapping() {
try {
HSNameMapper nameMapper = new HSNameMapper();
- String rawConfig =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
- + " id=\"memorytest\" "
+ + " id=\"memorytest\" "
+ " format=\"urn:mace:shibboleth:1.0:nameIdentifier\""
+ " type=\"SharedMemoryShibHandle\" handleTTL=\"1800\"/>";
parser.parse(new InputSource(new StringReader(rawConfig)));
nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
- SAMLNameIdentifier nameId =
- nameMapper.getNameIdentifierName(
- "memorytest",
- new AuthNPrincipal("testprincipal"),
- new BasicServiceProvider(),
- new BasicIdentityProvider("urn-x:testid"));
+ SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("memorytest", new AuthNPrincipal(
+ "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
- AuthNPrincipal principal =
- nameMapper.getPrincipal(nameId, new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:testid"));
assertEquals("Round-trip handle validation failed.", principal.getName(), "testprincipal");
fail("Error exercising NameMaper: " + e.getMessage());
}
}
+
+ public void testMemoryMappingBadQualifier() {
+
+ try {
+
+ HSNameMapper nameMapper = new HSNameMapper();
+
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ + " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
+ + " id=\"memorytest\" "
+ + " format=\"urn:mace:shibboleth:1.0:nameIdentifier\""
+ + " type=\"SharedMemoryShibHandle\" handleTTL=\"1800\"/>";
+
+ parser.parse(new InputSource(new StringReader(rawConfig)));
+ nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
+
+ SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("memory", new AuthNPrincipal(
+ "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:good"));
+
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:bad"));
+
+ fail("Expected failure for bad name qualifier.");
+
+ } catch (NameIdentifierMappingException e) {
+ //This exception should be generated by this test
+
+ } catch (MalformedURLException e) {
+ fail("Error in test specification: " + e.getMessage());
+
+ } catch (Exception e) {
+ fail("Error exercising NameMaper: " + e.getMessage());
+ }
+ }
public void testPrincipalMapping() {
NameMapper nameMapper = new NameMapper();
String format = "urn-x:test:NameIdFormat1";
- String rawConfig =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
- + " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
- + " format=\""
- + format
- + "\""
- + " type=\"Principal\"/>";
+ + " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
+ + " format=\"" + format + "\"" + " type=\"Principal\"/>";
parser.parse(new InputSource(new StringReader(rawConfig)));
nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
SAMLNameIdentifier nameId = new SAMLNameIdentifier("testprincipal", "urn-x:testid", format);
- AuthNPrincipal principal =
- nameMapper.getPrincipal(nameId, new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:testid"));
assertEquals("Round-trip handle validation failed.", principal.getName(), "testprincipal");
}
}
+
+ public void testPrincipalMappingBadQualifier() {
+
+ try {
+
+ NameMapper nameMapper = new NameMapper();
+
+ String format = "urn-x:test:NameIdFormat1";
+ String rawConfig = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<NameMapping xmlns=\"urn:mace:shibboleth:namemapper:1.0\""
+ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ + " xsi:schemaLocation=\"urn:mace:shibboleth:namemapper:1.0 namemapper.xsd\" "
+ + " format=\"" + format + "\"" + " type=\"Principal\"/>";
+ parser.parse(new InputSource(new StringReader(rawConfig)));
+ nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
+
+ SAMLNameIdentifier nameId = new SAMLNameIdentifier("testprincipal", "urn-x:good", format);
+
+ AuthNPrincipal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
+ new BasicIdentityProvider("urn-x:bad"));
+
+ fail("Expected failure for bad name qualifier.");
+
+ } catch (NameIdentifierMappingException e) {
+ //This exception should be generated by this test
+
+ } catch (MalformedURLException e) {
+ fail("Error in test specification: " + e.getMessage());
+
+ } catch (Exception e) {
+ fail("Error exercising NameMaper: " + e.getMessage());
+ }
+
+ }
}
class BasicIdentityProvider implements IdentityProvider {
+
String id;
public BasicIdentityProvider(String id) {
+
this.id = id;
}
public String getProviderId() {
+
return id;
}
public Credential getResponseSigningCredential() {
+
return null;
}
public Credential getAssertionSigningCredential() {
+
return null;
}
}
+
class BasicServiceProvider implements ServiceProvider {
public String getProviderId() {
return null;
}
-}
+}
\ No newline at end of file