diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth')
16 files changed, 3667 insertions, 0 deletions
diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/AuthSchemeBase.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/AuthSchemeBase.java new file mode 100644 index 000000000..e1b98db9e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/AuthSchemeBase.java @@ -0,0 +1,169 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ChallengeState; +import ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract authentication scheme class that serves as a basis + * for all authentication schemes supported by HttpClient. This class + * defines the generic way of parsing an authentication challenge. It + * does not make any assumptions regarding the format of the challenge + * nor does it impose any specific way of responding to that challenge. + * + * + * @since 4.0 + */ +@NotThreadSafe +public abstract class AuthSchemeBase implements ContextAwareAuthScheme { + + private ChallengeState challengeState; + + /** + * Creates an instance of <tt>AuthSchemeBase</tt> with the given challenge + * state. + * + * @since 4.2 + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public AuthSchemeBase(final ChallengeState challengeState) { + super(); + this.challengeState = challengeState; + } + + public AuthSchemeBase() { + super(); + } + + /** + * Processes the given challenge token. Some authentication schemes + * may involve multiple challenge-response exchanges. Such schemes must be able + * to maintain the state information when dealing with sequential challenges + * + * @param header the challenge header + * + * @throws MalformedChallengeException is thrown if the authentication challenge + * is malformed + */ + public void processChallenge(final Header header) throws MalformedChallengeException { + Args.notNull(header, "Header"); + final String authheader = header.getName(); + if (authheader.equalsIgnoreCase(AUTH.WWW_AUTH)) { + this.challengeState = ChallengeState.TARGET; + } else if (authheader.equalsIgnoreCase(AUTH.PROXY_AUTH)) { + this.challengeState = ChallengeState.PROXY; + } else { + throw new MalformedChallengeException("Unexpected header name: " + authheader); + } + + final CharArrayBuffer buffer; + int pos; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + pos = ((FormattedHeader) header).getValuePos(); + } else { + final String s = header.getValue(); + if (s == null) { + throw new MalformedChallengeException("Header value is null"); + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + pos = 0; + } + while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + final int beginIndex = pos; + while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + final int endIndex = pos; + final String s = buffer.substring(beginIndex, endIndex); + if (!s.equalsIgnoreCase(getSchemeName())) { + throw new MalformedChallengeException("Invalid scheme identifier: " + s); + } + + parseChallenge(buffer, pos, buffer.length()); + } + + + @SuppressWarnings("deprecation") + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + return authenticate(credentials, request); + } + + protected abstract void parseChallenge( + CharArrayBuffer buffer, int beginIndex, int endIndex) throws MalformedChallengeException; + + /** + * Returns <code>true</code> if authenticating against a proxy, <code>false</code> + * otherwise. + */ + public boolean isProxy() { + return this.challengeState != null && this.challengeState == ChallengeState.PROXY; + } + + /** + * Returns {@link ChallengeState} value or <code>null</code> if unchallenged. + * + * @since 4.2 + */ + public ChallengeState getChallengeState() { + return this.challengeState; + } + + @Override + public String toString() { + final String name = getSchemeName(); + if (name != null) { + return name.toUpperCase(Locale.ENGLISH); + } else { + return super.toString(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicScheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicScheme.java new file mode 100644 index 000000000..c58b5e582 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicScheme.java @@ -0,0 +1,219 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.nio.charset.Charset; + +import org.mozilla.apache.commons.codec.binary.Base64; +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ChallengeState; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; +import ch.boye.httpclientandroidlib.util.EncodingUtils; + +/** + * Basic authentication scheme as defined in RFC 2617. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicScheme extends RFC2617Scheme { + +/* Base64 instance removed by HttpClient for Android script. */ + /** Whether the basic authentication process is complete */ + private boolean complete; + + /** + * @since 4.3 + */ + public BasicScheme(final Charset credentialsCharset) { + super(credentialsCharset); +/* Base64 instance removed by HttpClient for Android script. */ + this.complete = false; + } + + /** + * Creates an instance of <tt>BasicScheme</tt> with the given challenge + * state. + * + * @since 4.2 + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public BasicScheme(final ChallengeState challengeState) { + super(challengeState); +/* Base64 instance removed by HttpClient for Android script. */ + } + + public BasicScheme() { + this(Consts.ASCII); + } + + /** + * Returns textual designation of the basic authentication scheme. + * + * @return <code>basic</code> + */ + public String getSchemeName() { + return "basic"; + } + + /** + * Processes the Basic challenge. + * + * @param header the challenge header + * + * @throws MalformedChallengeException is thrown if the authentication challenge + * is malformed + */ + @Override + public void processChallenge( + final Header header) throws MalformedChallengeException { + super.processChallenge(header); + this.complete = true; + } + + /** + * Tests if the Basic authentication process has been completed. + * + * @return <tt>true</tt> if Basic authorization has been processed, + * <tt>false</tt> otherwise. + */ + public boolean isComplete() { + return this.complete; + } + + /** + * Returns <tt>false</tt>. Basic authentication scheme is request based. + * + * @return <tt>false</tt>. + */ + public boolean isConnectionBased() { + return false; + } + + /** + * @deprecated (4.2) Use {@link ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme#authenticate( + * Credentials, HttpRequest, ch.boye.httpclientandroidlib.protocol.HttpContext)} + */ + @Deprecated + public Header authenticate( + final Credentials credentials, final HttpRequest request) throws AuthenticationException { + return authenticate(credentials, request, new BasicHttpContext()); + } + + /** + * Produces basic authorization header for the given set of {@link Credentials}. + * + * @param credentials The set of credentials to be used for authentication + * @param request The request being authenticated + * @throws ch.boye.httpclientandroidlib.auth.InvalidCredentialsException if authentication + * credentials are not valid or not applicable for this authentication scheme + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + * + * @return a basic authorization string + */ + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + + Args.notNull(credentials, "Credentials"); + Args.notNull(request, "HTTP request"); + final StringBuilder tmp = new StringBuilder(); + tmp.append(credentials.getUserPrincipal().getName()); + tmp.append(":"); + tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword()); + + final byte[] base64password = Base64.encodeBase64( + EncodingUtils.getBytes(tmp.toString(), getCredentialsCharset(request))); + + final CharArrayBuffer buffer = new CharArrayBuffer(32); + if (isProxy()) { + buffer.append(AUTH.PROXY_AUTH_RESP); + } else { + buffer.append(AUTH.WWW_AUTH_RESP); + } + buffer.append(": Basic "); + buffer.append(base64password, 0, base64password.length); + + return new BufferedHeader(buffer); + } + + /** + * Returns a basic <tt>Authorization</tt> header value for the given + * {@link Credentials} and charset. + * + * @param credentials The credentials to encode. + * @param charset The charset to use for encoding the credentials + * + * @return a basic authorization header + * + * @deprecated (4.3) use {@link #authenticate(Credentials, HttpRequest, HttpContext)}. + */ + @Deprecated + public static Header authenticate( + final Credentials credentials, + final String charset, + final boolean proxy) { + Args.notNull(credentials, "Credentials"); + Args.notNull(charset, "charset"); + + final StringBuilder tmp = new StringBuilder(); + tmp.append(credentials.getUserPrincipal().getName()); + tmp.append(":"); + tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword()); + + final byte[] base64password = Base64.encodeBase64( + EncodingUtils.getBytes(tmp.toString(), charset)); + + final CharArrayBuffer buffer = new CharArrayBuffer(32); + if (proxy) { + buffer.append(AUTH.PROXY_AUTH_RESP); + } else { + buffer.append(AUTH.WWW_AUTH_RESP); + } + buffer.append(": Basic "); + buffer.append(base64password, 0, base64password.length); + + return new BufferedHeader(buffer); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicSchemeFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicSchemeFactory.java new file mode 100644 index 000000000..c4eb8c6be --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicSchemeFactory.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package ch.boye.httpclientandroidlib.impl.auth; + +import java.nio.charset.Charset; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthSchemeFactory; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link AuthSchemeProvider} implementation that creates and initializes + * {@link BasicScheme} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class BasicSchemeFactory implements AuthSchemeFactory, AuthSchemeProvider { + + private final Charset charset; + + /** + * @since 4.3 + */ + public BasicSchemeFactory(final Charset charset) { + super(); + this.charset = charset; + } + + public BasicSchemeFactory() { + this(null); + } + + public AuthScheme newInstance(final HttpParams params) { + return new BasicScheme(); + } + + public AuthScheme create(final HttpContext context) { + return new BasicScheme(this.charset); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestScheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestScheme.java new file mode 100644 index 000000000..542f6236b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestScheme.java @@ -0,0 +1,489 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Formatter; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.StringTokenizer; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ChallengeState; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.message.BasicHeaderValueFormatter; +import ch.boye.httpclientandroidlib.message.BasicNameValuePair; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; +import ch.boye.httpclientandroidlib.util.EncodingUtils; + +/** + * Digest authentication scheme as defined in RFC 2617. + * Both MD5 (default) and MD5-sess are supported. + * Currently only qop=auth or no qop is supported. qop=auth-int + * is unsupported. If auth and auth-int are provided, auth is + * used. + * <p/> + * Since the digest username is included as clear text in the generated + * Authentication header, the charset of the username must be compatible + * with the HTTP element charset used by the connection. + * + * @since 4.0 + */ +@NotThreadSafe +public class DigestScheme extends RFC2617Scheme { + + /** + * Hexa values used when creating 32 character long digest in HTTP DigestScheme + * in case of authentication. + * + * @see #encode(byte[]) + */ + private static final char[] HEXADECIMAL = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', + 'e', 'f' + }; + + /** Whether the digest authentication process is complete */ + private boolean complete; + + private static final int QOP_UNKNOWN = -1; + private static final int QOP_MISSING = 0; + private static final int QOP_AUTH_INT = 1; + private static final int QOP_AUTH = 2; + + private String lastNonce; + private long nounceCount; + private String cnonce; + private String a1; + private String a2; + + /** + * @since 4.3 + */ + public DigestScheme(final Charset credentialsCharset) { + super(credentialsCharset); + this.complete = false; + } + + /** + * Creates an instance of <tt>DigestScheme</tt> with the given challenge + * state. + * + * @since 4.2 + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public DigestScheme(final ChallengeState challengeState) { + super(challengeState); + } + + public DigestScheme() { + this(Consts.ASCII); + } + + /** + * Processes the Digest challenge. + * + * @param header the challenge header + * + * @throws MalformedChallengeException is thrown if the authentication challenge + * is malformed + */ + @Override + public void processChallenge( + final Header header) throws MalformedChallengeException { + super.processChallenge(header); + this.complete = true; + } + + /** + * Tests if the Digest authentication process has been completed. + * + * @return <tt>true</tt> if Digest authorization has been processed, + * <tt>false</tt> otherwise. + */ + public boolean isComplete() { + final String s = getParameter("stale"); + if ("true".equalsIgnoreCase(s)) { + return false; + } else { + return this.complete; + } + } + + /** + * Returns textual designation of the digest authentication scheme. + * + * @return <code>digest</code> + */ + public String getSchemeName() { + return "digest"; + } + + /** + * Returns <tt>false</tt>. Digest authentication scheme is request based. + * + * @return <tt>false</tt>. + */ + public boolean isConnectionBased() { + return false; + } + + public void overrideParamter(final String name, final String value) { + getParameters().put(name, value); + } + + /** + * @deprecated (4.2) Use {@link ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme#authenticate( + * Credentials, HttpRequest, ch.boye.httpclientandroidlib.protocol.HttpContext)} + */ + @Deprecated + public Header authenticate( + final Credentials credentials, final HttpRequest request) throws AuthenticationException { + return authenticate(credentials, request, new BasicHttpContext()); + } + + /** + * Produces a digest authorization string for the given set of + * {@link Credentials}, method name and URI. + * + * @param credentials A set of credentials to be used for athentication + * @param request The request being authenticated + * + * @throws ch.boye.httpclientandroidlib.auth.InvalidCredentialsException if authentication credentials + * are not valid or not applicable for this authentication scheme + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + * + * @return a digest authorization string + */ + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + + Args.notNull(credentials, "Credentials"); + Args.notNull(request, "HTTP request"); + if (getParameter("realm") == null) { + throw new AuthenticationException("missing realm in challenge"); + } + if (getParameter("nonce") == null) { + throw new AuthenticationException("missing nonce in challenge"); + } + // Add method name and request-URI to the parameter map + getParameters().put("methodname", request.getRequestLine().getMethod()); + getParameters().put("uri", request.getRequestLine().getUri()); + final String charset = getParameter("charset"); + if (charset == null) { + getParameters().put("charset", getCredentialsCharset(request)); + } + return createDigestHeader(credentials, request); + } + + private static MessageDigest createMessageDigest( + final String digAlg) throws UnsupportedDigestAlgorithmException { + try { + return MessageDigest.getInstance(digAlg); + } catch (final Exception e) { + throw new UnsupportedDigestAlgorithmException( + "Unsupported algorithm in HTTP Digest authentication: " + + digAlg); + } + } + + /** + * Creates digest-response header as defined in RFC2617. + * + * @param credentials User credentials + * + * @return The digest-response as String. + */ + private Header createDigestHeader( + final Credentials credentials, + final HttpRequest request) throws AuthenticationException { + final String uri = getParameter("uri"); + final String realm = getParameter("realm"); + final String nonce = getParameter("nonce"); + final String opaque = getParameter("opaque"); + final String method = getParameter("methodname"); + String algorithm = getParameter("algorithm"); + // If an algorithm is not specified, default to MD5. + if (algorithm == null) { + algorithm = "MD5"; + } + + final Set<String> qopset = new HashSet<String>(8); + int qop = QOP_UNKNOWN; + final String qoplist = getParameter("qop"); + if (qoplist != null) { + final StringTokenizer tok = new StringTokenizer(qoplist, ","); + while (tok.hasMoreTokens()) { + final String variant = tok.nextToken().trim(); + qopset.add(variant.toLowerCase(Locale.ENGLISH)); + } + if (request instanceof HttpEntityEnclosingRequest && qopset.contains("auth-int")) { + qop = QOP_AUTH_INT; + } else if (qopset.contains("auth")) { + qop = QOP_AUTH; + } + } else { + qop = QOP_MISSING; + } + + if (qop == QOP_UNKNOWN) { + throw new AuthenticationException("None of the qop methods is supported: " + qoplist); + } + + String charset = getParameter("charset"); + if (charset == null) { + charset = "ISO-8859-1"; + } + + String digAlg = algorithm; + if (digAlg.equalsIgnoreCase("MD5-sess")) { + digAlg = "MD5"; + } + + final MessageDigest digester; + try { + digester = createMessageDigest(digAlg); + } catch (final UnsupportedDigestAlgorithmException ex) { + throw new AuthenticationException("Unsuppported digest algorithm: " + digAlg); + } + + final String uname = credentials.getUserPrincipal().getName(); + final String pwd = credentials.getPassword(); + + if (nonce.equals(this.lastNonce)) { + nounceCount++; + } else { + nounceCount = 1; + cnonce = null; + lastNonce = nonce; + } + final StringBuilder sb = new StringBuilder(256); + final Formatter formatter = new Formatter(sb, Locale.US); + formatter.format("%08x", nounceCount); + formatter.close(); + final String nc = sb.toString(); + + if (cnonce == null) { + cnonce = createCnonce(); + } + + a1 = null; + a2 = null; + // 3.2.2.2: Calculating digest + if (algorithm.equalsIgnoreCase("MD5-sess")) { + // H( unq(username-value) ":" unq(realm-value) ":" passwd ) + // ":" unq(nonce-value) + // ":" unq(cnonce-value) + + // calculated one per session + sb.setLength(0); + sb.append(uname).append(':').append(realm).append(':').append(pwd); + final String checksum = encode(digester.digest(EncodingUtils.getBytes(sb.toString(), charset))); + sb.setLength(0); + sb.append(checksum).append(':').append(nonce).append(':').append(cnonce); + a1 = sb.toString(); + } else { + // unq(username-value) ":" unq(realm-value) ":" passwd + sb.setLength(0); + sb.append(uname).append(':').append(realm).append(':').append(pwd); + a1 = sb.toString(); + } + + final String hasha1 = encode(digester.digest(EncodingUtils.getBytes(a1, charset))); + + if (qop == QOP_AUTH) { + // Method ":" digest-uri-value + a2 = method + ':' + uri; + } else if (qop == QOP_AUTH_INT) { + // Method ":" digest-uri-value ":" H(entity-body) + HttpEntity entity = null; + if (request instanceof HttpEntityEnclosingRequest) { + entity = ((HttpEntityEnclosingRequest) request).getEntity(); + } + if (entity != null && !entity.isRepeatable()) { + // If the entity is not repeatable, try falling back onto QOP_AUTH + if (qopset.contains("auth")) { + qop = QOP_AUTH; + a2 = method + ':' + uri; + } else { + throw new AuthenticationException("Qop auth-int cannot be used with " + + "a non-repeatable entity"); + } + } else { + final HttpEntityDigester entityDigester = new HttpEntityDigester(digester); + try { + if (entity != null) { + entity.writeTo(entityDigester); + } + entityDigester.close(); + } catch (final IOException ex) { + throw new AuthenticationException("I/O error reading entity content", ex); + } + a2 = method + ':' + uri + ':' + encode(entityDigester.getDigest()); + } + } else { + a2 = method + ':' + uri; + } + + final String hasha2 = encode(digester.digest(EncodingUtils.getBytes(a2, charset))); + + // 3.2.2.1 + + final String digestValue; + if (qop == QOP_MISSING) { + sb.setLength(0); + sb.append(hasha1).append(':').append(nonce).append(':').append(hasha2); + digestValue = sb.toString(); + } else { + sb.setLength(0); + sb.append(hasha1).append(':').append(nonce).append(':').append(nc).append(':') + .append(cnonce).append(':').append(qop == QOP_AUTH_INT ? "auth-int" : "auth") + .append(':').append(hasha2); + digestValue = sb.toString(); + } + + final String digest = encode(digester.digest(EncodingUtils.getAsciiBytes(digestValue))); + + final CharArrayBuffer buffer = new CharArrayBuffer(128); + if (isProxy()) { + buffer.append(AUTH.PROXY_AUTH_RESP); + } else { + buffer.append(AUTH.WWW_AUTH_RESP); + } + buffer.append(": Digest "); + + final List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>(20); + params.add(new BasicNameValuePair("username", uname)); + params.add(new BasicNameValuePair("realm", realm)); + params.add(new BasicNameValuePair("nonce", nonce)); + params.add(new BasicNameValuePair("uri", uri)); + params.add(new BasicNameValuePair("response", digest)); + + if (qop != QOP_MISSING) { + params.add(new BasicNameValuePair("qop", qop == QOP_AUTH_INT ? "auth-int" : "auth")); + params.add(new BasicNameValuePair("nc", nc)); + params.add(new BasicNameValuePair("cnonce", cnonce)); + } + // algorithm cannot be null here + params.add(new BasicNameValuePair("algorithm", algorithm)); + if (opaque != null) { + params.add(new BasicNameValuePair("opaque", opaque)); + } + + for (int i = 0; i < params.size(); i++) { + final BasicNameValuePair param = params.get(i); + if (i > 0) { + buffer.append(", "); + } + final String name = param.getName(); + final boolean noQuotes = ("nc".equals(name) || "qop".equals(name) + || "algorithm".equals(name)); + BasicHeaderValueFormatter.INSTANCE.formatNameValuePair(buffer, param, !noQuotes); + } + return new BufferedHeader(buffer); + } + + String getCnonce() { + return cnonce; + } + + String getA1() { + return a1; + } + + String getA2() { + return a2; + } + + /** + * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long + * <CODE>String</CODE> according to RFC 2617. + * + * @param binaryData array containing the digest + * @return encoded MD5, or <CODE>null</CODE> if encoding failed + */ + static String encode(final byte[] binaryData) { + final int n = binaryData.length; + final char[] buffer = new char[n * 2]; + for (int i = 0; i < n; i++) { + final int low = (binaryData[i] & 0x0f); + final int high = ((binaryData[i] & 0xf0) >> 4); + buffer[i * 2] = HEXADECIMAL[high]; + buffer[(i * 2) + 1] = HEXADECIMAL[low]; + } + + return new String(buffer); + } + + + /** + * Creates a random cnonce value based on the current time. + * + * @return The cnonce value as String. + */ + public static String createCnonce() { + final SecureRandom rnd = new SecureRandom(); + final byte[] tmp = new byte[8]; + rnd.nextBytes(tmp); + return encode(tmp); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("DIGEST [complete=").append(complete) + .append(", nonce=").append(lastNonce) + .append(", nc=").append(nounceCount) + .append("]"); + return builder.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestSchemeFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestSchemeFactory.java new file mode 100644 index 000000000..0582bed49 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestSchemeFactory.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package ch.boye.httpclientandroidlib.impl.auth; + +import java.nio.charset.Charset; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthSchemeFactory; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link AuthSchemeProvider} implementation that creates and initializes + * {@link DigestScheme} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class DigestSchemeFactory implements AuthSchemeFactory, AuthSchemeProvider { + + private final Charset charset; + + /** + * @since 4.3 + */ + public DigestSchemeFactory(final Charset charset) { + super(); + this.charset = charset; + } + + public DigestSchemeFactory() { + this(null); + } + + public AuthScheme newInstance(final HttpParams params) { + return new DigestScheme(); + } + + public AuthScheme create(final HttpContext context) { + return new DigestScheme(this.charset); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpAuthenticator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpAuthenticator.java new file mode 100644 index 000000000..28b8b74d0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpAuthenticator.java @@ -0,0 +1,245 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.IOException; +import java.util.Locale; +import java.util.Map; +import java.util.Queue; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.auth.AuthOption; +import ch.boye.httpclientandroidlib.auth.AuthProtocolState; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * @since 4.3 + */ +public class HttpAuthenticator { + + public HttpClientAndroidLog log; + + public HttpAuthenticator(final HttpClientAndroidLog log) { + super(); + this.log = log != null ? log : new HttpClientAndroidLog(getClass()); + } + + public HttpAuthenticator() { + this(null); + } + + public boolean isAuthenticationRequested( + final HttpHost host, + final HttpResponse response, + final AuthenticationStrategy authStrategy, + final AuthState authState, + final HttpContext context) { + if (authStrategy.isAuthenticationRequested(host, response, context)) { + this.log.debug("Authentication required"); + if (authState.getState() == AuthProtocolState.SUCCESS) { + authStrategy.authFailed(host, authState.getAuthScheme(), context); + } + return true; + } else { + switch (authState.getState()) { + case CHALLENGED: + case HANDSHAKE: + this.log.debug("Authentication succeeded"); + authState.setState(AuthProtocolState.SUCCESS); + authStrategy.authSucceeded(host, authState.getAuthScheme(), context); + break; + case SUCCESS: + break; + default: + authState.setState(AuthProtocolState.UNCHALLENGED); + } + return false; + } + } + + public boolean handleAuthChallenge( + final HttpHost host, + final HttpResponse response, + final AuthenticationStrategy authStrategy, + final AuthState authState, + final HttpContext context) { + try { + if (this.log.isDebugEnabled()) { + this.log.debug(host.toHostString() + " requested authentication"); + } + final Map<String, Header> challenges = authStrategy.getChallenges(host, response, context); + if (challenges.isEmpty()) { + this.log.debug("Response contains no authentication challenges"); + return false; + } + + final AuthScheme authScheme = authState.getAuthScheme(); + switch (authState.getState()) { + case FAILURE: + return false; + case SUCCESS: + authState.reset(); + break; + case CHALLENGED: + case HANDSHAKE: + if (authScheme == null) { + this.log.debug("Auth scheme is null"); + authStrategy.authFailed(host, null, context); + authState.reset(); + authState.setState(AuthProtocolState.FAILURE); + return false; + } + case UNCHALLENGED: + if (authScheme != null) { + final String id = authScheme.getSchemeName(); + final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH)); + if (challenge != null) { + this.log.debug("Authorization challenge processed"); + authScheme.processChallenge(challenge); + if (authScheme.isComplete()) { + this.log.debug("Authentication failed"); + authStrategy.authFailed(host, authState.getAuthScheme(), context); + authState.reset(); + authState.setState(AuthProtocolState.FAILURE); + return false; + } else { + authState.setState(AuthProtocolState.HANDSHAKE); + return true; + } + } else { + authState.reset(); + // Retry authentication with a different scheme + } + } + } + final Queue<AuthOption> authOptions = authStrategy.select(challenges, host, response, context); + if (authOptions != null && !authOptions.isEmpty()) { + if (this.log.isDebugEnabled()) { + this.log.debug("Selected authentication options: " + authOptions); + } + authState.setState(AuthProtocolState.CHALLENGED); + authState.update(authOptions); + return true; + } else { + return false; + } + } catch (final MalformedChallengeException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn("Malformed challenge: " + ex.getMessage()); + } + authState.reset(); + return false; + } + } + + public void generateAuthResponse( + final HttpRequest request, + final AuthState authState, + final HttpContext context) throws HttpException, IOException { + AuthScheme authScheme = authState.getAuthScheme(); + Credentials creds = authState.getCredentials(); + switch (authState.getState()) { + case FAILURE: + return; + case SUCCESS: + ensureAuthScheme(authScheme); + if (authScheme.isConnectionBased()) { + return; + } + break; + case CHALLENGED: + final Queue<AuthOption> authOptions = authState.getAuthOptions(); + if (authOptions != null) { + while (!authOptions.isEmpty()) { + final AuthOption authOption = authOptions.remove(); + authScheme = authOption.getAuthScheme(); + creds = authOption.getCredentials(); + authState.update(authScheme, creds); + if (this.log.isDebugEnabled()) { + this.log.debug("Generating response to an authentication challenge using " + + authScheme.getSchemeName() + " scheme"); + } + try { + final Header header = doAuth(authScheme, creds, request, context); + request.addHeader(header); + break; + } catch (final AuthenticationException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn(authScheme + " authentication error: " + ex.getMessage()); + } + } + } + return; + } else { + ensureAuthScheme(authScheme); + } + } + if (authScheme != null) { + try { + final Header header = doAuth(authScheme, creds, request, context); + request.addHeader(header); + } catch (final AuthenticationException ex) { + if (this.log.isErrorEnabled()) { + this.log.error(authScheme + " authentication error: " + ex.getMessage()); + } + } + } + } + + private void ensureAuthScheme(final AuthScheme authScheme) { + Asserts.notNull(authScheme, "Auth scheme"); + } + + @SuppressWarnings("deprecation") + private Header doAuth( + final AuthScheme authScheme, + final Credentials creds, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + if (authScheme instanceof ContextAwareAuthScheme) { + return ((ContextAwareAuthScheme) authScheme).authenticate(creds, request, context); + } else { + return authScheme.authenticate(creds, request); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpEntityDigester.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpEntityDigester.java new file mode 100644 index 000000000..053951587 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpEntityDigester.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.MessageDigest; + +class HttpEntityDigester extends OutputStream { + + private final MessageDigest digester; + private boolean closed; + private byte[] digest; + + HttpEntityDigester(final MessageDigest digester) { + super(); + this.digester = digester; + this.digester.reset(); + } + + @Override + public void write(final int b) throws IOException { + if (this.closed) { + throw new IOException("Stream has been already closed"); + } + this.digester.update((byte) b); + } + + @Override + public void write(final byte[] b, final int off, final int len) throws IOException { + if (this.closed) { + throw new IOException("Stream has been already closed"); + } + this.digester.update(b, off, len); + } + + @Override + public void close() throws IOException { + if (this.closed) { + return; + } + this.closed = true; + this.digest = this.digester.digest(); + super.close(); + } + + public byte[] getDigest() { + return this.digest; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngine.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngine.java new file mode 100644 index 000000000..ce2457000 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngine.java @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +/** + * Abstract NTLM authentication engine. The engine can be used to + * generate Type1 messages and Type3 messages in response to a + * Type2 challenge. + * + * @since 4.0 + */ +public interface NTLMEngine { + + /** + * Generates a Type1 message given the domain and workstation. + * + * @param domain Optional Windows domain name. Can be <code>null</code>. + * @param workstation Optional Windows workstation name. Can be + * <code>null</code>. + * @return Type1 message + * @throws NTLMEngineException + */ + String generateType1Msg( + String domain, + String workstation) throws NTLMEngineException; + + /** + * Generates a Type3 message given the user credentials and the + * authentication challenge. + * + * @param username Windows user name + * @param password Password + * @param domain Windows domain name + * @param workstation Windows workstation name + * @param challenge Type2 challenge. + * @return Type3 response. + * @throws NTLMEngineException + */ + String generateType3Msg( + String username, + String password, + String domain, + String workstation, + String challenge) throws NTLMEngineException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineException.java new file mode 100644 index 000000000..88ef96912 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineException.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; + +/** + * Signals NTLM protocol failure. + * + * + * @since 4.0 + */ +@Immutable +public class NTLMEngineException extends AuthenticationException { + + private static final long serialVersionUID = 6027981323731768824L; + + public NTLMEngineException() { + super(); + } + + /** + * Creates a new NTLMEngineException with the specified message. + * + * @param message the exception detail message + */ + public NTLMEngineException(final String message) { + super(message); + } + + /** + * Creates a new NTLMEngineException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt> + * if the cause is unavailable, unknown, or not a <tt>Throwable</tt> + */ + public NTLMEngineException(final String message, final Throwable cause) { + super(message, cause); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java new file mode 100644 index 000000000..195148f6b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java @@ -0,0 +1,1672 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.UnsupportedEncodingException; +import java.security.Key; +import java.security.MessageDigest; +import java.util.Arrays; +import java.util.Locale; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import org.mozilla.apache.commons.codec.binary.Base64; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.EncodingUtils; + +/** + * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM + * authentication protocol. + * + * @since 4.1 + */ +@NotThreadSafe +final class NTLMEngineImpl implements NTLMEngine { + + // Flags we use; descriptions according to: + // http://davenport.sourceforge.net/ntlm.html + // and + // http://msdn.microsoft.com/en-us/library/cc236650%28v=prot.20%29.aspx + protected static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001; // Unicode string encoding requested + protected static final int FLAG_REQUEST_TARGET = 0x00000004; // Requests target field + protected static final int FLAG_REQUEST_SIGN = 0x00000010; // Requests all messages have a signature attached, in NEGOTIATE message. + protected static final int FLAG_REQUEST_SEAL = 0x00000020; // Request key exchange for message confidentiality in NEGOTIATE message. MUST be used in conjunction with 56BIT. + protected static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080; // Request Lan Manager key instead of user session key + protected static final int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security. MUST be set in NEGOTIATE and CHALLENGE both + protected static final int FLAG_DOMAIN_PRESENT = 0x00001000; // Domain is present in message + protected static final int FLAG_WORKSTATION_PRESENT = 0x00002000; // Workstation is present in message + protected static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000; // Requests a signature block on all messages. Overridden by REQUEST_SIGN and REQUEST_SEAL. + protected static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security + protected static final int FLAG_REQUEST_VERSION = 0x02000000; // Request protocol version + protected static final int FLAG_TARGETINFO_PRESENT = 0x00800000; // From server in challenge message, indicating targetinfo is present + protected static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange + protected static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000; // Request explicit key exchange + protected static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000; // Must be used in conjunction with SEAL + + + /** Secure random generator */ + private static final java.security.SecureRandom RND_GEN; + static { + java.security.SecureRandom rnd = null; + try { + rnd = java.security.SecureRandom.getInstance("SHA1PRNG"); + } catch (final Exception ignore) { + } + RND_GEN = rnd; + } + + /** Character encoding */ + static final String DEFAULT_CHARSET = "ASCII"; + + /** The character set to use for encoding the credentials */ + private String credentialCharset = DEFAULT_CHARSET; + + /** The signature string as bytes in the default encoding */ + private static final byte[] SIGNATURE; + + static { + final byte[] bytesWithoutNull = EncodingUtils.getBytes("NTLMSSP", "ASCII"); + SIGNATURE = new byte[bytesWithoutNull.length + 1]; + System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length); + SIGNATURE[bytesWithoutNull.length] = (byte) 0x00; + } + + /** + * Returns the response for the given message. + * + * @param message + * the message that was received from the server. + * @param username + * the username to authenticate with. + * @param password + * the password to authenticate with. + * @param host + * The host. + * @param domain + * the NT domain to authenticate in. + * @return The response. + * @throws ch.boye.httpclientandroidlib.HttpException + * If the messages cannot be retrieved. + */ + final String getResponseFor(final String message, final String username, final String password, + final String host, final String domain) throws NTLMEngineException { + + final String response; + if (message == null || message.trim().equals("")) { + response = getType1Message(host, domain); + } else { + final Type2Message t2m = new Type2Message(message); + response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m + .getFlags(), t2m.getTarget(), t2m.getTargetInfo()); + } + return response; + } + + /** + * Creates the first message (type 1 message) in the NTLM authentication + * sequence. This message includes the user name, domain and host for the + * authentication session. + * + * @param host + * the computer name of the host requesting authentication. + * @param domain + * The domain to authenticate with. + * @return String the message to add to the HTTP request header. + */ + String getType1Message(final String host, final String domain) throws NTLMEngineException { + return new Type1Message(domain, host).getResponse(); + } + + /** + * Creates the type 3 message using the given server nonce. The type 3 + * message includes all the information for authentication, host, domain, + * username and the result of encrypting the nonce sent by the server using + * the user's password as the key. + * + * @param user + * The user name. This should not include the domain name. + * @param password + * The password. + * @param host + * The host that is originating the authentication request. + * @param domain + * The domain to authenticate within. + * @param nonce + * the 8 byte array the server sent. + * @return The type 3 message. + * @throws NTLMEngineException + * If {@link #RC4(byte[],byte[])} fails. + */ + String getType3Message(final String user, final String password, final String host, final String domain, + final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation) + throws NTLMEngineException { + return new Type3Message(domain, host, user, password, nonce, type2Flags, target, + targetInformation).getResponse(); + } + + /** + * @return Returns the credentialCharset. + */ + String getCredentialCharset() { + return credentialCharset; + } + + /** + * @param credentialCharset + * The credentialCharset to set. + */ + void setCredentialCharset(final String credentialCharset) { + this.credentialCharset = credentialCharset; + } + + /** Strip dot suffix from a name */ + private static String stripDotSuffix(final String value) { + if (value == null) { + return null; + } + final int index = value.indexOf("."); + if (index != -1) { + return value.substring(0, index); + } + return value; + } + + /** Convert host to standard form */ + private static String convertHost(final String host) { + return stripDotSuffix(host); + } + + /** Convert domain to standard form */ + private static String convertDomain(final String domain) { + return stripDotSuffix(domain); + } + + private static int readULong(final byte[] src, final int index) throws NTLMEngineException { + if (src.length < index + 4) { + throw new NTLMEngineException("NTLM authentication - buffer too small for DWORD"); + } + return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8) + | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24); + } + + private static int readUShort(final byte[] src, final int index) throws NTLMEngineException { + if (src.length < index + 2) { + throw new NTLMEngineException("NTLM authentication - buffer too small for WORD"); + } + return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8); + } + + private static byte[] readSecurityBuffer(final byte[] src, final int index) throws NTLMEngineException { + final int length = readUShort(src, index); + final int offset = readULong(src, index + 4); + if (src.length < offset + length) { + throw new NTLMEngineException( + "NTLM authentication - buffer too small for data item"); + } + final byte[] buffer = new byte[length]; + System.arraycopy(src, offset, buffer, 0, length); + return buffer; + } + + /** Calculate a challenge block */ + private static byte[] makeRandomChallenge() throws NTLMEngineException { + if (RND_GEN == null) { + throw new NTLMEngineException("Random generator not available"); + } + final byte[] rval = new byte[8]; + synchronized (RND_GEN) { + RND_GEN.nextBytes(rval); + } + return rval; + } + + /** Calculate a 16-byte secondary key */ + private static byte[] makeSecondaryKey() throws NTLMEngineException { + if (RND_GEN == null) { + throw new NTLMEngineException("Random generator not available"); + } + final byte[] rval = new byte[16]; + synchronized (RND_GEN) { + RND_GEN.nextBytes(rval); + } + return rval; + } + + protected static class CipherGen { + + protected final String domain; + protected final String user; + protected final String password; + protected final byte[] challenge; + protected final String target; + protected final byte[] targetInformation; + + // Information we can generate but may be passed in (for testing) + protected byte[] clientChallenge; + protected byte[] clientChallenge2; + protected byte[] secondaryKey; + protected byte[] timestamp; + + // Stuff we always generate + protected byte[] lmHash = null; + protected byte[] lmResponse = null; + protected byte[] ntlmHash = null; + protected byte[] ntlmResponse = null; + protected byte[] ntlmv2Hash = null; + protected byte[] lmv2Hash = null; + protected byte[] lmv2Response = null; + protected byte[] ntlmv2Blob = null; + protected byte[] ntlmv2Response = null; + protected byte[] ntlm2SessionResponse = null; + protected byte[] lm2SessionResponse = null; + protected byte[] lmUserSessionKey = null; + protected byte[] ntlmUserSessionKey = null; + protected byte[] ntlmv2UserSessionKey = null; + protected byte[] ntlm2SessionResponseUserSessionKey = null; + protected byte[] lanManagerSessionKey = null; + + public CipherGen(final String domain, final String user, final String password, + final byte[] challenge, final String target, final byte[] targetInformation, + final byte[] clientChallenge, final byte[] clientChallenge2, + final byte[] secondaryKey, final byte[] timestamp) { + this.domain = domain; + this.target = target; + this.user = user; + this.password = password; + this.challenge = challenge; + this.targetInformation = targetInformation; + this.clientChallenge = clientChallenge; + this.clientChallenge2 = clientChallenge2; + this.secondaryKey = secondaryKey; + this.timestamp = timestamp; + } + + public CipherGen(final String domain, final String user, final String password, + final byte[] challenge, final String target, final byte[] targetInformation) { + this(domain, user, password, challenge, target, targetInformation, null, null, null, null); + } + + /** Calculate and return client challenge */ + public byte[] getClientChallenge() + throws NTLMEngineException { + if (clientChallenge == null) { + clientChallenge = makeRandomChallenge(); + } + return clientChallenge; + } + + /** Calculate and return second client challenge */ + public byte[] getClientChallenge2() + throws NTLMEngineException { + if (clientChallenge2 == null) { + clientChallenge2 = makeRandomChallenge(); + } + return clientChallenge2; + } + + /** Calculate and return random secondary key */ + public byte[] getSecondaryKey() + throws NTLMEngineException { + if (secondaryKey == null) { + secondaryKey = makeSecondaryKey(); + } + return secondaryKey; + } + + /** Calculate and return the LMHash */ + public byte[] getLMHash() + throws NTLMEngineException { + if (lmHash == null) { + lmHash = lmHash(password); + } + return lmHash; + } + + /** Calculate and return the LMResponse */ + public byte[] getLMResponse() + throws NTLMEngineException { + if (lmResponse == null) { + lmResponse = lmResponse(getLMHash(),challenge); + } + return lmResponse; + } + + /** Calculate and return the NTLMHash */ + public byte[] getNTLMHash() + throws NTLMEngineException { + if (ntlmHash == null) { + ntlmHash = ntlmHash(password); + } + return ntlmHash; + } + + /** Calculate and return the NTLMResponse */ + public byte[] getNTLMResponse() + throws NTLMEngineException { + if (ntlmResponse == null) { + ntlmResponse = lmResponse(getNTLMHash(),challenge); + } + return ntlmResponse; + } + + /** Calculate the LMv2 hash */ + public byte[] getLMv2Hash() + throws NTLMEngineException { + if (lmv2Hash == null) { + lmv2Hash = lmv2Hash(domain, user, getNTLMHash()); + } + return lmv2Hash; + } + + /** Calculate the NTLMv2 hash */ + public byte[] getNTLMv2Hash() + throws NTLMEngineException { + if (ntlmv2Hash == null) { + ntlmv2Hash = ntlmv2Hash(domain, user, getNTLMHash()); + } + return ntlmv2Hash; + } + + /** Calculate a timestamp */ + public byte[] getTimestamp() { + if (timestamp == null) { + long time = System.currentTimeMillis(); + time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch. + time *= 10000; // tenths of a microsecond. + // convert to little-endian byte array. + timestamp = new byte[8]; + for (int i = 0; i < 8; i++) { + timestamp[i] = (byte) time; + time >>>= 8; + } + } + return timestamp; + } + + /** Calculate the NTLMv2Blob */ + public byte[] getNTLMv2Blob() + throws NTLMEngineException { + if (ntlmv2Blob == null) { + ntlmv2Blob = createBlob(getClientChallenge2(), targetInformation, getTimestamp()); + } + return ntlmv2Blob; + } + + /** Calculate the NTLMv2Response */ + public byte[] getNTLMv2Response() + throws NTLMEngineException { + if (ntlmv2Response == null) { + ntlmv2Response = lmv2Response(getNTLMv2Hash(),challenge,getNTLMv2Blob()); + } + return ntlmv2Response; + } + + /** Calculate the LMv2Response */ + public byte[] getLMv2Response() + throws NTLMEngineException { + if (lmv2Response == null) { + lmv2Response = lmv2Response(getLMv2Hash(),challenge,getClientChallenge()); + } + return lmv2Response; + } + + /** Get NTLM2SessionResponse */ + public byte[] getNTLM2SessionResponse() + throws NTLMEngineException { + if (ntlm2SessionResponse == null) { + ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(),challenge,getClientChallenge()); + } + return ntlm2SessionResponse; + } + + /** Calculate and return LM2 session response */ + public byte[] getLM2SessionResponse() + throws NTLMEngineException { + if (lm2SessionResponse == null) { + final byte[] clChallenge = getClientChallenge(); + lm2SessionResponse = new byte[24]; + System.arraycopy(clChallenge, 0, lm2SessionResponse, 0, clChallenge.length); + Arrays.fill(lm2SessionResponse, clChallenge.length, lm2SessionResponse.length, (byte) 0x00); + } + return lm2SessionResponse; + } + + /** Get LMUserSessionKey */ + public byte[] getLMUserSessionKey() + throws NTLMEngineException { + if (lmUserSessionKey == null) { + lmUserSessionKey = new byte[16]; + System.arraycopy(getLMHash(), 0, lmUserSessionKey, 0, 8); + Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00); + } + return lmUserSessionKey; + } + + /** Get NTLMUserSessionKey */ + public byte[] getNTLMUserSessionKey() + throws NTLMEngineException { + if (ntlmUserSessionKey == null) { + final MD4 md4 = new MD4(); + md4.update(getNTLMHash()); + ntlmUserSessionKey = md4.getOutput(); + } + return ntlmUserSessionKey; + } + + /** GetNTLMv2UserSessionKey */ + public byte[] getNTLMv2UserSessionKey() + throws NTLMEngineException { + if (ntlmv2UserSessionKey == null) { + final byte[] ntlmv2hash = getNTLMv2Hash(); + final byte[] truncatedResponse = new byte[16]; + System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16); + ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash); + } + return ntlmv2UserSessionKey; + } + + /** Get NTLM2SessionResponseUserSessionKey */ + public byte[] getNTLM2SessionResponseUserSessionKey() + throws NTLMEngineException { + if (ntlm2SessionResponseUserSessionKey == null) { + final byte[] ntlm2SessionResponseNonce = getLM2SessionResponse(); + final byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length]; + System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length); + System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length); + ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,getNTLMUserSessionKey()); + } + return ntlm2SessionResponseUserSessionKey; + } + + /** Get LAN Manager session key */ + public byte[] getLanManagerSessionKey() + throws NTLMEngineException { + if (lanManagerSessionKey == null) { + try { + final byte[] keyBytes = new byte[14]; + System.arraycopy(getLMHash(), 0, keyBytes, 0, 8); + Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd); + final Key lowKey = createDESKey(keyBytes, 0); + final Key highKey = createDESKey(keyBytes, 7); + final byte[] truncatedResponse = new byte[8]; + System.arraycopy(getLMResponse(), 0, truncatedResponse, 0, truncatedResponse.length); + Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); + des.init(Cipher.ENCRYPT_MODE, lowKey); + final byte[] lowPart = des.doFinal(truncatedResponse); + des = Cipher.getInstance("DES/ECB/NoPadding"); + des.init(Cipher.ENCRYPT_MODE, highKey); + final byte[] highPart = des.doFinal(truncatedResponse); + lanManagerSessionKey = new byte[16]; + System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length); + System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length); + } catch (final Exception e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + return lanManagerSessionKey; + } + } + + /** Calculates HMAC-MD5 */ + static byte[] hmacMD5(final byte[] value, final byte[] key) + throws NTLMEngineException { + final HMACMD5 hmacMD5 = new HMACMD5(key); + hmacMD5.update(value); + return hmacMD5.getOutput(); + } + + /** Calculates RC4 */ + static byte[] RC4(final byte[] value, final byte[] key) + throws NTLMEngineException { + try { + final Cipher rc4 = Cipher.getInstance("RC4"); + rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4")); + return rc4.doFinal(value); + } catch (final Exception e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + + /** + * Calculates the NTLM2 Session Response for the given challenge, using the + * specified password and client challenge. + * + * @return The NTLM2 Session Response. This is placed in the NTLM response + * field of the Type 3 message; the LM response field contains the + * client challenge, null-padded to 24 bytes. + */ + static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge, + final byte[] clientChallenge) throws NTLMEngineException { + try { + // Look up MD5 algorithm (was necessary on jdk 1.4.2) + // This used to be needed, but java 1.5.0_07 includes the MD5 + // algorithm (finally) + // Class x = Class.forName("gnu.crypto.hash.MD5"); + // Method updateMethod = x.getMethod("update",new + // Class[]{byte[].class}); + // Method digestMethod = x.getMethod("digest",new Class[0]); + // Object mdInstance = x.newInstance(); + // updateMethod.invoke(mdInstance,new Object[]{challenge}); + // updateMethod.invoke(mdInstance,new Object[]{clientChallenge}); + // byte[] digest = (byte[])digestMethod.invoke(mdInstance,new + // Object[0]); + + final MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(challenge); + md5.update(clientChallenge); + final byte[] digest = md5.digest(); + + final byte[] sessionHash = new byte[8]; + System.arraycopy(digest, 0, sessionHash, 0, 8); + return lmResponse(ntlmHash, sessionHash); + } catch (final Exception e) { + if (e instanceof NTLMEngineException) { + throw (NTLMEngineException) e; + } + throw new NTLMEngineException(e.getMessage(), e); + } + } + + /** + * Creates the LM Hash of the user's password. + * + * @param password + * The password. + * + * @return The LM Hash of the given password, used in the calculation of the + * LM Response. + */ + private static byte[] lmHash(final String password) throws NTLMEngineException { + try { + final byte[] oemPassword = password.toUpperCase(Locale.ENGLISH).getBytes("US-ASCII"); + final int length = Math.min(oemPassword.length, 14); + final byte[] keyBytes = new byte[14]; + System.arraycopy(oemPassword, 0, keyBytes, 0, length); + final Key lowKey = createDESKey(keyBytes, 0); + final Key highKey = createDESKey(keyBytes, 7); + final byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII"); + final Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); + des.init(Cipher.ENCRYPT_MODE, lowKey); + final byte[] lowHash = des.doFinal(magicConstant); + des.init(Cipher.ENCRYPT_MODE, highKey); + final byte[] highHash = des.doFinal(magicConstant); + final byte[] lmHash = new byte[16]; + System.arraycopy(lowHash, 0, lmHash, 0, 8); + System.arraycopy(highHash, 0, lmHash, 8, 8); + return lmHash; + } catch (final Exception e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + + /** + * Creates the NTLM Hash of the user's password. + * + * @param password + * The password. + * + * @return The NTLM Hash of the given password, used in the calculation of + * the NTLM Response and the NTLMv2 and LMv2 Hashes. + */ + private static byte[] ntlmHash(final String password) throws NTLMEngineException { + try { + final byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked"); + final MD4 md4 = new MD4(); + md4.update(unicodePassword); + return md4.getOutput(); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e); + } + } + + /** + * Creates the LMv2 Hash of the user's password. + * + * @return The LMv2 Hash, used in the calculation of the NTLMv2 and LMv2 + * Responses. + */ + private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash) + throws NTLMEngineException { + try { + final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); + // Upper case username, upper case domain! + hmacMD5.update(user.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); + if (domain != null) { + hmacMD5.update(domain.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); + } + return hmacMD5.getOutput(); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e); + } + } + + /** + * Creates the NTLMv2 Hash of the user's password. + * + * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2 + * Responses. + */ + private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash) + throws NTLMEngineException { + try { + final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); + // Upper case username, mixed case target!! + hmacMD5.update(user.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); + if (domain != null) { + hmacMD5.update(domain.getBytes("UnicodeLittleUnmarked")); + } + return hmacMD5.getOutput(); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e); + } + } + + /** + * Creates the LM Response from the given hash and Type 2 challenge. + * + * @param hash + * The LM or NTLM Hash. + * @param challenge + * The server challenge from the Type 2 message. + * + * @return The response (either LM or NTLM, depending on the provided hash). + */ + private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException { + try { + final byte[] keyBytes = new byte[21]; + System.arraycopy(hash, 0, keyBytes, 0, 16); + final Key lowKey = createDESKey(keyBytes, 0); + final Key middleKey = createDESKey(keyBytes, 7); + final Key highKey = createDESKey(keyBytes, 14); + final Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); + des.init(Cipher.ENCRYPT_MODE, lowKey); + final byte[] lowResponse = des.doFinal(challenge); + des.init(Cipher.ENCRYPT_MODE, middleKey); + final byte[] middleResponse = des.doFinal(challenge); + des.init(Cipher.ENCRYPT_MODE, highKey); + final byte[] highResponse = des.doFinal(challenge); + final byte[] lmResponse = new byte[24]; + System.arraycopy(lowResponse, 0, lmResponse, 0, 8); + System.arraycopy(middleResponse, 0, lmResponse, 8, 8); + System.arraycopy(highResponse, 0, lmResponse, 16, 8); + return lmResponse; + } catch (final Exception e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + + /** + * Creates the LMv2 Response from the given hash, client data, and Type 2 + * challenge. + * + * @param hash + * The NTLMv2 Hash. + * @param clientData + * The client data (blob or client challenge). + * @param challenge + * The server challenge from the Type 2 message. + * + * @return The response (either NTLMv2 or LMv2, depending on the client + * data). + */ + private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData) + throws NTLMEngineException { + final HMACMD5 hmacMD5 = new HMACMD5(hash); + hmacMD5.update(challenge); + hmacMD5.update(clientData); + final byte[] mac = hmacMD5.getOutput(); + final byte[] lmv2Response = new byte[mac.length + clientData.length]; + System.arraycopy(mac, 0, lmv2Response, 0, mac.length); + System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length); + return lmv2Response; + } + + /** + * Creates the NTLMv2 blob from the given target information block and + * client challenge. + * + * @param targetInformation + * The target information block from the Type 2 message. + * @param clientChallenge + * The random 8-byte client challenge. + * + * @return The blob, used in the calculation of the NTLMv2 Response. + */ + private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) { + final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 }; + final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; + final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; + final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; + final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8 + + unknown1.length + targetInformation.length + unknown2.length]; + int offset = 0; + System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length); + offset += blobSignature.length; + System.arraycopy(reserved, 0, blob, offset, reserved.length); + offset += reserved.length; + System.arraycopy(timestamp, 0, blob, offset, timestamp.length); + offset += timestamp.length; + System.arraycopy(clientChallenge, 0, blob, offset, 8); + offset += 8; + System.arraycopy(unknown1, 0, blob, offset, unknown1.length); + offset += unknown1.length; + System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length); + offset += targetInformation.length; + System.arraycopy(unknown2, 0, blob, offset, unknown2.length); + offset += unknown2.length; + return blob; + } + + /** + * Creates a DES encryption key from the given key material. + * + * @param bytes + * A byte array containing the DES key material. + * @param offset + * The offset in the given byte array at which the 7-byte key + * material starts. + * + * @return A DES encryption key created from the key material starting at + * the specified offset in the given byte array. + */ + private static Key createDESKey(final byte[] bytes, final int offset) { + final byte[] keyBytes = new byte[7]; + System.arraycopy(bytes, offset, keyBytes, 0, 7); + final byte[] material = new byte[8]; + material[0] = keyBytes[0]; + material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1); + material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2); + material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3); + material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4); + material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5); + material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6); + material[7] = (byte) (keyBytes[6] << 1); + oddParity(material); + return new SecretKeySpec(material, "DES"); + } + + /** + * Applies odd parity to the given byte array. + * + * @param bytes + * The data whose parity bits are to be adjusted for odd parity. + */ + private static void oddParity(final byte[] bytes) { + for (int i = 0; i < bytes.length; i++) { + final byte b = bytes[i]; + final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) + ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0; + if (needsParity) { + bytes[i] |= (byte) 0x01; + } else { + bytes[i] &= (byte) 0xfe; + } + } + } + + /** NTLM message generation, base class */ + static class NTLMMessage { + /** The current response */ + private byte[] messageContents = null; + + /** The current output position */ + private int currentOutputPosition = 0; + + /** Constructor to use when message contents are not yet known */ + NTLMMessage() { + } + + /** Constructor to use when message contents are known */ + NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException { + messageContents = Base64.decodeBase64(EncodingUtils.getBytes(messageBody, + DEFAULT_CHARSET)); + // Look for NTLM message + if (messageContents.length < SIGNATURE.length) { + throw new NTLMEngineException("NTLM message decoding error - packet too short"); + } + int i = 0; + while (i < SIGNATURE.length) { + if (messageContents[i] != SIGNATURE[i]) { + throw new NTLMEngineException( + "NTLM message expected - instead got unrecognized bytes"); + } + i++; + } + + // Check to be sure there's a type 2 message indicator next + final int type = readULong(SIGNATURE.length); + if (type != expectedType) { + throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType) + + " message expected - instead got type " + Integer.toString(type)); + } + + currentOutputPosition = messageContents.length; + } + + /** + * Get the length of the signature and flags, so calculations can adjust + * offsets accordingly. + */ + protected int getPreambleLength() { + return SIGNATURE.length + 4; + } + + /** Get the message length */ + protected int getMessageLength() { + return currentOutputPosition; + } + + /** Read a byte from a position within the message buffer */ + protected byte readByte(final int position) throws NTLMEngineException { + if (messageContents.length < position + 1) { + throw new NTLMEngineException("NTLM: Message too short"); + } + return messageContents[position]; + } + + /** Read a bunch of bytes from a position in the message buffer */ + protected void readBytes(final byte[] buffer, final int position) throws NTLMEngineException { + if (messageContents.length < position + buffer.length) { + throw new NTLMEngineException("NTLM: Message too short"); + } + System.arraycopy(messageContents, position, buffer, 0, buffer.length); + } + + /** Read a ushort from a position within the message buffer */ + protected int readUShort(final int position) throws NTLMEngineException { + return NTLMEngineImpl.readUShort(messageContents, position); + } + + /** Read a ulong from a position within the message buffer */ + protected int readULong(final int position) throws NTLMEngineException { + return NTLMEngineImpl.readULong(messageContents, position); + } + + /** Read a security buffer from a position within the message buffer */ + protected byte[] readSecurityBuffer(final int position) throws NTLMEngineException { + return NTLMEngineImpl.readSecurityBuffer(messageContents, position); + } + + /** + * Prepares the object to create a response of the given length. + * + * @param maxlength + * the maximum length of the response to prepare, not + * including the type and the signature (which this method + * adds). + */ + protected void prepareResponse(final int maxlength, final int messageType) { + messageContents = new byte[maxlength]; + currentOutputPosition = 0; + addBytes(SIGNATURE); + addULong(messageType); + } + + /** + * Adds the given byte to the response. + * + * @param b + * the byte to add. + */ + protected void addByte(final byte b) { + messageContents[currentOutputPosition] = b; + currentOutputPosition++; + } + + /** + * Adds the given bytes to the response. + * + * @param bytes + * the bytes to add. + */ + protected void addBytes(final byte[] bytes) { + if (bytes == null) { + return; + } + for (final byte b : bytes) { + messageContents[currentOutputPosition] = b; + currentOutputPosition++; + } + } + + /** Adds a USHORT to the response */ + protected void addUShort(final int value) { + addByte((byte) (value & 0xff)); + addByte((byte) (value >> 8 & 0xff)); + } + + /** Adds a ULong to the response */ + protected void addULong(final int value) { + addByte((byte) (value & 0xff)); + addByte((byte) (value >> 8 & 0xff)); + addByte((byte) (value >> 16 & 0xff)); + addByte((byte) (value >> 24 & 0xff)); + } + + /** + * Returns the response that has been generated after shrinking the + * array if required and base64 encodes the response. + * + * @return The response as above. + */ + String getResponse() { + final byte[] resp; + if (messageContents.length > currentOutputPosition) { + final byte[] tmp = new byte[currentOutputPosition]; + System.arraycopy(messageContents, 0, tmp, 0, currentOutputPosition); + resp = tmp; + } else { + resp = messageContents; + } + return EncodingUtils.getAsciiString(Base64.encodeBase64(resp)); + } + + } + + /** Type 1 message assembly class */ + static class Type1Message extends NTLMMessage { + protected byte[] hostBytes; + protected byte[] domainBytes; + + /** Constructor. Include the arguments the message will need */ + Type1Message(final String domain, final String host) throws NTLMEngineException { + super(); + try { + // Strip off domain name from the host! + final String unqualifiedHost = convertHost(host); + // Use only the base domain name! + final String unqualifiedDomain = convertDomain(domain); + + hostBytes = unqualifiedHost != null? unqualifiedHost.getBytes("ASCII") : null; + domainBytes = unqualifiedDomain != null ? unqualifiedDomain + .toUpperCase(Locale.ENGLISH).getBytes("ASCII") : null; + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode unsupported: " + e.getMessage(), e); + } + } + + /** + * Getting the response involves building the message before returning + * it + */ + @Override + String getResponse() { + // Now, build the message. Calculate its length first, including + // signature or type. + final int finalLength = 32 + 8 /*+ hostBytes.length + domainBytes.length */; + + // Set up the response. This will initialize the signature, message + // type, and flags. + prepareResponse(finalLength, 1); + + // Flags. These are the complete set of flags we support. + addULong( + //FLAG_WORKSTATION_PRESENT | + //FLAG_DOMAIN_PRESENT | + + // Required flags + //FLAG_REQUEST_LAN_MANAGER_KEY | + FLAG_REQUEST_NTLMv1 | + FLAG_REQUEST_NTLM2_SESSION | + + // Protocol version request + FLAG_REQUEST_VERSION | + + // Recommended privacy settings + FLAG_REQUEST_ALWAYS_SIGN | + //FLAG_REQUEST_SEAL | + //FLAG_REQUEST_SIGN | + + // These must be set according to documentation, based on use of SEAL above + FLAG_REQUEST_128BIT_KEY_EXCH | + FLAG_REQUEST_56BIT_ENCRYPTION | + //FLAG_REQUEST_EXPLICIT_KEY_EXCH | + + FLAG_REQUEST_UNICODE_ENCODING); + + // Domain length (two times). + addUShort(/*domainBytes.length*/0); + addUShort(/*domainBytes.length*/0); + + // Domain offset. + addULong(/*hostBytes.length +*/ 32 + 8); + + // Host length (two times). + addUShort(/*hostBytes.length*/0); + addUShort(/*hostBytes.length*/0); + + // Host offset (always 32 + 8). + addULong(32 + 8); + + // Version + addUShort(0x0105); + // Build + addULong(2600); + // NTLM revision + addUShort(0x0f00); + + + // Host (workstation) String. + //addBytes(hostBytes); + + // Domain String. + //addBytes(domainBytes); + + + return super.getResponse(); + } + + } + + /** Type 2 message class */ + static class Type2Message extends NTLMMessage { + protected byte[] challenge; + protected String target; + protected byte[] targetInfo; + protected int flags; + + Type2Message(final String message) throws NTLMEngineException { + super(message, 2); + + // Type 2 message is laid out as follows: + // First 8 bytes: NTLMSSP[0] + // Next 4 bytes: Ulong, value 2 + // Next 8 bytes, starting at offset 12: target field (2 ushort lengths, 1 ulong offset) + // Next 4 bytes, starting at offset 20: Flags, e.g. 0x22890235 + // Next 8 bytes, starting at offset 24: Challenge + // Next 8 bytes, starting at offset 32: ??? (8 bytes of zeros) + // Next 8 bytes, starting at offset 40: targetinfo field (2 ushort lengths, 1 ulong offset) + // Next 2 bytes, major/minor version number (e.g. 0x05 0x02) + // Next 8 bytes, build number + // Next 2 bytes, protocol version number (e.g. 0x00 0x0f) + // Next, various text fields, and a ushort of value 0 at the end + + // Parse out the rest of the info we need from the message + // The nonce is the 8 bytes starting from the byte in position 24. + challenge = new byte[8]; + readBytes(challenge, 24); + + flags = readULong(20); + + if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) { + throw new NTLMEngineException( + "NTLM type 2 message has flags that make no sense: " + + Integer.toString(flags)); + } + + // Do the target! + target = null; + // The TARGET_DESIRED flag is said to not have understood semantics + // in Type2 messages, so use the length of the packet to decide + // how to proceed instead + if (getMessageLength() >= 12 + 8) { + final byte[] bytes = readSecurityBuffer(12); + if (bytes.length != 0) { + try { + target = new String(bytes, "UnicodeLittleUnmarked"); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + } + + // Do the target info! + targetInfo = null; + // TARGET_DESIRED flag cannot be relied on, so use packet length + if (getMessageLength() >= 40 + 8) { + final byte[] bytes = readSecurityBuffer(40); + if (bytes.length != 0) { + targetInfo = bytes; + } + } + } + + /** Retrieve the challenge */ + byte[] getChallenge() { + return challenge; + } + + /** Retrieve the target */ + String getTarget() { + return target; + } + + /** Retrieve the target info */ + byte[] getTargetInfo() { + return targetInfo; + } + + /** Retrieve the response flags */ + int getFlags() { + return flags; + } + + } + + /** Type 3 message assembly class */ + static class Type3Message extends NTLMMessage { + // Response flags from the type2 message + protected int type2Flags; + + protected byte[] domainBytes; + protected byte[] hostBytes; + protected byte[] userBytes; + + protected byte[] lmResp; + protected byte[] ntResp; + protected byte[] sessionKey; + + + /** Constructor. Pass the arguments we will need */ + Type3Message(final String domain, final String host, final String user, final String password, final byte[] nonce, + final int type2Flags, final String target, final byte[] targetInformation) + throws NTLMEngineException { + // Save the flags + this.type2Flags = type2Flags; + + // Strip off domain name from the host! + final String unqualifiedHost = convertHost(host); + // Use only the base domain name! + final String unqualifiedDomain = convertDomain(domain); + + // Create a cipher generator class. Use domain BEFORE it gets modified! + final CipherGen gen = new CipherGen(unqualifiedDomain, user, password, nonce, target, targetInformation); + + // Use the new code to calculate the responses, including v2 if that + // seems warranted. + byte[] userSessionKey; + try { + // This conditional may not work on Windows Server 2008 R2 and above, where it has not yet + // been tested + if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) && + targetInformation != null && target != null) { + // NTLMv2 + ntResp = gen.getNTLMv2Response(); + lmResp = gen.getLMv2Response(); + if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { + userSessionKey = gen.getLanManagerSessionKey(); + } else { + userSessionKey = gen.getNTLMv2UserSessionKey(); + } + } else { + // NTLMv1 + if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) { + // NTLM2 session stuff is requested + ntResp = gen.getNTLM2SessionResponse(); + lmResp = gen.getLM2SessionResponse(); + if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { + userSessionKey = gen.getLanManagerSessionKey(); + } else { + userSessionKey = gen.getNTLM2SessionResponseUserSessionKey(); + } + } else { + ntResp = gen.getNTLMResponse(); + lmResp = gen.getLMResponse(); + if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { + userSessionKey = gen.getLanManagerSessionKey(); + } else { + userSessionKey = gen.getNTLMUserSessionKey(); + } + } + } + } catch (final NTLMEngineException e) { + // This likely means we couldn't find the MD4 hash algorithm - + // fail back to just using LM + ntResp = new byte[0]; + lmResp = gen.getLMResponse(); + if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { + userSessionKey = gen.getLanManagerSessionKey(); + } else { + userSessionKey = gen.getLMUserSessionKey(); + } + } + + if ((type2Flags & FLAG_REQUEST_SIGN) != 0) { + if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) { + sessionKey = RC4(gen.getSecondaryKey(), userSessionKey); + } else { + sessionKey = userSessionKey; + } + } else { + sessionKey = null; + } + + try { + hostBytes = unqualifiedHost != null ? unqualifiedHost + .getBytes("UnicodeLittleUnmarked") : null; + domainBytes = unqualifiedDomain != null ? unqualifiedDomain + .toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked") : null; + userBytes = user.getBytes("UnicodeLittleUnmarked"); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e); + } + } + + /** Assemble the response */ + @Override + String getResponse() { + final int ntRespLen = ntResp.length; + final int lmRespLen = lmResp.length; + + final int domainLen = domainBytes != null ? domainBytes.length : 0; + final int hostLen = hostBytes != null ? hostBytes.length: 0; + final int userLen = userBytes.length; + final int sessionKeyLen; + if (sessionKey != null) { + sessionKeyLen = sessionKey.length; + } else { + sessionKeyLen = 0; + } + + // Calculate the layout within the packet + final int lmRespOffset = 72; // allocate space for the version + final int ntRespOffset = lmRespOffset + lmRespLen; + final int domainOffset = ntRespOffset + ntRespLen; + final int userOffset = domainOffset + domainLen; + final int hostOffset = userOffset + userLen; + final int sessionKeyOffset = hostOffset + hostLen; + final int finalLength = sessionKeyOffset + sessionKeyLen; + + // Start the response. Length includes signature and type + prepareResponse(finalLength, 3); + + // LM Resp Length (twice) + addUShort(lmRespLen); + addUShort(lmRespLen); + + // LM Resp Offset + addULong(lmRespOffset); + + // NT Resp Length (twice) + addUShort(ntRespLen); + addUShort(ntRespLen); + + // NT Resp Offset + addULong(ntRespOffset); + + // Domain length (twice) + addUShort(domainLen); + addUShort(domainLen); + + // Domain offset. + addULong(domainOffset); + + // User Length (twice) + addUShort(userLen); + addUShort(userLen); + + // User offset + addULong(userOffset); + + // Host length (twice) + addUShort(hostLen); + addUShort(hostLen); + + // Host offset + addULong(hostOffset); + + // Session key length (twice) + addUShort(sessionKeyLen); + addUShort(sessionKeyLen); + + // Session key offset + addULong(sessionKeyOffset); + + // Flags. + addULong( + //FLAG_WORKSTATION_PRESENT | + //FLAG_DOMAIN_PRESENT | + + // Required flags + (type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) | + (type2Flags & FLAG_REQUEST_NTLMv1) | + (type2Flags & FLAG_REQUEST_NTLM2_SESSION) | + + // Protocol version request + FLAG_REQUEST_VERSION | + + // Recommended privacy settings + (type2Flags & FLAG_REQUEST_ALWAYS_SIGN) | + (type2Flags & FLAG_REQUEST_SEAL) | + (type2Flags & FLAG_REQUEST_SIGN) | + + // These must be set according to documentation, based on use of SEAL above + (type2Flags & FLAG_REQUEST_128BIT_KEY_EXCH) | + (type2Flags & FLAG_REQUEST_56BIT_ENCRYPTION) | + (type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) | + + (type2Flags & FLAG_TARGETINFO_PRESENT) | + (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) | + (type2Flags & FLAG_REQUEST_TARGET) + ); + + // Version + addUShort(0x0105); + // Build + addULong(2600); + // NTLM revision + addUShort(0x0f00); + + // Add the actual data + addBytes(lmResp); + addBytes(ntResp); + addBytes(domainBytes); + addBytes(userBytes); + addBytes(hostBytes); + if (sessionKey != null) { + addBytes(sessionKey); + } + + return super.getResponse(); + } + } + + static void writeULong(final byte[] buffer, final int value, final int offset) { + buffer[offset] = (byte) (value & 0xff); + buffer[offset + 1] = (byte) (value >> 8 & 0xff); + buffer[offset + 2] = (byte) (value >> 16 & 0xff); + buffer[offset + 3] = (byte) (value >> 24 & 0xff); + } + + static int F(final int x, final int y, final int z) { + return ((x & y) | (~x & z)); + } + + static int G(final int x, final int y, final int z) { + return ((x & y) | (x & z) | (y & z)); + } + + static int H(final int x, final int y, final int z) { + return (x ^ y ^ z); + } + + static int rotintlft(final int val, final int numbits) { + return ((val << numbits) | (val >>> (32 - numbits))); + } + + /** + * Cryptography support - MD4. The following class was based loosely on the + * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java. + * Code correctness was verified by looking at MD4.java from the jcifs + * library (http://jcifs.samba.org). It was massaged extensively to the + * final form found here by Karl Wright (kwright@metacarta.com). + */ + static class MD4 { + protected int A = 0x67452301; + protected int B = 0xefcdab89; + protected int C = 0x98badcfe; + protected int D = 0x10325476; + protected long count = 0L; + protected byte[] dataBuffer = new byte[64]; + + MD4() { + } + + void update(final byte[] input) { + // We always deal with 512 bits at a time. Correspondingly, there is + // a buffer 64 bytes long that we write data into until it gets + // full. + int curBufferPos = (int) (count & 63L); + int inputIndex = 0; + while (input.length - inputIndex + curBufferPos >= dataBuffer.length) { + // We have enough data to do the next step. Do a partial copy + // and a transform, updating inputIndex and curBufferPos + // accordingly + final int transferAmt = dataBuffer.length - curBufferPos; + System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); + count += transferAmt; + curBufferPos = 0; + inputIndex += transferAmt; + processBuffer(); + } + + // If there's anything left, copy it into the buffer and leave it. + // We know there's not enough left to process. + if (inputIndex < input.length) { + final int transferAmt = input.length - inputIndex; + System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); + count += transferAmt; + curBufferPos += transferAmt; + } + } + + byte[] getOutput() { + // Feed pad/length data into engine. This must round out the input + // to a multiple of 512 bits. + final int bufferIndex = (int) (count & 63L); + final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex); + final byte[] postBytes = new byte[padLen + 8]; + // Leading 0x80, specified amount of zero padding, then length in + // bits. + postBytes[0] = (byte) 0x80; + // Fill out the last 8 bytes with the length + for (int i = 0; i < 8; i++) { + postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i)); + } + + // Update the engine + update(postBytes); + + // Calculate final result + final byte[] result = new byte[16]; + writeULong(result, A, 0); + writeULong(result, B, 4); + writeULong(result, C, 8); + writeULong(result, D, 12); + return result; + } + + protected void processBuffer() { + // Convert current buffer to 16 ulongs + final int[] d = new int[16]; + + for (int i = 0; i < 16; i++) { + d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8) + + ((dataBuffer[i * 4 + 2] & 0xff) << 16) + + ((dataBuffer[i * 4 + 3] & 0xff) << 24); + } + + // Do a round of processing + final int AA = A; + final int BB = B; + final int CC = C; + final int DD = D; + round1(d); + round2(d); + round3(d); + A += AA; + B += BB; + C += CC; + D += DD; + + } + + protected void round1(final int[] d) { + A = rotintlft((A + F(B, C, D) + d[0]), 3); + D = rotintlft((D + F(A, B, C) + d[1]), 7); + C = rotintlft((C + F(D, A, B) + d[2]), 11); + B = rotintlft((B + F(C, D, A) + d[3]), 19); + + A = rotintlft((A + F(B, C, D) + d[4]), 3); + D = rotintlft((D + F(A, B, C) + d[5]), 7); + C = rotintlft((C + F(D, A, B) + d[6]), 11); + B = rotintlft((B + F(C, D, A) + d[7]), 19); + + A = rotintlft((A + F(B, C, D) + d[8]), 3); + D = rotintlft((D + F(A, B, C) + d[9]), 7); + C = rotintlft((C + F(D, A, B) + d[10]), 11); + B = rotintlft((B + F(C, D, A) + d[11]), 19); + + A = rotintlft((A + F(B, C, D) + d[12]), 3); + D = rotintlft((D + F(A, B, C) + d[13]), 7); + C = rotintlft((C + F(D, A, B) + d[14]), 11); + B = rotintlft((B + F(C, D, A) + d[15]), 19); + } + + protected void round2(final int[] d) { + A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3); + D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5); + C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9); + B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13); + + A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3); + D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5); + C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9); + B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13); + + A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3); + D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5); + C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9); + B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13); + + A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3); + D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5); + C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9); + B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13); + + } + + protected void round3(final int[] d) { + A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3); + D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9); + C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11); + B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15); + + A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3); + D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9); + C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11); + B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15); + + A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3); + D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9); + C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11); + B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15); + + A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3); + D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9); + C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11); + B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15); + + } + + } + + /** + * Cryptography support - HMACMD5 - algorithmically based on various web + * resources by Karl Wright + */ + static class HMACMD5 { + protected byte[] ipad; + protected byte[] opad; + protected MessageDigest md5; + + HMACMD5(final byte[] input) throws NTLMEngineException { + byte[] key = input; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (final Exception ex) { + // Umm, the algorithm doesn't exist - throw an + // NTLMEngineException! + throw new NTLMEngineException( + "Error getting md5 message digest implementation: " + ex.getMessage(), ex); + } + + // Initialize the pad buffers with the key + ipad = new byte[64]; + opad = new byte[64]; + + int keyLength = key.length; + if (keyLength > 64) { + // Use MD5 of the key instead, as described in RFC 2104 + md5.update(key); + key = md5.digest(); + keyLength = key.length; + } + int i = 0; + while (i < keyLength) { + ipad[i] = (byte) (key[i] ^ (byte) 0x36); + opad[i] = (byte) (key[i] ^ (byte) 0x5c); + i++; + } + while (i < 64) { + ipad[i] = (byte) 0x36; + opad[i] = (byte) 0x5c; + i++; + } + + // Very important: update the digest with the ipad buffer + md5.reset(); + md5.update(ipad); + + } + + /** Grab the current digest. This is the "answer". */ + byte[] getOutput() { + final byte[] digest = md5.digest(); + md5.update(opad); + return md5.digest(digest); + } + + /** Update by adding a complete array */ + void update(final byte[] input) { + md5.update(input); + } + + /** Update the algorithm */ + void update(final byte[] input, final int offset, final int length) { + md5.update(input, offset, length); + } + + } + + public String generateType1Msg( + final String domain, + final String workstation) throws NTLMEngineException { + return getType1Message(workstation, domain); + } + + public String generateType3Msg( + final String username, + final String password, + final String domain, + final String workstation, + final String challenge) throws NTLMEngineException { + final Type2Message t2m = new Type2Message(challenge); + return getType3Message( + username, + password, + workstation, + domain, + t2m.getChallenge(), + t2m.getFlags(), + t2m.getTarget(), + t2m.getTargetInfo()); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMScheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMScheme.java new file mode 100644 index 000000000..68873954b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMScheme.java @@ -0,0 +1,164 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.InvalidCredentialsException; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.auth.NTCredentials; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * NTLM is a proprietary authentication scheme developed by Microsoft + * and optimized for Windows platforms. + * + * @since 4.0 + */ +@NotThreadSafe +public class NTLMScheme extends AuthSchemeBase { + + enum State { + UNINITIATED, + CHALLENGE_RECEIVED, + MSG_TYPE1_GENERATED, + MSG_TYPE2_RECEVIED, + MSG_TYPE3_GENERATED, + FAILED, + } + + private final NTLMEngine engine; + + private State state; + private String challenge; + + public NTLMScheme(final NTLMEngine engine) { + super(); + Args.notNull(engine, "NTLM engine"); + this.engine = engine; + this.state = State.UNINITIATED; + this.challenge = null; + } + + /** + * @since 4.3 + */ + public NTLMScheme() { + this(new NTLMEngineImpl()); + } + + public String getSchemeName() { + return "ntlm"; + } + + public String getParameter(final String name) { + // String parameters not supported + return null; + } + + public String getRealm() { + // NTLM does not support the concept of an authentication realm + return null; + } + + public boolean isConnectionBased() { + return true; + } + + @Override + protected void parseChallenge( + final CharArrayBuffer buffer, + final int beginIndex, final int endIndex) throws MalformedChallengeException { + this.challenge = buffer.substringTrimmed(beginIndex, endIndex); + if (this.challenge.length() == 0) { + if (this.state == State.UNINITIATED) { + this.state = State.CHALLENGE_RECEIVED; + } else { + this.state = State.FAILED; + } + } else { + if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) { + this.state = State.FAILED; + throw new MalformedChallengeException("Out of sequence NTLM response message"); + } else if (this.state == State.MSG_TYPE1_GENERATED) { + this.state = State.MSG_TYPE2_RECEVIED; + } + } + } + + public Header authenticate( + final Credentials credentials, + final HttpRequest request) throws AuthenticationException { + NTCredentials ntcredentials = null; + try { + ntcredentials = (NTCredentials) credentials; + } catch (final ClassCastException e) { + throw new InvalidCredentialsException( + "Credentials cannot be used for NTLM authentication: " + + credentials.getClass().getName()); + } + String response = null; + if (this.state == State.FAILED) { + throw new AuthenticationException("NTLM authentication failed"); + } else if (this.state == State.CHALLENGE_RECEIVED) { + response = this.engine.generateType1Msg( + ntcredentials.getDomain(), + ntcredentials.getWorkstation()); + this.state = State.MSG_TYPE1_GENERATED; + } else if (this.state == State.MSG_TYPE2_RECEVIED) { + response = this.engine.generateType3Msg( + ntcredentials.getUserName(), + ntcredentials.getPassword(), + ntcredentials.getDomain(), + ntcredentials.getWorkstation(), + this.challenge); + this.state = State.MSG_TYPE3_GENERATED; + } else { + throw new AuthenticationException("Unexpected state: " + this.state); + } + final CharArrayBuffer buffer = new CharArrayBuffer(32); + if (isProxy()) { + buffer.append(AUTH.PROXY_AUTH_RESP); + } else { + buffer.append(AUTH.WWW_AUTH_RESP); + } + buffer.append(": NTLM "); + buffer.append(response); + return new BufferedHeader(buffer); + } + + public boolean isComplete() { + return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMSchemeFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMSchemeFactory.java new file mode 100644 index 000000000..0484df643 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMSchemeFactory.java @@ -0,0 +1,56 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package ch.boye.httpclientandroidlib.impl.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthSchemeFactory; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link AuthSchemeProvider} implementation that creates and initializes + * {@link NTLMScheme} instances configured to use the default {@link NTLMEngine} + * implementation. + * + * @since 4.1 + */ +@Immutable +@SuppressWarnings("deprecation") +public class NTLMSchemeFactory implements AuthSchemeFactory, AuthSchemeProvider { + + public AuthScheme newInstance(final HttpParams params) { + return new NTLMScheme(); + } + + public AuthScheme create(final HttpContext context) { + return new NTLMScheme(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/RFC2617Scheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/RFC2617Scheme.java new file mode 100644 index 000000000..93d1bf843 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/RFC2617Scheme.java @@ -0,0 +1,151 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.ChallengeState; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.auth.params.AuthPNames; +import ch.boye.httpclientandroidlib.message.BasicHeaderValueParser; +import ch.boye.httpclientandroidlib.message.HeaderValueParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract authentication scheme class that lays foundation for all + * RFC 2617 compliant authentication schemes and provides capabilities common + * to all authentication schemes defined in RFC 2617. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe // AuthSchemeBase, params +public abstract class RFC2617Scheme extends AuthSchemeBase { + + private final Map<String, String> params; + private final Charset credentialsCharset; + + /** + * Creates an instance of <tt>RFC2617Scheme</tt> with the given challenge + * state. + * + * @since 4.2 + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public RFC2617Scheme(final ChallengeState challengeState) { + super(challengeState); + this.params = new HashMap<String, String>(); + this.credentialsCharset = Consts.ASCII; + } + + /** + * @since 4.3 + */ + public RFC2617Scheme(final Charset credentialsCharset) { + super(); + this.params = new HashMap<String, String>(); + this.credentialsCharset = credentialsCharset != null ? credentialsCharset : Consts.ASCII; + } + + public RFC2617Scheme() { + this(Consts.ASCII); + } + + + /** + * @since 4.3 + */ + public Charset getCredentialsCharset() { + return credentialsCharset; + } + + String getCredentialsCharset(final HttpRequest request) { + String charset = (String) request.getParams().getParameter(AuthPNames.CREDENTIAL_CHARSET); + if (charset == null) { + charset = getCredentialsCharset().name(); + } + return charset; + } + + @Override + protected void parseChallenge( + final CharArrayBuffer buffer, final int pos, final int len) throws MalformedChallengeException { + final HeaderValueParser parser = BasicHeaderValueParser.INSTANCE; + final ParserCursor cursor = new ParserCursor(pos, buffer.length()); + final HeaderElement[] elements = parser.parseElements(buffer, cursor); + if (elements.length == 0) { + throw new MalformedChallengeException("Authentication challenge is empty"); + } + this.params.clear(); + for (final HeaderElement element : elements) { + this.params.put(element.getName().toLowerCase(Locale.ENGLISH), element.getValue()); + } + } + + /** + * Returns authentication parameters map. Keys in the map are lower-cased. + * + * @return the map of authentication parameters + */ + protected Map<String, String> getParameters() { + return this.params; + } + + /** + * Returns authentication parameter with the given name, if available. + * + * @param name The name of the parameter to be returned + * + * @return the parameter with the given name + */ + public String getParameter(final String name) { + if (name == null) { + return null; + } + return this.params.get(name.toLowerCase(Locale.ENGLISH)); + } + + /** + * Returns authentication realm. The realm may not be null. + * + * @return the authentication realm + */ + public String getRealm() { + return getParameter("realm"); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java new file mode 100644 index 000000000..5ab0348ad --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.IOException; + +/** + * Abstract SPNEGO token generator. Implementations should take an Kerberos ticket and transform + * into a SPNEGO token. + * <p> + * Implementations of this interface are expected to be thread-safe. + * + * @since 4.1 + * + * @deprecated (4.2) subclass {@link KerberosScheme} and override + * {@link KerberosScheme#generateGSSToken(byte[], org.ietf.jgss.Oid, String)} + */ +@Deprecated +public interface SpnegoTokenGenerator { + + byte [] generateSpnegoDERObject(byte [] kerberosTicket) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java new file mode 100644 index 000000000..71a6f892f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Authentication credentials required to respond to a authentication + * challenge are invalid + * + * + * @since 4.0 + */ +@Immutable +public class UnsupportedDigestAlgorithmException extends RuntimeException { + + private static final long serialVersionUID = 319558534317118022L; + + /** + * Creates a new UnsupportedAuthAlgoritmException with a <tt>null</tt> detail message. + */ + public UnsupportedDigestAlgorithmException() { + super(); + } + + /** + * Creates a new UnsupportedAuthAlgoritmException with the specified message. + * + * @param message the exception detail message + */ + public UnsupportedDigestAlgorithmException(final String message) { + super(message); + } + + /** + * Creates a new UnsupportedAuthAlgoritmException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt> + * if the cause is unavailable, unknown, or not a <tt>Throwable</tt> + */ + public UnsupportedDigestAlgorithmException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/package-info.java new file mode 100644 index 000000000..9e35c852d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +/** + * Default implementations of standard and common HTTP authentication + * schemes. + */ +package ch.boye.httpclientandroidlib.impl.auth; |