commit | author | age
|
56b3f3
|
1 |
/* |
JM |
2 |
* Copyright 2014 gitblit.com. |
|
3 |
* |
|
4 |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not |
|
5 |
* use this file except in compliance with the License. You may obtain a copy of |
|
6 |
* the License at |
|
7 |
* |
|
8 |
* http://www.apache.org/licenses/LICENSE-2.0 |
|
9 |
* |
|
10 |
* Unless required by applicable law or agreed to in writing, software |
|
11 |
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
12 |
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
13 |
* License for the specific language governing permissions and limitations under |
|
14 |
* the License. |
|
15 |
*/ |
bcc8a0
|
16 |
package com.gitblit.transport.ssh; |
JM |
17 |
|
|
18 |
import java.io.Serializable; |
|
19 |
import java.security.PublicKey; |
c78b25
|
20 |
import java.util.Arrays; |
JM |
21 |
import java.util.List; |
bcc8a0
|
22 |
|
JM |
23 |
import org.apache.commons.codec.binary.Base64; |
|
24 |
import org.apache.sshd.common.SshException; |
d41034
|
25 |
import org.apache.sshd.common.util.buffer.Buffer; |
JM |
26 |
import org.apache.sshd.common.util.buffer.ByteArrayBuffer; |
bcc8a0
|
27 |
import org.eclipse.jgit.lib.Constants; |
JM |
28 |
|
c78b25
|
29 |
import com.gitblit.Constants.AccessPermission; |
bcc8a0
|
30 |
import com.gitblit.utils.StringUtils; |
8b70d5
|
31 |
import com.google.common.base.Joiner; |
bcc8a0
|
32 |
|
JM |
33 |
/** |
|
34 |
* Class that encapsulates a public SSH key and it's metadata. |
|
35 |
* |
|
36 |
* @author James Moger |
|
37 |
* |
|
38 |
*/ |
|
39 |
public class SshKey implements Serializable { |
|
40 |
|
|
41 |
private static final long serialVersionUID = 1L; |
|
42 |
|
|
43 |
private String rawData; |
|
44 |
|
|
45 |
private PublicKey publicKey; |
|
46 |
|
|
47 |
private String comment; |
|
48 |
|
|
49 |
private String fingerprint; |
|
50 |
|
4a6999
|
51 |
private String toString; |
JM |
52 |
|
c78b25
|
53 |
private AccessPermission permission; |
JM |
54 |
|
bcc8a0
|
55 |
public SshKey(String data) { |
8b70d5
|
56 |
// strip out line breaks (issue-571) |
JM |
57 |
this.rawData = Joiner.on("").join(data.replace("\r\n", "\n").split("\n")); |
c78b25
|
58 |
this.permission = AccessPermission.PUSH; |
bcc8a0
|
59 |
} |
JM |
60 |
|
|
61 |
public SshKey(PublicKey key) { |
|
62 |
this.publicKey = key; |
|
63 |
this.comment = ""; |
c78b25
|
64 |
this.permission = AccessPermission.PUSH; |
bcc8a0
|
65 |
} |
JM |
66 |
|
|
67 |
public PublicKey getPublicKey() { |
|
68 |
if (publicKey == null && rawData != null) { |
|
69 |
// instantiate the public key from the raw key data |
|
70 |
final String[] parts = rawData.split(" ", 3); |
|
71 |
if (comment == null && parts.length == 3) { |
|
72 |
comment = parts[2]; |
|
73 |
} |
|
74 |
final byte[] bin = Base64.decodeBase64(Constants.encodeASCII(parts[1])); |
|
75 |
try { |
d41034
|
76 |
publicKey = new ByteArrayBuffer(bin).getRawPublicKey(); |
bcc8a0
|
77 |
} catch (SshException e) { |
039686
|
78 |
throw new RuntimeException(e); |
bcc8a0
|
79 |
} |
JM |
80 |
} |
|
81 |
return publicKey; |
|
82 |
} |
|
83 |
|
|
84 |
public String getAlgorithm() { |
|
85 |
return getPublicKey().getAlgorithm(); |
|
86 |
} |
|
87 |
|
|
88 |
public String getComment() { |
|
89 |
if (comment == null && rawData != null) { |
|
90 |
// extract comment from the raw data |
|
91 |
final String[] parts = rawData.split(" ", 3); |
|
92 |
if (parts.length == 3) { |
|
93 |
comment = parts[2]; |
|
94 |
} |
|
95 |
} |
|
96 |
return comment; |
|
97 |
} |
|
98 |
|
|
99 |
public void setComment(String comment) { |
|
100 |
this.comment = comment; |
ad897d
|
101 |
if (rawData != null) { |
JM |
102 |
rawData = null; |
|
103 |
} |
bcc8a0
|
104 |
} |
JM |
105 |
|
c78b25
|
106 |
/** |
JM |
107 |
* Returns true if this key may be used to clone or fetch. |
|
108 |
* |
|
109 |
* @return true if this key can be used to clone or fetch |
|
110 |
*/ |
|
111 |
public boolean canClone() { |
|
112 |
return permission.atLeast(AccessPermission.CLONE); |
|
113 |
} |
|
114 |
|
|
115 |
/** |
|
116 |
* Returns true if this key may be used to push changes. |
|
117 |
* |
|
118 |
* @return true if this key can be used to push changes |
|
119 |
*/ |
|
120 |
public boolean canPush() { |
|
121 |
return permission.atLeast(AccessPermission.PUSH); |
|
122 |
} |
|
123 |
|
|
124 |
/** |
|
125 |
* Returns the access permission for the key. |
|
126 |
* |
|
127 |
* @return the access permission for the key |
|
128 |
*/ |
|
129 |
public AccessPermission getPermission() { |
|
130 |
return permission; |
|
131 |
} |
|
132 |
|
|
133 |
/** |
|
134 |
* Control the access permission assigned to this key. |
|
135 |
* |
|
136 |
* @param value |
|
137 |
*/ |
|
138 |
public void setPermission(AccessPermission value) throws IllegalArgumentException { |
|
139 |
List<AccessPermission> permitted = Arrays.asList(AccessPermission.SSHPERMISSIONS); |
|
140 |
if (!permitted.contains(value)) { |
|
141 |
throw new IllegalArgumentException("Illegal SSH public key permission specified: " + value); |
|
142 |
} |
|
143 |
this.permission = value; |
|
144 |
} |
|
145 |
|
bcc8a0
|
146 |
public String getRawData() { |
JM |
147 |
if (rawData == null && publicKey != null) { |
|
148 |
// build the raw data manually from the public key |
d41034
|
149 |
Buffer buf = new ByteArrayBuffer(); |
bcc8a0
|
150 |
|
JM |
151 |
// 1: identify the algorithm |
|
152 |
buf.putRawPublicKey(publicKey); |
|
153 |
String alg = buf.getString(); |
|
154 |
|
|
155 |
// 2: encode the key |
|
156 |
buf.clear(); |
|
157 |
buf.putPublicKey(publicKey); |
|
158 |
String b64 = Base64.encodeBase64String(buf.getBytes()); |
|
159 |
|
|
160 |
String c = getComment(); |
|
161 |
rawData = alg + " " + b64 + (StringUtils.isEmpty(c) ? "" : (" " + c)); |
|
162 |
} |
|
163 |
return rawData; |
|
164 |
} |
|
165 |
|
|
166 |
public String getFingerprint() { |
|
167 |
if (fingerprint == null) { |
|
168 |
StringBuilder sb = new StringBuilder(); |
|
169 |
// append the key hash as colon-separated pairs |
|
170 |
String hash; |
|
171 |
if (rawData != null) { |
|
172 |
final String[] parts = rawData.split(" ", 3); |
|
173 |
final byte [] bin = Base64.decodeBase64(Constants.encodeASCII(parts[1])); |
|
174 |
hash = StringUtils.getMD5(bin); |
|
175 |
} else { |
521cb6
|
176 |
// TODO calculate the correct hash from a PublicKey instance |
JM |
177 |
hash = StringUtils.getMD5(getPublicKey().getEncoded()); |
bcc8a0
|
178 |
} |
JM |
179 |
for (int i = 0; i < hash.length(); i += 2) { |
|
180 |
sb.append(hash.charAt(i)).append(hash.charAt(i + 1)).append(':'); |
|
181 |
} |
|
182 |
sb.setLength(sb.length() - 1); |
|
183 |
fingerprint = sb.toString(); |
|
184 |
} |
|
185 |
return fingerprint; |
|
186 |
} |
|
187 |
|
|
188 |
@Override |
|
189 |
public boolean equals(Object o) { |
|
190 |
if (o instanceof PublicKey) { |
|
191 |
return getPublicKey().equals(o); |
|
192 |
} else if (o instanceof SshKey) { |
|
193 |
return getPublicKey().equals(((SshKey) o).getPublicKey()); |
|
194 |
} |
|
195 |
return false; |
|
196 |
} |
|
197 |
|
|
198 |
@Override |
|
199 |
public int hashCode() { |
|
200 |
return getPublicKey().hashCode(); |
|
201 |
} |
|
202 |
|
|
203 |
@Override |
|
204 |
public String toString() { |
4a6999
|
205 |
if (toString == null) { |
JM |
206 |
StringBuilder sb = new StringBuilder(); |
|
207 |
// TODO append the keysize |
|
208 |
int keySize = 0; |
|
209 |
if (keySize > 0) { |
|
210 |
sb.append(keySize).append(' '); |
|
211 |
} |
|
212 |
// append fingerprint |
|
213 |
sb.append(' '); |
|
214 |
sb.append(getFingerprint()); |
|
215 |
// append the comment |
|
216 |
String c = getComment(); |
|
217 |
if (!StringUtils.isEmpty(c)) { |
|
218 |
sb.append(' '); |
|
219 |
sb.append(c); |
|
220 |
} |
|
221 |
// append algorithm |
|
222 |
String alg = getAlgorithm(); |
|
223 |
if (!StringUtils.isEmpty(alg)) { |
|
224 |
sb.append(" (").append(alg).append(")"); |
|
225 |
} |
|
226 |
toString = sb.toString(); |
|
227 |
} |
|
228 |
return toString; |
bcc8a0
|
229 |
} |
JM |
230 |
} |