32 | * This interface is a base interface that defines commonality across all the modes of authentications,
33 | * then the subtype defines a specific type of secret.
34 | *
35 | *
36 | * @see SSHUserPassword
37 | * @see SSHUserPrivateKey
38 | * @see SSHUserListBoxModel
39 | * @deprecated Use {@link StandardUsernameCredentials} as the common class.
40 | */
41 | @Deprecated
42 | public interface SSHUser extends StandardUsernameCredentials {
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/SSHUserListBoxModel.java:
--------------------------------------------------------------------------------
1 | package com.cloudbees.jenkins.plugins.sshcredentials;
2 |
3 | import com.cloudbees.plugins.credentials.CredentialsMatcher;
4 | import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel;
5 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
6 | import com.cloudbees.plugins.credentials.common.StandardUsernameListBoxModel;
7 | import com.cloudbees.plugins.credentials.domains.DomainRequirement;
8 | import edu.umd.cs.findbugs.annotations.NonNull;
9 | import hudson.Util;
10 | import hudson.security.ACL;
11 | import jenkins.model.Jenkins;
12 |
13 | import java.util.Arrays;
14 | import java.util.Collection;
15 | import java.util.Collections;
16 | import java.util.List;
17 |
18 | /** @deprecated Use {@link StandardUsernameListBoxModel} with {@link SSHAuthenticator} instead. */
19 | public class SSHUserListBoxModel extends AbstractIdCredentialsListBoxModel {
20 | /**
21 | * {@inheritDoc}
22 | */
23 | @NonNull
24 | protected String describe(@NonNull StandardUsernameCredentials c) {
25 | String description = Util.fixEmptyAndTrim(c.getDescription());
26 | return c.getUsername() + (description != null ? " (" + description + ")" : "");
27 | }
28 |
29 | /**
30 | * @deprecated use {@link #with(com.cloudbees.plugins.credentials.common.IdCredentials)}
31 | */
32 | @Deprecated
33 | public SSHUserListBoxModel add(StandardUsernameCredentials u) {
34 | with(u);
35 | return this;
36 | }
37 |
38 | /**
39 | * Adds a collection of credentials (they will be filtered with {@link SSHAuthenticator#matcher()} implicitly).
40 | *
41 | * @param col the collection of credentials.
42 | * @return {@code this} for method chaining.
43 | * @deprecated use {@link #withMatching(CredentialsMatcher, Iterable)} or {@link #withAll(Iterable)}
44 | */
45 | @Deprecated
46 | public SSHUserListBoxModel addCollection(Collection extends StandardUsernameCredentials> col) {
47 | withMatching(SSHAuthenticator.matcher(), col);
48 | return this;
49 | }
50 |
51 | /**
52 | * Adds all the system-scoped credentials (they will be filtered with {@link SSHAuthenticator#matcher()}
53 | * implicitly).
54 | *
55 | * These credentials are meant to be used for system configuration and other things scoped to the {@link Jenkins}
56 | * object,
57 | * such as slaves.
58 | *
59 | *
60 | * @return {@code this} for method chaining.
61 | * @deprecated use {@link #withSystemScopeCredentials()}
62 | */
63 | @Deprecated
64 | public SSHUserListBoxModel addSystemScopeCredentials() {
65 | return withSystemScopeCredentials();
66 | }
67 |
68 | /**
69 | * Adds all the system-scoped credentials (they will be filtered with {@link SSHAuthenticator#matcher()}
70 | * implicitly).
71 | *
72 | * These credentials are meant to be used for system configuration and other things scoped to the {@link Jenkins}
73 | * object,
74 | * such as slaves.
75 | *
76 | *
77 | * @return {@code this} for method chaining.
78 | */
79 | public SSHUserListBoxModel withSystemScopeCredentials() {
80 | return withSystemScopeCredentials(Collections.emptyList());
81 | }
82 |
83 | /**
84 | * Adds all the system-scoped credentials (they will be filtered with {@link SSHAuthenticator#matcher()}
85 | * implicitly).
86 | *
87 | * These credentials are meant to be used for system configuration and other things scoped to the {@link Jenkins}
88 | * object,
89 | * such as slaves.
90 | *
91 | *
92 | * @param domainRequirements the domain requirements
93 | * @return {@code this} for method chaining.
94 | */
95 | public SSHUserListBoxModel withSystemScopeCredentials(DomainRequirement... domainRequirements) {
96 | return withSystemScopeCredentials(SSHAuthenticator.matcher(), domainRequirements);
97 | }
98 |
99 | /**
100 | * Adds all the system-scoped credentials.
101 | *
102 | * These credentials are meant to be used for system configuration and other things scoped to the {@link Jenkins}
103 | * object,
104 | * such as slaves.
105 | *
106 | *
107 | * @param matcher a matcher to filter the credentials
108 | * @param domainRequirements the domain requirements
109 | * @return {@code this} for method chaining.
110 | */
111 | public SSHUserListBoxModel withSystemScopeCredentials(CredentialsMatcher matcher,
112 | DomainRequirement... domainRequirements) {
113 | return withSystemScopeCredentials(matcher, Arrays.asList(domainRequirements));
114 | }
115 |
116 | /**
117 | * Adds all the system-scoped credentials (they will be filtered with {@link SSHAuthenticator#matcher()}
118 | * implicitly).
119 | *
120 | * These credentials are meant to be used for system configuration and other things scoped to the {@link Jenkins}
121 | * object,
122 | * such as slaves.
123 | *
124 | *
125 | * @param domainRequirements the domain requirements
126 | * @return {@code this} for method chaining.
127 | */
128 | public SSHUserListBoxModel withSystemScopeCredentials(List domainRequirements) {
129 | return withSystemScopeCredentials(SSHAuthenticator.matcher(), domainRequirements);
130 | }
131 |
132 | /**
133 | * Adds all the system-scoped credentials.
134 | *
135 | * These credentials are meant to be used for system configuration and other things scoped to the {@link Jenkins}
136 | * object,
137 | * such as slaves.
138 | *
139 | *
140 | * @param matcher a matcher to filter the credentials
141 | * @param domainRequirements the domain requirements
142 | * @return {@code this} for method chaining.
143 | */
144 | public SSHUserListBoxModel withSystemScopeCredentials(CredentialsMatcher matcher,
145 | List domainRequirements) {
146 | includeMatchingAs(ACL.SYSTEM, Jenkins.getActiveInstance(), StandardUsernameCredentials.class,
147 | domainRequirements, matcher);
148 | return this;
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/SSHUserPassword.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.cloudbees.jenkins.plugins.sshcredentials;
25 |
26 | import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
27 |
28 | /**
29 | * Details of a SSH user with password.
30 | * @deprecated use {@link StandardUsernamePasswordCredentials}
31 | */
32 | @Deprecated
33 | public interface SSHUserPassword extends SSHUser, StandardUsernamePasswordCredentials {
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/SSHUserPrivateKey.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.cloudbees.jenkins.plugins.sshcredentials;
25 |
26 | import edu.umd.cs.findbugs.annotations.CheckForNull;
27 | import edu.umd.cs.findbugs.annotations.NonNull;
28 | import hudson.util.Secret;
29 | import java.util.List;
30 |
31 | /**
32 | * Details of a SSH user with a private key.
33 | */
34 | public interface SSHUserPrivateKey extends SSHUser {
35 | /**
36 | * Returns the first private key. This should be in OpenSSH format.
37 | *
38 | * @return This is the actual content of the first private key and not the path to the private key.
39 | * @deprecated use {@link #getPrivateKeys()}
40 | */
41 | @Deprecated
42 | @NonNull
43 | default String getPrivateKey() {
44 | List keys = getPrivateKeys();
45 | return keys.isEmpty() ? "" : keys.get(0);
46 | }
47 |
48 | /**
49 | * Gets the passphrase for the private keys or {@code null} if the private keys are not protected by a
50 | * passphase.
51 | *
52 | * @return the passphrase for the private keys or {@code null} if the private key are not protected by
53 | * a passphase.
54 | */
55 | @CheckForNull
56 | Secret getPassphrase();
57 |
58 | /**
59 | * Returns a collection of keys to try in order for authentication.
60 | *
61 | * @return a collection of keys to try in order for authentication.
62 | * @since 0.5
63 | * @see SSHAuthenticator#getPrivateKeys
64 | */
65 | @NonNull
66 | List getPrivateKeys();
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/BaseSSHUser.java:
--------------------------------------------------------------------------------
1 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
2 |
3 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUser;
4 | import com.cloudbees.plugins.credentials.CredentialsScope;
5 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
6 | import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials;
7 | import edu.umd.cs.findbugs.annotations.NonNull;
8 | import edu.umd.cs.findbugs.annotations.Nullable;
9 | import org.apache.commons.lang.StringUtils;
10 | import org.kohsuke.stapler.DataBoundSetter;
11 |
12 | /**
13 | * @author stephenc
14 | * @since 28/02/2012 13:44
15 | */
16 | public class BaseSSHUser extends BaseStandardCredentials implements SSHUser, StandardUsernameCredentials {
17 |
18 | /**
19 | * Ensure consistent serialization.
20 | */
21 | private static final long serialVersionUID = 1L;
22 |
23 | /**
24 | * The username.
25 | */
26 | protected final String username;
27 |
28 | @Nullable
29 | private Boolean usernameSecret = false;
30 |
31 | public BaseSSHUser(CredentialsScope scope, String id, String username, String description) {
32 | super(scope, id, description);
33 | this.username = username;
34 | }
35 |
36 | protected Object readResolve() {
37 | if (usernameSecret == null) {
38 | usernameSecret = true;
39 | }
40 | return this;
41 | }
42 |
43 | /**
44 | * {@inheritDoc}
45 | */
46 | @NonNull
47 | public String getUsername() {
48 | return StringUtils.isEmpty(username) ? System.getProperty("user.name") : username;
49 | }
50 |
51 | @Override
52 | public boolean isUsernameSecret() {
53 | return usernameSecret;
54 | }
55 |
56 | @DataBoundSetter
57 | public void setUsernameSecret(boolean usernameSecret) {
58 | this.usernameSecret = usernameSecret;
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPassword.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
25 |
26 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPassword;
27 | import com.cloudbees.plugins.credentials.CredentialsResolver;
28 | import com.cloudbees.plugins.credentials.CredentialsScope;
29 | import com.cloudbees.plugins.credentials.ResolveWith;
30 | import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
31 | import edu.umd.cs.findbugs.annotations.NonNull;
32 | import hudson.model.Descriptor;
33 | import hudson.util.Secret;
34 |
35 | /**
36 | * A simple username / password for use with SSH connections.
37 | *
38 | * @deprecated use {@link UsernamePasswordCredentialsImpl}
39 | */
40 | @ResolveWith(BasicSSHUserPassword.ResolverImpl.class)
41 | @Deprecated
42 | public class BasicSSHUserPassword extends BaseSSHUser implements SSHUserPassword {
43 |
44 | /**
45 | * Ensure consistent serialization.
46 | */
47 | private static final long serialVersionUID = 1L;
48 |
49 | /**
50 | * The password.
51 | */
52 | private final Secret password;
53 |
54 | /**
55 | * Constructor for stapler.
56 | *
57 | * @param scope the credentials scope
58 | * @param id
59 | * @param username the username.
60 | * @param password the password.
61 | * @param description the description.
62 | */
63 | public BasicSSHUserPassword(CredentialsScope scope, String id, String username, String password,
64 | String description) {
65 | super(scope, id, username, description);
66 | this.password = Secret.fromString(password);
67 | }
68 |
69 | /**
70 | * {@inheritDoc}
71 | */
72 | @NonNull
73 | public Secret getPassword() {
74 | return password;
75 | }
76 |
77 | @Override
78 | protected Object readResolve() {
79 | UsernamePasswordCredentialsImpl resolved;
80 | try {
81 | resolved = new UsernamePasswordCredentialsImpl(getScope(), getId(), getDescription(), getUsername(), getPassword().getEncryptedValue());
82 | } catch (Descriptor.FormException e) {
83 | throw new RuntimeException(e);
84 | }
85 | resolved.setUsernameSecret(true);
86 | return resolved;
87 | }
88 |
89 | /**
90 | * Resolve credentials for legacy code.
91 | *
92 | * @since 0.5
93 | */
94 | public static class ResolverImpl
95 | extends CredentialsResolver {
96 |
97 | /**
98 | * Default constructor.
99 | */
100 | public ResolverImpl() {
101 | super(UsernamePasswordCredentialsImpl.class);
102 | }
103 |
104 | @NonNull
105 | @Override
106 | protected BasicSSHUserPassword doResolve(@NonNull UsernamePasswordCredentialsImpl original) {
107 | return new BasicSSHUserPassword(original.getScope(), original.getId(), original.getUsername(),
108 | original.getPassword().getEncryptedValue(), original.getDescription());
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
25 |
26 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
27 | import com.cloudbees.plugins.credentials.CredentialsScope;
28 | import edu.umd.cs.findbugs.annotations.CheckForNull;
29 | import edu.umd.cs.findbugs.annotations.NonNull;
30 | import hudson.DescriptorExtensionList;
31 | import hudson.Extension;
32 | import hudson.RelativePath;
33 | import hudson.model.AbstractDescribableImpl;
34 | import hudson.model.Descriptor;
35 | import hudson.model.Items;
36 | import hudson.util.FormValidation;
37 | import hudson.util.Secret;
38 | import java.io.File;
39 | import java.io.IOException;
40 | import java.io.Serializable;
41 | import java.security.PrivateKey;
42 | import java.security.UnrecoverableKeyException;
43 | import java.security.interfaces.RSAPrivateKey;
44 | import java.util.ArrayList;
45 | import java.util.Arrays;
46 | import java.util.Collections;
47 | import java.util.List;
48 | import java.util.concurrent.TimeUnit;
49 | import java.util.logging.Level;
50 | import java.util.logging.Logger;
51 | import java.util.stream.Collectors;
52 |
53 | import jenkins.bouncycastle.api.PEMEncodable;
54 | import jenkins.model.Jenkins;
55 | import jenkins.security.FIPS140;
56 | import net.jcip.annotations.GuardedBy;
57 | import org.apache.commons.io.FileUtils;
58 | import org.apache.commons.lang.StringUtils;
59 | import org.kohsuke.stapler.DataBoundConstructor;
60 | import org.kohsuke.stapler.QueryParameter;
61 | import org.kohsuke.stapler.interceptor.RequirePOST;
62 |
63 | /**
64 | * A simple username / password for use with SSH connections.
65 | */
66 | public class BasicSSHUserPrivateKey extends BaseSSHUser implements SSHUserPrivateKey {
67 |
68 | /**
69 | * Ensure consistent serialization.
70 | */
71 | private static final long serialVersionUID = 1L;
72 |
73 | /**
74 | * The password.
75 | */
76 | private final Secret passphrase;
77 |
78 | /**
79 | * The private key. If you care about securing this, use a passphrase.
80 | */
81 | private final PrivateKeySource privateKeySource;
82 |
83 | /**
84 | * The private key.
85 | */
86 | @GuardedBy("this")
87 | private transient List privateKeys;
88 |
89 | /**
90 | * The maximum amount of time to cache the private keys before refreshing.
91 | *
92 | * @since 1.1
93 | */
94 | @GuardedBy("this")
95 | private transient long privateKeysLastModified;
96 |
97 | /**
98 | * Constructor for stapler.
99 | *
100 | * @param scope the credentials scope
101 | * @param username the username.
102 | * @param privateKeySource the private key.
103 | * @param passphrase the password.
104 | * @param description the description.
105 | */
106 | @DataBoundConstructor
107 | public BasicSSHUserPrivateKey(CredentialsScope scope, String id, String username, PrivateKeySource privateKeySource,
108 | String passphrase,
109 | String description) {
110 | super(scope, id, username, description);
111 | this.privateKeySource = privateKeySource == null ? new DirectEntryPrivateKeySource("") : privateKeySource;
112 | this.passphrase = fixEmpty(passphrase == null ? null : Secret.fromString(passphrase));
113 | checkKeyFipsCompliance(this.privateKeySource.getPrivateKeys().isEmpty() ? "" : this.privateKeySource.getPrivateKeys().get(0), this.passphrase);
114 | }
115 |
116 | private static Secret fixEmpty(Secret secret) {
117 | return secret == null ? null : secret.getPlainText().isEmpty() ? null : secret;
118 | }
119 |
120 | @Override
121 | protected synchronized Object readResolve() {
122 | if (privateKeySource == null) {
123 | Secret passphrase = getPassphrase();
124 | if (privateKeys != null) {
125 | return new BasicSSHUserPrivateKey(
126 | getScope(),
127 | getId(),
128 | getUsername(),
129 | new DirectEntryPrivateKeySource(privateKeys),
130 | passphrase == null ? null : passphrase.getEncryptedValue(),
131 | getDescription()
132 | );
133 | }
134 | return new BasicSSHUserPrivateKey(
135 | getScope(),
136 | getId(),
137 | getUsername(),
138 | new DirectEntryPrivateKeySource(""),
139 | passphrase == null ? null : passphrase.getEncryptedValue(),
140 | getDescription()
141 | );
142 | }
143 | checkKeyFipsCompliance(this.privateKeySource.getPrivateKeys().isEmpty() ? "" : privateKeySource.getPrivateKeys().get(0), passphrase);
144 | if (passphrase != null && fixEmpty(passphrase) == null) {
145 | return new BasicSSHUserPrivateKey(
146 | getScope(),
147 | getId(),
148 | getUsername(),
149 | privateKeySource,
150 | null,
151 | getDescription()
152 | );
153 | }
154 | return super.readResolve();
155 | }
156 |
157 | @NonNull
158 | public synchronized List getPrivateKeys() {
159 | if (privateKeySource == null) {
160 | return Collections.emptyList();
161 | }
162 | long lastModified = privateKeySource.getPrivateKeysLastModified();
163 | if (privateKeys == null || privateKeys.isEmpty() || lastModified > privateKeysLastModified) {
164 | this.privateKeys = privateKeySource.getPrivateKeys().stream()
165 | .map(privateKey -> privateKey.endsWith("\n") ? privateKey : privateKey + "\n")
166 | .collect(Collectors.toList());
167 | this.privateKeysLastModified = lastModified;
168 | }
169 | return privateKeys;
170 | }
171 |
172 | @NonNull
173 | public PrivateKeySource getPrivateKeySource() {
174 | return privateKeySource == null ? new DirectEntryPrivateKeySource("") : privateKeySource;
175 | }
176 |
177 | /**
178 | * {@inheritDoc}
179 | */
180 | @CheckForNull
181 | public Secret getPassphrase() {
182 | return passphrase;
183 | }
184 |
185 | /**
186 | * Checks if provided key is compliant with FIPS 140-2.
187 | * OpenSSH keys are not compliant. (the structure that contains the key is not ASN.1.)
188 | * Only Ed25519 or RSA (with a minimum size of 2048) keys are accepted.
189 | * Method will throw an {@link IllegalArgumentException} if key is not compliant.
190 | * @param privateKeySource the keySource
191 | * @param passphrase the secret used with the key (null if no secret provided)
192 | */
193 | private static void checkKeyFipsCompliance(String privateKeySource, Secret passphrase) {
194 | if (!FIPS140.useCompliantAlgorithms()) {
195 | return; // maintain existing behaviour if not in FIPS mode
196 | }
197 | if (StringUtils.isBlank(privateKeySource)) {
198 | return;
199 | }
200 | try {
201 | char[] pass = passphrase == null ? null : passphrase.getPlainText().toCharArray();
202 | if (pass != null && pass.length < 14) {
203 | throw new IllegalArgumentException(Messages.BasicSSHUserPrivateKey_TooShortPassphraseFIPS());
204 | }
205 | PEMEncodable pem = PEMEncodable.decode(privateKeySource, pass);
206 | PrivateKey privateKey = pem.toPrivateKey();
207 | if (privateKey == null) { //somehow malformed key or unknown algorithm
208 | throw new IllegalArgumentException(Messages.BasicSSHUserPrivateKey_UnknownAlgorithmFIPS());
209 | }
210 | if (privateKey instanceof RSAPrivateKey) {
211 | if (((RSAPrivateKey) privateKey).getModulus().bitLength() < 2048) {
212 | throw new IllegalArgumentException(Messages.BasicSSHUserPrivateKey_InvalidKeySizeFIPS());
213 | }
214 | } else if (!"Ed25519".equals(privateKey.getAlgorithm())) {
215 | // TODO: update this to use the JDK17 support when available in the plugin baseline
216 | // Using algorithm name to check elliptic curve, as EdECPrivateKey is not available in jdk11
217 | throw new IllegalArgumentException(Messages.BasicSSHUserPrivateKey_InvalidAlgorithmFIPS(privateKey.getAlgorithm()));
218 | }
219 | } catch (IOException ex) { // OpenSSH keys will raise this, so we need to check if it's a valid openssh key
220 | throw new IllegalArgumentException(Messages.BasicSSHUserPrivateKey_InvalidKeyFormatFIPS(ex.getLocalizedMessage()), ex);
221 | } catch (UnrecoverableKeyException ex) {
222 | String errorMessage = ex.getLocalizedMessage() == null ? "wrong passphrase" : ex.getLocalizedMessage();
223 | throw new IllegalArgumentException(Messages.BasicSSHUserPrivateKey_KeyParseErrorFIPS(errorMessage), ex);
224 | }
225 | }
226 |
227 | /**
228 | * {@inheritDoc}
229 | */
230 | @Extension
231 | public static class DescriptorImpl extends BaseStandardCredentialsDescriptor {
232 |
233 | /**
234 | * {@inheritDoc}
235 | */
236 | @NonNull
237 | @Override
238 | public String getDisplayName() {
239 | return Messages.BasicSSHUserPrivateKey_DisplayName();
240 | }
241 |
242 | public DescriptorExtensionList> getPrivateKeySources() {
243 | return Jenkins.get().getDescriptorList(PrivateKeySource.class);
244 | }
245 |
246 | /**
247 | * {@inheritDoc}
248 | */
249 | public String getIconClassName() {
250 | return "symbol-fingerprint";
251 | }
252 | }
253 |
254 | /**
255 | * A source of private keys
256 | */
257 | public static abstract class PrivateKeySource extends AbstractDescribableImpl {
258 | /**
259 | * Gets the private key from the source
260 | */
261 | @NonNull
262 | public abstract List getPrivateKeys();
263 |
264 | /**
265 | * Returns a revision count or a timestamp (in either case strictly increasing after changes to the private
266 | * keys)
267 | *
268 | * @return a revision count or a timestamp.
269 | * @since 1.4
270 | */
271 | public long getPrivateKeysLastModified() {
272 | return 1; // pick a default that is greater than the field initializer for constant sources.
273 | }
274 |
275 | /**
276 | * Returns {@code true} if and only if the source is self contained.
277 | *
278 | * @return {@code true} if and only if the source is self contained.
279 | * @since 1.7
280 | * @deprecated no more used since FileOnMaster- and Users- PrivateKeySource are deprecated too
281 | */
282 | @Deprecated
283 | public boolean isSnapshotSource() {
284 | return false;
285 | }
286 |
287 | }
288 |
289 | /**
290 | * Descriptor for a {@link PrivateKeySource}
291 | */
292 | public static abstract class PrivateKeySourceDescriptor extends Descriptor {
293 | }
294 |
295 | /**
296 | * Let the user enter the key directly via copy & paste
297 | */
298 | public static class DirectEntryPrivateKeySource extends PrivateKeySource implements Serializable {
299 | /**
300 | * Ensure consistent serialization.
301 | */
302 | private static final long serialVersionUID = 1L;
303 |
304 | private final Secret privateKey;
305 |
306 | public DirectEntryPrivateKeySource(String privateKey) {
307 | this(Secret.fromString(privateKey.endsWith("\n") ? privateKey : privateKey + "\n"));
308 | }
309 |
310 | @DataBoundConstructor
311 | public DirectEntryPrivateKeySource(Secret privateKey) {
312 | this.privateKey = privateKey;
313 | }
314 |
315 | public DirectEntryPrivateKeySource(List privateKeys) {
316 | this(StringUtils.join(privateKeys, "\f"));
317 | }
318 |
319 | /**
320 | * {@inheritDoc}
321 | */
322 | @NonNull
323 | @Override
324 | public List getPrivateKeys() {
325 | String privateKeys = Secret.toString(privateKey);
326 | return StringUtils.isBlank(privateKeys)
327 | ? Collections.emptyList()
328 | : Arrays.asList(StringUtils.split(privateKeys, "\f"));
329 | }
330 |
331 | /**
332 | * Returns the private key.
333 | *
334 | * @return the private key.
335 | */
336 | @SuppressWarnings("unused") // used by Jelly EL
337 | public Secret getPrivateKey() {
338 | return privateKey;
339 | }
340 |
341 | /**
342 | * {@inheritDoc}
343 | */
344 | @Override
345 | public boolean isSnapshotSource() {
346 | return true;
347 | }
348 |
349 | /**
350 | * {@inheritDoc}
351 | */
352 | @Extension
353 | public static class DescriptorImpl extends PrivateKeySourceDescriptor {
354 |
355 | /**
356 | * {@inheritDoc}
357 | */
358 | @NonNull
359 | @Override
360 | public String getDisplayName() {
361 | return Messages.BasicSSHUserPrivateKey_DirectEntryPrivateKeySourceDisplayName();
362 | }
363 |
364 | @RequirePOST
365 | public FormValidation doCheckPrivateKey (@QueryParameter String privateKey,
366 | @RelativePath("..") @QueryParameter String passphrase) {
367 | try {
368 | checkKeyFipsCompliance(privateKey, Secret.fromString(passphrase));
369 | return FormValidation.ok();
370 | } catch (IllegalArgumentException ex) {
371 | return FormValidation.error(ex, ex.getMessage());
372 | }
373 |
374 | }
375 | }
376 | }
377 |
378 | /**
379 | * Let the user reference a file on the disk.
380 | * @deprecated This approach has security vulnerability and should be migrated to {@link DirectEntryPrivateKeySource}
381 | */
382 | @Deprecated
383 | public static class FileOnMasterPrivateKeySource extends PrivateKeySource {
384 |
385 | /**
386 | * Our logger
387 | */
388 | private static final Logger LOGGER = Logger.getLogger(FileOnMasterPrivateKeySource.class.getName());
389 |
390 | /**
391 | * The path to the private key.
392 | */
393 | private final String privateKeyFile;
394 |
395 | /**
396 | * When any of the key files was last modified.
397 | */
398 | private transient volatile long lastModified;
399 |
400 | /**
401 | * When we will next try a refresh of the status.
402 | */
403 | private transient volatile long nextCheckLastModified;
404 |
405 | public FileOnMasterPrivateKeySource(String privateKeyFile) {
406 | this.privateKeyFile = privateKeyFile;
407 | }
408 |
409 | /**
410 | * {@inheritDoc}
411 | */
412 | @NonNull
413 | @Override
414 | public List getPrivateKeys() {
415 | if (privateKeyFile != null) {
416 | File key = new File(privateKeyFile);
417 | if (key.isFile()) {
418 | try {
419 | return Collections.singletonList(FileUtils.readFileToString(key));
420 | } catch (IOException e) {
421 | LOGGER.log(Level.WARNING, "Could not read private key file " + privateKeyFile, e);
422 | }
423 | }
424 | }
425 | return Collections.emptyList();
426 | }
427 |
428 | /**
429 | * Returns the private key file name.
430 | *
431 | * @return the private key file name.
432 | */
433 | public String getPrivateKeyFile() {
434 | return privateKeyFile;
435 | }
436 |
437 | private Object readResolve() {
438 | if (privateKeyFile != null
439 | && privateKeyFile.startsWith("---")
440 | && privateKeyFile.contains("---BEGIN")
441 | && privateKeyFile.contains("---END")) {
442 | // this is a borked upgrade, not actually the file name but is actually the key contents
443 | return new DirectEntryPrivateKeySource(privateKeyFile);
444 | }
445 |
446 | Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS);
447 |
448 | LOGGER.log(Level.INFO, "SECURITY-440: Migrating FileOnMasterPrivateKeySource to DirectEntryPrivateKeySource");
449 | // read the content of the file and then migrate to Direct
450 | return new DirectEntryPrivateKeySource(getPrivateKeys());
451 | }
452 |
453 | @Override
454 | public long getPrivateKeysLastModified() {
455 | if (nextCheckLastModified > System.currentTimeMillis() || lastModified < 0) {
456 | lastModified = Long.MIN_VALUE;
457 | if (privateKeyFile != null) {
458 | File file = new File(privateKeyFile);
459 | if (file.exists()) {
460 | lastModified = file.lastModified();
461 | }
462 | }
463 | nextCheckLastModified = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30);
464 | }
465 | return lastModified;
466 | }
467 | }
468 |
469 | /**
470 | * Let the user
471 | * @deprecated This approach has security vulnerability and should be migrated to {@link DirectEntryPrivateKeySource}
472 | */
473 | @Deprecated
474 | public static class UsersPrivateKeySource extends PrivateKeySource {
475 |
476 | /**
477 | * Our logger
478 | */
479 | private static final Logger LOGGER = Logger.getLogger(UsersPrivateKeySource.class.getName());
480 |
481 | /**
482 | * When any of the key files was last modified.
483 | */
484 | private transient volatile long lastModified;
485 |
486 | /**
487 | * When we will next try a refresh of the status.
488 | */
489 | private transient volatile long nextCheckLastModified;
490 |
491 | private List files() {
492 | List files = new ArrayList<>();
493 | File sshHome = new File(new File(System.getProperty("user.home")), ".ssh");
494 | for (String keyName : Arrays.asList("id_ecdsa", "id_ed25519", "id_rsa", "id_dsa", "identity")) {
495 | File key = new File(sshHome, keyName);
496 | if (key.isFile()) {
497 | files.add(key);
498 | }
499 | }
500 | return files;
501 | }
502 |
503 | /**
504 | * {@inheritDoc}
505 | */
506 | @NonNull
507 | @Override
508 | public List getPrivateKeys() {
509 | List keys = new ArrayList<>();
510 | for (File file : files()) {
511 | try {
512 | keys.add(FileUtils.readFileToString(file));
513 | } catch (IOException e) {
514 | LOGGER.log(Level.WARNING, "Could not read private key", e);
515 | }
516 | }
517 | return keys;
518 | }
519 |
520 | @Override
521 | public long getPrivateKeysLastModified() {
522 | if (nextCheckLastModified > System.currentTimeMillis() || lastModified < 0) {
523 | lastModified = Long.MIN_VALUE;
524 | for (File file : files()) {
525 | lastModified = Math.max(lastModified, file.lastModified());
526 | }
527 | nextCheckLastModified = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30);
528 | }
529 | return lastModified;
530 | }
531 |
532 | private Object readResolve() {
533 | Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS);
534 |
535 | LOGGER.log(Level.INFO, "SECURITY-440: Migrating UsersPrivateKeySource to DirectEntryPrivateKeySource");
536 | // read the content of the file and then migrate to Direct
537 | return new DirectEntryPrivateKeySource(getPrivateKeys());
538 | }
539 | }
540 |
541 | static {
542 | // the critical field allow the permission check to make the XML read to fail completely in case of violation
543 | Items.XSTREAM2.addCriticalField(BasicSSHUserPrivateKey.class, "privateKeySource");
544 | }
545 | }
546 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/SSHUserPrivateKeySnapshotTaker.java:
--------------------------------------------------------------------------------
1 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
2 |
3 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
4 | import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey.DirectEntryPrivateKeySource;
5 | import com.cloudbees.plugins.credentials.CredentialsSnapshotTaker;
6 |
7 | import hudson.Extension;
8 | import hudson.util.Secret;
9 |
10 | @Extension
11 | public class SSHUserPrivateKeySnapshotTaker extends CredentialsSnapshotTaker {
12 | /**
13 | * {@inheritDoc}
14 | */
15 | @Override
16 | public Class type() {
17 | return SSHUserPrivateKey.class;
18 | }
19 |
20 | /**
21 | * {@inheritDoc}
22 | */
23 | @Override
24 | public SSHUserPrivateKey snapshot(SSHUserPrivateKey credentials) {
25 | if (credentials instanceof BasicSSHUserPrivateKey && ((BasicSSHUserPrivateKey) credentials).getPrivateKeySource() instanceof DirectEntryPrivateKeySource) {
26 | return credentials;
27 | }
28 | final Secret passphrase = credentials.getPassphrase();
29 | return new BasicSSHUserPrivateKey(credentials.getScope(), credentials.getId(), credentials.getUsername(),
30 | new DirectEntryPrivateKeySource(credentials.getPrivateKeys()),
31 | passphrase == null ? null : passphrase.getEncryptedValue(), credentials.getDescription());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPasswordAuthenticator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
25 |
26 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator;
27 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticatorFactory;
28 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
29 | import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
30 | import com.trilead.ssh2.Connection;
31 | import edu.umd.cs.findbugs.annotations.CheckForNull;
32 | import edu.umd.cs.findbugs.annotations.NonNull;
33 | import edu.umd.cs.findbugs.annotations.Nullable;
34 | import org.jenkinsci.plugins.variant.OptionalExtension;
35 |
36 | import java.io.IOException;
37 | import java.util.Arrays;
38 | import java.util.List;
39 | import java.util.Locale;
40 | import java.util.logging.Logger;
41 |
42 | /**
43 | * Does password auth with a {@link Connection}.
44 | */
45 | public class TrileadSSHPasswordAuthenticator extends SSHAuthenticator {
46 |
47 | /**
48 | * Our logger
49 | */
50 | private static final Logger LOGGER = Logger.getLogger(TrileadSSHPasswordAuthenticator.class.getName());
51 | private static final String PASSWORD = "password";
52 | private static final String KEYBOARD_INTERACTIVE = "keyboard-interactive";
53 |
54 | /**
55 | * Constructor.
56 | *
57 | * @param connection the connection we will be authenticating.
58 | * @deprecated
59 | */
60 | @Deprecated
61 | public TrileadSSHPasswordAuthenticator(Connection connection, StandardUsernamePasswordCredentials user) {
62 | this(connection, user, null);
63 | }
64 |
65 | /**
66 | * Constructor.
67 | *
68 | * @param connection the connection we will be authenticating.
69 | * @since 1.4
70 | */
71 | public TrileadSSHPasswordAuthenticator(@NonNull Connection connection,
72 | @NonNull StandardUsernamePasswordCredentials user,
73 | @CheckForNull String username) {
74 | super(connection, user, username);
75 | }
76 |
77 | /**
78 | * {@inheritDoc}
79 | */
80 | @Override
81 | public boolean canAuthenticate() {
82 | try {
83 | for (String authMethod : getConnection().getRemainingAuthMethods(getUsername())) {
84 | if (PASSWORD.equals(authMethod)) {
85 | // prefer password
86 | return true;
87 | }
88 | if (KEYBOARD_INTERACTIVE.equals(authMethod)) {
89 | return true;
90 | }
91 | }
92 | } catch (IOException e) {
93 | e.printStackTrace(getListener().error("Failed to authenticate"));
94 | }
95 | return false;
96 | }
97 |
98 | /**
99 | * {@inheritDoc}
100 | */
101 | @Override
102 | protected boolean doAuthenticate() {
103 | final StandardUsernamePasswordCredentials user = getUser();
104 | final String username = getUsername();
105 |
106 | try {
107 | final Connection connection = getConnection();
108 | final String password = user.getPassword().getPlainText();
109 | boolean tried = false;
110 |
111 | List availableMethods = Arrays.asList(connection.getRemainingAuthMethods(username));
112 | if (availableMethods.contains(PASSWORD)) {
113 | // prefer password
114 | if (connection.authenticateWithPassword(username, password)) {
115 | LOGGER.fine("Authentication with 'password' succeeded.");
116 | return true;
117 | }
118 | getListener().error("Failed to authenticate as %s. Wrong password. (credentialId:%s/method:password)",
119 | username, user.getId());
120 | tried = true;
121 | }
122 | if (availableMethods.contains(KEYBOARD_INTERACTIVE)) {
123 | if (connection.authenticateWithKeyboardInteractive(username, (name, instruction, numPrompts, prompt, echo) -> {
124 | // most SSH servers just use keyboard interactive to prompt for the password
125 | // match "assword" is safer than "password"... you don't *want* to know why!
126 | return prompt != null && prompt.length > 0 && prompt[0].toLowerCase(Locale.ENGLISH)
127 | .contains("assword")
128 | ? new String[]{password}
129 | : new String[0];
130 | })) {
131 | LOGGER.fine("Authentication with 'keyboard-interactive' succeeded.");
132 | return true;
133 | }
134 | getListener()
135 | .error("Failed to authenticate as %s. Wrong password. "
136 | + "(credentialId:%s/method:keyboard-interactive)",
137 | username, user.getId());
138 | tried = true;
139 | }
140 |
141 | if (!tried) {
142 | getListener().error("The server does not allow password authentication. Available options are %s",
143 | availableMethods);
144 | }
145 | } catch (IOException e) {
146 | e.printStackTrace(getListener()
147 | .error("Unexpected error while trying to authenticate as %s with credential=%s", username,
148 | user.getId()));
149 | }
150 | return false;
151 | }
152 |
153 | /**
154 | * {@inheritDoc}
155 | */
156 | @OptionalExtension(requirePlugins = {"trilead-api"})
157 | public static class Factory extends SSHAuthenticatorFactory {
158 |
159 | /**
160 | * {@inheritDoc}
161 | */
162 | @Override
163 | protected SSHAuthenticator newInstance(@NonNull C connection,
164 | @NonNull U user) {
165 | return newInstance(connection, user, null);
166 | }
167 |
168 | /**
169 | * {@inheritDoc}
170 | */
171 | @Nullable
172 | @Override
173 | @SuppressWarnings("unchecked")
174 | protected SSHAuthenticator newInstance(@NonNull C connection,
175 | @NonNull U user,
176 | @CheckForNull String
177 | username) {
178 | if (supports(connection.getClass(), user.getClass())) {
179 | return (SSHAuthenticator) new TrileadSSHPasswordAuthenticator((Connection) connection,
180 | (StandardUsernamePasswordCredentials) user, username);
181 | }
182 | return null;
183 | }
184 |
185 | /**
186 | * {@inheritDoc}
187 | */
188 | @Override
189 | protected boolean supports(@NonNull Class connectionClass,
190 | @NonNull Class userClass) {
191 | return Connection.class.isAssignableFrom(connectionClass)
192 | && StandardUsernamePasswordCredentials.class.isAssignableFrom(userClass);
193 | }
194 |
195 | private static final long serialVersionUID = 1L;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPublicKeyAuthenticator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
25 |
26 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator;
27 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticatorFactory;
28 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
29 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
30 | import com.trilead.ssh2.Connection;
31 | import edu.umd.cs.findbugs.annotations.CheckForNull;
32 | import edu.umd.cs.findbugs.annotations.NonNull;
33 | import edu.umd.cs.findbugs.annotations.Nullable;
34 | import hudson.util.Secret;
35 | import org.jenkinsci.plugins.variant.OptionalExtension;
36 |
37 | import java.io.IOException;
38 | import java.util.ArrayList;
39 | import java.util.Arrays;
40 | import java.util.Collection;
41 | import java.util.List;
42 | import java.util.logging.Logger;
43 |
44 | /**
45 | * Does public key auth with a {@link Connection}.
46 | */
47 | public class TrileadSSHPublicKeyAuthenticator extends SSHAuthenticator {
48 |
49 | /**
50 | * Our logger.
51 | */
52 | private static final Logger LOGGER = Logger.getLogger(TrileadSSHPublicKeyAuthenticator.class.getName());
53 | private static final String PUBLICKEY = "publickey";
54 |
55 | /**
56 | * Constructor.
57 | *
58 | * @param connection the connection we will be authenticating.
59 | */
60 | public TrileadSSHPublicKeyAuthenticator(Connection connection, SSHUserPrivateKey user) {
61 | this(connection, user, null);
62 | }
63 |
64 | /**
65 | * Constructor.
66 | *
67 | * @param connection the connection we will be authenticating.
68 | */
69 | public TrileadSSHPublicKeyAuthenticator(@NonNull Connection connection,
70 | @NonNull SSHUserPrivateKey user,
71 | @CheckForNull String username) {
72 | super(connection, user, username);
73 | }
74 |
75 | /**
76 | * {@inheritDoc}
77 | */
78 | @Override
79 | public boolean canAuthenticate() {
80 | try {
81 | return getRemainingAuthMethods().contains(PUBLICKEY);
82 | } catch (IOException e) {
83 | e.printStackTrace(getListener().error("Failed to authenticate"));
84 | return false;
85 | }
86 | }
87 |
88 | private List getRemainingAuthMethods() throws IOException {
89 | return Arrays.asList(getConnection().getRemainingAuthMethods(getUsername()));
90 | }
91 |
92 | /**
93 | * {@inheritDoc}
94 | */
95 | @Override
96 | protected boolean doAuthenticate() {
97 | final SSHUserPrivateKey user = getUser();
98 | final String username = getUsername();
99 | try {
100 | final Connection connection = getConnection();
101 | final Secret userPassphrase = user.getPassphrase();
102 | final String passphrase = userPassphrase == null ? null : userPassphrase.getPlainText();
103 |
104 | Collection availableMethods = getRemainingAuthMethods();
105 | if (availableMethods.contains(PUBLICKEY)) {
106 | int count = 0;
107 | List ioe = new ArrayList<>();
108 | for (String privateKey : getPrivateKeys(user)) {
109 | try {
110 | if (connection.authenticateWithPublicKey(username, privateKey.toCharArray(), passphrase)) {
111 | LOGGER.fine("Authentication with 'publickey' succeeded.");
112 | return true;
113 | }
114 | } catch (IOException e) {
115 | ioe.add(e);
116 | }
117 | count++;
118 | getListener()
119 | .error("Server rejected the %d private key(s) for %s (credentialId:%s/method:publickey)",
120 | count, username, user.getId());
121 | }
122 | for (IOException e : ioe) {
123 | e.printStackTrace(getListener()
124 | .error("Failed to authenticate as %s with credential=%s", username, getUser().getId()));
125 | }
126 | return false;
127 | } else {
128 | getListener().error("The server does not allow public key authentication. Available options are %s",
129 | availableMethods);
130 | return false;
131 | }
132 | } catch (IOException e) {
133 | e.printStackTrace(getListener()
134 | .error("Failed to authenticate as %s with credential=%s", username, getUser().getId()));
135 | return false;
136 | }
137 | }
138 |
139 | /**
140 | * {@inheritDoc}
141 | */
142 | @OptionalExtension(requirePlugins = {"trilead-api"})
143 | public static class Factory extends SSHAuthenticatorFactory {
144 |
145 | /**
146 | * {@inheritDoc}
147 | */
148 | @Override
149 | @SuppressWarnings("unchecked")
150 | protected SSHAuthenticator newInstance(@NonNull C connection,
151 | @NonNull U user) {
152 | return newInstance(connection, user, null);
153 | }
154 |
155 | /**
156 | * {@inheritDoc}
157 | */
158 | @Nullable
159 | @Override
160 | @SuppressWarnings("unchecked")
161 | protected SSHAuthenticator newInstance(@NonNull C connection,
162 | @NonNull U user,
163 | @CheckForNull String
164 | username) {
165 | if (supports(connection.getClass(), user.getClass())) {
166 | return (SSHAuthenticator) new TrileadSSHPublicKeyAuthenticator((Connection) connection,
167 | (SSHUserPrivateKey) user, username);
168 | }
169 | return null;
170 | }
171 |
172 | /**
173 | * {@inheritDoc}
174 | */
175 | @Override
176 | protected boolean supports(@NonNull Class connectionClass,
177 | @NonNull Class userClass) {
178 | return Connection.class.isAssignableFrom(connectionClass)
179 | && SSHUserPrivateKey.class.isAssignableFrom(userClass);
180 | }
181 |
182 | private static final long serialVersionUID = 1L;
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/Messages.properties:
--------------------------------------------------------------------------------
1 | #
2 | # The MIT License
3 | #
4 | # Copyright (c) 2011-2013, CloudBees, Inc., Stephen Connolly.
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in
14 | # all copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | # THE SOFTWARE.
23 | #
24 | SSHUserListBoxModel.EmptySelection=- none -
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey/DirectEntryPrivateKeySource/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey/DirectEntryPrivateKeySource/config_de.properties:
--------------------------------------------------------------------------------
1 | #
2 | # The MIT License
3 | #
4 | # Copyright (c) 2013 Harald Albers
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in
14 | # all copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | # THE SOFTWARE.
23 |
24 | Key=Schl\u00FCssel
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey/DirectEntryPrivateKeySource/config_ja.properties:
--------------------------------------------------------------------------------
1 | # The MIT License
2 | #
3 | # Copyright (c) 2013 Seiji Sogabe.
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | # THE SOFTWARE.
22 |
23 | Key=\u9375
24 |
25 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey/credentials.jelly:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey/credentials_de.properties:
--------------------------------------------------------------------------------
1 | #
2 | # The MIT License
3 | #
4 | # Copyright (c) 2013 Harald Albers
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in
14 | # all copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | # THE SOFTWARE.
23 |
24 | Username=Benutzername
25 | Private\ Key=Privater Schl\u00fcssel
26 | Passphrase=Passwort/Passphrase
27 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey/credentials_ja.properties:
--------------------------------------------------------------------------------
1 | # The MIT License
2 | #
3 | # Copyright (c) 2013 Seiji Sogabe.
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | # THE SOFTWARE.
22 |
23 | Username=\u30e6\u30fc\u30b6\u30fc\u540d
24 | Private\ Key=\u79d8\u5bc6\u9375
25 | Passphrase=\u30d1\u30b9\u30d5\u30ec\u30fc\u30ba
26 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey/help-usernameSecret.html:
--------------------------------------------------------------------------------
1 |
2 | By default, usernames are not masked in the build log.
3 | You may choose to mask a credential's username if you consider it to be sensitive information.
4 | However, this can interfere with diagnostics.
5 | For example, if the username is a common word it will cause unrelated occurrences of that word to also be masked.
6 |
7 | Regardless of this setting, the username will be displayed in the selection dropdown to anyone permitted to reconfigure the credentials.
8 |
9 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey/passphraseChangeEvent.js:
--------------------------------------------------------------------------------
1 | //TODO: this snippet, as well as ids in passphrase and private key fields can be removed once https://issues.jenkins.io/browse/JENKINS-65616 is completed
2 | const passphraseElements = document.getElementsByClassName('sshCredentials_passphrase');
3 |
4 | if (passphraseElements.length > 0) {
5 | // Failsafe in case there's more than 1 element we'll only use the first one. Should not happen.
6 | passphraseElements[0].addEventListener("change", event => {
7 | var newEvent = new Event("change", {"bubbles": true})
8 | const privateKeyElements = document.getElementsByName('_.privateKey');
9 | if (privateKeyElements.length > 0) {
10 | privateKeyElements[0].dispatchEvent(newEvent)
11 | }
12 | })
13 | }
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/Messages.properties:
--------------------------------------------------------------------------------
1 | #
2 | # The MIT License
3 | #
4 | # Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in
14 | # all copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | # THE SOFTWARE.
23 | #
24 | BasicSSHUserPrivateKey.DirectEntryPrivateKeySourceDisplayName=Enter directly
25 | BasicSSHUserPrivateKey.DisplayName=SSH Username with private key
26 | BasicSSHUserPrivateKey.UnknownAlgorithmFIPS=Private key can not be obtained from provided data.
27 | BasicSSHUserPrivateKey.InvalidKeySizeFIPS=Key size below 2048 for RSA keys is not accepted in FIPS mode.
28 | BasicSSHUserPrivateKey.InvalidAlgorithmFIPS=Key algorithm {0} is not accepted in FIPS mode.
29 | BasicSSHUserPrivateKey.InvalidKeyFormatFIPS=Provided data can not be parsed: {0}.
30 | BasicSSHUserPrivateKey.KeyParseErrorFIPS=Can not parse key: {0}
31 | BasicSSHUserPrivateKey.TooShortPassphraseFIPS=Password is too short (< 14 characters).
32 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/Messages_de.properties:
--------------------------------------------------------------------------------
1 | #
2 | # The MIT License
3 | #
4 | # Copyright (c) 2013 Harald Albers
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in
14 | # all copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | # THE SOFTWARE.
23 | #
24 | BasicSSHUserPrivateKey.DirectEntryPrivateKeySourceDisplayName=Direkt eingeben
25 | BasicSSHUserPrivateKey.DisplayName=SSH Benutzername und privater Schl\u00fcssel
26 |
--------------------------------------------------------------------------------
/src/main/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/Messages_ja.properties:
--------------------------------------------------------------------------------
1 | #
2 | # The MIT License
3 | #
4 | # Copyright (c) 2011-2013, CloudBees, Inc., Stephen Connolly, Seiji Sogabe
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in
14 | # all copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | # THE SOFTWARE.
23 | #
24 | BasicSSHUserPrivateKey.DirectEntryPrivateKeySourceDisplayName=\u76f4\u63a5\u5165\u529b
25 | BasicSSHUserPrivateKey.DisplayName=SSH \u30e6\u30fc\u30b6\u30fc\u540d\u3068\u79d8\u5bc6\u9375
26 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | Allows storage of SSH credentials in Jenkins
4 |
5 |
--------------------------------------------------------------------------------
/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest.java:
--------------------------------------------------------------------------------
1 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
2 |
3 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
4 | import com.cloudbees.plugins.credentials.CredentialsMatchers;
5 | import com.cloudbees.plugins.credentials.CredentialsProvider;
6 | import com.cloudbees.plugins.credentials.CredentialsScope;
7 | import com.cloudbees.plugins.credentials.CredentialsStore;
8 | import com.cloudbees.plugins.credentials.domains.Domain;
9 | import hudson.ExtensionList;
10 | import hudson.security.ACL;
11 | import hudson.util.FormValidation;
12 | import org.apache.commons.io.FileUtils;
13 | import org.apache.commons.lang.StringUtils;
14 | import org.junit.Rule;
15 | import org.junit.Test;
16 | import org.jvnet.hudson.test.Issue;
17 | import org.jvnet.hudson.test.JenkinsRule;
18 | import org.jvnet.hudson.test.RealJenkinsRule;
19 | import org.jvnet.hudson.test.recipes.LocalData;
20 |
21 | import java.io.IOException;
22 | import java.nio.charset.Charset;
23 | import java.nio.file.Paths;
24 | import java.util.Iterator;
25 |
26 | import static org.junit.Assert.*;
27 |
28 | public class BasicSSHUserPrivateKeyFIPSTest {
29 |
30 | @Rule public RealJenkinsRule rule = new RealJenkinsRule().omitPlugins("eddsa-api", "trilead-api")
31 | .javaOptions("-Djenkins.security.FIPS140.COMPLIANCE=true");
32 |
33 | @Test
34 | @Issue("JENKINS-73408")
35 | public void nonCompliantKeysLaunchExceptionTest() throws Throwable {
36 | rule.then(BasicSSHUserPrivateKeyFIPSTest::checkNonCompliantKeysLaunchException);
37 | }
38 |
39 | private static void checkNonCompliantKeysLaunchException(JenkinsRule r) throws IOException{
40 | new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "no-key", "user",
41 | null, null, "no key provided doesn't throw exceptions");
42 | assertThrows(IllegalArgumentException.class, () -> new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "nopass-openssh-ed25519", "user",
43 | getKey("openssh-ed25519-nopass"), null, "openssh ED25519 with no encryption is not compliant"));
44 | assertThrows(IllegalArgumentException.class, () -> new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "rsa1024", "user",
45 | getKey("rsa1024"), "fipsvalidpassword", "Invalid size key"));
46 | assertThrows(IllegalArgumentException.class, () -> new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "openssh-rsa1024", "user",
47 | getKey("openssh-rsa1024"), "fipsvalidpassword", "Invalid key format"));
48 | new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "ed25519", "user",
49 | getKey("ed25519"), "fipsvalidpassword", "Elliptic curve accepted key");
50 | new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "rsa2048", "user",
51 | getKey("rsa2048"), "fipsvalidpassword", "RSA 2048 accepted key");
52 | assertThrows(IllegalArgumentException.class, () -> new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "rsa1024-short-pass", "user",
53 | getKey("unencrypted-rsa1024"), "password", "password shorter than 14 chatacters is invalid"));
54 | new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "unencrypted-rsa2048", "user",
55 | getKey("unencrypted-rsa2048"), null, "RSA 2048 with no encryption is valid");
56 | assertThrows(IllegalArgumentException.class, () -> new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "rsa2048-wrong-pass", "user",
57 | getKey("rsa2048"), "NOT-fipsvalidpassword", "Wrong password avoids getting size or algorithm"));
58 | assertThrows(IllegalArgumentException.class, () -> new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "dsa2048", "user",
59 | getKey("dsa2048"), "fipsvalidpassword", "DSA is not accepted"));
60 | assertThrows(IllegalArgumentException.class, () -> new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "not-a-key", "user",
61 | getKey("not-a-key"), "fipsvalidpassword", "Provided data is not a key"));
62 | }
63 |
64 | @Test
65 | @Issue("JENKINS-73408")
66 | public void invalidKeyIsNotSavedInFIPSModeTest() throws Throwable {
67 | rule.then(BasicSSHUserPrivateKeyFIPSTest::checkInvalidKeyIsNotSavedInFIPSMode);
68 | }
69 |
70 | private static void checkInvalidKeyIsNotSavedInFIPSMode(JenkinsRule r) throws IOException {
71 | BasicSSHUserPrivateKey entry = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "rsa2048", "user", getKey("rsa2048"), "fipsvalidpassword", "RSA 1024 accepted key");
72 | Iterator stores = CredentialsProvider.lookupStores(r.jenkins).iterator();
73 | assertTrue(stores.hasNext());
74 | CredentialsStore store = stores.next();
75 | store.addCredentials(Domain.global(), entry);
76 | store.save();
77 | // Valid key is saved
78 | SSHUserPrivateKey cred = CredentialsMatchers.firstOrNull(
79 | CredentialsProvider.lookupCredentialsInItem(SSHUserPrivateKey.class, null, ACL.SYSTEM2),
80 | CredentialsMatchers.withId("rsa2048"));
81 | assertNotNull(cred);
82 | assertThrows(IllegalArgumentException.class, () -> store.addCredentials(Domain.global(),
83 | new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "rsa1024", "user", getKey("rsa1024"), "fipsvalidpassword", "Invalid size key")));
84 | store.save();
85 | // Invalid key threw an exception, so it wasn't saved
86 | cred = CredentialsMatchers.firstOrNull(
87 | CredentialsProvider.lookupCredentialsInItem(SSHUserPrivateKey.class, null, ACL.SYSTEM2),
88 | CredentialsMatchers.withId("rsa1024"));
89 | assertNull(cred);
90 | }
91 | @Test
92 | @LocalData
93 | @Issue("JENKINS-73408")
94 | public void invalidKeysAreRemovedOnStartupTest() throws Throwable {
95 | rule.then(BasicSSHUserPrivateKeyFIPSTest::checkInvalidKeysAreRemovedOnStartup);
96 | }
97 |
98 | private static void checkInvalidKeysAreRemovedOnStartup(JenkinsRule r) {
99 | SSHUserPrivateKey cred = CredentialsMatchers.firstOrNull(
100 | CredentialsProvider.lookupCredentialsInItem(SSHUserPrivateKey.class, null, ACL.SYSTEM2),
101 | CredentialsMatchers.withId("valid-rsa-key"));
102 | assertNotNull(cred);
103 | cred = CredentialsMatchers.firstOrNull(
104 | CredentialsProvider.lookupCredentialsInItem(SSHUserPrivateKey.class, null, ACL.SYSTEM2),
105 | CredentialsMatchers.withId("invalid-rsa-key"));
106 | assertNull(cred);
107 | }
108 |
109 | @Test
110 | @Issue("JENKINS-73408")
111 | public void formValidationTest() throws Throwable {
112 | rule.then(BasicSSHUserPrivateKeyFIPSTest::checkFormValidation);
113 | }
114 |
115 | private static void checkFormValidation(JenkinsRule r) throws IOException {
116 | BasicSSHUserPrivateKey.DirectEntryPrivateKeySource.DescriptorImpl descriptor = ExtensionList.lookupSingleton(BasicSSHUserPrivateKey.DirectEntryPrivateKeySource.DescriptorImpl.class);
117 | FormValidation result = descriptor.doCheckPrivateKey(getKey("rsa2048").getPrivateKey().getPlainText(), "fipsvalidpassword");
118 | assertTrue(StringUtils.isBlank(result.getMessage()));
119 | result = descriptor.doCheckPrivateKey(getKey("rsa1024").getPrivateKey().getPlainText(), "fipsvalidpassword");
120 | assertTrue(StringUtils.isNotBlank(result.getMessage()));
121 | }
122 |
123 | private static BasicSSHUserPrivateKey.DirectEntryPrivateKeySource getKey(String file) throws IOException {
124 | String keyText = FileUtils.readFileToString(Paths.get("src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest").resolve(file).toFile(), Charset.defaultCharset());
125 | return new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(keyText);
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2014 Jesse Glick.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
26 |
27 | import com.cloudbees.hudson.plugins.folder.Folder;
28 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
29 | import com.cloudbees.plugins.credentials.CredentialsMatchers;
30 | import com.cloudbees.plugins.credentials.CredentialsProvider;
31 | import com.cloudbees.plugins.credentials.domains.DomainRequirement;
32 | import java.util.List;
33 | import hudson.FilePath;
34 | import hudson.cli.CLICommandInvoker;
35 | import hudson.cli.UpdateJobCommand;
36 | import hudson.model.Hudson;
37 | import hudson.model.Job;
38 | import hudson.security.ACL;
39 | import jenkins.model.Jenkins;
40 |
41 | import org.junit.Test;
42 |
43 | import static hudson.cli.CLICommandInvoker.Matcher.failedWith;
44 | import static hudson.cli.CLICommandInvoker.Matcher.succeeded;
45 | import static org.hamcrest.MatcherAssert.assertThat;
46 | import static org.hamcrest.Matchers.containsString;
47 | import static org.hamcrest.Matchers.not;
48 | import static org.junit.Assert.*;
49 | import org.junit.Rule;
50 | import org.jvnet.hudson.test.Issue;
51 | import org.jvnet.hudson.test.JenkinsRule;
52 | import org.jvnet.hudson.test.recipes.LocalData;
53 |
54 | public class BasicSSHUserPrivateKeyTest {
55 |
56 | final static String TESTKEY_ID = "bc07f814-78bd-4b29-93d4-d25b93285f93";
57 | final static String TESTKEY_BEGIN = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAu1r+HHzmpybc4iwoP5+44FjvcaMkNEWeGQZlmPwLx70XW8+8";
58 | final static String TESTKEY_END = "sroT/IHW2jKMD0v8kKLUnKCZYzlw0By7+RvJ8lgzHB0D71f6EC1UWg==\n-----END RSA PRIVATE KEY-----\n";
59 |
60 | @Rule public JenkinsRule r = new JenkinsRule();
61 |
62 | @LocalData
63 | @Test
64 | public void readOldCredentials() {
65 | SSHUserPrivateKey supk = CredentialsMatchers.firstOrNull(
66 | CredentialsProvider.lookupCredentials(SSHUserPrivateKey.class, Hudson.get(), ACL.SYSTEM,
67 | (List)null),
68 | CredentialsMatchers.withId(TESTKEY_ID));
69 | assertNotNull(supk);
70 | List keyList = supk.getPrivateKeys();
71 | assertNotNull(keyList);
72 | assertEquals(keyList.size(), 1);
73 | String privateKey = keyList.get(0);
74 | assertNotNull(privateKey);
75 | assertTrue(privateKey.startsWith(TESTKEY_BEGIN));
76 | assertTrue(privateKey.endsWith(TESTKEY_END));
77 | }
78 |
79 | @Test
80 | public void ensureDirectEntryHasTrailingNewline() {
81 | String key = (new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource("test")).getPrivateKey().getPlainText();
82 | assertEquals("test\n", key);
83 | }
84 |
85 | // TODO demonstrate that all private key sources are round-tripped in XStream
86 |
87 | @Test
88 | @LocalData
89 | @Issue("SECURITY-440")
90 | public void userWithoutRunScripts_cannotMigrateDangerousPrivateKeySource() throws Exception {
91 | Folder folder = r.jenkins.createProject(Folder.class, "folder1");
92 |
93 | FilePath updateFolder = r.jenkins.getRootPath().child("update_folder.xml");
94 |
95 | { // as user with just configure, you cannot migrate
96 | CLICommandInvoker.Result result = new CLICommandInvoker(r, new UpdateJobCommand())
97 | .authorizedTo(Jenkins.READ, Job.READ, Job.CONFIGURE)
98 | .withStdin(updateFolder.read())
99 | .invokeWithArgs("folder1");
100 |
101 | assertThat(result.stderr(), containsString("user is missing the Overall/RunScripts permission"));
102 | assertThat(result, failedWith(1));
103 |
104 | // config file not touched
105 | String configFileContent = folder.getConfigFile().asString();
106 | assertThat(configFileContent, not(containsString("FileOnMasterPrivateKeySource")));
107 | assertThat(configFileContent, not(containsString("BasicSSHUserPrivateKey")));
108 | }
109 | { // but as admin with RUN_SCRIPTS, you can
110 | CLICommandInvoker.Result result = new CLICommandInvoker(r, new UpdateJobCommand())
111 | .authorizedTo(Jenkins.ADMINISTER)
112 | .withStdin(updateFolder.read())
113 | .invokeWithArgs("folder1");
114 |
115 | assertThat(result, succeeded());
116 | String configFileContent = folder.getConfigFile().asString();
117 | assertThat(configFileContent, containsString("BasicSSHUserPrivateKey"));
118 | assertThat(configFileContent, not(containsString("FileOnMasterPrivateKeySource")));
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPasswordAuthenticatorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
25 |
26 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator;
27 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticatorFactory;
28 | import com.cloudbees.plugins.credentials.CredentialsScope;
29 | import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
30 | import com.trilead.ssh2.Connection;
31 | import com.trilead.ssh2.ServerHostKeyVerifier;
32 | import hudson.model.Computer;
33 | import hudson.model.Items;
34 | import hudson.model.TaskListener;
35 | import hudson.remoting.VirtualChannel;
36 | import hudson.slaves.DumbSlave;
37 | import jenkins.security.MasterToSlaveCallable;
38 |
39 | import org.junit.After;
40 | import org.junit.Before;
41 | import org.junit.Rule;
42 | import org.junit.Test;
43 | import org.jvnet.hudson.test.JenkinsRule;
44 |
45 | import java.lang.reflect.InvocationTargetException;
46 | import java.lang.reflect.Proxy;
47 | import java.util.Collections;
48 | import java.util.List;
49 | import java.util.logging.Level;
50 | import java.util.logging.Logger;
51 |
52 | import static org.hamcrest.CoreMatchers.is;
53 | import static org.junit.Assert.assertNotNull;
54 | import static org.hamcrest.MatcherAssert.assertThat;
55 |
56 | public class TrileadSSHPasswordAuthenticatorTest {
57 |
58 | private Connection connection;
59 | private StandardUsernamePasswordCredentials user;
60 | private Object sshd;
61 |
62 | @Rule public JenkinsRule r = new JenkinsRule();
63 |
64 | @After
65 | public void tearDown() {
66 | if (connection != null) {
67 | connection.close();
68 | connection = null;
69 | }
70 | if (sshd!=null) {
71 | try {
72 | invoke(sshd, "stop", new Class>[] {Boolean.TYPE}, new Object[] {true});
73 | } catch (Throwable t) {
74 | Logger.getLogger(getClass().getName()).log(Level.WARNING, "Problems shutting down ssh server", t);
75 | }
76 | }
77 | }
78 |
79 | // disabled as Apache MINA sshd does not provide easy mech for giving a Keyboard Interactive authenticator
80 | // so this test relies on having a local sshd which is keyboard interactive only
81 | public void dontTestKeyboardInteractive() throws Exception {
82 | connection = new Connection("localhost");
83 | connection.connect(new NoVerifier());
84 | TrileadSSHPasswordAuthenticator instance =
85 | new TrileadSSHPasswordAuthenticator(connection, new BasicSSHUserPassword(CredentialsScope.SYSTEM,
86 | null, "....", // <---- put your username here
87 | "....", // <---- put your password here
88 | null));
89 | assertThat(instance.canAuthenticate(), is(true));
90 | assertThat(instance.authenticate(), is(true));
91 | assertThat(instance.isAuthenticated(), is(true));
92 | }
93 |
94 | @Before
95 | public void setUp() {
96 | user =(StandardUsernamePasswordCredentials) Items.XSTREAM.fromXML(Items.XSTREAM.toXML(new BasicSSHUserPassword(CredentialsScope.SYSTEM, null, "foobar", "foomanchu", null)));
97 | }
98 |
99 | @Test
100 | public void testPassword() throws Exception {
101 | sshd = createPasswordAuthenticatedSshServer();
102 | invoke(sshd, "start", null, null);
103 | int port = (Integer)invoke(sshd, "getPort", null, null);
104 | connection = new Connection("localhost", port);
105 | connection.connect(new NoVerifier());
106 | TrileadSSHPasswordAuthenticator instance =
107 | new TrileadSSHPasswordAuthenticator(connection, user);
108 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
109 | assertThat(instance.canAuthenticate(), is(true));
110 | assertThat(instance.authenticate(), is(true));
111 | assertThat(instance.isAuthenticated(), is(true));
112 | }
113 |
114 | private Object createPasswordAuthenticatedSshServer() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException {
115 | return createPasswordAuthenticatedSshServer(null);
116 | }
117 |
118 | private Object createPasswordAuthenticatedSshServer(final String username) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, InstantiationException {
119 | Object sshd = newDefaultSshServer();
120 | Class> keyPairProviderClass = newKeyPairProviderClass();
121 | Object provider = newProvider();
122 | Class> authenticatorClass = newAuthenticatorClass();
123 | Object authenticator = newAuthenticator(authenticatorClass, username);
124 | Object factory = newFactory();
125 |
126 | invoke(sshd, "setPort", new Class>[] {Integer.TYPE}, new Object[] {0});
127 | invoke(sshd, "setKeyPairProvider", new Class>[] {keyPairProviderClass}, new Object[] {provider});
128 | invoke(sshd, "setPasswordAuthenticator", new Class>[] {authenticatorClass}, new Object[] {authenticator});
129 | invoke(sshd, "setUserAuthFactories", new Class>[] {List.class}, new Object[] {Collections.singletonList(factory)});
130 |
131 | return sshd;
132 | }
133 |
134 | @Test
135 | public void testFactory() throws Exception {
136 | sshd = createPasswordAuthenticatedSshServer();
137 | invoke(sshd, "start", null, null);
138 | int port = (Integer)invoke(sshd, "getPort", null, null);
139 | connection = new Connection("localhost", port);
140 | connection.connect(new NoVerifier());
141 | SSHAuthenticator instance = SSHAuthenticator.newInstance(connection, user);
142 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
143 | assertThat(instance.canAuthenticate(), is(true));
144 | assertThat(instance.authenticate(), is(true));
145 | assertThat(instance.isAuthenticated(), is(true));
146 | }
147 |
148 | @Test
149 | public void testFactoryAltUsername() throws Exception {
150 | sshd = createPasswordAuthenticatedSshServer("bill");
151 | invoke(sshd, "start", null, null);
152 | int port = (Integer)invoke(sshd, "getPort", null, null);
153 | connection = new Connection("localhost", port);
154 | connection.connect(new NoVerifier());
155 | SSHAuthenticator instance = SSHAuthenticator.newInstance(connection, user, null);
156 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
157 | assertThat(instance.canAuthenticate(), is(true));
158 | assertThat(instance.authenticate(), is(false));
159 | assertThat(instance.isAuthenticated(), is(false));
160 | connection = new Connection("localhost", port);
161 | connection.connect(new NoVerifier());
162 | instance = SSHAuthenticator.newInstance(connection, user, "bill");
163 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
164 | assertThat(instance.canAuthenticate(), is(true));
165 | assertThat(instance.authenticate(), is(true));
166 | assertThat(instance.isAuthenticated(), is(true));
167 | }
168 |
169 | /**
170 | * Brings the {@link SSHAuthenticatorFactory} to a slave.
171 | */
172 | @Test
173 | public void testSlave() throws Exception {
174 | Object sshd = createPasswordAuthenticatedSshServer();
175 | invoke(sshd, "start", null, null);
176 |
177 | DumbSlave s = r.createSlave();
178 | Computer c = s.toComputer();
179 | assertNotNull(c);
180 | c.connect(false).get();
181 |
182 | final int port = (Integer)invoke(sshd, "getPort", null, null);
183 |
184 | TaskListener l = r.createTaskListener();
185 | VirtualChannel channel = c.getChannel();
186 | assertNotNull(channel);
187 | channel.call(new RemoteConnectionTest(port, user));
188 | }
189 |
190 | private static class NoVerifier implements ServerHostKeyVerifier {
191 | public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm,
192 | byte[] serverHostKey) {
193 | return true;
194 | }
195 | }
196 |
197 | private static final class RemoteConnectionTest extends MasterToSlaveCallable {
198 | private final int port;
199 | private final StandardUsernamePasswordCredentials user;
200 |
201 | public RemoteConnectionTest(int port, StandardUsernamePasswordCredentials user) {
202 | this.port = port;
203 | this.user = user;
204 | }
205 |
206 | public Void call() throws Exception {
207 | Connection connection = new Connection("localhost", port);
208 | connection.connect(new NoVerifier());
209 | SSHAuthenticator instance = SSHAuthenticator.newInstance(connection,user);
210 |
211 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
212 | assertThat(instance.canAuthenticate(), is(true));
213 | instance.authenticateOrFail();
214 | assertThat(instance.isAuthenticated(), is(true));
215 | connection.close();
216 | return null;
217 | }
218 |
219 | private static final long serialVersionUID = 1L;
220 | }
221 |
222 | private Object invoke(Object target, String methodName, Class>[] parameterTypes, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
223 | return target.getClass().getMethod(methodName, parameterTypes).invoke(target, args);
224 | }
225 |
226 | private Object newDefaultSshServer() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
227 | Object server = null;
228 | Class> serverClass;
229 | try {
230 | serverClass = Class.forName("org.apache.sshd.SshServer");
231 | } catch (ClassNotFoundException e) {
232 | serverClass = Class.forName("org.apache.sshd.server.SshServer");
233 | }
234 |
235 | server = serverClass.getDeclaredMethod("setUpDefaultServer").invoke(null);
236 | assertNotNull(server);
237 |
238 | return server;
239 | }
240 |
241 | private Class> newKeyPairProviderClass() throws ClassNotFoundException {
242 | Class> keyPairProviderClass;
243 | try {
244 | keyPairProviderClass = Class.forName("org.apache.sshd.common.KeyPairProvider");
245 | } catch (ClassNotFoundException e) {
246 | keyPairProviderClass = Class.forName("org.apache.sshd.common.keyprovider.KeyPairProvider");
247 | }
248 |
249 | return keyPairProviderClass;
250 | }
251 |
252 | private Object newProvider() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
253 | Class> providerClass = Class.forName("org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider");
254 | Object provider = providerClass.getConstructor().newInstance();
255 | assertNotNull(provider);
256 |
257 | return provider;
258 | }
259 |
260 | private Class> newAuthenticatorClass() throws ClassNotFoundException {
261 | Class> authenticatorClass;
262 | try {
263 | authenticatorClass = Class.forName("org.apache.sshd.server.auth.password.PasswordAuthenticator");
264 | } catch(ClassNotFoundException e) {
265 | authenticatorClass = Class.forName("org.apache.sshd.server.PasswordAuthenticator");
266 | }
267 |
268 | return authenticatorClass;
269 | }
270 |
271 | private Object newAuthenticator(Class> authenticatorClass, final String userName) throws IllegalArgumentException {
272 | Object authenticator = Proxy.newProxyInstance(
273 | authenticatorClass.getClassLoader(), new Class>[]{authenticatorClass}, (proxy, method, args) ->
274 | method.getName().equals("authenticate") ?
275 | (userName == null || userName.equals(args[0])) && "foomanchu".equals(args[1]) :
276 | null);
277 | assertNotNull(authenticator);
278 | return authenticator;
279 | }
280 |
281 | private Object newFactory() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
282 | Object factory = null;
283 | Class> factoryClass;
284 | try {
285 | factoryClass = Class.forName("org.apache.sshd.server.auth.UserAuthPassword$Factory");
286 | } catch (ClassNotFoundException e) {
287 | factoryClass = Class.forName("org.apache.sshd.server.auth.password.UserAuthPasswordFactory");
288 | }
289 |
290 | factory = factoryClass.getConstructor().newInstance();
291 |
292 | assertNotNull(factory);
293 | return factory;
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/TrileadSSHPublicKeyAuthenticatorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2011-2012, CloudBees, Inc., Stephen Connolly.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.cloudbees.jenkins.plugins.sshcredentials.impl;
25 |
26 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator;
27 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
28 | import com.cloudbees.plugins.credentials.CredentialsDescriptor;
29 | import com.cloudbees.plugins.credentials.CredentialsScope;
30 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
31 | import com.trilead.ssh2.Connection;
32 | import edu.umd.cs.findbugs.annotations.CheckForNull;
33 | import edu.umd.cs.findbugs.annotations.NonNull;
34 | import hudson.util.Secret;
35 |
36 | import org.junit.After;
37 | import org.junit.Before;
38 | import org.junit.Rule;
39 | import org.junit.Test;
40 | import org.jvnet.hudson.test.JenkinsRule;
41 |
42 | import java.lang.reflect.InvocationTargetException;
43 | import java.util.Collections;
44 | import java.util.List;
45 | import java.util.logging.Level;
46 | import java.util.logging.Logger;
47 |
48 | import static java.lang.reflect.Proxy.*;
49 | import static org.hamcrest.CoreMatchers.is;
50 | import static org.hamcrest.MatcherAssert.assertThat;
51 | import static org.junit.Assert.assertNotNull;
52 |
53 | public class TrileadSSHPublicKeyAuthenticatorTest {
54 |
55 | private Connection connection;
56 | private SSHUserPrivateKey user;
57 |
58 | @Rule public JenkinsRule r = new JenkinsRule();
59 |
60 | @After
61 | public void tearDown() {
62 | if (connection != null) {
63 | connection.close();
64 | connection = null;
65 | }
66 | }
67 |
68 | @Before
69 | public void setUp() {
70 | user = new SSHUserPrivateKey() {
71 |
72 | @NonNull
73 | public String getUsername() {
74 | return "foobar";
75 | }
76 |
77 | @NonNull
78 | public String getDescription() {
79 | return "";
80 | }
81 |
82 | @NonNull
83 | public String getId() {
84 | return "";
85 | }
86 |
87 | public CredentialsScope getScope() {
88 | return CredentialsScope.SYSTEM;
89 | }
90 |
91 | @NonNull
92 | public CredentialsDescriptor getDescriptor() {
93 | return new CredentialsDescriptor() {
94 | @NonNull
95 | @Override
96 | public String getDisplayName() {
97 | return "";
98 | }
99 | };
100 | }
101 |
102 | @CheckForNull
103 | public Secret getPassphrase() {
104 | return null;
105 | }
106 |
107 | @NonNull
108 | public List getPrivateKeys() {
109 | // just want a valid key... I generated this and have thrown it away (other than here)
110 | // do not use other than in this test
111 | return List.of("-----BEGIN RSA PRIVATE KEY-----\n"
112 | + "MIICWQIBAAKBgQDADDwooNPJNQB4N4bJPiBgq/rkWKMABApX0w4trSkkX5q+l+CL\n"
113 | + "CuddGGAsAu6XPari8v49ipbBmHqRLP9+X3ARGWKU2gDvGTBr99/ReUl2YgVjCwy+\n"
114 | + "KMrGCN7SNTgRo6StwVaPhh6pUpNTQciDe/kOwUnQFWSM6/lwkOD1Uod45wIBIwKB\n"
115 | + "gHi3O8HELVnmzRhdaqphkLHLL/0/B18Ye4epPBy1/JqFPLJQ1kjFBnUIAe/HVCSN\n"
116 | + "KZX30wIcmUZ9GdeYoJiTwsfTy9t2KwHjqrapTfiekVZAW+3iDBqRZMxQ5MoK7b6g\n"
117 | + "w5HrrtrtPfYuAsBnYjIS6qsKAVT3vdolJ5eai/RlPO4LAkEA76YuUozC/dW7Ox+R\n"
118 | + "1Njd6cWJsRVXGemkSYY/rSh0SbfHAebqL/bDg8xXim9UiuD9Hc6md3glHQj6iKvl\n"
119 | + "BxWq4QJBAM0moKiM16WFSFJP1wVDj0Bnx6DkJYSpf5u+C0ghBVoqIYKq6/P/gRE2\n"
120 | + "+ColsLu6AYftaEJVpAgxeTU/IsGoJMcCQHRmqMkCipiMYkFJ2R49cxnGWNJa0ojt\n"
121 | + "03QrQ3/9tNNZQ2dS5sbW8UAEKoURgNW9vMVVvpHMpE/uaw8u65W6ESsCQDTAyjn4\n"
122 | + "VLWIrDJsTTveLCaBFhNt3cMHA45ysnGiF1GzD+5mdzAdITBP9qvAjIgLQjjlRrH4\n"
123 | + "w8eXsXQXjJgyjR0CQHfvhiMPG5pWwmXpsEOFo6GKSvOC/5sNEcnddenuO/2T7WWi\n"
124 | + "o1LQh9naeuX8gti0vNR8+KtMEaIcJJeWnk56AVY=\n"
125 | + "-----END RSA PRIVATE KEY-----\n");
126 | }
127 | };
128 | }
129 |
130 | @Test
131 | public void testAuthenticate() throws Exception {
132 | Object sshd = newDefaultSshServer();
133 | Class> keyPairProviderClass = newKeyPairProviderClass();
134 | Object provider = newProvider();
135 | Class> authenticatorClass = newAuthenticatorClass();
136 | Object authenticator = newAuthenticator(authenticatorClass, "foobar");
137 | Object factory = newFactory();
138 | assertNotNull(factory);
139 |
140 | invoke(sshd, "setPort", new Class>[] {Integer.TYPE}, new Object[] {0});
141 | invoke(sshd, "setKeyPairProvider", new Class>[] {keyPairProviderClass}, new Object[] {provider});
142 | invoke(sshd, "setPublickeyAuthenticator", new Class>[] {authenticatorClass}, new Object[] {authenticator});
143 | invoke(sshd, "setUserAuthFactories", new Class>[] {List.class}, new Object[] {Collections.singletonList(factory)});
144 |
145 | try {
146 | invoke(sshd, "start", null, null);
147 | int port = (Integer)invoke(sshd, "getPort", null, null);
148 | connection = new Connection("localhost", port);
149 | connection.connect((hostname, port1, serverHostKeyAlgorithm, serverHostKey) -> true);
150 | TrileadSSHPublicKeyAuthenticator instance =
151 | new TrileadSSHPublicKeyAuthenticator(connection, user);
152 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
153 | assertThat(instance.canAuthenticate(), is(true));
154 | assertThat(instance.authenticate(), is(true));
155 | assertThat(instance.isAuthenticated(), is(true));
156 | } finally {
157 | try {
158 | invoke(sshd, "stop", new Class>[] {Boolean.TYPE}, new Object[] {true});
159 | } catch (Throwable t) {
160 | Logger.getLogger(getClass().getName()).log(Level.WARNING, "Problems shutting down ssh server", t);
161 | }
162 | }
163 | }
164 |
165 | @Test
166 | public void testFactory() throws Exception {
167 | Object sshd = newDefaultSshServer();
168 | Class> keyPairProviderClass = newKeyPairProviderClass();
169 | Object provider = newProvider();
170 | Class> authenticatorClass = newAuthenticatorClass();
171 | Object authenticator = newAuthenticator(authenticatorClass, "foobar");
172 | Object factory = newFactory();
173 | assertNotNull(factory);
174 |
175 | invoke(sshd, "setPort", new Class>[] {Integer.TYPE}, new Object[] {0});
176 | invoke(sshd, "setKeyPairProvider", new Class>[] {keyPairProviderClass}, new Object[] {provider});
177 | invoke(sshd, "setPublickeyAuthenticator", new Class>[] {authenticatorClass}, new Object[] {authenticator});
178 | invoke(sshd, "setUserAuthFactories", new Class>[] {List.class}, new Object[] {Collections.singletonList(factory)});
179 | try {
180 | invoke(sshd, "start", null, null);
181 | int port = (Integer)invoke(sshd, "getPort", null, null);
182 | connection = new Connection("localhost", port);
183 | connection.connect((hostname, port1, serverHostKeyAlgorithm, serverHostKey) -> true);
184 | SSHAuthenticator instance = SSHAuthenticator.newInstance(connection, user);
185 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
186 | assertThat(instance.canAuthenticate(), is(true));
187 | assertThat(instance.authenticate(), is(true));
188 | assertThat(instance.isAuthenticated(), is(true));
189 | } finally {
190 | try {
191 | invoke(sshd, "stop", new Class>[] {Boolean.TYPE}, new Object[] {true});
192 | } catch (Throwable t) {
193 | Logger.getLogger(getClass().getName()).log(Level.WARNING, "Problems shutting down ssh server", t);
194 | }
195 | }
196 | }
197 |
198 | @Test
199 | public void testAltUsername() throws Exception {
200 | Object sshd = newDefaultSshServer();
201 | Class> keyPairProviderClass = newKeyPairProviderClass();
202 | Object provider = newProvider();
203 | Class> authenticatorClass = newAuthenticatorClass();
204 | Object authenticator = newAuthenticator(authenticatorClass, "bill");
205 | Object factory = newFactory();
206 |
207 | invoke(sshd, "setPort", new Class>[] {Integer.TYPE}, new Object[] {0});
208 | invoke(sshd, "setKeyPairProvider", new Class>[] {keyPairProviderClass}, new Object[] {provider});
209 | invoke(sshd, "setPublickeyAuthenticator", new Class>[] {authenticatorClass}, new Object[] {authenticator});
210 | invoke(sshd, "setUserAuthFactories", new Class>[] {List.class}, new Object[] {Collections.singletonList(factory)});
211 | try {
212 | invoke(sshd, "start", null, null);
213 | int port = (Integer)invoke(sshd, "getPort", null, null);
214 | connection = new Connection("localhost", port);
215 | connection.connect((hostname, port12, serverHostKeyAlgorithm, serverHostKey) -> true);
216 | SSHAuthenticator instance = SSHAuthenticator.newInstance(connection, user, null);
217 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
218 | assertThat(instance.canAuthenticate(), is(true));
219 | assertThat(instance.authenticate(), is(false));
220 | assertThat(instance.isAuthenticated(), is(false));
221 | connection = new Connection("localhost", port);
222 | connection.connect((hostname, port1, serverHostKeyAlgorithm, serverHostKey) -> true);
223 | instance = SSHAuthenticator.newInstance(connection, user, "bill");
224 | assertThat(instance.getAuthenticationMode(), is(SSHAuthenticator.Mode.AFTER_CONNECT));
225 | assertThat(instance.canAuthenticate(), is(true));
226 | assertThat(instance.authenticate(), is(true));
227 | assertThat(instance.isAuthenticated(), is(true));
228 | } finally {
229 | try {
230 | invoke(sshd, "stop", new Class>[] {Boolean.TYPE}, new Object[] {true});
231 | } catch (Throwable t) {
232 | Logger.getLogger(getClass().getName()).log(Level.WARNING, "Problems shutting down ssh server", t);
233 | }
234 | }
235 | }
236 |
237 | private Object invoke(Object target, String methodName, Class>[] parameterTypes, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
238 | return target.getClass().getMethod(methodName, parameterTypes).invoke(target, args);
239 | }
240 |
241 | private Object newDefaultSshServer() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
242 | Object sshd = null;
243 | Class> sshdClass;
244 | try {
245 | sshdClass = Class.forName("org.apache.sshd.SshServer");
246 | } catch (ClassNotFoundException e) {
247 | sshdClass = Class.forName("org.apache.sshd.server.SshServer");
248 | }
249 |
250 | sshd = sshdClass.getDeclaredMethod("setUpDefaultServer").invoke(null);
251 | assertNotNull(sshd);
252 |
253 | return sshd;
254 | }
255 |
256 | private Class> newKeyPairProviderClass() throws ClassNotFoundException {
257 | Class> keyPairProviderClass;
258 | try {
259 | keyPairProviderClass = Class.forName("org.apache.sshd.common.KeyPairProvider");
260 | } catch (ClassNotFoundException e) {
261 | keyPairProviderClass = Class.forName("org.apache.sshd.common.keyprovider.KeyPairProvider");
262 | }
263 |
264 | return keyPairProviderClass;
265 | }
266 |
267 | private Object newProvider() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
268 | Class> providerClass = Class.forName("org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider");
269 | Object provider = providerClass.getConstructor().newInstance();
270 | assertNotNull(provider);
271 |
272 | return provider;
273 | }
274 |
275 | private Class> newAuthenticatorClass() throws ClassNotFoundException {
276 | Class> authenticatorClass;
277 | try {
278 | authenticatorClass = Class.forName("org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator");
279 | } catch(ClassNotFoundException e) {
280 | authenticatorClass = Class.forName("org.apache.sshd.server.PublickeyAuthenticator");
281 | }
282 |
283 | return authenticatorClass;
284 | }
285 |
286 | private Object newAuthenticator(Class> authenticatorClass, final String userName) throws IllegalArgumentException {
287 | Object authenticator = newProxyInstance(
288 | authenticatorClass.getClassLoader(), new Class>[]{authenticatorClass},
289 | (proxy, method, args) -> method.getName().equals("authenticate") ? userName.equals(args[0]) : null);
290 | assertNotNull(authenticator);
291 | return authenticator;
292 | }
293 |
294 | private Object newFactory() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
295 | Object factory = null;
296 | Class> factoryClass;
297 | try {
298 | factoryClass = Class.forName("org.apache.sshd.server.auth.UserAuthPublicKey$Factory");
299 | } catch (ClassNotFoundException e) {
300 | factoryClass = Class.forName("org.apache.sshd.server.auth.pubkey.UserAuthPublicKeyFactory");
301 | }
302 |
303 | factory = factoryClass.getConstructor().newInstance();
304 |
305 | assertNotNull(factory);
306 | return factory;
307 | }
308 | }
309 |
--------------------------------------------------------------------------------
/src/test/java/com/cloudbees/jenkins/plugins/sshcredentials/jcasc/ExportImportRoundTripCasCSSHCredentialsTest.java:
--------------------------------------------------------------------------------
1 | package com.cloudbees.jenkins.plugins.sshcredentials.jcasc;
2 |
3 | import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey;
4 | import com.cloudbees.plugins.credentials.CredentialsProvider;
5 | import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
6 | import hudson.util.Secret;
7 | import io.jenkins.plugins.casc.misc.RoundTripAbstractTest;
8 | import jenkins.model.Jenkins;
9 | import org.jvnet.hudson.test.RestartableJenkinsRule;
10 |
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | import static org.junit.Assert.assertEquals;
15 | import static org.junit.Assert.assertNotNull;
16 |
17 | public class ExportImportRoundTripCasCSSHCredentialsTest extends RoundTripAbstractTest {
18 | @Override
19 | public void assertConfiguredAsExpected(RestartableJenkinsRule restartableJenkinsRule, String s) {
20 | List creds = CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class, Jenkins.getInstanceOrNull(), null, Collections.emptyList());
21 | assertEquals(1, creds.size());
22 | StandardUsernamePasswordCredentials cred = creds.get(0);
23 | assertEquals("userid", cred.getId());
24 | assertEquals("username-of-userid", cred.getUsername());
25 | assertEquals("password-of-userid", cred.getPassword().getPlainText());
26 |
27 | List creds2 = CredentialsProvider.lookupCredentials(BasicSSHUserPrivateKey.class,Jenkins.getInstanceOrNull(), null, Collections.emptyList());
28 | assertEquals(1, creds2.size());
29 | BasicSSHUserPrivateKey cred2 = creds2.get(0);
30 | assertEquals("userid2", cred2.getId());
31 | assertEquals("username-of-userid2", cred2.getUsername());
32 | Secret passphrase = cred2.getPassphrase();
33 | assertNotNull(passphrase);
34 | assertEquals("passphrase-of-userid2", passphrase.getPlainText());
35 | assertEquals("the description of userid2", cred2.getDescription());
36 | assertEquals(1, cred2.getPrivateKeySource().getPrivateKeys().size());
37 | String directKey = cred2.getPrivateKeySource().getPrivateKeys().get(0);
38 | assertEquals("sp0ds9d+skkfjf", directKey);
39 | }
40 |
41 | @Override
42 | public String stringInLogExpected() {
43 | return "Setting class com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey.passphrase = ****";
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/invalidKeysAreRemovedOnStartupTest/credentials.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | GLOBAL
11 | valid-rsa-key
12 | valid RSA 2048 key
13 |
14 | false
15 | {AQAAABAAAAAgQpXlmd7aog5KHqyd4CslpWwvXrItpGOGihMO+b0g0kTXV7McPM5feOqpUnZmsLPv}
16 |
17 | {AQAAABAAAAdQHZ1QzGY21uxH3qs3Ukuy1Qju/IMka1ZPE6ls1IhMS97JVNc5BU0hU2vZ6t6RvGuLOfrplGHVD+A7oU9RTM3my8alGi1NX7hllw0tP+U72FmegoB59SYGpld8WvqwK/YFqvx2HnWNC1Cc+ZFkVDTm+0Uhl6JgfSzhXRgGgFrOfvIaXI+Bl1gUGRhmUjA/Oto8YSFk1VO6SnGBwhEcPyD7u9LAyklV84fLiOCxn3wWefWry1GEfOJk1l46UMHZSoqvqoc+avSHPIEzrhcPkRV5IJRoh9avB8bmzQu2W1J7upV2yzW7a50YU6FuVuhj8o7xc9aVFuOJiI2P1FC+iEu2TnhakKF28LlQbVN1A0eLdHgsBGz8pfudchmjR2FSe1/7G5MUd6ZbVoUDjrvDoM70oFqZz92nl0SXjrMw8eO4omOA/iLRyq2oHFmCzbjaGhp/SFsS39FYwlwhKwOK9vg8GDpDqZZGROpBbhWOJv47kFmzicLlYTMfeiRQXzeEXzJH5vR2a8MrgLp+bZGqIDZs7efV7IzBZb5QQ81MJ+fJ+STOOT/+RqMRcTN/0WpHxbtcloyBXa2sEP9v7fnaKCyH6mBLUdKLdlJGqdbadObUfCKPIk5ZHFeRng8/aZqZvFQ4GcgzCvHKn7cUcgLI12Cn/oL3o1BBhdBmXGI1NJ5QfyIr0DmOJ9oAdYyJFunn5SkHqjxn0MxAnmwlFeYVyBanBwn0d4Xq6pZ0a1/sKs74A0yzXn/Aaj/H/a1artHM0qA4P+yXy3magNAtzSVa4WwI74E4daYB881meSi9vNOijGqLvQLW87ZXE29jyXT26W1+bnEMMvw6OSkhsRbrBE04kFljBtwkPF7hsiueuVPZRgBQxKo2w9LU95ZCc0XJRc/x6gPJMH4c1QzTEvLmdvgr3tTYcKk781qHQAC+/z9ciP/RT0pA/m6ffs5CSZ5/XcuT+OkJn+A1uxQigTGfKkOH6KNDLkYh6aECl9O44UDS++6ceq8SBhwKmQoHNRC++FpouN5/vAoQCOCb4G1Fiy7iBpTHB4Fv71dHCRrffrz0utu9Iwy2USiCBGKmWfm4TzwWwyHJfOQDBjeJQT36tzjVG0yq9PjGkn8JAWGyxGvuu1R9kFH7pxGTJXFGAhdjJTlE8kb1v43GkST+qTgLz4D1dXkMcCZ1dXFObP12/X7iMJqvAR5q3QUGKcfX9DKJm0ZnBwmuhyMRpo3NrYnrpvENHE8VgSgZ8apfITtFT6eXjCOAG4aoIMB8tn+uRHmtxiW/1EZTwrDumSCttpqtgAmQPOtcYmSrtpuhjWooSVGEeodz7IHpi6rgw3okpskcA/mVcKwU/2QXFbIt6u6TR35NQSnoSoKf4XyZrpvZf5/gcaK7ALgjGR0AU9mjdMH39ldSuQuITHXQxa/xIPOMcFVEiUY9TPgVpx8Q3Z5KgBatb0KdotfB2Dcoxt1ey99unOPQF9gUUb2fS2L4kTjb9szY26GJAXztikooXDnl53PKNTlNNVNom5AezScu8FQSWbZuy8m9y/XQB72K91VBzPg+0uJsDCXjWK4seaJVjyQ+zFiWcxon3kC3ngft7vtZpwKTTTMAOtyXkOwvK8ri8sJHSCxrPHc8gz0Qx9vG71nU4jaUWLYOwgFis0Z1C2YhCGA+5JBuPd66mboHKqRE7W7pA443IkZqtJOpIcFXWPUGu/2NCr4hDCuNrudIvA9BU2ARUmYhdFySd4LGiRBVXtsQ+eBcBRmP4FT+2Evo3NhGtvvnG8eQzZcShOmm1utLxyjQcEaJ2bpoXe7CFQmas8sx+VZChRfEyPbRCnGmsXl9snl3xe9zu/otjIpZMjTRH/X7qQTqlk1PVtyY0HK8AIe774sIV/vV+BdTQPBYdSD6Nukb5q+Uw6EPdTowoZEgX+R/1wPYRaDVQNlfPexFWpRnGTyO4InCSW49ZLx3QE49HsRjRFEfIl3QlkAxro7nrkQnqiA3fxy0fiO2tRFuEvthmW60wLxbD+Io9pMTHFq4yfqHtLEpxumpWsDF2QK7+tqU1VoN2WAZapCkN6iwphswTnt2YYXr4XaPQb+eC/zUOz+nk5N7jiQ2h1x3wZidwUskznZRi3I4mMXy6fEH5ZcFyF67K/8Y5XY5yVEckskqtJtC+QSm0Mvi1VYDeOMq42q9OwmWYMqewJ5i4tv0cavgF8sDjlkjE4MnVOC2BJvVHs67isCcZrU/hsUrUikwmTcU8NSq7FbCjbEj1rL84bKrRUhOTcNeNGyygwSOOmC01OivmAyaY6Pi+08p0hrJ0R2q6fDX0t4hajo6dVPTtLb3eThplZf7qCjmuT6z1ASwYFenrrWMh8P/nuimmVCpx3ZP+TazNW3NzHktWxxnPZB6s9FH0spCWmaiGHXZpIzU5vj9dftJ2XWJ7LDsUqKyRfBWDH7X5AfWTh3FeCxBwvi9K4uu7wB2kx/ZQAHYiztGh4H5DJpTOSxCkZNrQfhUqtyIUZhspp3J6pXzP0eiu2H90w==}
18 |
19 |
20 |
21 | GLOBAL
22 | invalid-rsa-key
23 | invalid rsa 1024 key
24 |
25 | false
26 | {AQAAABAAAAAgUqB2y9bDaBL0LNtjF8YMW3oi9Lu3x1hogk1uaAF1u4QKrJ983/4W5hXa6xPkkz+N}
27 |
28 | {AQAAABAAAARAY16D8r9HDW7KhowwwRzIineCEp6z7vSv7A39XaacZgTIVPUReaAHpOTzBHbbgjwd7bbKDMqfIvT7Dm8/KbqHAgFFUf71fJW0W21g9rDJkZ2GdQh1FTeLEY0ZOQRsT/iTeSXHB6RQMk44xSsMqX93f1qfwh1kwBGTXyDaCLurinKFJZ60OxR4wrcSnwsBk8eJxOun8kLg7sTb/H30NIt4QsOAtKnsMbDISK495NRzaozIJw55BenmGmOEmeAPdOn6pFiBkXCf/oM62LztzdBJgx7x4r/S7J7q568F4tKfwaPjZqkBoiUAwNRw7zQV97YQeppcWobVZejtyYBjU0eb9lSsZBbVCAZRN13RIh6ACNXEr6oGt0nnQGMQtcTozkOCDe4TXQNlozx/hebolwJLtj8yk6PPp0PCqAh3AUXv/Q4JDiepfeKzUJZ//TgSyVYQc5sPxvLr5Ld3wN+g4FVjyyEAlrkuwrXGdvI1qdiIw0u6YBXAiyKqDEPRQ4cNdgwafPY43MU9Vbwhv2coS53rMAM8DCAkNsPM/wiYnMvZM18QwCSuDLgWblryAI1QWqIkcob3bpA/8DU05hpOK4SjePdi5UWKRl8uVNHtcQGdtGGgiD/+Q/Mmn6H6/OHVJIMy7ffR3x/bimfNkiEcan4W/OCeXvCm0A49hNcrBaCECjYhlQ8pZHnCMQPNXLfZAG1FPSJ5MYdqBf2p0Scknz24ChcxEtwTOe+iP7BtAk7xhbJBRdwc7N1k+Efi4CQPBetw8vgGH7jVk/nTmYmRhSWFgKLUZekgtanzQyNfTx1OXO66ZiV43+D1el6HSxgoigW690J2j6Rzkv+DFSnm93EVqwgJTlpz2qhRUwYTcpBJ82c+4DQwPhXzyXKGBFn6icO2YkXkFGl45NNpAK6qVz43AsbAbk7DtaYQBRyVbHv49W7/yJVNpOMeQmZpF+4QTI6YZSBPDoZK8FFL360uxqBgAPG4NFXPvHWaJNfL4OpSMxFW9kMyHI94OGrh1aT2jhM3K46Oak3sbfg7TRuj8HnEAfshNLvmbsxyoVpcvXWVCL3NOBM4AW+tsOJyg0tkb5wiv19rDw3IhELihx7wsM4du4qdfOJe9A4fL5wfkr8aP8SvOINxYfB5dHvK4M21Z1xWEmZnp6hWuC/1vdD+qDjzjXYTgclTGMR9yWHrt0jRHAV2OdrNzeVHhdJEd+0SRi9uXka1qshcbN4aBC0FMv0eiqSKQkZd/FybCe47/G11HO2HNtxwILeBTkVWI93DpFeX4km3Bmd6eIL260JngW48Qm42Dya2RPtpV2lljBp+fph1gIhpXn5PsDrGZ+racs75v/n7UBEcn6kRq9fIY/p+4dMXNbzfY7ekNLhchfPkgWO7ob2X1qFBGbHF7WF1q66FXwGvYZixgaXGabL/C60S2xqi+fyxsrBZx6Ag8ZjXvq9w9k9sb66aNY1VXqReMu1O}
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/invalidKeysAreRemovedOnStartupTest/secrets/hudson.util.Secret:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/ssh-credentials-plugin/b0309091cafd2efa85b2dcc29a50666834c3ff09/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/invalidKeysAreRemovedOnStartupTest/secrets/hudson.util.Secret
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/invalidKeysAreRemovedOnStartupTest/secrets/master.key:
--------------------------------------------------------------------------------
1 | dca8ecbe52238770405093581f4cd2c5cbb711eea9b3a538aeb0feba7a21868e68b0fa3392b5fc68e3819821f2fedf9291c64e3326ab40f7d4988a032d6071423dc307f874bd58cc744c1d7e080773e11c20684988f8d057a0ea3cfac726d9c0d3cd2b47a88da987aa5170e244c836510d2ff729d095add7bd8002a2cdb037f0
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/dsa2048:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIICXAIBADCCAjUGByqGSM44BAEwggIoAoIBAQDEAdc6tEsWhI6pEdDZ2OscOCP0
3 | OAjGLk3OG2aTTjoeO0lYxCTuaUEmtUkr7z2l8qcadBrukCRLs7bl+LQDjpjRR+Ik
4 | 7BOARhfhChM5GSKwRSVp4SM2gSpJiuunKUzeFEEkyWQoTO03ZoCLa1LH/R3qmruE
5 | TLRkAx9QHAA1NQE8NpRjj3bCIxSlRExfnxsxLSp72ZCKqIPUUXrKoz6d9fU3hy6G
6 | nRPhwtoNb7BSG12EVDFmYkUDUN+Z/HlPMNW1rlHpqGusU9sciWu/Cfedf5SrjH9C
7 | fDNzUt+gwBHJLWHhYjw0VK8VuY/KR4QxbrVT6Akl/8ry36C93AT0RH1STQMZAh0A
8 | liBmm55sKk/ArcFVEBiMMLHvfY8XPhjEVsEy+wKCAQB9JqFtjRG+uSTtRjLmofJ/
9 | cEVK9WLfVtab+k0RrjMUzRmN07HeK+p5hfpDAziAVoj//ohmxLwfJ4PZ4xnlZ8W2
10 | bBmzL+ak6veTr08WukyTYpMyMRElhsHrWUv674zF7nJk7pmF3WGB2dhUWsMKJX6D
11 | HyalQrSP+z2gjFwa1lWj1pcBNXt19LT4Rb0xIOqVVxkcKHkvNXnnbrL9TKyKOA7B
12 | D+RXKi8/eRb+dCIgRtarDrY+fr7YEvh57bNKlTGfCoCSlDxgEdJqDogiOv5SaAOK
13 | JbA0NgUDZBGNNRrlenu57D3GJ8OowdBA0ME277dZpKWkvlDdz4snNvbca5gB/qvp
14 | BB4CHG9sf6t38uKIK/q3zjKbQMrqMdWSZO0o+ARBtjE=
15 | -----END PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/ed25519:
--------------------------------------------------------------------------------
1 | -----BEGIN ENCRYPTED PRIVATE KEY-----
2 | MIGjMF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBAbW5KJoN3rrAgoZUcE
3 | AO/GAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQoyo3fFspm3I3J2ko
4 | jmeMYARAMFdbiQX5Fd/IqrIHXlr6bMhc2On8aOsCU1pfFVJ9o2VK/PYQQQnEvkCZ
5 | jnOIE4OSBOG2ABNRclnxAL8CHfNQQw==
6 | -----END ENCRYPTED PRIVATE KEY-----
7 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/not-a-key:
--------------------------------------------------------------------------------
1 | I'm not a key
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/openssh-ed25519-nopass:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
3 | QyNTUxOQAAACBijRTQ8qO1R5x2WdTkQxbSlUc0EIVEHGWkToICZf6Z1AAAAKA1h7sHNYe7
4 | BwAAAAtzc2gtZWQyNTUxOQAAACBijRTQ8qO1R5x2WdTkQxbSlUc0EIVEHGWkToICZf6Z1A
5 | AAAEAuKgBGoss8OYtdfaAGbIHh6sULQ6VGPJw4vUvP1xUmJ2KNFNDyo7VHnHZZ1ORDFtKV
6 | RzQQhUQcZaROggJl/pnUAAAAG3BlcmVATUFDLXBidWVub3llcmJlcy5sb2NhbAEC
7 | -----END OPENSSH PRIVATE KEY-----
8 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/openssh-rsa1024:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBFPydw9e
3 | K2cDWnCXMFL9mVAAAAGAAAAAEAAACXAAAAB3NzaC1yc2EAAAADAQABAAAAgQCo24dDC5o8
4 | IFfdrM6sRRQaKkyqzSWXpsMT5rSZ1lcZLGwYr4Qs5WoX6YYxfoWIvEwvd9LDJ0h2uAyHQk
5 | yxoKrZgSSOYAGjZlBQLvDQbDUNjFdi3XYERBEQDxvhtiQvoo0F1/YgokljkZHGv9xSL7Br
6 | ovNZZ5ITIP40EFYbQ5uF7QAAAiBEXY+V77fzRSmV5SfSikMB1sDBwyLUtD/HVJbiqYl/3E
7 | OsJYvM1BwL0quFt7u5s6wefY7eT5p2N6y2+Z5Vk6ZGxoRJlPRqS0QHXslyz/8YaAynTV2E
8 | QGFAiyWLfWhE2u51QUcWGDXaf10gW7YyOZwHFnA8iZi8+RikxUk2lRjtFwQ4pcZsz+eT6h
9 | 7TcBPmSe3tgNd/6fgajR4/9G4h9ZqNGOiBEtZvONKN8KXr61rebK+wuMrjUfg1u0BjyqTs
10 | W13DnH4uLlC7r+2/zfGyMjwt/pw/dwhQwilZY7G3Ckp3O3jv3eAb1AmcRP5oOU+obWIcb2
11 | dee5jbRWgMrS6QEI5yGCDE8st6ezglmVECdRoXsSz+YYwAKPFg47Abh9mRSZEKuX0MyxI8
12 | n/U194AW2eO6G4u90TCFyiblJSjcoDRavbaunB3oYwhyBT6QKJh1nSd4f/TbyHWnTSFcOJ
13 | ARn6SOZnS2S2ARk4t1pvNro94Ww6qCKloBGjlQevBfoEkftb9exSI6/0qTmsjFZiMjiADH
14 | WEJQw+df6KZ9mIZ8xERYGXgBz43EvSjD3+D5DuZm+pFqstCtGxoznL8pGQaBWbuoVGHD+V
15 | /Ncuu7SpolnLFQGjyIvDgSZUoQQKKj3MwN4BLhEiy/ok3TwSau4QzZ6OAiawhF4qIb4nIJ
16 | OA/6HO+9ViuxIxrhKoUTJi88FIhGThXV9GLK4nQf7w6lyu3JD8qS
17 | -----END OPENSSH PRIVATE KEY-----
18 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/rsa1024:
--------------------------------------------------------------------------------
1 | -----BEGIN ENCRYPTED PRIVATE KEY-----
2 | MIIC5TBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQmqBfFEhTkNYDOmt9
3 | C4uwjAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEJEGWoJ0cmwkrkxP
4 | fAE3lZcEggKAnjVdB4IjbyIrMyeJEFZ6MYhmHKyKdX5RtbOUqGRoZiF9+ilJStGq
5 | rwDo5ltASjRpVaNgrJZIY3445BXxRWuJXFIeyxhM9ULpuhDItdt+DzpNCZHIBOCR
6 | dPm4m4K/Ot8v7HFBwMLc2+ttA9/Eif1eGAxC7+7qztH9fUo+ZIvlDxApOkz7MZ/u
7 | PxFNun+uVrzzz3tDYepDvdmQLi1TTUxNgcAOb0b+E810VoBwN/83Pltion4jwhkf
8 | eJHfu7B/M/vj7wGz+BFJZoGjHPOk8BF0T8lxm03DLkVgkXh3suDdEerRB11j10CW
9 | 9ETvDGZg19gAZNMFTzIeiIwpGB1ODnlB0iqgD4yy55fCS42vTROwaBhKKE3d8EIp
10 | C6sy3sfxKn0vOe6bkP2ZgHos0XTriEL6l04UtUpJZHUqG8GMa3031EfD1m4NK190
11 | tS8alFHLD7icci2PZPl5A/Sw5+9DtHI9gM9gZxtIm+eJ0lXCoODmcG5Zuqfcjr1o
12 | FT/Imcf9+lzQp0IX6nK4zd3MzAPxpwDyGCu9bIGiIERvWgtWixknfcv88t7F/05O
13 | TU7uPYCqnUScT6VwmBQyDV5cgDboulBZWaFa2XCR8VbD3xuQwVT8R/dtvpcOHQ4j
14 | B5ScU3aRCEn8eunR6ajs2j4avjVJmFTCdFUtme2z8m/imahK0dtcojaqz8G9jq7l
15 | bkgrtinCKcQ8UdRDVwUwfd6ZFWEHjKhmc32PHJP095VtYQSgPpdwARTwgcd4W55w
16 | TRuaqHif6bz/B2qYQuLCnkla9YJxfwZKwDSbCXaFb34j/1/c+Tu8zhrzv44s6c2Y
17 | x3Sq3CRiOdekpCiD7aM0TSnuw+qb2JBYQw==
18 | -----END ENCRYPTED PRIVATE KEY-----
19 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/rsa2048:
--------------------------------------------------------------------------------
1 | -----BEGIN ENCRYPTED PRIVATE KEY-----
2 | MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQ8hizqQOjbhxZTbGQ
3 | rv/R5wICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIRxwhpaURx7cEggTI
4 | nIiZhjydZ4kCzb7XQilJhfZThSbXO7JyrYsUT++/rsJ+ZBzeC6n1PoHuqxNDjpls
5 | o66iqBVU1B1nm7LfD4VbutozueZ3T68GZp19ZVjDQBU7ZRHCwDtFTTUPXz1u7Sne
6 | 8H87rXKge/XxeatnmfQFEGnCLYP0DBzztNvPCf3szfFsqYoD2sJmrXdhOkiEgrWr
7 | rqnSoZ3fgUIxWx5iLSZ408hrESbt70RuvNWiDFnXaWbXfYNUQ8pNJiePkspZGVnx
8 | uTg9U0yjRxiKZBiXjO92HOGBxeTeOdtO4UtELBn1Vfg4+zX2V1K4oIdwivSW9aa/
9 | Pgqs0VdlxVUMalUW6Bj38KtYk0SYfgh/OSzSHYv+GyX2gtNumbUFKjSnuATWLs+r
10 | KsVXFSEMXzNyhIP9FwvKVCMcEutJYAbfVPL/YWdPmSgEBKCNQlrG1JNyD3Pj0G/F
11 | 1VFss953WV9rJb6fNjbxhOlCA3b3sBXdZVsvocTSJtCcwGXBug06XwOMiJ/kYpeC
12 | xNXm0NRRd8z17PeFYnc01KCp2W4cptReD6bxfG9sUgRKDOxfC1MOs8Uc9MspmeAZ
13 | QKJ6ZoCNcB232IeuXN5Y2IERb2DDtYeLtOhxIeZ/yPPTO2khfQ/dThUvLxomdgJa
14 | S9p13OApJjrGAGh2+29nRtD58u/M6oMKPQw48aJxGV8dO+fmusK00iC+4N5Gvqc1
15 | +DLonBY1+zQ5x8WyTXGZNzO1MxIdo3DjcEZ1yKYvwlV2Bh+rUY3t8SuyMuh0i3QG
16 | 1RWQXdVfOJ9FY139S/wLF47B5rFsXEkP7drfEPYWWKvnpaibgtI4xxb3JoNWbe5c
17 | 5xnMtrFPB4XP+2g0tIWEEsRyWVfY/Fhowhg0z1iVZ/ne77slEgKBLoZHcmkMNCQc
18 | MturgIErRd+fqOuiKYFGdtrce204XMvqDwNklfgvxA3PVb5PZREgfkZ5t8VmtoPV
19 | XXEqs71O6iKK+v/n9c4TW0m3Osryj028+o5wLWu1obTRnrpLU0pobA47mLeOB4HW
20 | 87nORN41whDJPSEDuMxVm3yvH5Oy9sHLwF5y+AQZYVU6zuxtkB2fK7uIMvith7nH
21 | jTLDfk0pxRp5H1e2K0SVRmDld0u7jGL01bPQz2qzIqrBB2xNN2owqEibOLM9OEZS
22 | xoGI/zejgJa6xp/geuE9D62sGU7jo2T66NETPmJjd6oZYtX5oEfseZ2z1h9Sv7eL
23 | Au3v8OYwGu+dpbCm91XHnJJplN+yiZP64KNarwl8A25AAetKjBOdJSLEnwKz4Ive
24 | KUSJcvjaPCXWa8cjmEh9ntbIUBbjEShjI3DAwLIzpHvLMrCAPnoX4KialbE8xwOJ
25 | cEnb1fsJjf3O6nFsbSa0UzeEznMMSrES3SZhjqlJtdcnEhMhwpJuZUAJlPsmaRDd
26 | DJ37LkTXzjjidK/IcIVd3qoyxs4V2xxrjI1lVAim3i6/opRTm2G+VzE2aEcTidDP
27 | 17bRJyhu+A0Twsxl8M9xDuE11VAIHqnwkQ3d/A2onNdn/EVPtgRjw9YImMtAKqbT
28 | LvXEiyGlyu/q6TLCrNN+X3WtbTwhXatGnmiY3a6W1GJw8TOJq4kxt84PBHMgOcZU
29 | 5dHthPwLgg6akbcrl5zEeUyt0wfavsKE
30 | -----END ENCRYPTED PRIVATE KEY-----
31 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/unencrypted-rsa1024:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANU/5VsOXcefJGtd
3 | yd0iAryR1Xjb4S6AXtgSIXlnzRITuKcaHvHaYgCy1+UWwOjwa88ZgBU7/+VKKf8i
4 | oI082I5pSgLwkmAR592r+pb7iUXp683KIPRZt4PkUlzl4Diq72zb+CRLclflW3H1
5 | HFx7WlUHaOK4sENJAhyyltW7b+8/AgMBAAECgYAKbpPDvpIr38ciUXY8kRtdKi0t
6 | OmRrp+/71fc5PzCy/6/6zLZS0oIU0qrpFBHf01MS0IaJg2PjTZt8Va9Q+XcGeY3u
7 | 8YCMVd5gI8bwd8/UN0oYtuClxvFZ3iviz4Fu2A1cI8NSKD6tfAkDP89IkxGp4Oqs
8 | Q8ksxkY+duvl+MHt4QJBAPe1cwQUK9i8vUBzqZb5NaU/GmfmsmL7E2PZS3Kr4wqg
9 | aCyWPh+2K2efoHHh5OdW1tPMis2TDDJMviBSHSdd8PkCQQDcYyzLeb7PY9HVzS0N
10 | qXQmySCKhedJXvMszzphxxJGD8L1X9RRrVffbkLVl2HUrGFovzk4InDI2lxm9erU
11 | 6qf3AkEA1uii+Af0Hp9pZnCy0xw1sb90zm41mHCS2w8cSUndukt+9igHkAXB1K6G
12 | SiedLCSIT8tnJYINk9pHHc2AI7Z6KQJAOtGRPAETJuCaOTiYRKQsJsnZEH9qWg+o
13 | URZBm6T4wJAmVTytOttLr4sK9VyAtLUJDl6y08OFXGXC8YvCj+7MwwJAff9zDf3R
14 | pxUI+kL1rd1TfpygcGLUo4+V3iQ4Hw5KJcWCyz4fwufaJyb8sClgDZPlEKQqtPqX
15 | ZHuLFx/ytd/1tQ==
16 | -----END PRIVATE KEY-----
17 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest/unencrypted-rsa2048:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDsIgLuzl5X4NaT
3 | x1VZSo8wAOd+Bdq5o00D6oxvOQBkQAx6nJWWhbAF9jp/NW72tDaWa3EoaU0xLsNt
4 | ajBZnI4t/3w7HYfGfoLUvumxBFit35gC73yLCpZdpY7PomTLe3XzV76Chhcl7D1o
5 | otJ/p02KR24SMxpcloBC67a1lzqFBGvu4SvWNSKavzToucdHBOPSkbxzQNJdppJR
6 | XJRernIU417DPEUY683n/XbUaejD/JZS33ZAvUCcE+dq0qVVY8QvgjsfYep6HrBD
7 | Qrt+COQHDjvt+wQ6x+ALsM8anveB+BpxK4bvPtiwDvR46N2pgV6cdlX1MRuHD33p
8 | 5C+K21pFAgMBAAECggEACILYhjdsBFDdvk/cZ+G3GlM+BNEJmp1XN+fq9FG8QODK
9 | Hyh1TKQ4ijTfOn/FJlR49aA2+ogcUl4aiMWseBGbhYiEhGdcSHxKebtnFiYcKCnV
10 | NqESy4djKhbyK2u+cR/1fWlRO/uNtz4C0wdJydrqGyzXiJ8zUqXRRuUGHeXiI+WW
11 | LbX9sWQJnQ/prQU5hH1n4PD/zSHrUVLWf2SCZcCrltI2uR/fNBE3MLClMMqclcnY
12 | +FzBILtiMPjPBZWIHYbDuKk3lqsqdo8uzUO5kp0CXLetXs78tDcL314syDg/3H0F
13 | 6P60e8IXTfmBQhukD+BkrPyNu+zSi13ZHORIyUkaIQKBgQD3JYd9y8FOGJ11Dxsx
14 | KRXpHaTc6vYnL5PgTPGTucCCGHjuiMGgVtYSIocBW5X4mBR0/qw0SnmjopZkzoV8
15 | kqLOWlKB46vZcT3TAGjs9xeNZhxsxXpM/q7/qiGPFzUHlsXre+hGVIXk/obHZ3Hy
16 | x9i4ZlemfZi8JpXY3b1k/7HW3QKBgQD0l3roIYsm0W0IS1BplaxEmf8xy5mq5MCW
17 | 2wS+B/m8G8ddKVAwzhczfbGkzjna7ibnlh8Gf7aKcpCxaDNP67BJiagfB7JjpSXn
18 | CAMMt/OCLl4mzSuQt/OsVqx5FVtoBmkK0wlk4YlY81tkfTLiQ461tgTmr5BjhJYH
19 | rIXkMCP2iQKBgQCpH15jf/4M4h1F0IuMDZB30JhTsNm3IQCxehXKQE9y9yoyGRVf
20 | Em01Rbla/YBX+EHveaL/uYMZrhX6b7S69WFBkl9pkRG1H5/t9xbWKZRNZ3XGHTC+
21 | 5X19aL/EOl5Iji1sIoNlNUvW0zIJ3EkGmSk9rpMGVGYjQshB+iMzrSHWZQKBgBdm
22 | fq3Ct8to8eN/QRw445hUm2OqNPNymzJTleqQXMYwaixxjWh97x5QAjTgPgzCCBrT
23 | 8/ftNAue1lUCwRX+WIlQkDMXy2tZG300+QW4e6WSxhM0QdzAnKF6UVnsPyh+pIIS
24 | mq16HmfSMIY2rC2VhQvBdEqVtVywDKKDUPP53xbxAoGAQuDSWkPoiPIKQEiKBSkN
25 | gIE5/38ODnVPr7/vXX+CI/vfvlfPRTweJ35W9zv7xJAIBfRn5/rc2fmBPsGJUvdV
26 | 6OJzpMzMZboHly5q9J5Uhbhz40YQZVGJT9cfkuP+syAGpl5lo7TN1cHRuncdSMVJ
27 | X4+nMzQE3yif9ZKhSn540rY=
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyTest/readOldCredentials/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.554.1
5 | 2
6 | NORMAL
7 | true
8 |
9 |
10 | false
11 |
12 | ${JENKINS_HOME}/workspace/${ITEM_FULLNAME}
13 | ${ITEM_ROOTDIR}/builds
14 |
15 |
16 |
17 |
18 |
19 | 5
20 | 0
21 |
22 |
23 |
24 | All
25 | false
26 | false
27 |
28 |
29 |
30 | All
31 | 0
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyTest/readOldCredentials/credentials.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | GLOBAL
11 | bc07f814-78bd-4b29-93d4-d25b93285f93
12 | Just testing
13 | testing
14 | 0wJzosMUuj6wb67Cyc8aYw==
15 |
16 | -----BEGIN RSA PRIVATE KEY-----
17 | MIIEpAIBAAKCAQEAu1r+HHzmpybc4iwoP5+44FjvcaMkNEWeGQZlmPwLx70XW8+8
18 | 3d2BMLjhcw1zLsYG3FWpCyn8cB1OmjKiGjvnP5EBoAolvh3qOcWKyWlVGWGgs10B
19 | 0Cgnd3OBXRPQd1cBdiQZmmeCrrH0OjQefYIF2WYN+F8iuNGraAaRsXLgITanjTb1
20 | 6w1dnk+KLpU2J5G6kG1f/Qxl4pgny80S/3TktqoknbOrYDMOSA1Zdww39cpXJHp6
21 | feEs8tavC93rOsR2O4ZfVUCjTFAF5FtIdRv3LXY3Q5W/AyY1h45Wk6mMVnEluFjB
22 | aA+gzVAVaHFQfuhwoj4B7jWCmfHsPG1WmyK0YQIDAQABAoIBADt1qlXiMdV0mP9S
23 | okdm6maQ8xTugKvyODWa+R1vSFHQqhwiNr927+xFkI9SAm8iu8SrjuWTIqF2O57m
24 | WNnYjxB2dbyT29yVY+OH1P8M5cwTVsv1xYCJbdUUHEcs5akqPLWAyXteRHQq1+as
25 | 6cxNOov/PonHr55WNH7kLtLRMV54jZ68nrEh5pWdabFa0f7d/ByIvYRJm7lpjtSp
26 | EBp5AseXzSg2EZP3HDPYYPDHK0tMPginz9+YuQCQFoMYAkVZoKFJGsWICktd5Uk7
27 | wveOJLOfMng1Iww6CEc871GcLn5LbafLWRxjZssK2Z1fC+pZYLLZPeKDMSxoRXm6
28 | PShUC1ECgYEA623dmwJNYfVRgAgOYhhcxzH4TAXUmUIpadEUjOkAhe3w/abDawFT
29 | 9ianhqfhTjSZGBtUttcN40Vy+P4bsqfQKZ7p6KzrdR2zWjlYcICWhZDZPGmMxpMZ
30 | mUFhZXsLRVhn8qed8w1t6eju7S+t9satKIMC/KrhNsFzjrgbU9eC+m0CgYEAy7nN
31 | gMwQeGxAQSJr9H7eKkthnjMe77rLIAZEbDJhcwYVz+Iie/E4hjESQ+IuvXa1VawD
32 | O6cD0wWdOhH2McNdMNIbM4IOaO/TOaR5jfQwBWmb4iG2BZQWQQE/HUBnoJQWUhqm
33 | b+D+s4bHh4J+bs+ptgg9Sd9V+VxJBcu2QDmI6UUCgYBTb1pMJyK5hrFdiH1gcnXe
34 | +myetKpFrlby83AvCBxxWoQ/wKwc7hmNcOGKLVEB4E4pZvY83jZDx0cZyySRyjtR
35 | pMoM9ct0dBQt84jORiQSLeVvLZEAhv1ZfPxBdLvn1Y7xRkoJ60Z60Vxrnqwueva/
36 | Fr8mQIEUYLbNa53ztrrqeQKBgQCqOY4k2F3KwWjPA9wAZyFrZaEjdsOavBGNqK7z
37 | WQVj/umq0eDOfzgjqE0Cu7MiTFYoR5pL9bmUUVSWePuliQANEwH3f+xackmkGHIY
38 | 0rhtTVkbEd/tuVb+6fO6lV4BJrufzvTS9sTbbPq7l6XdIVdE6o2LdDl6Kko5tYWL
39 | FIf5oQKBgQDLHK/9NTb3VHp+Qriu2Vp8Pnaw6YF6pETfgjyrH2ULSW/R07v+AECC
40 | sPr+d/hx2MQWp54HglY8lv98rOrRjMiRw1GVoXs+Ut9vkupmrpvzNE7ITl0tzBqD
41 | sroT/IHW2jKMD0v8kKLUnKCZYzlw0By7+RvJ8lgzHB0D71f6EC1UWg==
42 | -----END RSA PRIVATE KEY-----
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyTest/userWithoutRunScripts_cannotMigrateDangerousPrivateKeySource/update_folder.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | From SSH
15 | from_ssh
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | All
28 | false
29 | false
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | false
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/jcasc/configuration-as-code.yaml:
--------------------------------------------------------------------------------
1 | jenkins:
2 | systemMessage: Jenkins with SSH Credentials for JCasC test
3 |
4 | credentials:
5 | system:
6 | domainCredentials:
7 | - credentials:
8 | - usernamePassword:
9 | scope: SYSTEM
10 | id: "userid"
11 | username: "username-of-userid"
12 | password: "password-of-userid"
13 | - basicSSHUserPrivateKey:
14 | scope: SYSTEM
15 | id: "userid2"
16 | username: "username-of-userid2"
17 | passphrase: "passphrase-of-userid2"
18 | description: "the description of userid2"
19 | privateKeySource:
20 | directEntry:
21 | privateKey: "sp0ds9d+skkfjf"
22 |
--------------------------------------------------------------------------------