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);
如果输入参数为字节数组,就不能使用上述方式来操作了。
对于一个长整型、整型、浮点型等数据类型来说,二进制转换虽然不复杂,我们完全有能力实现,但是转换过程中要考虑的细节很多,往往容易在补码、转码等问题上出现纰漏,这时就不如使用现成的开源实现。也许“不要重复制造轮子”的经典论断就是这么来的。