James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
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 }