JDK 11 java.security.sasl.jmod - Security SASL Module

JDK 11 java.security.sasl.jmod is the JMOD file for JDK 11 Security SASL (Simple Authentication and Security Layer) module.

JDK 11 Security SASL module compiled class files are stored in \fyicenter\jdk-11.0.1\jmods\java.security.sasl.jmod.

JDK 11 Security SASL module compiled class files are also linked and stored in the \fyicenter\jdk-11.0.1\lib\modules JImage file.

JDK 11 Security SASL module source code files are stored in \fyicenter\jdk-11.0.1\lib\src.zip\java.security.sasl.

You can click and view the content of each source code file in the list below.

✍: FYIcenter

com/sun/security/sasl/digest/DigestMD5Server.java

/*
 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.security.sasl.digest;

import java.security.NoSuchAlgorithmException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Arrays;

import java.util.logging.Level;

import javax.security.sasl.*;
import javax.security.auth.callback.*;

/**
  * An implementation of the DIGEST-MD5 server SASL mechanism.
  * (<a href="http://www.ietf.org/rfc/rfc2831.txt">RFC 2831</a>)
  * <p>
  * The DIGEST-MD5 SASL mechanism specifies two modes of authentication.
  * <ul><li>Initial Authentication
  * <li>Subsequent Authentication - optional, (currently not supported)
  * </ul>
  *
  * Required callbacks:
  * - RealmCallback
  *      used as key by handler to fetch password
  * - NameCallback
  *      used as key by handler to fetch password
  * - PasswordCallback
  *      handler must enter password for username/realm supplied
  * - AuthorizeCallback
  *      handler must verify that authid/authzids are allowed and set
  *      authorized ID to be the canonicalized authzid (if applicable).
  *
  * Environment properties that affect the implementation:
  * javax.security.sasl.qop:
  *    specifies list of qops; default is "auth"; typically, caller should set
  *    this to "auth, auth-int, auth-conf".
  * javax.security.sasl.strength
  *    specifies low/medium/high strength of encryption; default is all available
  *    ciphers [high,medium,low]; high means des3 or rc4 (128); medium des or
  *    rc4-56; low is rc4-40.
  * javax.security.sasl.maxbuf
  *    specifies max receive buf size; default is 65536
  * javax.security.sasl.sendmaxbuffer
  *    specifies max send buf size; default is 65536 (min of this and client's max
  *    recv size)
  *
  * com.sun.security.sasl.digest.utf8:
  *    "true" means to use UTF-8 charset; "false" to use ISO-8859-1 encoding;
  *    default is "true".
  * com.sun.security.sasl.digest.realm:
  *    space-separated list of realms; default is server name (fqdn parameter)
  *
  * @author Rosanna Lee
  */

final class DigestMD5Server extends DigestMD5Base implements SaslServer {
    private static final String MY_CLASS_NAME = DigestMD5Server.class.getName();

    private static final String UTF8_DIRECTIVE = "charset=utf-8,";
    private static final String ALGORITHM_DIRECTIVE = "algorithm=md5-sess";

    /*
     * Always expect nonce count value to be 1 because we support only
     * initial authentication.
     */
    private static final int NONCE_COUNT_VALUE = 1;

    /* "true" means use UTF8; "false" ISO 8859-1; default is "true" */
    private static final String UTF8_PROPERTY =
        "com.sun.security.sasl.digest.utf8";

    /* List of space-separated realms used for authentication */
    private static final String REALM_PROPERTY =
        "com.sun.security.sasl.digest.realm";

    /* Directives encountered in responses sent by the client. */
    private static final String[] DIRECTIVE_KEY = {
        "username",    // exactly once
        "realm",       // exactly once if sent by server
        "nonce",       // exactly once
        "cnonce",      // exactly once
        "nonce-count", // atmost once; default is 00000001
        "qop",         // atmost once; default is "auth"
        "digest-uri",  // atmost once; (default?)
        "response",    // exactly once
        "maxbuf",      // atmost once; default is 65536
        "charset",     // atmost once; default is ISO-8859-1
        "cipher",      // exactly once if qop is "auth-conf"
        "authzid",     // atmost once; default is none
        "auth-param",  // >= 0 times (ignored)
    };

    /* Indices into DIRECTIVE_KEY */
    private static final int USERNAME = 0;
    private static final int REALM = 1;
    private static final int NONCE = 2;
    private static final int CNONCE = 3;
    private static final int NONCE_COUNT = 4;
    private static final int QOP = 5;
    private static final int DIGEST_URI = 6;
    private static final int RESPONSE = 7;
    private static final int MAXBUF = 8;
    private static final int CHARSET = 9;
    private static final int CIPHER = 10;
    private static final int AUTHZID = 11;
    private static final int AUTH_PARAM = 12;

    /* Server-generated/supplied information */
    private String specifiedQops;
    private byte[] myCiphers;
    private List<String> serverRealms;

    DigestMD5Server(String protocol, String serverName, Map<String, ?> props,
            CallbackHandler cbh) throws SaslException {
        super(props, MY_CLASS_NAME, 1,
                protocol + "/" + (serverName==null?"*":serverName),
                cbh);

        serverRealms = new ArrayList<String>();

        useUTF8 = true;  // default

        if (props != null) {
            specifiedQops = (String) props.get(Sasl.QOP);
            if ("false".equals((String) props.get(UTF8_PROPERTY))) {
                useUTF8 = false;
                logger.log(Level.FINE, "DIGEST80:Server supports ISO-Latin-1");
            }

            String realms = (String) props.get(REALM_PROPERTY);
            if (realms != null) {
                StringTokenizer parser = new StringTokenizer(realms, ", \t\n");
                int tokenCount = parser.countTokens();
                String token = null;
                for (int i = 0; i < tokenCount; i++) {
                    token = parser.nextToken();
                    logger.log(Level.FINE, "DIGEST81:Server supports realm {0}",
                        token);
                    serverRealms.add(token);
                }
            }
        }

        encoding = (useUTF8 ? "UTF8" : "8859_1");

        // By default, use server name as realm
        if (serverRealms.isEmpty()) {
            if (serverName == null) {
                throw new SaslException(
                        "A realm must be provided in props or serverName");
            } else {
                serverRealms.add(serverName);
            }
        }
    }

    public  byte[] evaluateResponse(byte[] response) throws SaslException {
        if (response.length > MAX_RESPONSE_LENGTH) {
            throw new SaslException(
                "DIGEST-MD5: Invalid digest response length. Got:  " +
                response.length + " Expected < " + MAX_RESPONSE_LENGTH);
        }

        byte[] challenge;
        switch (step) {
        case 1:
            if (response.length != 0) {
                throw new SaslException(
                    "DIGEST-MD5 must not have an initial response");
            }

            /* Generate first challenge */
            String supportedCiphers = null;
            if ((allQop&PRIVACY_PROTECTION) != 0) {
                myCiphers = getPlatformCiphers();
                StringBuilder sb = new StringBuilder();

                // myCipher[i] is a byte that indicates whether CIPHER_TOKENS[i]
                // is supported
                for (int i = 0; i < CIPHER_TOKENS.length; i++) {
                    if (myCiphers[i] != 0) {
                        if (sb.length() > 0) {
                            sb.append(',');
                        }
                        sb.append(CIPHER_TOKENS[i]);
                    }
                }
                supportedCiphers = sb.toString();
            }

            try {
                challenge = generateChallenge(serverRealms, specifiedQops,
                    supportedCiphers);

                step = 3;
                return challenge;
            } catch (UnsupportedEncodingException e) {
                throw new SaslException(
                    "DIGEST-MD5: Error encoding challenge", e);
            } catch (IOException e) {
                throw new SaslException(
                    "DIGEST-MD5: Error generating challenge", e);
            }

            // Step 2 is performed by client

        case 3:
            /* Validates client's response and generate challenge:
             *    response-auth = "rspauth" "=" response-value
             */
            try {
                byte[][] responseVal = parseDirectives(response, DIRECTIVE_KEY,
                    null, REALM);
                challenge = validateClientResponse(responseVal);
            } catch (SaslException e) {
                throw e;
            } catch (UnsupportedEncodingException e) {
                throw new SaslException(
                    "DIGEST-MD5: Error validating client response", e);
            } finally {
                step = 0;  // Set to invalid state
            }

            completed = true;

            /* Initialize SecurityCtx implementation */
            if (integrity && privacy) {
                secCtx = new DigestPrivacy(false /* not client */);
            } else if (integrity) {
                secCtx = new DigestIntegrity(false /* not client */);
            }

            return challenge;

        default:
            // No other possible state
            throw new SaslException("DIGEST-MD5: Server at illegal state");
        }
    }

    /**
     * Generates challenge to be sent to client.
     *  digest-challenge  =
     *    1#( realm | nonce | qop-options | stale | maxbuf | charset
     *               algorithm | cipher-opts | auth-param )
     *
     *        realm             = "realm" "=" <"> realm-value <">
     *        realm-value       = qdstr-val
     *        nonce             = "nonce" "=" <"> nonce-value <">
     *        nonce-value       = qdstr-val
     *        qop-options       = "qop" "=" <"> qop-list <">
     *        qop-list          = 1#qop-value
     *        qop-value         = "auth" | "auth-int" | "auth-conf" |
     *                             token
     *        stale             = "stale" "=" "true"
     *        maxbuf            = "maxbuf" "=" maxbuf-value
     *        maxbuf-value      = 1*DIGIT
     *        charset           = "charset" "=" "utf-8"
     *        algorithm         = "algorithm" "=" "md5-sess"
     *        cipher-opts       = "cipher" "=" <"> 1#cipher-value <">
     *        cipher-value      = "3des" | "des" | "rc4-40" | "rc4" |
     *                            "rc4-56" | token
     *        auth-param        = token "=" ( token | quoted-string )
     */
    private byte[] generateChallenge(List<String> realms, String qopStr,
        String cipherStr) throws UnsupportedEncodingException, IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // Realms (>= 0)
        for (int i = 0; realms != null && i < realms.size(); i++) {
            out.write("realm=\"".getBytes(encoding));
            writeQuotedStringValue(out, realms.get(i).getBytes(encoding));
            out.write('"');
            out.write(',');
        }

        // Nonce - required (1)
        out.write(("nonce=\"").getBytes(encoding));
        nonce = generateNonce();
        writeQuotedStringValue(out, nonce);
        out.write('"');
        out.write(',');

        // QOP - optional (1) [default: auth]
        // qop="auth,auth-conf,auth-int"
        if (qopStr != null) {
            out.write(("qop=\"").getBytes(encoding));
            // Check for quotes in case of non-standard qop options
            writeQuotedStringValue(out, qopStr.getBytes(encoding));
            out.write('"');
            out.write(',');
        }

        // maxbuf - optional (1) [default: 65536]
        if (recvMaxBufSize != DEFAULT_MAXBUF) {
            out.write(("maxbuf=\"" + recvMaxBufSize + "\",").getBytes(encoding));
        }

        // charset - optional (1) [default: ISO 8859_1]
        if (useUTF8) {
            out.write(UTF8_DIRECTIVE.getBytes(encoding));
        }

        if (cipherStr != null) {
            out.write("cipher=\"".getBytes(encoding));
            // Check for quotes in case of custom ciphers
            writeQuotedStringValue(out, cipherStr.getBytes(encoding));
            out.write('"');
            out.write(',');
        }

        // algorithm - required (1)
        out.write(ALGORITHM_DIRECTIVE.getBytes(encoding));

        return out.toByteArray();
    }

    /**
     * Validates client's response.
     *   digest-response  = 1#( username | realm | nonce | cnonce |
     *                          nonce-count | qop | digest-uri | response |
     *                          maxbuf | charset | cipher | authzid |
     *                          auth-param )
     *
     *       username         = "username" "=" <"> username-value <">
     *       username-value   = qdstr-val
     *       cnonce           = "cnonce" "=" <"> cnonce-value <">
     *       cnonce-value     = qdstr-val
     *       nonce-count      = "nc" "=" nc-value
     *       nc-value         = 8LHEX
     *       qop              = "qop" "=" qop-value
     *       digest-uri       = "digest-uri" "=" <"> digest-uri-value <">
     *       digest-uri-value  = serv-type "/" host [ "/" serv-name ]
     *       serv-type        = 1*ALPHA
     *       host             = 1*( ALPHA | DIGIT | "-" | "." )
     *       serv-name        = host
     *       response         = "response" "=" response-value
     *       response-value   = 32LHEX
     *       LHEX             = "0" | "1" | "2" | "3" |
     *                          "4" | "5" | "6" | "7" |
     *                          "8" | "9" | "a" | "b" |
     *                          "c" | "d" | "e" | "f"
     *       cipher           = "cipher" "=" cipher-value
     *       authzid          = "authzid" "=" <"> authzid-value <">
     *       authzid-value    = qdstr-val
     * sets:
     *   negotiatedQop
     *   negotiatedCipher
     *   negotiatedRealm
     *   negotiatedStrength
     *   digestUri (checked and set to clients to account for case diffs)
     *   sendMaxBufSize
     *   authzid (gotten from callback)
     * @return response-value ('rspauth') for client to validate
     */
    private byte[] validateClientResponse(byte[][] responseVal)
        throws SaslException, UnsupportedEncodingException {

        /* CHARSET: optional atmost once */
        if (responseVal[CHARSET] != null) {
            // The client should send this directive only if the server has
            // indicated it supports UTF-8.
            if (!useUTF8 ||
                !"utf-8".equals(new String(responseVal[CHARSET], encoding))) {
                throw new SaslException("DIGEST-MD5: digest response format " +
                    "violation. Incompatible charset value: " +
                    new String(responseVal[CHARSET]));
            }
        }

        // maxbuf: atmost once
        int clntMaxBufSize =
            (responseVal[MAXBUF] == null) ? DEFAULT_MAXBUF
            : Integer.parseInt(new String(responseVal[MAXBUF], encoding));

        // Max send buf size is min of client's max recv buf size and
        // server's max send buf size
        sendMaxBufSize = ((sendMaxBufSize == 0) ? clntMaxBufSize :
            Math.min(sendMaxBufSize, clntMaxBufSize));

        /* username: exactly once */
        String username;
        if (responseVal[USERNAME] != null) {
            username = new String(responseVal[USERNAME], encoding);
            logger.log(Level.FINE, "DIGEST82:Username: {0}", username);
        } else {
            throw new SaslException("DIGEST-MD5: digest response format " +
                "violation. Missing username.");
        }

        /* realm: exactly once if sent by server */
        negotiatedRealm = ((responseVal[REALM] != null) ?
            new String(responseVal[REALM], encoding) : "");
        logger.log(Level.FINE, "DIGEST83:Client negotiated realm: {0}",
            negotiatedRealm);

        if (!serverRealms.contains(negotiatedRealm)) {
            // Server had sent at least one realm
            // Check that response is one of these
            throw new SaslException("DIGEST-MD5: digest response format " +
                "violation. Nonexistent realm: " + negotiatedRealm);
        }
        // Else, client specified realm was one of server's or server had none

        /* nonce: exactly once */
        if (responseVal[NONCE] == null) {
            throw new SaslException("DIGEST-MD5: digest response format " +
                "violation. Missing nonce.");
        }
        byte[] nonceFromClient = responseVal[NONCE];
        if (!Arrays.equals(nonceFromClient, nonce)) {
            throw new SaslException("DIGEST-MD5: digest response format " +
                "violation. Mismatched nonce.");
        }

        /* cnonce: exactly once */
        if (responseVal[CNONCE] == null) {
            throw new SaslException("DIGEST-MD5: digest response format " +
                "violation. Missing cnonce.");
        }
        byte[] cnonce = responseVal[CNONCE];

        /* nonce-count: atmost once */
        if (responseVal[NONCE_COUNT] != null &&
            NONCE_COUNT_VALUE != Integer.parseInt(
                new String(responseVal[NONCE_COUNT], encoding), 16)) {
            throw new SaslException("DIGEST-MD5: digest response format " +
                "violation. Nonce count does not match: " +
                new String(responseVal[NONCE_COUNT]));
        }

        /* qop: atmost once; default is "auth" */
        negotiatedQop = ((responseVal[QOP] != null) ?
            new String(responseVal[QOP], encoding) : "auth");

        logger.log(Level.FINE, "DIGEST84:Client negotiated qop: {0}",
            negotiatedQop);

        // Check that QOP is one sent by server
        byte cQop;
        switch (negotiatedQop) {
            case "auth":
                cQop = NO_PROTECTION;
                break;
            case "auth-int":
                cQop = INTEGRITY_ONLY_PROTECTION;
                integrity = true;
                rawSendSize = sendMaxBufSize - 16;
                break;
            case "auth-conf":
                cQop = PRIVACY_PROTECTION;
                integrity = privacy = true;
                rawSendSize = sendMaxBufSize - 26;
                break;
            default:
                throw new SaslException("DIGEST-MD5: digest response format " +
                    "violation. Invalid QOP: " + negotiatedQop);
        }
        if ((cQop&allQop) == 0) {
            throw new SaslException("DIGEST-MD5: server does not support " +
                " qop: " + negotiatedQop);
        }

        if (privacy) {
            negotiatedCipher = ((responseVal[CIPHER] != null) ?
                new String(responseVal[CIPHER], encoding) : null);
            if (negotiatedCipher == null) {
                throw new SaslException("DIGEST-MD5: digest response format " +
                    "violation. No cipher specified.");
            }

            int foundCipher = -1;
            logger.log(Level.FINE, "DIGEST85:Client negotiated cipher: {0}",
                negotiatedCipher);

            // Check that cipher is one that we offered
            for (int j = 0; j < CIPHER_TOKENS.length; j++) {
                if (negotiatedCipher.equals(CIPHER_TOKENS[j]) &&
                    myCiphers[j] != 0) {
                    foundCipher = j;
                    break;
                }
            }
            if (foundCipher == -1) {
                throw new SaslException("DIGEST-MD5: server does not " +
                    "support cipher: " + negotiatedCipher);
            }
            // Set negotiatedStrength
            if ((CIPHER_MASKS[foundCipher]&HIGH_STRENGTH) != 0) {
                negotiatedStrength = "high";
            } else if ((CIPHER_MASKS[foundCipher]&MEDIUM_STRENGTH) != 0) {
                negotiatedStrength = "medium";
            } else {
                // assume default low
                negotiatedStrength = "low";
            }

            logger.log(Level.FINE, "DIGEST86:Negotiated strength: {0}",
                negotiatedStrength);
        }

        // atmost once
        String digestUriFromResponse = ((responseVal[DIGEST_URI]) != null ?
            new String(responseVal[DIGEST_URI], encoding) : null);

        if (digestUriFromResponse != null) {
            logger.log(Level.FINE, "DIGEST87:digest URI: {0}",
                digestUriFromResponse);
        }

        // serv-type "/" host [ "/" serv-name ]
        // e.g.: smtp/mail3.example.com/example.com
        // e.g.: ftp/ftp.example.com
        // e.g.: ldap/ldapserver.example.com

        // host should match one of service's configured service names
        // Check against digest URI that mech was created with

        if (uriMatches(digestUri, digestUriFromResponse)) {
            digestUri = digestUriFromResponse; // account for case-sensitive diffs
        } else {
            throw new SaslException("DIGEST-MD5: digest response format " +
                "violation. Mismatched URI: " + digestUriFromResponse +
                "; expecting: " + digestUri);
        }

        // response: exactly once
        byte[] responseFromClient = responseVal[RESPONSE];
        if (responseFromClient == null) {
            throw new SaslException("DIGEST-MD5: digest response format " +
                " violation. Missing response.");
        }

        // authzid: atmost once
        byte[] authzidBytes;
        String authzidFromClient = ((authzidBytes=responseVal[AUTHZID]) != null?
            new String(authzidBytes, encoding) : username);

        if (authzidBytes != null) {
            logger.log(Level.FINE, "DIGEST88:Authzid: {0}",
                new String(authzidBytes));
        }

        // Ignore auth-param

        // Get password need to generate verifying response
        char[] passwd;
        try {
            // Realm and Name callbacks are used to provide info
            RealmCallback rcb = new RealmCallback("DIGEST-MD5 realm: ",
                negotiatedRealm);
            NameCallback ncb = new NameCallback("DIGEST-MD5 authentication ID: ",
                username);

            // PasswordCallback is used to collect info
            PasswordCallback pcb =
                new PasswordCallback("DIGEST-MD5 password: ", false);

            cbh.handle(new Callback[] {rcb, ncb, pcb});
            passwd = pcb.getPassword();
            pcb.clearPassword();

        } catch (UnsupportedCallbackException e) {
            throw new SaslException(
                "DIGEST-MD5: Cannot perform callback to acquire password", e);

        } catch (IOException e) {
            throw new SaslException(
                "DIGEST-MD5: IO error acquiring password", e);
        }

        if (passwd == null) {
            throw new SaslException(
                "DIGEST-MD5: cannot acquire password for " + username +
                " in realm : " + negotiatedRealm);
        }

        try {
            // Validate response value sent by client
            byte[] expectedResponse;

            try {
                expectedResponse = generateResponseValue("AUTHENTICATE",
                    digestUri, negotiatedQop, username, negotiatedRealm,
                    passwd, nonce /* use own nonce */,
                    cnonce, NONCE_COUNT_VALUE, authzidBytes);

            } catch (NoSuchAlgorithmException e) {
                throw new SaslException(
                    "DIGEST-MD5: problem duplicating client response", e);
            } catch (IOException e) {
                throw new SaslException(
                    "DIGEST-MD5: problem duplicating client response", e);
            }

            if (!Arrays.equals(responseFromClient, expectedResponse)) {
                throw new SaslException("DIGEST-MD5: digest response format " +
                    "violation. Mismatched response.");
            }

            // Ensure that authzid mapping is OK
            try {
                AuthorizeCallback acb =
                    new AuthorizeCallback(username, authzidFromClient);
                cbh.handle(new Callback[]{acb});

                if (acb.isAuthorized()) {
                    authzid = acb.getAuthorizedID();
                } else {
                    throw new SaslException("DIGEST-MD5: " + username +
                        " is not authorized to act as " + authzidFromClient);
                }
            } catch (SaslException e) {
                throw e;
            } catch (UnsupportedCallbackException e) {
                throw new SaslException(
                    "DIGEST-MD5: Cannot perform callback to check authzid", e);
            } catch (IOException e) {
                throw new SaslException(
                    "DIGEST-MD5: IO error checking authzid", e);
            }

            return generateResponseAuth(username, passwd, cnonce,
                NONCE_COUNT_VALUE, authzidBytes);
        } finally {
            // Clear password
            for (int i = 0; i < passwd.length; i++) {
                passwd[i] = 0;
            }
        }
    }

    private static boolean uriMatches(String thisUri, String incomingUri) {
        // Full match
        if (thisUri.equalsIgnoreCase(incomingUri)) {
            return true;
        }
        // Unbound match
        if (thisUri.endsWith("/*")) {
            int protoAndSlash = thisUri.length() - 1;
            String thisProtoAndSlash = thisUri.substring(0, protoAndSlash);
            String incomingProtoAndSlash = incomingUri.substring(0, protoAndSlash);
            return thisProtoAndSlash.equalsIgnoreCase(incomingProtoAndSlash);
        }
        return false;
    }

    /**
     * Server sends a message formatted as follows:
     *    response-auth = "rspauth" "=" response-value
     *   where response-value is calculated as above, using the values sent in
     *   step two, except that if qop is "auth", then A2 is
     *
     *       A2 = { ":", digest-uri-value }
     *
     *   And if qop is "auth-int" or "auth-conf" then A2 is
     *
     *       A2 = { ":", digest-uri-value, ":00000000000000000000000000000000" }
     *
     * Clears password afterwards.
     */
    private byte[] generateResponseAuth(String username, char[] passwd,
        byte[] cnonce, int nonceCount, byte[] authzidBytes) throws SaslException {

        // Construct response value

        try {
            byte[] responseValue = generateResponseValue("",
                digestUri, negotiatedQop, username, negotiatedRealm,
                passwd, nonce, cnonce, nonceCount, authzidBytes);

            byte[] challenge = new byte[responseValue.length + 8];
            System.arraycopy("rspauth=".getBytes(encoding), 0, challenge, 0, 8);
            System.arraycopy(responseValue, 0, challenge, 8,
                responseValue.length );

            return challenge;

        } catch (NoSuchAlgorithmException e) {
            throw new SaslException("DIGEST-MD5: problem generating response", e);
        } catch (IOException e) {
            throw new SaslException("DIGEST-MD5: problem generating response", e);
        }
    }

    public String getAuthorizationID() {
        if (completed) {
            return authzid;
        } else {
            throw new IllegalStateException(
                "DIGEST-MD5 server negotiation not complete");
        }
    }
}

com/sun/security/sasl/digest/DigestMD5Server.java

 

Or download all of them as a single archive file:

File name: java.security.sasl-11.0.1-src.zip
File size: 76634 bytes
Release date: 2018-11-04
Download 

 

JDK 11 java.smartcardio.jmod - Smart Card IO Module

JDK 11 java.security.jgss.jmod - Security JGSS Module

Download and Use JDK 11

⇑⇑ FAQ for JDK (Java Development Kit)

2020-09-15, 12009👍, 0💬