Mercurial > 510Connectbot
comparison src/ch/ethz/ssh2/signature/RSASHA1Verify.java @ 278:d7e088fa2123 ganymed
start conversion from trilead to ganymed
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Fri, 18 Jul 2014 16:45:43 -0700 |
parents | 91a31873c42a |
children | e1c445af8e46 |
comparison
equal
deleted
inserted
replaced
277:e0da43026046 | 278:d7e088fa2123 |
---|---|
1 /* | 1 |
2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. | 2 package com.trilead.ssh2.signature; |
3 * Please refer to the LICENSE.txt for licensing details. | |
4 */ | |
5 package ch.ethz.ssh2.signature; | |
6 | 3 |
7 import java.io.IOException; | 4 import java.io.IOException; |
8 import java.math.BigInteger; | 5 import java.math.BigInteger; |
9 import java.security.DigestException; | 6 import java.security.InvalidKeyException; |
7 import java.security.KeyFactory; | |
8 import java.security.NoSuchAlgorithmException; | |
9 import java.security.Signature; | |
10 import java.security.SignatureException; | |
11 import java.security.interfaces.RSAPrivateKey; | |
12 import java.security.interfaces.RSAPublicKey; | |
13 import java.security.spec.InvalidKeySpecException; | |
14 import java.security.spec.KeySpec; | |
15 import java.security.spec.RSAPublicKeySpec; | |
10 | 16 |
11 import ch.ethz.ssh2.PacketFormatException; | 17 import com.trilead.ssh2.log.Logger; |
12 import ch.ethz.ssh2.crypto.SimpleDERReader; | 18 import com.trilead.ssh2.packets.TypesReader; |
13 import ch.ethz.ssh2.crypto.digest.SHA1; | 19 import com.trilead.ssh2.packets.TypesWriter; |
14 import ch.ethz.ssh2.log.Logger; | 20 |
15 import ch.ethz.ssh2.packets.TypesReader; | |
16 import ch.ethz.ssh2.packets.TypesWriter; | |
17 | 21 |
18 /** | 22 /** |
19 * RSASHA1Verify. | 23 * RSASHA1Verify. |
20 * | 24 * |
21 * @author Christian Plattner | 25 * @author Christian Plattner, plattner@trilead.com |
22 * @version $Id: RSASHA1Verify.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $ | 26 * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ |
23 */ | 27 */ |
24 public class RSASHA1Verify { | 28 public class RSASHA1Verify { |
25 private static final Logger log = Logger.getLogger(RSASHA1Verify.class); | 29 private static final Logger log = Logger.getLogger(RSASHA1Verify.class); |
26 | 30 |
27 public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException { | 31 public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException { |
28 TypesReader tr = new TypesReader(key); | 32 TypesReader tr = new TypesReader(key); |
29 | |
30 String key_format = tr.readString(); | 33 String key_format = tr.readString(); |
31 | 34 |
32 if(!key_format.equals("ssh-rsa")) { | 35 if (key_format.equals("ssh-rsa") == false) |
33 throw new IllegalArgumentException("Not a ssh-rsa public key"); | 36 throw new IllegalArgumentException("This is not a ssh-rsa public key"); |
34 } | |
35 | 37 |
36 BigInteger e = tr.readMPINT(); | 38 BigInteger e = tr.readMPINT(); |
37 BigInteger n = tr.readMPINT(); | 39 BigInteger n = tr.readMPINT(); |
38 | 40 |
39 if(tr.remain() != 0) { | 41 if (tr.remain() != 0) |
40 throw new PacketFormatException("Padding in RSA public key"); | 42 throw new IOException("Padding in RSA public key!"); |
43 | |
44 KeySpec keySpec = new RSAPublicKeySpec(n, e); | |
45 | |
46 try { | |
47 KeyFactory kf = KeyFactory.getInstance("RSA"); | |
48 return (RSAPublicKey) kf.generatePublic(keySpec); | |
41 } | 49 } |
42 | 50 catch (NoSuchAlgorithmException nsae) { |
43 return new RSAPublicKey(e, n); | 51 IOException ioe = new IOException("No RSA KeyFactory available"); |
52 ioe.initCause(nsae); | |
53 throw ioe; | |
54 } | |
55 catch (InvalidKeySpecException ikse) { | |
56 IOException ioe = new IOException("No RSA KeyFactory available"); | |
57 ioe.initCause(ikse); | |
58 throw ioe; | |
59 } | |
44 } | 60 } |
45 | 61 |
46 public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException { | 62 public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException { |
47 TypesWriter tw = new TypesWriter(); | 63 TypesWriter tw = new TypesWriter(); |
64 tw.writeString("ssh-rsa"); | |
65 tw.writeMPInt(pk.getPublicExponent()); | |
66 tw.writeMPInt(pk.getModulus()); | |
67 return tw.getBytes(); | |
68 } | |
48 | 69 |
70 public static byte[] decodeSSHRSASignature(byte[] sig) throws IOException { | |
71 TypesReader tr = new TypesReader(sig); | |
72 String sig_format = tr.readString(); | |
73 | |
74 if (sig_format.equals("ssh-rsa") == false) | |
75 throw new IOException("Peer sent wrong signature format"); | |
76 | |
77 /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string | |
78 * containing s (which is an integer, without lengths or padding, unsigned and in | |
79 * network byte order)." See also below. | |
80 */ | |
81 byte[] s = tr.readByteString(); | |
82 | |
83 if (s.length == 0) | |
84 throw new IOException("Error in RSA signature, S is empty."); | |
85 | |
86 if (log.isEnabled()) { | |
87 log.log(80, "Decoding ssh-rsa signature string (length: " + s.length + ")"); | |
88 } | |
89 | |
90 if (tr.remain() != 0) | |
91 throw new IOException("Padding in RSA signature!"); | |
92 | |
93 if (s[0] == 0 && s[1] == 0 && s[2] == 0) { | |
94 int i = 0; | |
95 int j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000) | |
96 | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff); | |
97 i += j; | |
98 j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000) | |
99 | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff); | |
100 byte[] tmp = new byte[j]; | |
101 System.arraycopy(s, i, tmp, 0, j); | |
102 sig = tmp; | |
103 } | |
104 | |
105 return s; | |
106 } | |
107 | |
108 public static byte[] encodeSSHRSASignature(byte[] s) throws IOException { | |
109 TypesWriter tw = new TypesWriter(); | |
49 tw.writeString("ssh-rsa"); | 110 tw.writeString("ssh-rsa"); |
50 tw.writeMPInt(pk.getE()); | 111 |
51 tw.writeMPInt(pk.getN()); | 112 /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string |
113 * containing s (which is an integer, without lengths or padding, unsigned and in | |
114 * network byte order)." | |
115 */ | |
116 | |
117 /* Remove first zero sign byte, if present */ | |
118 | |
119 if ((s.length > 1) && (s[0] == 0x00)) | |
120 tw.writeString(s, 1, s.length - 1); | |
121 else | |
122 tw.writeString(s, 0, s.length); | |
52 | 123 |
53 return tw.getBytes(); | 124 return tw.getBytes(); |
54 } | 125 } |
55 | 126 |
56 public static RSASignature decodeSSHRSASignature(byte[] sig) throws IOException { | 127 public static byte[] generateSignature(byte[] message, RSAPrivateKey pk) throws IOException { |
57 TypesReader tr = new TypesReader(sig); | 128 try { |
58 | 129 Signature s = Signature.getInstance("SHA1withRSA"); |
59 String sig_format = tr.readString(); | 130 s.initSign(pk); |
60 | 131 s.update(message); |
61 if(!sig_format.equals("ssh-rsa")) { | 132 return s.sign(); |
62 throw new PacketFormatException("Peer sent wrong signature format"); | |
63 } | 133 } |
64 | 134 catch (NoSuchAlgorithmException e) { |
65 /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string | 135 IOException ex = new IOException(); |
66 * containing s (which is an integer, without lengths or padding, unsigned and in | 136 ex.initCause(e); |
67 * network byte order)." See also below. | 137 throw ex; |
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 } | 138 } |
75 | 139 catch (InvalidKeyException e) { |
76 if(log.isDebugEnabled()) { | 140 IOException ex = new IOException(); |
77 log.debug("Decoding ssh-rsa signature string (length: " + s.length + ")"); | 141 ex.initCause(e); |
142 throw ex; | |
78 } | 143 } |
79 | 144 catch (SignatureException e) { |
80 if(tr.remain() != 0) { | 145 IOException ex = new IOException(); |
81 throw new PacketFormatException("Padding in RSA signature!"); | 146 ex.initCause(e); |
147 throw ex; | |
82 } | 148 } |
83 | |
84 return new RSASignature(new BigInteger(1, s)); | |
85 } | 149 } |
86 | 150 |
87 public static byte[] encodeSSHRSASignature(RSASignature sig) throws IOException { | 151 public static boolean verifySignature(byte[] message, byte[] ds, RSAPublicKey dpk) throws IOException { |
88 TypesWriter tw = new TypesWriter(); | 152 try { |
89 | 153 Signature s = Signature.getInstance("SHA1withRSA"); |
90 tw.writeString("ssh-rsa"); | 154 s.initVerify(dpk); |
91 | 155 s.update(message); |
92 /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string | 156 return s.verify(ds); |
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 } | 157 } |
104 else { | 158 catch (NoSuchAlgorithmException e) { |
105 tw.writeString(s, 0, s.length); | 159 IOException ex = new IOException(); |
160 ex.initCause(e); | |
161 throw ex; | |
106 } | 162 } |
107 | 163 catch (InvalidKeyException e) { |
108 return tw.getBytes(); | 164 IOException ex = new IOException(); |
109 } | 165 ex.initCause(e); |
110 | 166 throw ex; |
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 } | 167 } |
118 catch(DigestException e) { | 168 catch (SignatureException e) { |
119 throw new IOException(e); | 169 IOException ex = new IOException(); |
170 ex.initCause(e); | |
171 throw ex; | |
120 } | 172 } |
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 } | 173 } |
283 } | 174 } |