You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2026/06/10 08:43:19 UTC

(jena) branch main updated (42a439f3e3 -> 1034f3ee9b)

This is an automated email from the ASF dual-hosted git repository.

afs pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git


    from 42a439f3e3 Bump concurrently from 9.2.1 to 10.0.3 in /jena-fuseki2/jena-fuseki-ui
     new f9f5a85567 Bump the maven-patch-group group across 1 directory with 2 updates
     new 1034f3ee9b GH-3977: Support SHA-256 for digest authentication

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../main/java/org/apache/jena/http/HttpLib.java    |   6 +-
 .../org/apache/jena/http/auth/AuthChallenge.java   |   6 +-
 .../java/org/apache/jena/http/auth/AuthHttp.java   |  51 ++++---
 .../java/org/apache/jena/http/auth/AuthLib.java    |  12 +-
 .../org/apache/jena/http/auth/DigestAlgorithm.java |  57 ++++++++
 .../java/org/apache/jena/http/auth/DigestLib.java  |  29 +++-
 .../org/apache/jena/http/auth/TestAuthDigest.java  | 159 ++++++++++++++++++++-
 .../jena/http/auth/TestAuthHeaderParser.java       |   5 -
 .../jena/fuseki/main/auth/AuthBearerFilter.java    |   4 +
 pom.xml                                            |   2 +-
 10 files changed, 288 insertions(+), 43 deletions(-)
 create mode 100644 jena-arq/src/main/java/org/apache/jena/http/auth/DigestAlgorithm.java


(jena) 01/02: Bump the maven-patch-group group across 1 directory with 2 updates

Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

afs pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit f9f5a855673cfb4d351f29d63e0ad48e0d6b4378
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Mon Jun 8 14:16:42 2026 +0000

    Bump the maven-patch-group group across 1 directory with 2 updates
    
    Bumps the maven-patch-group group with 2 updates in the / directory: [org.eclipse.jetty:jetty-bom](https://github.com/jetty/jetty.project) and [org.eclipse.jetty.ee11:jetty-ee11-bom](https://github.com/jetty/jetty.project).
    
    
    Updates `org.eclipse.jetty:jetty-bom` from 12.1.9 to 12.1.10
    - [Release notes](https://github.com/jetty/jetty.project/releases)
    - [Commits](https://github.com/jetty/jetty.project/compare/jetty-12.1.9...jetty-12.1.10)
    
    Updates `org.eclipse.jetty.ee11:jetty-ee11-bom` from 12.1.9 to 12.1.10
    - [Release notes](https://github.com/jetty/jetty.project/releases)
    - [Commits](https://github.com/jetty/jetty.project/compare/jetty-12.1.9...jetty-12.1.10)
    
    Updates `org.eclipse.jetty.ee11:jetty-ee11-bom` from 12.1.9 to 12.1.10
    - [Release notes](https://github.com/jetty/jetty.project/releases)
    - [Commits](https://github.com/jetty/jetty.project/compare/jetty-12.1.9...jetty-12.1.10)
    
    ---
    updated-dependencies:
    - dependency-name: org.eclipse.jetty:jetty-bom
      dependency-version: 12.1.10
      dependency-type: direct:production
      update-type: version-update:semver-patch
      dependency-group: maven-patch-group
    - dependency-name: org.eclipse.jetty.ee11:jetty-ee11-bom
      dependency-version: 12.1.10
      dependency-type: direct:production
      update-type: version-update:semver-patch
      dependency-group: maven-patch-group
    - dependency-name: org.eclipse.jetty.ee11:jetty-ee11-bom
      dependency-version: 12.1.10
      dependency-type: direct:production
      update-type: version-update:semver-patch
      dependency-group: maven-patch-group
    ...
    
    Signed-off-by: dependabot[bot] <su...@github.com>
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index b2285e0601..fdd1c79b9a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,7 +71,7 @@
     <ver.jakarta.json>2.0.1</ver.jakarta.json>
 
     <!-- These two must be in-step -->
-    <ver.jetty>12.1.9</ver.jetty>
+    <ver.jetty>12.1.10</ver.jetty>
     <ver.jakarta-servlet>6.1.0</ver.jakarta-servlet>
 
     <!--


(jena) 02/02: GH-3977: Support SHA-256 for digest authentication

Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

afs pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit 1034f3ee9b9f147d7bc0d481892ee546acf08a48
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Mon Jun 8 20:38:26 2026 +0100

    GH-3977: Support SHA-256 for digest authentication
---
 .../main/java/org/apache/jena/http/HttpLib.java    |   6 +-
 .../org/apache/jena/http/auth/AuthChallenge.java   |   6 +-
 .../java/org/apache/jena/http/auth/AuthHttp.java   |  51 ++++---
 .../java/org/apache/jena/http/auth/AuthLib.java    |  12 +-
 .../org/apache/jena/http/auth/DigestAlgorithm.java |  57 ++++++++
 .../java/org/apache/jena/http/auth/DigestLib.java  |  29 +++-
 .../org/apache/jena/http/auth/TestAuthDigest.java  | 159 ++++++++++++++++++++-
 .../jena/http/auth/TestAuthHeaderParser.java       |   5 -
 .../jena/fuseki/main/auth/AuthBearerFilter.java    |   4 +
 9 files changed, 287 insertions(+), 42 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/http/HttpLib.java b/jena-arq/src/main/java/org/apache/jena/http/HttpLib.java
index 422978e154..58cd087d5f 100644
--- a/jena-arq/src/main/java/org/apache/jena/http/HttpLib.java
+++ b/jena-arq/src/main/java/org/apache/jena/http/HttpLib.java
@@ -445,7 +445,11 @@ public class HttpLib {
         String path = uri.getRawPath();
         if ( path == null || path.isEmpty() )
             path = "/";
-        return path;
+        String target = path;
+        String qs = uri.getRawQuery();
+        if ( qs != null )
+            target = target+"?"+qs;
+        return target;
     }
 
     /** Return a HttpRequest */
diff --git a/jena-arq/src/main/java/org/apache/jena/http/auth/AuthChallenge.java b/jena-arq/src/main/java/org/apache/jena/http/auth/AuthChallenge.java
index 41e2fb9072..2a3e37a10c 100644
--- a/jena-arq/src/main/java/org/apache/jena/http/auth/AuthChallenge.java
+++ b/jena-arq/src/main/java/org/apache/jena/http/auth/AuthChallenge.java
@@ -38,6 +38,8 @@ public class AuthChallenge {
     public final String nonce;
     public final String opaque;
     public final String qop;
+    public final String algorithm;
+
     // May be null;
     public final Map<String, String> authParams;
 
@@ -80,13 +82,14 @@ public class AuthChallenge {
                                      get(auth, AuthHttp.strNonce), // Required for digest, not for basic.
                                      get(auth, AuthHttp.strOpaque),
                                      get(auth, AuthHttp.strQop),
+                                     get(auth, AuthHttp.strAlgorithm),
                                      auth.getAuthParams());
         } catch (NullPointerException ex) {
             return null;
         }
     }
 
-    private AuthChallenge(AuthScheme authScheme, AuthHeader authHeader, String realm, String nonce, String opaque, String qop, Map<String, String> authParams) {
+    private AuthChallenge(AuthScheme authScheme, AuthHeader authHeader, String realm, String nonce, String opaque, String qop, String algorithm, Map<String, String> authParams) {
         Objects.requireNonNull(authScheme);
         Objects.requireNonNull(authHeader);
         this.authScheme = authScheme;
@@ -95,6 +98,7 @@ public class AuthChallenge {
         this.nonce = nonce;
         this.opaque = opaque;
         this.qop = qop;
+        this.algorithm = algorithm;
         this.authParams = authParams;
     }
 
diff --git a/jena-arq/src/main/java/org/apache/jena/http/auth/AuthHttp.java b/jena-arq/src/main/java/org/apache/jena/http/auth/AuthHttp.java
index d2bc4dbbb9..54fee56752 100644
--- a/jena-arq/src/main/java/org/apache/jena/http/auth/AuthHttp.java
+++ b/jena-arq/src/main/java/org/apache/jena/http/auth/AuthHttp.java
@@ -23,33 +23,40 @@ package org.apache.jena.http.auth;
 
 import java.util.Objects ;
 
-import org.apache.commons.codec.digest.DigestUtils ;
-
-/** Constants and operations from RFC 2617 (digest, now RFC 7616; basic, now RFC 7617), using MD5 (the default) */
+/**
+ * Constants and operations from RFC 2617 (digest, now RFC 7616; basic, now RFC 7617)
+ * Original digest authentication: RFC 2069
+ */
 class AuthHttp {
-    public static String strUsername = "username";
-    public static String strRealm    = "realm";
-    public static String strNonce    = "nonce";
-    public static String strNc       = "nc";
-    public static String strCNonce   = "cnonce";
-    public static String strQop      = "qop";
-    public static String strResponse = "response";
-    public static String strOpaque   = "opaque";
-    public static String strUri      = "uri";
+    // Field names
+    public static final String strUsername      = "username";
+    public static final String strRealm         = "realm";
+    public static final String strNonce         = "nonce";
+    public static final String strNc            = "nc";
+    public static final String strCNonce        = "cnonce";
+    public static final String strQop           = "qop";
+    public static final String strResponse      = "response";
+    public static final String strOpaque        = "opaque";
+    public static final String strUri           = "uri";
+    public static final String strAlgorithm     = "algorithm";
 
-    public static String KD(String data) {
-        return H(data) ;
-    }
+    // Algorithm names.
+    public static final String algortihmSHA256           = "SHA-256";
+    public static final String algortihmSHA256_sess      = "SHA-256-sess";
+    public static final String algortihmSHA512_256       = "SHA-512-256";
+    public static final String algortihmSHA512_256_sess  = "SHA-512-256-sess";
+    public static final String algortihmMD5              = "MD5";
+    public static final String algortihmMD5_sess         = "MD5-sess";
 
-    public static String KD(String secret, String data) {
-        return H(secret+":"+data) ;
+    public static String KD(String secret, String data, DigestAlgorithm digestAlgorithm) {
+        return H(secret+":"+data, digestAlgorithm) ;
     }
 
-    public static String H(String string) {
-        return DigestUtils.md5Hex(string) ;
+    public static String H(String string, DigestAlgorithm algorithm) {
+        return algorithm.digest.apply(string);
     }
 
-    public static String A1_MD5(String username, String realm, String password) {
+    public static String A1(String username, String realm, String password, DigestAlgorithm digestAlgorithm) {
         Objects.requireNonNull(username) ;
         Objects.requireNonNull(realm) ;
         Objects.requireNonNull(password) ;
@@ -57,14 +64,14 @@ class AuthHttp {
         return s ;
     }
 
-    public static String A1_MD5_sess(String username, String realm, String password, String nonce, String cnonce) {
+    public static String A1_sess(String username, String realm, String password, String nonce, String cnonce, DigestAlgorithm digestAlgorithm) {
         Objects.requireNonNull(username) ;
         Objects.requireNonNull(realm) ;
         Objects.requireNonNull(password) ;
         Objects.requireNonNull(nonce) ;
         Objects.requireNonNull(cnonce) ;
         String s = username+":"+realm+":"+password ;
-        String x = H(s)+":"+nonce+":"+cnonce ;
+        String x = H(s, digestAlgorithm)+":"+nonce+":"+cnonce ;
         return s ;
     }
 
diff --git a/jena-arq/src/main/java/org/apache/jena/http/auth/AuthLib.java b/jena-arq/src/main/java/org/apache/jena/http/auth/AuthLib.java
index 14a2ac0b75..16a1b28cc9 100644
--- a/jena-arq/src/main/java/org/apache/jena/http/auth/AuthLib.java
+++ b/jena-arq/src/main/java/org/apache/jena/http/auth/AuthLib.java
@@ -83,9 +83,9 @@ public class AuthLib {
 
     /* Handle a 401 (authentication challenge). */
     private static <T> CompletableFuture<HttpResponse<T>> handle401Async(HttpClient httpClient,
-                                                 HttpRequest request,
-                                                 BodyHandler<T> bodyHandler,
-                                                 HttpResponse<T> httpResponse401) {
+                                                                         HttpRequest request,
+                                                                         BodyHandler<T> bodyHandler,
+                                                                         HttpResponse<T> httpResponse401) {
         AuthChallenge aHeader = wwwAuthenticateHeader(httpResponse401);
         if ( aHeader == null )
             // No valid header - simply return the original response.
@@ -103,7 +103,6 @@ public class AuthLib {
                 throw HttpException.create(httpResponse401);
         }
 
-        // Request target - no query string.
         AuthRequestModifier authRequestModifier;
         switch (aHeader.authScheme) {
             case BASIC :
@@ -113,6 +112,8 @@ public class AuthLib {
                 String requestTarget = HttpLib.requestTargetServer(request.uri());
                 authRequestModifier = digestAuthModifier(aHeader, passwordRecord.getUsername(), passwordRecord.getPassword(),
                                                          request.method(), requestTarget);
+                if ( authRequestModifier == null )
+                    throw HttpException.error("Unrecognized digest algorithm");
                 break;
             }
             case BEARER : {
@@ -154,7 +155,6 @@ public class AuthLib {
             return null;
         // Choose first digest or bearer, else the first basic. Prefer digest or bearer to basic.
         AuthChallenge aHeader = null;
-        String result = null;
         for ( String headerValue : headers ) {
             AuthChallenge aHeader2 = AuthChallenge.parse(headerValue);
             if ( aHeader2 == null ) {
@@ -163,7 +163,7 @@ public class AuthLib {
             }
             AuthScheme authScheme = aHeader2.authScheme;
             switch(authScheme) {
-                case  DIGEST :
+                case DIGEST :
                     return aHeader2;
                 case BASIC:
                     if ( aHeader == null )
diff --git a/jena-arq/src/main/java/org/apache/jena/http/auth/DigestAlgorithm.java b/jena-arq/src/main/java/org/apache/jena/http/auth/DigestAlgorithm.java
new file mode 100644
index 0000000000..c320d9be17
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/http/auth/DigestAlgorithm.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.http.auth;
+
+import static org.apache.jena.http.auth.AuthHttp.*;
+import java.util.function.Function;
+
+import org.apache.commons.codec.digest.DigestUtils;
+
+/** Algorithms for digest authentication. */
+public enum DigestAlgorithm {
+    SHA_256(algortihmSHA256, DigestUtils::sha256Hex),
+    SHA_256_sess(algortihmSHA256_sess, DigestUtils::sha256Hex),
+    SHA_512_256(algortihmSHA512_256, DigestUtils::sha512_256Hex),
+    SHA_512_256_sess(algortihmSHA512_256_sess, DigestUtils::sha512_256Hex),
+    MD5(algortihmMD5, DigestUtils::md5Hex),
+    MD5_sess(algortihmMD5, DigestUtils::md5Hex)
+    ;
+    public final String algorithmName;
+    public final Function<String, String> digest;
+
+    private DigestAlgorithm(String algorithmName, Function<String, String> digest) {
+        this.algorithmName = algorithmName ;
+        this.digest = digest;
+    }
+
+    /** Algothm name to enum, or null (unecognized) */
+    public static DigestAlgorithm fromString(String name) {
+        return switch(name) {
+            case algortihmSHA256 -> SHA_256;
+            case algortihmSHA256_sess -> SHA_256_sess;
+            case algortihmSHA512_256 -> SHA_512_256;
+            case algortihmSHA512_256_sess -> SHA_512_256_sess;
+            case algortihmMD5 -> MD5;
+            case algortihmMD5_sess -> MD5_sess;
+            default -> null;
+        };
+    }
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/http/auth/DigestLib.java b/jena-arq/src/main/java/org/apache/jena/http/auth/DigestLib.java
index 654ce8e6ea..50243a77fa 100644
--- a/jena-arq/src/main/java/org/apache/jena/http/auth/DigestLib.java
+++ b/jena-arq/src/main/java/org/apache/jena/http/auth/DigestLib.java
@@ -21,7 +21,7 @@
 
 package org.apache.jena.http.auth;
 
-import static org.apache.jena.http.auth.AuthHttp.A1_MD5;
+import static org.apache.jena.http.auth.AuthHttp.A1;
 import static org.apache.jena.http.auth.AuthHttp.A2_auth;
 import static org.apache.jena.http.auth.AuthHttp.H;
 import static org.apache.jena.http.auth.AuthHttp.KD;
@@ -47,21 +47,34 @@ class DigestLib {
                                                      String username, String password,
                                                      String method, String requestTarget,
                                                      String cnonce, String nc, String authType) {
-        String a1 = A1_MD5(username, auth.realm, password) ;
+        DigestAlgorithm dAlgo = getAlgorithm(auth);
+        if ( dAlgo == null )
+            // Unrecognized algorithm name.
+            return null;
+        String a1 = A1(username, auth.realm, password, dAlgo) ;
         if ( auth.qop == null ) {
             // RFC 2069
             // Firefox seems to prefer this form??
-            return KD(H(a1), auth.nonce+":"+H(A2_auth(method, requestTarget))) ;
+            return KD(H(a1, dAlgo), auth.nonce+":"+H(A2_auth(method, requestTarget), dAlgo), dAlgo) ;
         }
         else {
             Objects.nonNull(cnonce) ;
             Objects.nonNull(nc) ;
-            return KD(H(a1),
-                      auth.nonce+":"+nc+":"+cnonce+":"+authType+":"+H(A2_auth(method, requestTarget))
+            return KD(H(a1, dAlgo),
+                      auth.nonce+":"+nc+":"+cnonce+":"+authType+":"+H(A2_auth(method, requestTarget), dAlgo),
+                      dAlgo
                     ) ;
         }
     }
 
+    public static DigestAlgorithm getAlgorithm(AuthChallenge challenge) {
+        if ( challenge.algorithm == null )
+            // Default by RFC 7616
+            return DigestAlgorithm.MD5;
+        // null means ignore.
+        return DigestAlgorithm.fromString(challenge.algorithm);
+    }
+
     private static Random nonceGenerator = new SecureRandom();
 
     /**
@@ -105,10 +118,16 @@ class DigestLib {
             String responseField = calcDigestChallengeResponse(aHeader, user, password,
                                                                method, requestTarget,
                                                                clientNonce, nc, "auth");
+            if ( responseField == null ) {
+                return null;
+                // Unrecognized algorithm. RFC says "ignore request"
+            }
+
             StringBuilder stringBuilder = new StringBuilder();
             stringBuilder.append("Digest ");
             field(stringBuilder, true, "username", user, true);
             field(stringBuilder, false, "realm", aHeader.realm, true);
+            field(stringBuilder, false, "algorithm", aHeader.algorithm, false);
             field(stringBuilder, false, "nonce", aHeader.nonce, true);
             field(stringBuilder, false, "uri", requestTarget, true);
             field(stringBuilder, false, "qop", "auth", false);
diff --git a/jena-arq/src/test/java/org/apache/jena/http/auth/TestAuthDigest.java b/jena-arq/src/test/java/org/apache/jena/http/auth/TestAuthDigest.java
index 9ca17e770b..eb02b7bebd 100644
--- a/jena-arq/src/test/java/org/apache/jena/http/auth/TestAuthDigest.java
+++ b/jena-arq/src/test/java/org/apache/jena/http/auth/TestAuthDigest.java
@@ -22,13 +22,21 @@
 package org.apache.jena.http.auth;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
 import org.junit.jupiter.api.Test;
 
 public class TestAuthDigest {
-    // Example from RFC 2617
+    // Examples from RFC 2617
+
+    // digest 1
+    // No algorithm - use MD5
+    // MD5:          6629fae49393a05397450978507c4ef1
+    // SHA-256:      5abdd07184ba512a22c53f41470e5eea7dcaa3a93a59b630c13dfe0a5dc6e38b
+    // SHA-512-256:  f23c08ec7334a881f8286e68450ddbd9f0cd91c41481f0e1433604da8113c6dc
+
     @Test
-    public void digest_1() {
+    public void digest_1_no_algorithm() {
         String header = "Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\","+
                 " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","+
                 " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"";
@@ -49,7 +57,84 @@ public class TestAuthDigest {
         assertEquals("6629fae49393a05397450978507c4ef1", responseField);
     }
 
+
+    @Test
+    public void digest_1_md5() {
+        String header = "Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\","+
+                " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","+
+                " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"" +
+                " algorithm=MD5";
+        AuthChallenge aHeader = AuthChallenge.parse(header);
+
+        String requestTarget = "/dir/index.html";
+        String method = "GET";
+        String cnonce = "0a4f113b";
+        String nc = "00000001";
+        String username = "Mufasa";
+        String password = "Circle Of Life";
+
+        String responseField =
+                DigestLib.calcDigestChallengeResponse(aHeader, username, password,
+                                                      method, requestTarget,
+                                                      cnonce, nc, "auth");
+
+        assertEquals("6629fae49393a05397450978507c4ef1", responseField);
+    }
+
+    @Test
+    public void digest_1_sha256() {
+        String header = "Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\","+
+                " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","+
+                " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""+
+                " algorithm=SHA-256";
+        AuthChallenge aHeader = AuthChallenge.parse(header);
+
+        String requestTarget = "/dir/index.html";
+        String method = "GET";
+        String cnonce = "0a4f113b";
+        String nc = "00000001";
+        String username = "Mufasa";
+        String password = "Circle Of Life";
+
+        String responseField =
+                DigestLib.calcDigestChallengeResponse(aHeader, username, password,
+                                                      method, requestTarget,
+                                                      cnonce, nc, "auth");
+
+        assertEquals("5abdd07184ba512a22c53f41470e5eea7dcaa3a93a59b630c13dfe0a5dc6e38b", responseField);
+    }
+
+    @Test
+    public void digest_1_sha512_256() {
+        String header = "Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\","+
+                " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","+
+                " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""+
+                " algorithm=SHA-512-256";
+        AuthChallenge aHeader = AuthChallenge.parse(header);
+
+        String requestTarget = "/dir/index.html";
+        String method = "GET";
+        String cnonce = "0a4f113b";
+        String nc = "00000001";
+        String username = "Mufasa";
+        String password = "Circle Of Life";
+
+        String responseField =
+                DigestLib.calcDigestChallengeResponse(aHeader, username, password,
+                                                      method, requestTarget,
+                                                      cnonce, nc, "auth");
+
+        assertEquals("f23c08ec7334a881f8286e68450ddbd9f0cd91c41481f0e1433604da8113c6dc", responseField);
+    }
+
+
     // Example from RFC 7616
+
+    // digest 2
+    // MD5:         8ca523f5e9506fed4657c9700eebdbec
+    // SHA-256      753927fa0e85d155564e2e272a28d1802ca10daf4496794697cf8db5856cb6c1
+    // SHA-512-256: 430d05014cecc49cab6fbe03176d41a1da86cbfe24a16580e22aaad928d960d0
+
     @Test
     public void digest_2() {
         String header = "Digest realm=\"http-auth@example.org\", qop=\"auth,auth-int\"," +
@@ -70,4 +155,74 @@ public class TestAuthDigest {
 
         assertEquals("8ca523f5e9506fed4657c9700eebdbec", responseField);
     }
+
+    // Example from RFC 7616
+    @Test
+    public void digest_2_sha256() {
+        String header = "Digest realm=\"http-auth@example.org\", qop=\"auth,auth-int\"," +
+                " nonce=\"7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v\","+
+                " opaque=\"FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS\""+
+                " algorithm=SHA-256";
+        AuthChallenge aHeader = AuthChallenge.parse(header);
+        String requestTarget = "/dir/index.html";
+        String method = "GET";
+        String cnonce = "f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ";
+        String nc = "00000001";
+        String username = "Mufasa";
+        String password = "Circle of Life"; // NB Different to RFC 2617
+
+        String responseField =
+                DigestLib.calcDigestChallengeResponse(aHeader, username, password,
+                                                      method, requestTarget,
+                                                      cnonce, nc, "auth");
+
+        assertEquals("753927fa0e85d155564e2e272a28d1802ca10daf4496794697cf8db5856cb6c1", responseField);
+    }
+
+    // Example from RFC 7616
+    @Test
+    public void digest_2_sha512_256() {
+        String header = "Digest realm=\"http-auth@example.org\", qop=\"auth,auth-int\"," +
+                " nonce=\"7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v\","+
+                " opaque=\"FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS\""+
+                " algorithm=SHA-512-256";
+        AuthChallenge aHeader = AuthChallenge.parse(header);
+        String requestTarget = "/dir/index.html";
+        String method = "GET";
+        String cnonce = "f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ";
+        String nc = "00000001";
+        String username = "Mufasa";
+        String password = "Circle of Life"; // NB Different to RFC 2617
+
+        String responseField =
+                DigestLib.calcDigestChallengeResponse(aHeader, username, password,
+                                                      method, requestTarget,
+                                                      cnonce, nc, "auth");
+
+        assertEquals("430d05014cecc49cab6fbe03176d41a1da86cbfe24a16580e22aaad928d960d0", responseField);
+    }
+
+    // Bad algorithm
+    @Test
+    public void digest_3_unknown() {
+        String header = "Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\","+
+                " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","+
+                " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""+
+                " algorithm=UNKNOWN";
+        AuthChallenge aHeader = AuthChallenge.parse(header);
+
+        String requestTarget = "/dir/index.html";
+        String method = "GET";
+        String cnonce = "0a4f113b";
+        String nc = "00000001";
+        String username = "Mufasa";
+        String password = "Circle Of Life";
+
+        String responseField =
+                DigestLib.calcDigestChallengeResponse(aHeader, username, password,
+                                                      method, requestTarget,
+                                                      cnonce, nc, "auth");
+
+        assertNull(responseField);
+    }
 }
diff --git a/jena-arq/src/test/java/org/apache/jena/http/auth/TestAuthHeaderParser.java b/jena-arq/src/test/java/org/apache/jena/http/auth/TestAuthHeaderParser.java
index 5978d48765..0d157992fd 100644
--- a/jena-arq/src/test/java/org/apache/jena/http/auth/TestAuthHeaderParser.java
+++ b/jena-arq/src/test/java/org/apache/jena/http/auth/TestAuthHeaderParser.java
@@ -41,7 +41,6 @@ public class TestAuthHeaderParser {
         return AuthHeader.parseChallenge(input);
     }
 
-
     @Test public void parse_empty() {
         AuthHeader auth = parseAuth("");
         assertNull(auth.getAuthScheme());
@@ -162,7 +161,6 @@ public class TestAuthHeaderParser {
         assertNull(map);
     }
 
-
     @Test public void parse_bearer_bad_01() {
         AuthHeader auth = parseAuth("Bearer ");
         assertTrue(auth.isBearerAuth());
@@ -191,8 +189,6 @@ public class TestAuthHeaderParser {
         assertEquals("a=b", auth.getUnknown());
     }
 
-
-
     @Test public void parse_challenge_01() {
         AuthHeader auth = parseChallenge("Bearer realm=\"somewhere\"");
         assertEquals(AuthScheme.BEARER, auth.getAuthScheme());
@@ -236,5 +232,4 @@ public class TestAuthHeaderParser {
         assertNotNull(map);
         assertEquals("hades", map.get("realm"));
     }
-
 }
\ No newline at end of file
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/auth/AuthBearerFilter.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/auth/AuthBearerFilter.java
index 5de9a31542..59d5893687 100644
--- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/auth/AuthBearerFilter.java
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/auth/AuthBearerFilter.java
@@ -133,6 +133,10 @@ public class AuthBearerFilter implements Filter {
 
             // ---- "Authorization:" header present.
             AuthHeader authHeader = getAuthToken(request, auth);
+            if ( authHeader == null ) {
+                ServletOps.errorBadRequest("Bad syntax in Authorization header");
+                return;
+            }
 
             // Test for Authorization: Bearer ..."
             if ( ! authHeader.isBearerAuth() ) {