OPEN API 사용을 위한 개인정보 복호화 플로우
1.
RSA 키 쌍 생성
a.
토스쪽에 공개키 전달
b.
서로간의 방화벽 허용이 필요함
•
토스 오피스 IP
◦
182.162.119.2
◦
211.106.118.97
◦
210.99.240.112
◦
15.164.196.66
◦
43.202.172.33
•
운영 서버 IP대역
◦
117.52.3.80 ~ 87
◦
211.115.96.80 ~ 87
2.
API를 통해 encryptedKey, iv 그리고 암호화된 개인정보를 받음
3.
encryptedKey를 1에서 생성한 키 쌍의 개인키로 RSA복호화를 진행하여 AES Key를 생성
a.
4.
3에서 만든 AES key와 iv 값으로 AES 복호화를 진행해주시면 됩니다
a.
AES 암복호화 샘플코드
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
class AesEncryptManager {
companion object {
const val GCM_TAG_BYTE_LENGTH = 16
}
fun encryptGcm(target: ByteArray, secretKey: ByteArray, iv: ByteArray): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val nonceSpec = GCMParameterSpec(GCM_TAG_BYTE_LENGTH * Byte.SIZE_BITS, iv)
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(secretKey, "AES"), nonceSpec)
val ciphertext = cipher.doFinal(target)
return Base64.getEncoder().encodeToString(ciphertext)
}
fun decryptGcm(target: String, secretKey: ByteArray, iv: ByteArray): ByteArray {
val encrypted = Base64.getDecoder().decode(target)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val nonceSpec = GCMParameterSpec(GCM_TAG_BYTE_LENGTH * Byte.SIZE_BITS, iv)
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(secretKey, "AES"), nonceSpec)
return cipher.doFinal(encrypted)
}
}
fun main() {
val aesEncryptManager = AesEncryptManager()
val secretKey = "l6JmtDcgDU3igWLgRF4QdVbjaleD9IMMphkfd6Egzb4="
// Base64.getEncoder().encodeToString(SecureRandom().nextBytes(ByteArray(32)))
val iv = "A230BGRSpFiqqt6B"
// Base64.getEncoder().encodeToString(SecureRandom().nextBytes(ByteArray(12)))
val target = "김토스"
val secretBytes = Base64.getDecoder().decode(secretKey)
val ivBytes = Base64.getDecoder().decode(iv)
val gcmEncrypted = aesEncryptManager.encryptGcm(target.toByteArray(), secretBytes, ivBytes)
val gcmDecrypted = aesEncryptManager.decryptGcm(gcmEncrypted, secretBytes, ivBytes)
println("GCM Encrypted: $gcmEncrypted")
println("GCM Decrypted: ${String(gcmDecrypted, Charsets.UTF_8)}")
}
Kotlin
복사
RSA 암복호화 샘플코드
•
OAEP 해시: SHA-256
•
MGF1 해시: SHA-1
fun encryptWithRSA(plain: ByteArray, publicKey: PublicKey): ByteArray {
val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
return cipher.doFinal(plain)
}
fun decryptWithRSA(encryptedTarget: String): ByteArray {
val privateKeyStr = Base64.getDecoder().decode("privateKey를 여기에 입력하세요")
val keyFactory = KeyFactory.getInstance("RSA")
val privateKey = keyFactory.generatePrivate(PKCS8EncodedKeySpec(privateKeyStr))
val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
cipher.init(Cipher.DECRYPT_MODE, privateKey)
val byteEncrypted: ByteArray = Base64.getDecoder().decode(encryptedTarget)
return cipher.doFinal(byteEncrypted)
}
Kotlin
복사
전체 샘플코드
import java.security.KeyFactory
import java.security.PublicKey
import java.security.SecureRandom
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
/**
* 외부 제휴사에게 제공할 샘플 코드
*/
class SampleEncryptManager {
companion object {
private const val GCM_TAG_BYTE_LENGTH = 16
}
fun generateAesKey(): ByteArray {
val key = ByteArray(32)
SecureRandom().nextBytes(key)
return key
}
fun generateIv(): ByteArray {
val iv = ByteArray(12)
SecureRandom().nextBytes(iv)
return iv
}
fun convertEncodedToPublicKey(encoded: ByteArray, algorithm: String): PublicKey {
val keyFactory = KeyFactory.getInstance(algorithm)
return keyFactory.generatePublic(X509EncodedKeySpec(encoded))
}
fun encryptGcm(target: ByteArray, secretKey: ByteArray, iv: ByteArray): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val nonceSpec = GCMParameterSpec(GCM_TAG_BYTE_LENGTH * Byte.SIZE_BITS, iv)
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(secretKey, "AES"), nonceSpec)
val ciphertext = cipher.doFinal(target)
return Base64.getEncoder().encodeToString(ciphertext)
}
fun decryptGcm(target: String, secretKey: ByteArray, iv: ByteArray): ByteArray {
val encrypted = Base64.getDecoder().decode(target)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val nonceSpec = GCMParameterSpec(GCM_TAG_BYTE_LENGTH * Byte.SIZE_BITS, iv)
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(secretKey, "AES"), nonceSpec)
return cipher.doFinal(encrypted)
}
fun encryptWithRSA(plain: ByteArray, publicKey: PublicKey): ByteArray {
val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
return cipher.doFinal(plain)
}
fun decryptWithRSA(encryptedTarget: String): ByteArray {
val privateKeyBytes = Base64.getDecoder().decode("privateKey를 여기에 입력하세요")
val keyFactory = KeyFactory.getInstance("RSA")
val privateKey = keyFactory.generatePrivate(PKCS8EncodedKeySpec(privateKeyBytes))
val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
cipher.init(Cipher.DECRYPT_MODE, privateKey)
val byteEncrypted: ByteArray = Base64.getDecoder().decode(encryptedTarget)
return cipher.doFinal(byteEncrypted)
}
}
fun main() {
val encryptManager = SampleEncryptManager()
// 1. 데이터 암호화
// AES 키 생성 예) l6JmtDcgDU3igWLgRF4QdVbjaleD9IMMphkfd6Egzb4=
val aesKeyBytes: ByteArray = encryptManager.generateAesKey()
val base64encodedAesKey: String = Base64.getEncoder().encodeToString(aesKeyBytes)
println("AES key: $base64encodedAesKey")
// IV 생성 예) A230BGRSpFiqqt6B
val ivBytes: ByteArray = encryptManager.generateIv()
val base64encodedIv: String = Base64.getEncoder().encodeToString(ivBytes)
println("IV: $base64encodedIv")
val name = "김토스"
// AES-GCM 암호화 (target을 암호화)
val gcmAesEncryptedName = encryptManager.encryptGcm(name.toByteArray(), aesKeyBytes, ivBytes)
println("GCM AES Encrypted name: $gcmAesEncryptedName")
// RSA 암호화 (AES 키 암호화)
val publicKeyBytes = Base64.getDecoder().decode("publicKey를 여기에 입력하세요")
val publicKey = encryptManager.convertEncodedToPublicKey(publicKeyBytes, "RSA")
val rsaEncryptedKey = encryptManager.encryptWithRSA(aesKeyBytes, publicKey)
// 2. API 응답
val encryptedName = gcmAesEncryptedName
val encryptedKey: String = Base64.getEncoder().encodeToString(rsaEncryptedKey)
val iv: String = Base64.getEncoder().encodeToString(ivBytes)
println("Response - encryptedName: $encryptedName")
println("Response - encryptedKey: $encryptedKey")
println("Response - iv: $iv")
// 3. 데이터 복호화
// RSA 복호화 (AES 키 복호화)
val rsaDecryptedKey = encryptManager.decryptWithRSA(encryptedKey)
val decodedIv = Base64.getDecoder().decode(iv)
val gcmDecrypted = encryptManager.decryptGcm(encryptedName, rsaDecryptedKey, decodedIv)
println("GCM AES Decrypted name: ${String(gcmDecrypted, Charsets.UTF_8)}")
}
Kotlin
복사