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