From 436bd3f0ecdee282c503a9eb0f7a240b7a68ff49 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Fri, 11 Apr 2014 14:51:50 -0400
Subject: [PATCH] Merged #6 "Support serving repositories over the SSH transport"

---
 src/main/java/com/gitblit/transport/ssh/SshKey.java |  212 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 212 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/gitblit/transport/ssh/SshKey.java b/src/main/java/com/gitblit/transport/ssh/SshKey.java
new file mode 100644
index 0000000..6a20d7d
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/SshKey.java
@@ -0,0 +1,212 @@
+package com.gitblit.transport.ssh;
+
+import java.io.Serializable;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.Buffer;
+import org.eclipse.jgit.lib.Constants;
+
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Class that encapsulates a public SSH key and it's metadata.
+ *
+ * @author James Moger
+ *
+ */
+public class SshKey implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private String rawData;
+
+	private PublicKey publicKey;
+
+	private String comment;
+
+	private String fingerprint;
+
+	private String toString;
+
+	private AccessPermission permission;
+
+	public SshKey(String data) {
+		this.rawData = data;
+		this.permission = AccessPermission.PUSH;
+	}
+
+	public SshKey(PublicKey key) {
+		this.publicKey = key;
+		this.comment = "";
+		this.permission = AccessPermission.PUSH;
+	}
+
+	public PublicKey getPublicKey() {
+		if (publicKey == null && rawData != null) {
+			// instantiate the public key from the raw key data
+			final String[] parts = rawData.split(" ", 3);
+			if (comment == null && parts.length == 3) {
+				comment = parts[2];
+			}
+			final byte[] bin = Base64.decodeBase64(Constants.encodeASCII(parts[1]));
+			try {
+				publicKey = new Buffer(bin).getRawPublicKey();
+			} catch (SshException e) {
+				e.printStackTrace();
+			}
+		}
+		return publicKey;
+	}
+
+	public String getAlgorithm() {
+		return getPublicKey().getAlgorithm();
+	}
+
+	public String getComment() {
+		if (comment == null && rawData != null) {
+			// extract comment from the raw data
+			final String[] parts = rawData.split(" ", 3);
+			if (parts.length == 3) {
+				comment = parts[2];
+			}
+		}
+		return comment;
+	}
+
+	public void setComment(String comment) {
+		this.comment = comment;
+		if (rawData != null) {
+			rawData = null;
+		}
+	}
+
+	/**
+	 * Returns true if this key may be used to clone or fetch.
+	 *
+	 * @return true if this key can be used to clone or fetch
+	 */
+	public boolean canClone() {
+		return permission.atLeast(AccessPermission.CLONE);
+	}
+
+	/**
+	 * Returns true if this key may be used to push changes.
+	 *
+	 * @return true if this key can be used to push changes
+	 */
+	public boolean canPush() {
+		return permission.atLeast(AccessPermission.PUSH);
+	}
+
+	/**
+	 * Returns the access permission for the key.
+	 *
+	 * @return the access permission for the key
+	 */
+	public AccessPermission getPermission() {
+		return permission;
+	}
+
+	/**
+	 * Control the access permission assigned to this key.
+	 *
+	 * @param value
+	 */
+	public void setPermission(AccessPermission value) throws IllegalArgumentException {
+		List<AccessPermission> permitted = Arrays.asList(AccessPermission.SSHPERMISSIONS);
+		if (!permitted.contains(value)) {
+			throw new IllegalArgumentException("Illegal SSH public key permission specified: " + value);
+		}
+		this.permission = value;
+	}
+
+	public String getRawData() {
+		if (rawData == null && publicKey != null) {
+			// build the raw data manually from the public key
+			Buffer buf = new Buffer();
+
+			// 1: identify the algorithm
+			buf.putRawPublicKey(publicKey);
+			String alg = buf.getString();
+
+			// 2: encode the key
+			buf.clear();
+			buf.putPublicKey(publicKey);
+			String b64 = Base64.encodeBase64String(buf.getBytes());
+
+			String c = getComment();
+			rawData = alg + " " + b64 + (StringUtils.isEmpty(c) ? "" : (" " + c));
+		}
+		return rawData;
+	}
+
+	public String getFingerprint() {
+		if (fingerprint == null) {
+			StringBuilder sb = new StringBuilder();
+			// append the key hash as colon-separated pairs
+			String hash;
+			if (rawData != null) {
+				final String[] parts = rawData.split(" ", 3);
+				final byte [] bin = Base64.decodeBase64(Constants.encodeASCII(parts[1]));
+				hash = StringUtils.getMD5(bin);
+			} else {
+				// TODO calculate the correct hash from a PublicKey instance
+				hash = StringUtils.getMD5(getPublicKey().getEncoded());
+			}
+			for (int i = 0; i < hash.length(); i += 2) {
+				sb.append(hash.charAt(i)).append(hash.charAt(i + 1)).append(':');
+			}
+			sb.setLength(sb.length() - 1);
+			fingerprint = sb.toString();
+		}
+		return fingerprint;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (o instanceof PublicKey) {
+			return getPublicKey().equals(o);
+		} else if (o instanceof SshKey) {
+			return getPublicKey().equals(((SshKey) o).getPublicKey());
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		return getPublicKey().hashCode();
+	}
+
+	@Override
+	public String toString() {
+		if (toString == null) {
+			StringBuilder sb = new StringBuilder();
+			// TODO append the keysize
+			int keySize = 0;
+			if (keySize > 0) {
+				sb.append(keySize).append(' ');
+			}
+			// append fingerprint
+			sb.append(' ');
+			sb.append(getFingerprint());
+			// append the comment
+			String c = getComment();
+			if (!StringUtils.isEmpty(c)) {
+				sb.append(' ');
+				sb.append(c);
+			}
+			// append algorithm
+			String alg = getAlgorithm();
+			if (!StringUtils.isEmpty(alg)) {
+				sb.append(" (").append(alg).append(")");
+			}
+			toString = sb.toString();
+		}
+		return toString;
+	}
+}

--
Gitblit v1.9.1