Supports private-keys with passphrase for config-server (#2169)

This commit is contained in:
Kaveh Shamsi
2022-09-29 14:57:52 +02:00
committed by GitHub
parent 11e213c71c
commit f63040ac56
5 changed files with 86 additions and 9 deletions

View File

@@ -23,11 +23,14 @@ import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.util.Collection;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
import org.apache.sshd.common.session.SessionContext;
import org.apache.sshd.common.util.io.resource.AbstractIoResource;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.springframework.util.StringUtils;
final class KeyPairUtils {
private static final KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceParser();
@@ -36,14 +39,18 @@ final class KeyPairUtils {
}
static Collection<KeyPair> load(SessionContext session, String privateKey)
static Collection<KeyPair> load(SessionContext session, String privateKey, String passphrase)
throws IOException, GeneralSecurityException {
return loader.loadKeyPairs(session, new StringResource(privateKey), null);
FilePasswordProvider passwordProvider = StringUtils.hasText(passphrase) ? FilePasswordProvider.of(passphrase)
: FilePasswordProvider.EMPTY;
return loader.loadKeyPairs(session, new StringResource(privateKey), passwordProvider);
}
static boolean isValid(String privateKey) {
static boolean isValid(String privateKey, String passphrase) {
try {
return !KeyPairUtils.load(null, privateKey).isEmpty();
return !KeyPairUtils.load(null, privateKey, passphrase).isEmpty();
}
catch (IOException | GeneralSecurityException ignored) {
return false;

View File

@@ -83,7 +83,7 @@ public class PrivateKeyValidator implements ConstraintValidator<PrivateKeyIsVali
private boolean isPrivateKeyFormatCorrect(JGitEnvironmentProperties sshUriProperties,
ConstraintValidatorContext context) {
if (KeyPairUtils.isValid(sshUriProperties.getPrivateKey())) {
if (KeyPairUtils.isValid(sshUriProperties.getPrivateKey(), sshUriProperties.getPassphrase())) {
return true;
}

View File

@@ -212,7 +212,7 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName());
return sshProperties == null ? Collections.emptyList()
: KeyPairUtils.load(session, sshProperties.getPrivateKey());
: KeyPairUtils.load(session, sshProperties.getPrivateKey(), sshProperties.getPassphrase());
}
}

View File

@@ -158,7 +158,23 @@ public class PropertyBasedSshSessionFactoryTest {
PrivateKey privateKey = getSshPrivateKey("gitlab.example.local");
assertThat(privateKey).isNotNull();
assertThat(privateKey).isEqualTo(toPrivateKey(PRIVATE_KEY));
assertThat(privateKey).isEqualTo(toPrivateKey(PRIVATE_KEY, null));
}
@Test
public void privateKeyWithPassphraseIsUsed() {
String keyWithPassphrase = getResourceAsString("/ssh/key-with-passphrase");
JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git");
sshKey.setPrivateKey(keyWithPassphrase);
sshKey.setPassphrase("secret");
setupSessionFactory(sshKey);
PrivateKey privateKey = getSshPrivateKey("gitlab.example.local");
assertThat(privateKey).isNotNull();
assertThat(privateKey).isEqualTo(toPrivateKey(keyWithPassphrase, "secret"));
}
@Test
@@ -261,9 +277,9 @@ public class PropertyBasedSshSessionFactoryTest {
}
}
private PrivateKey toPrivateKey(String key) {
private PrivateKey toPrivateKey(String key, String passphrase) {
try {
Collection<KeyPair> keyPairs = KeyPairUtils.load(null, key);
Collection<KeyPair> keyPairs = KeyPairUtils.load(null, key, passphrase);
return keyPairs.isEmpty() ? null : keyPairs.iterator().next().getPrivate();
}

View File

@@ -0,0 +1,54 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,7310DDCF47A57E8764693DFBACB0FB4A
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
-----END RSA PRIVATE KEY-----