纯Java实现数字证书生成签名的简单实例

package com.ylsoft.cert;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Vector;

import sun.misc.BASE64Encoder;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertAndKeyGen;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.ExtendedKeyUsageExtension;
import sun.security.x509.Extension;
import sun.security.x509.KeyIdentifier;
import sun.security.x509.KeyUsageExtension;
import sun.security.x509.SubjectKeyIdentifierExtension;
import sun.security.x509.X500Name;
import sun.security.x509.X500Signer;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

/**
 * 首先生成CA的根证书,然后有CA的根证书签署生成ScriptX的证书
 *
 * @author Administrator
 *
 */
public class GenX509Cert {
	/** 提供强加密随机数生成器 (RNG)* */
	private SecureRandom sr;

	public GenX509Cert() throws NoSuchAlgorithmException,
			NoSuchProviderException {
		// 返回实现指定随机数生成器 (RNG) 算法的 SecureRandom 对象。
		sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
	}

	public void createCert(X509Certificate certificate, PrivateKey rootPrivKey,
			KeyPair kp) throws CertificateException, IOException,
			InvalidKeyException, NoSuchAlgorithmException,
			NoSuchProviderException, SignatureException {

		// X.509 v1 证书的抽象类。此类提供了一种访问 X.509 v1 证书所有属性的标准方式。
		byte certbytes[] = certificate.getEncoded();

		// The X509CertImpl class represents an X.509 certificate.
		X509CertImpl x509certimpl = new X509CertImpl(certbytes);

		// The X509CertInfo class represents X.509 certificate information.
		X509CertInfo x509certinfo = (X509CertInfo) x509certimpl
				.get("x509.info");

		// This class defines the X509Key attribute for the Certificate.
		x509certinfo.set("key", new CertificateX509Key(kp.getPublic()));

		// This class defines the Extensions attribute for the Certificate
		CertificateExtensions certificateextensions = new CertificateExtensions();

		certificateextensions.set("SubjectKeyIdentifier",
				new SubjectKeyIdentifierExtension((new KeyIdentifier(kp
						.getPublic())).getIdentifier()));

		x509certinfo.set("extensions", certificateextensions);

		// 设置issuer域
		X500Name issuer = new X500Name(
				"CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN");
		x509certinfo.set("issuer.dname", issuer);

		// Constructs a name from a conventionally formatted string, such as
		// "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US". (RFC 1779 or RFC
		// 2253 style).
		X500Name subject = new X500Name(
				"CN=scriptx, OU=wps, O=wps, L=BJ, ST=BJ, C=CN");

		x509certinfo.set("subject.dname", subject);

		// 此 Signature 类用来为应用程序提供数字签名算法功能。返回实现指定签名算法的 Signature 对象。
		Signature signature = Signature.getInstance("MD5WithRSA");

		// 初始化这个用于签名的对象。如果使用其他参数再次调用此方法,此调用的结果将无效。
		signature.initSign(kp.getPrivate());

		// This class provides a binding between a Signature object and an
		// authenticated X.500 name (from an X.509 certificate chain), which is
		// needed in many public key signing applications.
		X500Signer signer = new X500Signer(signature, issuer);

		// This class identifies algorithms, such as cryptographic transforms,
		// each of which may be associated with parameters.
		AlgorithmId algorithmid = signer.getAlgorithmId();

		// This class defines the AlgorithmId for the Certificate.
		x509certinfo
				.set("algorithmID", new CertificateAlgorithmId(algorithmid));

		// 开始时间
		Date bdate = new Date();

		// 结束时间
		Date edate = new Date();

		// 天 小时 分 秒 毫秒
		edate.setTime(bdate.getTime() + 3650 * 24L * 60L * 60L * 1000L);

		// validity为有效时间长度 单位为秒,This class defines the interval for which the
		// certificate is valid.证书的有效时间
		CertificateValidity certificatevalidity = new CertificateValidity(
				bdate, edate);

		x509certinfo.set("validity", certificatevalidity);

		// This class defines the SerialNumber attribute for the Certificate.
		// 设置有效期域(包含开始时间和到期时间)域名等同与x509certinfo.VALIDITY
		x509certinfo.set("serialNumber", new CertificateSerialNumber(
				(int) (new Date().getTime() / 1000L)));

		// 设置序列号域,This class defines the version of the X509 Certificate.
		CertificateVersion cv = new CertificateVersion(CertificateVersion.V3);

		x509certinfo.set(X509CertInfo.VERSION, cv);

		// 设置版本号 只有v1 ,v2,v3这几个合法值
		/**
		 * 以上是证书的基本信息 如果要添加用户扩展信息 则比较麻烦 首先要确定version必须是v3否则不行 然后按照以下步骤
		 */
		ObjectIdentifier oid = new ObjectIdentifier(new int[] { 2, 5, 29, 15 });

		// 生成扩展域的id 是个int数组 第1位最大2 第2位最大39 最多可以几位不明....
		String userData = "Digital Signature, Non-Repudiation, Key Encipherment, Data Encipherment (f0)";

		byte l = (byte) userData.length();// 数据总长17位

		byte f = 0x04;

		byte[] bs = new byte[userData.length() + 2];

		bs[0] = f;

		bs[1] = l;

		for (int i = 2; i < bs.length; i++) {
			bs[i] = (byte) userData.charAt(i - 2);
		}

		Extension ext = new Extension(oid, true, bs);

		// 生成一个extension对象 参数分别为 oid,是否关键扩展,byte[]型的内容值
		// 其中内容的格式比较怪异 第一位是flag 这里取4暂时没出错 估计用来说明数据的用处的 第2位是后面的实际数据的长度,然后就是数据
		// 密钥用法
		KeyUsageExtension keyUsage = new KeyUsageExtension();

		keyUsage.set(KeyUsageExtension.DIGITAL_SIGNATURE, true);

		keyUsage.set(KeyUsageExtension.NON_REPUDIATION, true);

		keyUsage.set(KeyUsageExtension.KEY_ENCIPHERMENT, true);

		keyUsage.set(KeyUsageExtension.DATA_ENCIPHERMENT, true);

		// 增强密钥用法
ObjectIdentifier ekeyOid = new ObjectIdentifier(new int[] { 1, 3, 6, 1,
				5, 5, 7, 3, 3 });

Vector<ObjectIdentifier> vkeyOid = new Vector<ObjectIdentifier>();

vkeyOid.add(ekeyOid);

		ExtendedKeyUsageExtension exKeyUsage = new ExtendedKeyUsageExtension(
				vkeyOid);

		CertificateExtensions exts = new CertificateExtensions();

		exts.set("keyUsage", keyUsage);

		exts.set("extendedKeyUsage", exKeyUsage);

		// 如果有多个extension则都放入CertificateExtensions 类中,
		x509certinfo.set(X509CertInfo.EXTENSIONS, exts);
		// 设置extensions域

		X509CertImpl x509certimpl1 = new X509CertImpl(x509certinfo);

		x509certimpl1.sign(rootPrivKey, "MD5WithRSA");
		// 使用另一个证书的私钥来签名此证书 这里使用 md5散列 用rsa来加密

		BASE64Encoder base64 = new BASE64Encoder();

		FileOutputStream fos = new FileOutputStream(new File("f:\\ScriptX.crt"));

		base64.encodeBuffer(x509certimpl1.getEncoded(), fos);

		try {
			Certificate[] certChain = { x509certimpl1 };

			savePfx("scriptx", kp.getPrivate(), "123456", certChain,
					"f:\\ScriptX.pfx");

			FileInputStream in = new FileInputStream("F:\\ScriptX.pfx");

			KeyStore inputKeyStore = KeyStore.getInstance("pkcs12");

			inputKeyStore.load(in, "123456".toCharArray());

			Certificate cert = inputKeyStore.getCertificate("scriptx");

			System.out.print(cert.getPublicKey());

			PrivateKey privk = (PrivateKey) inputKeyStore.getKey("scriptx",
					"123456".toCharArray());

			FileOutputStream privKfos = new FileOutputStream(new File(
					"f:\\ScriptX.pvk"));

			privKfos.write(privk.getEncoded());

			System.out.print(privk);
			// base64.encode(key.getEncoded(), privKfos);

			in.close();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 生成文件
		x509certimpl1.verify(certificate.getPublicKey(), null);

	}

	/**
	 * 保存此根证书信息KeyStore Personal Information Exchange
	 *
	 * @param alias
	 * @param privKey
	 * @param pwd
	 * @param certChain
	 * @param filepath
	 * @throws Exception
	 */
	public void savePfx(String alias, PrivateKey privKey, String pwd,
			Certificate[] certChain, String filepath) throws Exception {
		// 此类表示密钥和证书的存储设施。
		// 返回指定类型的 keystore 对象。此方法从首选 Provider 开始遍历已注册安全提供者列表。返回一个封装 KeyStoreSpi
		// 实现的新 KeyStore 对象,该实现取自第一个支持指定类型的 Provider。
		KeyStore outputKeyStore = KeyStore.getInstance("pkcs12");

		System.out.println("KeyStore类型:" + outputKeyStore.getType());

		// 从给定输入流中加载此 KeyStore。可以给定一个密码来解锁 keystore(例如,驻留在硬件标记设备上的 keystore)或检验
		// keystore 数据的完整性。如果没有指定用于完整性检验的密码,则不会执行完整性检验。如果要创建空
		// keystore,或者不能从流中初始化 keystore,则传递 null 作为 stream 的参数。注意,如果此 keystore
		// 已经被加载,那么它将被重新初始化,并再次从给定输入流中加载。
		outputKeyStore.load(null, pwd.toCharArray());

		// 将给定密钥(已经被保护)分配给给定别名。如果受保护密钥的类型为
		// java.security.PrivateKey,则它必须附带证明相应公钥的证书链。如果底层 keystore 实现的类型为
		// jks,则必须根据 PKCS #8 标准中的定义将 key 编码为
		// EncryptedPrivateKeyInfo。如果给定别名已经存在,则与别名关联的 keystore
		// 信息将被给定密钥(还可能包括证书链)重写。
		outputKeyStore
				.setKeyEntry(alias, privKey, pwd.toCharArray(), certChain);

		// KeyStore.PrivateKeyEntry pke=new
		// KeyStore.PrivateKeyEntry(kp.getPrivate(),certChain);
		// KeyStore.PasswordProtection password=new
		// KeyStore.PasswordProtection("123456".toCharArray());
		// outputKeyStore.setEntry("scriptx", pke, password);

		FileOutputStream out = new FileOutputStream(filepath);

		// 将此 keystore 存储到给定输出流,并用给定密码保护其完整性。
		outputKeyStore.store(out, pwd.toCharArray());

		out.close();
	}

	public void saveJks(String alias, PrivateKey privKey, String pwd,
			Certificate[] certChain, String filepath) throws Exception {

		KeyStore outputKeyStore = KeyStore.getInstance("jks");

		System.out.println(outputKeyStore.getType());

		outputKeyStore.load(null, pwd.toCharArray());

		outputKeyStore
				.setKeyEntry(alias, privKey, pwd.toCharArray(), certChain);

		// KeyStore.PrivateKeyEntry pke=new
		// KeyStore.PrivateKeyEntry(kp.getPrivate(),certChain);
		// KeyStore.PasswordProtection password=new
		// KeyStore.PasswordProtection("123456".toCharArray());
		// outputKeyStore.setEntry("scriptx", pke, password);

		FileOutputStream out = new FileOutputStream(filepath);

		outputKeyStore.store(out, pwd.toCharArray());

		out.close();
	}

	/**
	 * 颁布根证书,自己作为CA
	 *
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws InvalidKeyException
	 * @throws IOException
	 * @throws CertificateException
	 * @throws SignatureException
	 * @throws UnrecoverableKeyException
	 */
	public void createRootCA() throws NoSuchAlgorithmException,
			NoSuchProviderException, InvalidKeyException, IOException,
			CertificateException, SignatureException, UnrecoverableKeyException {

		// 参数分别为公钥算法、签名算法 providername(因为不知道确切的 只好使用null 既使用默认的provider)
		// Generate a pair of keys, and provide access to them.
		CertAndKeyGen cak = new CertAndKeyGen("RSA", "MD5WithRSA", null);

		// Sets the source of random numbers used when generating keys.
		cak.setRandom(sr);

		// Generates a random public/private key pair, with a given key size.
		cak.generate(1024);

		// Constructs a name from a conventionally formatted string, such as
		// "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US". (RFC 1779 or RFC
		// 2253 style)
		X500Name subject = new X500Name(
				"CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN");

		// Returns a self-signed X.509v3 certificate for the public key. The
		// certificate is immediately valid. No extensions.
		// Such certificates normally are used to identify a "Certificate
		// Authority" (CA). Accordingly, they will not always be accepted by
		// other parties. However, such certificates are also useful when you
		// are bootstrapping your security infrastructure, or deploying system
		// prototypes.自签名的根证书
		X509Certificate certificate = cak.getSelfCertificate(subject,
				new Date(), 3650 * 24L * 60L * 60L);

		X509Certificate[] certs = { certificate };

		try {

			savePfx("RootCA", cak.getPrivateKey(), "123456", certs,
					"f:\\RootCa.pfx");

		} catch (Exception e) {

			e.printStackTrace();

		}

		// 后一个long型参数代表从现在开始的有效期 单位为秒(如果不想从现在开始算 可以在后面改这个域)
		BASE64Encoder base64 = new BASE64Encoder();

		FileOutputStream fos = new FileOutputStream(new File("f:\\RootCa.crt"));

		// fos.write(certificate.getEncoded());

		// 生成(保存)cert文件 base64加密 当然也可以不加密
		base64.encodeBuffer(certificate.getEncoded(), fos);

		fos.close();

	}

	public void signCert() throws NoSuchAlgorithmException,
			CertificateException, IOException, UnrecoverableKeyException,
			InvalidKeyException, NoSuchProviderException, SignatureException {

		try {

			KeyStore ks = KeyStore.getInstance("pkcs12");

			FileInputStream ksfis = new FileInputStream("f:\\RootCa.pfx");

			char[] storePwd = "123456".toCharArray();

			char[] keyPwd = "123456".toCharArray();

			// 从给定输入流中加载此 KeyStore。
			ks.load(ksfis, storePwd);

			ksfis.close();

			// 返回与给定别名关联的密钥(私钥),并用给定密码来恢复它。必须已经通过调用 setKeyEntry,或者以
			// PrivateKeyEntry
			// 或 SecretKeyEntry 为参数的 setEntry 关联密钥与别名。
			PrivateKey privK = (PrivateKey) ks.getKey("RootCA", keyPwd);

			// 返回与给定别名关联的证书。如果给定的别名标识通过调用 setCertificateEntry 创建的条目,或者通过调用以
			// TrustedCertificateEntry 为参数的 setEntry
			// 创建的条目,则返回包含在该条目中的可信证书。如果给定的别名标识通过调用 setKeyEntry 创建的条目,或者通过调用以
			// PrivateKeyEntry 为参数的 setEntry 创建的条目,则返回该条目中证书链的第一个元素。
			X509Certificate certificate = (X509Certificate) ks
					.getCertificate("RootCA");

			createCert(certificate, privK, genKey());

		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public KeyPair genKey() throws NoSuchAlgorithmException {

		KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");

		kpg.initialize(1024, sr);

		System.out.print(kpg.getAlgorithm());

		KeyPair kp = kpg.generateKeyPair();

		return kp;
	}

	public static void main(String[] args) {

		try {

			GenX509Cert gcert = new GenX509Cert();

			gcert.createRootCA();

			gcert.signCert();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

以上这篇纯Java实现数字证书生成签名的简单实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java加密和数字签名编程

    本文主要谈一下密码学中的加密和数字签名,以及其在java中如何进行使用.对密码学有兴趣的伙伴,推荐看Bruce Schneier的著作:Applied Crypotography.在jdk1.5的发行版本中安全性方面有了很大的改进,也提供了对RSA算法的直接支持,现在我们从实例入手解决问题(本文仅是作为简单介绍): 一.密码学上常用的概念 1)消息摘要: 这是一种与消息认证码结合使用以确保消息完整性的技术.主要使用单向散列函数算法,可用于检验消息的完整性,和通过散列密码直接以文本形式保存等,目前

  • 纯Java实现数字证书生成签名的简单实例

    package com.ylsoft.cert; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator;

  • Java File类的详解及简单实例

    Java File类的详解及简单实例 1. File():构造函数,一般是依据文件所在的指定位置来创建文件对象.  CanWrite():返回文件是否可写. CanRead():返回文件是否可读. CompareTo(File pathname):检查指定文件路径间的顺序. Delet():从文件系统内删除该文件. DeleteOnExit():程序顺利结束时从系统中删除文件. Equals(Object obj):检查特定对象的路径名是否相等. Exists():判断文件夹是否存在. GetA

  • Java实现一个小说采集程序的简单实例

    被标题吸引进来的不要骂我. 只是一个简单的实现,随手写了来下载一部喜欢的小说的.示例中的小说只是示例,不是我的菜. 使用了jsoup.挺好用的一个工具. 有需要的话,参考下自己改吧.挺简单的,是吧. 代码如下: package com.zhyea.doggie; import java.io.File; import java.io.FileWriter; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.n

  • 用Java程序判断是否是闰年的简单实例

    我们知道,(1)如果是整百的年份,能被400整除的,是闰年:(2)如果不是整百的年份,能被4整除的,也是闰年.每400年,有97个闰年.鉴于此,程序可以作以下设计: 第一步,判断年份是否被400整除,能的话,就是闰年.比如1600.2000.2400年是闰年. 第二步,在第一步不成立的基础上,判断年份能否被100整除,如果是,则不是闰年.比如1900.2100.2200年不是闰年. 第三步,在第二步不成立的基础上,判断年份能否被4整除,如果是,则是闰年.比如1996.2004.2008年是闰年.

  • java StringBuilder类的详解及简单实例

     java  StringBuilder类的详解及简单实例 实现代码: public class StringBuilderTest { /** * @param args */ public static void main(String[] args) { StringBuilder sb = new StringBuilder(); // 追加字符串 sb.append("java");//sb = "java" // 插入 sb.insert(0 , &qu

  • Js调用Java方法并互相传参的简单实例

    Js通过PhoneGap调用Java方法并互相传参的. 一.JAVA代码 写一个类,该类继承自Plugin并重写execute方法. import org.json.JSONArray; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.DialogInterface; impor

  • java在文件尾部追加内容的简单实例

    如下所示: import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; /** * 将内容追加到文件尾部. * @author haicheng.cao * */ public class AppendToFile { /** * A方法追加文件:使用RandomAccessFile */ public static void appendMethodA(String fileNa

  • python 实现红包随机生成算法的简单实例

    实例如下: </pre><pre name="code" class="python">#! /usr/bin/python # -*- coding: utf-8 -*- import random class CDispatch: def __init__(self,sum,count): self.sum = sum self.count=count #print 'init here sum =',sum,',count =',cou

  • java  StringBuilder类的详解及简单实例

     java  StringBuilder类的详解及简单实例 实现代码: public class StringBuilderTest { /** * @param args */ public static void main(String[] args) { StringBuilder sb = new StringBuilder(); // 追加字符串 sb.append("java");//sb = "java" // 插入 sb.insert(0 , &qu

  • Java中的泛型方法详解及简单实例

      java 泛型方法: 泛型是什么意思在这就不多说了,而Java中泛型类的定义也比较简单,例如:public class Test<T>{}.这样就定义了一个泛型类Test,在实例化该类时,必须指明泛型T的具体类型,例如:Test<Object> t = new Test<Object>();,指明泛型T的类型为Object. 但是Java中的泛型方法就比较复杂了. 泛型类,是在实例化类的时候指明泛型的具体类型:泛型方法,是在调用方法的时候指明泛型的具体类型.   定

随机推荐