forked from openrs2/openrs2
parent
353b8f0834
commit
101548e5b7
@ -0,0 +1,123 @@ |
||||
package dev.openrs2.common.crypto |
||||
|
||||
import org.bouncycastle.asn1.DERNull |
||||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers |
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo |
||||
import org.bouncycastle.asn1.pkcs.RSAPrivateKey |
||||
import org.bouncycastle.asn1.pkcs.RSAPublicKey |
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier |
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo |
||||
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator |
||||
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters |
||||
import org.bouncycastle.crypto.params.RSAKeyParameters |
||||
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters |
||||
import org.bouncycastle.crypto.util.PrivateKeyInfoFactory |
||||
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory |
||||
import org.bouncycastle.util.io.pem.PemObject |
||||
import org.bouncycastle.util.io.pem.PemReader |
||||
import org.bouncycastle.util.io.pem.PemWriter |
||||
import java.io.IOException |
||||
import java.math.BigInteger |
||||
import java.nio.file.Files |
||||
import java.nio.file.Path |
||||
|
||||
object Rsa { |
||||
private const val PUBLIC_KEY = "PUBLIC KEY" |
||||
private const val PRIVATE_KEY = "PRIVATE KEY" |
||||
|
||||
private val F4 = BigInteger("65537") |
||||
|
||||
/* |
||||
* The client writes the output of RSA to a 128 byte buffer. It prefixes |
||||
* the output with a single length byte, leaving 127 bytes for the actual |
||||
* RSA output. |
||||
* |
||||
* The maximum output length of RSA encryption is the key size plus one, so |
||||
* the maximum key size supported by the client is 126 bytes - or 1008 bits. |
||||
*/ |
||||
private const val KEY_LENGTH = 1008 |
||||
|
||||
// 1 in 2^80 |
||||
private const val CERTAINTY = 80 |
||||
|
||||
val RSAPrivateCrtKeyParameters.publicKey |
||||
get() = RSAKeyParameters(false, modulus, publicExponent) |
||||
|
||||
fun generateKeyPair(): Pair<RSAKeyParameters, RSAPrivateCrtKeyParameters> { |
||||
val generator = RSAKeyPairGenerator() |
||||
generator.init(RSAKeyGenerationParameters(F4, secureRandom, KEY_LENGTH, CERTAINTY)) |
||||
|
||||
val keyPair = generator.generateKeyPair() |
||||
return Pair(keyPair.public as RSAKeyParameters, keyPair.private as RSAPrivateCrtKeyParameters) |
||||
} |
||||
|
||||
fun readPublicKey(path: Path): RSAKeyParameters { |
||||
val der = readSinglePemObject(path, PUBLIC_KEY) |
||||
|
||||
val spki = SubjectPublicKeyInfo.getInstance(der) |
||||
validateAlgorithm(spki.algorithm) |
||||
|
||||
val key = RSAPublicKey.getInstance(spki.parsePublicKey()) |
||||
return RSAKeyParameters(false, key.modulus, key.publicExponent) |
||||
} |
||||
|
||||
fun writePublicKey(path: Path, key: RSAKeyParameters) { |
||||
val spki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(key) |
||||
return writeSinglePemObject(path, PUBLIC_KEY, spki.encoded) |
||||
} |
||||
|
||||
fun readPrivateKey(path: Path): RSAKeyParameters { |
||||
val der = readSinglePemObject(path, PRIVATE_KEY) |
||||
|
||||
val pki = PrivateKeyInfo.getInstance(der) |
||||
validateAlgorithm(pki.privateKeyAlgorithm) |
||||
|
||||
val key = RSAPrivateKey.getInstance(pki.parsePrivateKey()) |
||||
return RSAPrivateCrtKeyParameters( |
||||
key.modulus, |
||||
key.publicExponent, |
||||
key.privateExponent, |
||||
key.prime1, |
||||
key.prime2, |
||||
key.exponent1, |
||||
key.exponent2, |
||||
key.coefficient |
||||
) |
||||
} |
||||
|
||||
fun writePrivateKey(path: Path, key: RSAKeyParameters) { |
||||
val pki = PrivateKeyInfoFactory.createPrivateKeyInfo(key) |
||||
return writeSinglePemObject(path, PRIVATE_KEY, pki.encoded) |
||||
} |
||||
|
||||
private fun validateAlgorithm(id: AlgorithmIdentifier) { |
||||
if (id.algorithm != PKCSObjectIdentifiers.rsaEncryption) { |
||||
throw IOException("Invalid algorithm identifier, expecting rsaEncryption") |
||||
} |
||||
|
||||
if (id.parameters != DERNull.INSTANCE) { |
||||
throw IOException("Invalid algorithm parameters, expecting NULL") |
||||
} |
||||
} |
||||
|
||||
private fun readSinglePemObject(path: Path, type: String): ByteArray { |
||||
PemReader(Files.newBufferedReader(path)).use { |
||||
val obj = it.readPemObject() |
||||
if (obj == null || obj.type != type || it.readPemObject() != null) { |
||||
throw IOException("Expecting single $type PEM object") |
||||
} |
||||
|
||||
if (obj.headers.isNotEmpty()) { |
||||
throw IOException("PEM headers unsupported") |
||||
} |
||||
|
||||
return obj.content |
||||
} |
||||
} |
||||
|
||||
private fun writeSinglePemObject(path: Path, type: String, content: ByteArray) { |
||||
PemWriter(Files.newBufferedWriter(path)).use { |
||||
it.writeObject(PemObject(type, content)) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue