All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.security.web.authentication.www.DigestAuthUtils Maven / Gradle / Ivy

There is a newer version: 6.2.3
Show newest version
/*
 * Copyright 2002-2016 the original author or authors.
 *
 * 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.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.springframework.security.crypto.codec.Hex;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

final class DigestAuthUtils {

	private static final String[] EMPTY_STRING_ARRAY = new String[0];

	static String encodePasswordInA1Format(String username, String realm, String password) {
		String a1 = username + ":" + realm + ":" + password;

		return md5Hex(a1);
	}

	static String[] splitIgnoringQuotes(String str, char separatorChar) {
		if (str == null) {
			return null;
		}

		int len = str.length();

		if (len == 0) {
			return EMPTY_STRING_ARRAY;
		}

		List list = new ArrayList<>();
		int i = 0;
		int start = 0;
		boolean match = false;

		while (i < len) {
			if (str.charAt(i) == '"') {
				i++;
				while (i < len) {
					if (str.charAt(i) == '"') {
						i++;
						break;
					}
					i++;
				}
				match = true;
				continue;
			}
			if (str.charAt(i) == separatorChar) {
				if (match) {
					list.add(str.substring(start, i));
					match = false;
				}
				start = ++i;
				continue;
			}
			match = true;
			i++;
		}
		if (match) {
			list.add(str.substring(start, i));
		}

		return list.toArray(new String[list.size()]);
	}

	/**
	 * Computes the response portion of a Digest authentication header. Both
	 * the server and user agent should compute the response independently.
	 * Provided as a static method to simplify the coding of user agents.
	 *
	 * @param passwordAlreadyEncoded true if the password argument is already encoded in
	 * the correct format. False if it is plain text.
	 * @param username the user's login name.
	 * @param realm the name of the realm.
	 * @param password the user's password in plaintext or ready-encoded.
	 * @param httpMethod the HTTP request method (GET, POST etc.)
	 * @param uri the request URI.
	 * @param qop the qop directive, or null if not set.
	 * @param nonce the nonce supplied by the server
	 * @param nc the "nonce-count" as defined in RFC 2617.
	 * @param cnonce opaque string supplied by the client when qop is set.
	 * @return the MD5 of the digest authentication response, encoded in hex
	 * @throws IllegalArgumentException if the supplied qop value is unsupported.
	 */
	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;
		String a2 = httpMethod + ":" + uri;
		String a2Md5 = md5Hex(a2);

		if (passwordAlreadyEncoded) {
			a1Md5 = password;
		}
		else {
			a1Md5 = DigestAuthUtils.encodePasswordInA1Format(username, realm, password);
		}

		String digest;

		if (qop == null) {
			// as per RFC 2069 compliant clients (also reaffirmed by RFC 2617)
			digest = a1Md5 + ":" + nonce + ":" + a2Md5;
		}
		else if ("auth".equals(qop)) {
			// As per RFC 2617 compliant clients
			digest = a1Md5 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":"
					+ a2Md5;
		}
		else {
			throw new IllegalArgumentException("This method does not support a qop: '"
					+ qop + "'");
		}

		return md5Hex(digest);
	}

	/**
	 * Takes an array of Strings, and for each element removes any instances
	 * of removeCharacter, and splits the element based on the
	 * delimiter. A Map is then generated, with the left of the
	 * delimiter providing the key, and the right of the delimiter providing the value.
	 * 

* Will trim both the key and value before adding to the Map. *

* * @param array the array to process * @param delimiter to split each element using (typically the equals symbol) * @param removeCharacters one or more characters to remove from each element prior to * attempting the split operation (typically the quotation mark symbol) or * null if no removal should occur * @return a Map representing the array contents, or null if * the array to process was null or empty */ static Map splitEachArrayElementAndCreateMap(String[] array, String delimiter, String removeCharacters) { if ((array == null) || (array.length == 0)) { return null; } Map map = new HashMap<>(); for (String s : array) { String postRemove; if (removeCharacters == null) { postRemove = s; } else { postRemove = StringUtils.replace(s, removeCharacters, ""); } String[] splitThisArrayElement = split(postRemove, delimiter); if (splitThisArrayElement == null) { continue; } map.put(splitThisArrayElement[0].trim(), splitThisArrayElement[1].trim()); } return map; } /** * Splits a String at the first instance of the delimiter. *

* Does not include the delimiter in the response. *

* * @param toSplit the string to split * @param delimiter to split the string up with * @return a two element array with index 0 being before the delimiter, and index 1 * being after the delimiter (neither element includes the delimiter) * @throws IllegalArgumentException if an argument was invalid */ 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"); if (delimiter.length() != 1) { throw new IllegalArgumentException( "Delimiter can only be one character in length"); } int offset = toSplit.indexOf(delimiter); if (offset < 0) { return null; } String beforeDelimiter = toSplit.substring(0, offset); String afterDelimiter = toSplit.substring(offset + 1); 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()))); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy