private String createDigestResponse(final HttpRequest request) throws AuthenticationException {
        if (credentials == null) {
            throw new AuthenticationException("User credentials have not been provided");
        }
        final String uri = request.getRequestUri();
        final String method = request.getMethod();
        final String realm = this.paramMap.get("realm");
        final String nonce = this.paramMap.get("nonce");
        final String opaque = this.paramMap.get("opaque");
        final String algorithm = this.paramMap.get("algorithm");

        final Set<String> qopset = new HashSet<>(8);
        QualityOfProtection qop = QualityOfProtection.UNKNOWN;
        final String qoplist = this.paramMap.get("qop");
        if (qoplist != null) {
            final StringTokenizer tok = new StringTokenizer(qoplist, ",");
            while (tok.hasMoreTokens()) {
                final String variant = tok.nextToken().trim();
                qopset.add(variant.toLowerCase(Locale.ROOT));
            }
            final HttpEntity entity = request instanceof ClassicHttpRequest ? ((ClassicHttpRequest) request).getEntity() : null;
            if (entity != null && qopset.contains("auth-int")) {
                qop = QualityOfProtection.AUTH_INT;
            } else if (qopset.contains("auth")) {
                qop = QualityOfProtection.AUTH;
            } else if (qopset.contains("auth-int")) {
                qop = QualityOfProtection.AUTH_INT;
            }
        } else {
            qop = QualityOfProtection.MISSING;
        }

        if (qop == QualityOfProtection.UNKNOWN) {
            throw new AuthenticationException("None of the qop methods is supported: " + qoplist);
        }

        final Charset charset = AuthSchemeSupport.parseCharset(paramMap.get("charset"), defaultCharset);

        // If an algorithm is not specified, default to MD5.

        DigestAlgorithm digAlg = null;

        final MessageDigest digester;
        try {
            digAlg = DigestAlgorithm.fromString(algorithm == null ? "MD5" : algorithm);
            digester = createMessageDigest(digAlg.getBaseAlgorithm());
        } catch (final UnsupportedDigestAlgorithmException ex) {
            throw new AuthenticationException("Unsupported digest algorithm: " + digAlg);
        }

        if (nonce.equals(this.lastNonce)) {
            nounceCount++;
        } else {
            nounceCount = 1;
            cnonce = null;
            lastNonce = nonce;
        }

        final StringBuilder sb = new StringBuilder(8);
        try (final Formatter formatter = new Formatter(sb, Locale.ROOT)) {
            formatter.format("%08x", nounceCount);
        }
        final String nc = sb.toString();

        if (cnonce == null) {
            cnonce = formatHex(createCnonce(digAlg));
        }

        if (buffer == null) {
            buffer = new ByteArrayBuilder(128);
        } else {
            buffer.reset();
        }
        buffer.charset(charset);

        a1 = null;
        a2 = null;


        // Extract username and username*
        String username = credentials.getUserName();
        String encodedUsername = null;
        // Check if 'username' has invalid characters and use 'username*'
        if (username != null && containsInvalidABNFChars(username)) {
            encodedUsername = "UTF-8''" + PercentCodec.RFC5987.encode(username);
        }

        final String usernameForDigest;
        if (this.userhashSupported) {
            final String usernameRealm = username + ":" + realm;
            final byte[] hashedBytes = digester.digest(usernameRealm.getBytes(StandardCharsets.UTF_8));
            usernameForDigest = formatHex(hashedBytes); // Use hashed username for digest
            username = usernameForDigest;
        } else if (encodedUsername != null) {
            usernameForDigest = encodedUsername; // Use encoded username for digest
        } else {
            usernameForDigest = username; // Use regular username for digest
        }

        // 3.2.2.2: Calculating digest
        if (digAlg.isSessionBased()) {
            // H( unq(username-value) ":" unq(realm-value) ":" passwd )
            //      ":" unq(nonce-value)
            //      ":" unq(cnonce-value)

            // calculated one per session
            buffer.append(username).append(":").append(realm).append(":").append(credentials.getUserPassword());
            final String checksum = formatHex(digester.digest(this.buffer.toByteArray()));
            buffer.reset();
            buffer.append(checksum).append(":").append(nonce).append(":").append(cnonce);
        } else {
            // unq(username-value) ":" unq(realm-value) ":" passwd
            buffer.append(username).append(":").append(realm).append(":").append(credentials.getUserPassword());
        }
        a1 = buffer.toByteArray();

        final String hasha1 = formatHex(digester.digest(a1));
        buffer.reset();

        if (qop == QualityOfProtection.AUTH) {
            // Method ":" digest-uri-value
            a2 = buffer.append(method).append(":").append(uri).toByteArray();
        } else if (qop == QualityOfProtection.AUTH_INT) {
            // Method ":" digest-uri-value ":" H(entity-body)
            final HttpEntity entity = request instanceof ClassicHttpRequest ? ((ClassicHttpRequest) request).getEntity() : null;
            if (entity != null && !entity.isRepeatable()) {
                // If the entity is not repeatable, try falling back onto QOP_AUTH
                if (qopset.contains("auth")) {
                    qop = QualityOfProtection.AUTH;
                    a2 = buffer.append(method).append(":").append(uri).toByteArray();
                } 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 = buffer.append(method).append(":").append(uri)
                        .append(":").append(formatHex(entityDigester.getDigest())).toByteArray();
            }
        } else {
            a2 = buffer.append(method).append(":").append(uri).toByteArray();
        }

        final String hasha2 = formatHex(digester.digest(a2));
        buffer.reset();

        // 3.2.2.1

        final byte[] digestInput;
        if (qop == QualityOfProtection.MISSING) {
            buffer.append(hasha1).append(":").append(nonce).append(":").append(hasha2);
        } else {
            buffer.append(hasha1).append(":").append(nonce).append(":").append(nc).append(":")
                .append(cnonce).append(":").append(qop == QualityOfProtection.AUTH_INT ? "auth-int" : "auth")
                .append(":").append(hasha2);
        }
        digestInput = buffer.toByteArray();
        buffer.reset();

        final String digest = formatHex(digester.digest(digestInput));

        final CharArrayBuffer buffer = new CharArrayBuffer(128);
        buffer.append(StandardAuthScheme.DIGEST + " ");

        final List<BasicNameValuePair> params = new ArrayList<>(20);
        if (this.userhashSupported) {
            // Use hashed username for the 'username' parameter
            params.add(new BasicNameValuePair("username", usernameForDigest));
            params.add(new BasicNameValuePair("userhash", "true"));
        } else if (encodedUsername != null) {
            // Use encoded 'username*' parameter
            params.add(new BasicNameValuePair("username*", encodedUsername));
        } else {
            // Use regular 'username' parameter
            params.add(new BasicNameValuePair("username", username));
        }
        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 != QualityOfProtection.MISSING) {
            params.add(new BasicNameValuePair("qop", qop == QualityOfProtection.AUTH_INT ? "auth-int" : "auth"));
            params.add(new BasicNameValuePair("nc", nc));
            params.add(new BasicNameValuePair("cnonce", cnonce));
            params.add(new BasicNameValuePair("rspauth", hasha2));
        }
        if (algorithm != null) {
            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 buffer.toString();
    }
@deprecated This constructor is deprecated to enforce the use of {@link StandardCharsets#UTF_8} encoding
in compliance with RFC 7616 for HTTP Digest Access Authentication. Use the default constructor {@link #DigestScheme()} instead.

@param charset the {@link Charset} set to be used for encoding credentials. This parameter is ignored as UTF-8 is always used.
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/DigestScheme.java