加密服务总是关联到一个特定的算法或类型,它既提供了密码操作(如Digital Signature或MessageDigest),生成或供应所需的加密材料(Key或Parameters)加密操作,也会以一个安全的方式生成数据对象(KeyStore或Certificate),封装(压缩)密钥(可以用于加密操作)。

Java Security API中,一个engine class就是定义了一种加密服务,不同的engine class提供不同的服务。下面就来看看有哪些engine class:

1)MessageDigest:对消息进行hash算法生成消息摘要(digest)。

2)Signature:对数据进行签名、验证数字签名。

3)KeyPairGenerator:根据指定的算法生成配对的公钥、私钥。

4)KeyFactory:根据Key说明(KeySpec)生成公钥或者私钥。

5)CertificateFactory:创建公钥证书和证书吊销列表(CRLs)。

6)KeyStore:keystore是一个keys的数据库。Keystore中的私钥会有一个相关联的证书链,证书用于鉴定对应的公钥。一个keystore也包含其它的信任的实体。

7)AlgorithmParameters:管理算法参数。KeyPairGenerator就是使用算法参数,进行算法相关的运算,生成KeyPair的。生成Signature时也会用到。

8)AlgorithmParametersGenerator:用于生成AlgorithmParameters。

9)SecureRandom:用于生成随机数或者伪随机数。

10)CertPathBuilder:用于构建证书链。

11)CertPathValidator:用于校验证书链。

12)CertStore:存储、获取证书链、CRLs到(从)CertStore中。

从上面这些engine class中,可以看出JCA(Java加密框架)中主要就是提供了4种服务:Digest、Key、Cert、Signature、Alogorithm。

1) 对消息内容使用某种hash算法就可以生成Digest。

2) 利用KeyFactory、KeyPairGenerator就可以生成公钥、私钥。

3) 证书中心使用公钥就可生成Cert。

4) 可以使用私钥和Digest就可以消息进行签名Signature。

5) 不论是Digest、Key、Cert、Signature,都要使用到算法Algorithm。

JCA Core API

1)engine class的提供商Provider

从JCA的设计上来说,这些engine的实现都离不开Provider。

这个类继承了Properties,提供了JCA中的engine class。每个engine class都有getInstance()方法,它们都是从provider中获取相关实例的。所以说Provider是JCA engine class的提供商。

2)管理Provider的工具:Security

其实就是一个存放Provider的集合。如果你自定义了一个Provider,可以使用Java Security属性文件配置provider,也可以直接使用Security采用编程的方式来添加Provider。然后就可以使用自定义的engine class了。

Java Security 属性文件在Java Security Policy中已有提过。在安装目录下:

下面是一个自定义的Provider:

/**
* @author fs1194361820@163.com
*/
public class XYZProvider extends Provider{
public XYZProvider(){
super("XYZ", 1.0, "XYZ Security Provider v1.0");
put("MessageDigest.XYZ", XYZMessageDigest.class.getName());
}
}

已经默认配置了下列Provider:

配置为:security.provider.11=com.fjn.security.XYZProvider 即可。

编码方式就更加简单了:Security.addProvider(new XYZProvider());

3)消息摘要服务:MessageDigest

消息摘要服务其实就是使用hash算法将一段消息(可以是字符串、文件内容、html等)进行计算生成的一个byte[]。

常用加密算法MD5、SHA、SHA-1其实都是hash算法。

下面就给一个简单的MD5算法工具:

package com.fjn.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
*
* @author fs1194361820@163.com
*
*/
public class MD5 {
private static MessageDigest md5MsgDigest; static{
try {
md5MsgDigest=MessageDigest.getInstance("md5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
} // 转字符串
public static String byte2hex(byte[] b){
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0xFF);
if (stmp.length() == 1)
hs = hs + "0" + stmp;
else
hs = hs + stmp;
}
return hs.toUpperCase();
} public static String getMD5(String srcMsg){
if(srcMsg == null){
throw new IllegalArgumentException("srcMsg is null.");
}
byte[] md5Bytes=md5MsgDigest.digest(srcMsg.getBytes());
return byte2hex(md5Bytes);
} public static void main(String[] args) {
System.out.println(MD5.getMD5("hello"));
System.out.println(MD5.getMD5("world"));
}
}

Md5算法我并没有去实现,因为在JDK中已经内置了md5算法。上面的代码就是使用消息摘要服务,并使用md5算法,生成相应的摘要。

下面来一个自定义的MessageDigest:

package com.fjn.security.messageDigest;

import java.security.MessageDigest;
/**
* @author fs1194361820@163.com
*/
public class XYZMessageDigest extends MessageDigest{
private int hash;
private int store;
private int count; public XYZMessageDigest(){
super("XYZ");
engineReset();
} /**
* 算法执行过程,每次执行{@link MessageDigest#update(byte)}时,都会调用这个方法,
* 也就是使用这个算法对在已经计算的数据的基础上再次计算,计算出最新的结果
*/
@Override
protected void engineUpdate(byte b) {
switch (count) {
case 0:
store = (b << 24) & 0xff000000;
break;
case 1:
store |= (b << 16) & 0x00ff0000;
break;
case 2:
store |= (b << 8) & 0x0000ff00;
break;
case 3:
store |= (b << 0) & 0x000000ff;
break;
}
count++;
if(count==4){
hash = hash ^ store;
count = 0;
store = 0;
}
} @Override
protected void engineUpdate(byte[] b, int offset, int length) {
for (int i = 0; i < length; i++){
engineUpdate(b[i + offset]);
}
} /**
* 每次执行{@link MessageDigest#digest()}时,都会获取之前计算好的结果。
* 同时也会将数据置为初始状态。
*/
@Override
protected byte[] engineDigest() {
while (count != 0){
engineUpdate((byte) 0);
}
byte b[] = new byte[4];
b[0] = (byte) (hash >>> 24);
b[1] = (byte) (hash >>> 16);
b[2] = (byte) (hash >>> 8);
b[3] = (byte) (hash >>> 0);
engineReset();
return b;
} /**
* 数据转为初始状态
*/
@Override
protected void engineReset() {
hash = 0;
store = 0;
count = 0;
} }

这个自定义的MessageDigest中备注的内容,其实就是MessageDigest的执行过程,所有的MessageDigest都要遵从这个过程的。

测试用例:

package com.fjn.security.messageDigest;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.MessageDigest;
import java.security.Security; public class XYZMessageDigestTest {
private static String filename="MessageDigestTest.txt";
static{
Security.addProvider(new XYZProvider());
} public static void main(String[] args) throws Exception {
XYZMessageDigestTest test=new XYZMessageDigestTest();
test.writeMessage();
test.readMessage();
} public void writeMessage() throws Exception {
File file=new File(filename);
file.deleteOnExit();
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
MessageDigest md = MessageDigest.getInstance("XYZ");
ObjectOutputStream oos = new ObjectOutputStream(fos);
String data = "This have I thought good to deliver thee, "+
"that thou mightst not lose the dues of rejoicing " +
"by being ignorant of what greatness is promised thee.";
byte buf[] = data.getBytes();
md.update(buf);
oos.writeObject(data); // original message
oos.writeObject(md.digest()); // digest oos.close();
} public void readMessage() throws Exception{
File file=new File(filename);
FileInputStream fis=new FileInputStream(file);
ObjectInputStream ois=new ObjectInputStream(fis); Object o = ois.readObject(); // String data: original message
if (!(o instanceof String)) {
System.out.println("Unexpected data in file");
System.exit(-1);
}
String data = (String) o;
System.out.println("Got message : " + data);
o = ois.readObject(); // byte[] : digest
if (!(o instanceof byte[])) {
System.out.println("Unexpected data in file");
System.exit(-1);
}
byte origDigest[] = (byte []) o;
MessageDigest md = MessageDigest.getInstance("XYZ");
md.update(data.getBytes());
if (MessageDigest.isEqual(md.digest(), origDigest))
System.out.println("Message is valid");
else
System.out.println("Message was corrupted"); MessageDigest md2 = MessageDigest.getInstance("SHA");
md2.update(data.getBytes());
if (MessageDigest.isEqual(md2.digest(), origDigest))
System.out.println("Message is valid");
else
System.out.println("Message was corrupted"); ois.close();
}
}

在这个用例中,writeMessage()将一段字符串保存后并将生成的digest也保存。

readMessage()将消息读取后,使用MessageDigest.isEqual()方法进行比较,这样可以知道文件是否被人改动过。

而实际上利用私钥更新签名信息时,就是使用MessageDigest#update()方法的。

4)Key 相关的服务

Key包括公钥(PublicKey)、私钥(PrivateKey)两种。

4.1 KeyPairGenerator

这个服务用于生成PublicKey和PrivateKey。

获取实例后,只需要根据上面4种initialize方法进行初始化后,就可以生成KeyPair了。

@Test
public void generateKeyPair() throws Exception {
// 算法名称有规定的值,不能乱写的
KeyPairGenerator dsaKeyPairGenerator=KeyPairGenerator.getInstance("dsa");
SecureRandom random=SecureRandom.getInstance("SHA1PRNG","SUN");
random.setSeed(new byte[]{1,2,3,4});
/**
* jdk8: key必须在[512,1024]之间,并且是64的倍数。有的JDK版本要求是8的倍数,这要根据实际情况和需求设定
*/
dsaKeyPairGenerator.initialize(576, random); KeyPair keyPair=dsaKeyPairGenerator.generateKeyPair();
DSAPublicKey puk=(DSAPublicKey)keyPair.getPublic();
DSAPrivateKey pik=(DSAPrivateKey)keyPair.getPrivate(); System.out.println(puk.getFormat());
System.out.println(pik.getFormat()); System.out.println(puk);
System.out.println(pik);
}

第一次调用generateKeyPair()都会生成不同的KeyPair。KeyPairGenerator 每次生成的都是一个KeyPair。

4.2 KeyFactory

KeyFactory用于在Key与KeySpec之间转换,即可以根据key获取到KeySpec,也可以根据KeySpec获取Key。

package com.fjn.security.key;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.DSAPublicKeySpec; import org.junit.Test; public class KeyFactoryTest {
private static final String DSA="DSA";
private static final String keyspecFile="keyspec.text"; @Test
public void genenatePublicKey() throws Exception{
writeKeySpec();
readKeySpec();
} private void writeKeySpec() throws Exception {
File file=new File(keyspecFile);
file.deleteOnExit();
file.createNewFile(); KeyPairGenerator keyGen = KeyPairGenerator.getInstance(DSA);
keyGen.initialize(512, new SecureRandom());
KeyPair keyPair=keyGen.generateKeyPair(); KeyFactory factory=KeyFactory.getInstance(DSA);
DSAPublicKeySpec keySpec=factory.getKeySpec(keyPair.getPublic(), DSAPublicKeySpec.class);
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(keySpec.getY());
oos.writeObject(keySpec.getP());
oos.writeObject(keySpec.getQ());
oos.writeObject(keySpec.getG());
oos.flush();
oos.close();
} private void readKeySpec() throws Exception {
KeyFactory factory=KeyFactory.getInstance(DSA);
FileInputStream fis = new FileInputStream(keyspecFile);
ObjectInputStream ois = new ObjectInputStream(fis);
DSAPublicKeySpec keySpec = new DSAPublicKeySpec(
(BigInteger) ois.readObject(),
(BigInteger) ois.readObject(),
(BigInteger) ois.readObject(),
(BigInteger) ois.readObject());
ois.close();
PublicKey puk=factory.generatePublic(keySpec);
System.out.println("Got private key:\n"+puk);
}
}

5)Cert相关的服务

从上一篇的例子中知道,用户使用的Public Key有可能被不法分子偷偷地窜改,这样用户就得不到应有的服务,也会受到不法分子的危害。如何保证public key不被窜改或者替换呢?认证服务就出现了。

5.1 CertificateFactory

用于生成Certificate或者CRL的。

FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis); CertificateFactory cf = CertificateFactory.getInstance("X.509"); while (bis.available() > 0) {
Certificate cert = cf.generateCertificate(bis);
System.out.println(cert.toString());
}

什么是CRL ?

一个证书颁发机构需要证书吊销其颁发的证书——也许是虚假的,或者证书的用户已经使用证书从事非法行为。在这样的情况下,证书的有效期不足保护;证书必须立即失效。

下面的这个例子就是在验证完证书的有效性后,判断这个证书是否是一个吊销的证书。

public Certificate importCertificate(byte data[])
throws CertificateException {
X509Certificate c = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X509");
ByteArrayInputStream bais = new ByteArrayInputStream(data);
c = (X509Certificate) cf.generateCertificate(bais);
Principal p = c.getIssuerDN();
PublicKey pk = getPublicKey(p);
c.verify(pk);
InputStream crlFile = lookupCRLFile(p);
cf = CertificateFactory.getInstance("X509CRL");
X509CRL crl = (X509CRL) cf.generateCRL(crlFile);
if (crl.isRevoked(c))
throw new CertificateException("Certificate revoked");
} catch (NoSuchAlgorithmException nsae) {
throw new CertificateException("Can't verify certificate");
} catch (NoSuchProviderException nspe) {
throw new CertificateException("Can't verify certificate");
} catch (SignatureException se) {
throw new CertificateException("Can't verify certificate");
} catch (InvalidKeyException ike) {
throw new CertificateException("Can't verify certificate");
} catch (CRLException ce) {
// treat as no crl
}
return c;
}

5.2 CertPathBuilder构建证书链CertPath

CertPath就是之前说的证书链。其实就是一个Certificate的有序列表。在列表的最后的一个Cert是一个自签名的Cert。

5.3 CertPathValidator验证Cert链

CertPathValidator用于校验Cert。

6)KeyStore

一个KeyStore是一个key、cert的库,里面存储了PrivateKey, Aliases, Certs.

KeyStore将会有专门的说明。

7)Signature签名

用私钥签名,用公钥验证:

public class SignatureTest {

    @Test
public void test() throws Exception{
KeyPairGenerator keyPairGen=KeyPairGenerator.getInstance(Message.alogthem);
keyPairGen.initialize(1024);
KeyPair keyPair= keyPairGen.generateKeyPair();
PublicKey puk=keyPair.getPublic();
PrivateKey pik=keyPair.getPrivate(); String data="Hello, Java.";
Signature signature=Signature.getInstance("SHA1withDSA"); // private key sign
signature.initSign(pik);
signature.update(data.getBytes());
byte[] signinfo=signature.sign(); // public key resolve sign
signature.initVerify(puk);
boolean ok=signature.verify(signinfo);
System.out.println(ok); signature.update(data.getBytes());
ok=signature.verify(signinfo);
System.out.println(ok); } }

到此,JCA部分的engine class已经大体上有个了解了。接下来就是要学习如何应用它们了。

Java Security:Java加密框架(JCA)简要说明的更多相关文章

  1. 基础篇:java.security框架之签名、加密、摘要及证书

    前言 和前端进行数据交互时或者和第三方商家对接时,需要对隐私数据进行加密.单向加密,对称加密,非对称加密,其对应的算法也各式各样.java提供了统一的框架来规范(java.security)安全加密这 ...

  2. java.security.MessageDigest (1)

    我们知道,编程中数据的传输,保存,为了考虑安全性的问题,需要将数据进行加密.我们拿数据库做例子.如果一个用户注册系统的数据库,没有对用户的信息进行保存,如,我去页面注册,输入"Vicky&q ...

  3. java.lang.SecurityManager、java.security包

    学习java大概3年多了,一直没有好好研究过java安全相关的问题,总是会看到 SecurityManger sm = System.getSecurityManager(); if(sm!=null ...

  4. AES加密时的 java.security.InvalidKeyException: Illegal key size 异常

    程序代码 // 设置加密模式为AES的CBC模式 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); SecretKe ...

  5. 利用Java自带的MD5加密java.security.MessageDigest;

    MD5加密算法,即"Message-Digest Algorithm 5(信息-摘要算法)",它由MD2.MD3.MD4发展而来的一种单向函数算法(也就是HASH算法),它是国际著 ...

  6. Java之加密(信息摘要)工具类(依赖:java.security.MessageDigest或org.apache.commons.codec.digest.DigestUtils)

    依赖于java.security.MessageDigest,支持MD5,SHA-1,SHA-256 import java.security.MessageDigest; import java.s ...

  7. AES加密时抛出java.security.InvalidKeyException: Illegal key size or def

    原文:AES加密时抛出java.security.InvalidKeyException: Illegal key size or def 使用AES加密时,当密钥大于128时,代码会抛出 java. ...

  8. Java实现AES加密,异常java.security.InvalidKeyException: Illegal key size 的解决

    Java实现AES加密,抛出异常如下:java.security.InvalidKeyException: Illegal key size 代码参考 http://my.oschina.net/Ja ...

  9. AES加密时抛出java.security.InvalidKeyException: Illegal key size or default parametersIllegal key size or default parameters

    使用AES加密时,当密钥大于128时,代码会抛出java.security.InvalidKeyException: Illegal key size or default parameters Il ...

随机推荐

  1. iOS 阶段学习第25天笔记(iOS沙盒机制介绍)

    iOS学习(OC语言)知识点整理 一.iOS沙盒机制介绍 1)概念: 每个ios应用都有自己的应用沙盒,应用沙盒就是文件系统目录,与其他应用放入文件 系统隔离,ios系统不允许访问 其他应用的应用沙盒 ...

  2. Jquery验证插件 JqueryValidation 动态验证用户名等

    可以参考:http://www.w3cschool.cc/jquery/jquery-plugin-validate.html //form1 验证用户名 $("#form1"). ...

  3. SSH框架执行自己定义的SQL语句

    直接上代码 String hsql = "delete XTable x where x.Userid= ?"; Query query = this.getSession().c ...

  4. 完善ext.grid.panel中的查询功能(紧接上一篇)

    今天的代码主要是实现,Ext.grid.panel中的查询,其实我也是一名extjs新手,开始想的实现方式是另外再创建一个新的grid类来存放查询出的数据(就是有几个分类查询就创建几个grid类),这 ...

  5. 引用计数 vs. GC

    内存管理问题 内存管理是编程过程中的一个经典问题,早期在 C 语言时代,几乎都靠 malloc/free 手动管理内存.随着各个平台的发展,到现在被广泛采用的主要有两个方法: 引用计数 (ARC,Au ...

  6. PDF.NET 开发框架之 SOD框架 Ver 5.2 正式版开源源码发布

    PDF.NET 开发框架之 SOD框架 Ver 5.2.1.0307 正式版发布,包含以下部分: SOD_Pwmis.Core --包括下列数据提供程序 SqlServer SqlServerCe A ...

  7. java内存模型-总结

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM 和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM 和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照 ...

  8. IClient for js开发之地图的加载

    进行web开发之前首先需要安装IServer以及iClient for JavaScript的开发包.在这两中都具备的前提下进行第一步,如何调用IServer中发布的服务 调用iServer 中发布的 ...

  9. C# 如何在Excel 动态生成PivotTable

    Excel 中的透视表对于数据分析来说,非常的方便,而且很多业务人员对于Excel的操作也是非常熟悉的,因此用Excel作为分析数据的界面,不失为一种很好的选择.那么如何用C#从数据库中抓取数据,并在 ...

  10. 如何在Visual Studio中开发自己的代码生成器插件

     Visual Studio是美国微软公司开发的一个基本完整的开发工具集,它包括了整个软件生命周期中所需要的大部分工具,如UML工具.代码管控工具.集成开发环境(IDE)等等,且所写的目标代码适用于微 ...