zoukankan      html  css  js  c++  java
  • Java Security:Java加密框架(JCA)简要说明

          加密服务总是关联到一个特定的算法或类型,它既提供了密码操作(如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"));
        }
    }
    View Code

    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;        
        }
    
    }
    View Code

    这个自定义的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();
        }
    }
    View Code

    在这个用例中,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);
        }
    View Code

    第一次调用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:
    "+puk);
        }
    }
    View Code

     

    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;
    }
    View Code

     

    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);
            
        }
        
    }
    View Code

     

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

  • 相关阅读:
    获取windows所有用户名
    windbg内存查看(d*)
    Windbg查看调用堆栈(k*)
    Windbg调试互斥体(Mutex)死锁
    Windbg调试关键区(CriticalSection)死锁
    "R6002 floating point support not loaded"错误
    由可变参数引起的崩溃
    【Dubbo源码学习】负载均衡算法(2)-轮询算法的实现
    jdk1.8源码解析(1):HashMap源码解析
    Java annotation浅析
  • 原文地址:https://www.cnblogs.com/f1194361820/p/4262507.html
Copyright © 2011-2022 走看看