1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-14 07:34:50 +02:00
Smack/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
Florian Schmaus 403ecff2b2 Add SCRAM-SHA1 support
Thanks to Stefan Karlsson for helping with the implementation.

Also add SASLMechanism.checkIfSuccessfulOrThrow(), to increase the
security by verifying the mechanisms state at the end of SASL
authentication.

SASLMechanism now has a SASLPrep StringTransformer.

Refactor SHA1 functions out of StringUtils into SHA1 utility class.

Add MAC utility class.

Make DummyConnection getSentpacket() methods use generics to make unit
testing SCRAM-SHA1 easier.

Fixes SMACK-398
2014-10-21 15:03:48 +02:00

261 lines
8.7 KiB
Java

/**
*
* Copyright 2003-2007 Jive Software.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.Random;
/**
* A collection of utility methods for String objects.
*/
public class StringUtils {
public static final String MD5 = "MD5";
public static final String SHA1 = "SHA-1";
public static final String UTF8 = "UTF-8";
public static final String USASCII = "US-ASCII";
public static final String QUOTE_ENCODE = """;
public static final String APOS_ENCODE = "'";
public static final String AMP_ENCODE = "&";
public static final String LT_ENCODE = "<";
public static final String GT_ENCODE = ">";
public static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
/**
* Escapes all necessary characters in the String so that it can be used
* in an XML doc.
*
* @param string the string to escape.
* @return the string with appropriate characters escaped.
*/
public static CharSequence escapeForXML(final String string) {
if (string == null) {
return null;
}
final char[] input = string.toCharArray();
final int len = input.length;
final StringBuilder out = new StringBuilder((int)(len*1.3));
CharSequence toAppend;
char ch;
int last = 0;
int i = 0;
while (i < len) {
toAppend = null;
ch = input[i];
switch(ch) {
case '<':
toAppend = LT_ENCODE;
break;
case '>':
toAppend = GT_ENCODE;
break;
case '&':
toAppend = AMP_ENCODE;
break;
case '"':
toAppend = QUOTE_ENCODE;
break;
case '\'':
toAppend = APOS_ENCODE;
break;
default:
break;
}
if (toAppend != null) {
if (i > last) {
out.append(input, last, i - last);
}
out.append(toAppend);
last = ++i;
} else {
i++;
}
}
if (last == 0) {
return string;
}
if (i > last) {
out.append(input, last, i - last);
}
return out;
}
/**
* Hashes a String using the SHA-1 algorithm and returns the result as a
* String of hexadecimal numbers. This method is synchronized to avoid
* excessive MessageDigest object creation. If calling this method becomes
* a bottleneck in your code, you may wish to maintain a pool of
* MessageDigest objects instead of using this method.
* <p>
* A hash is a one-way function -- that is, given an
* input, an output is easily computed. However, given the output, the
* input is almost impossible to compute. This is useful for passwords
* since we can store the hash and a hacker will then have a very hard time
* determining the original password.
*
* @param data the String to compute the hash of.
* @return a hashed version of the passed-in String
* @deprecated use {@link org.jivesoftware.smack.util.SHA1#hex(String)} instead.
*/
@Deprecated
public synchronized static String hash(String data) {
return org.jivesoftware.smack.util.SHA1.hex(data);
}
/**
* Encodes an array of bytes as String representation of hexadecimal.
*
* @param bytes an array of bytes to convert to a hex string.
* @return generated hex string.
*/
public static String encodeHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_CHARS[v >>> 4];
hexChars[j * 2 + 1] = HEX_CHARS[v & 0x0F];
}
return new String(hexChars);
}
public static byte[] toBytes(String string) {
try {
return string.getBytes(StringUtils.UTF8);
}
catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 encoding not supported by platform", e);
}
}
/**
* Pseudo-random number generator object for use with randomString().
* The Random class is not considered to be cryptographically secure, so
* only use these random Strings for low to medium security applications.
*/
private static Random randGen = new Random();
/**
* Array of numbers and letters of mixed case. Numbers appear in the list
* twice so that there is a more equal chance that a number will be picked.
* We can use the array to get a random number or letter by picking a random
* array index.
*/
private static char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" +
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
/**
* Returns a random String of numbers and letters (lower and upper case)
* of the specified length. The method uses the Random class that is
* built-in to Java which is suitable for low to medium grade security uses.
* This means that the output is only pseudo random, i.e., each number is
* mathematically generated so is not truly random.<p>
*
* The specified length must be at least one. If not, the method will return
* null.
*
* @param length the desired length of the random String to return.
* @return a random String of numbers and letters of the specified length.
*/
public static String randomString(int length) {
if (length < 1) {
return null;
}
// Create a char buffer to put random letters and numbers in.
char [] randBuffer = new char[length];
for (int i=0; i<randBuffer.length; i++) {
randBuffer[i] = numbersAndLetters[randGen.nextInt(numbersAndLetters.length)];
}
return new String(randBuffer);
}
/**
* Returns true if CharSequence is not null and is not empty, false otherwise
* Examples:
* isNotEmpty(null) - false
* isNotEmpty("") - false
* isNotEmpty(" ") - true
* isNotEmpty("empty") - true
*
* @param cs checked CharSequence
* @return true if string is not null and is not empty, false otherwise
*/
public static boolean isNotEmpty(CharSequence cs) {
return !isNullOrEmpty(cs);
}
/**
* Returns true if the given CharSequence is null or empty.
*
* @param cs
* @return true if the given CharSequence is null or empty
*/
public static boolean isNullOrEmpty(CharSequence cs) {
return cs == null || isEmpty(cs);
}
/**
* Returns true if the given CharSequence is empty
*
* @param cs
* @return true if the given CharSequence is empty
*/
public static boolean isEmpty(CharSequence cs) {
return cs.length() == 0;
}
public static String collectionToString(Collection<String> collection) {
StringBuilder sb = new StringBuilder();
for (String s : collection) {
sb.append(s);
sb.append(" ");
}
String res = sb.toString();
// Remove the trailing whitespace
res = res.substring(0, res.length() - 1);
return res;
}
public static String returnIfNotEmptyTrimmed(String string) {
if (string == null)
return null;
String trimmedString = string.trim();
if (trimmedString.length() > 0) {
return trimmedString;
} else {
return null;
}
}
public static boolean nullSafeCharSequenceEquals(CharSequence csOne, CharSequence csTwo) {
return nullSafeCharSequenceComperator(csOne, csTwo) == 0;
}
public static int nullSafeCharSequenceComperator(CharSequence csOne, CharSequence csTwo) {
if (csOne == null ^ csTwo == null) {
return (csOne == null) ? -1 : 1;
}
if (csOne == null && csTwo == null) {
return 0;
}
return csOne.toString().compareTo(csTwo.toString());
}
}