4.2.3 相关API

Bouncy Castle的API主要包含了以下几个方面:

❑JCE工具及其扩展包

仅包括org.bouncycastle.jce包。这是对JCE框架的支持。其中定义了一些扩展算法的接口与实现,如ECC和ElGamal算法。

❑JCE支持者和测试包

包括org.bouncycastle.jce.provider包及其子包。本章提到的Bouncy Castle的安全提供者BouncyCastleProvider就位于该包中。

❑轻量级加密包

包括org.bouncycastle.crypto包及其子包。我们可以认为这个包完成了扩展算法的实现。

❑OCSP和OpenSSL PEM支持包

包括org.bouncycastle.ocsp包及其子包和org.bouncycastle.openssl包及其子包。这两个包都是与数字证书相关的支持包。OCSP(Online Certificate Status Protocol,在线证书状态协议)用于鉴定所需证书的(撤销)状态。具体协议内容请查看RFC 2560(http://www.ietf.org/rfc/rfc2560.txt)。OpenSSL用于管理数字证书,包括证书的申请和撤销等。

❑ASN.1编码支持包

包括org.bouncycastle.asn1包及其子包,该包体积最为庞大。标准的ASN.1编码规则有基本编码规则(Basic Encoding Rules, BER)、规范编码规则(Canonical Encoding Rules, CER)、唯一编码规则(Distinguished Encoding Rules, DER)、压缩编码规则(Packed Encoding Rules, PER)和XML编码规则(XML Encoding Rules, XER)。我们在导出数字证书时,常常会使用到DER编码。

❑工具包

包括org.bouncycastle.util包及其子包。提供了很多与编码转换有关的工具类,如Base64编码和十六进制编码。

❑其他包

包括org.bouncycastle.mozilla包及其子包和org.bouncycastle.x509包及其子包。org.bouncycastle.mozilla包用于支持基于Mozilla(网景)浏览器的公钥签名和身份认证。org.bouncycastle.x509包用于基于支持X.509格式的数字证书。

相信不论是谁,在看了上述介绍后都会汗颜。当然,作者也不例外。作者有意避开Bouncy Castle支系庞大的API,建议读者将Bouncy Castle作为支持的方式来使用其扩展算法。这样可以降低学习难度,这也是Sun构建JCE架构最初的目的。

本书将简要介绍org.bouncycastle.util.encoders包中的内容。

1.Base64

Base64类是用于Base64编码的工具类。当然,Sun内部实现了Base64算法,但相关实现并未在API中体现,请读者参考第5章的内容。


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

public class Base64

extends Object


❑方法详述

我们先来看编码方法:


//返回Base64编码结果。

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

//向输出流中写入Base64编码结果。

public static int encode(byte[]data, int off, int length, OutputStream out)

//向输出流中写入Base64编码结果。

public static int encode(byte[]data, OutputStream out)


以下是对应的解码方法:


//返回Base64解码结果。

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

//返回Base64解码结果,空格将被忽略。

public static byte[]decode(String data)

//向输出流中写入Base64解码结果,空格将被忽略。

public static int decode(String data, OutputStream out)


其实,Base64类在内部实现时调用了Base64Encoder类来完成相应的编码/解码操作。我们只需要知道如何使用Base64类就可以了。

❑实现示例

我们通过代码清单4-1来演示如何进行Base64编码和解码操作。

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


String str="base64编码";

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

byte[]input=str.getBytes();

//Base64编码

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

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

//Base64解码

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

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


查看控制台输出如下:


原文:Base64编码

编码后:QmFzZTY0IOe8lueggQ==

解码后:Base64编码


编码后的内容中,出现“=”符号,这是Base64编码的标志性符号。

2.UrlBase64

Base64算法最初用于电子邮件系统,后经演变成为显式传递Url参数的一种编码算法,通常称为“Url Base64”。它是Base64算法的变体,将字符映射表中用做补位的“=”换成“.”,并用“-”和“_”分别替代“+”和“/”,使得Base64编码符合Url参数规则,可以将二进制的数据以Get方式进行传输。有关Base64算法请参见第5章。


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

public class UrlBase64

extends Object


❑方法详述

以下为编码方法:


//返回Url Base64编码。

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


以下方法将编码结果输出至输出流中:


//向输出流中写入Url Base64编码。

public static int encode(byte[]data, OutputStream out)


以下为解码方法:


//返回Url Base64解码,空格忽略。

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

//返回Url Base64解码,空格忽略。

public static byte[]decode(String data)


以下方法可以将解码结果输出至输出流中:


//向输出流中写入Url Base64解码,空格忽略。

public static int decode(byte[]data, OutputStream out)

//向输出流中写入Url Base64解码,空格忽略。

public static int decode(String data, OutputStream out)


UrlBase64类在内部实现时,调用了UrlBase64Encoder类来完成相应的编码/解码操作。这是Bouncy Castle一贯的代码风格。

❑实现示例

我们对上述Base64算法实现稍作调整,改为Url Base64类完成编码和解码操作,如代码清单4-2所示。

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


String str="Base64编码";

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

byte[]input=str.getBytes();

//Url Base64编码

byte[]data=UrlBase64.encode(input);

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

//Url Base64解码

byte[]output=UrlBase64.decode(data);

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


观察控制台输出的内容:


原文:Base64编码

编码后:QmFzZTY0IOe8lueggQ..

解码后:Url Base64编码


编码后的内容中,出现了“.”符号,替换掉了“=”符号,符合了Url参数规则。当然,有些系统会对这个“.”符号较为敏感,如在文件系统中这可能导致一些错误。关于这个问题,请读者参考第5章的相关内容。

3.Hex

不用介绍,读者就能从字面上判断出Hex类和十六进制相关。Hex类用于十六进制转换。常配合消息摘要算法处理摘要值,以十六进制形式公示。


//用于十六进制编码/解码操作。

public class Hex

extends Object


❑方法详述

以下是十六进制编码方法:


//返回十六进制编码结果。

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


//返回十六进制编码结果。

public static byte[]encode(byte[]data, int off, int length)


以下方法将编码结果输出至输出流中:


//向输出流中写入十六进制编码结果。

public static int encode(byte[]data, int off, int length, OutputStream out)

//向输出流中写入十六进制编码结果。

public static int encode(byte[]data, OutputStream out)


以下是十六进制解码方法:


//返回十六进制解码结果。

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

//返回十六进制解码结果,空格忽略。

public static byte[]decode(String data)


以下方法将解码结果输出至输出流中:


//向输出流中写入十六进制解码结果,空格忽略。

public static int decode(String data, OutputStream out)


❑实现示例

我们继续对上述代码进行改造,如代码清单4-3所示:

代码清单4-3 Hex编码/解码1


String str="Hex编码";

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

byte[]input=str.getBytes();

//Hex编码。

byte[]data=Hex.encode(input);

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

//Hex解码。

byte[]output=Hex.decode(data);

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


观察控制台输出的内容:


原文:Hex编码

编码后:48657820e7bc96e7a081

解码后:Hex编码


这时候看到编码后的字符串样式就很眼熟了,在各大软件厂商的下载页面上都很常见,通常用做MD5的十六进制表示。

Sun虽然在Java API中提供了简单的进制转换方法,但并不方便。相应的进制转换方法均包含在封装类中,如Long类的toHexString()方法可将长整型转换为十六进制字符串,并通过parseLong()方法,指定进制参数将十六进制字符串转换为长整型,参考代码如下:


//长整型转换十六进制字符串。

String s=Long.toHexString(new Long(100));

//十六进制字符串转换长整型。

long l=Long.parseLong(s,16);


如果输入参数为字节数组,就不能使用上述方式来操作了。

对于一个长整型、整型、浮点型等数据类型来说,二进制转换虽然不复杂,我们完全有能力实现,但是转换过程中要考虑的细节很多,往往容易在补码、转码等问题上出现纰漏,这时就不如使用现成的开源实现。也许“不要重复制造轮子”的经典论断就是这么来的。