不依赖keytool工具,指令生成证书库,而是java代码生成,且导出到证书文件中。直接上代码:
证书工具类:
package com.daobo.security.utils import com.daobo.security.bean.Certification import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.x509.X509V1CertificateGenerator import sun.misc.BASE64Decoder import sun.misc.BASE64Encoder import java.io.BufferedInputStream import java.io.FileInputStream import java.math.BigInteger import java.security.* import java.security.Security.addProvider import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.RSAKeyGenParameterSpec import java.util.* import javax.security.auth.x500.X500Principal object CertificationUtil { const val strDnInfo : String = "CN=Test Certificate" /** * @param pair 密钥对 * @param startDate 有效期 * @param endDate 有效期 * @param info 证书信息 * @return * @throws InvalidKeyException * @throws NoSuchProviderException * @throws SignatureException */ @Throws(InvalidKeyException::class, NoSuchProviderException::class, SignatureException::class) @SuppressWarnings("deprecation") fun generateV1Certificate(pair: KeyPair, startDate: Date, endDate: Date, info: X500Principal): X509Certificate { // generate the certificate addProvider(BouncyCastleProvider()) val certGen = X509V1CertificateGenerator() certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())) certGen.setIssuerDN(info) certGen.setNotBefore(startDate) certGen.setNotAfter(endDate) certGen.setSubjectDN(X500Principal(strDnInfo)) certGen.setPublicKey(pair.public) // i get error here certGen.setSignatureAlgorithm("SHA256WithRSAEncryption") return certGen.generateX509Certificate(pair.private, "BC") } /** * * 生成证书文件 * @param address 文件保存的路径 * @param startDate 有效期 * @param endDate 有效期 * @param info 证书信息 * @param algorithm 算法名称 * @param keySize 密钥长度 * @param random 随机源 * @throws Exception */ @Throws(Exception::class) fun writeFilePkCert(fileName: String, startDate: Date, endDate: Date, info: X500Principal, algorithm: String, keySize: Int, random: SecureRandom, kpGen : Certification) { // create the keys val kp = kpGen.getKeyPair(algorithm, keySize, random) // generate the certificate val cert = generateV1Certificate(kp, startDate, endDate, info) // show some basic validation cert.checkValidity(Date()) cert.verify(cert.publicKey) CertFileUtil.getInstance(null).writeToFile(fileName, BASE64Encoder().encode(cert.encoded)) //System.out.println("valid certificate generated:"+cert.getPublicKey()); } @Throws(Exception::class) fun writeFileSkCert(fileName: String, kpGen : Certification) { //直接将私钥 string 写进到文件 CertFileUtil.getInstance(null).writeToFile(fileName, getKeyAsString(kpGen.privateKey!!)) //System.out.println("valid certificate generated:"+cert.getPublicKey()); } fun getKeyAsString(key: Key): String { val keyBytes = key.encoded val b64 = BASE64Encoder() return b64.encode(keyBytes) } //将String类型转换为PrivateKey类型 @Throws(Exception::class) fun getPrivateKeyFromString(key: String, algorithm: String): PrivateKey { val keyFactory = KeyFactory.getInstance(algorithm) val b64 = BASE64Decoder() val privateKeySpec = PKCS8EncodedKeySpec(b64.decodeBuffer(key)) return keyFactory.generatePrivate(privateKeySpec) } /** * 获取证书对象 * @param address 证书文件路径 * @return * @throws Exception */ @Throws(Exception::class) fun getCert(address: String): X509Certificate? { var cert: X509Certificate? = null val fis = FileInputStream(address) val bis = BufferedInputStream(fis) val cf = CertificateFactory.getInstance("X.509") while (bis.available() > 0) { cert = cf.generateCertificate(fis) as X509Certificate? } return cert } }certification Bean:
package com.daobo.security.bean import java.math.BigInteger import java.security.* import java.text.SimpleDateFormat import java.util.* import javax.crypto.Cipher class Certification { var strName : String = "" var strCreateTime : String = "" var strEffectiveTime : String = "" // 私钥: var privateKey: PrivateKey? = null // 公钥: var publicKey: PublicKey? = null constructor(name : String, createTime : String, effectiveTime: String){ strName = name strCreateTime = createTime strEffectiveTime = effectiveTime } constructor(name : String, effectiveTime : String) { this.strName = name this.strEffectiveTime = effectiveTime val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") strCreateTime = format.format(Date()) } @Throws(GeneralSecurityException::class, NoSuchAlgorithmException::class) fun getKeyPair(algorithm : String, keySize : Int, random : SecureRandom) : KeyPair { val kpGen = KeyPairGenerator.getInstance(algorithm) kpGen.initialize(keySize, random) val keyPair = kpGen.generateKeyPair() this.privateKey = keyPair!!.private this.publicKey = keyPair!!.public return keyPair } fun isExpired () : Boolean { var date = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(strEffectiveTime) if(date.before(Date())){ return true } return false } // 把私钥导出为字节 fun getPrivateKey(): ByteArray { return this.privateKey!!.encoded } // 把公钥导出为字节 fun getPublicKey(): ByteArray { return this.publicKey!!.encoded } // 用公钥加密: @Throws(GeneralSecurityException::class) fun encrypt(message: ByteArray): ByteArray { val cipher = Cipher.getInstance("RSA") cipher.init(Cipher.ENCRYPT_MODE, this.publicKey) return cipher.doFinal(message) } // 用私钥解密: @Throws(GeneralSecurityException::class) fun decrypt(input: ByteArray): ByteArray { val cipher = Cipher.getInstance("RSA") cipher.init(Cipher.DECRYPT_MODE, this.privateKey) return cipher.doFinal(input) } }写文件的工具类:
package com.daobo.security.utils import android.content.Context import android.os.Environment import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.OutputStreamWriter import java.nio.charset.Charset class CertFileUtil { private var applicationContext : Context? = null var certRootPath : String = "" companion object { private var instance: CertFileUtil? = null fun getInstance(context: Context?): CertFileUtil { if (instance == null) instance = CertFileUtil(context) return instance!! } } private constructor (context: Context?) { this.applicationContext = context LogUtil.init(LogUtil.INFO_LEVEL, "$certRootPath/daobo/log/") val state = Environment.getExternalStorageState() if (Environment.MEDIA_MOUNTED == state) { // 已经挂载了sd卡 certRootPath = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS).absolutePath certRootPath += "/daobo/cert/" } else { LogUtil.info( "=== 读取 sd 状态不可用!===") } } fun certPKFileName (certName : String) : String { return certName + "_pk.cer" } fun certSKFileName (certName : String) : String { return certName + "_sk.cer" } fun writeToFile(fileName : String, fileData : String) { try { val filePath = File(certRootPath) if (!filePath.exists()) { filePath.mkdirs() } val file = File(filePath, fileName) val fos = FileOutputStream(file) val wr = OutputStreamWriter(fos, Charset.forName("UTF-8")) //wr.write("-----BEGIN CERTIFICATE-----\n") wr.write(fileData) //wr.write("\n-----END CERTIFICATE-----\n") wr.flush() wr.close() //val fos = FileOutputStream(file) //fos.write(fileData) //fos.flush() fos.close() }catch (e:Exception) { e.printStackTrace() } } fun readCertFile(fileName : String) : ByteArray { var filePath = File(certRootPath) var files = filePath.listFiles() if(files == null || files.isEmpty()){ //Toast.makeText(context, "文件为空!", Toast.LENGTH_SHORT).show() LogUtil.info("directory=daoboCert=== 文件为空!===") return ByteArray(0) } // 拿到输入流 for(f in files){ if(fileName == f.name){ val input = FileInputStream(files[0]) try { // 建立存储器 var buf = ByteArray(input.available()) // 读取到存储器 input.read(buf) return buf } catch (e: Exception) { e.printStackTrace() }finally { // 关闭输入流 input.close() } } } return ByteArray(0) } }说明下,需要依赖一个证书的库:bcprov-jdk15to18-165.jar (bouncycastle)链接,还需要单独去下载一个Android使用的base64编码的工具jar
公钥是有对应的生成cer证书的操作,但是私钥没有,一半经过base64直接写到文件中。公钥证书有几个关键的证书参数:
//获取发布者标识 Principal principalIssuer = x509Certificate.getIssuerDN(); //获取证书的主体标识 Principal principalSubject = x509Certificate.getSubjectDN(); //保存证书的序列号 list.add(x509Certificate.getSerialNumber())
