mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-12-05 11:51:09 +01:00
YubikeyDataDecryptorFactory: WIP with SECP521r1 keys
This commit is contained in:
parent
f26d91d41f
commit
89bce1ca14
2 changed files with 63 additions and 5 deletions
|
|
@ -12,6 +12,7 @@ import org.bouncycastle.bcpg.KeyIdentifier
|
||||||
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags
|
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags
|
||||||
import org.bouncycastle.bcpg.PublicKeyPacket
|
import org.bouncycastle.bcpg.PublicKeyPacket
|
||||||
import org.bouncycastle.crypto.params.KeyParameter
|
import org.bouncycastle.crypto.params.KeyParameter
|
||||||
|
import org.bouncycastle.jce.ECNamedCurveTable
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey
|
import org.bouncycastle.openpgp.PGPPublicKey
|
||||||
import org.bouncycastle.openpgp.operator.PGPPad
|
import org.bouncycastle.openpgp.operator.PGPPad
|
||||||
|
|
@ -21,9 +22,11 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider
|
||||||
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory
|
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory
|
||||||
import org.bouncycastle.openpgp.operator.bc.RFC6637KDFCalculator
|
import org.bouncycastle.openpgp.operator.bc.RFC6637KDFCalculator
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter
|
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter
|
||||||
|
import org.pgpainless.bouncycastle.extensions.getCurveName
|
||||||
import org.pgpainless.decryption_verification.HardwareSecurity
|
import org.pgpainless.decryption_verification.HardwareSecurity
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint
|
import org.pgpainless.key.OpenPgpV4Fingerprint
|
||||||
import org.pgpainless.key.SubkeyIdentifier
|
import org.pgpainless.key.SubkeyIdentifier
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class YubikeyDataDecryptorFactory(
|
class YubikeyDataDecryptorFactory(
|
||||||
|
|
@ -32,6 +35,10 @@ class YubikeyDataDecryptorFactory(
|
||||||
) : HardwareSecurity.HardwareDataDecryptorFactory(subkeyIdentifier, callback) {
|
) : HardwareSecurity.HardwareDataDecryptorFactory(subkeyIdentifier, callback) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val LOGGER = LoggerFactory.getLogger(YubikeyDataDecryptorFactory::class.java)
|
||||||
|
|
||||||
val ADMIN_PIN: CharArray = "12345678".toCharArray()
|
val ADMIN_PIN: CharArray = "12345678".toCharArray()
|
||||||
val USER_PIN: CharArray = "123456".toCharArray()
|
val USER_PIN: CharArray = "123456".toCharArray()
|
||||||
|
|
||||||
|
|
@ -42,8 +49,7 @@ class YubikeyDataDecryptorFactory(
|
||||||
pubkey: PGPPublicKey
|
pubkey: PGPPublicKey
|
||||||
): HardwareSecurity.HardwareDataDecryptorFactory {
|
): HardwareSecurity.HardwareDataDecryptorFactory {
|
||||||
val openpgpSession = OpenPgpSession(smartCardConnection)
|
val openpgpSession = OpenPgpSession(smartCardConnection)
|
||||||
// openpgpSession.verifyAdminPin(ADMIN_PIN)
|
val decKeyIdentifier = SubkeyIdentifier(OpenPgpV4Fingerprint(pubkey))
|
||||||
val decKeyIdentifier: SubkeyIdentifier = SubkeyIdentifier(OpenPgpV4Fingerprint(pubkey))
|
|
||||||
|
|
||||||
val isRSAKey = pubkey.algorithm == PublicKeyAlgorithmTags.RSA_GENERAL
|
val isRSAKey = pubkey.algorithm == PublicKeyAlgorithmTags.RSA_GENERAL
|
||||||
|| pubkey.algorithm == PublicKeyAlgorithmTags.RSA_SIGN
|
|| pubkey.algorithm == PublicKeyAlgorithmTags.RSA_SIGN
|
||||||
|
|
@ -60,13 +66,18 @@ class YubikeyDataDecryptorFactory(
|
||||||
openpgpSession.verifyAdminPin(ADMIN_PIN)
|
openpgpSession.verifyAdminPin(ADMIN_PIN)
|
||||||
openpgpSession.verifyUserPin(USER_PIN, true)
|
openpgpSession.verifyUserPin(USER_PIN, true)
|
||||||
|
|
||||||
|
LOGGER.debug("Attempt decryption with key {}", keyIdentifier)
|
||||||
|
|
||||||
if(isRSAKey) {
|
if(isRSAKey) {
|
||||||
// easy
|
// easy
|
||||||
|
LOGGER.debug("Key is RSA key of length {}", pubkey.bitStrength)
|
||||||
val decryptedSessionKey = openpgpSession.decrypt(sessionKeyData)
|
val decryptedSessionKey = openpgpSession.decrypt(sessionKeyData)
|
||||||
return decryptedSessionKey
|
return decryptedSessionKey
|
||||||
} else {
|
} else {
|
||||||
// meh...
|
// meh...
|
||||||
|
val curveName = pubkey.getCurveName()
|
||||||
val ecPubKey: ECDHPublicBCPGKey = pubkey.publicKeyPacket.key as ECDHPublicBCPGKey
|
val ecPubKey: ECDHPublicBCPGKey = pubkey.publicKeyPacket.key as ECDHPublicBCPGKey
|
||||||
|
LOGGER.debug("Key is ECDH key over curve $curveName")
|
||||||
// split session data into peer key and encrypted session key
|
// split session data into peer key and encrypted session key
|
||||||
|
|
||||||
// peer key
|
// peer key
|
||||||
|
|
@ -83,9 +94,8 @@ class YubikeyDataDecryptorFactory(
|
||||||
System.arraycopy(sessionKeyData, 2 + pLen + 1, keyEnc, 0, keyLen)
|
System.arraycopy(sessionKeyData, 2 + pLen + 1, keyEnc, 0, keyLen)
|
||||||
|
|
||||||
// perform ECDH key agreement via the Yubikey
|
// perform ECDH key agreement via the Yubikey
|
||||||
val x9Params =
|
val params = ECNamedCurveTable.getParameterSpec(curveName)
|
||||||
org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOIDLazy(ecPubKey.curveOID)
|
val publicPoint = params.curve.decodePoint(pEnc)
|
||||||
val publicPoint = x9Params.curve.decodePoint(pEnc)
|
|
||||||
val peerKey = JcaPGPKeyConverter().setProvider(BouncyCastleProvider())
|
val peerKey = JcaPGPKeyConverter().setProvider(BouncyCastleProvider())
|
||||||
.getPublicKey(
|
.getPublicKey(
|
||||||
PGPPublicKey(
|
PGPPublicKey(
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,20 @@ import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
||||||
import com.yubico.yubikit.desktop.CompositeDevice
|
import com.yubico.yubikit.desktop.CompositeDevice
|
||||||
import com.yubico.yubikit.desktop.YubiKitManager
|
import com.yubico.yubikit.desktop.YubiKitManager
|
||||||
import com.yubico.yubikit.openpgp.OpenPgpSession
|
import com.yubico.yubikit.openpgp.OpenPgpSession
|
||||||
|
import org.bouncycastle.jce.ECNamedCurveTable
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
|
import org.bouncycastle.openpgp.PGPUtil
|
||||||
import org.bouncycastle.openpgp.api.bc.BcOpenPGPImplementation
|
import org.bouncycastle.openpgp.api.bc.BcOpenPGPImplementation
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter
|
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter
|
||||||
import org.gnupg.GnuPGDummyKeyUtil
|
import org.gnupg.GnuPGDummyKeyUtil
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.pgpainless.PGPainless
|
import org.pgpainless.PGPainless
|
||||||
import org.pgpainless.algorithm.KeyFlag
|
import org.pgpainless.algorithm.KeyFlag
|
||||||
import org.pgpainless.algorithm.OpenPGPKeyVersion
|
import org.pgpainless.algorithm.OpenPGPKeyVersion
|
||||||
|
import org.pgpainless.bouncycastle.extensions.getCurveName
|
||||||
import org.pgpainless.bouncycastle.extensions.toOpenPGPKey
|
import org.pgpainless.bouncycastle.extensions.toOpenPGPKey
|
||||||
import org.pgpainless.decryption_verification.ConsumerOptions
|
import org.pgpainless.decryption_verification.ConsumerOptions
|
||||||
import org.pgpainless.encryption_signing.EncryptionOptions
|
import org.pgpainless.encryption_signing.EncryptionOptions
|
||||||
|
|
@ -119,6 +124,7 @@ class YubikeyTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test() {
|
fun test() {
|
||||||
|
println(CERT)
|
||||||
val api = PGPainless(BcOpenPGPImplementation())
|
val api = PGPainless(BcOpenPGPImplementation())
|
||||||
val key = api.readKey().parseKey(KEY)
|
val key = api.readKey().parseKey(KEY)
|
||||||
|
|
||||||
|
|
@ -171,4 +177,46 @@ class YubikeyTest {
|
||||||
assertEquals("Hello, World!\n", String(msg))
|
assertEquals("Hello, World!\n", String(msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val pubKeyAscii = // "modernKeyRing"
|
||||||
|
"""
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Comment: C68A 9140 9A00 3C55 5D8A 62A5 D3ED 03F9 FF75 68D4
|
||||||
|
Comment: john doe <j.doe@example.com>
|
||||||
|
|
||||||
|
mDMEaR7iehYJKwYBBAHaRw8BAQdA5XMRsc8HUXiJkjtOSCj86v+OeemU41U08Lmi
|
||||||
|
2PQ3Tnm0HGpvaG4gZG9lIDxqLmRvZUBleGFtcGxlLmNvbT7CnwQTFgoAUQkQ0+0D
|
||||||
|
+f91aNQWoQTGipFAmgA8VV2KYqXT7QP5/3Vo1AWCaR7iegKbAQUVCgkICwUWAgMB
|
||||||
|
AAQLCQgHCScJAQkCCQMIAQKeCQWJCWYBgAKZAQAAFecBALy6FxELczpihvkVJPa2
|
||||||
|
iaV7zDcfFBvX4KyTr506hxufAP4ixT59d/QRMWmuRN6QRkRcgduLaw2l/Hs/zBuV
|
||||||
|
tQjHDsKfBBMWCgBRApsBBRUKCQgLBRYCAwEABAsJCAcJJwkBCQIJAwgBAp4JCRDT
|
||||||
|
7QP5/3Vo1BahBMaKkUCaADxVXYpipdPtA/n/dWjUBYJpHuJ7BYkAAAAAApkBAAAK
|
||||||
|
SQD9FpbJAinkmaeHluaKmiCp0HggoGF8aji9rDqSvUDtnWsA/i5I1eZ0rPvxZc6z
|
||||||
|
pIbfRHdbdgOTmTZEOOz82GQVmsMLuDgEaR7iehIKKwYBBAGXVQEFAQEHQHc9W+J1
|
||||||
|
IPl7nekdLrx5SLdvYnNNocULlqqLoDgN3fV4AwEIB8J4BBgWCgAqCRDT7QP5/3Vo
|
||||||
|
1BahBMaKkUCaADxVXYpipdPtA/n/dWjUBYJpHuJ6ApsMAACh/AEA1r+JB8uhMX7N
|
||||||
|
l4B3QOF9zLmUXhihRvE0tyY3cCCwUrYA/2yqF1mA8dHsDuDnWEUYxgX+ZpYBXr+P
|
||||||
|
j9ZKl/HoNeIOuDMEaR7iehYJKwYBBAHaRw8BAQdAQPTWzF21MpuSRjclxeAS+lZH
|
||||||
|
ulTwm/HsOaVpur8vSZ/CwC8EGBYKAKEJENPtA/n/dWjUFqEExoqRQJoAPFVdimKl
|
||||||
|
0+0D+f91aNQFgmke4noCmwJ2IAQZFgoAHQWCaR7iehYhBMqKZfP+EDi1iRE58a14
|
||||||
|
HgK5jFyDAAoJEK14HgK5jFyDoRUA/0GmLpBUVFSEdbSh+o7tz6xncAIjkm20LWIy
|
||||||
|
PF81ilR9AP0a/MVoE9ivY7HK9uu79cc2Y5IratjiXRpamYqODQutAQAA848A/Ro1
|
||||||
|
SfAfFAmMDfcbuKvpQEK/d4T3455End3ohd5TXb7VAP9/wMxzLJ1K5mE6LTQ5Hw4b
|
||||||
|
m9XtYRYVHugI27XFacaFAg==
|
||||||
|
=3yy3
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getx9ParamsTest() {
|
||||||
|
val certificate = org.bouncycastle.openpgp.api.OpenPGPKeyReader().parseCertificate(pubKeyAscii.toByteArray())
|
||||||
|
val pubKey = certificate.getEncryptionKeys().first().getPGPPublicKey()
|
||||||
|
|
||||||
|
assertTrue(pubKey.isEncryptionKey())
|
||||||
|
|
||||||
|
val ecPubKey = pubKey.getPublicKeyPacket().getKey() as org.bouncycastle.bcpg.ECDHPublicBCPGKey
|
||||||
|
val params = ECNamedCurveTable.getParameterSpec(pubKey.getCurveName())
|
||||||
|
|
||||||
|
assertNotNull(params) // fails
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue