feat: supports ECDSA keypair

This commit is contained in:
hstyi
2025-10-01 11:46:56 +08:00
committed by hstyi
parent 0aabe1b0dc
commit 5fc76d955a
4 changed files with 40 additions and 4 deletions

View File

@@ -287,6 +287,9 @@ class KeyManagerPanel(private val accountOwner: AccountOwner) : JPanel(BorderLay
typeComboBox.addItem("RSA") typeComboBox.addItem("RSA")
typeComboBox.addItem("ED25519") typeComboBox.addItem("ED25519")
typeComboBox.addItem("ECDSA-SHA2-NISTP256")
typeComboBox.addItem("ECDSA-SHA2-NISTP384")
typeComboBox.addItem("ECDSA-SHA2-NISTP521")
// 默认 RSA // 默认 RSA
lengthComboBox.addItem(1024) lengthComboBox.addItem(1024)
@@ -396,6 +399,12 @@ class KeyManagerPanel(private val accountOwner: AccountOwner) : JPanel(BorderLay
lengthComboBox.addItem(1024 * 4) lengthComboBox.addItem(1024 * 4)
lengthComboBox.addItem(1024 * 8) lengthComboBox.addItem(1024 * 8)
lengthComboBox.selectedItem = 1024 * 2 lengthComboBox.selectedItem = 1024 * 2
} else if (typeComboBox.selectedItem == "ECDSA-SHA2-NISTP256") {
lengthComboBox.addItem(256)
} else if (typeComboBox.selectedItem == "ECDSA-SHA2-NISTP384") {
lengthComboBox.addItem(384)
} else if (typeComboBox.selectedItem == "ECDSA-SHA2-NISTP521") {
lengthComboBox.addItem(521)
} }
} }
} }
@@ -413,6 +422,17 @@ class KeyManagerPanel(private val accountOwner: AccountOwner) : JPanel(BorderLay
super.doCancelAction() super.doCancelAction()
} }
private fun genKeyPair(): KeyPair {
val keyType = when (typeComboBox.selectedItem) {
"ED25519" -> KeyPairProvider.SSH_ED25519
"ECDSA-SHA2-NISTP256" -> KeyPairProvider.ECDSA_SHA2_NISTP256
"ECDSA-SHA2-NISTP384" -> KeyPairProvider.ECDSA_SHA2_NISTP384
"ECDSA-SHA2-NISTP521" -> KeyPairProvider.ECDSA_SHA2_NISTP521
else -> KeyPairProvider.SSH_RSA
}
return KeyUtils.generateKeyPair(keyType, lengthComboBox.selectedItem as Int)
}
override fun doOKAction() { override fun doOKAction() {
if (ohKeyPair == OhKeyPair.empty) { if (ohKeyPair == OhKeyPair.empty) {
@@ -422,9 +442,7 @@ class KeyManagerPanel(private val accountOwner: AccountOwner) : JPanel(BorderLay
return return
} }
val keyType = if (typeComboBox.selectedItem == "RSA") val keyPair = genKeyPair()
KeyPairProvider.SSH_RSA else KeyPairProvider.SSH_ED25519
val keyPair = KeyUtils.generateKeyPair(keyType, lengthComboBox.selectedItem as Int)
ohKeyPair = OhKeyPair( ohKeyPair = OhKeyPair(
id = randomUUID(), id = randomUUID(),
name = nameTextField.text, name = nameTextField.text,

View File

@@ -2,6 +2,7 @@ package app.termora.keymgr
import app.termora.AES.decodeBase64 import app.termora.AES.decodeBase64
import app.termora.RSA import app.termora.RSA
import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder
import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider
import org.apache.sshd.common.session.SessionContext import org.apache.sshd.common.session.SessionContext
import org.apache.sshd.common.util.security.eddsa.Ed25519PublicKeyDecoder import org.apache.sshd.common.util.security.eddsa.Ed25519PublicKeyDecoder
@@ -25,6 +26,8 @@ class OhKeyPairKeyPairProvider(private val id: String) : AbstractResourceKeyPair
when (ohKeyPair.type) { when (ohKeyPair.type) {
"RSA" -> RSA.generatePublic(ohKeyPair.publicKey.decodeBase64()) "RSA" -> RSA.generatePublic(ohKeyPair.publicKey.decodeBase64())
"ED25519" -> Ed25519PublicKeyDecoder.INSTANCE.generatePublicKey((X509EncodedKeySpec(ohKeyPair.publicKey.decodeBase64()))) "ED25519" -> Ed25519PublicKeyDecoder.INSTANCE.generatePublicKey((X509EncodedKeySpec(ohKeyPair.publicKey.decodeBase64())))
"ECDSA-SHA2-NISTP256","ECDSA-SHA2-NISTP384","ECDSA-SHA2-NISTP521" ->
ECDSAPublicKeyEntryDecoder.INSTANCE.generatePublicKey(X509EncodedKeySpec(ohKeyPair.publicKey.decodeBase64()))
else -> throw UnsupportedOperationException("${ohKeyPair.type} is not supported") else -> throw UnsupportedOperationException("${ohKeyPair.type} is not supported")
} }
} as PublicKey } as PublicKey
@@ -33,6 +36,8 @@ class OhKeyPairKeyPairProvider(private val id: String) : AbstractResourceKeyPair
when (ohKeyPair.type) { when (ohKeyPair.type) {
"RSA" -> RSA.generatePrivate(ohKeyPair.privateKey.decodeBase64()) "RSA" -> RSA.generatePrivate(ohKeyPair.privateKey.decodeBase64())
"ED25519" -> Ed25519PublicKeyDecoder.INSTANCE.generatePrivateKey(PKCS8EncodedKeySpec(ohKeyPair.privateKey.decodeBase64())) "ED25519" -> Ed25519PublicKeyDecoder.INSTANCE.generatePrivateKey(PKCS8EncodedKeySpec(ohKeyPair.privateKey.decodeBase64()))
"ECDSA-SHA2-NISTP256","ECDSA-SHA2-NISTP384","ECDSA-SHA2-NISTP521" ->
ECDSAPublicKeyEntryDecoder.INSTANCE.generatePrivateKey(PKCS8EncodedKeySpec(ohKeyPair.privateKey.decodeBase64()))
else -> throw UnsupportedOperationException("${ohKeyPair.type} is not supported") else -> throw UnsupportedOperationException("${ohKeyPair.type} is not supported")
} }
} as PrivateKey } as PrivateKey

View File

@@ -15,6 +15,19 @@ class KeyUtilsTest {
assertEquals(KeyUtils.getKeySize(KeyUtils.generateKeyPair("ssh-rsa", 1024).public), 1024) assertEquals(KeyUtils.getKeySize(KeyUtils.generateKeyPair("ssh-rsa", 1024).public), 1024)
} }
@Test
fun test_ECDSA() {
assertEquals(KeyUtils.getKeySize(KeyUtils.generateKeyPair(KeyPairProvider.ECDSA_SHA2_NISTP256, 256).private), 256)
assertEquals(KeyUtils.getKeySize(KeyUtils.generateKeyPair(KeyPairProvider.ECDSA_SHA2_NISTP256, 256).public), 256)
assertEquals(KeyUtils.getKeySize(KeyUtils.generateKeyPair(KeyPairProvider.ECDSA_SHA2_NISTP384, 384).private), 384)
assertEquals(KeyUtils.getKeySize(KeyUtils.generateKeyPair(KeyPairProvider.ECDSA_SHA2_NISTP384, 384).public), 384)
assertEquals(KeyUtils.getKeySize(KeyUtils.generateKeyPair(KeyPairProvider.ECDSA_SHA2_NISTP521, 521).private), 521)
assertEquals(KeyUtils.getKeySize(KeyUtils.generateKeyPair(KeyPairProvider.ECDSA_SHA2_NISTP521, 521).public), 521)
}
@Test @Test
fun test_ed25519() { fun test_ed25519() {
val keyPair = KeyUtils.generateKeyPair(KeyPairProvider.SSH_ED25519, 256) val keyPair = KeyUtils.generateKeyPair(KeyPairProvider.SSH_ED25519, 256)

View File

@@ -1,4 +1,4 @@
FROM linuxserver/openssh-server FROM linuxserver/openssh-server:9.3_p2-r1-ls147
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update && apk add wget tmux gcc zip p7zip g++ git make zsh htop stress-ng inetutils-telnet xclock xcalc xorg-server xinit && wget https://ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz \ && apk update && apk add wget tmux gcc zip p7zip g++ git make zsh htop stress-ng inetutils-telnet xclock xcalc xorg-server xinit && wget https://ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz \
&& tar -xf lrzsz-0.12.20.tar.gz && cd lrzsz-0.12.20 && ./configure && make && make install \ && tar -xf lrzsz-0.12.20.tar.gz && cd lrzsz-0.12.20 && ./configure && make && make install \