Skip to content

Commit d2b9605

Browse files
committed
Merge branch 'release/0.5.6'
2 parents 267b528 + ecde971 commit d2b9605

File tree

5 files changed

+96
-64
lines changed

5 files changed

+96
-64
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Thanks to [Ryan Stradling](http://github.com/rstradling) for these.
143143
Via [clojars](http://clojars.org) and
144144
[Leiningen](http://github.com/technomancy/leiningen).
145145

146-
:dependencies [clj-ssh "0.5.5"]
146+
:dependencies [clj-ssh "0.5.6"]
147147

148148
or your favourite maven repository aware tool.
149149

ReleaseNotes.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# Release Notes
22

3-
Current release is 0.5.5
3+
## 0.5.6
4+
5+
- Allow generate-keypair to write key files
6+
7+
- Factor out keypair generation
8+
Simplifies add-identity by factoring out the keypair creation.
9+
10+
- Update to jsch 0.1.50
411

512
## 0.5.5
613

project.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(defproject clj-ssh "0.5.5"
1+
(defproject clj-ssh "0.5.6"
22
:description "Library for using SSH from clojure."
33
:url "https://github.com/hugoduncan/clj-ssh"
44
:license {:name "Eclipse Public License"
@@ -12,4 +12,4 @@
1212
[com.jcraft/jsch.agentproxy.pageant "0.0.5"]
1313
[com.jcraft/jsch.agentproxy.core "0.0.5"]
1414
[com.jcraft/jsch.agentproxy.jsch "0.0.5"]
15-
[com.jcraft/jsch "0.1.49"]])
15+
[com.jcraft/jsch "0.1.50"]])

src/clj_ssh/ssh.clj

Lines changed: 79 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,69 @@
127127
com.jcraft.jsch.IdentityFile 'newInstance [String String JSch]
128128
nil private-key-path public-key-path agent))
129129

130+
(defn ^KeyPair keypair
131+
"Return a KeyPair object for the given options.
132+
133+
:private-key A string specifying the private key
134+
:public-key A string specifying the public key
135+
:private-key-path A string specifying a path to the private key
136+
:public-key-path A string specifying a path to the public key
137+
:passphrase A byte array containing the passphrase
138+
:comment A comment for the key"
139+
[^JSch agent {:keys [^String public-key
140+
^String private-key
141+
^String public-key-path
142+
^String private-key-path
143+
^String comment
144+
^bytes passphrase]
145+
:as options}]
146+
{:pre [(map? options)]}
147+
(cond
148+
private-key
149+
(let [^KeyPair keypair
150+
(KeyPair/load agent (as-bytes private-key) (as-bytes public-key))]
151+
(when passphrase
152+
(.decrypt keypair passphrase))
153+
(.setPublicKeyComment keypair comment)
154+
keypair)
155+
156+
(and public-key-path private-key-path)
157+
(let [keypair (KeyPair/load agent private-key-path public-key-path)]
158+
(when passphrase
159+
(.decrypt keypair passphrase))
160+
(.setPublicKeyComment keypair comment)
161+
keypair)
162+
163+
private-key-path
164+
(let [keypair (KeyPair/load agent private-key-path)]
165+
(when passphrase
166+
(.decrypt keypair passphrase))
167+
(.setPublicKeyComment keypair comment)
168+
keypair)
169+
170+
:else
171+
(throw
172+
(ex-info
173+
"Don't know how to create keypair"
174+
{:reason :do-not-know-how-to-create-keypair
175+
:args options}))))
176+
177+
;; JSch's IdentityFile has a private constructor that would let us avoid this
178+
;; were it public.
179+
(deftype KeyPairIdentity [^JSch jsch ^String identity ^KeyPair kpair]
180+
Identity
181+
(^boolean setPassphrase [_ ^bytes passphrase] (.. kpair (decrypt passphrase)))
182+
(getPublicKeyBlob [_] (.. kpair getPublicKeyBlob))
183+
(^bytes getSignature [_ ^bytes data] (.. kpair (getSignature data)))
184+
(getAlgName [_]
185+
(String. (reflect/call-method KeyPair 'getKeyTypeName [] kpair)))
186+
(getName [_] identity)
187+
(isEncrypted [_] (.. kpair isEncrypted))
188+
(clear [_] (.. kpair dispose)))
189+
130190
(defn add-identity
131-
"Add an identity to the agent.
191+
"Add an identity to the agent. The identity is passed with the :identity
192+
keyword argument, or constructed from the other keyword arguments.
132193
133194
:private-key A string specifying the private key
134195
:public-key A string specifying the public key
@@ -145,61 +206,12 @@
145206
^bytes passphrase]
146207
:as options}]
147208
{:pre [(map? options)]}
148-
(let [name (or name private-key-path public-key)
149-
id-repository (fn []
150-
(reflect/call-method
151-
com.jcraft.jsch.JSch 'getIdentityRepository []
152-
agent))
153-
local-repo? (fn [id-repo]
154-
;; LocalIdentityRepository is not public, so we can't use
155-
;; instance?
156-
(= "com.jcraft.jsch.LocalIdentityRepository"
157-
(.getName ^Class (type id-repo))))]
158-
(cond
159-
identity
160-
(.addIdentity agent identity passphrase)
161-
162-
private-key
163-
(let [^com.jcraft.jsch.IdentityRepository id-repo (id-repository)]
164-
(if (local-repo? id-repo)
165-
(.addIdentity
166-
agent name (as-bytes private-key) (as-bytes public-key) passphrase)
167-
(let [^KeyPair keypair
168-
(KeyPair/load
169-
agent (as-bytes private-key) (as-bytes public-key))]
170-
(when passphrase
171-
(.decrypt keypair passphrase))
172-
(.setPublicKeyComment keypair "Added by clj-ssh")
173-
(.add id-repo (.forSSHAgent keypair)))))
174-
175-
(and public-key-path private-key-path)
176-
(let [^com.jcraft.jsch.IdentityRepository id-repo (id-repository)]
177-
(if (local-repo? id-repo)
178-
(.addIdentity
179-
agent
180-
(file-path private-key-path) (file-path public-key-path) passphrase)
181-
(let [keypair (KeyPair/load agent private-key-path public-key-path)]
182-
(when passphrase
183-
(.decrypt keypair passphrase))
184-
(.setPublicKeyComment keypair name)
185-
(.add id-repo (.forSSHAgent keypair)))))
186-
187-
private-key-path
188-
(let [^com.jcraft.jsch.IdentityRepository id-repo (id-repository)]
189-
(if (local-repo? id-repo)
190-
(.addIdentity agent (file-path private-key-path) passphrase)
191-
(let [keypair (KeyPair/load agent private-key-path)]
192-
(when passphrase
193-
(.decrypt keypair passphrase))
194-
(.setPublicKeyComment keypair name)
195-
(.add id-repo (.forSSHAgent keypair)))))
196-
197-
:else
198-
(throw
199-
(ex-info
200-
"Don't know how to add identity"
201-
{:reason :do-not-know-how-to-add-identity
202-
:args options})))))
209+
(let [^String comment (or name private-key-path public-key)
210+
^Identity identity
211+
(or identity
212+
(KeyPairIdentity.
213+
agent comment (keypair agent (assoc options :comment comment))))]
214+
(.addIdentity agent identity passphrase)))
203215

204216
(defn add-identity-with-keychain
205217
"Add a private key, only if not already known, using the keychain to obtain
@@ -920,11 +932,18 @@ cmd specifies a command to exec. Valid commands are:
920932

921933
(defn generate-keypair
922934
"Generate a keypair, returned as [private public] byte arrays.
923-
Valid types are :rsa and :dsa. key-size is in bytes. passphrase
924-
can be a string or byte array."
925-
[agent key-type key-size passphrase]
935+
Valid types are :rsa and :dsa. key-size is in bytes. passphrase can be a
936+
string or byte array. Optionally writes the keypair to the paths specified
937+
using the :private-key-path and :public-key-path keys."
938+
[agent key-type key-size passphrase
939+
& {:keys [comment private-key-path public-key-path]}]
926940
(let [keypair (KeyPair/genKeyPair agent (key-type key-types) key-size)]
927-
(when passphrase (.setPassphrase keypair passphrase))
941+
(when passphrase
942+
(.setPassphrase keypair passphrase))
943+
(when public-key-path
944+
(.writePublicKey keypair public-key-path comment))
945+
(when private-key-path
946+
(.writePrivateKey keypair private-key-path))
928947
(let [pub-baos (ByteArrayOutputStream.)
929948
pri-baos (ByteArrayOutputStream.)]
930949
(.writePublicKey keypair pub-baos "")

test/clj_ssh/ssh_test.clj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,12 @@
497497
(test-scp-to-with session)
498498
(test-scp-from-with session)))))
499499

500+
(deftest keypair-test
501+
(let [agent (ssh-agent {:use-system-ssh-agent false})]
502+
(is (keypair agent {:private-key-path (private-key-path)}))
503+
(is (keypair agent {:private-key-path (private-key-path)
504+
:public-key-path (public-key-path)}))))
505+
500506
(deftest generate-keypair-test
501507
(let [agent (ssh-agent {:use-system-ssh-agent false})]
502508
(let [[priv pub] (generate-keypair agent :rsa 1024 "hello")]

0 commit comments

Comments
 (0)