`
runfeel
  • 浏览: 928755 次
文章分类
社区版块
存档分类
最新评论

Android邮件中的Base64和Quoted-Printable编码

 
阅读更多

田海立@CSDN

2012-07-31

Email在网络上传输时,采用MIME(MultipurposeInternet Mail Extensions)。邮件传输只能传送US-ASCII字符,邮件中包含的其他字符必须通过一定的编码转换之后才能传输。对于Subject或/和附件名称为中文字符的邮件,有些邮件系统因为缺少编码(字符编码和传输编码)信息,导致乱码情况的发生。本文分析Android中Email系统的编码——Base64和Quoted-Printable。

邮件的Subject和附件名,用一种简短的格式指示传输编码和字符编码。字符编码是可以是UTF-8、GB2312等;传输编码常用的有BASE64和Quoted-Printable。本文主要看传输编码,关于字符编码的Unicode编码,可以参考《Unicode编码及其实现:UTF-16、UTF-8, and more》。

一、Base64编码

Base64编码在现在网络传输上应用广泛。Base64可以把要转换的内容,转换成可打印字符(包含字符表’A’~’Z’, ‘a’~’z’, ‘0’~’9’, ‘+’, ‘/’,共64个,以及’=’)。

字符表(64个字符,索引只需6bits,即最大0x3F):

索引

对应字符

索引

对应字符

索引

对应字符

索引

对应字符

0

A

17

R

34

i

51

z

1

B

18

S

35

j

52

0

2

C

19

T

36

k

53

1

3

D

20

U

37

l

54

2

4

E

21

V

38

m

55

3

5

F

22

W

39

n

56

4

6

G

23

X

40

o

57

5

7

H

24

Y

41

p

58

6

8

I

25

Z

42

q

59

7

9

J

26

a

43

r

60

8

10

K

27

b

44

s

61

9

11

L

28

c

45

t

62

+

12

M

29

d

46

u

63

/

13

N

30

e

47

v

14

O

31

f

48

w

15

P

32

g

49

x

16

Q

33

h

50

y

具体转换规则为:

1. 3字符转换成4个字符;

3个8Bits的字符有24Bits,每6个Bits组成一个BASE64字符表的索引,通过索引找到转换后的字符。

亦即,a7..a0 b7..b0c7..c0 -> A7..A2A1A0B7..B4 B3..B0C7C6C5..C0

A7..A2 第一个字符在字符表中索引;

A1A0B7..B4 第二个字符在字符表中索引;

B3..B0C7C6 第三个字符在字符表中索引;

C5..C0 第四个字符在字符表中索引。

2. 转换后的内容,每76个字符加一个换行符;

3. 最后的不足3个字符的字符要进行特别处理

3.1 若剩余两个字符未处理,则:

这两个剩余的字符与0x00组成一个数据,得到三个字符的索引,最后一个字符用’=’。

亦即,a7..a0 b7..b00..0 -> A7..A2A1A0B7..B4 B3..B000

A7..A2 第一个字符在字符表中索引;

A1A0B7..B4 第二个字符在字符表中索引;

B3..B0 00 第三个字符在字符表中索引;

第四个字符:’=’。

3.2 若剩余一个字符未处理,则:

这个剩余的字符与0x0000组成一个数据,得到两个字符的索引,最后两个字符都用’=’。

亦即,a7..a0 0..00..0 -> A7..A2A1A0 0..0

A7..A2 第一个字符在字符表中索引;

A1A0 0..0 第二个字符在字符表中索引;

第三、第四个字符:‘=’,‘=’。

二、Quoted-Printable编码

Quoted-Printable编码比较简单,扫描要编码的内容,对每个字节进行处理:

  • 如果是空格符(0x20),用‘_’替换;
  • 如果是[33, 127),并且不是特殊限制字符{=_?\"#$%&'(),.:;<>@[\\]^`{|}~},直接用原始字符加入,不做处理;
  • 其他字符,用‘=’加内码信息替换。

三、Email Subject和附件名的表达格式

有了Base64和Quoted-Printable的编码方式,要有一定的格式指示采用的哪种传输编码,同时还要指定编码的字符所采用的字符编码方式。

Email的Subject和附件名的表达格式:<prefix><charset>?<encodeMode>?<encodedContent><suffix>

其中,

  • <prefix> 固定为“=?”;
  • <charset> 为字符编码格式;
  • <encodeMode> 为传输编码格式:B代表Base64;Q代表Quote-Printable
  • <encodedContent> 为用encodeMode 编码过的字符编码为charset的字符串
  • <suffix> 固定为“?=”

比如要把“吕晶晶jj9.jpg”作为Subject或者附件名称通过Email传输。编码过程如下:

3.1.UTF-8编码

E59095 E699B6 E699B6 6A6A392E6A7067
吕     晶     晶     j j 9 . j p g

3.2.Base64编码

E59095 E699B6 E699B6 6A6A39 2E6A7067  3Bytes
E59095 -> 111001011001000010010101     二进制
       -> 111001 011001 000010 010101  6Bits(二进制)
       -> 57     25    2      21      索引(十进制)
       -> '5'    'Z'   'C'    'V'     编码后的字符
E699B6 -> 111001101001100110110110     二进制
       -> 111001 101001 100110 110110  6Bits(二进制)
       -> 57     41    38     54      索引(十进制)
       -> '5'    'p'   'm'    '2'     编码后的字符
E699B6 -> 111001101001100110110110     二进制
       -> 111001 101001 100110 110110  6Bits(二进制)
       -> 57     41    38     54     索引(十进制)
       -> '5'    'p'   'm'    '2'     编码后的字符
6A6A39 -> 011010100110101000111001     二进制
       -> 011010 100110 101000 111001  6Bits(二进制)
       -> 26     38    40     57      索引(十进制)
       -> 'a'    'm'   'o'    '5'     编码后的字符
2E6A70 -> 001011100110101001110000     二进制
       -> 001011 100110 101001 110000  6Bits(二进制)
       -> 11     38    41     48      索引(十进制)
       -> 'L'    'm'   'p'    'w'     编码后的字符
670000 -> 011001110000000000000000     二进制
       -> 011001 110000 000000 000000  6Bits(二进制)
       -> 25     48                    索引(十进制)
       -> 'Z'    'w'   '='    '='     编码后的字符

编码过程:

  • 把要编码的内容(“吕晶晶jj9.jpg”UTF-8编码的内容)按照3个字节一组分组[Line#1];
  • 每6bits拆分,得到在字符表中的索引[Line#3&4;Line#7&8; Line#11&12; Line#15&16; Line#19&20];
  • 通过索引查表,得到编码后的字符[Line#5; Line#9; Line#13; Line#7; Line#21];
  • 对未最后一个字节做处理[Line#22~#25]。

所以,得到Base64编码[Line#5;Line#9; Line#13; Line#7; Line#21]:

5ZCV5pm25pm2amo5LmpwZw==

3.3. 最终Base64编码结果

再按格式,加上前缀、字符编码、传输编码及后缀,得到:

=?UTF-8?B?5ZCV5pm25pm2amo5LmpwZw==?=

3.4. Quoted-Printable编码结果

如果传输编码用Quoted-Printable编码,可以得到:

=?UTF-8?Q?=E5=90=95=E6=99=B6=E6=99=B6jj9.jpg?=

编码过程比较简单,读者可参照第二部分的Quoted-Printable编码自行分析。


四、Android中Email相关的实现

Android原生Email的实现中,对Base64、Quoted-Printable的编码和解码是采用第三方开源包mime4j实现的。具体来说,对所有Base64/Quoted-Printable编码过的字段是可以解码的,但是在发送邮件时,只是对Subject进行了编码,对附件名称没有进行编码。这也导致了中文附件名称乱码问题。

传输编码和解码的使用都是通过com.android.email.mail.internet.MimeUtility,调用org.apache.james.mime4j.decoder.DecoderUtil或org.apache.james.mime4j.codec.EncoderUtil实现的。


4.1 解码

com.android.email.mail.internet.MimeUtility中与解码相关的有下面几个static的方法:

public static StringunfoldAndDecode(String s);
public static Stringunfold(String s);
public static Stringdecode(String s);

unfoldAndDecode包含了unfold和decode两个操作过程。unfold去掉编码过内容的CRLF;decode是真正的解码实现。

decode调用org.apache.james.mime4j.decoder.DecoderUtil#decodeEncodedWords()

decodeEncodedWords()通过判定传输编码,选择通过decodeB()进行Base64解码;还是通过decodeQ()进行Quoted-Printable解码。


4.2 编码

com.android.email.mail.internet.MimeUtility中与编码相关的,有下面几个static的方法:

public static StringfoldAndEncode(String s);
public static StringfoldAndEncode2(String s, int usedCharacters)
public static Stringfold(String s, int usedCharacters)

foldAndEncode没有做任何操作,foldAndEncode2才真正实现了编码。foldAndEncode2通过org.apache.james.mime4j.codec.EncoderUtil#encodeIfNecessary实现。

4.2.1 是否需要编码

编码过后,会增加字串的长度,并不是非要编码不可的。EncoderUtil #hasToBeEncoded()通过对原始字串的分析,判定是否一定要编码。

  • 如果字串中只包含一般可打印字符,没必要编码;
  • 如果字串中包含控制字符、大于127的字符,一定要进行编码。

4.2.2 编码的选择

编码的选择包括字符编码的选择和传输编码的选择。

字符编码的选择通过EncoderUtil#determineCharset()进行。

  • 如果要编码的字串中的字符中UnicodeCodePoint有大于0xFF,进行UTF-8编码;
  • 如果要编码的字串中的字符中UnicodeCodePoint有大于0x7F,进行ISO-8859-1编码;
  • 否则,进行US-ASCII编码。

传输编码的选择通过EncoderUtil#determineEncoding ()进行。

determineEncoding查看要编码的字串中的需要Quoted-Printable编码的字符所占的比例,只有需要编码的比例低于30%时,才采用Quoted-Printable编码,不然一律采用Base64编码。

4.2.3 编码的实现

通过encodeB()进行Base64编码;还是通过encodeQ()进行Quoted-Printable编码。

4.3 通过加编码信息解决问题

Android Email的实现中,对

  • 接收到邮件的Subject和附件名称以及其他字段,都进行了解码操作;
  • 发送/保存邮件时,只是对Subject进行了编码,对附件名称没有进行编码

所以,在接收到Android Email客户端发送的带有中文附件的邮件,会发生附件名是乱码的问题。解决方式是在发送或保存邮件地方,对附件名称进行本文前段论述的编码。

五、仍然未决的问题

4.4 的解决方式,能够解决新发送邮件的问题,但是对于存量的已经存在的邮件,它们的附件名称还是乱码。而且没有经过编码的邮件用别的邮件客户端(比如Outlook)接收,能够正确解析出附件的名称,这也说明即便没有进行编码和指定编码格式,客户端也是可以解码的。只是笔者通过试验,还是没搞懂具体怎么隐含编码/解码的。如果有知道如何实现的,望读者不吝赐教!

下面是通过Android Email客户端发送附件名称为“吕晶晶jj9.jpg”,接收到的附件名称,不知道是如何编/解码的?

发送的UTF-8名称

E59095 E699B6 E699B6 6A6A392E6A7067
吕     晶     晶     j j 9 . j p g

接收到的名称(这是什么样的编码?下面的十六进制编码是从收到的邮件的附件名里抓取到的,有谁知道其编码原则,望不吝赐教!)

C3A5C290C295 C3A6C299C2B6 C3A6C299C2B6 6A6A392E6A7067
吕           晶           晶           j j 9 . j p g



分享到:
评论

相关推荐

    Quoted-printable 编码介绍、Quoted-printable编码解码转换方法

    Quoted-printable编码是一种用于电子邮件传输中的字符编码方案,它允许文本包含一些非ASCII字符,同时保持邮件的可读性。Quoted-printable编码利用可打印的ASCII字符来表示原始数据中可能无法直接显示的字节,尤其是...

    base64和quoted编码,解码的源码

    Base64和Quoted-Printable编码是两种常见的数据编码方式,它们被广泛应用于网络通信、电子邮件、文本传输等领域,以确保数据在不同系统间安全、正确地传递。 Base64编码是一种基于64个字符的编码方法,这64个字符...

    MIME Quoted Printable &amp; Base64 编码解码程序

    2. 填充处理:Quoted-Printable中的"="填充和Base64中的"="尾部填充处理。 3. 分组和解组:Base64编码时要按3字节一组进行编码,解码时需按4个字符一组进行解码。 4. 错误检测和恢复:解码过程可能会遇到编码错误,...

    Quoted_Printable编码解码易语言源码

    Quoted_Printable编码是一种在电子邮件和网页中常见的字符编码方式,用于在ASCII字符集中表示非ASCII字符,确保数据在传输过程中不会被误解或丢失。这种编码方式特别适用于7位ASCII字符集不能涵盖的字符,例如某些...

    smtp.tar.gz_quoted-printable

    `quoted-printable`编码就是这样的一个方法,它是RFC 2045和RFC 2047中定义的一种编码方式,主要用于在邮件主体中保持可读性。 `quoted-printable`编码的主要目的是将非ASCII字符转换为ASCII,确保邮件内容在传输...

    C# Quoted-Printable编码、解码

    Quoted-Printable编码格式能够让普通文本邮件中包含非ASCII字符,同时保持邮件的可读性和兼容性。在C#中实现Quoted-Printable编码和解码的功能,对于处理邮件或需要对特定数据进行编码的场景非常有用。 编码...

    qp.rar_mime QP_qp_quoted printable_字节base64_邮件 ASCII

    Quoted-Printable也是MIME邮件中常用的编码方式之一。同Base64一样,它也将输入的字符串或数据编码成全是ASCII码的可打印字符串。 Quoted-Printable编码的基本方法是:输入数据在33-60、62-126范围内的,直接输出...

    常用编码VBA.zip

    在"常用编码VBA.zip"这个压缩包中,包含了一系列用于处理不同编码方式的VBA宏,如Base64编码、Quoted-Printable编码、URL编码、UTF8编码和Unicode编码。 1. **Base64编码**:Base64是一种用64个字符来表示任意二...

    易语言-Quoted_Printable编码解码易语言

    Quoted_Printable是一种字符编码方式,常用于电子邮件和纯文本文件中,以确保非ASCII字符或可能会引起混乱的特殊字符(如空格、换行符)在传输过程中能正确无误地显示。在易语言中,我们可以编写程序来实现这种编码...

    Quoted_Printable 解码-编码 -易语言

    例如,一个字节的值为20H(空格),在Quoted_Printable中会被编码为"=20"。如果一个字符是可打印的ASCII字符,那么它可以直接保留不变。 在易语言中,实现Quoted_Printable编码需要以下几个步骤: 1. 遍历输入的...

    C# 生成VCF demo源代码,支持Android,outlook QuotedPrintable 中文编码

    VCF(vCard)是一种标准格式,用于存储个人联系信息,如姓名、电话号码、电子邮件地址等。...在实际开发中,理解VCF文件格式和Quoted-Printable编码的原理,能够帮助你更好地创建、管理和分享联系人信息。

    邮件编码解码工具 md5,base64,qp编码解码,非常小巧有用

    md5,base64,qp编码解码,非常小巧有用

    用C写的电子邮件解码程序

    根据提供的文件信息,本文将详细解析“用C写的电子邮件解码程序”中涉及的主要编码算法:Base64、Quoted-Printable、7bit 和 8bit 的解码原理及其实现方法。 ### 一、Base64 解码 #### 1.1 基本概念 Base64 是一种...

    电子邮件编码解码易语言源码

    此“电子邮件编码解码易语言源码”提供了对两种常见的电子邮件编码方式——BASE64和Quoted-Printable的实现,支持UTF8和GBK编码格式,这对于理解电子邮件的内部工作原理和开发相关的应用非常有帮助。 首先,让我们...

    乱码查看器--方便的编码转换工具

    乱码察看器顾名思义就是用来察看各种乱码的工具软件,目前支持MIME/BASE64,Quoted-Printable,HZ和UUCode四种形式的编码和解码,通过一些特殊的算法,本程序还可以解开部分由于字节高位被屏蔽而形成的死乱码(使用...

    CTF中那些脑洞大开的编码和加密[汇编].pdf

    本文总结了CTF中常见的编码和加密方法,涵盖了ASCII编码、Base64/32/16编码、shellcode编码、Quoted-printable编码、XXencode编码、UUencode编码、URL编码、Unicode编码、Escape/Unescape编码、HTML实体编码、敲击码...

    各种形式的编码解码工具

    Quoted-printable MIME + Base64 MIME + Quoted-printable 如输入:测试CSDN 结果为: 测试CSDN \uXXXX \u6D4B\u8BD5\u0043\u0053\u0044\u004E \UXXXXXXXX \U00006D4B\U00008BD5\U00000043\U00000053\U00000044\U...

    乱码查看器乱码察看器顾名思义就是用来察看各种乱码的工具软件,目前支持MIME/BASE64

    Quoted-Printable,HZ和UUCode四种形式的编码和解码,通过一些特殊的算法, 本程序还可以解开部分由于字节高位被屏蔽而形成的死乱码(使用其它方式的解 码),对于EMail中比较常见的乱码,还进行了特别的处理,可以...

    pop3接收邮件及解码

    常见的编码方式有Base64、Quoted-printable和MIME等。Base64用于将二进制数据转化为ASCII文本,Quoted-printable则用于编码非打印字符,MIME(Multipurpose Internet Mail Extensions)则是一种扩展标准,它定义了...

Global site tag (gtag.js) - Google Analytics