Chinaunix首页 | 论坛 | 博客
  • 博客访问: 71267
  • 博文数量: 33
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 220
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-15 22:22
文章分类

全部博文(33)

文章存档

2016年(3)

2015年(23)

2014年(7)

我的朋友

分类: Java

2015-08-27 16:51:56

最近刚尝试实现java RSA证书生成,网上倒是有现成但变成自己的还有一段距离,首先记录一下以备自己以后查看。

第一部分:首先说明对证书内容的处理,做到能够知道证书的结构,能够替换证书的公钥以及相应的签名达到自签的目的。PS:主要是整理代码所用到的语句,因此将所有的代码罗列到一起整理。
      File rootfile = new File("RSARoot.cer");
       File userfile = new File("RSAUser.cer"); 

       FileInputStream frootin = new FileInputStream(rootfile);
       FileInputStream fuserin = new FileInputStream(userfile);

       byte[] rootcertencoded=Streams.readAll(frootin);
       byte[] usercertencoded=Streams.readAll(fuserin);

  //得到X509CertificateStructure  这里只写明得到根证书的
      ASN1Sequence seq=ASN1Sequence.getInstance(ASN1Object.fromByteArray(rootcertencoded)); //写入证书数据的时候是DEREncode形式
       X509CertificateStructure X509cert=new X509CertificateStructure(seq);

//从X509CertificationStructrue结构中得到根证书公钥信息,  X509CertificateStructure——>证书体TBSCertificateStruct——>公钥信息
      TBSCertificateStructure  tbscert=X509cert.getTBSCertificate();
       SubjectPublicKeyInfo pubkeyInfo=tbscert.getSubjectPublicKeyInfo();
       DERBitString   pubkeybit=pubkeyInfo.getPublicKeyData();
      byte[] pubkey =  pubkeybit.getBytes[];         //可以通过System.out.println(new String (Hex.encode(pubkey)));格式输出

//271位长度的公钥中提取出256位(nn)与(e)10001,其实结合放入的时候编码规则理解更好
     ASN1StreamParser aIn = new ASN1StreamParser(pubkey);
      ASN1SequenceParser    pubseq = (ASN1SequenceParser)aIn.readObject();

      DERInteger    derbN=  DERInteger.getInstance(pubseq.readObject());   //公钥写入就是DERInteger的形式所以。。。
      DERInteger    derbE=  DERInteger.getInstance(pubseq.readObject());  //这种内部代码实现知道相对应的数据值
      BigInteger bN = derbN.getValue();   
      BigInteger bE = derbE.getValue(); 

//得到证书体的hash值,验证最后明文是否正确的
     
byte[] CertTBS=x509cert.getTBSCertificate().getDEREncoded();
                     //System.out.println("=="+new String(Hex.encode(CertTBS)));
       java.security.MessageDigest md = java.security.MessageDigest.getInstance( "SHA-256" );
       md.update(CertTBS);
       byte hashvalue[] = md.digest(); 
          System.out.println("hash value is");
       System.out.println(new String(Hex.encode(hashvalue)));

      //得到签名值,是从X509CertificateStruct得到
      DERBitString certsig=x509cert.getSignature();
       BigInteger bsign = new BigInteger(1,certsig.getBytes());  
       System.out.println("Signature is");
       System.out.println(bsign.toString(16));
     
     //使用公钥验证签名
     BigInteger Message=bsign.modPow(bE, bN);
 输出形式为256byte(2048位)的(1fffff。。。。。。。。+hash)值

第二部分:生成RSA证书
  方法一:X509V3CertificateGenerator 这种方法不需要自己进行签名,但有一个问题就是不鞥自己对hash值进行填充签名

        Security.addProvider(new BouncyCastleProvider());
         String issue  = "C=CN,O=sdk,ST=wuhan,L=wuhan,CN=root,EmailAddress=**";
         String  subject ="C=CN,O=sdk,ST=wuhan,L=wuhan,CN=root,EmailAddress=**";

         RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new  BigInteger("",16),  new BigInteger("10001",16)); //(n,e)
         RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger("",16), new BigInteger("",16));      // (n,privatekey)
        KeyFactory fact;
        fact = KeyFactory.getInstance("RSA", "BC");
         PublicKey publicKey = fact.generatePublic(publicKeySpec);   //publickey中包含加密信息RSA
         PrivateKey privateKey = fact.generatePrivate(privateKeySpec);
//设置证书信息生成证书
         X509Certificate cert = null;  
          X509V3CertificateGenerator v = new X509V3CertificateGenerator(); 
         //设置证书扩展信息
                   KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature|KeyUsage.dataEncipherment);                                          
                    xeg.addExtension(X509Extensions.KeyUsage, false, keyUsage);
                     xeg.addExtension(X509Extensions.BasicConstraints, false,  new BasicConstraints(true));
                     xeg.addExtension(X509Extensions.SubjectKeyIdentifier, false, new DEROctetString(digest));               
                    X509Extensions xes = xeg.generate();
                     v.setExtensions(xes);
            v.setIssuerDN(new X509Principal(issuer));
            v.setSubjectDN(new X509Principal(subject));
            v.setSerialNumber(serial);
            v.setNotBefore(notBefore);
            v.setNotAfter(notAfter);
                  KeyFactory fact;
                  fact = KeyFactory.getInstance("RSA", "BC");
                  PublicKey publicKey = fact.generatePublic(publicKeySpec);
             v.setPublicKey(publicKey);
             v.setSignatureAlgorithm("SHA256WithRSAEncryption");     //这个函数拿不到证书主体部分
             cert = v.generateX509Certificate(privKey,"BC");
//将证书写出
           byte[] buf = cert.getEncoded();
            FileOutputStream fOut = new FileOutputStream("RSARoot.cer");
            fOut.write(buf);
            fOut.close(); 
            System.out.println("creat root cert over.........");

   方法二:V3TBSCertificateGenerator可以自己对消息进行填充,主要用到的也是这个
1. 证书参数
   Security.addProvider(new BouncyCastleProvider());
         String issue  = "C=CN,O=sdk,ST=wuhan,L=wuhan,CN=root,EmailAddress=**";
         String  subject ="C=CN,O=sdk,ST=wuhan,L=wuhan,CN=root,EmailAddress=**";

         RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new  BigInteger("",16),  new BigInteger("10001",16)); //(n,e)
         RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger("",16), new BigInteger("",16));      // (n,privatekey)
         KeyFactory fact;
        fact = KeyFactory.getInstance("RSA", "BC");
         PublicKey publicKey = fact.generatePublic(publicKeySpec);   //publickey中包含加密信息RSA
         PrivateKey privateKey = fact.generatePrivate(privateKeySpec);
//设置签名算法 sha256withRSA OID1.2.840.113549.1.1.11
       DERObjectIdentifier sigOID = new DERObjectIdentifier("1.2.840.113549.1.1.11");
       AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(sigOID , new DERNull());
2.设置证书信息
      V3TBSCertificateGenerator v3 = new V3TBSCertificateGenerator();
                       X509ExtensionsGenerator xeg = new X509ExtensionsGenerator();
                 //// //有效使用时间
                       KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature|KeyUsage.dataEncipherment);
                       xeg.addExtension(X509Extensions.KeyUsage, false, keyUsage);
                        xeg.addExtension(X509Extensions.BasicConstraints, false,  new BasicConstraints(true));
                        xeg.addExtension(X509Extensions.SubjectKeyIdentifier, false, new DEROctetString(digest));               
                        X509Extensions xes = xeg.generate();
                        v.setExtensions(xes);
 
            v3.setIssuer(issuer);
            v3.setSubject(subject);
            v3.setSerialNumber(new DERInteger(serial));
            v3.setStartDate(new DERUTCTime(notBefore));
            v3.setEndDate(new DERUTCTime(notAfter));
            v3.setSignature(algorithmIdentifier);
//特别注意一下这个设置publickeyinfo方法,如果直接这样写的话,证书产生的RSApublicke>271个byte,多一个RSA算法信息,由于publickey中
//本身含有RSA算法信息,因此在验证提取公钥的时候需要注意一下
                      DERObjectIdentifier sigOID = new DERObjectIdentifier("1.2.840.113549.1.1.1"); //RSA的OID
                      AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(sigOID, new DERNull());   
                      SubjectPublicKeyInfo publicKeyInfo1 = new SubjectPublicKeyInfo(algorithmIdentifier, publicKey.getEncoded());
                       v3.setSubjectPublicKeyInfo(publicKeyInfo1);
//可以直接作如下处理将RSA(n,e)公钥对放入,产生271位公钥信息,则可以直接用上面的证书提取公钥的方法将n,e提出
             byte[] nn= publicKeySpec.getModulus().toByteArray();  
//    //如果不是1 就系统自动进行添加字段00 ,不用自己另外添加?最后的公钥长度为271   不添加也是可以的??<271byte就能够正确读出
//公钥值
                byte[] publikey= new byte[257]; pubkey[0]= 0x00;     //n
               System.arraycopy(pubkey1, 0x00, pubkey, 0x01, 256);
             //RSA OID 1.2.840.113549.1.1.1
              byte[] ee= publicKeySpec.getPublicExponent().toByteArray();
              DERObjectIdentifier sigOID = new DERObjectIdentifier("1.2.840.113549.1.1.1");
              AlgorithmIdentifier algorithmIdentifier1 = new AlgorithmIdentifier(sigOID , new DERNull());
   //什么时候用DERInteger,什么时候用DERBitString下面这些是正确的 
               DERInteger pub = new DERInteger(nn);
               DERInteger exp= new DERInteger(ee);
               ASN1EncodableVector vector= new ASN1EncodableVector();  
               vector.add(pubBitString);
               vector.add(expBitString);
              DERSequence sequence = new DERSequence(vector);
              SubjectPublicKeyInfo publicKeyInfo2 = new SubjectPublicKeyInfo(algorithmIdentifier1 ,sequence);
              v3.setSubjectPublicKeyInfo(publicKeyInfo2);

3.进行hash签名
               ASN1EncodableVector vector= new ASN1EncodableVector();  
            ASN1EncodableVector vector = new ASN1EncodableVector();  
              vector.add(v3.generateTBSCertificate());  
               vector.add(algorithmIdentifier);  

            byte[] certBlock = v3.genrerateTBSCertificate.getDEREncoded();
            byte[] signature = null;
            java.security.MessageDigest  = java.security.MessageDigest.getInstance( "SHA-256" );
            md.update(certBlock);
            byte[] hashvalue = md.digest(); 
            System.out.println("hash value is");
            System.out.println(new String(Hex.encode(hashvalue)));
//对hash值进行填充,占2048位,这样签名出来的数据也是2048位的(这里的2048位指的是存储占用的空间)
             String  add1="1fffffffff...... "
          // System.out.println(Hex.decode(add1).length);   //这里为什么不行,长度不是2的整数倍,不能计算
             String Hash=add1+( new String(Hex.encode(hashvalue)));  
             System.out.println("待签名值:");
             System.out.println(Hash);    
              BigInteger Mm=new BigInteger(Hash,16);
//签名证书的公私钥对
             BigInteger nn=new BigInteger("  ",16);
             BigInteger privatekey=new BigInteger("",16);
             //进行签名
             BigInteger signInfo=Mm.modPow(privatekey, nn);
             signature = result.toByteArray();
          //如果首位为1则需要进行下面这种操作!
//注意这种bytetoArray()方法是以反码形式存储的,因此如果最高为为1,则会多出8位(0x00)257byte

      //System.arraycopy(signInfo, 0x01, signInfo1 , 0x00, 0x100);
             vector.add(new DERBitString(signInfo));  
             X509CertificateStructure cert1=new X509CertificateStructure(new DERSequence(vector));
             byte[] buf = cert1.getEncoded();
              FileOutputStream fOut = new FileOutputStream("RSARoot.cer");
              fOut.write(buf);
              fOut.close();
               System.out.println("creat root cert over.........");

基本的RSA证书生成都语句在里,相应的数据操作自己再做对应的研究。 

阅读(1090) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~