不依赖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())