SEC-1303: Added internal Hex and Base64 classes, and moved commons-codec dependency to test scope

This commit is contained in:
Luke Taylor 2009-11-24 09:31:03 +00:00
parent cd6711d21a
commit 69699431b1
21 changed files with 832 additions and 127 deletions

View File

@ -59,6 +59,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>

View File

@ -69,10 +69,6 @@
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>

View File

@ -16,13 +16,12 @@
package org.springframework.security.authentication.encoding;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.Assert;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import org.springframework.security.core.codec.Base64;
import org.springframework.util.Assert;
/**
* A version of {@link ShaPasswordEncoder} which supports Ldap SHA and SSHA (salted-SHA) encodings. The values are
@ -103,13 +102,13 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
prefix = forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
}
return prefix + new String(Base64.encodeBase64(hash));
return prefix + new String(Base64.encode(hash));
}
private byte[] extractSalt(String encPass) {
String encPassNoLabel = encPass.substring(6);
byte[] hashAndSalt = Base64.decodeBase64(encPassNoLabel.getBytes());
byte[] hashAndSalt = Base64.decode(encPassNoLabel.getBytes());
int saltLength = hashAndSalt.length - SHA_LENGTH;
byte[] salt = new byte[saltLength];
System.arraycopy(hashAndSalt, SHA_LENGTH, salt, 0, saltLength);

View File

@ -16,8 +16,8 @@ package org.springframework.security.authentication.encoding;
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.codec.Hex;
/**
* MD4 implementation of PasswordEncoder.
@ -45,7 +45,7 @@ public class Md4PasswordEncoder extends BaseDigestPasswordEncoder {
*/
public String encodePassword(String rawPass, Object salt) {
String saltedPass = mergePasswordAndSalt(rawPass, salt, false);
byte[] passBytes;
try {
@ -53,16 +53,16 @@ public class Md4PasswordEncoder extends BaseDigestPasswordEncoder {
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported!");
}
Md4 md4 = new Md4();
md4.update(passBytes, 0, passBytes.length);
byte[] resBuf = md4.digest();
if (getEncodeHashAsBase64()) {
return new String(Base64.encodeBase64(resBuf));
return new String(Base64.encode(resBuf));
} else {
return new String(Hex.encodeHex(resBuf));
return new String(Hex.encode(resBuf));
}
}
@ -84,4 +84,4 @@ public class Md4PasswordEncoder extends BaseDigestPasswordEncoder {
public String getAlgorithm() {
return "MD4";
}
}
}

View File

@ -1,12 +1,12 @@
package org.springframework.security.authentication.encoding;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.codec.Hex;
/**
* Base for digest password encoders.
* <p>This class can be used stand-alone, or one of the subclasses can be used for compatiblity and convenience.
@ -74,7 +74,7 @@ public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder {
MessageDigest messageDigest = getMessageDigest();
byte[] digest;
try {
digest = messageDigest.digest(saltedPass.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
@ -82,9 +82,9 @@ public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder {
}
if (getEncodeHashAsBase64()) {
return new String(Base64.encodeBase64(digest));
return new String(Base64.encode(digest));
} else {
return new String(Hex.encodeHex(digest));
return new String(Hex.encode(digest));
}
}

View File

@ -0,0 +1,649 @@
package org.springframework.security.core.codec;
/**
* Base64 encoder which is a reduced version of Robert Harder's public domain implementation.
* See <a href="http://iharder.net/base64">http://iharder.net/base64</a> for more information.
* <p>
* For internal use only.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public final class Base64 {
/** No options specified. Value is zero. */
public final static int NO_OPTIONS = 0;
/** Specify encoding in first bit. Value is one. */
public final static int ENCODE = 1;
/** Specify decoding in first bit. Value is zero. */
public final static int DECODE = 0;
/** Do break lines when encoding. Value is 8. */
public final static int DO_BREAK_LINES = 8;
/**
* Encode using Base64-like encoding that is URL- and Filename-safe as described
* in Section 4 of RFC3548:
* <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
* It is important to note that data encoded this way is <em>not</em> officially valid Base64,
* or at the very least should not be called Base64 without also specifying that is
* was encoded using the URL- and Filename-safe dialect.
*/
public final static int URL_SAFE = 16;
/**
* Encode using the special "ordered" dialect of Base64 described here:
* <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
*/
public final static int ORDERED = 32;
/** Maximum line length (76) of Base64 output. */
private final static int MAX_LINE_LENGTH = 76;
/** The equals sign (=) as a byte. */
private final static byte EQUALS_SIGN = (byte)'=';
/** The new line character (\n) as a byte. */
private final static byte NEW_LINE = (byte)'\n';
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
/** The 64 valid Base64 values. */
/* Host platform me be something funny like EBCDIC, so we hardcode these values. */
private final static byte[] _STANDARD_ALPHABET = {
(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
(byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
(byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
(byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
(byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
(byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
(byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
(byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
};
/**
* Translates a Base64 value to either its 6-bit reconstruction value
* or a negative number indicating some other meaning.
**/
private final static byte[] _STANDARD_DECODABET = {
-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
-5,-5, // Whitespace: Tab and Linefeed
-9,-9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
-9,-9,-9,-9,-9, // Decimal 27 - 31
-5, // Whitespace: Space
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
62, // Plus sign at decimal 43
-9,-9,-9, // Decimal 44 - 46
63, // Slash at decimal 47
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
-9,-9,-9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9,-9,-9, // Decimal 62 - 64
0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
-9,-9,-9,-9,-9,-9, // Decimal 91 - 96
26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
-9,-9,-9,-9 // Decimal 123 - 126
/*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
/**
* Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:
* <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
* Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
*/
private final static byte[] _URL_SAFE_ALPHABET = {
(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
(byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
(byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
(byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
(byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
(byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
(byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
(byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
};
/**
* Used in decoding URL- and Filename-safe dialects of Base64.
*/
private final static byte[] _URL_SAFE_DECODABET = {
-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
-5,-5, // Whitespace: Tab and Linefeed
-9,-9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
-9,-9,-9,-9,-9, // Decimal 27 - 31
-5, // Whitespace: Space
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
-9, // Plus sign at decimal 43
-9, // Decimal 44
62, // Minus sign at decimal 45
-9, // Decimal 46
-9, // Slash at decimal 47
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
-9,-9,-9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9,-9,-9, // Decimal 62 - 64
0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
-9,-9,-9,-9, // Decimal 91 - 94
63, // Underscore at decimal 95
-9, // Decimal 96
26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
-9,-9,-9,-9 // Decimal 123 - 126
/*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
/**
* I don't get the point of this technique, but someone requested it,
* and it is described here:
* <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
*/
private final static byte[] _ORDERED_ALPHABET = {
(byte)'-',
(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
(byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
(byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
(byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
(byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
(byte)'_',
(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
(byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
(byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
(byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
};
/**
* Used in decoding the "ordered" dialect of Base64.
*/
private final static byte[] _ORDERED_DECODABET = {
-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
-5,-5, // Whitespace: Tab and Linefeed
-9,-9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
-9,-9,-9,-9,-9, // Decimal 27 - 31
-5, // Whitespace: Space
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
-9, // Plus sign at decimal 43
-9, // Decimal 44
0, // Minus sign at decimal 45
-9, // Decimal 46
-9, // Slash at decimal 47
1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine
-9,-9,-9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9,-9,-9, // Decimal 62 - 64
11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M'
24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z'
-9,-9,-9,-9, // Decimal 91 - 94
37, // Underscore at decimal 95
-9, // Decimal 96
38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm'
51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z'
-9,-9,-9,-9 // Decimal 123 - 126
/*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
public static byte[] decode(byte[] bytes) {
return decode(bytes, 0, bytes.length, NO_OPTIONS);
}
public static byte[] encode(byte[] bytes) {
return encodeBytesToBytes(bytes, 0, bytes.length, NO_OPTIONS);
}
public static boolean isBase64(byte[] bytes) {
try {
decode(bytes);
} catch (InvalidBase64CharacterException e) {
return false;
}
return true;
}
/**
* Returns one of the _SOMETHING_ALPHABET byte arrays depending on
* the options specified.
* It's possible, though silly, to specify ORDERED <b>and</b> URLSAFE
* in which case one of them will be picked, though there is
* no guarantee as to which one will be picked.
*/
private final static byte[] getAlphabet( int options ) {
if ((options & URL_SAFE) == URL_SAFE) {
return _URL_SAFE_ALPHABET;
} else if ((options & ORDERED) == ORDERED) {
return _ORDERED_ALPHABET;
} else {
return _STANDARD_ALPHABET;
}
}
/**
* Returns one of the _SOMETHING_DECODABET byte arrays depending on
* the options specified.
* It's possible, though silly, to specify ORDERED and URL_SAFE
* in which case one of them will be picked, though there is
* no guarantee as to which one will be picked.
*/
private final static byte[] getDecodabet( int options ) {
if( (options & URL_SAFE) == URL_SAFE) {
return _URL_SAFE_DECODABET;
} else if ((options & ORDERED) == ORDERED) {
return _ORDERED_DECODABET;
} else {
return _STANDARD_DECODABET;
}
}
/* ******** E N C O D I N G M E T H O D S ******** */
/**
* <p>Encodes up to three bytes of the array <var>source</var>
* and writes the resulting four Base64 bytes to <var>destination</var>.
* The source and destination arrays can be manipulated
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accomodate <var>srcOffset</var> + 3 for
* the <var>source</var> array or <var>destOffset</var> + 4 for
* the <var>destination</var> array.
* The actual number of significant bytes in your array is
* given by <var>numSigBytes</var>.</p>
* <p>This is the lowest level of the encoding methods with
* all possible parameters.</p>
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param numSigBytes the number of significant bytes in your array
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @return the <var>destination</var> array
* @since 1.3
*/
private static byte[] encode3to4(
byte[] source, int srcOffset, int numSigBytes,
byte[] destination, int destOffset, int options ) {
byte[] ALPHABET = getAlphabet( options );
// 1 2 3
// 01234567890123456789012345678901 Bit position
// --------000000001111111122222222 Array position from threeBytes
// --------| || || || | Six bit groups to index ALPHABET
// >>18 >>12 >> 6 >> 0 Right shift necessary
// 0x3f 0x3f 0x3f Additional AND
// Create buffer with zero-padding if there are only one or two
// significant bytes passed in the array.
// We have to shift left 24 in order to flush out the 1's that appear
// when Java treats a value as negative that is cast from a byte to an int.
int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
| ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
| ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
switch( numSigBytes )
{
case 3:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
return destination;
case 2:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
destination[ destOffset + 3 ] = EQUALS_SIGN;
return destination;
case 1:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = EQUALS_SIGN;
destination[ destOffset + 3 ] = EQUALS_SIGN;
return destination;
default:
return destination;
}
}
/**
* Similar to {@link #encodeBytes(byte[], int, int, int)} but returns
* a byte array instead of instantiating a String. This is more efficient
* if you're working with I/O streams and have large data sets to encode.
*
*
* @param source The data to convert
* @param off Offset in array where conversion should begin
* @param len Length of data to convert
* @param options Specified options
* @return The Base64-encoded data as a String
* @see Base64#DO_BREAK_LINES
* @throws java.io.IOException if there is an error
* @throws NullPointerException if source array is null
* @throws IllegalArgumentException if source array, offset, or length are invalid
* @since 2.3.1
*/
private static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) {
if( source == null ){
throw new NullPointerException( "Cannot serialize a null array." );
} // end if: null
if( off < 0 ){
throw new IllegalArgumentException( "Cannot have negative offset: " + off );
} // end if: off < 0
if( len < 0 ){
throw new IllegalArgumentException( "Cannot have length offset: " + len );
} // end if: len < 0
if( off + len > source.length ){
throw new IllegalArgumentException(
String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length));
} // end if: off < 0
boolean breakLines = (options & DO_BREAK_LINES) > 0;
//int len43 = len * 4 / 3;
//byte[] outBuff = new byte[ ( len43 ) // Main 4:3
// + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
// + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
// Try to determine more precisely how big the array needs to be.
// If we get it right, we don't have to do an array copy, and
// we save a bunch of memory.
int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding
if( breakLines ){
encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters
}
byte[] outBuff = new byte[ encLen ];
int d = 0;
int e = 0;
int len2 = len - 2;
int lineLength = 0;
for( ; d < len2; d+=3, e+=4 ) {
encode3to4( source, d+off, 3, outBuff, e, options );
lineLength += 4;
if( breakLines && lineLength >= MAX_LINE_LENGTH )
{
outBuff[e+4] = NEW_LINE;
e++;
lineLength = 0;
} // end if: end of line
} // en dfor: each piece of array
if( d < len ) {
encode3to4( source, d+off, len - d, outBuff, e, options );
e += 4;
} // end if: some padding needed
// Only resize array if we didn't guess it right.
if( e < outBuff.length - 1 ){
byte[] finalOut = new byte[e];
System.arraycopy(outBuff,0, finalOut,0,e);
//System.err.println("Having to resize array from " + outBuff.length + " to " + e );
return finalOut;
} else {
//System.err.println("No need to resize array.");
return outBuff;
}
}
/* ******** D E C O D I N G M E T H O D S ******** */
/**
* Decodes four bytes from array <var>source</var>
* and writes the resulting bytes (up to three of them)
* to <var>destination</var>.
* The source and destination arrays can be manipulated
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accomodate <var>srcOffset</var> + 4 for
* the <var>source</var> array or <var>destOffset</var> + 3 for
* the <var>destination</var> array.
* This method returns the actual number of bytes that
* were converted from the Base64 encoding.
* <p>This is the lowest level of the decoding methods with
* all possible parameters.</p>
*
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @param options alphabet type is pulled from this (standard, url-safe, ordered)
* @return the number of decoded bytes converted
* @throws NullPointerException if source or destination arrays are null
* @throws IllegalArgumentException if srcOffset or destOffset are invalid
* or there is not enough room in the array.
* @since 1.3
*/
private static int decode4to3(
byte[] source, int srcOffset,
byte[] destination, int destOffset, int options ) {
// Lots of error checking and exception throwing
if( source == null ){
throw new NullPointerException( "Source array was null." );
} // end if
if( destination == null ){
throw new NullPointerException( "Destination array was null." );
} // end if
if( srcOffset < 0 || srcOffset + 3 >= source.length ){
throw new IllegalArgumentException( String.format(
"Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) );
} // end if
if( destOffset < 0 || destOffset +2 >= destination.length ){
throw new IllegalArgumentException( String.format(
"Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) );
} // end if
byte[] DECODABET = getDecodabet( options );
// Example: Dk==
if( source[ srcOffset + 2] == EQUALS_SIGN ) {
// Two ways to do the same thing. Don't know which way I like best.
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
| ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
destination[ destOffset ] = (byte)( outBuff >>> 16 );
return 1;
}
// Example: DkL=
else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) {
// Two ways to do the same thing. Don't know which way I like best.
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
| ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
| ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
destination[ destOffset ] = (byte)( outBuff >>> 16 );
destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
return 2;
}
// Example: DkLE
else {
// Two ways to do the same thing. Don't know which way I like best.
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
| ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
| ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
| ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
destination[ destOffset ] = (byte)( outBuff >> 16 );
destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
destination[ destOffset + 2 ] = (byte)( outBuff );
return 3;
}
}
/**
* Low-level access to decoding ASCII characters in
* the form of a byte array. <strong>Ignores GUNZIP option, if
* it's set.</strong> This is not generally a recommended method,
* although it is used internally as part of the decoding process.
* Special case: if len = 0, an empty array is returned. Still,
* if you need more speed and reduced memory footprint (and aren't
* gzipping), consider this method.
*
* @param source The Base64 encoded data
* @param off The offset of where to begin decoding
* @param len The length of characters to decode
* @param options Can specify options such as alphabet type to use
* @return decoded data
* @throws IllegalArgumentException If bogus characters exist in source data
*/
private static byte[] decode( byte[] source, int off, int len, int options ) {
// Lots of error checking and exception throwing
if( source == null ){
throw new NullPointerException( "Cannot decode null source array." );
} // end if
if( off < 0 || off + len > source.length ){
throw new IllegalArgumentException( String.format(
"Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) );
} // end if
if( len == 0 ){
return new byte[0];
}else if( len < 4 ){
throw new IllegalArgumentException(
"Base64-encoded string must have at least four characters, but length specified was " + len );
} // end if
byte[] DECODABET = getDecodabet( options );
int len34 = len * 3 / 4; // Estimate on array size
byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
int outBuffPosn = 0; // Keep track of where we're writing
byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space
int b4Posn = 0; // Keep track of four byte input buffer
int i = 0; // Source array counter
byte sbiCrop = 0; // Low seven bits (ASCII) of input
byte sbiDecode = 0; // Special value from DECODABET
for( i = off; i < off+len; i++ ) { // Loop through source
sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
sbiDecode = DECODABET[ sbiCrop ]; // Special value
// White space, Equals sign, or legit Base64 character
// Note the values such as -5 and -9 in the
// DECODABETs at the top of the file.
if( sbiDecode >= WHITE_SPACE_ENC ) {
if( sbiDecode >= EQUALS_SIGN_ENC ) {
b4[ b4Posn++ ] = sbiCrop; // Save non-whitespace
if( b4Posn > 3 ) { // Time to decode?
outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
b4Posn = 0;
// If that was the equals sign, break out of 'for' loop
if( sbiCrop == EQUALS_SIGN ) {
break;
}
}
}
}
else {
// There's a bad input character in the Base64 stream.
throw new InvalidBase64CharacterException( String.format(
"Bad Base64 input character '%c' in array position %d", source[i], i ) );
}
}
byte[] out = new byte[ outBuffPosn ];
System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
return out;
}
}
class InvalidBase64CharacterException extends IllegalArgumentException {
InvalidBase64CharacterException(String message) {
super(message);
}
}

View File

@ -0,0 +1,37 @@
package org.springframework.security.core.codec;
/**
* Hex data encoder. Converts byte arrays (such as those obtained from message digests)
* into hexadecimal string representation.
* <p>
* For internal use only.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public final class Hex {
private static final char[] HEX = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
public static char[] encode(byte[] bytes) {
final int nBytes = bytes.length;
char[] result = new char[2*nBytes];
int j = 0;
for (int i=0; i < nBytes; i++) {
// Char for top 4 bits
result[j++] = HEX[(0xF0 & bytes[i]) >>> 4 ];
// Bottom 4
result[j++] = HEX[(0x0F & bytes[i])];
}
return result;
}
// public static byte[] decode(char[] hex) {
//
// }
}

View File

@ -4,31 +4,31 @@ import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Date;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.codec.Hex;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Basic implementation of {@link TokenService} that is compatible with clusters and across machine restarts,
* without requiring database persistence.
*
*
* <p>
* Keys are produced in the format:
* </p>
*
*
* <p>
* Base64(creationTime + ":" + hex(pseudoRandomNumber) + ":" + extendedInformation + ":" +
* Sha512Hex(creationTime + ":" + hex(pseudoRandomNumber) + ":" + extendedInformation + ":" + serverSecret) )
* </p>
*
*
* <p>
* In the above, <code>creationTime</code>, <code>tokenKey</code> and <code>extendedInformation</code>
* are equal to that stored in {@link Token}. The <code>Sha512Hex</code> includes the same payload,
* plus a <code>serverSecret</code>.
* </p>
*
*
* <p>
* The <code>serverSecret</code> varies every millisecond. It relies on two static server-side secrets. The first
* is a password, and the second is a server integer. Both of these must remain the same for any issued keys
@ -39,7 +39,7 @@ import org.springframework.util.StringUtils;
* to the computed hash). Recall that framework features depending on token services should reject tokens
* that are relatively old in any event.
* </p>
*
*
* <p>
* A further consideration of this class is the requirement for cryptographically strong pseudo-random numbers.
* To this end, the use of {@link SecureRandomFactoryBean} is recommended to inject the property.
@ -48,7 +48,7 @@ import org.springframework.util.StringUtils;
* <p>
* This implementation uses UTF-8 encoding internally for string manipulation.
* </p>
*
*
* @author Ben Alex
*
*/
@ -57,7 +57,7 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
private String serverSecret;
private Integer serverInteger;
private SecureRandom secureRandom;
public Token allocateToken(String extendedInformation) {
Assert.notNull(extendedInformation, "Must provided non-null extendedInformation (but it can be empty)");
long creationTime = new Date().getTime();
@ -68,8 +68,8 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
// Compute key
String sha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
String keyPayload = content + ":" + sha512Hex;
String key = convertToString(Base64.encodeBase64(convertToBytes(keyPayload)));
String key = convertToString(Base64.encode(convertToBytes(keyPayload)));
return new DefaultToken(key, creationTime, extendedInformation);
}
@ -77,19 +77,19 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
if (key == null || "".equals(key)) {
return null;
}
String[] tokens = StringUtils.delimitedListToStringArray(convertToString(Base64.decodeBase64(convertToBytes(key))), ":");
String[] tokens = StringUtils.delimitedListToStringArray(convertToString(Base64.decode(convertToBytes(key))), ":");
Assert.isTrue(tokens.length >= 4, "Expected 4 or more tokens but found " + tokens.length);
long creationTime;
try {
creationTime = Long.decode(tokens[0]).longValue();
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("Expected number but found " + tokens[0]);
}
String serverSecret = computeServerSecretApplicableAt(creationTime);
String pseudoRandomNumber = tokens[1];
// Permit extendedInfo to itself contain ":" characters
StringBuffer extendedInfo = new StringBuffer();
for (int i = 2; i < tokens.length-1; i++) {
@ -98,17 +98,17 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
}
extendedInfo.append(tokens[i]);
}
String sha1Hex = tokens[tokens.length-1];
// Verification
String content = new Long(creationTime).toString() + ":" + pseudoRandomNumber + ":" + extendedInfo.toString();
String expectedSha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
Assert.isTrue(expectedSha512Hex.equals(sha1Hex), "Key verification failure");
return new DefaultToken(key, creationTime, extendedInfo.toString());
}
private byte[] convertToBytes(String input) {
try {
return input.getBytes("UTF-8");
@ -116,7 +116,7 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
throw new RuntimeException(e);
}
}
private String convertToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
@ -124,16 +124,16 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
throw new RuntimeException(e);
}
}
/**
* @return a pseduo random number (hex encoded)
*/
private String generatePseudoRandomNumber() {
byte[] randomizedBits = new byte[pseudoRandomNumberBits];
secureRandom.nextBytes(randomizedBits);
return new String(Hex.encodeHex(randomizedBits));
return new String(Hex.encode(randomizedBits));
}
private String computeServerSecretApplicableAt(long time) {
return serverSecret + ":" + new Long(time % serverInteger.intValue()).intValue();
}
@ -144,11 +144,11 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
public void setServerSecret(String serverSecret) {
this.serverSecret = serverSecret;
}
public void setSecureRandom(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
}
/**
* @param pseudoRandomNumberBits changes the number of bits issued (must be >= 0; defaults to 256)
*/

View File

@ -3,15 +3,15 @@ package org.springframework.security.core.token;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
import org.springframework.security.core.codec.Hex;
/**
* Provides SHA512 digest methods.
*
*
* <p>
* Based on Commons Codec, which does not presently provide SHA512 support.
* </p>
*
*
* @author Ben Alex
* @since 2.0.1
*
@ -43,7 +43,7 @@ public abstract class Sha512DigestUtils {
}
/**
* Calculates the SHA digest and returns the value as a
* Calculates the SHA digest and returns the value as a
* <code>byte[]</code>.
*
* @param data Data to digest
@ -54,7 +54,7 @@ public abstract class Sha512DigestUtils {
}
/**
* Calculates the SHA digest and returns the value as a
* Calculates the SHA digest and returns the value as a
* <code>byte[]</code>.
*
* @param data Data to digest
@ -71,7 +71,7 @@ public abstract class Sha512DigestUtils {
* @return SHA digest as a hex string
*/
public static String shaHex(byte[] data) {
return new String(Hex.encodeHex(sha(data)));
return new String(Hex.encode(sha(data)));
}
/**
@ -81,7 +81,7 @@ public abstract class Sha512DigestUtils {
* @return SHA digest as a hex string
*/
public static String shaHex(String data) {
return new String(Hex.encodeHex(sha(data)));
return new String(Hex.encode(sha(data)));
}
}

View File

@ -15,18 +15,15 @@
package org.springframework.security.remoting.httpinvoker;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import java.io.IOException;
import java.net.HttpURLConnection;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor;
import java.io.IOException;
import java.net.HttpURLConnection;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.context.SecurityContextHolder;
/**
@ -71,7 +68,7 @@ public class AuthenticationSimpleHttpInvokerRequestExecutor extends SimpleHttpIn
if ((auth != null) && (auth.getName() != null) && (auth.getCredentials() != null)) {
String base64 = auth.getName() + ":" + auth.getCredentials().toString();
con.setRequestProperty("Authorization", "Basic " + new String(Base64.encodeBase64(base64.getBytes())));
con.setRequestProperty("Authorization", "Basic " + new String(Base64.encode(base64.getBytes())));
if (logger.isDebugEnabled()) {
logger.debug("HttpInvocation now presenting via BASIC authentication SecurityContextHolder-derived: "

View File

@ -23,8 +23,8 @@ import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.springframework.core.NestedRuntimeException;
import org.springframework.security.core.codec.Base64;
import org.springframework.util.Assert;
/**
@ -102,7 +102,7 @@ public final class EncryptionUtils {
public static String encrypt(String key, String inputString) throws EncryptionException {
isValidKey(key);
final byte[] cipherText = cipher(key, stringToByteArray(inputString), Cipher.ENCRYPT_MODE);
return byteArrayToString(Base64.encodeBase64(cipherText));
return byteArrayToString(Base64.encode(cipherText));
}
/**
@ -115,7 +115,7 @@ public final class EncryptionUtils {
*/
public static byte[] encrypt(String key, byte[] inputBytes) throws EncryptionException {
isValidKey(key);
return Base64.encodeBase64(cipher(key, inputBytes, Cipher.ENCRYPT_MODE));
return Base64.encode(cipher(key, inputBytes, Cipher.ENCRYPT_MODE));
}
/**
@ -128,7 +128,7 @@ public final class EncryptionUtils {
*/
public static String decrypt(String key, String inputString) throws EncryptionException {
Assert.hasText(key, "A key is required to attempt decryption");
final byte[] cipherText = cipher(key, Base64.decodeBase64(stringToByteArray(inputString)), Cipher.DECRYPT_MODE);
final byte[] cipherText = cipher(key, Base64.decode(stringToByteArray(inputString)), Cipher.DECRYPT_MODE);
return byteArrayToString(cipherText);
}
@ -142,7 +142,7 @@ public final class EncryptionUtils {
*/
public static byte[] decrypt(String key, byte[] inputBytes) throws EncryptionException {
Assert.hasText(key, "A key is required to attempt decryption");
return cipher(key, Base64.decodeBase64(inputBytes), Cipher.DECRYPT_MODE);
return cipher(key, Base64.decode(inputBytes), Cipher.DECRYPT_MODE);
}
private static void isValidKey(String key) {

View File

@ -702,11 +702,6 @@
<version>1.1.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>

View File

@ -30,6 +30,12 @@
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>

View File

@ -4,6 +4,8 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
@ -16,11 +18,14 @@ import org.springframework.util.StringUtils;
* @since 3.0
*/
public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
private FilterInvocation filterInvocation;
//private FilterInvocation filterInvocation;
/** Allows direct access to the request object */
public final HttpServletRequest request;
public WebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
super(a);
this.filterInvocation = fi;
//this.filterInvocation = fi;
this.request = fi.getRequest();
}
/**
@ -39,7 +44,7 @@ public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
}
InetAddress requiredAddress = parseAddress(ipAddress);
InetAddress remoteAddress = parseAddress(filterInvocation.getHttpRequest().getRemoteAddr());
InetAddress remoteAddress = parseAddress(request.getRemoteAddr());
if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
throw new IllegalArgumentException("IP Address in expression must be the same type as " +

View File

@ -1,6 +1,9 @@
package org.springframework.security.web.authentication.rememberme;
import org.apache.commons.codec.binary.Base64;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
@ -11,6 +14,7 @@ import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.core.userdetails.UserDetailsService;
@ -21,10 +25,6 @@ import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Base class for RememberMeServices implementations.
*
@ -160,11 +160,11 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
cookieValue = cookieValue + "=";
}
if (!Base64.isArrayByteBase64(cookieValue.getBytes())) {
if (!Base64.isBase64(cookieValue.getBytes())) {
throw new InvalidCookieException( "Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
}
String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
String cookieAsPlainText = new String(Base64.decode(cookieValue.getBytes()));
return StringUtils.delimitedListToStringArray(cookieAsPlainText, DELIMITER);
}
@ -187,7 +187,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
String value = sb.toString();
sb = new StringBuffer(new String(Base64.encodeBase64(value.getBytes())));
sb = new StringBuffer(new String(Base64.encode(value.getBytes())));
while (sb.charAt(sb.length() - 1) == '=') {
sb.deleteCharAt(sb.length() - 1);

View File

@ -7,9 +7,9 @@ import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.util.Assert;
@ -151,13 +151,13 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
protected String generateSeriesData() {
byte[] newSeries = new byte[seriesLength];
random.nextBytes(newSeries);
return new String(Base64.encodeBase64(newSeries));
return new String(Base64.encode(newSeries));
}
protected String generateTokenData() {
byte[] newToken = new byte[tokenLength];
random.nextBytes(newToken);
return new String(Base64.encodeBase64(newToken));
return new String(Base64.encode(newToken));
}
private void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {

View File

@ -16,13 +16,15 @@
package org.springframework.security.web.authentication.rememberme;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.codec.Hex;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Date;
@ -129,7 +131,15 @@ public class TokenBasedRememberMeServices extends AbstractRememberMeServices {
* MD5 ("username:tokenExpiryTime:password:key")
*/
protected String makeTokenSignature(long tokenExpiryTime, String username, String password) {
return DigestUtils.md5Hex(username + ":" + tokenExpiryTime + ":" + password + ":" + getKey());
String data = username + ":" + tokenExpiryTime + ":" + password + ":" + getKey();
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No MD5 algorithm available!");
}
return new String(Hex.encode(digest.digest(data.getBytes())));
}
protected boolean isTokenExpired(long tokenExpiryTime) {

View File

@ -24,13 +24,13 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.NullRememberMeServices;
@ -117,7 +117,7 @@ public class BasicAuthenticationFilter extends GenericFilterBean {
if ((header != null) && header.startsWith("Basic ")) {
byte[] base64Token = header.substring(6).getBytes("UTF-8");
String token = new String(Base64.decodeBase64(base64Token), getCredentialsCharset(request));
String token = new String(Base64.decode(base64Token), getCredentialsCharset(request));
String username = "";
String password = "";

View File

@ -1,27 +1,28 @@
package org.springframework.security.web.authentication.www;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.security.core.codec.Hex;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
abstract class DigestAuthUtils {
final class DigestAuthUtils {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
public final static String encodePasswordInA1Format(String username, String realm, String password) {
static String encodePasswordInA1Format(String username, String realm, String password) {
String a1 = username + ":" + realm + ":" + password;
String a1Md5 = new String(DigestUtils.md5Hex(a1));
String a1Md5 = md5Hex(a1);
return a1Md5;
}
final static String[] splitIgnoringQuotes(String str, char separatorChar) {
static String[] splitIgnoringQuotes(String str, char separatorChar) {
if (str == null) {
return null;
}
@ -87,12 +88,12 @@ abstract class DigestAuthUtils {
* @return the MD5 of the digest authentication response, encoded in hex
* @throws IllegalArgumentException if the supplied qop value is unsupported.
*/
final static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
throws IllegalArgumentException {
String a1Md5 = null;
String a2 = httpMethod + ":" + uri;
String a2Md5 = new String(DigestUtils.md5Hex(a2));
String a2Md5 = md5Hex(a2);
if (passwordAlreadyEncoded) {
a1Md5 = password;
@ -112,7 +113,7 @@ abstract class DigestAuthUtils {
throw new IllegalArgumentException("This method does not support a qop: '" + qop + "'");
}
String digestMd5 = new String(DigestUtils.md5Hex(digest));
String digestMd5 = new String(md5Hex(digest));
return digestMd5;
}
@ -130,7 +131,7 @@ abstract class DigestAuthUtils {
* @return a <code>Map</code> representing the array contents, or <code>null</code> if the array to process was
* null or empty
*/
final static Map<String, String> splitEachArrayElementAndCreateMap(String[] array, String delimiter, String removeCharacters) {
static Map<String, String> splitEachArrayElementAndCreateMap(String[] array, String delimiter, String removeCharacters) {
if ((array == null) || (array.length == 0)) {
return null;
}
@ -169,7 +170,7 @@ abstract class DigestAuthUtils {
* (neither element includes the delimiter)
* @throws IllegalArgumentException if an argument was invalid
*/
final static String[] split(String toSplit, String delimiter) {
static String[] split(String toSplit, String delimiter) {
Assert.hasLength(toSplit, "Cannot split a null or empty string");
Assert.hasLength(delimiter, "Cannot use a null or empty delimiter to split a string");
@ -188,4 +189,15 @@ abstract class DigestAuthUtils {
return new String[]{beforeDelimiter, afterDelimiter};
}
static String md5Hex(String data) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No MD5 algorithm available!");
}
return new String(Hex.encode(digest.digest(data.getBytes())));
}
}

View File

@ -21,14 +21,13 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.web.AuthenticationEntryPoint;
/**
@ -82,9 +81,9 @@ public class DigestAuthenticationEntryPoint implements AuthenticationEntryPoint,
// format of nonce is:
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
long expiryTime = System.currentTimeMillis() + (nonceValiditySeconds * 1000);
String signatureValue = new String(DigestUtils.md5Hex(expiryTime + ":" + key));
String signatureValue = new String(DigestAuthUtils.md5Hex(expiryTime + ":" + key));
String nonceValue = expiryTime + ":" + signatureValue;
String nonceValueBase64 = new String(Base64.encodeBase64(nonceValue.getBytes()));
String nonceValueBase64 = new String(Base64.encode(nonceValue.getBytes()));
// qop is quality of protection, as defined by RFC 2617.
// we do not use opaque due to IE violation of RFC 2617 in not

View File

@ -25,8 +25,6 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
@ -38,6 +36,7 @@ import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
@ -96,7 +95,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
@Override
@Override
public void afterPropertiesSet() {
Assert.notNull(userDetailsService, "A UserDetailsService is required");
Assert.notNull(authenticationEntryPoint, "A DigestAuthenticationEntryPoint is required");
@ -168,7 +167,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
}
// Check nonce was a Base64 encoded (as sent by DigestAuthenticationEntryPoint)
if (!Base64.isArrayByteBase64(nonce.getBytes())) {
if (!Base64.isBase64(nonce.getBytes())) {
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.nonceEncoding",
new Object[]{nonce}, "Nonce is not encoded in Base64; received nonce {0}")));
@ -179,7 +178,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
// Decode nonce from Base64
// format of nonce is:
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
String nonceAsPlainText = new String(Base64.decodeBase64(nonce.getBytes()));
String nonceAsPlainText = new String(Base64.decode(nonce.getBytes()));
String[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, ":");
if (nonceTokens.length != 2) {
@ -205,7 +204,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
}
// Check signature of nonce matches this expiry time
String expectedNonceSignature = DigestUtils.md5Hex(nonceExpiryTime + ":"
String expectedNonceSignature = DigestAuthUtils.md5Hex(nonceExpiryTime + ":"
+ this.getAuthenticationEntryPoint().getKey());
if (!expectedNonceSignature.equals(nonceTokens[1])) {
@ -305,11 +304,11 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
UsernamePasswordAuthenticationToken authRequest;
if (createAuthenticatedToken) {
authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
}
else
{
authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword());
authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword());
}
authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
@ -367,23 +366,23 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
/**
/**
* If you set this property, the Authentication object, which is
* created after the successful digest authentication will be marked
* as <b>authenticated</b> and filled with the authorities loaded by
* as <b>authenticated</b> and filled with the authorities loaded by
* the UserDetailsService. It therefore will not be re-authenticated
* by your AuthenticationProvider. This means, that only the password
* of the user is checked, but not the flags like isEnabled() or
* isAccountNonExpired(). You will save some time by enabling this flag,
* isAccountNonExpired(). You will save some time by enabling this flag,
* as otherwise your UserDetailsService will be called twice. A more secure
* option would be to introduce a cache around your UserDetailsService, but
* if you don't use these flags, you can also safely enable this option.
*
*
* @param createAuthenticatedToken default is false
*/
public void setCreateAuthenticatedToken(boolean createAuthenticatedToken) {
this.createAuthenticatedToken = createAuthenticatedToken;
}
this.createAuthenticatedToken = createAuthenticatedToken;
}
}