`
lucky_god
  • 浏览: 37540 次
社区版块
存档分类
最新评论

HTTP digest RFC2671规范 加密实现(JAVA)

    博客分类:
  • HTTP
阅读更多
HTTP diest认证

一、介绍:
可用于访问资源时的身份认证。当客户端未通过服务器的认证,服务器返回401,并传递WWW-Authenticate信息,客户端可通过WWW-Authenticate,生成response返回给服务器来实现认证。


二、实例:

1)客户端发起一次请求,报文如下:
引用

GET / HTTP/1.1
Host: 192.168.7.202:7547
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8



2)服务器响应401,报文如下:
引用

HTTP/1.1 401 Unauthorized
Content-Length: 0
Connection: close
WWW-Authenticate: Digest realm="realm@easycwmp",qop="auth",nonce="c10c9897f05a9aee2e2c5fdebf03bb5b0001b1ef",
opaque="328458fab28345ae87ab3210a8513b14eff452a2"



3)客户端根据WWW-Authenticate生成response,报文如下:
引用

GET / HTTP/1.1
Host: 192.168.7.202:7547
Connection: keep-alive
Authorization: Digest username="povodo", realm="realm@easycwmp", qop=auth, nonce="c10c9897f05a9aee2e2c5fdebf03bb5b0001b1ef", opaque="328458fab28345ae87ab3210a8513b14eff452a2", uri="/", response="77d8c3e7ad338e86b8d039867651b65f", nc=00000001, cnonce="d5324153548c43d8"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8



4)服务器响应:报文如下:
引用

HTTP/1.1 200 OK
Content-Length: 0



一次完整的认证完成了。


三、认证方法实现(JAVA):

加密算法:
//我用的是commons-codec-1.9.jar包
import org.apache.commons.codec.binary.Hex;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.SecureRandom;
public class Digests {
	/**
	 * 加密遵循RFC2671规范
	 * 将相关参数加密生成一个MD5字符串,并返回
	 */
	public static String http_da_calc_HA1(String username, String realm, String password,
								   String nonce, String nc, String cnonce, String qop,
								   String method, String uri, String algorithm){
		String HA1,HA2;
		if ("MD5-sess".equals(algorithm)){
			HA1 = HA1_MD5_sess(username,realm,password,nonce,cnonce);
		} else{
			HA1 = HA1_MD5(username,realm,password);
		}
		byte[] md5Byte = md5(HA1.getBytes());
		HA1 = new String(Hex.encodeHex(md5Byte));

		md5Byte = md5(HA2(method, uri).getBytes());
		HA2 = new String(Hex.encodeHex(md5Byte));

		String original = HA1 + ":" + (nonce +":"+ nc +":"+ cnonce +":"+ qop) + ":" + HA2;

		md5Byte = md5(original.getBytes());
		return new String(Hex.encodeHex(md5Byte));

	}

	/**
	 * algorithm值为MD5时规则
	 */
	private static String HA1_MD5(String username, String realm, String password){
		return username+":"+realm+":"+password;
	}

	/**
	 * algorithm值为MD5-sess时规则
	 */
	private static String HA1_MD5_sess(String username, String realm, String password, String nonce, String cnonce){
//		MD5(username:realm:password):nonce:cnonce

		String s = HA1_MD5(username, realm, password);
		byte[] md5Byte = md5(s.getBytes());
		String smd5 = new String(Hex.encodeHex(md5Byte));

		return smd5+":"+nonce+":"+cnonce;
	}

	private static String HA2(String method, String uri){
		return method+":"+uri;
	}

	/**
	 * 对输入字符串进行md5散列.
	 */
	public static byte[] md5(byte[] input) {
		return digest(input, MD5, null, 1);
	}

	/**
	 * 对字符串进行散列, 支持md5与sha1算法.
	 */
	private static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
		try {
			MessageDigest digest = MessageDigest.getInstance(algorithm);

			if (salt != null) {
				digest.update(salt);
			}

			byte[] result = digest.digest(input);

			for (int i = 1; i < iterations; i++) {
				digest.reset();
				result = digest.digest(result);
			}
			return result;
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
//测试
	public static void main(String[] args) {
		String s = http_da_calc_HA1("povodo", "realm@easycwmp", "povodo",
				"c10c9897f05a9aee2e2c5fdebf03bb5b0001b1ef", "00000001", "d5324153548c43d8", "auth",
				"GET", "/", "MD5");
		System.out.println("加密后response为:"+s);
	}




四、客户端请求(GET方式示例):
//我这里用的fastjson-1.1.41.jar
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.povodo.map.servlet.StringUtils;
import org.apache.commons.codec.binary.Hex;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class HttpRequestUtils {

    static int nc = 0;    //调用次数

    /**
     * 向指定URL发送GET方法的请求
     * @param url      发送请求的URL
     * @param param    请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @param username 验证所需的用户名
     * @param password 验证所需的密码
     * @return URL 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String username, String password) {

        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {

            String wwwAuth = sendGet(url, param);       //发起一次授权请求
            if (wwwAuth.startsWith("WWW-Authenticate:")) {
                wwwAuth = wwwAuth.replaceFirst("WWW-Authenticate:", "");
            } else {
                return wwwAuth;
            }

            nc ++;

            String urlNameString = url + (StringUtils.isNotEmpty(param) ? "?" + param : "");
            URL realUrl = new URL(urlNameString);

            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();

            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");

            //授权信息
            String authentication = getAuthorization(wwwAuth, realUrl.getPath(), username, password);
            connection.setRequestProperty("Authorization", authentication);

            // 建立实际的连接
            connection.connect();

            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }

            nc = 0;
        } catch (Exception e) {
            nc = 0;
            throw new RuntimeException(e);
        } finally {
            try {
                if (in != null) in.close();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result.toString();
    }

    /**
     * 生成授权信息
     * @param authorization 上一次调用返回401的WWW-Authenticate数据
     * @param username      用户名
     * @param password      密码
     * @return 授权后的数据, 应放在http头的Authorization里
     * @throws IOException 异常
     */
    private static String getAuthorization(String authorization, String uri, String username, String password) throws IOException {

        uri = StringUtils.isEmpty(uri) ? "/" : uri;
        String temp = authorization.replaceFirst("Digest", "").trim();
        String json = "{\"" + temp.replaceAll("=", "\":").replaceAll(",", ",\"") + "}";

        JSONObject jsonObject = JSON.parseObject(json);
        String cnonce = new String(Hex.encodeHex(Digests.generateSalt(8)));    //客户端随机数
        String ncstr = ("00000000" + nc).substring(Integer.toString(nc).length());     //认证的次数,第一次是1,第二次是2...
        String algorithm = jsonObject.getString("algorithm");

        String response = Digests.http_da_calc_HA1(username, jsonObject.getString("realm"), password,
                jsonObject.getString("nonce"), ncstr, cnonce, jsonObject.getString("qop"),
                "GET", uri, algorithm);

        //组成响应authorization
        authorization = "Digest username=\"" + username + "\"," + temp;
        authorization += ",uri=\"" + uri
                + "\",nc=\"" + ncstr
                + "\",cnonce=\"" + cnonce
                + "\",response=\"" + response+"\"";
        return authorization;
    }

    /**
     * 向指定URL发送GET方法的请求
     *
     * @param url   发送请求的URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return URL 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {

            String urlNameString = url + (StringUtils.isNotEmpty(param) ? "?" + param : "");
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");

            connection.connect();

            //返回401时需再次用用户名和密码请求
            //此情况返回服务器的 WWW-Authenticate 信息
            if (((HttpURLConnection) connection).getResponseCode() == 401) {
                Map<String, List<String>> map = connection.getHeaderFields();
                return "WWW-Authenticate:" + map.get("WWW-Authenticate").get(0);
            }

            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
            throw new RuntimeException("get请求发送失败",e);
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) in.close();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result.toString();
    }
}



相关代码已上传附件。
如有错误,欢迎指正!


5、参考:
详解HTTP中的摘要认证机制
RFC 2017文档说明




分享到:
评论

相关推荐

    RFC 2617中文版(HTTP Digest Authentication)

    在RFC 2617中文版中,还对HTTP Digest Authentication的其他方面进行了详细的描述,例如,访问鉴别框架、基本鉴别方案和摘要报头的规范等。 访问鉴别框架是指在HTTP协议中对鉴别的框架结构,该框架包括客户端、...

    Http Digest 鉴权

    “摘要”式认证( Digest authentication)是一个简单的认证机制,最初是为HTTP协议开发的,因而也常叫做HTTP摘要,在RFC2671中描述。其身份验证机制很简单,它采用杂凑式(hash)加密方法,以避免用明文传输用户的...

    MD5加密 java源文件

    根据给定的文件标题、描述、标签以及部分内容,本文将详细介绍MD5加密算法在Java源文件中的实现原理及其代码逻辑。 ### MD5算法概述 MD5(Message-Digest Algorithm 5)是一种广泛使用的散列算法,它能将任意长度...

    rfc3261关于SIP的中文文档。

    RFC3261是SIP的核心规范,它定义了SIP的语法、语义以及交互过程,是理解和实现SIP系统的基础。这份中文文档对于那些英语不是母语,但在开发SIP相关应用的人员来说,无疑提供了极大的便利。 SIP的基本概念包括: 1....

    java good java good

    综上所述,本文档主要关注的是Java编程语言中MD5加密算法的应用,特别是通过实现KeyBean类来执行RFC 1321中的消息摘要算法。通过理解MD5算法的基本原理及其在Java中的实现方式,开发者可以更好地利用这种技术来确保...

    rfc3261中文

    - **HTTP鉴权**:使用HTTP Basic或Digest鉴权机制。 - **S/MIME**:使用加密和数字签名来保护消息内容。 - **TLS**:使用SSL/TLS来加密传输层通信。 #### 十七、SDP的Offer/Answer模式 SDP(Session Description ...

    一个实现MD5的简洁的java类

    在Java中,可以利用`java.security.MessageDigest`类来实现MD5加密。 在提供的Java代码中,`StringUtil`类包含了一个`MD5Encode`方法,用于对字符串进行MD5加密。首先,它获取`MessageDigest`实例,指定算法为"MD5...

    md5加密md5加密

    在给定的部分内容中,包含了一段Java代码示例,展示了如何使用Java实现MD5加密。 ```java public String EncoderByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException { // 获取MD5...

    Md5的Java实现

    MD5算法的标准规范由RFC 1321文档定义,并提供了一组测试用例来验证实现的正确性。 #### MD5算法的关键特点 1. **唯一性**:对于不同的输入,其输出应该是不同的。 2. **不可逆性**:通过散列值很难推导出原始的...

    MD5加密工具类

    根据给定的信息,“MD5Util”是指一个用于实现MD5加密功能的工具类。这类工具通常封装了对MD5算法的调用,并提供了简单的API供开发者使用。下面我们来详细探讨MD5Util可能包含的一些关键知识点: ##### 2.1 常用...

    MD5密码加密算法类MD5keyBean.java

    keyBean 类实现了RSA Data Security, Inc.在提交给IETF 的RFC1321中的keyBean message-digest算法。

    java版的 MD5

    这三个算法的描述和c语言源代码在Internet RFC 1321中有详细的描述(http://www.ietf.org/rfc/rfc1321.txt),这是一份最权威的文档,由Ronald L. Rivest在1992年8月向IETF提交。 希望能够给你的加密工作带来一些为...

    在JSP中如何实现MD5加密的方法

    在JSP中实现MD5加密方法涉及了多个方面的知识点,包括JSP基础、MD5加密技术原理以及Java编程技巧。下面将详细介绍这些知识点。 首先,JSP(Java Server Pages)是一种动态网页技术,允许开发者将Java代码嵌入到HTML...

    Java中4大基本加密算法解析

    简单的java加密算法有: ...  Base64是网络上常见的用于传输8Bit字节代码的编码方式之一,大家可以查看RFC2045~RFC2049,上面有MIME的详细规范。Base64编码可用于在HTTP环境下传递较长的标识信息。例如,在Jav

    commons-codec_加解密使用方法包_codecmd5crypt_commons加密解码_

    Apache Commons Codec库并未直接提供SHA算法,但可以通过Java内置的java.security.MessageDigest类进行SHA加密。 五、Metaphone和Soundex: Metaphone和Soundex是两种音译编码算法,主要用于英文单词的相似性比较,...

    httpclient4.5的JAR包

    4. **Cookie组件**:处理HTTP Cookie,支持RFC 6265规范。 5. **Redirect组件**:处理重定向逻辑,支持自动或手动重定向。 6. **Protocol组件**:定义了HTTP协议的实现,如HTTP/1.1和HTTP/2。 7. **Security组件**:...

    httpclient jar 包 4.5

    4. **Cookie管理**:HttpClient 4.5支持Cookie规范,包括RFC6265,提供了`CookieStore`和`CookieSpecs`接口,方便处理和管理服务器返回的Cookie。 5. **认证与安全**:HttpClient支持多种认证机制,如Basic Auth、...

    commons-codec-1.14-bin.zip

    这个版本包含了对各种编码标准和算法的实现,使得开发者能够更方便地进行数据转换和信息加密。 Apache Commons Codec库的核心功能包括: 1. **Base64编码和解码**:Base64是一种用于将任意二进制数据转换为ASCII...

Global site tag (gtag.js) - Google Analytics