public void challenge(HttpServletRequest req, HttpServletResponse resp) {

    boolean stale = false;

    if (this.maintainState) {

      String authHeader = req.getHeader("Authorization");
      if (authHeader != null) {

        String headerFields = authHeader.substring("Digest: ".length() - 1);

        String nonce = HttpUtil.extractHeaderField(headerFields, "nonce");
        String opaque = HttpUtil.extractHeaderField(headerFields, "opaque");
        if (nonce != null && opaque != null) {
          StateEntry entry = (StateEntry) this.stateMap.remove(nonce + ":" + opaque);
          if (entry != null) {
            stale = entry.isStale();
          }
        }
      }
    }

    String nonce = generateNonce();
    String opaque = generateOpaque();

    // FIXME: Why is getRealm() deprecated?
    String challengeHeader = "Digest realm=\"" + this.principalStore.getRealm() + "\"";
    challengeHeader += ", nonce=\"" + nonce + "\"";
    challengeHeader += ", opaque=\"" + opaque + "\"";
    challengeHeader += ", stale=" + stale;
    challengeHeader += ", algorithm=MD5";
    challengeHeader += ", qop=auth";

    if (this.maintainState) {

      Date timestamp = new Date();
      StateEntry entry = new StateEntry(null, timestamp, nonce, 1, opaque);
      this.stateMap.put(nonce + ":" + opaque, entry);
    }

    try {
      resp.setHeader("WWW-Authenticate", challengeHeader);
      resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    } catch (IOException e) {
      throw new AuthenticationProcessingException(
          "Unable to present authentication challenge to user", e);
    }
  }
  public boolean postAuthentication(HttpServletRequest req, HttpServletResponse resp) {

    String authHeader = req.getHeader("Authorization");
    if (authHeader == null) {
      return false;
    }

    String headerFields = authHeader.substring("Digest: ".length() - 1);

    String nonce = HttpUtil.extractHeaderField(headerFields, "nonce");
    String opaque = HttpUtil.extractHeaderField(headerFields, "opaque");
    if (nonce == null || opaque == null) {
      return false;
    }

    Principal principal = SecurityContext.getSecurityContext().getPrincipal();
    if (principal == null) {
      return false;
    }

    if (this.maintainState) {

      StateEntry entry = (StateEntry) this.stateMap.remove(nonce + ":" + opaque);
      if (entry == null) {
        return false;
      }
      Date timestamp = new Date();
      String nextNonce = this.generateNonce();
      entry.setUsername(principal.getQualifiedName());
      entry.setNonce(nextNonce);
      entry.setTimestamp(timestamp);
      entry.setNonceCount(entry.getNonceCount() + 1);
      entry.setStale(false);

      this.stateMap.put(nextNonce + ":" + opaque, entry);
      resp.addHeader("Authentication-Info", "nextnonce=" + nextNonce);
    }

    return false;
  }
  private AuthResult doAuthenticate(
      HttpServletRequest request,
      String uri,
      String response,
      String nc,
      String nonce,
      String cnonce,
      String qop,
      String username,
      String opaque) {

    StateEntry stateEntry = null;

    if (this.maintainState) {
      stateEntry = this.stateMap.get(nonce + ":" + opaque);

      if (stateEntry != null) {

        if (!stateEntry.getNonce().equals(nonce)) {
          stateEntry.setStale(true);
          throw new AuthenticationException(
              "Authentication header field 'nonce': "
                  + nonce
                  + " does not match expected value: "
                  + stateEntry.getNonce());
        }

        try {
          long nonceCount = Long.parseLong(nc, 16);
          if (nonceCount != stateEntry.getNonceCount()) {
            throw new AuthenticationException(
                "Authentication header field 'nc', value "
                    + nc
                    + " did not match expected value "
                    + stateEntry.getNonceCount());
          }
        } catch (NumberFormatException e) {
          throw new InvalidAuthenticationRequestException(
              "Authentication header field 'nc' was not a hex number: " + nc);
        }
      }
    }

    Principal principal = principalFactory.getPrincipal(username, Principal.Type.USER);
    if (principal == null) {
      throw new AuthenticationException(
          "Unable to authenticate principal using HTTP/Digest for request"
              + request
              + ": no principal found");
    }

    if (!this.principalStore.validatePrincipal(principal)) {
      throw new AuthenticationException("Unknown principal in HTTP/Digest request: " + principal);
    }

    String componentA1 = this.principalStore.getMD5HashString(principal);

    if (componentA1 == null) {
      throw new AuthenticationException("Password hash for principal " + principal + " not found");
    }

    String componentA2 = MD5.md5sum(request.getMethod() + ":" + uri);

    StringBuffer b = new StringBuffer();
    b.append(componentA1);

    b.append(":").append(nonce);
    if (nc != null) b.append(":").append(nc);
    if (cnonce != null) b.append(":").append(cnonce);
    if (qop != null) b.append(":").append(qop);
    b.append(":").append(componentA2);

    String serverDigest = MD5.md5sum(b.toString());

    if (this.logger.isDebugEnabled()) {
      this.logger.debug("client digest: '" + response + "', server digest: '" + serverDigest + "'");
    }

    if (!serverDigest.equals(response)) {
      throw new AuthenticationException(
          "Authentication failure for principal " + principal + " (incorrect password)");
    }

    if (this.maintainState && stateEntry == null) {

      stateEntry = new StateEntry(null, new Date(), nonce, 1, opaque);
      stateEntry.setStale(true);
      this.stateMap.put(nonce + ":" + opaque, stateEntry);
      throw new AuthenticationException("Nothing known about request  " + request);

    } else if (this.maintainState && stateEntry != null && stateEntry.isStale()) {
      throw new AuthenticationException(
          "Stale nonce header field in authentication request: " + request);
    }

    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Successfully authenticated principal " + principal);
    }
    return new AuthResult(principal.getQualifiedName());
  }