unify helpers and remove codacy
parent
ea73a704b3
commit
30a5a0cbad
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
exclude_paths:
|
|
||||||
- 'aosp/**'
|
|
@ -0,0 +1,93 @@
|
|||||||
|
package cfig.helper
|
||||||
|
|
||||||
|
import cfig.io.Struct3
|
||||||
|
import com.google.common.math.BigIntegerMath
|
||||||
|
import org.apache.commons.codec.binary.Hex
|
||||||
|
import org.bouncycastle.util.io.pem.PemReader
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.math.RoundingMode
|
||||||
|
import java.security.Security
|
||||||
|
|
||||||
|
class KeyHelper {
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(KeyHelper::class.java)
|
||||||
|
|
||||||
|
@Throws(IllegalArgumentException::class)
|
||||||
|
fun parsePemPubkey(inputStream: InputStream): org.bouncycastle.asn1.pkcs.RSAPublicKey {
|
||||||
|
val p = PemReader(InputStreamReader(inputStream)).readPemObject()
|
||||||
|
if ("RSA PUBLIC KEY" != p.type) {
|
||||||
|
throw IllegalArgumentException("input is not valid 'RSA PUBLIC KEY'")
|
||||||
|
}
|
||||||
|
return org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(p.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IllegalArgumentException::class)
|
||||||
|
fun parsePemPrivateKeyBC(inputStream: InputStream): org.bouncycastle.asn1.pkcs.RSAPrivateKey {
|
||||||
|
val p = PemReader(InputStreamReader(inputStream)).readPemObject()
|
||||||
|
if ("RSA PRIVATE KEY" != p.type) {
|
||||||
|
throw IllegalArgumentException("input is not valid 'RSA PRIVATE KEY'")
|
||||||
|
}
|
||||||
|
return org.bouncycastle.asn1.pkcs.RSAPrivateKey.getInstance(p.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fun parsePemPrivateKey(inputStream: InputStream): java.security.PrivateKey {
|
||||||
|
// val rsa = BC.parsePemPrivateKeyBC(inputStream)
|
||||||
|
// return generateRsaPrivateKey(rsa.modulus, rsa.privateExponent)
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
read RSA private key
|
||||||
|
assert exp == 65537
|
||||||
|
num_bits = log2(modulus)
|
||||||
|
|
||||||
|
@return: AvbRSAPublicKeyHeader formatted bytearray
|
||||||
|
https://android.googlesource.com/platform/external/avb/+/master/libavb/avb_crypto.h#158
|
||||||
|
from avbtool::encode_rsa_key()
|
||||||
|
*/
|
||||||
|
fun encodeRSAkey(key: ByteArray): ByteArray {
|
||||||
|
val rsa = parsePemPrivateKeyBC(ByteArrayInputStream(key))
|
||||||
|
assert(65537.toBigInteger() == rsa.publicExponent)
|
||||||
|
val numBits: Int = BigIntegerMath.log2(rsa.modulus, RoundingMode.CEILING)
|
||||||
|
log.debug("modulus: " + rsa.modulus)
|
||||||
|
log.debug("numBits: $numBits")
|
||||||
|
val b = BigInteger.valueOf(2).pow(32)
|
||||||
|
val n0inv = (b - rsa.modulus.modInverse(b)).toLong()
|
||||||
|
log.debug("n0inv = $n0inv")
|
||||||
|
val r = BigInteger.valueOf(2).pow(numBits)
|
||||||
|
val rrModn = (r * r).mod(rsa.modulus)
|
||||||
|
log.debug("BB: " + numBits / 8 + ", mod_len: " + rsa.modulus.toByteArray().size + ", rrmodn = " + rrModn.toByteArray().size)
|
||||||
|
val unsignedModulo = rsa.modulus.toByteArray().sliceArray(1..numBits / 8) //remove sign byte
|
||||||
|
log.debug("unsigned modulo: " + Hex.encodeHexString(unsignedModulo))
|
||||||
|
val ret = Struct3("!II${numBits / 8}b${numBits / 8}b").pack(
|
||||||
|
numBits,
|
||||||
|
n0inv,
|
||||||
|
unsignedModulo,
|
||||||
|
rrModn.toByteArray())
|
||||||
|
log.debug("rrmodn: " + Hex.encodeHexString(rrModn.toByteArray()))
|
||||||
|
log.debug("RSA: " + Hex.encodeHexString(ret))
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fun listAll() {
|
||||||
|
Security.getProviders().forEach {
|
||||||
|
val sb = StringBuilder("Provider: " + it.name + "{")
|
||||||
|
it.stringPropertyNames().forEach { key ->
|
||||||
|
sb.append(" (k=" + key + ",v=" + it.getProperty(key) + "), ")
|
||||||
|
}
|
||||||
|
sb.append("}")
|
||||||
|
log.info(sb.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
for (item in Security.getAlgorithms("Cipher")) {
|
||||||
|
log.info("Cipher: $i -> $item")
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,187 @@
|
|||||||
|
package cfig.helper
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Hex
|
||||||
|
import org.apache.commons.exec.CommandLine
|
||||||
|
import org.apache.commons.exec.DefaultExecutor
|
||||||
|
import org.apache.commons.exec.ExecuteException
|
||||||
|
import org.apache.commons.exec.PumpStreamHandler
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.security.*
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec
|
||||||
|
import java.security.spec.RSAPrivateKeySpec
|
||||||
|
import java.security.spec.RSAPublicKeySpec
|
||||||
|
import java.security.spec.X509EncodedKeySpec
|
||||||
|
import java.util.*
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
|
||||||
|
class KeyHelper2 {
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(KeyHelper2::class.java)
|
||||||
|
|
||||||
|
fun parseRsaPk8(inputData: ByteArray): java.security.PrivateKey {
|
||||||
|
val spec = PKCS8EncodedKeySpec(inputData)
|
||||||
|
return KeyFactory.getInstance("RSA").generatePrivate(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseRsaKey(keyText: String): PrivateKey {
|
||||||
|
val publicKeyPEM = keyText
|
||||||
|
.replace("-----BEGIN RSA PRIVATE KEY-----", "")
|
||||||
|
.replace(System.lineSeparator().toRegex(), "")
|
||||||
|
.replace("\n", "")
|
||||||
|
.replace("\r", "")
|
||||||
|
.replace("-----END RSA PRIVATE KEY-----", "")
|
||||||
|
log.warn("trimmed key")
|
||||||
|
log.warn(publicKeyPEM)
|
||||||
|
val encoded: ByteArray = Base64.getDecoder().decode(publicKeyPEM)
|
||||||
|
val keySpec = X509EncodedKeySpec(encoded)
|
||||||
|
return KeyFactory.getInstance("RSA").generatePrivate(keySpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parsePemPubkey(keyText: String): java.security.interfaces.RSAPublicKey {
|
||||||
|
val publicKeyPEM = keyText
|
||||||
|
.replace("-----BEGIN PUBLIC KEY-----", "")
|
||||||
|
.replace(System.lineSeparator().toRegex(), "")
|
||||||
|
.replace("\n", "")
|
||||||
|
.replace("\r", "")
|
||||||
|
.replace("-----END PUBLIC KEY-----", "")
|
||||||
|
val encoded: ByteArray = Base64.getDecoder().decode(publicKeyPEM)
|
||||||
|
val keySpec = X509EncodedKeySpec(encoded)
|
||||||
|
return KeyFactory.getInstance("RSA").generatePublic(keySpec) as java.security.interfaces.RSAPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun parsePemPubCert(keyText: String) {
|
||||||
|
val publicKeyPEM = keyText
|
||||||
|
.replace("-----BEGIN CERTIFICATE-----", "")
|
||||||
|
.replace(System.lineSeparator().toRegex(), "")
|
||||||
|
.replace("\n", "")
|
||||||
|
.replace("\r", "")
|
||||||
|
.replace("-----END CERTIFICATE-----", "")
|
||||||
|
val encoded: ByteArray = Base64.getDecoder().decode(publicKeyPEM)
|
||||||
|
val keySpec = X509EncodedKeySpec(encoded)
|
||||||
|
// return KeyFactory.getInstance("RSA").generatePublic(keySpec) as java.security.interfaces.RSAPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
in: modulus, expo
|
||||||
|
out: PublicKey
|
||||||
|
*/
|
||||||
|
fun generateRsaPublicKey(modulus: BigInteger, publicExponent: BigInteger): java.security.PublicKey {
|
||||||
|
return KeyFactory.getInstance("RSA").generatePublic(RSAPublicKeySpec(modulus, publicExponent))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
in: modulus, expo
|
||||||
|
out: PrivateKey
|
||||||
|
*/
|
||||||
|
fun generateRsaPrivateKey(modulus: BigInteger, privateExponent: BigInteger): java.security.PrivateKey {
|
||||||
|
return KeyFactory.getInstance("RSA").generatePrivate(RSAPrivateKeySpec(modulus, privateExponent))
|
||||||
|
}
|
||||||
|
|
||||||
|
//inspired by
|
||||||
|
// https://stackoverflow.com/questions/40242391/how-can-i-sign-a-raw-message-without-first-hashing-it-in-bouncy-castle
|
||||||
|
// "specifying Cipher.ENCRYPT mode or Cipher.DECRYPT mode doesn't make a difference;
|
||||||
|
// both simply perform modular exponentiation"
|
||||||
|
fun rawSign(privk: java.security.PrivateKey, data: ByteArray): ByteArray {
|
||||||
|
return Cipher.getInstance("RSA/ECB/NoPadding").let { cipher ->
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, privk)
|
||||||
|
cipher.update(data)
|
||||||
|
cipher.doFinal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun rawSignOpenSsl(keyPath: String, data: ByteArray): ByteArray {
|
||||||
|
log.debug("raw input: " + Hex.encodeHexString(data))
|
||||||
|
log.debug("Raw sign data size = ${data.size}, key = $keyPath")
|
||||||
|
var ret = byteArrayOf()
|
||||||
|
val exe = DefaultExecutor()
|
||||||
|
val stdin = ByteArrayInputStream(data)
|
||||||
|
val stdout = ByteArrayOutputStream()
|
||||||
|
val stderr = ByteArrayOutputStream()
|
||||||
|
exe.streamHandler = PumpStreamHandler(stdout, stderr, stdin)
|
||||||
|
try {
|
||||||
|
exe.execute(CommandLine.parse("openssl rsautl -sign -inkey $keyPath -raw"))
|
||||||
|
ret = stdout.toByteArray()
|
||||||
|
log.debug("Raw signature size = " + ret.size)
|
||||||
|
} catch (e: ExecuteException) {
|
||||||
|
log.error("Execute error")
|
||||||
|
} finally {
|
||||||
|
log.debug("OUT: " + Hex.encodeHexString(stdout.toByteArray()))
|
||||||
|
log.debug("ERR: " + String(stderr.toByteArray()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret.isEmpty()) throw RuntimeException("raw sign failed")
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pyAlg2java(alg: String): String {
|
||||||
|
return when (alg) {
|
||||||
|
"sha1" -> "sha-1"
|
||||||
|
"sha224" -> "sha-224"
|
||||||
|
"sha256" -> "sha-256"
|
||||||
|
"sha384" -> "sha-384"
|
||||||
|
"sha512" -> "sha-512"
|
||||||
|
else -> throw IllegalArgumentException("unknown algorithm: [$alg]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
openssl dgst -sha256 <file>
|
||||||
|
*/
|
||||||
|
fun sha256(inData: ByteArray): ByteArray {
|
||||||
|
return MessageDigest.getInstance("SHA-256").digest(inData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rsa(inData: ByteArray, inKey: java.security.PrivateKey): ByteArray {
|
||||||
|
return Cipher.getInstance("RSA").let {
|
||||||
|
it.init(Cipher.ENCRYPT_MODE, inKey)
|
||||||
|
it.doFinal(inData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sha256rsa(inData: ByteArray, inKey: java.security.PrivateKey): ByteArray {
|
||||||
|
return rsa(sha256(inData), inKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sign(inData: ByteArray, privateKey: PrivateKey): String {
|
||||||
|
val signature = Signature.getInstance("SHA256withRSA").let {
|
||||||
|
it.initSign(privateKey)
|
||||||
|
it.update(inData)
|
||||||
|
it.sign()
|
||||||
|
}
|
||||||
|
return Base64.getEncoder().encodeToString(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verify(inData: ByteArray, signature: ByteArray, pubKey: PublicKey): Boolean {
|
||||||
|
return Signature.getInstance("SHA256withRSA").let {
|
||||||
|
it.initVerify(pubKey)
|
||||||
|
it.update(inData)
|
||||||
|
it.verify(signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun verify(inData: ByteArray, base64Signature: String, pubKey: PublicKey): Boolean {
|
||||||
|
val signatureBytes = Base64.getDecoder().decode(base64Signature)
|
||||||
|
return Signature.getInstance("SHA256withRSA").let {
|
||||||
|
it.initVerify(pubKey)
|
||||||
|
it.update(inData)
|
||||||
|
it.verify(signatureBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verify2(inData: ByteArray, encrypedHash: ByteArray, inKey: java.security.PublicKey): Boolean {
|
||||||
|
val calcHash = sha256(inData)
|
||||||
|
val decrypedHash = Cipher.getInstance("RSA").let {
|
||||||
|
it.init(Cipher.DECRYPT_MODE, inKey)
|
||||||
|
it.doFinal(encrypedHash)
|
||||||
|
}
|
||||||
|
return calcHash.contentEquals(decrypedHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,341 @@
|
|||||||
|
package cfig.helper
|
||||||
|
|
||||||
|
import org.apache.commons.exec.CommandLine
|
||||||
|
import org.bouncycastle.util.encoders.Hex
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.lang.RuntimeException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class OpenSslHelper {
|
||||||
|
enum class KeyFormat {
|
||||||
|
PEM, //header + metadata + base64 der
|
||||||
|
DER // der format
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IKey {
|
||||||
|
val name: String
|
||||||
|
val data: ByteArray
|
||||||
|
fun writeTo(fileName: String) {
|
||||||
|
FileOutputStream(fileName, false).use {
|
||||||
|
it.write(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PK1Key(val format: KeyFormat = KeyFormat.PEM,
|
||||||
|
override val data: ByteArray = byteArrayOf(),
|
||||||
|
override val name: String = "RSA Private") : IKey {
|
||||||
|
/*
|
||||||
|
PEM private key -> PEM/DER public key
|
||||||
|
*/
|
||||||
|
fun getPublicKey(pubKeyFormat: KeyFormat): PK1PubKey {
|
||||||
|
if (format != KeyFormat.PEM) {
|
||||||
|
throw IllegalArgumentException("can not handle $format private key")
|
||||||
|
}
|
||||||
|
val ret = Helper.powerRun("openssl rsa -in - -pubout -outform ${pubKeyFormat.name}",
|
||||||
|
ByteArrayInputStream(data))
|
||||||
|
log.info("privateToPublic:stderr: ${String(ret[1])}")
|
||||||
|
return PK1PubKey(format = pubKeyFormat, data = ret[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
file based:
|
||||||
|
openssl rsa -in private.pem -pubout -out public.pem
|
||||||
|
stream based:
|
||||||
|
openssl rsa -in - -pubout
|
||||||
|
*/
|
||||||
|
fun getPk8PublicKey(): Pk8PubKey {
|
||||||
|
if (this.format != KeyFormat.PEM) {
|
||||||
|
throw java.lang.IllegalArgumentException("Only PEM key is supported")
|
||||||
|
}
|
||||||
|
val ret = Helper.powerRun2("openssl rsa -in - -pubout",
|
||||||
|
ByteArrayInputStream(data))
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("getPk8PublicKey:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
return Pk8PubKey(KeyFormat.PEM, ret[1] as ByteArray)
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
file based:
|
||||||
|
openssl pkcs8 -nocrypt -in $(rsa_key) -topk8 -outform DER -out $(pk8_key)
|
||||||
|
stream based:
|
||||||
|
openssl pkcs8 -nocrypt -in - -topk8 -outform DER
|
||||||
|
*/
|
||||||
|
fun toPk8(pk8Format: KeyFormat): PK8RsaKey {
|
||||||
|
val ret = Helper.powerRun("openssl pkcs8 -nocrypt -in - -topk8 -outform ${pk8Format.name}",
|
||||||
|
ByteArrayInputStream(data))
|
||||||
|
log.info("toPk8Private:stderr: ${String(ret[1])}")
|
||||||
|
return PK8RsaKey(format = pk8Format, data = ret[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toCsr(): Csr {
|
||||||
|
val info = "/C=CN/ST=Shanghai/L=Shanghai/O=XXX/OU=infra/CN=gerrit/emailAddress=webmaster@XX.com"
|
||||||
|
val cmdLine = CommandLine.parse("openssl req -new -key - -subj").apply {
|
||||||
|
this.addArgument("$info", true)
|
||||||
|
}
|
||||||
|
val ret = Helper.powerRun3(cmdLine, ByteArrayInputStream(data))
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("toCsr:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
return Csr(data = ret[1] as ByteArray)
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toV1Cert(): 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 tmpFile = File.createTempFile("pk1.", ".csr")
|
||||||
|
tmpFile.writeBytes(csr.data)
|
||||||
|
tmpFile.deleteOnExit()
|
||||||
|
val ret = Helper.powerRun2("openssl x509 -req -in ${tmpFile.path} -signkey - -days 180",
|
||||||
|
ByteArrayInputStream(data))
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("toCrt:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
return Crt(ret[1] as ByteArray)
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/*
|
||||||
|
-> PEM RSA private key
|
||||||
|
*/
|
||||||
|
fun generate(keyLength: Int): PK1Key {
|
||||||
|
val ret = Helper.powerRun("openssl genrsa $keyLength", null)
|
||||||
|
log.info("generateRSA:stderr: ${String(ret[1])}")
|
||||||
|
return PK1Key(format = KeyFormat.PEM, data = ret[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PK8RsaKey(val format: KeyFormat = KeyFormat.PEM,
|
||||||
|
override val data: ByteArray = byteArrayOf(),
|
||||||
|
override val name: String = "PK8 Private") : IKey {
|
||||||
|
|
||||||
|
/*
|
||||||
|
file based:
|
||||||
|
openssl pkcs8 -nocrypt -in $(pk8_key) -inform DER -out $(rsa_key).converted.tmp
|
||||||
|
openssl rsa -in $(rsa_key).converted.tmp -out $(rsa_key).converted
|
||||||
|
stream based:
|
||||||
|
openssl pkcs8 -nocrypt -in - -inform DER
|
||||||
|
openssl rsa -in - -out -
|
||||||
|
*/
|
||||||
|
fun toPk1(): PK1Key {
|
||||||
|
if (this.format != KeyFormat.PEM) {
|
||||||
|
throw IllegalArgumentException("Only pk8+pem can be converted to RSA")
|
||||||
|
}
|
||||||
|
val ret = Helper.powerRun2("openssl rsa -in -",
|
||||||
|
ByteArrayInputStream(data))
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("toRsaPrivate:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
return PK1Key(KeyFormat.PEM, ret[1] as ByteArray)
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
openssl pkcs8 -nocrypt -in - -inform DER
|
||||||
|
*/
|
||||||
|
fun transform(inFormat: KeyFormat, outFormat: KeyFormat): PK8RsaKey {
|
||||||
|
val ret = Helper.powerRun2("openssl pkcs8 -nocrypt -in - -inform ${inFormat.name} -outform ${outFormat.name}",
|
||||||
|
ByteArrayInputStream(data))
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("transform:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
return PK8RsaKey(data = ret[1] as ByteArray)
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
file based:
|
||||||
|
openssl rsa -in pkcs8.pem -pubout -out public_pkcs8.pem
|
||||||
|
stream based:
|
||||||
|
openssl rsa -in - -pubout
|
||||||
|
*/
|
||||||
|
fun getPublicKey(): Pk8PubKey {
|
||||||
|
if (this.format != KeyFormat.PEM) {
|
||||||
|
throw java.lang.IllegalArgumentException("Only PEM key is supported")
|
||||||
|
}
|
||||||
|
val ret = Helper.powerRun2("openssl rsa -in - -pubout",
|
||||||
|
ByteArrayInputStream(data))
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("getPublicKey:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
return Pk8PubKey(KeyFormat.PEM, ret[1] as ByteArray)
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PK1PubKey(
|
||||||
|
val format: KeyFormat = KeyFormat.PEM,
|
||||||
|
override val data: ByteArray = byteArrayOf(),
|
||||||
|
override val name: String = "RSA Public"
|
||||||
|
) : IKey
|
||||||
|
|
||||||
|
class Pk8PubKey(
|
||||||
|
val format: KeyFormat = KeyFormat.PEM,
|
||||||
|
override val data: ByteArray = byteArrayOf(),
|
||||||
|
override val name: String = "Pk8 Public"
|
||||||
|
) : IKey
|
||||||
|
|
||||||
|
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") {
|
||||||
|
val tmpFile = File.createTempFile("tmp.", ".jks").apply { this.deleteOnExit() }
|
||||||
|
tmpFile.writeBytes(this.data)
|
||||||
|
val ret = Helper.powerRun2("keytool -list -v -deststorepass $passWord -keystore $tmpFile",
|
||||||
|
null)
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("Jks.check:stdout: ${String(ret[1] as ByteArray)}")
|
||||||
|
log.info("Jks.check:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Crt(val data: ByteArray = byteArrayOf()) {
|
||||||
|
//Result: trustedCertEntry
|
||||||
|
//keytool -importcert -file 2017key.crt -deststorepass somepassword -srcstorepass somepassword -keystore 2017key.2.jks
|
||||||
|
fun toJks(paramSrcPass: String = "somepassword", paramDstPass: String = "somepassword"): 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 " +
|
||||||
|
" -keystore ${outFile.path}",
|
||||||
|
ByteArrayInputStream("yes\n".toByteArray()))
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("toJks:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
log.info("toJks:stdout: ${String(ret[1] as ByteArray)}")
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outFile.exists()) {
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
val outData = outFile.readBytes()
|
||||||
|
outFile.delete()
|
||||||
|
return Jks(data = outData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pfx(override val name: String = "androiddebugkey",
|
||||||
|
var thePassword: String = "somepassword",
|
||||||
|
override var data: ByteArray = byteArrayOf()) : IKey {
|
||||||
|
fun generate(pk1: PK1Key, crt: Crt) {
|
||||||
|
val pk1File = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
|
||||||
|
pk1File.writeBytes(pk1.data)
|
||||||
|
|
||||||
|
val crtFile = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
|
||||||
|
crtFile.writeBytes(crt.data)
|
||||||
|
|
||||||
|
//openssl pkcs12 -export -out $(pfx_cert) -inkey $(rsa_key) -in $(crt_file) -password pass:$(thePassword) -name $(thePfxName)
|
||||||
|
val cmd = "openssl pkcs12 -export " +
|
||||||
|
" -inkey ${pk1File.path} " +
|
||||||
|
" -in ${crtFile.path} " +
|
||||||
|
" -password pass:${this.thePassword} -name ${this.name}"
|
||||||
|
val ret = Helper.powerRun2(cmd, null)
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("toPfx:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
log.info("toPfx:stdout: ${Hex.toHexString(ret[1] as ByteArray)}")
|
||||||
|
this.data = ret[1] as ByteArray
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Zkeytool -importkeystore -deststorepass $(thePassword) -destkeystore $(jks_file) -srckeystore $(pfx_cert) -srcstoretype PKCS12 -srcstorepass $(thePassword)
|
||||||
|
fun toJks(): Jks {
|
||||||
|
val jksFile = File.createTempFile("tmp.", ".file").apply { this.delete() }
|
||||||
|
val thisFile = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
|
||||||
|
thisFile.writeBytes(this.data)
|
||||||
|
val cmd = "keytool -importkeystore " +
|
||||||
|
" -srcstorepass $thePassword -deststorepass $thePassword " +
|
||||||
|
" -destkeystore ${jksFile.path} " +
|
||||||
|
" -srckeystore $thisFile -srcstoretype PKCS12"
|
||||||
|
val ret = Helper.powerRun2(cmd, null)
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("toJks:error: " + String(ret[2] as ByteArray))
|
||||||
|
log.info("toJks:stdout: " + String(ret[1] as ByteArray))
|
||||||
|
this.data = ret[1] as ByteArray
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
val outDate = jksFile.readBytes()
|
||||||
|
jksFile.delete()
|
||||||
|
return Jks(this.name, outDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(OpenSslHelper::class.java)
|
||||||
|
fun decodePem(keyText: String): ByteArray {
|
||||||
|
val publicKeyPEM = keyText
|
||||||
|
.replace("-----BEGIN .*-----".toRegex(), "")
|
||||||
|
.replace(System.lineSeparator().toRegex(), "")
|
||||||
|
.replace("\n", "")
|
||||||
|
.replace("\r", "")
|
||||||
|
.replace("-----END .*-----".toRegex(), "")
|
||||||
|
return Base64.getDecoder().decode(publicKeyPEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toPfx(password: String = "somepassword", keyName: String = "androiddebugkey", pk1: PK1Key, crt: Crt) {
|
||||||
|
val pk1File = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
|
||||||
|
pk1File.writeBytes(pk1.data)
|
||||||
|
|
||||||
|
val crtFile = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
|
||||||
|
crtFile.writeBytes(crt.data)
|
||||||
|
|
||||||
|
//openssl pkcs12 -export -out $(pfx_cert) -inkey $(rsa_key) -in $(crt_file) -password pass:$(thePassword) -name $(thePfxName)
|
||||||
|
val cmd = "openssl pkcs12 -export -inkey ${pk1File.path} -in ${crtFile.path} -password pass:$password -name $keyName"
|
||||||
|
val ret = Helper.powerRun2(cmd, null)
|
||||||
|
if (ret[0] as Boolean) {
|
||||||
|
log.info("toPfx:error: ${String(ret[2] as ByteArray)}")
|
||||||
|
log.info("toPfx:stdout: ${Hex.toHexString(ret[1] as ByteArray)}")
|
||||||
|
} else {
|
||||||
|
log.error("stdout: " + String(ret[1] as ByteArray))
|
||||||
|
log.error("stderr: " + String(ret[2] as ByteArray))
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
Subproject commit 23b636091aa768f141266dafa14f89fb4417eb5a
|
Subproject commit d95de4b8d5d5fa7d36f3696e14be23d22fcdf7f5
|
Loading…
Reference in New Issue