Skip to content

Commit d4b951a

Browse files
committed
GH-364: Use flags for RSA signature requests in AbstractAgentClient
Respect the flags in OpenSSH signing requests. These flags tell what kind of signature should be produced for an RSA key: ssh-rsa, rsa-sha2-256, or rsa-sha2-512. Previous code only set the flags on the client side, but ignored them in the server side and thus always answered with an ssh-rsa signature. Bug: #364
1 parent 9f5c03a commit d4b951a

File tree

3 files changed

+154
-2
lines changed

3 files changed

+154
-2
lines changed

CHANGES.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
* [GH-300](https://github.com/apache/mina-sshd/issues/300) Read the channel id in `SSH_MSG_CHANNEL_OPEN_CONFIRMATION` as unsigned int.
3838
* [GH-313](https://github.com/apache/mina-sshd/issues/313) Log exceptions in the SFTP subsystem before sending a failure status reply.
3939
* [GH-322](https://github.com/apache/mina-sshd/issues/322) Add basic Android O/S awareness.
40-
* [GH-325](https://github.com/apache/mina-sshd/issues/325) SftpFileSystemProvider: fix deletions of symlinks through `Files.delete()`.
40+
* [GH-325](https://github.com/apache/mina-sshd/issues/325) `SftpFileSystemProvider`: fix deletions of symlinks through `Files.delete()`.
41+
* [GH-364](https://github.com/apache/mina-sshd/issues/364) `AbstractAgentClient`: respect flags for RSA signature requests.
4142

4243

4344
* [SSHD-1295](https://issues.apache.org/jira/browse/SSHD-1295) Fix cancellation of futures and add options to cancel futures on time-outs.

sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.sshd.agent.SshAgent;
3030
import org.apache.sshd.agent.SshAgentConstants;
3131
import org.apache.sshd.common.config.keys.KeyUtils;
32+
import org.apache.sshd.common.keyprovider.KeyPairProvider;
3233
import org.apache.sshd.common.util.ValidateUtils;
3334
import org.apache.sshd.common.util.buffer.Buffer;
3435
import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -130,7 +131,23 @@ protected void process(int cmd, Buffer req, Buffer rep) throws Exception {
130131
KeyUtils.getKeyType(signingKey),
131132
"Cannot resolve key type of %s",
132133
signingKey.getClass().getSimpleName());
133-
Map.Entry<String, byte[]> result = agent.sign(null, signingKey, keyType, data);
134+
String signatureAlgorithm = keyType;
135+
if (KeyPairProvider.SSH_RSA.equals(keyType)) {
136+
switch (flags) {
137+
case 4:
138+
signatureAlgorithm = KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS;
139+
break;
140+
case 2:
141+
signatureAlgorithm = KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS;
142+
break;
143+
case 0:
144+
break;
145+
default:
146+
throw new IllegalArgumentException("SSH2_AGENTC_SIGN_REQUEST: Unknown flag value 0x"
147+
+ Integer.toHexString(flags) + ", only 0, 2, or 4 are allowed.");
148+
}
149+
}
150+
Map.Entry<String, byte[]> result = agent.sign(null, signingKey, signatureAlgorithm, data);
134151
String algo = result.getKey();
135152
byte[] signature = result.getValue();
136153
Buffer sig = new ByteArrayBuffer(algo.length() + signature.length + Long.SIZE, false);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.sshd.agent;
20+
21+
import java.io.IOException;
22+
import java.security.KeyPair;
23+
import java.util.Arrays;
24+
import java.util.List;
25+
import java.util.Map;
26+
27+
import org.apache.sshd.agent.common.AbstractAgentClient;
28+
import org.apache.sshd.agent.common.AbstractAgentProxy;
29+
import org.apache.sshd.agent.local.AgentImpl;
30+
import org.apache.sshd.common.config.keys.KeyUtils;
31+
import org.apache.sshd.common.keyprovider.KeyPairProvider;
32+
import org.apache.sshd.common.signature.BuiltinSignatures;
33+
import org.apache.sshd.common.signature.Signature;
34+
import org.apache.sshd.common.util.buffer.Buffer;
35+
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
36+
import org.apache.sshd.common.util.security.SecurityUtils;
37+
import org.apache.sshd.util.test.BaseTestSupport;
38+
import org.apache.sshd.util.test.NoIoTestCase;
39+
import org.junit.Test;
40+
import org.junit.experimental.categories.Category;
41+
import org.junit.runner.RunWith;
42+
import org.junit.runners.Parameterized;
43+
44+
/**
45+
* Simple short-circuited test for {@link AbstractAgentClient} and {@link AbstractAgentProxy}.
46+
*/
47+
@Category(NoIoTestCase.class)
48+
@RunWith(Parameterized.class)
49+
public class AgentUnitTest extends BaseTestSupport {
50+
51+
private String algorithm;
52+
53+
private BuiltinSignatures factory;
54+
55+
public AgentUnitTest(String algorithm, BuiltinSignatures factory) {
56+
this.algorithm = algorithm;
57+
this.factory = factory;
58+
}
59+
60+
@Parameterized.Parameters(name = "{0}")
61+
public static List<Object[]> getParameters() {
62+
return Arrays.asList(new Object[] { KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS, BuiltinSignatures.rsaSHA512 },
63+
new Object[] { KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS, BuiltinSignatures.rsaSHA256 },
64+
new Object[] { KeyPairProvider.SSH_RSA, BuiltinSignatures.rsa });
65+
}
66+
67+
@Test
68+
public void testRsaSignature() throws Exception {
69+
SshAgent agent = new AgentImpl();
70+
KeyPair pair = SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM).generateKeyPair();
71+
agent.addIdentity(pair, "test key");
72+
Server server = new Server(agent);
73+
Client client = new Client(server);
74+
server.setClient(client);
75+
byte[] data = { 'd', 'a', 't', 'a' };
76+
Map.Entry<String, byte[]> result = client.sign(null, pair.getPublic(), algorithm, data);
77+
assertEquals("Unexpected signature algorithm", algorithm, result.getKey());
78+
byte[] signature = result.getValue();
79+
Signature verifier = factory.get();
80+
verifier.initVerifier(null, pair.getPublic());
81+
verifier.update(null, data);
82+
assertTrue("Signature should validate", verifier.verify(null, signature));
83+
}
84+
85+
private static class Server extends AbstractAgentClient {
86+
87+
private Client client;
88+
89+
protected Server(SshAgent agent) {
90+
super(agent);
91+
}
92+
93+
@Override
94+
protected void reply(Buffer buf) throws IOException {
95+
client.setResult(buf.getCompactData());
96+
}
97+
98+
void setClient(Client client) {
99+
this.client = client;
100+
}
101+
102+
void request(byte[] data) throws IOException {
103+
messageReceived(ByteArrayBuffer.getCompactClone(data));
104+
}
105+
}
106+
107+
private static class Client extends AbstractAgentProxy {
108+
109+
private final Server server;
110+
111+
private byte[] result;
112+
113+
protected Client(Server server) {
114+
super(null);
115+
this.server = server;
116+
}
117+
118+
@Override
119+
protected Buffer request(Buffer buffer) throws IOException {
120+
server.request(buffer.getCompactData());
121+
Buffer received = ByteArrayBuffer.getCompactClone(result);
122+
return new ByteArrayBuffer(received.getBytes());
123+
}
124+
125+
@Override
126+
public boolean isOpen() {
127+
return true;
128+
}
129+
130+
void setResult(byte[] data) {
131+
result = data;
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)