4.3.2 相关API

Commons Codec的API主要包含了以下几个方面:

❑org.apache.commons.codec:该包内主要定义了一些编码转换的接口。

❑org.apache.commons.codec.binary:该包内主要完成了编码转换实现,如Base64、二进制、十六进制和字符集编码。

❑org.apache.commons.codec.digest:该包内仅有一个实现类DigestUtils,它是对Java原生消息摘要实现的改进。

❑org.apache.commons.codec.language:该包内主要完成了语言和语音编码器实现。

❑org.apache.commons.codec.net:该包内主要完成了网络相关的编码和解码,如Url编码/解码。

本书将主要阐述用于Base64和十六进制编码的转换实现,以及消息摘要算法相关实现。

1.Base64

Commons Codec也提供了用于Base64算法的实现类,与Bouncy Castle同名—Base64类。与Bouncy Castle提供的Base64类有所不同的是,Commons Codec提供的Base64类遵循RFC 2045(http://www.ietf.org/rfc/rfc2045.txt)。RFC全称为“Request For Comments”,意为请求注解。有关细节,请参见第5章的内容。

同时,这个Base64类实现了Bouncy Castle提供的UrlBase64类的Url编码功能。


//用于Base64编码/解码转换。

public class Base64

implements BinaryEncoder, BinaryDecoder


❑方法详述

这个Base64类提供了相关的构造方法,用于控制构建一般Base64算法或Url Base64算法。以下两个构造方法互为映射:


//构建Base64算法实现

public Base64()

//构建Url Base64算法实现

public Base64(boolean urlSafe)


以下构造方法指定了每行字符个数:


//构建指定每行字符数的Base64算法实现。

public Base64(int lineLength)


在上述构造方法的基础上,加入了回车换行符的控制:


//构建指定每行字符数的Base64算法实现,并控制每行字符数和行末符号。

public Base64(int lineLength, byte[]lineSeparator)


以下构造方法提供了是否支持Url Base64算法的支持:


/构建指定每行字符数的Base64算法实现,并控制每行字符数和行末符号及是否支持Url Base64算法。/

public Base64(int lineLength, byte[]lineSeparator, boolean urlSafe)


我们先来了解Base64类的静态方法。

下面是最为常用的Base64编码方法:


//以字节数组形式返回Base64编码结果。

public static byte[]encodeBase64(byte[]binaryData)


这个方法返回的是一般Base64编码,与之不同的是下面两种方法:


/以字节数组形式返回Base64编码结果,输出结果中每76个字符追加一个回车换行符。/

public static byte[]encodeBase64Chunked(byte[]binaryData)

//以字符串形式返回Base64编码结果,输出结果中每76个字符追加一个回车换行符。

public static String encodeBase64String(byte[]binaryData)


在内部实现上,上述方法均调用了以下这种方法:


/以字节数组形式返回Base64编码结果,对输出结果中每76个字符追加一个回车换行符可控。/

public static byte[]encodeBase64(byte[]binaryData, boolean isChunked)


对应上述编码方法,以下是相应的解码方法:


//以字节数字形式返回Base64解码结果。

public static byte[]decodeBase64(byte[]base64Data)

//以字节数字形式返回Base64解码结果。

public static byte[]decodeBase64(String base64String)


在4.2.3节中,我们提到了Url Base64算法。主要差别是将原Base64字符映射表中的“+”和“/”替换为“-”和“_”。Commons Codec的Base64类也完成了同样的实现,提供了如下方法:


//以字节数组形式返回Url Base64编码结果。

public static byte[]encodeBase64URLSafe(byte[]binaryData)

//以字符串形式返回Url Base64编码结果。

public static String encodeBase64URLSafeString(byte[]binaryData)


上述两种方法实际上调用了如下方法:


/以字节数组形式返回Base64编码结果,是否加入回车换行符可控,是否使用Url Base64编码可控。/

public static byte[]encodeBase64(byte[]binaryData, boolean isChunked, boolean urlSafe)


下述方法则更为细致一些:


/以字节数组形式返回Base64编码结果,对结果中每行多少个字符、是否加入回车换行符可控,是否使用Url Base64编码可控。/

public static byte[]encodeBase64(byte[]binaryData, boolean isChunked, boolean

urlSafe, int maxResultSize)


在这个Base64类中,除了提供了对于字节数组和字符串形式的编码/解码转换,同时也提供了BigInteger类型和字节数组形式的编码/解码转换。


//以字节数组形式返回Base64编码结果。

public static byte[]encodeInteger(BigInteger bigInt)

//以BigInteger形式返回Base64解码结果。

public static BigInteger decodeInteger(byte[]pArray)


如果需要判断一个字节数组(或一个字节)中是否包含了Base64字符映射表中的字符,可以使用如下方法:


//测试输入的字节数组是否包含Base64字符映射表中的字符。

public static boolean isArrayByteBase64(byte[]arrayOctet)

//判别输入字节是否在Base64字符映射表中。

public static boolean isBase64(byte octet)


在对上述静态方法了解一番后,下述方法理解起来就相对容易多了。它们绝大部分调用了上述静态方法。

以下方法互为Base64编码/解码实现:


//以字节数组形式返回Base64编码结果。

public byte[]encode(byte[]pArray)

//以字节数组形式返回Base64解码结果。

public byte[]decode(byte[]pArray)


如果需要字节数组和字符串之间的Base64编码/解码实现,可使用如下方法:


//以字节数组形式返回Base64解码结果。

public byte[]decode(String pArray)

//以字符串形式返回Base64编码结果。

public String encodeToString(byte[]pArray)


以下方法用于对象形式的Base64编码/解码操作:


//以对象形式返回Base64编码结果。

public Object encode(Object pObject)

//以对象形式返回Base64解码结果。

public Object decode(Object pObject)


此外,完成Base64初始化后,你可以通过如下方法获知当前实例化对象是否支持Url Basea64算法。


//判别是否是Url Base64编码。

public boolean isUrlSafe()


❑实现示例

对应4.2.3节中Base64算法的编码解码实现如代码清单4-4所示。

代码清单4-4 Base64编码/解码2


String str="Base64编码";

System.err.println("原文:\t"+str);

byte[]input=str.getBytes();

//Base64编码

byte[]data=Base64.encodeBase64(input);

System.err.println("编码后:\t"+new String(data));

//Base64解码

byte[]output=Base64.decodeBase64(data);

System.err.println("解码后:\t"+new String(output));


控制台输出与4.2.3节中一致:


原文:Base64编码

编码后:QmFzZTY0IOe8lueggQ==

解码后:Base64编码


对应4.2.3节中Url Base64算法的编码解码实现如下:


String str="Base64编码";

System.err.println("原文:\t"+str);

byte[]input=str.getBytes();

//Url Base64编码

byte[]data=Base64.encodeBase64URLSafe(input);

System.err.println("编码后:\t"+new String(data));

//Url Base64解码

byte[]output=Base64.decodeBase64(data);

System.err.println("解码后:\t"+new String(output));


注意控制台输出结果为:


原文:Base64编码

编码后:QmFzZTY0IOe8lueggQ

解码后:Base64编码


读者需要注意这一点,Bouncy Castle和Commons Codec对于Url Base64算法的理解上有所不同。关于Url Base64算法,本身没有一个统一的标准,没有像RFC 2045这样的明文规定。

两者不同之处在于对原Base64算法中补位概念的理解,如下所示:

❑Bouncy Castle:使用“.”符号替代“=”进行补位。

❑Commons Codec:不进行补位。

在使用Url Base64算法时,需要注意选择合适的实现。

2.Base64InputStream

Commons Codec在1.4版本中,加入了对Base64输入/输出流的支持,包含Base64InputStream和Base64OutputStream两个类。


//完成Base64编码/解码输入流操作。

public class Base64InputStream

extends FilterInputStream


❑方法详述

既然是流操作,就一定有相应的构造方法。Base64InputStream类可通过如下构造方法获得实例化对象:


//通过输入流构造,默认不支持Base64编码。

public Base64InputStream(InputStream in)


如果需要支持Base64编码,需要使用如下方法,将doEncode参数设置为true。


//通过输入流构造,可设置是否支持Base64编码。

public Base64InputStream(InputStream in, boolean doEncode)


以下方法还指定了每行字符数及行末符号:


//通过输入流构造,可设置是否支持Base64编码,并指定每行字符数及行末符号。

public Base64InputStream(InputStream in, boolean doEncode, int lineLength,

byte[]lineSeparator)


获得输入流最主要的操作就是进行读操作了,Base64InputStream类提供如下两种方法:


//每次读一个字节。

public int read()

//按偏移量读入字节数组。

public int read(byte[]b, int offset, int len)


下述方法在当前版本未实现:


//用于测试是否支持标记和重置操作,未实现返回值为false。

public boolean markSupported()


其余FilterInputStream类提供的方法,Base64InputStream类未覆盖。

❑实现示例

如果在通信环境中,消息收发双方使用Base64算法对消息隐蔽就可以使用Base64InputStream和Base64OutputStream类了。我们通过代码清单4-5来演示如何接收发送方传递过来的Base64编码消息。

代码清单4-5 Base64输入流操作


//实例化Base64InputStream,用作Base64解码。

Base64InputStream input=new Base64InputStream(is, false);

//使用DataInputStream包装Base64InputStream。

DataInputStream dis=new DataInputStream(input);

//信息体

byte[]data=new byte[contentLength];

//读入

dis.readFully(data);

//关闭流

dis.close();

//控制台输出

System.err.println(new String(data));


其中,is为网络输入流,contentLength为网络流长度。通过上述实现,在控制台中,我们获得如下内容:

Base64解码

在网络流完成读取后,获得了解码结果。数据是如何编码并转换为网络流呢?请读者关注下面的内容。

3.Base64OutputStream

Base64OutputStream类自然是对Base64算法输出流的支持。


//完成Base64编码/解码输出流操作。

public class Base64OutputStream

extends FilterOutputStream


❑方法详述

Base64OutputStream类可通过如下构造方法获得实例化对象:


//通过输入流构造,默认不支持Base64编码。

public Base64OutputStream(OutputStream out)


如果需要Base64编码需要使用如下构造方法,并将doEncode设置为true:


//通过输入流构造,可设置是否支持Base64编码。

public Base64OutputStream(OutputStream out, boolean doEncode)


同时,我们还可以在构造操作时指定每行字符数及行末符号,如下方法所示:


//通过输入流构造,可设置是否支持Base64编码,并指定每行字符数及行末符号。

public Base64OutputStream(OutputStream out, boolean doEncode, int lineLength,

byte[]lineSeparator)


既然是输出流,最主要的就是写操作了,也就是如下方法:


//写操作,写入b[]中。

public void write(byte[]b, int offset, int len)

//写操作,写入b[]中。

public void write(int i)


完成写操作后,需要执行如下操作:


//清空流

public void flush()


//关闭流

public void close()


❑实现示例

在消息发送时对输出流做相应实现如代码清单4-6所示。

代码清单4-6 Base64输出流操作


//实例化Base64OutputStream,用作Base64编码。

Base64OutputStream output=new Base64OutputStream(os, true);

//使用DataOutputStream包装Base64OutputStream。

DataOutputStream dos=new DataOutputStream(output);

//写操作

dos.write(data);

//清空

dos.flush();

//关闭流

dos.close();


其中,os是网络输出流,data中是我们传输的消息。

相信读者已经猜到作者传送什么内容了,参见如下代码:


String str="Base64编码";

byte[]data=str.getBytes();


4.Hex

Commons Codec也提供了用于十六进制编码/解码转换的实现类,与Bouncy Castle所提供的类同名,也叫做Hex类。所不同的是,Commons Codec提供了更为细致的API。


//用于十六进制编码/解码转换。

public class Hex

implements BinaryEncoder, BinaryDecoder


❑方法详述

Hex类与Base64类相类似,既提供静态方法,也提供对应接口实现的方法。以下为该类的构造方法:


//空构造方法,使用默认字符集。

public Hex()

//根据指定字符集构造。

public Hex(String csName)


我们先来了解Hex类的静态方法。

Hex类提供了4种静态方法,以下是编码操作方法:


//以字符数组形式返回十六进制编码结果。

public static char[]encodeHex(byte[]data)

//以字符串形式返回十六进制编码结果。

public static String encodeHexString(byte[]data)


有时候我们对输出的十六进制编码结果有要求,字母必须大写/小写,可以使用如下方法:


//以字符数组形式返回十六进制编码结果,对结果字符大小写可控。

public static char[]encodeHex(byte[]data, boolean toLowerCase)


上述3种方法中,最为常用的非encodeHexString()方法莫属了。尽管编码方法很多,但解码方法只有这一个:


//以字节数组形式返回十六进制解码结果。

public static byte[]decodeHex(char[]data)


以下两种方法,实际上是调用了上述相应的静态方法:


//以字节数组形式返回十六进制编码结果。

public byte[]encode(byte[]array)

//以字节数组形式返回十六进制解码结果。

public byte[]decode(byte[]array)


以下是对对象的编码/解码方法:


//以对象的形式返回对对象十六进制编码结果。

public Object encode(Object object)

//以对象的形式返回对对象十六进制解码结果。

public Object decode(Object object)


对于对象的编码/解码操作,实际上是先判断输入参数是否是String类型,如果不是则强制转换为byte[]类型。如果转换失败,则抛出异常。

此外,Hex类还提供了如下方法:


//获得字符集名称

public String getCharsetName()

//转换字符串输出

public String toString()


❑实现示例

参考4.2.3节中十六进制编码/解码实现示例,做相应修改,如代码清单4-7所示。

代码清单4-7 Hex编码/解码2


String str="Hex编码";

System.err.println("原文:\t"+str);

byte[]input=str.getBytes();

//Hex编码

String data=Hex.encodeHexString(input);

System.err.println("编码后:\t"+data);

//Hex解码

byte[]output=Hex.decodeHex(data.toCharArray());

System.err.println("解码后:\t"+new String(output));


观察控制台输出的内容,一定是一样的结果:


原文:Hex编码

编码后:48657820e7bc96e7a081

解码后:Hex编码


5.DigestUtils

DigestUtils类是对Sun提供的MessageDigest类进行了一次封装,提供了更为实用的算法支持,弥补了消息摘要结果十六进制编码转换的缺憾。


//用于实现MD5和SHA系列的消息摘要算法。

public class DigestUtils


❑方法详述

DigestUtils类是一个工具类,它提供了MD5和SHA系列消息摘要算法的实现。

以下是DigestUtils类提供的用于完成MD5算法的静态方法:


//以字节数组形式返回MD5消息摘要信息。

public static byte[]md5(byte[]data)

//以字节数组形式返回MD5消息摘要信息。

public static byte[]md5(InputStream data)

//以字节数组形式返回MD5消息摘要信息。

public static byte[]md5(String data)


下述方法提供了MD5算法十六进制字符串形式的结果:


//以十六进制字符串形式返回MD5消息摘要信息。

public static String md5Hex(byte[]data)

//以十六进制字符串形式返回MD5消息摘要信息。

public static String md5Hex(InputStream data)

//以十六进制字符串形式返回MD5消息摘要信息。

public static String md5Hex(String data)


DigestUtils类提供了Java 6所支持的全部SHA系列算法,包括SHA-1、SHA-256、SHA-384和SHA-512算法。

以下是DigestUtils类提供的用于完成SHA-1算法的静态方法:


//以字节数组形式返回SHA消息摘要信息。

public static byte[]sha(byte[]data)

//以字节数组形式返回SHA消息摘要信息。

public static byte[]sha(InputStream data)

//以字节数组形式返回SHA消息摘要信息。

public static byte[]sha(String data)


下述方法提供了SHA-1算法十六进制字符串形式的结果:


//以十六进制字符串形式返回SHA消息摘要信息。

public static String shaHex(byte[]data)

//以十六进制字符串形式返回SHA消息摘要信息。

public static String shaHex(InputStream data)

//以十六进制字符串形式返回SHA消息摘要信息。

public static String shaHex(String data)


以下是DigestUtils类提供的用于完成SHA-256算法的静态方法:


//以字节数组形式返回SHA-256消息摘要信息。

public static byte[]sha256(byte[]data)

//以字节数组形式返回SHA-256消息摘要信息。

public static byte[]sha256(InputStream data)

//以字节数组形式返回SHA-256消息摘要信息。

public static byte[]sha256(String data)


下述方法提供了SHA-256算法十六进制字符串形式的结果:


//以十六进制字符串形式返回SHA-256消息摘要信息。

public static String sha256Hex(byte[]data)

//以十六进制字符串形式返回SHA-256消息摘要信息。

public static String sha256Hex(InputStream data)

//以十六进制字符串形式返回SHA-256消息摘要信息。

public static String sha256Hex(String data)


以下是DigestUtils类提供的用于完成SHA-384算法的静态方法:


//以字节数组形式返回SHA-384消息摘要信息。

public static byte[]sha384(byte[]data)

//以字节数组形式返回SHA-384消息摘要信息。

public static byte[]sha384(InputStream data)

//以字节数组形式返回SHA-384消息摘要信息。

public static byte[]sha384(String data)


下述方法提供了SHA-384算法十六进制字符串形式的结果:


//以十六进制字符串形式返回SHA-384消息摘要信息。

public static String sha384Hex(byte[]data)

//以十六进制字符串形式返回SHA-384消息摘要信息。

public static String sha384Hex(InputStream data)

//以十六进制字符串形式返回SHA-384消息摘要信息。

public static String sha384Hex(String data)


以下是DigestUtils类提供的用于完成SHA-512算法的静态方法:


//以字节数组形式返回SHA-512消息摘要信息。

public static byte[]sha512(byte[]data)

//以字节数组形式返回SHA-512消息摘要信息。

public static byte[]sha512(InputStream data)

//以字节数组形式返回SHA-512消息摘要信息。

public static byte[]sha512(String data)


下述方法提供了SHA-512算法十六进制字符串形式的结果:


//以十六进制字符串形式返回SHA-512消息摘要信息。

public static String sha512Hex(byte[]data)

//以十六进制字符串形式返回SHA-512消息摘要信息。

public static String sha512Hex(InputStream data)

//以十六进制字符串形式返回SHA-512消息摘要信息。

public static String sha512Hex(String data)


❑实现示例

我们以MD5算法摘要处理为例,如代码清单4-8所示。

代码清单4-8 MD5摘要处理


String inputStr="MD5消息摘要";

System.err.println("原文:\t"+inputStr);

//执行MD5消息摘要

String md5Hex=DigestUtils.md5Hex(inputStr);

System.err.println("加密后:\t"+md5Hex);


注意控制台的输出:


原文:MD5消息摘要

加密后:aa24863099cf24696d4eb0f82c918849


原文MD5处理后,获得了一个32个字符的十六进制字符串。

有关消息摘要算法相关内容,请参见第6章。