利用SMTP发送Mail详解(三)
来源:互联网 发布:淘宝店铺横幅图片 编辑:程序博客网 时间:2024/04/28 20:41
5). DIGEST-MD5认证方式
DIGEST-MD5认证也是Challenge/Response的方式, 与CRAM-MD5相比, 它的Challenge信息更多, 其Response计算方式也非常复杂, 我在测试时也是以认证失败而告终, 只是将在网上找到的资料整理于此, 能为后来研究的人多提供点资料, 或者有兴趣的朋友们可以和我一起讨论下.
我们先看下DIGEST-MD5认证发送响应信息:
DIGEST-MD5服务器格式说明(见RFC 2831 Digest SASL Mechanism Mai 2000):
digest-challenge =
1 # (Reich | Nonce | qop-Optionen | schal | MAXBUF | charset
Algorithmus | Chiffre-opts | auth-param)
realm = "Reich" "=" < "> Reich-Wert <">
Reich-Wert = qdstr-val
Nonce = "Nonce" "=" < "> Nonce-Wert <">
Nonce-Wert = qdstr-val
qop-options = "qop" "=" < "> qop-Liste <">
qop-list = 1 # qop-Wert
qop-Wert = "auth" | "auth-int" | "auth-conf" |
Token
stale = "veraltete" "=" "true"
MAXBUF = "MAXBUF" "=" MAXBUF-Wert
MAXBUF-Wert = 1 * DIGIT
charset = "charset" = "" UTF-8 "
algorithm = "Algorithmus" "=" "md5-sess"
Chiffre-opts = "Chiffre" "=" < "> 1 # Null-Wert <">
Chiffre-value = "3des" | "des" | "RC4-40" | "RC4" |
"RC4-56" | Token
auth-param = Token "=" (token | quoted-string)
DIGEST-MD5客户端响应格式说明(见RFC 2831 Digest SASL Mechanism Mai 2000):
digest-response = 1 # (Benutzername | Reich | Nonce | cnonce |
Nonce-count | qop | digest-uri | Antwort |
MAXBUF | charset | Chiffre | authzid |
auth-param)
username = "username" = "<"> username-Wert < ">
Benutzernamen-Wert = qdstr-val
cnonce = "cnonce" "=" < "> cnonce-Wert <">
cnonce-Wert = qdstr-val
Nonce-count = "nc" "=" nc-Wert
nc-Wert = 8LHEX
qop = "qop" "=" qop-Wert
digest-uri = "digest-uri" = "<"> digest-uri-value < ">
digest-uri-value = serv-type "/" host [ "/" serv-name] //eg: smtp/mail3.example.com/example.com
serv-type = 1 * ALPHA //www for web-service, ftp for ftp-dienst, SMTP for mail-versand-service …
host = 1 * (ALPHA | DIGIT | "-" | ".")
serv-name = host
response = "Antwort" "=" Response-Wert
response-value = 32LHEX
LHEX = "0" | "1" | "2" | "3" |
"4" | "5" | "6" | "7" |
"8" | "9" | "a" | "b" |
"c" | "d" | "e" | "f"
cipher = "Chiffre" "=" Null-Wert
authzid = "authzid" "=" < "> authzid-Wert <">
authzid-Wert = qdstr-val
其各字段具体含义见相关文档, 这里只介始几个需要用到的字段是如何产生的, C/S响应示例如下:
S: realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",qop="auth",
algorithm=md5-sess,charset=utf-8
C: charset=utf-8,username="chris",realm="elwood.innosoft.com",
nonce="OA6MG9tEQGm2hh",nc=00000001,cnonce="OA6MHXh6VqTrRk",
digest-uri="imap/elwood.innosoft.com",
response=d388dad90d4bbd760a152321f2143af7,qop=auth
S: rspauth=ea40f60335c427b5527b84dbabcdfffd
The password in this example was "secret".
从这个示例可以看出, 客户端返回的信息比服务器端发送过来的多了以下几个:
username, nc, cnonce, digest-uri和respone
username就不用说了, nc是8位长的16进制数字符串,统计客户端使用nonce发出请求的次数(包含当前请求),例示我们可以设为”00000001”, cnonce是是用了4个随机数组成一个8位长16进制的字符串,digest-uri是由在realm前加上请求类型(如http, smtp等), response是一个32位长的16进制数组, 计算公式如下:
If the "qop" value is "auth" or "auth-int":
request-digest = <"> < KD ( H(A1), unq(nonce-value)
":" nc-value
":" unq(cnonce-value)
":" unq(qop-value)
":" H(A2)
) <">
If the "qop" directive is not present (this construction is for
compatibility with RFC 2069):
request-digest =
<"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) >
<">
See below for the definitions for A1 and A2.
Read more: http://www.faqs.org/rfcs/rfc2617.html#ixzz0c4s8ck3F
KD(secret,data)表示分类算法,其中data指数据,secret表示采用的方法.如果表示校验和算法时,data要写成H(data);而unq(X)表示将带引号字符串的引号去掉。
对于"MD5" 和"MD5-sess" 算法:
H(data) = MD5(data)
和
KD(secret, data) = H(concat(secret, ":", data))
如果"algorithm"指定为"MD5"或没有指定,A1计算方式如下:
A1 = unq(username-value) ":" unq(realm-value) ":" passwd
//Password为用户密码
如果"algorithm"指定为"MD5-sess", 则需要nonce和cnonce的参与:
A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd )
":" unq(nonce-value) ":" unq(cnonce-value)
如果"qop"没有指定或指定为"auth", A2计算方式如下:
A2 = Method ":" digest-uri-value
如果"qop"没有指定或指定为"auth int", A2计算方式如下:
A2 = Method ":" digest-uri-value ":" H(entity-body)
Method是http请求时的方法(post,get), 由于英文水平比较差, 很多都看不明白, 有兴趣的朋友可以自己去看看原文(http://www.faqs.org/rfcs/rfc2617.html), 这里还提供了DIGEST验证的代码:
File "digcalc.h":
#define HASHLEN 16
typedef char HASH[HASHLEN];
#define HASHHEXLEN 32
typedef char HASHHEX[HASHHEXLEN+1];
#define IN
#define OUT
/* calculate H(A1) as per HTTP Digest spec */
void DigestCalcHA1(
IN char * pszAlg,
IN char * pszUserName,
IN char * pszRealm,
IN char * pszPassword,
IN char * pszNonce,
IN char * pszCNonce,
OUT HASHHEX SessionKey
);
/* calculate request-digest/response-digest as per HTTP Digest spec */
void DigestCalcResponse(
IN HASHHEX HA1, /* H(A1) */
IN char * pszNonce, /* nonce from server */
IN char * pszNonceCount, /* 8 hex digits */
IN char * pszCNonce, /* client nonce */
IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
IN char * pszMethod, /* method from the request */
IN char * pszDigestUri, /* requested URL */
IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
OUT HASHHEX Response /* request-digest or response-digest */
);
File "digcalc.c":
#include <global.h>
#include <md5.h>
#include <string.h>
#include "digcalc.h"
void CvtHex(
IN HASH Bin,
OUT HASHHEX Hex
)
{
unsigned short i;
unsigned char j;
for (i = 0; i < HASHLEN; i++) {
j = (Bin[i] >> 4) & 0xf;
if (j <= 9)
Hex[i*2] = (j + '0');
else
Hex[i*2] = (j + 'a' - 10);
j = Bin[i] & 0xf;
if (j <= 9)
Hex[i*2+1] = (j + '0');
else
Hex[i*2+1] = (j + 'a' - 10);
};
Hex[HASHHEXLEN] = '/0';
};
/* calculate H(A1) as per spec */
void DigestCalcHA1(
IN char * pszAlg,
IN char * pszUserName,
IN char * pszRealm,
IN char * pszPassword,
IN char * pszNonce,
IN char * pszCNonce,
OUT HASHHEX SessionKey
)
{
MD5_CTX Md5Ctx;
HASH HA1;
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
MD5Final(HA1, &Md5Ctx);
if (stricmp(pszAlg, "md5-sess") == 0) {
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, HA1, HASHLEN);
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
MD5Final(HA1, &Md5Ctx);
};
CvtHex(HA1, SessionKey);
};
/* calculate request-digest/response-digest as per HTTP Digest spec */
void DigestCalcResponse(
IN HASHHEX HA1, /* H(A1) */
IN char * pszNonce, /* nonce from server */
IN char * pszNonceCount, /* 8 hex digits */
IN char * pszCNonce, /* client nonce */
IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
IN char * pszMethod, /* method from the request */
IN char * pszDigestUri, /* requested URL */
IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
OUT HASHHEX Response /* request-digest or response-digest */
)
{
MD5_CTX Md5Ctx;
HASH HA2;
HASH RespHash;
HASHHEX HA2Hex;
// calculate H(A2)
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
if (stricmp(pszQop, "auth-int") == 0) {
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
};
MD5Final(HA2, &Md5Ctx);
CvtHex(HA2, HA2Hex);
// calculate response
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
MD5Update(&Md5Ctx, ":", 1);
if (*pszQop) {
MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
MD5Update(&Md5Ctx, ":", 1);
};
MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
MD5Final(RespHash, &Md5Ctx);
CvtHex(RespHash, Response);
};
File "digtest.c":
#include <stdio.h>
#include "digcalc.h"
void main(int argc, char ** argv) {
char * pszNonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093";
char * pszCNonce = "0a4f113b";
char * pszUser = "Mufasa";
char * pszRealm = "testrealm@host.com";
char * pszPass = "Circle Of Life";
char * pszAlg = "md5";
char szNonceCount[9] = "00000001";
char * pszMethod = "GET";
char * pszQop = "auth";
char * pszURI = "/dir/index.html";
HASHHEX HA1;
HASHHEX HA2 = "";
HASHHEX Response;
DigestCalcHA1(pszAlg, pszUser, pszRealm, pszPass, pszNonce,
pszCNonce, HA1);
DigestCalcResponse(HA1, pszNonce, szNonceCount, pszCNonce, pszQop,
pszMethod, pszURI, HA2, Response);
printf("Response = %s/n", Response);
};
到这里,关于使用SMTP发送mail就结束了, 由于水平有限, 有很多地方可能讲不够透彻!!!
- 利用SMTP发送Mail详解(三)
- 利用SMTP发送Mail详解(一)
- 利用SMTP发送Mail详解(二)
- 加入身份验证信息的SMTP mail发送
- System.Net.Mail 发送邮件 SMTP协议
- java mail 通过smtp验证发送
- 使用Python发送E-Mail (SMTP协议)
- linux--mail设置smtp发送邮件
- 用Apache Mail发送SMTP邮件
- java mail SMTP 发送验证代码 javax.mail.AuthenticationFailedException
- 利用SMTP发送邮件(C#)
- 利用SMTP发送验证验证
- Python利用SMTP发送邮件
- 利用java mail发送邮件
- 利用javax.mail发送邮件
- 利用mail 发送163 邮件
- .Net 2.0 下带身份验证信息的SMTP mail发送
- 使用commom mail smtp 发送email 不同服务器比较。
- 让alexa收录你的网站,怎么让alexa快速收录你的站呢?
- css div圆角代码
- 大背景
- delphi代理邮件服务器代码
- TortoiseSVN - 翻译 - Download下载 - 1.6.5
- 利用SMTP发送Mail详解(三)
- 永远消失的村庄
- c#
- 转载的:flash 中文不显示问题
- C++文件操作
- 基础概念辨析:重访数据抽象(On Understanding Data Abstraction, Revisited)
- 常用Javascript操作收集
- 《三重门》读后感
- 情绪