💨
About SSL/TLS Connection
- 通信経路を暗号化するTLS(Transport Layer Security)が規格化されている
- TLS は SSL(Secure Socket Layer)3.0 をbaseに TLS 標準化された
暗号化の仕組み
- 以下の2つを組み合わせて暗号化通信を実現している
- 共通鍵方式(AES)
- 公開鍵方式(RSA) → 応用版: デジタル署名
共通鍵方式 (Advanced Encryption Standard)
暗号化/復号化 に用いられる鍵は同じものを利用する
AES暗号化方式
- 暗号化アルゴリズムRijndael
- 鍵長が長いほど多くのラウンドを繰り返して暗号化する
- 128-AESは10ラウンド
- 192-AESは12ラウンド
- 256-AESは14ラウンド
- 鍵長が長いほど多くのラウンドを繰り返して暗号化する
公開鍵方式 (Rivest–Shamir–Adleman)
- 暗号化/復号化 に用いられる鍵が異なる
- 公開鍵で暗号化されたものは、秘密鍵のみで復号化可能
- 秘密鍵で暗号化されたものは、公開鍵のみで復号化可能
RSA暗号化方式
- 受信者が公開鍵と秘密鍵を生成する
- 受信者は送信者に公開鍵を送る
- 送信者がメッセージを公開鍵で暗号化する
- 受信者がメッセージを秘密鍵で復号する
RSA暗号の応用事例
- 秘密鍵を持つ側が、秘密鍵によって自身の署名を暗号化する
- この署名を受け取った側が公開鍵によって署名を復号できれば、その署名が公開鍵と対になる秘密鍵で暗号化されたものだと判明する
→ デジタル署名
AES/RSAを使い分ける理由
- RSAは安全性が高いがスループットが遅い
- RSAに比べて、AESはハードウェア処理が行われ 3 ~ 10 倍程度の速度になる
そのため、最も重要な共通鍵の交換時のみRSAでの通信が行われ、その後は通信速度の速いAESによる暗号化が実施される
AES vs RSA Benchmark
というわけで、実際にbenchmarkを取ってみる
@Fork(1)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5) //
@Measurement(iterations = 10) // CNT
open class EncryptionBenchmark {
private lateinit var aesKey: SecretKey
private lateinit var rsaKeyPair: KeyPair
private lateinit var aesData: ByteArray
private lateinit var rsaData: ByteArray
private lateinit var encryptedAESData: ByteArray
private lateinit var encryptedRSAData: ByteArray
@Setup
fun setup() {
// AES key setup
val keyGen = KeyGenerator.getInstance("AES")
keyGen.init(256)
aesKey = keyGen.generateKey()
// RSA key setup
val keyPairGen = KeyPairGenerator.getInstance("RSA")
keyPairGen.initialize(2048)
rsaKeyPair = keyPairGen.generateKeyPair()
// Sample data for AES
aesData = ByteArray(1024)
SecureRandom().nextBytes(aesData)
// Sample data for RSA (limited to 245 bytes)
rsaData = ByteArray(245)
SecureRandom().nextBytes(rsaData)
// Encrypt data for decryption benchmark
encryptedAESData = aesEncryption()
encryptedRSAData = rsaEncryption()
}
@Benchmark
fun aesEncryption(): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, aesKey)
return cipher.doFinal(aesData)
}
@Benchmark
fun aesDecrypt(): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, aesKey, cipher.parameters)
return cipher.doFinal(encryptedAESData)
}
@Benchmark
fun rsaEncryption(): ByteArray {
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.public)
return cipher.doFinal(rsaData)
}
@Benchmark
fun rsaDecrypt(): ByteArray {
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.private)
return cipher.doFinal(encryptedRSAData)
}
}
Benchmark Summary
Benchmark Mode Cnt Score Error Units
EncryptionBenchmark.aesEncryption thrpt 10 69.760 ± 0.628 ops/ms
EncryptionBenchmark.rsaEncryption thrpt 10 14.888 ± 0.553 ops/ms
EncryptionBenchmark.aesDecrypt thrpt 10 67.767 ± 4.767 ops/ms
EncryptionBenchmark.rsaDecrypt thrpt 10 0.650 ± 0.020 ops/ms
- AES暗号化: 1ミリ秒あたり約69.760回の操作が可能
- RSA暗号化: 1ミリ秒あたり約14.888回の操作が可能
- AES復号化: 1ミリ秒あたり約67.767回の操作が可能
- RSA復号化: 1ミリ秒あたり約0.650回の操作が可能
想定通りの結果
Discussion