From 19632ce23589a227234f41a3fc4f5653916bf109 Mon Sep 17 00:00:00 2001 From: cfig Date: Fri, 10 Dec 2021 17:01:53 +0800 Subject: [PATCH] helper: massive refinement --- bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt | 7 +- bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt | 2 +- bbootimg/src/test/kotlin/KeyUtilTest.kt | 16 +- bbootimg/src/test/kotlin/avb/BlobTest.kt | 2 +- .../main/kotlin/cfig/helper/CryptoHelper.kt | 73 ++- .../src/main/kotlin/cfig/helper/Launcher.kt | 528 ++++++++++++------ .../main/kotlin/cfig/helper/OpenSslHelper.kt | 64 ++- 7 files changed, 482 insertions(+), 210 deletions(-) diff --git a/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt b/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt index d72dbee..7dee7e0 100644 --- a/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt +++ b/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt @@ -50,13 +50,14 @@ data class AuthBlob( } } - fun calcSignature(hash: ByteArray, algorithm_name: String): ByteArray { + private fun calcSignature(hash: ByteArray, algorithm_name: String): ByteArray { val alg = Algorithms.get(algorithm_name)!! return if (alg.name == "NONE") { byteArrayOf() } else { - val k = CryptoHelper.KeyBox.parse(Files.readAllBytes(Paths.get(alg.defaultKey.replace(".pem", ".pk8")))) as PrivateKey - CryptoHelper.Signer.rawRsa(k, Helper.join(alg.padding, hash)) + val k = CryptoHelper.KeyBox.parse2(Files.readAllBytes(Paths.get(alg.defaultKey.replace(".pem", ".pk8")))) as Array<*> + assert(k[0] as Boolean) + CryptoHelper.Signer.rawRsa(k[2] as PrivateKey, Helper.join(alg.padding, hash)) } } diff --git a/bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt b/bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt index a0c151f..fd94148 100644 --- a/bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt +++ b/bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt @@ -139,7 +139,7 @@ class AuxBlob( if (key == null) { algKey = Files.readAllBytes((Paths.get(alg.defaultKey))) } - val rsa = CryptoHelper.KeyBox.parse(algKey!!) as RSAPrivateKey //BC RSA + val rsa = (CryptoHelper.KeyBox.parse2(algKey!!) as Array<*>)[2] as RSAPrivateKey //BC RSA encodedKey = CryptoHelper.KeyBox.encodeRSAkey(rsa) assert(alg.public_key_num_bytes == encodedKey.size) } else { diff --git a/bbootimg/src/test/kotlin/KeyUtilTest.kt b/bbootimg/src/test/kotlin/KeyUtilTest.kt index 0eaa800..d9a5fd3 100644 --- a/bbootimg/src/test/kotlin/KeyUtilTest.kt +++ b/bbootimg/src/test/kotlin/KeyUtilTest.kt @@ -35,14 +35,14 @@ class KeyUtilTest { fun parseKeys() { val keyFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey println("Key: $keyFile") - val k = CryptoHelper.KeyBox.parse(File(keyFile.replace("pem", "pk8")).readBytes()) as RSAPrivateKey + val k = (CryptoHelper.KeyBox.parse2(File(keyFile.replace("pem", "pk8")).readBytes()) as Array<*>)[2] as RSAPrivateKey println(k.privateExponent) println(k.modulus) - val k2 = CryptoHelper.KeyBox.parse(File(keyFile).readBytes()) as org.bouncycastle.asn1.pkcs.RSAPrivateKey + val k2 = (CryptoHelper.KeyBox.parse2(File(keyFile).readBytes()) as Array<*>)[2] as org.bouncycastle.asn1.pkcs.RSAPrivateKey println(k2.privateExponent) println(k2.modulus) //KeyHelper2.parseRsaPk8(FileInputStream(keyFile).readAllBytes()) - CryptoHelper.KeyBox.parse(File(keyFile.replace("pem", "pk8")).readBytes()) + CryptoHelper.KeyBox.parse2(File(keyFile.replace("pem", "pk8")).readBytes()) } @Test @@ -87,7 +87,7 @@ class KeyUtilTest { val expectedSig = "28e17bc57406650ed78785fd558e7c1861cc4014c900d72b61c03cdbab1039e713b5bb19b556d04d276b46aae9b8a3999ccbac533a1cce00f83cfb83e2beb35ed7329f71ffec04fc2839a9b44e50abd66ea6c3d3bea6705e93e9139ecd0331170db18eba36a85a78bc49a5447260a30ed19d956cb2f8a71f6b19e57fdca43e052d1bb7840bf4c3efb47111f4d77764236d2e013fbf3b2577e4a3e01c9d166a5e890ef96210882e6e88ceca2fe3a2201f4961210d4ec6167f5dfd0e038e4a146f960caecab7d15ba65f6edcf5dbd25f5af543cfb8da4338bdbc872eec3f8e72aa8db679099e70952d3f7176c0b9111bf20ad1390eab1d09a859105816fdf92fbb" val privkFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8") - val k = CryptoHelper.KeyBox.parse(Files.readAllBytes(Paths.get(privkFile))) as PrivateKey + val k = (CryptoHelper.KeyBox.parse2(Files.readAllBytes(Paths.get(privkFile))) as Array<*>)[2] as PrivateKey val encData = CryptoHelper.Signer.rawRsa(k, data) assertEquals(expectedSig, Helper.toHexString(encData)) } @@ -108,7 +108,7 @@ class KeyUtilTest { Helper.fromHexString("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004206317a4c8d86accc8258c1ac23ef0ebd18bc3301033") val signature = Signature.getInstance("NONEwithRSA") val keyFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8") - val k = CryptoHelper.KeyBox.parse(Files.readAllBytes(Paths.get(keyFile))) as PrivateKey + val k = (CryptoHelper.KeyBox.parse2(Files.readAllBytes(Paths.get(keyFile))) as Array<*>)[2] as PrivateKey signature.initSign(k) signature.update(data) println("data size " + data.size) @@ -194,9 +194,11 @@ class KeyUtilTest { fun signData() { val data = KeyUtilTest::class.java.classLoader.getResourceAsStream("data").readAllBytes() println(Helper.toHexString(data)) - val privKey = CryptoHelper.KeyBox.parse( - KeyUtilTest::class.java.classLoader.getResourceAsStream("testkey.pk8").readAllBytes() + //@formatter:off + val privKey = ( + (CryptoHelper.KeyBox.parse2(KeyUtilTest::class.java.classLoader.getResourceAsStream("testkey.pk8").readAllBytes()) as Array<*>)[2] ) as PrivateKey + //@formatter:on println("sha256=" + Helper.toHexString(CryptoHelper.Hasher.sha256(data))) val signedHash = CryptoHelper.Signer.sha256rsa(data, privKey) println("Signed Hash: " + Helper.toHexString(signedHash)) diff --git a/bbootimg/src/test/kotlin/avb/BlobTest.kt b/bbootimg/src/test/kotlin/avb/BlobTest.kt index 902b3f2..aaf872e 100644 --- a/bbootimg/src/test/kotlin/avb/BlobTest.kt +++ b/bbootimg/src/test/kotlin/avb/BlobTest.kt @@ -35,7 +35,7 @@ class BlobTest { run {//decode pub key and check val decodedKey = CryptoHelper.KeyBox.decodeRSAkey(encodedKey) //val rsa = KeyHelper.parsePemPrivateKeyBC(ByteArrayInputStream(Helper.fromHexString(keyStr))) //BC RSA - val rsa = CryptoHelper.KeyBox.parse(Helper.fromHexString(keyStr)) as RSAPrivateKey //BC RSA + val rsa = (CryptoHelper.KeyBox.parse2(Helper.fromHexString(keyStr)) as Array<*>)[2] as RSAPrivateKey //BC RSA assert(rsa.modulus.equals(decodedKey.modulus)) assert(rsa.publicExponent.equals(decodedKey.publicExponent)) } diff --git a/helper/src/main/kotlin/cfig/helper/CryptoHelper.kt b/helper/src/main/kotlin/cfig/helper/CryptoHelper.kt index 5bcdfbf..b158338 100644 --- a/helper/src/main/kotlin/cfig/helper/CryptoHelper.kt +++ b/helper/src/main/kotlin/cfig/helper/CryptoHelper.kt @@ -9,10 +9,7 @@ import org.apache.commons.exec.PumpStreamHandler import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.util.io.pem.PemReader import org.slf4j.LoggerFactory -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.io.InputStreamReader +import java.io.* import java.math.BigInteger import java.math.RoundingMode import java.security.KeyFactory @@ -30,11 +27,40 @@ import javax.crypto.Cipher class CryptoHelper { class KeyBox { companion object { - fun parse(data: ByteArray): Any { + fun parseToPk8(kFile: String): OpenSslHelper.PK8RsaKey { + val inBytes = File(kFile).readBytes() + val k = parse2(inBytes) as Array<*> + val kType = if ((k[1] as String) == "PEM") OpenSslHelper.KeyFormat.PEM else OpenSslHelper.KeyFormat.DER + return when (k[2]) { + is org.bouncycastle.asn1.pkcs.RSAPrivateKey -> { + OpenSslHelper.PK1Key(kType, inBytes).toPk8(OpenSslHelper.KeyFormat.PEM) + } + is java.security.interfaces.RSAPrivateKey -> { + OpenSslHelper.PK8RsaKey(kType, inBytes).let { + if (it.format == OpenSslHelper.KeyFormat.DER) { + it.transform(OpenSslHelper.KeyFormat.DER, OpenSslHelper.KeyFormat.PEM) + } else { + it + } + } + } + else -> { + throw IllegalArgumentException("unknown PK1/PK8 private key") + } + } + } + + fun parse2(data: ByteArray): Any { + var bSuccess = false + var retType = "NA" + var ret: Any = false + val p = PemReader(InputStreamReader(ByteArrayInputStream(data))).readPemObject() - return if (p != null) { + if (p != null) { log.debug("parse PEM: " + p.type) - when (p.type) { + bSuccess = true + retType = "PEM" + ret = when (p.type) { "RSA PUBLIC KEY" -> { org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(p.content) as org.bouncycastle.asn1.pkcs.RSAPublicKey } @@ -59,47 +85,58 @@ class CryptoHelper { } else -> throw IllegalArgumentException("unsupported type: ${p.type}") } + return arrayOf(bSuccess, retType, ret) } else { - var bSuccess = false - var ret: Any = false //try 1 try { val spec = PKCS8EncodedKeySpec(data) val privateKey = KeyFactory.getInstance("RSA").generatePrivate(spec) log.debug("Parse PKCS8:Private") ret = privateKey + retType = "RAW" bSuccess = true } catch (e: java.security.spec.InvalidKeySpecException) { log.debug("not PKCS8:Private") } - if (bSuccess) return ret - + if (bSuccess) return arrayOf(bSuccess, retType, ret) //try 2 try { log.debug("Parse X509:Public") val spec = X509EncodedKeySpec(data) + retType = "RAW" ret = KeyFactory.getInstance("RSA").generatePublic(spec) bSuccess = true } catch (e: java.security.spec.InvalidKeySpecException) { log.debug(e.toString()) log.debug("not X509:Public") } - if (bSuccess) return ret + if (bSuccess) return arrayOf(bSuccess, retType, ret) //try 3: jks try { - val pwdArray = "androiddebugkey".toCharArray() + val envPassword = System.getProperty("password") ?: "secretpassword" + log.warn("trying with password=$envPassword") val ks = KeyStore.getInstance("JKS") - ks.load(ByteArrayInputStream(data), pwdArray) + ks.load(ByteArrayInputStream(data), envPassword.toCharArray()) + ret = ks + retType = "JKS" + bSuccess = true } catch (e: IOException) { if (e.toString().contains("Keystore was tampered with, or password was incorrect")) { - log.info("JKS password wrong") + log.info("JKS password wrong #1") + bSuccess = false + retType = "JKS" + ret = KeyStore.getInstance("JKS") + } + if (e.toString().contains("keystore password was incorrect")) { + log.info("JKS password wrong #2") bSuccess = false - ret = true + retType = "JKS" + ret = KeyStore.getInstance("JKS") } } //at last - return ret + return arrayOf(bSuccess, retType, ret) } } @@ -282,4 +319,4 @@ class CryptoHelper { } } } -} \ No newline at end of file +} diff --git a/helper/src/main/kotlin/cfig/helper/Launcher.kt b/helper/src/main/kotlin/cfig/helper/Launcher.kt index 8142c42..6871c5d 100644 --- a/helper/src/main/kotlin/cfig/helper/Launcher.kt +++ b/helper/src/main/kotlin/cfig/helper/Launcher.kt @@ -1,201 +1,393 @@ +@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE") + package cfig.helper -import com.google.common.io.Files +import cfig.helper.OpenSslHelper.KeyFormat import org.bouncycastle.util.io.pem.PemReader import org.slf4j.LoggerFactory -import java.io.ByteArrayInputStream -import java.io.File -import java.io.InputStreamReader +import java.io.* +import java.security.cert.Certificate +import java.util.* import kotlin.system.exitProcess -import cfig.helper.OpenSslHelper.KeyFormat class Launcher { companion object { + private const val hint1 = "private RSA key -> RSA public key(PEM)" + private const val hint2 = "private RSA key -> RSA public key(DER)" + private const val hint3 = "(PEM) --> (DER)" + private const val hint4 = "RSA private: PK1 <=> PK8(PEM)" + private const val hint5 = "RSA private(PK8): => Public Key(PK8, PEM)" + private const val hint7 = "RSA private: PK1 => PK8(DER)" + private const val hint10 = "PK8 RSA: PEM <=> DER" + private const val hint11 = "RSA public(PK8): PEM => DER" + private const val hintCsr = "PK1 RSA PEM ==> CSR" + private const val hintCrt = "PK1 RSA PEM ==> CRT" + private const val hint22 = "CRT ==> JKS" + private const val hintJks = "Generate JKS" + val allSupportedCommands = + listOf("genrsa", "toPublicKey", "decodePEM", "toPk8", "toPk1", "toJks", "toCsr", "toCrt").toString() + + private val log = LoggerFactory.getLogger(Launcher::class.java) + fun help() { println("Help:") println("\tcrypo.list") println("\tcrypto.key.parse ") - println("\tcrypto.key.genrsa ") - println("\tcrypto.key.1 ") + println("\tcrypto.key.0 : ") + println("\tcrypto.key.1 : $hint1") + println("\tcrypto.key.2 : $hint2") + println("\tcrypto.key.3 : $hint3") + println("\tcrypto.key.4 : $hint4") + println("\tcrypto.key.5 : $hint5") + println("\tcrypto.key.7 : $hint7") + println("\tcrypto.key.10 : $hint10") + println("\tcrypto.key.11 : $hint11") + println("\tcrypto.key.22 : $hint22") + println("\tcrypto.key.csr : $hintCsr") + println("\tcrypto.key.21 : $hintCrt") + println("\tcrypto.key.jks : $hintJks") } - } -} -fun main(args: Array) { - val log = LoggerFactory.getLogger("main") - if (args.isEmpty()) { - Launcher.help() - exitProcess(0) + fun getSize(size: Long): String? { + val kilo = 1024L + val mega = kilo * kilo + val giga = mega * kilo + val tera = giga * kilo + var s = "" + val kb: Double = size.toDouble() / kilo + val mb: Double = kb / kilo + val gb: Double = mb / kilo + val tb: Double = gb / kilo + if (size < kilo) { + s = "$size Bytes" + } else if (size in kilo until mega) { + s = String.format("%.2f", kb) + " KB" + } else if (size in mega until giga) { + s = String.format("%.2f", mb) + " MB" + } else if (size in giga until tera) { + s = String.format("%.2f", gb) + " GB" + } else if (size >= tera) { + s = String.format("%.2f", tb) + " TB" + } + return s + } } - when (args[0]) { - "crypo.list" -> { - CryptoHelper.listAll() + fun processCommand(inFile: String, info2: Properties) { + FileInputStream(inFile).use { fis -> + info2.load(fis) } - "crypto.key.parse" -> { - val ba = File(args[1]).readBytes() - val k = CryptoHelper.KeyBox.parse(ba) - if (k is Boolean) { - if (k) { - log.info("File recognized but not parsed") - } else { - log.warn("Unrecognized file " + args[1]) + log.warn("CMD=" + info2.getProperty("cmd")) + val kFile = info2.getProperty("file") + when (val cmd = info2.getProperty("cmd")) { + "genrsa" -> { + val kLen = info2.getProperty("rsa.len").toInt() + OpenSslHelper.PK1Key.generate(kLen).apply { + writeTo(kFile) } - } else { - log.info("Recognized " + k::class) + log.info("RSA key(len=$kLen) written to $kFile") } - } - "crypto.key.genrsa", "crypto.key.0" -> { - val kLen: Int = args[1].toInt() - val kOut = args[2] - OpenSslHelper.PK1Key.generate(kLen).apply { - writeTo(kOut) + "toPublicKey" -> { + val k = CryptoHelper.KeyBox.parse2(File(kFile).readBytes()) as Array<*> + val bSuccess = k[0] as Boolean + val bType = k[1] as String + var outFile = File(kFile).name + ".pub" + when (k[2]) { + is org.bouncycastle.asn1.pkcs.RSAPrivateKey -> { + val rsa = OpenSslHelper.PK1Key(data = File(kFile).readBytes()) + run { //action 1 + val hint = "private RSA key -> RSA public key(PEM)" + outFile = File(kFile).name + ".pem.pub" + val rsaPubPEM = rsa.getPublicKey(KeyFormat.PEM).apply { + writeTo(outFile) + log.info("$hint: $kFile => $outFile") + } + } + + run { //action 2 + val hint = "private RSA key -> RSA public key(DER)" + outFile = File(kFile).name + ".der.pub" + val rsaPubDer = rsa.getPublicKey(KeyFormat.DER).apply { + writeTo(outFile) + log.info("$hint: $kFile => $outFile") + } + } + } + else -> { + } + } } - log.info("RSA key(len=$kLen) written to $kOut") - } - "crypto.key.1" -> { - //Action-1: private RSA key -> RSA public key(PEM) - val hint = "private RSA key -> RSA public key(PEM)" - val kFile = args[1] - val outFile = args[2] - assert(CryptoHelper.KeyBox.parse(File(kFile).readBytes()) is org.bouncycastle.asn1.pkcs.RSAPrivateKey) - val rsa = OpenSslHelper.PK1Key(data = File(kFile).readBytes()) - val rsaPubPEM = rsa.getPublicKey(KeyFormat.PEM).apply { - writeTo(outFile) + "decodePEM" -> { + val hint = "(PEM) --> (DER)" + val outFile = File(kFile).name + ".raw" + val decodeFromPem = CryptoHelper.KeyBox.decodePem(File(kFile).readText()) + File(outFile).writeBytes(decodeFromPem) log.info("$hint: $kFile => $outFile") } - } - "crypto.key.2" -> { - //Action-2: private RSA key -> RSA public key(DER) - val kFile = args[1] - val outFile = args[2] - assert(CryptoHelper.KeyBox.parse(File(kFile).readBytes()) is org.bouncycastle.asn1.pkcs.RSAPrivateKey) - val rsa = OpenSslHelper.PK1Key(data = File(kFile).readBytes()) - val rsaPubDer = rsa.getPublicKey(KeyFormat.DER).apply { - writeTo(outFile) - log.info("RSA pub key(der) written to $outFile") + "toCrt" -> { + val hint = "PK1 RSA PEM ==> CRT" + val outFile = File(kFile).name + ".crt" + CryptoHelper.KeyBox.parseToPk8(kFile) + .toPk1() + .toV1Cert(info2.getProperty("csr.info")) + .writeTo(outFile) + log.info("$hint: $kFile => $outFile") } - } - "crypto.key.3" -> { - //Action-3: (PEM) --> (DER) - val kFile = args[1] - val outFile = args[2] - val decodeFromPem = CryptoHelper.KeyBox.decodePem(File(kFile).readText()) - Files.write(decodeFromPem, File(outFile)) - log.info("PEM ($kFile) => raw ($outFile)") - } - "crypto.key.4" -> { - //Action-4: - var hint = "RSA private: PK1 <=> PK8(PEM)" - val kFile = args[1] - val outFile = args[2] - when (val k = CryptoHelper.KeyBox.parse(File(kFile).readBytes())) { - is org.bouncycastle.asn1.pkcs.RSAPrivateKey -> { - hint = "RSA private: PK1 => PK8(PEM)" - val rsa = OpenSslHelper.PK1Key(data = File(kFile).readBytes()) - rsa.toPk8(KeyFormat.PEM).writeTo(outFile) + "toCsr" -> { + val hint = "PK1 RSA PEM ==> CSR" + val outFile = File(kFile).name + ".csr" + val inBytes = File(kFile).readBytes() + val k = (CryptoHelper.KeyBox.parse2(inBytes) as Array<*>)[2] + assert(k is org.bouncycastle.asn1.pkcs.RSAPrivateKey) { + "${k!!::class} is not org.bouncycastle.asn1.pkcs.RSAPrivateKey" } - is java.security.interfaces.RSAPrivateKey -> { - hint = "RSA private: PK8 => PK1(PEM)" - val rsa = OpenSslHelper.PK8RsaKey(data = File(kFile).readBytes()) - rsa.toPk1().writeTo(outFile) + OpenSslHelper.PK1Key(KeyFormat.PEM, inBytes).toCsr(info2.getProperty("csr.info")).writeTo(outFile) + log.info("$hint: $kFile => $outFile") + } + "toJks" -> { + val hint = "RSA ==> JKS" + val outFile = File(kFile).name + ".jks" + val pk8 = CryptoHelper.KeyBox.parseToPk8(kFile) + val crt = pk8.toPk1().toV1Cert(info2.getProperty("csr.info")) + OpenSslHelper.Pfx( + name = info2.getProperty("pfx.alias") ?: "androiddebugkey", + thePassword = info2.getProperty("pfx.password") ?: "somepassword" + ) + .generate(pk8.toPk1(), crt) + .toJks() + .writeTo(outFile) + log.info("$hint: $kFile => $outFile") + } + "toPk1" -> { + val inBytes = File(kFile).readBytes() + val k = CryptoHelper.KeyBox.parse2(inBytes) as Array<*> + val kType = if ((k[1] as String) == "PEM") KeyFormat.PEM else KeyFormat.DER + val outFile = File(info2.getProperty("file")).name + ".pk1" + assert(k[2] is java.security.interfaces.RSAPrivateKey) { + "${k[2]!!::class} is NOT java.security.interfaces.RSAPrivateKey" } - else -> { - hint = "RSA private: PK1 <=> PK8(PEM)" - log.warn(hint) - throw IllegalArgumentException("unsupported $k") + val hint = "RSA private: PK8($kType) => PK1(PEM)" + log.info("Running: $hint") + OpenSslHelper.PK8RsaKey(format = kType, data = File(kFile).readBytes()) + .toPk1() + .also { + log.info("$kFile -> $outFile") + } + .writeTo(outFile) + } + "toPk8" -> { + val inBytes = File(kFile).readBytes() + val k = CryptoHelper.KeyBox.parse2(inBytes) as Array<*> + val kType = if ((k[1] as String) == "PEM") KeyFormat.PEM else KeyFormat.DER + val outFileStem = File(info2.getProperty("file")).name + assert(k[2] is org.bouncycastle.asn1.pkcs.RSAPrivateKey) + val hint = "RSA private: PK1 => PK8(PEM,DER)" + log.info("Running: $hint") + OpenSslHelper.PK1Key(data = File(kFile).readBytes()).let { rsa -> + rsa.toPk8(KeyFormat.PEM).writeTo("$outFileStem.pem_pk8") + rsa.toPk8(KeyFormat.DER).writeTo("$outFileStem.der_pk8") } + log.info("$hint: $kFile => $outFileStem.pem_pk8, $outFileStem.der_pk8") + } + else -> { + //pass + } + } //end-of-cmd-mode + } + + fun processFile(inFile: String, info2: Properties) { + val preFile = info2.getProperty("file") + info2["file"] = (if (preFile == null) "" else "$preFile,") + File(inFile).canonicalFile.path + info2["propFile"] = File(info2.getProperty("file")).name + ".prop" + val k = CryptoHelper.KeyBox.parse2(File(inFile).readBytes()) as Array<*> + val bSuccess = k[0] as Boolean + val bType = k[1] as String + if (bSuccess) { + info2["fileType"] = bType + info2["fileClazz"] = k[2]!!::class.toString() + log.info("Recognized $bType: " + k[2]!!::class) + if (k[2] is sun.security.x509.X509CertImpl) { + val crt = k[2] as sun.security.x509.X509CertImpl + val crtSubj = (crt.get("x509.info") as sun.security.x509.X509CertInfo).get("subject").toString() + val subj2 = crtSubj + .replace(", ", "/") + .replace("EMAILADDRESS", "emailAddress") + .replace("\\s".toRegex(), "") + info2.setProperty("csr.info", "/$subj2") + } + if (k[2] is sun.security.rsa.RSAPrivateCrtKeyImpl) { + val pk8 = k[2] as sun.security.rsa.RSAPrivateCrtKeyImpl + info2.setProperty("rsa.len", pk8.modulus.bitLength().toString()) + info2["rsa.modulus"] = Helper.toHexString(pk8.modulus.toByteArray()) + info2["rsa.privateExponent"] = Helper.toHexString(pk8.privateExponent.toByteArray()) + info2["rsa.publicExponent"] = Helper.toHexString(pk8.publicExponent.toByteArray()) + info2["rsa.primeP"] = Helper.toHexString(pk8.primeP.toByteArray()) + info2["rsa.primeQ"] = Helper.toHexString(pk8.primeQ.toByteArray()) + } + if (k[2] is org.bouncycastle.asn1.pkcs.RSAPrivateKey) { + val rsa = k[2] as org.bouncycastle.asn1.pkcs.RSAPrivateKey + info2.setProperty("rsa.len", rsa.modulus.bitLength().toString()) + info2["rsa.modulus"] = Helper.toHexString(rsa.modulus.toByteArray()) + info2["rsa.privateExponent"] = Helper.toHexString(rsa.privateExponent.toByteArray()) + info2["rsa.publicExponent"] = Helper.toHexString(rsa.publicExponent.toByteArray()) + info2["rsa.primeP"] = Helper.toHexString(rsa.prime1.toByteArray()) + info2["rsa.primeQ"] = Helper.toHexString(rsa.prime2.toByteArray()) + } + } else { + if (bType == "NA") { + log.warn("Unrecognized file $inFile") + } else { + log.warn("Recognized but not parsed $bType: $inFile") } - log.info("$hint: $kFile => $outFile") - } - "crypto.key.5" -> { - val hint = "RSA private(PK8): => Public Key(PK8, PEM)" - val kFile = args[1] - val outFile = args[2] - assert(CryptoHelper.KeyBox.parse(File(kFile).readBytes()) is org.bouncycastle.asn1.pkcs.RSAPrivateKey) - val pk8rsa = OpenSslHelper.PK8RsaKey(KeyFormat.PEM, File(kFile).readBytes()) - pk8rsa.getPublicKey().writeTo(outFile) - log.info("$hint: $kFile => $outFile") - } - "crypto.key.7" -> { - val hint = "RSA private: PK1 => PK8(DER)" - val kFile = args[1] - val outFile = args[2] - assert(CryptoHelper.KeyBox.parse(File(kFile).readBytes()) is org.bouncycastle.asn1.pkcs.RSAPrivateKey) - val rsa = OpenSslHelper.PK1Key(data = File(kFile).readBytes()) - rsa.toPk8(KeyFormat.DER).writeTo(outFile) - log.info("$hint: $kFile => $outFile") - } - "crypto.key.10" -> { - //Action-10: - var hint = "" - val kFile = args[1] - val outFile = args[2] - val inBytes = File(kFile).readBytes() - assert(CryptoHelper.KeyBox.parse(inBytes) is java.security.interfaces.RSAPrivateKey) - val p = PemReader(InputStreamReader(ByteArrayInputStream(File(kFile).readBytes()))).readPemObject() - if (p != null) {//pem - hint = "PK8 RSA: PEM => DER" - OpenSslHelper.PK8RsaKey(KeyFormat.PEM, inBytes).transform(KeyFormat.PEM, KeyFormat.DER).writeTo(outFile) - } else {//der - hint = "PK8 RSA: DER => PEM" - OpenSslHelper.PK8RsaKey(KeyFormat.DER, inBytes).transform(KeyFormat.DER, KeyFormat.PEM).writeTo(outFile) - } - log.info("$hint: $kFile => $outFile") - } - "crypto.key.11" -> { - val hint = "RSA public(PK8): PEM => DER" - val kFile = args[1] - val outFile = args[2] - File(outFile).writeBytes( - CryptoHelper.KeyBox.decodePem(File(kFile).readText()) - ) - log.info("$hint: $kFile => $outFile") - } - "crypto.key.csr" -> { - //Action-xx: - val hint = "PK1 RSA PEM ==> CSR" - val kFile = args[1] - val outFile = args[2] - val inBytes = File(kFile).readBytes() - assert(CryptoHelper.KeyBox.parse(inBytes) is org.bouncycastle.asn1.pkcs.RSAPrivateKey) - OpenSslHelper.PK1Key(KeyFormat.PEM,inBytes).toCsr().writeTo(outFile) - log.info("$hint: $kFile => $outFile") } - "crypto.key.crt" -> { - //Action-xx: - val hint = "PK1 RSA PEM ==> CRT" - val kFile = args[1] - val outFile = args[2] - val inBytes = File(kFile).readBytes() - assert(CryptoHelper.KeyBox.parse(inBytes) is org.bouncycastle.asn1.pkcs.RSAPrivateKey) - OpenSslHelper.PK1Key(KeyFormat.PEM,inBytes).toV1Cert().writeTo(outFile) - log.info("$hint: $kFile => $outFile") + } + + fun todo(args: Array) { + when (args[0]) { + "crypo.list" -> { + CryptoHelper.listAll() + } + "crypto.key.5" -> { + val hint = "RSA private(PK8): => Public Key(PK8, PEM)" + val kFile = args[1] + val outFile = args[2] + assert((CryptoHelper.KeyBox.parse2(File(kFile).readBytes()) as Array<*>)[2] is org.bouncycastle.asn1.pkcs.RSAPrivateKey) + val pk8rsa = OpenSslHelper.PK8RsaKey(KeyFormat.PEM, File(kFile).readBytes()) + pk8rsa.getPublicKey().writeTo(outFile) + log.info("$hint: $kFile => $outFile") + } + "crypto.key.10" -> { + //Action-10: + var hint = "PK8 RSA: PEM <=> DER" + val kFile = args[1] + val outFile = args[2] + val inBytes = File(kFile).readBytes() + assert((CryptoHelper.KeyBox.parse2(inBytes) as Array<*>)[2] is java.security.interfaces.RSAPrivateKey) + val p = PemReader(InputStreamReader(ByteArrayInputStream(File(kFile).readBytes()))).readPemObject() + if (p != null) {//pem + hint = "PK8 RSA: PEM => DER" + OpenSslHelper.PK8RsaKey(KeyFormat.PEM, inBytes).transform(KeyFormat.PEM, KeyFormat.DER) + .writeTo(outFile) + } else {//der + hint = "PK8 RSA: DER => PEM" + OpenSslHelper.PK8RsaKey(KeyFormat.DER, inBytes).transform(KeyFormat.DER, KeyFormat.PEM) + .writeTo(outFile) + } + log.info("$hint: $kFile => $outFile") + } + "crypto.key.11" -> { + val hint = "RSA public(PK8): PEM => DER" + val kFile = args[1] + val outFile = args[2] + File(outFile).writeBytes( + CryptoHelper.KeyBox.decodePem(File(kFile).readText()) + ) + log.info("$hint: $kFile => $outFile") + } + "crypto.key.22" -> { + //Action-xx: + val hint = "CRT ==> JKS" + val kFile = args[1] + val crtFile = args[2] + val outFile = args[3] + assert((CryptoHelper.KeyBox.parse2(File(crtFile).readBytes()) as Array<*>)[2] is Certificate) + val envPassword = System.getProperty("password") ?: "secretpassword" + val envAlias = System.getProperty("alias") ?: "someUnknownAlias" + val crt = OpenSslHelper.Crt(File(crtFile).readBytes()) + val rsa = OpenSslHelper.PK1Key(KeyFormat.PEM, File(kFile).readBytes()) + OpenSslHelper.Pfx(name = envAlias, thePassword = envPassword).generate(rsa, crt).toJks() + .writeTo(outFile) + log.info("$hint: $kFile => $outFile") + } + "crypto.key.jks" -> { + //Action-xx: + val hint = "Generate JKS" + val keypass = args[1] + val storepass = args[2] + val alias = args[3] + val outFile = args[4] + OpenSslHelper.Jks.generate(keypass, storepass, alias, null, outFile) + log.info("$hint: ==> $outFile") + } + "crypto.key.23" -> { + //Action-xx: + val hint = "PK1 ==> JKS" + val kFile = args[1] + val outFile = args[2] + val rsa = OpenSslHelper.PK1Key(KeyFormat.PEM, File(kFile).readBytes()) + val crt = rsa.toV1Cert() + OpenSslHelper.Pfx(name = "androiddebugkey", thePassword = "somepassword").generate(rsa, crt).toJks() + .writeTo(outFile) + log.info("$hint: $kFile => $outFile") + } + "crypto.key.xx" -> { + //Action-xx: + val hint = "" + val kFile = args[1] + val k = CryptoHelper.KeyBox.parse2(File(kFile).readBytes()) as Array<*> + println(k[2]!!.toString()) + val crt = k[2] as sun.security.x509.X509CertImpl + log.info("type=" + crt.type + ", name=" + crt.name) + for (item in crt.elements) { + log.info("# $item") + } + val crtInfo = crt.get("x509.info") as sun.security.x509.X509CertInfo + for (item in crtInfo.elements) { + log.info("## $item") + } + log.info("subject=>") + log.info(crtInfo.get("subject").toString()) + log.info("issuer=>") + log.info(crtInfo.get("issuer").toString()) + log.info("$hint: $kFile => ") + } + else -> { + Launcher.help() + exitProcess(1) + } } - "crypto.key.22" -> { - //Action-xx: - val hint = "CRT ==> JKS" - val kFile = args[1] - val outFile = args[2] - val inBytes = File(kFile).readBytes() - assert(CryptoHelper.KeyBox.parse(inBytes) is org.bouncycastle.asn1.pkcs.RSAPrivateKey) - OpenSslHelper.PK1Key(KeyFormat.PEM,inBytes).toV1Cert().writeTo(outFile) - log.info("$hint: $kFile => $outFile") + } +} + + +fun main(args: Array) { + val log = LoggerFactory.getLogger("main") + val allSupportedCommands = + listOf("genrsa", "toPublicKey", "decodePEM", "toPk8", "toPk1", "toJks", "toCsr", "toCrt").toString() + + val info2 = Properties().apply { + this.setProperty("all_cmds", allSupportedCommands) + this["available_cmds"] = this.getProperty("all_cmds") + this["cmd"] = "" + this.setProperty( + "csr.info", "/C=CN/ST=Shanghai/L=Shanghai/O=XXX/OU=infra/CN=gerrit/emailAddress=webmaster@XX.com" + ) + this.setProperty("pfx.alias", "androiddebugkey") + this.setProperty("pfx.password", "somepassword") + } + + if (args.isEmpty()) { + info2.setProperty("rsa.len", 4096.toString()) + info2["file"] = "k" + info2["propFile"] = "run.prop" + FileOutputStream(info2.getProperty("propFile")).use { fos -> + info2.store(fos, null) + log.info("Writing to " + info2.getProperty("propFile")) } - "crypto.key.xx" -> { - //Action-xx: - val hint = "" - val kFile = args[1] - val outFile = args[2] - File(outFile).writeBytes( - CryptoHelper.KeyBox.decodePem(File(kFile).readText()) - ) - log.info("$hint: $kFile => $outFile") + exitProcess(0) + } + + if (args.size == 1 && File(args[0]).exists() && args[0].endsWith(".prop")) { //cmd mode + Launcher().processCommand(args[0], info2) + } else { // file mode + args.forEachIndexed { index, s -> + log.warn("[${index + 1}/${args.size}] Processing $s ...") + Launcher().processFile(s, info2) } - else -> { - Launcher.help() - exitProcess(1) + FileOutputStream(info2.getProperty("propFile")).use { fos -> + info2.setProperty("all_cmds", allSupportedCommands) + info2.store(fos, null) + log.info("Writing to " + info2.getProperty("propFile")) } } - return -} +} \ No newline at end of file diff --git a/helper/src/main/kotlin/cfig/helper/OpenSslHelper.kt b/helper/src/main/kotlin/cfig/helper/OpenSslHelper.kt index eed15b7..950a123 100644 --- a/helper/src/main/kotlin/cfig/helper/OpenSslHelper.kt +++ b/helper/src/main/kotlin/cfig/helper/OpenSslHelper.kt @@ -114,14 +114,14 @@ class OpenSslHelper { } } - fun toV1Cert(): Crt { + fun toV1Cert(info: String? = null): Crt { //full command: // openssl x509 -req -in 2017key.csr -signkey 2017key.rsa.pem -out theCert.crt -days 180 //send RSA key as input stream: // openssl x509 -req -in 2017key.csr -signkey - -out theCert.crt -days 180 //send RSA key as input stream, crt as output stream: // openssl x509 -req -in 2017key.csr -signkey - -days 180 - val csr = this.toCsr() + val csr = this.toCsr(info) val tmpFile = File.createTempFile("pk1.", ".csr") tmpFile.writeBytes(csr.data) tmpFile.deleteOnExit() @@ -148,6 +148,12 @@ class OpenSslHelper { log.info("generateRSA:stderr: ${String(ret[1])}") return PK1Key(format = KeyFormat.PEM, data = ret[0]) } + + fun generate(keyLength: Int, password: String): PK1Key { + val ret = Helper.powerRun("openssl genrsa -aes256 -passout pass:$password $keyLength", null) + log.info("generateRSA:stderr: ${String(ret[1])}") + return PK1Key(format = KeyFormat.PEM, data = ret[0]) + } } } @@ -166,12 +172,16 @@ class OpenSslHelper { openssl rsa -in - -out - */ fun toPk1(): PK1Key { - if (this.format != KeyFormat.PEM) { - throw IllegalArgumentException("Only pk8+pem can be converted to RSA") - } + val transformed = + if (this.format != KeyFormat.PEM) { + log.warn("Only pk8+pem can be converted to RSA, transforming ...") + this.transform(KeyFormat.DER, KeyFormat.PEM) + } else { + this + } val ret = Helper.powerRun2( "openssl rsa -in $stdin", - ByteArrayInputStream(data) + ByteArrayInputStream(transformed.data) ) if (ret[0] as Boolean) { log.info("toRsaPrivate:error: ${String(ret[2] as ByteArray)}") @@ -241,7 +251,7 @@ class OpenSslHelper { class Csr(override val name: String = "CSR", override val data: ByteArray = byteArrayOf()) : IKey class Jks(override val name: String = "Java Keystore", override val data: ByteArray = byteArrayOf()) : IKey { //keytool -list -v -deststorepass $(thePassword) -keystore $(jks_file) - fun check(passWord: String = "somepassword") { + fun check(passWord: String = "secretpassword") { val tmpFile = File.createTempFile("tmp.", ".jks").apply { this.deleteOnExit() } tmpFile.writeBytes(this.data) val ret = Helper.powerRun2( @@ -257,18 +267,47 @@ class OpenSslHelper { throw RuntimeException() } } + + companion object { + fun generate(keyPass: String, storePass: String, alias: String, dname: String?, outFile: String) { + //val cmd = "keytool -genkey -noprompt -alias $alias -dname \"$defaultInfo\" -keystore $outFile -storepass $storePass -keypass $keyPass" + val defaultInfo = + dname ?: "CN=Unknown, OU=Unknown, O=Unknown Software Company, L=Unknown, ST=Unknown, C=Unknown" + val cmd = CommandLine.parse("keytool -genkey -noprompt -keyalg RSA").apply { + addArgument("-alias").addArgument(alias) + addArgument("-dname").addArgument(defaultInfo, false) + addArgument("-keystore").addArgument(outFile) + addArgument("-storepass").addArgument(storePass) + addArgument("-keypass").addArgument(keyPass) + } + val ret = Helper.powerRun3(cmd, null) + if (ret[0] as Boolean) { + log.info("Jks.gen:stdout: ${String(ret[1] as ByteArray)}") + log.info("Jks.gen:error: ${String(ret[2] as ByteArray)}") + } else { + log.error("Jks.gen:stdout: " + String(ret[1] as ByteArray)) + log.error("Jks.gen:stderr: " + String(ret[2] as ByteArray)) + throw RuntimeException() + } + } + } } class Crt(override val data: ByteArray = byteArrayOf(), override val name: String = "crt") : IKey { //Result: trustedCertEntry - //keytool -importcert -file 2017key.crt -deststorepass somepassword -srcstorepass somepassword -keystore 2017key.2.jks - fun toJks(paramSrcPass: String = "somepassword", paramDstPass: String = "somepassword"): Jks { + //keytool -importcert -file 2017key.crt -deststorepass secretpassword -srcstorepass secretpassword -keystore 2017key.2.jks + fun toJks( + paramSrcPass: String = "secretpassword", + paramDstPass: String = "secretpassword", + alias: String = "awesomeKey" + ): Jks { val crtFile = File.createTempFile("tmp.", ".crt").apply { this.deleteOnExit() } crtFile.writeBytes(this.data) val outFile = File.createTempFile("tmp.", ".jks").apply { this.delete() } val ret = Helper.powerRun2( "keytool -importcert -file ${crtFile.path}" + " -deststorepass $paramDstPass -srcstorepass $paramSrcPass " + + " -alias $alias " + " -keystore ${outFile.path}", ByteArrayInputStream("yes\n".toByteArray()) ) @@ -292,10 +331,10 @@ class OpenSslHelper { class Pfx( override val name: String = "androiddebugkey", - var thePassword: String = "somepassword", + var thePassword: String = "secretpassword", override var data: ByteArray = byteArrayOf() ) : IKey { - fun generate(pk1: PK1Key, crt: Crt) { + fun generate(pk1: PK1Key, crt: Crt): Pfx { val pk1File = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() } pk1File.writeBytes(pk1.data) @@ -317,6 +356,7 @@ class OpenSslHelper { log.error("stderr: " + String(ret[2] as ByteArray)) throw RuntimeException() } + return this } //keytool -importkeystore -deststorepass $(thePassword) -destkeystore $(jks_file) -srckeystore $(pfx_cert) -srcstoretype PKCS12 -srcstorepass $(thePassword) @@ -348,7 +388,7 @@ class OpenSslHelper { private val log = LoggerFactory.getLogger(OpenSslHelper::class.java) val stdin = if (System.getProperty("os.name").contains("Mac")) "/dev/stdin" else "-" - fun toPfx(password: String = "somepassword", keyName: String = "androiddebugkey", pk1: PK1Key, crt: Crt) { + fun toPfx(password: String = "secretpassword", keyName: String = "androiddebugkey", pk1: PK1Key, crt: Crt) { val pk1File = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() } pk1File.writeBytes(pk1.data)