程序员人生 网站导航

深入理解Android之Java Security第一部分

栏目:综合技术时间:2015-04-11 09:10:51

深入理解Android之Java Security(第1部份)

从事Android工作4年以来,只有前1年不到的时间是用C++在开发东西(主要是开发DLNA组件,目前我已将它们全部开源,参考http://blog.csdn.net/innost/article/details/40216763),后面的工作几近都在用Java。自以为Java相干的东西都见过了,可前段时间有个朋友给我花了1个多小时讲授他们某套系统的安全部系结构,其中触及到很多专业术语,比如Message Digest(消息摘要)、Digital Signature(数字签名)、KeyStore(恕我不知道翻译成甚么好,还是用英文原称吧)、CA(Certificate Authority)等。我当时脑袋就大了,尼玛弄Java这么久,历来没接触过啊。为此,我特地到AndroidFramework代码中查询了下,Android平台里与之相干的东西还有1个KeyChain。

原来,上述内容都属于Java世界中1个早已存在的知识模块,那就是JavaSecurity。Java Security包括很多知识点,常见的有MD5,DigitalSignature等,而Android在Java Seurity以外,拓展了1个android.security包,此包中就提供了KeyChain。

本文将介绍Java Security相干的基础知识,然后介绍下Android平台上与之相干的使用处景。

实际上,在1些金融,银行,电子支付方面的利用程序中,JavaSecurity使用的地方非常多。

代码路径:

  • Security.java:libcore/lunl/src/main/java/java/security/
  • TrustedCertificateStore.java:libcore /crypto/src/main/java/org/conscrypt/

本文例子:

https://code.csdn.net/Innost/androidsecuritydemo  目前已公然。

1  Java Security背景知识

Java Security实际上是Java平台中1个比较独立的模块。除软件实现上内容外,它实际上对应了1系列的规范。从Java2开始,Java Security包括主要3个重要的规范:

  • JavaCryptography Extension(简写为JCE),JCE所包括的内容有加解密,密钥交换,消息摘要(Message Digest,比如MD5等),密钥管理等。本文所触及的大部份内容都属于JCE的范畴。
  • JavaSecure Socket Extension(简写为JSSE),JSSE所包括的内容就是Java层的SSL/TLS。简单点说,使用JSSE就能够创建SSL/TLS socket了。
  • JavaAuthentication and Authorization Service(简写为JAAS),JSSA和认证/授权有关。这部份内容在客户端接触得会比较少1点,所以本文不拟讨论它。

在上述3个子模块或规范中,JCE是JavaSecurity的大头,其他两个子模块JSSE和JAAS都依赖于它,比如SSL/TLS在工作进程中需要使用密钥对数据进行加解密,那末密钥的创建和使用就依托JCE子模块了。

另外,既然和安全相干,那末对安全敏感的相干部门或政府肯定会有所干涉。Java是在美国被发明的,所以美国政府对Java Security方面的出口(比如哪些模块,哪些功能能给其他国家使用)有相干的限制。例如,不允许出口的JCE(从软件实现上看,可能就是从Java官网上下载到的几个Jar包文件)支持1些高级的加解密功能(比如在密钥长度等方面有所限制)。

注意,Java Security包括的内容非常多,而本文将重点关注Java SecurityAPI与其使用方法方面的知识。对Java Security其他细节感兴趣的同学可在浏览完本文后,再浏览参考文献[1]。

 

1.1  JCE介绍

1.1.1  JCE设计架构介绍

介绍JCE之前,先来说解下JCE的设计架构。JCE在设计时重点关注两个问题:

  • 其功能具体实现的独立性和可互操作性。
  • 使用算法的独立性和可扩大性。

对独立性而言,1个最通用的做法就是把定义和实现通过抽象类或基类的方式进行解耦合。在JCE中:

  • 它首先把功能组合成1个1个的Service(也称作Engine)。比如,对应“加解密”的API组合成CipherService,对应“消息摘要”的API组合成MessageDigest Service,对应“签名”的API组合成SignatureService。JCE为这些Service都定义了1个诸如XXXSpi的抽象类。其中,SPI是Service Provider Interface的缩写。
  • 上述SPI的具体实现则由不同的Provider来提供。比如JDK会提供1组默许的实现(固然,这些实现之前是由Sun公司,现在由Oracle提供。在这个时候,前面提到的政府管制就可以插1脚了)。注意,Provider可以提供1组Service的实现,也能够提供单个Service的实现。

注意,可互操作性是指Provider A实现的MD5值,能被Provider B辨认。明显,这个要求是公道的。

图1所示为JCE中1些类的定义:


图1  JCE中1些类的定义

图1展现了JCE中的1些类定义。大家先关注左下角的Provider和Security类:

  • Security是java.security包的主要管理类。通过它可向JCE框架注册不同的Provider。
  • Android平台上,每个利用程序在启动的时候都会默许注册1个类型为AndroidKeyStoreProvider的对象。

注意,由于历史缘由,JCE的Service分散定义在好些个Package中:

  • java.security:此Package包括最多的Service定义。
  • javax.crypto:此package包括加解密相干的Service定义。

上述这两个package都属于JCE的内容。从使用者的角度来看,其实不太需要关注它们到底定义在哪一个Package中(代码中通过1句import xxx就能够把对应的包引入,后续编写代码时候,直接使用相干类就行了)。

BTW,个人感觉Java类定义非常多,而且有些类和它们所在包的关系仿佛有些混乱。

 

(1)  Android平台中Provider的注册

前面已说过,JCE框架对使用者提供的是基础类(抽象类或接口类),而具体实现需要有地方注册到JCE框架中。所以,我们来看看Android平台中,JCE框架都注册了哪些Provider:

[-->Security.java]

static {

   boolean loaded = false;

   try {

       //从资源文件里搜索是不是有security.properties定义,Android平台是没有这个文件的

      InputStreamconfigStream =

         Security.class.getResourceAsStream("security.properties");

       InputStream input = newBufferedInputStream(configStream);

       secprops.load(input);

       loaded = true;

       configStream.close();

        }......

     if (!loaded) {//注册默许的Provider

        registerDefaultProviders();

     }

      ......

    }

 

   private static void registerDefaultProviders() {

     /*

    JCE对Provider的管理非常简单,就是将Provider类名和对应的优先级存在属性列表里

    比以下面的:OpenSSLProvider,它对应的优先级是1.

     优先级是甚么意思呢?前面说过,不同的Provider可以实现同1个功能集合,比如都实现

     MessageDigestSpi。那末用户在创建MessageDigest的实例时,如果没有指明Provider名,

     JCE默许从第1个(依照优先级,由1到n)Provider开始搜索,直到找到第1个实现了

     MessageDigestSpi的Provider(比如Provider X)为止。然后,MessageDigest的实例

      就会由Provider X创建。图2展现了1个例子

    */

     secprops.put("security.provider.1",

           "com.android.org.conscrypt.OpenSSLProvider");

     secprops.put("security.provider.2",

            "org.apache.harmony.security.provider.cert.DRLCertFactory");

    secprops.put("security.provider.3",

       "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider");

    secprops.put("security.provider.4",

        "org.apache.harmony.security.provider.crypto.CryptoProvider");

      //JSSE有关

      secprops.put("security.provider.5",

        "com.android.org.conscrypt.JSSEProvider");

 }

图2展现了Provider优先级使用的例子:


图2  Provider优先级示例

图2中:

  • 左侧的Application要使用MD5算法来创建1个MessageDigest实例。而左下角有3个Provider(A、B、C)。其中B和C支持MD5算法。所以JCE会使用第1个实现MD5算法的Provider(即Provider B)来创建MessageDigest实例。
  • 右侧的Application也要使用MD5算法来创建MD实例。但是它在参数中指明了Provider的名字(“ProviderC”),所以JCE就会直接使用ProviderC来创建这个MD实例。

注意,图2中的SHA⑴/256和MD5都是MessageDigest的1种算法,本文后面会介绍它们。

(2) AndroidKeyStoreProvider

Android平台中,每一个利用程序启动时都会注册1个类型为“AndroidKeyStoreProvider”的对象。这是在ActivityThread中完成的,代码以下所示:

[-->ActivityThread.java::main]

public static void main(String[] args) {

   ......

  Security.addProvider(new AndroidKeyStoreProvider());

  ......

}

来看看AndroidKeyStoreProvider是甚么,代码以下所示:

[-->AndroidKeyStoreProvider::AndroidKeyStoreProvider]

public class AndroidKeyStoreProvider extends Provider {

  public static final StringPROVIDER_NAME = "AndroidKeyStore";

  publicAndroidKeyStoreProvider() {

    super(PROVIDER_NAME, 1.0,"Android KeyStore security provider");

    put("KeyStore." + AndroidKeyStore.NAME,

          AndroidKeyStore.class.getName());

    put("KeyPairGenerator.RSA",AndroidKeyPairGenerator.class.getName());

  }

}

AndroidKeyStoreProvider很简单,但是看上去还是不明白它是干甚么的?其实,这个问题的更深层次的问题是:Provider是干甚么的?

  • Provider从Properties类派生。Properties本质上是1个属性列表,也就是它保存了1串的属性名和对应的属性值。
  • 从图2的例子可知,利用程序只说要创建MD5的MessageDigest实例。在代码中,“MD5”是1个字符串,而MessageDigest实例肯定是由某个类创建的(假定这个类名叫com.md5.test)。那末,这个“MD5”字符串就是1个Key,对应的属性值就是这个类的类名(“com.md5.test”)。JCE根据属性key,找到属性value,然后创建这个类的实例就完成了工作!

固然,Provider的内容和功能比这要复杂,不过我们对Provider的实现没甚么兴趣,大家只要知道它存储了1系列的属性key和value就能够了。JCE会根据情况去查询这些key和对应的value。

(3)  实例介绍

来个例子,看看Android系统上都有哪些Provider:

[-->DemoActivity.java::testProvider]

void testProvider(){

   e(TAG, "***Begin TestProviders***");

   //获得系统所有注册的Provider

   Provider[] providers = Security.getProviders();

   for(Providerprovider:providers){

      //输出Provider名

      e(TAG,"Provider:" + provider+" info:");

      //前面说了,provider其实就是包括了1组key/value值。下面将打印每一个Provider的

      //这些信息

      Set<Entry<Object,Object>>allKeyAndValues = provider.entrySet();

     Iterator<Entry<Object, Object>> iterator =allKeyAndValues.iterator();

      while(iterator.hasNext()){

        Entry<Object,Object> oneKeyAndValue =iterator.next();

        Object key = oneKeyAndValue.getKey();

        Object value =oneKeyAndValue.getValue();

        //打印Key的类型和值

       e(TAG,"===>" + "Keytype="+key.getClass().getSimpleName()+"

                           Key="+key.toString());

        //打印Value的类型和值

      e(TAG,"===>" + "Valuetype="+value.getClass().getSimpleName()+"

                           Value="+value.toString());

        }

     }

     e(TAG, "***End TestProviders*** ");

 }

在控制台中,通过adb logcat | grep ASDemo就能够显示testProvider的输出信息了,如图3所示:


图3  testProvider输出示例

图3打出了AndroidOpenSSLprovider的信息:

  • 其中Key和Value的类型都是String。
  • Key的值其实都是JCE中1些Service或算法的名称或别名。此处先不讨论这些细节,以后碰到再说。

了解完JCE框架后,我们分别来介绍JCE中的1些重要Service。

1.1.2  Key知识介绍

谈到安全,大家第1想到的就是密钥,即Key。那末大家再仔细想一想下面这两个问题:

  • Key从何而来?即代码中怎样创建Key。
  • Key怎样传递给外部使用者?外部使用者多是1个开发者,这时候候就不能把代码中的1个对象通过email发给他了,而是要把Key的书面表达情势(参考资料[1]把它叫做externalrepresentations,即外部表达情势。我为了强调这类表达情势,把它称为书面表达情势。便可有把Key写到文档里,发给其他人)发给人家。

图4解释了上述问题:


图4  Key示意

图4中:

  • Key怎样创建?在JCE中是通过Generator类创建的,这时候候在代码中得到的是1个Key实例对象。
  • Key怎样传递?这就触及到如何书面表达Key了,最最经常使用的方式就是把它表示成16进制(比如图4中下部Encoded Key Data“0AF34C4E56...”)。或,由于Key产生是基于算法的,这时候候就能够把参与计算的关键变量的值弄出来。比如图4右上角的“Param P=3,ParamQ=4”。所以,Key的书面表达情势有两种,1种是16进制密钥数据,1种是基于算法的关键变量(这类方式叫KeySpecification)。
  • 尔后,我们可以把16进制或关键变量发给对方。对方拿到Key的书面表达式后,下1步要做的就是还原出代码中的key对象。这时候候要用到的就是KeyFactory了。所以,KeyFactory的输入是Key的2进制数据或KeySpecification,输出就是Key对象。

在安全领域中,Key分为两种:

  • 对称Key:即加密和解密用得是同1个Key。JCE中,对称key的创建由KeyGenerator类来完成。
  • 非对称Key:即加密和解密用得是两个Key。这两个Key构成1个Key对(KeyPair)。其中1个Key叫公钥(PublicKey),另外1个Key叫私钥(PrivateKey)。公钥加密的数据只能用私钥解密,而私钥加密的数据只能用公钥解密。私钥1般自己保存,而公钥是需要发给合作方的。(此处我们还不讨论公钥和私钥的使用处景,仅限介绍公钥和私钥的概念)。JCE中,非对称Key的创建由KeyPairGenerator类来完成。

图5所示为JCE中Key相干的类和继承关系。


图5  JCE Key相干类

图5中:

  • PublicKey,PrivateKey和SecretKey都派生自Key接口。所以,这3个类也是接口类,而且没有定义新的接口。
  • DSAPublicKey和RSAPublicKey也派生自PublicKey接口。DSA和RSA是两种不同的算法。

(1)  Key实例介绍

先来看对称key的创建和导入(也就是把Key的书面表达导入到程序中并生成Key对象)

[-->DemoActivity.java::testKey]

{//对称key即SecretKey创建和导入

     //假定双方约定使用DES算法来生成对称密钥

     e(TAG,"==>secret key: generated it using DES");

     KeyGeneratorkeyGenerator = KeyGenerator.getInstance("DES");

     //设置密钥长度。注意,每种算法所支持的密钥长度都是不1样的。DES只支持64位长度密钥

     //(或许是算法本身的限制,或是不同Provider的限制,或是政府管制的限制

     keyGenerator.init(64);

     //生成SecretKey对象,即创建1个对称密钥

     SecretKey secretKey = keyGenerator.generateKey();

     //获得2进制的书面表达

     byte[] keyData =secretKey.getEncoded();

     //平常使用时,1般会把上面的2进制数组通过Base64编码转换成字符串,然后发给使用者

     String keyInBase64 =Base64.encodeToString(keyData,Base64.DEFAULT);

     e(TAG,"==>secret key: encrpted data ="+ bytesToHexString(keyData));

     e(TAG,"==>secrety key:base64code=" + keyInBase64);

     e(TAG,"==>secrety key:alg=" + secretKey.getAlgorithm());

    

    //假定对方收到了base64编码后的密钥,首先要得到其2进制表达式

   byte[] receivedKeyData =Base64.decode(keyInBase64,Base64.DEFAULT);

   //用2进制数组构造KeySpec对象。对称key使用SecretKeySpec类

   SecretKeySpec keySpec =new SecretKeySpec(receivedKeyData,”DES”);

   //创建对称Key导入用的SecretKeyFactory

   SecretKeyFactorysecretKeyFactory = SecretKeyFactory.getInstance(”DES”);

   //根据KeySpec还原Key对象,即把key的书面表达式转换成了Key对象

   SecretKey receivedKeyObject = secretKeyFactory.generateSecret(keySpec);

   byte[]encodedReceivedKeyData = receivedKeyObject.getEncoded();

   e(TAG,"==>secret key: received key encoded data ="

                                +bytesToHexString(encodedReceivedKeyData));

如果1切正常的话,红色代码和绿色代码打印出的2进制表示应当完全1样。此测试的结果如图6所示:


图6  SecretKey测试结果

此处有几点说明:

  • 对称key的创建有不同的算法支持,根据参考资料[1],1般有Blowfish, DES, DESede,HmacMD5,HmacSHA1,PBEWithMD5AndDES, and PBEWithMD5AndTripleDES这些算法。但是Android平台,乃至不同手机是不是都支持这些算法,则需要在testProvider那个例子去查询。
  • 另外,KeyGenerator如果支持上面的算法,但是SecretKeyFactory则不1定支持。比如ScreteKeyFactory则不支持HmacSHA1。

注意,本文会讨论太多算法相干的内容。

再来看KeyPair的用例:

[-->DemoActivity.java::KeyPair测试]

{//public/private key test

  e(TAG, "==>keypair: generated it using RSA");

  //使用RSA算法创建KeyPair

  KeyPairGeneratorkeyPairGenerator = KeyPairGenerator.getInstance("RSA");

  //设置密钥长度

  keyPairGenerator.initialize(1024);

  //创建非对称密钥对,即KeyPair对象

  KeyPair keyPair =keyPairGenerator.generateKeyPair();

  //获得密钥对中的公钥和私钥对象

  PublicKey publicKey =keyPair.getPublic();

  PrivateKey privateKey =keyPair.getPrivate();

  //打印base64编码后的公钥和私钥值

  e(TAG,"==>publickey:"+bytesToHexString(publicKey.getEncoded()));

  e(TAG, "==>privatekey:"+bytesToHexString(privateKey.getEncoded()));

 

  /*

   现在要斟酌如何把公钥传递给使用者。虽然可以和对称密钥1样,把2进制数组取出来,但是

   对非对称密钥来讲,JCE不支持直接通过2进制数组来还原KeySpec(多是算法不支持)。

   那该怎样办呢?前面曾说了,除直接还原2进制数组外,还可以通过具体算法的参数来还原

   RSA非对称密钥就得使用这类方法:

   1 首先我们要获得RSA公钥的KeySpec。

  */

  //获得RSAPublicKeySpec的class对象

  Class spec = Class.forName("java.security.spec.RSAPublicKeySpec");

   //创建KeyFactory,并获得RSAPublicKeySpec

  KeyFactory keyFactory = KeyFactory.getInstance("RSA");

  RSAPublicKeySpecrsaPublicKeySpec =

            (RSAPublicKeySpec)keyFactory.getKeySpec(publicKey, spec);

  //对RSA算法来讲,只要获得modulusexponent这两个RSA算法特定的参数就能够了

  BigInteger modulus =rsaPublicKeySpec.getModulus();

  BigInteger exponent =rsaPublicKeySpec.getPublicExponent();

  //把这两个参数转换成Base64编码,然后发送给对方

  e(TAG,"==>rsa pubkey spec:modulus="+

             bytesToHexString(modulus.toByteArray()));

  e(TAG,"==>rsa pubkey spec:exponent="+

             bytesToHexString(exponent.toByteArray()));

 

  //假定接收方收到了代表modulus和exponent的base64字符串并得到了它们的2进制表达式

  byte[] modulusByteArry =modulus.toByteArray();

  byte[] exponentByteArry =exponent.toByteArray();

  //由接收到的参数构造RSAPublicKeySpec对象

  RSAPublicKeySpecreceivedKeySpec = new RSAPublicKeySpec(

                    newBigInteger(modulusByteArry),

                    new BigInteger(exponentByteArry));

  //根据RSAPublicKeySpec对象获得公钥对象

  KeyFactoryreceivedKeyFactory = keyFactory.getInstance("RSA");

  PublicKey receivedPublicKey =

                 receivedKeyFactory.generatePublic(receivedKeySpec);

  e(TAG, "==>received pubkey:"+

                   bytesToHexString(receivedPublicKey.getEncoded()));

}

如果1切正常的话,上述代码中红色和黑色代码段将输出完全1样的公钥2进制数据。如图7所示:


图7  KeyPair测试示意图

在Android平台的JCE中,非对称Key的经常使用算法有“RSA”、“DSA”、“Diffie?Hellman”、“Elliptic Curve (EC)”等。

(2)  Key知识小结

我自己在学习Key的时候,最迷惑的就是前面提到的两个问题:

Key是甚么?虽然“密钥”这个词常常让我联想到实际生活中的钥匙,但是在学习JavaSecurity之前,我1直不知道在代码中(或编程时),它究竟是个甚么玩意。并且,它到底怎样创建。

创建Key以后,我怎样把它传递给其他人。就好比钥匙1样,你总得给个实物给人家吧?

现在来看这两个问题的总结性回答:):

  • JCE中,Key根据加密方法的不同,分为对称Key和非对称Key两大类。其中对称Key由类SecretKey表达,而非对称Key常常是公钥和私钥构成1个密钥对。在程序里,他们的类型分别是PublicKey、PrivateKey和KeyPair。
  • SecretKey和KeyPair都有对应的Generator类来创建。其中,创建对称Key的是KeyGenerator,创建KeyPair的是KeyPairGenerator。1方创建Key后,怎样把密钥信息传递给其他人呢?这时候就需要用到Key的外部表现情势了(我把它叫做书面表达情势)。
  • Key的书面表达式有两种,1种是直接把它的2进制数组弄出来,然后编码成base64发给对方。对方从这个base64编码字符串中得到2进制数组。这个2进制数组叫KeyEncoded Data。怎样把它转换成代码中的Key实例呢?对对称Key来讲,先用这个2进制数组构造1个SecretKeySpec,然后再用SecretKeyFactory构造出终究的SecretKey对象。
  • Key的另外1种表示式就是利用创建Key的算法。这个主要针对非对称Key。Key创建时,总是需要依赖1些特定的算法,而这些算法也会有1些参数。类似于学数学时,算法对应于1个公式,我们只要把参数值带入进去就可以得到结果1样。所以,在这类情况下,我们只要把参数保存起来,然后传递给对方可以了。对方拿到这些个参数,再构造出对应算法所需要的KeySpec对象,最后由KeyPairFactory创建终究的Key对象。

不理解上述内容的同学,请把实例代码再仔细看看!

 

1.1.3  Certificates知识介绍

JCE中,Certificates(是复数喔!)是证书之意。“证书”也是1个平常生活中经常使用的词,但是在JCE中,或说在Java Security中,它有甚么用呢?

这个问题的答案还是和Key的传递有关系。前面例子中我们说,创建密钥的人1般会把Key的书面表达情势转换成Base64编码后的字符串发给使用者。使用者再解码,然后还原Key就能够用了。

上面这个流程本身没有甚么隐患,但是,是否是随便1个人(1个组织,1个机构等等等等)给你发1个key,你就敢用呢?简单点说,你怎样判断是不是该信任给你发Key的某个人或某个机构呢?

好吧,这就好比现实生活中有个人说自己是警察,那你肯定得要他取出警官证或甚么别的东西来证明他是警察。这个警官证的作用就是证书的作用。

1般而言,我们会把Key的2进制表达式放到证书里,证书本身再填上其他信息(比如此证书是谁签发的,甚么时候签发的,有效期多久,证书的数字签名等等)。

初看起来,好像证书比直接发base64字符串要正规点,1方面它包括了证书签发者的信息,而且还有数字签名以避免内容被篡改。

但是,证书解决了信任的问题吗?很明显是没有的。由于证书是谁都可以制作的。既然不是人人都可以相信,那末,也不是随意甚么证书都可以被信任。

怎样办?先来看现实生活中是怎样解决信任问题的。

现实生活中也有很多证书,大到房产证、身份证,小到离职证明、介绍信。对方怎样判断你拿的这些证是真实有效的呢?

对头,看证书是谁/或哪一个机构的“手墨”!比如,结婚证首先要看是否是民政局的章。那....民政局是不是应当被信任呢???

好吧。关于民政局是不是应当被信任的这个问题在技术上基本无解,它是1个死循环。由于,即便能找到另外1个机构证明民政局合法有效,但这个问题仍然会顺流而上,说民政局该被信任的那个机构其本身是不是能被信任呢?....此问题终究会没完没了的问下去。

那怎样办?没甚么好办法。只要大家公认民政局能被信任,就能够了。

同理,对证书的是不是可信任问题而言:

  • 首先,在全球范围内(或是1个国家,1个地区)设置1些顶级证书签发机构,凡是由这些证书签发机构(CertificateAuthorities)签发的证书,我们要无条件的信任(不斟酌其他捏造等因素,证书是不是被篡改可通过数字签名来判断)。就和民政局似的,不信民政局还信谁?:)。
  • 这么多证书要发,顶级CA肯定忙不过来,所以还可以设立各种级别的CA,比如北京市的民政局弄1个CA,湖南省的公安厅弄1个CA。这类级别的CA是不是该无条件信任呢?不1定。看你对它的了解。anyway,这个级别的CA可以把自己拿到顶级CA那去认个证。比方说,湖南省公安厅发了1个证书,它可以到顶级CA那去盖个章。由于顶级CA是无条件信任的,所以湖南省公安厅这个证书可以被信任。
  • 公司A要发证书给客户使用,最好是先拿到上1级或相干领域的CA(不1定是顶级CA)那去盖个章。最后,公司A把盖了章的证书a发给客户使用就能够了。

客户拿到证书a后,首先要检查下:

  • 客户拿到证书a后,发现是某个CA签发的。如果是顶级CA签发的,那好办,直接信任。如果不是顶级CA签发的。客户再去查看这个发证的CA能不能被信任,溯流而上,直到跟踪到顶级CA为止。所以,证书,常常不是1个证书的事情,而是1个证书链的事情!
  • 另外,如果客户本身就信任公司A,那其实公司A也不需要去找CA认证,直接把证书a给客户就能够了。固然,这个时候的证书a就不需要CA的章了。

......唧唧歪歪半天,其实关于证书的核心问题就1个:

证书背后常常是1个证书链。

  • 大家不要小视这个问题。证书链常常会把问题弄复杂。比如甲方有CA A签发的证书,而乙方有CA B签发的证书。那末甲乙双方要相互信任的话,得把甲乙双方证书链上的证书都弄过来校验1遍,直到顶级CA为止。固然,甲乙证书链的顶级CA可能还不是同1个。

.......

为了方便,系统(PC,Android,乃至阅读器)等都会把1些顶级CA(也叫Root CA,即根CA)的证书默许集成到系统里。这些RootCA用作自己身份证明的证书(包括该CA的公钥等信息)叫根证书。根证书理论上是需要被信任的。以Android为例,它在libcore/luni/src/main/files/cacerts下放了150多个根证书(以Android 4.4为例),如图8所示:


图8  Android自带的根证书文件

我们随意打开1个根证书文件看看,以下所示:

[某证书文件的内容,用记事本打开便可]

-----BEGIN CERTIFICATE-----

MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMC

VVMxFDASBgNVBAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBD

ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9v

dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDAxMDExMTY0MTI4WhcNMjEwMTE0

MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dlbGxzIEZhcmdvMSww

KgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEvMC0G

A1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEi

MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n13

5zHCLielTWi5MbqNQ1mXx3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHE

SxP9cMIlrCL1dQu3U+SlK93OvRw6esP3E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4O

JgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5OEL8pahbSCOz6+MlsoCu

ltQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4jsNtlAHCE

AQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMB

AAGjYTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcB

CzAyMDAGCCsGAQUFBwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRw

b2xpY3kwDQYJKoZIhvcNAQEFBQADggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo

7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrvm+0fazbuSCUlFLZWohDo7qd/

0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0ROhPs7fpvcmR7

nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx

x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ

33ZwmVxwQ023tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s=

-----END CERTIFICATE-----

Certificate:  #下面是证书的明文内容

    Data:

        Version: 3 (0x2)

        Serial Number:971282334 (0x39e4979e)

    Signature Algorithm: sha1WithRSAEncryption

        Issuer: C=US, O=Wells Fargo, OU=Wells Fargo CertificationAuthority, CN=Wells Fargo Root Certificate Authority

        Validity

            Not Before: Oct11 16:41:28 2000 GMT

            Not After : Jan14 16:41:28 2021 GMT

        Subject: C=US, O=Wells Fargo, OU=Wells Fargo CertificationAuthority, CN=Wells Fargo Root Certificate Authority

        Subject Public Key Info:#Public KeyKeySpec表达式

            Public Key Algorithm: rsaEncryption  #PublicKey的算法

                Public-Key: (2048 bit)

                Modulus:

                   00:d5:a8:33:3b:26:f9:34:ff:cd:9b:7e:e5:04:47:

                   ce:00:e2:7d:77:e7:31:c2:2e:27:a5:4d:68:b9:31:

                   ba:8d:43:59:97:c7:73:aa:7f:3d:5c:40:9e:05:e5:

                    a1:e2:89:d9:4c:b8:3f:9b:f9:0c:b4:c8:62:19:2c:

                   45:ae:91:1e:73:71:41:c4:4b:13:fd:70:c2:25:ac:

                   22:f5:75:0b:b7:53:e4:a5:2b:dd:ce:bd:1c:3a:7a:

                   c3:f7:13:8f:26:54:9c:16:6b:6b:af:fb:d8:96:b1:

                    60:9a:48:e0:25:22:24:79:34:ce:0e:26:00:0b:4e:

                   ab:fd:8b:ce:82:d7:2f:08:70:68:c1:a8:0a:f9:74:

                   4f:07:ab:a4:f9:e2:83:7e:27:73:74:3e:b8:f9:38:

                   42:fc:a5:a8:5b:48:23:b3:eb:e3:25:b2:80:ae:96:

                    d4:0a:9c:c2:78:9a:c6:68:18:ae:37:62:37:5e:51:

                   75:a8:58:63:c0:51:ee:40:78:7e:a8:af:1a:a0:e1:

                   b0:78:9d:50:8c:7b:e7:b3:fc:8e:23:b0:db:65:00:

                   70:84:01:08:00:14:6e:54:86:9a:ba:cc:f9:37:10:

                   f6:e0:de:84:2d:9d:a4:85:37:d3:87:e3:15:d0:c1:

                   17:90:7e:19:21:6a:12:a9:76:fd:12:02:e9:4f:21:

                    5e:17

                Exponent: 65537 (0x10001)

        X509v3 extensions:

            X509v3 BasicConstraints: critical

                CA:TRUE

            X509v3Certificate Policies:

                Policy:2.16.840.1.114171.903.1.11

                  CPS:http://www.wellsfargo.com/certpolicy

 

    Signature Algorithm: sha1WithRSAEncryption  #数字签名,以后再讲

        d2:27:dd:9c:0a:77:2b:bb:22:f2:02:b5:4a:4a:91:f9:d1:2d:

        be:e4:bb:1a:68:ef:0e:a4:00:e9:ee:e7:ef:ee:f6:f9:e5:74:

        a4:c2:d8:52:58:c4:74:fb:ce:6b:b5:3b:29:79:18:5a:ef:9b:

        ed:1f:6b:36:ee:48:25:25:14:b6:56:a2:10:e8:ee:a7:7f:d0:

        3f:a3:d0:c3:5d:26:ee:07:cc:c3:c1:24:21:87:1e:df:2a:12:

        53:6f:41:16:e7:ed:ae:94:fa:8c:72:fa:13:47:f0:3c:7e:ae:

        7d:11:3a:13:ec:ed:fa:6f:72:64:7b:9d:7d:7f:26:fd:7a:fb:

        25:ad:ea:3e:29:7f:4c:e3:00:57:32:b0:b3:e9:ed:53:17:d9:

        8b:b2:14:0e:30:e8:e5:d5:13:c6:64:af:c4:00:d5:d8:58:24:

        fc:f5:8f:ec:f1:c7:7d:a5:db:0f:27:d1:c6:f2:40:88:e6:1f:

        f6:61:a8:f4:42:c8:b9:37:d3:a9:be:2c:56:78:c2:72:9b:59:

        5d:35:40:8a:e8:4e:63:1a:b6:e9:20:6a:51:e2:ce:a4:90:df:

        76:70:99:5c:70:43:4d:b7:b6:a7:19:64:4e:92:b7:c5:91:3c:

        7f:48:16:65:7b:16:fd:cb:fc:fb:d9:d5:d6:4f:21:65:3b:4a:

         7f:47:a3:fb

SHA1 Fingerprint=93:E6:AB:22:03:03:B5:23:28:DC:DA:56:9E:BA:E4:D1:D1:CC:FB:65

关于证书文件,还有1些容易混淆的事情要交代:

  • 前面讲过,证书有很多格式,但是目前通用格式为X.509格式。在上面的证书文件中,从Certificate这1行开始到文件最后,都是符合X.509格式的。那文件前面的“-----BEGINCERTIFICATE-----”到“-----END CERTIFICATE-----”是些甚么?
  • X.509是证书的格式,但是证书和我们看到的文件还是有1些差异。证书需要封装在文件里。不同系统支持不同的证书文件,每种证书文件,所要求包括的具体的X.509证书内容也不1样。另外,某些证书文件还能包括除X.509以外的其他有用的数据。

下面是1些常见的证书文件格式,1般用文件后缀名标示。

  • .pem(Privacy-enhanced ElectronicMail) Base64 编码的证书,编码信息(行将示例中X.509证书的明文内容用Base64编码后得到的那个字符串)放在"-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----"之间。所以,Android平台里的根CA文件都是PEM证书文件。
  • .cer,.crt, .der:证书内容为ASCII编码,2进制格式,但也能够和PEM1样采取base64编码。
  • .p7b,.p7c
------分隔线----------------------------
------分隔线----------------------------

最新技术推荐