|
127 | 127 | com.jcraft.jsch.IdentityFile 'newInstance [String String JSch]
|
128 | 128 | nil private-key-path public-key-path agent))
|
129 | 129 |
|
| 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 | + |
130 | 190 | (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. |
132 | 193 |
|
133 | 194 | :private-key A string specifying the private key
|
134 | 195 | :public-key A string specifying the public key
|
|
145 | 206 | ^bytes passphrase]
|
146 | 207 | :as options}]
|
147 | 208 | {: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))) |
203 | 215 |
|
204 | 216 | (defn add-identity-with-keychain
|
205 | 217 | "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:
|
920 | 932 |
|
921 | 933 | (defn generate-keypair
|
922 | 934 | "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]}] |
926 | 940 | (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)) |
928 | 947 | (let [pub-baos (ByteArrayOutputStream.)
|
929 | 948 | pri-baos (ByteArrayOutputStream.)]
|
930 | 949 | (.writePublicKey keypair pub-baos "")
|
|
0 commit comments