android平台使用java动态生成公私钥,并导出证书文件

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

 

 

你可能感兴趣的:(Android开发)