• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
3  * Please refer to the LICENSE.txt for licensing details.
4  */
5 package ch.ethz.ssh2.signature;
6 
7 import java.io.IOException;
8 import java.math.BigInteger;
9 
10 import ch.ethz.ssh2.crypto.SimpleDERReader;
11 import ch.ethz.ssh2.crypto.digest.SHA1;
12 import ch.ethz.ssh2.log.Logger;
13 import ch.ethz.ssh2.packets.TypesReader;
14 import ch.ethz.ssh2.packets.TypesWriter;
15 
16 /**
17  * RSASHA1Verify.
18  *
19  * @author Christian Plattner
20  * @version $Id: RSASHA1Verify.java 41 2011-06-02 10:36:41Z dkocher@sudo.ch $
21  */
22 public class RSASHA1Verify
23 {
24 	private static final Logger log = Logger.getLogger(RSASHA1Verify.class);
25 
decodeSSHRSAPublicKey(byte[] key)26 	public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException
27 	{
28 		TypesReader tr = new TypesReader(key);
29 
30 		String key_format = tr.readString();
31 
32 		if (key_format.equals("ssh-rsa") == false)
33 			throw new IllegalArgumentException("This is not a ssh-rsa public key");
34 
35 		BigInteger e = tr.readMPINT();
36 		BigInteger n = tr.readMPINT();
37 
38 		if (tr.remain() != 0)
39 			throw new IOException("Padding in RSA public key!");
40 
41 		return new RSAPublicKey(e, n);
42 	}
43 
encodeSSHRSAPublicKey(RSAPublicKey pk)44 	public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException
45 	{
46 		TypesWriter tw = new TypesWriter();
47 
48 		tw.writeString("ssh-rsa");
49 		tw.writeMPInt(pk.getE());
50 		tw.writeMPInt(pk.getN());
51 
52 		return tw.getBytes();
53 	}
54 
decodeSSHRSASignature(byte[] sig)55 	public static RSASignature decodeSSHRSASignature(byte[] sig) throws IOException
56 	{
57 		TypesReader tr = new TypesReader(sig);
58 
59 		String sig_format = tr.readString();
60 
61 		if (sig_format.equals("ssh-rsa") == false)
62 			throw new IOException("Peer sent wrong signature format");
63 
64 		/* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
65 		 * containing s (which is an integer, without lengths or padding, unsigned and in
66 		 * network byte order)." See also below.
67 		 */
68 
69 		byte[] s = tr.readByteString();
70 
71 		if (s.length == 0)
72 			throw new IOException("Error in RSA signature, S is empty.");
73 
74 		if (log.isDebugEnabled())
75 		{
76 			log.debug("Decoding ssh-rsa signature string (length: " + s.length + ")");
77 		}
78 
79 		if (tr.remain() != 0)
80 			throw new IOException("Padding in RSA signature!");
81 
82 		return new RSASignature(new BigInteger(1, s));
83 	}
84 
encodeSSHRSASignature(RSASignature sig)85 	public static byte[] encodeSSHRSASignature(RSASignature sig) throws IOException
86 	{
87 		TypesWriter tw = new TypesWriter();
88 
89 		tw.writeString("ssh-rsa");
90 
91 		/* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
92 		 * containing s (which is an integer, without lengths or padding, unsigned and in
93 		 * network byte order)."
94 		 */
95 
96 		byte[] s = sig.getS().toByteArray();
97 
98 		/* Remove first zero sign byte, if present */
99 
100 		if ((s.length > 1) && (s[0] == 0x00))
101 			tw.writeString(s, 1, s.length - 1);
102 		else
103 			tw.writeString(s, 0, s.length);
104 
105 		return tw.getBytes();
106 	}
107 
generateSignature(byte[] message, RSAPrivateKey pk)108 	public static RSASignature generateSignature(byte[] message, RSAPrivateKey pk) throws IOException
109 	{
110 		SHA1 md = new SHA1();
111 		md.update(message);
112 		byte[] sha_message = new byte[md.getDigestLength()];
113 		md.digest(sha_message);
114 
115 		byte[] der_header = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00,
116 				0x04, 0x14 };
117 
118 		int rsa_block_len = (pk.getN().bitLength() + 7) / 8;
119 
120 		int num_pad = rsa_block_len - (2 + der_header.length + sha_message.length) - 1;
121 
122 		if (num_pad < 8)
123 			throw new IOException("Cannot sign with RSA, message too long");
124 
125 		byte[] sig = new byte[der_header.length + sha_message.length + 2 + num_pad];
126 
127 		sig[0] = 0x01;
128 
129 		for (int i = 0; i < num_pad; i++)
130 		{
131 			sig[i + 1] = (byte) 0xff;
132 		}
133 
134 		sig[num_pad + 1] = 0x00;
135 
136 		System.arraycopy(der_header, 0, sig, 2 + num_pad, der_header.length);
137 		System.arraycopy(sha_message, 0, sig, 2 + num_pad + der_header.length, sha_message.length);
138 
139 		BigInteger m = new BigInteger(1, sig);
140 
141 		BigInteger s = m.modPow(pk.getD(), pk.getN());
142 
143 		return new RSASignature(s);
144 	}
145 
verifySignature(byte[] message, RSASignature ds, RSAPublicKey dpk)146 	public static boolean verifySignature(byte[] message, RSASignature ds, RSAPublicKey dpk) throws IOException
147 	{
148 		SHA1 md = new SHA1();
149 		md.update(message);
150 		byte[] sha_message = new byte[md.getDigestLength()];
151 		md.digest(sha_message);
152 
153 		BigInteger n = dpk.getN();
154 		BigInteger e = dpk.getE();
155 		BigInteger s = ds.getS();
156 
157 		if (n.compareTo(s) <= 0)
158 		{
159 			log.warning("ssh-rsa signature: n.compareTo(s) <= 0");
160 			return false;
161 		}
162 
163 		int rsa_block_len = (n.bitLength() + 7) / 8;
164 
165 		/* And now the show begins */
166 
167 		if (rsa_block_len < 1)
168 		{
169 			log.warning("ssh-rsa signature: rsa_block_len < 1");
170 			return false;
171 		}
172 
173 		byte[] v = s.modPow(e, n).toByteArray();
174 
175 		int startpos = 0;
176 
177 		if ((v.length > 0) && (v[0] == 0x00))
178 			startpos++;
179 
180 		if ((v.length - startpos) != (rsa_block_len - 1))
181 		{
182 			log.warning("ssh-rsa signature: (v.length - startpos) != (rsa_block_len - 1)");
183 			return false;
184 		}
185 
186 		if (v[startpos] != 0x01)
187 		{
188 			log.warning("ssh-rsa signature: v[startpos] != 0x01");
189 			return false;
190 		}
191 
192 		int pos = startpos + 1;
193 
194 		while (true)
195 		{
196 			if (pos >= v.length)
197 			{
198 				log.warning("ssh-rsa signature: pos >= v.length");
199 				return false;
200 			}
201 			if (v[pos] == 0x00)
202 				break;
203 			if (v[pos] != (byte) 0xff)
204 			{
205 				log.warning("ssh-rsa signature: v[pos] != (byte) 0xff");
206 				return false;
207 			}
208 			pos++;
209 		}
210 
211 		int num_pad = pos - (startpos + 1);
212 
213 		if (num_pad < 8)
214 		{
215 			log.warning("ssh-rsa signature: num_pad < 8");
216 			return false;
217 		}
218 
219 		pos++;
220 
221 		if (pos >= v.length)
222 		{
223 			log.warning("ssh-rsa signature: pos >= v.length");
224 			return false;
225 		}
226 
227 		SimpleDERReader dr = new SimpleDERReader(v, pos, v.length - pos);
228 
229 		byte[] seq = dr.readSequenceAsByteArray();
230 
231 		if (dr.available() != 0)
232 		{
233 			log.warning("ssh-rsa signature: dr.available() != 0");
234 			return false;
235 		}
236 
237 		dr.resetInput(seq);
238 
239 		/* Read digestAlgorithm */
240 
241 		byte digestAlgorithm[] = dr.readSequenceAsByteArray();
242 
243 		/* Inspired by RFC 3347, however, ignoring the comment regarding old BER based implementations */
244 
245 		if ((digestAlgorithm.length < 8) || (digestAlgorithm.length > 9))
246 		{
247 			log.warning("ssh-rsa signature: (digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)");
248 			return false;
249 		}
250 
251 		byte[] digestAlgorithm_sha1 = new byte[] { 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00 };
252 
253 		for (int i = 0; i < digestAlgorithm.length; i++)
254 		{
255 			if (digestAlgorithm[i] != digestAlgorithm_sha1[i])
256 			{
257 				log.warning("ssh-rsa signature: digestAlgorithm[i] != digestAlgorithm_sha1[i]");
258 				return false;
259 			}
260 		}
261 
262 		byte[] digest = dr.readOctetString();
263 
264 		if (dr.available() != 0)
265 		{
266 			log.warning("ssh-rsa signature: dr.available() != 0 (II)");
267 			return false;
268 		}
269 
270 		if (digest.length != sha_message.length)
271 		{
272 			log.warning("ssh-rsa signature: digest.length != sha_message.length");
273 			return false;
274 		}
275 
276 		for (int i = 0; i < sha_message.length; i++)
277 		{
278 			if (sha_message[i] != digest[i])
279 			{
280 				log.warning("ssh-rsa signature: sha_message[i] != digest[i]");
281 				return false;
282 			}
283 		}
284 
285 		return true;
286 	}
287 }
288