记一次java代码生成的SSL证书在EMQX服务器校验失败原因和解决,附上代码

前段时间做设备证书服务端生成,网上copy一堆代码,但是结果生成的证书在emqx里都是认证失败,无奈,请教同事,请教度娘,请教各种论坛,废话有点多,下面说正题。
使用服务器上生成的SSL证书格式为PKCS1的格式,而java代码内提供的是PKCS8 具体两种格式的差异可以百度 PKCS8 和PKCS1。
找到问题的根源那么就容易了,然后开始问度娘 两种格式的转换,废话不多说,直接上代码,以下代码均来源于网络:

引入相关依赖
<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.11</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.5</version>
		</dependency>
		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.60</version>
		</dependency>

java内转换代码

/**
	 * PKCS8转PKCS1
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	private String pkcs8To1(PublicKey key) throws Exception {
		byte[] pubBytes = Base64.decodeBase64(Base64.encodeBase64URLSafeString(key.getEncoded()));

		SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(pubBytes);
		ASN1Primitive primitive = spkInfo.parsePublicKey();
		byte[] publicKeyPKCS1 = primitive.getEncoded();

		return pkcs1ToPem(publicKeyPKCS1, false);
	}
    /**
	 * PKCS8转PKCS1
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	private String pkcs8To1(PrivateKey key) throws Exception {
		String privateKey = Base64.encodeBase64URLSafeString(key.getEncoded());
		return privatePem(privateKey);
	}
	public String privatePem(String privateKey) throws Exception {
		byte[] privBytes = Base64.decodeBase64(privateKey);

		PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes);
		ASN1Encodable encodable = pkInfo.parsePrivateKey();
		ASN1Primitive primitive = encodable.toASN1Primitive();
		byte[] privateKeyPKCS1 = primitive.getEncoded();

		return pkcs1ToPem(privateKeyPKCS1, false);
	}

	private String pkcs1ToPem(byte[] pcks1KeyBytes, boolean isPublic) throws Exception {
		String type;
		if (isPublic) {
			type = "RSA PUBLIC KEY";
		} else {
			type = "RSA PRIVATE KEY";
		}

		PemObject pemObject = new PemObject(type, pcks1KeyBytes);
		StringWriter stringWriter = new StringWriter();
		PemWriter pemWriter = new PemWriter(stringWriter);
		pemWriter.writeObject(pemObject);
		pemWriter.close();
		String pemString = stringWriter.toString();

		return pemString;
	}
后边是完整的生成证书的代码
   import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import sun.misc.BASE64Encoder;
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.AlgorithmId;
import sun.security.x509.AuthorityKeyIdentifierExtension;
import sun.security.x509.BasicConstraintsExtension;
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.KeyIdentifier;
import sun.security.x509.SubjectKeyIdentifierExtension;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

@SuppressWarnings("restriction")
public class X509CertService {

	private static final String NEW_LINE = System.getProperty("line.separator");
	/** 在将java生成的证书导出到文件的时候,需要将下面两行信息对应的添加到证书内容的头部后尾部 */
	private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----";
	private static final String END_CERTIFICATE = "-----END CERTIFICATE-----";

	/** 证书签名算法, e.g. SHA256withRSA */
	private static final String SIGN_ALGO = "SHA256withRSA";
	/** 私钥安全算法,e.g. RSA */
	private static final String RSA_ALGORITHM = "RSA";
	/** 验证有效期 */
	public static final int VALIDITY_DAY = 365;
	/** subject默认信息 */
	public static final String SUBJECT_FORMAT = "CN=%s/O=OwnTracks.org/OU=generate-CA/emailAddress=nobody@example.net";

	@Data
	@AllArgsConstructor
	@NoArgsConstructor
	@Builder
	public static class X509Sign {
		/** 私钥 */
		private PrivateKey privateKey;
		/** 签发的证书 */
		private X509Certificate certificate;
		/** 签发的私钥字符串 */
		private String privateCer;
		/** 签发的公钥字符串 */
		private String publicKeyCer;
	}

	/**
	 * 生成证书的公钥和私钥
	 * 
	 * @param algorithm
	 * @param digestSignAlgo
	 * @param keySize
	 * @return
	 * @throws Exception
	 */
	public CertAndKeyGen generateCertAndKeyGen() throws Exception {

		CertAndKeyGen certAndKeyGen = new CertAndKeyGen(RSA_ALGORITHM, SIGN_ALGO, null);
		// 生成一对key 参数为key的长度 对于rsa不能小于512
		certAndKeyGen.generate(2048);
		certAndKeyGen.setRandom(new SecureRandom());
		return certAndKeyGen;
	}

	/**
	 * 创建根证书, 并保存根证书到指定路径的文件中, crt和key分开存储文件。
	 * 创建SSL根证书的逻辑,很重要,此函数调用频次不高,创建根证书,也就是自签名证书。
	 *
	 * @param algorithm      私钥安全算法,e.g. RSA
	 * @param keySize        私钥长度,越长越安全,RSA要求不能小于512, e.g. 2048
	 * @param digestSignAlgo 信息摘要以及签名算法 e.g. SHA256withRSA
	 * @param subj           证书所有者信息描述,e.g.
	 *                       CN=iotp,OU=tkcloud,O=taikang,L=wuhan,S=hubei,C=CN
	 * @param validDays      证书有效期天数,e.g. 3650即10年
	 * @param rootCACrtPath  根证书所要存入的全路径,e.g. /opt/certs/iot/rootCA.crt
	 * @param rootCAKeyPath  根证书对应秘钥key所要存入的全路径,e.g. /opt/certs/iot/rootCA.key
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws InvalidKeyException
	 * @throws IOException
	 * @throws CertificateException
	 * @throws SignatureException
	 * @throws UnrecoverableKeyException
	 * @return 私钥和证书对的map对象
	 * @throws Exception
	 */
	public X509Sign signRoot(String subject) throws Exception {

		Date now = new Date();
		// 参数分别为 公钥算法 签名算法 providerName(因为不知道确切的 只好使用null 既使用默认的provider)
		CertAndKeyGen cak = generateCertAndKeyGen();
		// 证书拥有者subject的描述name
		sun.security.x509.X500Name _subject = new X500Name(subject);

		// 给证书配置扩展信息
		PublicKey publicKey = cak.getPublicKey();
		PrivateKey privateKey = cak.getPrivateKey();

		CertificateExtensions exts = new CertificateExtensions();
		exts.set(SubjectKeyIdentifierExtension.NAME,
				new SubjectKeyIdentifierExtension((new KeyIdentifier(publicKey)).getIdentifier()));
		exts.set(AuthorityKeyIdentifierExtension.NAME,
				new AuthorityKeyIdentifierExtension(new KeyIdentifier(publicKey), null, null));
		// 设置是否根证书
		BasicConstraintsExtension bce = new BasicConstraintsExtension(true, -1);
		exts.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(false, bce.getExtensionValue()));

		// 配置证书的有效期,并生成根证书(自签名证书)
		long validate = 3650 * 24L * 60L * 60L;
		X509Certificate certificate = cak.getSelfCertificate(_subject, now, validate, exts);

		String privatePkcs1 = pkcs8To1(privateKey);
		String publicPkcs1 = pkcs8To1(publicKey);
		return new X509Sign(privateKey, certificate, privatePkcs1, publicPkcs1);
	}

	public static void main(String[] args) throws Exception {
		X509CertService service = new X509CertService();

		String deviceId = "1.2.156.156.11.11.55";
		String caSubject = "CN=An MQTT broker/O=jd.com/OU=iot-platform/emailAddress=iot-platform@example.com";

		X509Sign ca = service.signRoot(caSubject);
		service.export(ca, "ca");

		String serverSubject = "CN=localhost/O=jd.com/OU=iot-platform/emailAddress=iot-platform@example.com";
		X509Sign server = service.sign(serverSubject);
		service.export(server, "server");

		String clientSubject = String.format("CN=%s/O=OwnTracks.org/OU=generate-CA/emailAddress=nobody@example.net", deviceId);
		X509Sign device = service.sign(clientSubject);
		service.export(device, "1.2.156.156.11.11.55");
	}

	private KeyPair createKeyPair() throws Exception {
		// 为RSA算法创建一个KeyPairGenerator对象
		KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
		// 初始化KeyPairGenerator对象,密钥长度
		kpg.initialize(2048);
		// 生成密匙对
		return kpg.generateKeyPair();
	}

	/**
	 * 签发证书
	 * 
	 * @param subject
	 * @param extend
	 * @return
	 * @throws Exception
	 */
	public X509Sign sign(String subject) throws Exception {
		String path = "/work/admin/x509";
		X509Certificate ca = getCertficate(new File(path + "/ca.crt"));
		// 根证书key
		PrivateKey key = getPrivateKey(path + "/ca.key");
		KeyPair gen = createKeyPair();
		PrivateKey privateKey = gen.getPrivate();
		PublicKey publicKey = gen.getPublic();
		return sign(ca, key, privateKey, publicKey, subject, VALIDITY_DAY);
	}

	/**
	 * 签发证书
	 * 
	 * @param subject
	 * @param extend
	 * @return
	 * @throws Exception
	 */
	public X509Sign sign(String subject, int day) throws Exception {
		String path = "/work/admin/x509";
		X509Certificate ca = getCertficate(new File(path + "/ca.crt"));
		// 根证书key
		PrivateKey key = getPrivateKey(path + "/ca.key");
		KeyPair gen = createKeyPair();
		PrivateKey privateKey = gen.getPrivate();
		PublicKey publicKey = gen.getPublic();
		return sign(ca, key, privateKey, publicKey, subject, day);
	}

	/**
	 * 创建X509的证书, 由ca证书完成签名。
	 *
	 * subject,issuer都遵循X500Principle规范, 即: X500Principal由可分辨名称表示,例如“CN = Duke,OU =
	 * JavaSoft,O = Sun Microsystems,C = US”。
	 *
	 * @param ca        根证书对象
	 * @param caKey     CA证书对应的私钥对象
	 * @param publicKey 待签发证书的公钥对象
	 * @param subj      证书拥有者的主题信息,签发者和主题拥有者名称都转写X500Principle规范,格式:CN=country,ST=state,L=Locality,OU=OrganizationUnit,O=Organization
	 * @param validDays 证书有效期天数
	 * @param sginAlgo  证书签名算法, e.g. SHA256withRSA
	 *
	 * @return cert 新创建得到的X509证书
	 */
	public static X509Certificate createUserCert(X509Certificate ca, PrivateKey caKey, PublicKey publicKey, String subj,
			long validDays, String sginAlgo) {

		// 获取ca证书
		X509Certificate caCert = ca;

		X509CertInfo x509CertInfo = new X509CertInfo();

		try {
			// 设置证书的版本号
			x509CertInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));

			// 设置证书的序列号,基于当前时间计算
			x509CertInfo.set(X509CertInfo.SERIAL_NUMBER,
					new CertificateSerialNumber((int) (System.currentTimeMillis() / 1000L)));

			/**
			 * 下面这个设置算法ID的代码,是错误的,会导致证书验证失败,但是报错不是很明确。 若将生成的证书存为keystore,让后keytool转换 会出现异常。
			 * AlgorithmId algorithmId = new AlgorithmId(AlgorithmId.SHA256_oid);
			 */
			AlgorithmId algorithmId = AlgorithmId.get(sginAlgo);
			x509CertInfo.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algorithmId));

			// 设置证书的签发者信息
			X500Name issuer = new X500Name(caCert.getIssuerX500Principal().toString());
			x509CertInfo.set(X509CertInfo.ISSUER, issuer);

			// 设置证书的拥有者信息
			X500Name subject = new X500Name(subj);
			x509CertInfo.set(X509CertInfo.SUBJECT, subject);

			// 设置证书的公钥
			x509CertInfo.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));

			// 设置证书有效期
			Date beginDate = new Date();
			Date endDate = new Date(beginDate.getTime() + validDays * 24 * 60 * 60 * 1000L);
			CertificateValidity cv = new CertificateValidity(beginDate, endDate);
			x509CertInfo.set(X509CertInfo.VALIDITY, cv);

			CertificateExtensions exts = new CertificateExtensions();

			/*
			 * 以上是证书的基本信息 如果要添加用户扩展信息 则比较麻烦 首先要确定version必须是v3否则不行 然后按照以下步骤
			 *
			 */
			exts.set("SubjectKeyIdentifier",
					new SubjectKeyIdentifierExtension((new KeyIdentifier(publicKey)).getIdentifier()));
			exts.set("AuthorityKeyIdentifier",
					new AuthorityKeyIdentifierExtension(new KeyIdentifier(ca.getPublicKey()), null, null));
			exts.set("BasicConstraints", new BasicConstraintsExtension(false, false, 0));
			x509CertInfo.set("extensions", exts);

		} catch (CertificateException cee) {
			cee.printStackTrace();
		} catch (IOException eio) {
			eio.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		// 获取CA私钥
		PrivateKey caPrivateKey = caKey;
		// 用CA的私钥给当前证书进行签名,获取最终的下游证书(证书链的下一节点)
		X509CertImpl cert = new X509CertImpl(x509CertInfo);
		try {
			cert.sign(caPrivateKey, sginAlgo);
		} catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException | NoSuchProviderException
				| SignatureException e3) {
			e3.printStackTrace();
		}
		return cert;
	}

	/**
	 * 签证用户证书
	 * 
	 * @param ca                   根证书对象
	 * @param privateKey待签发证书的私钥对象
	 * @param publicKey            待签发证书的公钥对象
	 * @param subject              证书拥有者的主题信息,签发者和主题拥有者名称都转写X500Principle规范,格式:CN=country,ST=state,L=Locality,OU=OrganizationUnit,O=Organization
	 * @return X509Certificate 新创建得到的X509证书
	 */
	public X509Sign sign(X509Certificate ca, PrivateKey caKey, PrivateKey privateKey, PublicKey publicKey,
			String subject, int day) throws Exception {
		X509CertInfo x509CertInfo = new X509CertInfo();
		// 证书版本,此方案采用V3
		x509CertInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
		// 设置证书的序列号,基于当前时间计算
		x509CertInfo.set(X509CertInfo.SERIAL_NUMBER, getCertSerualNumber());
		/**
		 * 下面这个设置算法ID的代码,是错误的,会导致证书验证失败,但是报错不是很明确。 若将生成的证书存为keystore,让后keytool转换 会出现异常。
		 * AlgorithmId algorithmId = new AlgorithmId(AlgorithmId.SHA256_oid);
		 */
		AlgorithmId algorithmId = AlgorithmId.get(SIGN_ALGO);
		x509CertInfo.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algorithmId));

		// 设置证书的签发者信息
		X500Name issuer = new X500Name(ca.getIssuerX500Principal().toString());
		x509CertInfo.set(X509CertInfo.ISSUER, issuer);

		// 设置证书的拥有者信息
		x509CertInfo.set(X509CertInfo.SUBJECT, new X500Name(subject));

		// 设置证书的公钥
		x509CertInfo.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));

		// 设置证书有效期
		x509CertInfo.set(X509CertInfo.VALIDITY, getCertValidity(day));
		// 以上是证书的基本信息 如果要添加用户扩展信息 则比较麻烦 首先要确定version必须是v3否则不行 然后按照以下步骤
		CertificateExtensions exts = new CertificateExtensions();

		exts.set(SubjectKeyIdentifierExtension.NAME,
				new SubjectKeyIdentifierExtension((new KeyIdentifier(publicKey)).getIdentifier()));
		exts.set(AuthorityKeyIdentifierExtension.NAME,
				new AuthorityKeyIdentifierExtension(new KeyIdentifier(publicKey), null, null));
		exts.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(false, false, -1));

		x509CertInfo.set(CertificateExtensions.NAME, exts);

		// 用CA的私钥给当前证书进行签名,获取最终的下游证书(证书链的下一节点)
		X509CertImpl certificate = new X509CertImpl(x509CertInfo);
		certificate.sign(caKey, SIGN_ALGO);
		String privatePkcs1 = pkcs8To1(privateKey);
		String publicPkcs1 = pkcs8To1(publicKey);
		return new X509Sign(privateKey, certificate, privatePkcs1, publicPkcs1);
	}

	public byte[] readCaCert() throws Exception {
		String output = "/work/admin/x509/ca.crt";
		File file = new File(output);
		if (!file.exists()) {
			throw new Exception("证书不存在");
		}
		// 这个客户端证书,是用来发送给服务端的,准备做双向验证用的。
		FileInputStream input = null;
		byte[] b = null;
		try {
			input = new FileInputStream(file);
			b = new byte[input.available()];
			input.read(b);
		} finally {
			if (input != null) {
				input.close();
			}
		}
		return b;
	}

	/**
	 * 读取证书
	 * 
	 * @param name
	 * @return
	 * @throws Exception
	 */
	public byte[] readDeviceCertificate(String name) throws Exception {
		String output = getDeviceOutput();
		File file = new File(output + "/" + name + ".crt");
		if (!file.exists()) {
			throw new Exception("设备证书不存在!");
		}
		// 这个客户端证书,是用来发送给服务端的,准备做双向验证用的。
		FileInputStream input = null;
		byte[] b = null;
		try {
			input = new FileInputStream(file);
			b = new byte[input.available()];
			input.read(b);
		} finally {
			if (input != null) {
				input.close();
			}
		}
		return b;
	}

	/**
	 * 获取设备证书
	 * 
	 * @param name
	 * @return
	 * @throws Exception
	 */
	public Certificate getDeviceCertificate(String name) throws Exception {
		String output = getDeviceOutput();
		File file = new File(output + "/" + name + ".crt");
		if (!file.exists()) {
			throw new Exception("设备证书不存在!");
		}
		return getCertficate(file);
	}

	private void bakFile(File file) {
		if (file.exists()) {
			file.renameTo(new File(file.getAbsolutePath() + "." + System.currentTimeMillis()));
		}
	}

	/**
	 * 将JAVA创建的证书内容导出到文件, 基于BASE64转码了。
	 *
	 *
	 * @param devCrt  设备证书对象
	 * @param crtPath 设备证书存储路径
	 */
	private void exportCrt(Certificate devCrt, String exportPath) throws Exception {
		BASE64Encoder base64Crt = new BASE64Encoder();
		FileOutputStream fosCrt = null;
		File file = new File(exportPath);
		bakFile(file);
		try {
			fosCrt = new FileOutputStream(file);
			String cont = BEGIN_CERTIFICATE + NEW_LINE;
			fosCrt.write(cont.getBytes());
			base64Crt.encodeBuffer(devCrt.getEncoded(), fosCrt);
			cont = END_CERTIFICATE;
			fosCrt.write(cont.getBytes());
		} finally {
			if (fosCrt != null) {
				try {
					fosCrt.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * PKCS8转PKCS1
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	private String pkcs8To1(PublicKey key) throws Exception {
		byte[] pubBytes = Base64.decodeBase64(Base64.encodeBase64URLSafeString(key.getEncoded()));

		SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(pubBytes);
		ASN1Primitive primitive = spkInfo.parsePublicKey();
		byte[] publicKeyPKCS1 = primitive.getEncoded();

		return pkcs1ToPem(publicKeyPKCS1, false);
	}

	/**
	 * PKCS8转PKCS1
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	private String pkcs8To1(PrivateKey key) throws Exception {
		String privateKey = Base64.encodeBase64URLSafeString(key.getEncoded());
		return privatePem(privateKey);
	}

	/**
	 * 导出私钥内容到文件中,以base64编码。 注意,java生成的私钥文件默认是PKCS#8的格式,加载的时候,要注意对应关系。
	 *
	 * @param key
	 * @param keyPath
	 */
	private void exportKey(String key, String exportPath) throws Exception {
		FileOutputStream fosKey = null;
		File file = new File(exportPath);
		bakFile(file);
		try {
			fosKey = new FileOutputStream(file);
			fosKey.write(key.getBytes());
		} finally {
			if (fosKey != null) {
				try {
					fosKey.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public String privatePem(String privateKey) throws Exception {
		byte[] privBytes = Base64.decodeBase64(privateKey);

		PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes);
		ASN1Encodable encodable = pkInfo.parsePrivateKey();
		ASN1Primitive primitive = encodable.toASN1Primitive();
		byte[] privateKeyPKCS1 = primitive.getEncoded();

		return pkcs1ToPem(privateKeyPKCS1, false);
	}

	private String pkcs1ToPem(byte[] pcks1KeyBytes, boolean isPublic) throws Exception {
		String type;
		if (isPublic) {
			type = "RSA PUBLIC KEY";
		} else {
			type = "RSA PRIVATE KEY";
		}

		PemObject pemObject = new PemObject(type, pcks1KeyBytes);
		StringWriter stringWriter = new StringWriter();
		PemWriter pemWriter = new PemWriter(stringWriter);
		pemWriter.writeObject(pemObject);
		pemWriter.close();
		String pemString = stringWriter.toString();

		return pemString;
	}

	private String getDeviceOutput() {
		return "/work/admin/x509/device";
	}

	/**
	 * 读取设备私钥
	 * 
	 * @param deviceId
	 * @return
	 * @throws Exception
	 */
	public String readDeviceKey(String deviceId) throws Exception {
		String output = getDeviceOutput() + "/" + deviceId + ".key";
		File file = new File(output);
		byte[] bytes = FileUtils.readFileToByteArray(file);
		return new String(bytes);
	}

	/**
	 * 利用开源的工具类BC解析私钥,例如openssl私钥文件格式为pem,需要去除页眉页脚后才能被java读取
	 *
	 * @param file 私钥文件
	 * @return 私钥对象
	 * @throws Exception
	 */
	public PrivateKey getPrivateKey(String privateKeyPath) throws Exception {
		File file = new File(privateKeyPath);
		PrivateKey privKey = null;
		PemReader pemReader = null;
		try {
			pemReader = new PemReader(new FileReader(file));
			PemObject pemObject = pemReader.readPemObject();
			byte[] pemContent = pemObject.getContent();
			// 支持从PKCS#1或PKCS#8 格式的私钥文件中提取私钥, PKCS#1的私钥,主要是openssl默认生成的编码格式
			if (pemObject.getType().endsWith("RSA PRIVATE KEY")) {
				// 取得私钥 for PKCS#1 openssl genrsa 默认生成的私钥就是PKCS1的编码
				org.bouncycastle.asn1.pkcs.RSAPrivateKey asn1PrivateKey = org.bouncycastle.asn1.pkcs.RSAPrivateKey
						.getInstance(pemContent);
				RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(asn1PrivateKey.getModulus(),
						asn1PrivateKey.getPrivateExponent());
				KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
				privKey = keyFactory.generatePrivate(rsaPrivateKeySpec);
			} else if (pemObject.getType().endsWith("PRIVATE KEY")) {
				/*
				 * java创建的私钥,默认是PKCS#8格式 通过openssl pkcs8 -topk8转换为pkcs8,例如(-nocrypt不做额外加密操作):
				 * openssl pkcs8 -topk8 -in pri.key -out pri8.key -nocrypt 取得私钥 for PKCS#8
				 */
				PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(pemContent);
				KeyFactory kf = KeyFactory.getInstance(RSA_ALGORITHM);
				privKey = kf.generatePrivate(privKeySpec);
			}
		} finally {
			if (pemReader != null) {
				pemReader.close();
			}
		}
		return privKey;
	}

	/**
	 * 导出
	 * 
	 * @param sign
	 * @param name
	 * @throws Exception
	 */
	public void export(X509Sign sign, String name) throws Exception {
		String output = getDeviceOutput();
		exportCrt(sign.getCertificate(), output + "/" + name + ".crt");
		exportKey(sign.getPrivateCer(), output + "/" + name + ".key");
	}

	/**
	 * 从经过base64转化后的证书文件中构建证书对象,是一个标准的X509证书,
	 *
	 * 且非常重要的是,文件头部含有-----BEGIN CERTIFICATE----- 文件的尾部含有 -----END CERTIFICATE-----
	 * 若没有上述头和尾部,证书验证的时候会报certificate_unknown。
	 *
	 * @param crtFile 经过base64处理的证书文件
	 * @return X509的证书
	 */
	public X509Certificate getCertficate(File crtFile) throws Exception {
		// 这个客户端证书,是用来发送给服务端的,准备做双向验证用的。
		CertificateFactory cf;
		X509Certificate cert = null;
		FileInputStream crtIn = null;
		try {
			cf = CertificateFactory.getInstance("X509");
			crtIn = new FileInputStream(crtFile);
			cert = (X509Certificate) cf.generateCertificate(crtIn);
		} finally {
			if (crtIn != null) {
				try {
					crtIn.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return cert;
	}

	/**
	 * 得到新证书的序列号
	 * 
	 * @return CertificateSerialNumber
	 */
	private CertificateSerialNumber getCertSerualNumber() {
		Calendar vCal = null;
		vCal = Calendar.getInstance();
		int vSerialNum = 0;
		vSerialNum = (int) (vCal.getTimeInMillis() / 1000);
		return new CertificateSerialNumber(vSerialNum);
	}

	/**
	 * 
	 * 得到新证书有效日期
	 * 
	 * @throws Exception
	 * @return CertificateValidity
	 */

	private CertificateValidity getCertValidity(int day) throws Exception {
		long vValidity = (60 * 60 * 24 * 1000L) * day;
		Calendar vCal = null;
		Date vBeginDate = null, vEndDate = null;
		vCal = Calendar.getInstance();
		vBeginDate = vCal.getTime();
		vEndDate = vCal.getTime();
		vEndDate.setTime(vBeginDate.getTime() + vValidity);
		return new CertificateValidity(vBeginDate, vEndDate);
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值