|
|
@ -1,16 +1,46 @@ |
|
|
|
package dev.openrs2.common.crypto |
|
|
|
package dev.openrs2.common.crypto |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.google.common.jimfs.Jimfs |
|
|
|
import io.netty.buffer.Unpooled |
|
|
|
import io.netty.buffer.Unpooled |
|
|
|
import org.bouncycastle.crypto.params.RSAKeyParameters |
|
|
|
import org.bouncycastle.crypto.params.RSAKeyParameters |
|
|
|
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters |
|
|
|
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters |
|
|
|
import org.bouncycastle.util.Properties |
|
|
|
import org.bouncycastle.util.Properties |
|
|
|
import java.math.BigInteger |
|
|
|
import java.math.BigInteger |
|
|
|
|
|
|
|
import java.nio.file.Files |
|
|
|
import kotlin.test.Test |
|
|
|
import kotlin.test.Test |
|
|
|
import kotlin.test.assertEquals |
|
|
|
import kotlin.test.assertEquals |
|
|
|
|
|
|
|
import kotlin.test.assertFalse |
|
|
|
|
|
|
|
import kotlin.test.assertTrue |
|
|
|
|
|
|
|
|
|
|
|
object RsaTest { |
|
|
|
object RsaTest { |
|
|
|
private const val ALLOW_UNSAFE_MOD = "org.bouncycastle.rsa.allow_unsafe_mod" |
|
|
|
private const val ALLOW_UNSAFE_MOD = "org.bouncycastle.rsa.allow_unsafe_mod" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// example data from https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Example |
|
|
|
|
|
|
|
private val PUBLIC_KEY = allowUnsafeMod { RSAKeyParameters(false, BigInteger("3233"), BigInteger("17")) } |
|
|
|
|
|
|
|
private val PRIVATE_KEY = allowUnsafeMod { RSAKeyParameters(true, BigInteger("3233"), BigInteger("413")) } |
|
|
|
|
|
|
|
private val PRIVATE_KEY_CRT = allowUnsafeMod { RSAPrivateCrtKeyParameters( |
|
|
|
|
|
|
|
BigInteger("3233"), // modulus |
|
|
|
|
|
|
|
BigInteger("17"), // public exponent |
|
|
|
|
|
|
|
BigInteger("413"), // private exponent |
|
|
|
|
|
|
|
BigInteger("61"), // p |
|
|
|
|
|
|
|
BigInteger("53"), // q |
|
|
|
|
|
|
|
BigInteger("53"), // dP |
|
|
|
|
|
|
|
BigInteger("49"), // dQ |
|
|
|
|
|
|
|
BigInteger("38") // qInv |
|
|
|
|
|
|
|
) } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val PUBLIC_KEY_PEM = listOf( |
|
|
|
|
|
|
|
"-----BEGIN PUBLIC KEY-----", |
|
|
|
|
|
|
|
"MBswDQYJKoZIhvcNAQEBBQADCgAwBwICDKECARE=", |
|
|
|
|
|
|
|
"-----END PUBLIC KEY-----" |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
private val PRIVATE_KEY_PEM = listOf( |
|
|
|
|
|
|
|
"-----BEGIN PRIVATE KEY-----", |
|
|
|
|
|
|
|
"MDMCAQAwDQYJKoZIhvcNAQEBBQAEHzAdAgEAAgIMoQIBEQICAZ0CAT0CATUCATUC", |
|
|
|
|
|
|
|
"ATECASY=", |
|
|
|
|
|
|
|
"-----END PRIVATE KEY-----" |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
fun testGenerateKeyPair() { |
|
|
|
fun testGenerateKeyPair() { |
|
|
|
val (public, private) = Rsa.generateKeyPair() |
|
|
|
val (public, private) = Rsa.generateKeyPair() |
|
|
@ -24,45 +54,27 @@ object RsaTest { |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
fun testEncrypt() { |
|
|
|
fun testEncrypt() { |
|
|
|
// from https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Example |
|
|
|
val ciphertext = Rsa.encrypt(BigInteger("65"), PUBLIC_KEY) |
|
|
|
val public = allowUnsafeMod { RSAKeyParameters(false, BigInteger("3233"), BigInteger("17")) } |
|
|
|
|
|
|
|
val ciphertext = Rsa.encrypt(BigInteger("65"), public) |
|
|
|
|
|
|
|
assertEquals(BigInteger("2790"), ciphertext) |
|
|
|
assertEquals(BigInteger("2790"), ciphertext) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
fun testDecrypt() { |
|
|
|
fun testDecrypt() { |
|
|
|
// from https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Example |
|
|
|
val ciphertext = Rsa.decrypt(BigInteger("2790"), PRIVATE_KEY) |
|
|
|
val public = allowUnsafeMod { RSAKeyParameters(true, BigInteger("3233"), BigInteger("413")) } |
|
|
|
|
|
|
|
val ciphertext = Rsa.decrypt(BigInteger("2790"), public) |
|
|
|
|
|
|
|
assertEquals(BigInteger("65"), ciphertext) |
|
|
|
assertEquals(BigInteger("65"), ciphertext) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
fun testDecryptCrt() { |
|
|
|
fun testDecryptCrt() { |
|
|
|
// from https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Example |
|
|
|
val ciphertext = Rsa.decrypt(BigInteger("2790"), PRIVATE_KEY_CRT) |
|
|
|
val private = allowUnsafeMod { RSAPrivateCrtKeyParameters( |
|
|
|
|
|
|
|
BigInteger("3233"), // modulus |
|
|
|
|
|
|
|
BigInteger("17"), // public exponent |
|
|
|
|
|
|
|
BigInteger("413"), // private exponent |
|
|
|
|
|
|
|
BigInteger("61"), // p |
|
|
|
|
|
|
|
BigInteger("53"), // q |
|
|
|
|
|
|
|
BigInteger("53"), // dP |
|
|
|
|
|
|
|
BigInteger("49"), // dQ |
|
|
|
|
|
|
|
BigInteger("38") // qInv |
|
|
|
|
|
|
|
) } |
|
|
|
|
|
|
|
val ciphertext = Rsa.decrypt(BigInteger("2790"), private) |
|
|
|
|
|
|
|
assertEquals(BigInteger("65"), ciphertext) |
|
|
|
assertEquals(BigInteger("65"), ciphertext) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
fun testEncryptByteBuf() { |
|
|
|
fun testEncryptByteBuf() { |
|
|
|
// from https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Example |
|
|
|
|
|
|
|
val public = allowUnsafeMod { RSAKeyParameters(false, BigInteger("3233"), BigInteger("17")) } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val plaintext = Unpooled.wrappedBuffer(byteArrayOf(65)) |
|
|
|
val plaintext = Unpooled.wrappedBuffer(byteArrayOf(65)) |
|
|
|
try { |
|
|
|
try { |
|
|
|
val ciphertext = plaintext.rsaEncrypt(public) |
|
|
|
val ciphertext = plaintext.rsaEncrypt(PUBLIC_KEY) |
|
|
|
try { |
|
|
|
try { |
|
|
|
val expectedCiphertext = Unpooled.wrappedBuffer(byteArrayOf(10, 230.toByte())) |
|
|
|
val expectedCiphertext = Unpooled.wrappedBuffer(byteArrayOf(10, 230.toByte())) |
|
|
|
try { |
|
|
|
try { |
|
|
@ -80,12 +92,9 @@ object RsaTest { |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
fun testDecryptByteBuf() { |
|
|
|
fun testDecryptByteBuf() { |
|
|
|
// from https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Example |
|
|
|
|
|
|
|
val private = allowUnsafeMod { RSAKeyParameters(true, BigInteger("3233"), BigInteger("413")) } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val ciphertext = Unpooled.wrappedBuffer(byteArrayOf(10, 230.toByte())) |
|
|
|
val ciphertext = Unpooled.wrappedBuffer(byteArrayOf(10, 230.toByte())) |
|
|
|
try { |
|
|
|
try { |
|
|
|
val plaintext = ciphertext.rsaDecrypt(private) |
|
|
|
val plaintext = ciphertext.rsaDecrypt(PRIVATE_KEY) |
|
|
|
try { |
|
|
|
try { |
|
|
|
val expectedPlaintext = Unpooled.wrappedBuffer(byteArrayOf(65)) |
|
|
|
val expectedPlaintext = Unpooled.wrappedBuffer(byteArrayOf(65)) |
|
|
|
try { |
|
|
|
try { |
|
|
@ -101,6 +110,74 @@ object RsaTest { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun testReadPublicKey() { |
|
|
|
|
|
|
|
Jimfs.newFileSystem().use { fs -> |
|
|
|
|
|
|
|
val file = Files.createTempFile(fs.getPath("/"), "public", ".key") |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Files.write(file, PUBLIC_KEY_PEM) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val key = allowUnsafeMod { Rsa.readPublicKey(file) } |
|
|
|
|
|
|
|
assertFalse(key.isPrivate) |
|
|
|
|
|
|
|
assertEquals(PUBLIC_KEY.modulus, key.modulus) |
|
|
|
|
|
|
|
assertEquals(PUBLIC_KEY.exponent, key.exponent) |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
Files.deleteIfExists(file) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun testWritePublicKey() { |
|
|
|
|
|
|
|
Jimfs.newFileSystem().use { fs -> |
|
|
|
|
|
|
|
val file = Files.createTempFile(fs.getPath("/"), "public", ".key") |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Rsa.writePublicKey(file, PUBLIC_KEY) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assertEquals(PUBLIC_KEY_PEM, Files.readAllLines(file)) |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
Files.deleteIfExists(file) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun testReadPrivateKey() { |
|
|
|
|
|
|
|
Jimfs.newFileSystem().use { fs -> |
|
|
|
|
|
|
|
val file = Files.createTempFile(fs.getPath("/"), "private", ".key") |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Files.write(file, PRIVATE_KEY_PEM) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val key = allowUnsafeMod { Rsa.readPrivateKey(file) } as RSAPrivateCrtKeyParameters |
|
|
|
|
|
|
|
assertTrue(key.isPrivate) |
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_CRT.modulus, key.modulus) |
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_CRT.exponent, key.exponent) |
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_CRT.publicExponent, key.publicExponent) |
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_CRT.p, key.p) |
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_CRT.q, key.q) |
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_CRT.dp, key.dp) |
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_CRT.dq, key.dq) |
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_CRT.qInv, key.qInv) |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
Files.deleteIfExists(file) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun testWritePrivateKey() { |
|
|
|
|
|
|
|
Jimfs.newFileSystem().use { fs -> |
|
|
|
|
|
|
|
val file = Files.createTempFile(fs.getPath("/"), "private", ".key") |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Rsa.writePrivateKey(file, PRIVATE_KEY_CRT) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assertEquals(PRIVATE_KEY_PEM, Files.readAllLines(file)) |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
Files.deleteIfExists(file) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun <T> allowUnsafeMod(f: () -> T): T { |
|
|
|
private fun <T> allowUnsafeMod(f: () -> T): T { |
|
|
|
Properties.setThreadOverride(ALLOW_UNSAFE_MOD, true) |
|
|
|
Properties.setThreadOverride(ALLOW_UNSAFE_MOD, true) |
|
|
|
try { |
|
|
|
try { |
|
|
|