1 package org
.argeo
.cms
.ssh
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStreamReader
;
6 import java
.io
.StringReader
;
7 import java
.io
.StringWriter
;
8 import java
.nio
.charset
.StandardCharsets
;
9 import java
.nio
.file
.Files
;
10 import java
.nio
.file
.Path
;
11 import java
.nio
.file
.Paths
;
12 import java
.security
.GeneralSecurityException
;
13 import java
.security
.KeyFactory
;
14 import java
.security
.KeyPair
;
15 import java
.security
.PrivateKey
;
16 import java
.security
.PublicKey
;
17 import java
.security
.interfaces
.RSAPrivateCrtKey
;
18 import java
.security
.spec
.RSAPublicKeySpec
;
20 import org
.apache
.sshd
.common
.config
.keys
.KeyUtils
;
21 import org
.apache
.sshd
.common
.config
.keys
.PublicKeyEntry
;
22 import org
.argeo
.cms
.bc
.BcUtils
;
23 import org
.bouncycastle
.asn1
.pkcs
.PrivateKeyInfo
;
24 import org
.bouncycastle
.openssl
.PEMDecryptorProvider
;
25 import org
.bouncycastle
.openssl
.PEMEncryptedKeyPair
;
26 import org
.bouncycastle
.openssl
.PEMKeyPair
;
27 import org
.bouncycastle
.openssl
.PEMParser
;
28 import org
.bouncycastle
.openssl
.PKCS8Generator
;
29 import org
.bouncycastle
.openssl
.jcajce
.JcaPEMKeyConverter
;
30 import org
.bouncycastle
.openssl
.jcajce
.JcaPEMWriter
;
31 import org
.bouncycastle
.openssl
.jcajce
.JcaPKCS8Generator
;
32 import org
.bouncycastle
.openssl
.jcajce
.JceOpenSSLPKCS8DecryptorProviderBuilder
;
33 import org
.bouncycastle
.openssl
.jcajce
.JceOpenSSLPKCS8EncryptorBuilder
;
34 import org
.bouncycastle
.openssl
.jcajce
.JcePEMDecryptorProviderBuilder
;
35 import org
.bouncycastle
.operator
.InputDecryptorProvider
;
36 import org
.bouncycastle
.operator
.OutputEncryptor
;
37 import org
.bouncycastle
.pkcs
.PKCS8EncryptedPrivateKeyInfo
;
39 @SuppressWarnings("restriction")
40 public class SshKeyPair
{
41 public final static String RSA_KEY_TYPE
= "ssh-rsa";
43 private PublicKey publicKey
;
44 private PrivateKey privateKey
;
45 private KeyPair keyPair
;
47 public SshKeyPair(KeyPair keyPair
) {
49 this.publicKey
= keyPair
.getPublic();
50 this.privateKey
= keyPair
.getPrivate();
51 this.keyPair
= keyPair
;
54 public SshKeyPair(PublicKey publicKey
, PrivateKey privateKey
) {
56 this.publicKey
= publicKey
;
57 this.privateKey
= privateKey
;
58 this.keyPair
= new KeyPair(publicKey
, privateKey
);
61 public KeyPair
asKeyPair() {
65 public String
getPublicKeyAsOpenSshString() {
66 return PublicKeyEntry
.toString(publicKey
);
69 public String
getPrivateKeyAsPemString(char[] password
) {
73 if (password
!= null) {
74 JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder
= new JceOpenSSLPKCS8EncryptorBuilder(
75 PKCS8Generator
.PBE_SHA1_3DES
);
76 encryptorBuilder
.setPasssword(password
);
77 OutputEncryptor oe
= encryptorBuilder
.build();
78 JcaPKCS8Generator gen
= new JcaPKCS8Generator(privateKey
, oe
);
84 StringWriter sw
= new StringWriter();
85 JcaPEMWriter pemWrt
= new JcaPEMWriter(sw
);
86 pemWrt
.writeObject(obj
);
89 } catch (Exception e
) {
90 throw new RuntimeException("Cannot convert private key", e
);
94 public static SshKeyPair
loadOrGenerate(Path privateKeyPath
, int size
, char[] password
) {
96 SshKeyPair sshKeyPair
;
97 if (Files
.exists(privateKeyPath
)) {
98 // String privateKeyStr = new String(Files.readAllBytes(privateKeyPath), StandardCharsets.US_ASCII);
100 new InputStreamReader(Files
.newInputStream(privateKeyPath
), StandardCharsets
.US_ASCII
),
102 // TOD make sure public key is consistemt
104 sshKeyPair
= generate(size
);
105 Files
.write(privateKeyPath
,
106 sshKeyPair
.getPrivateKeyAsPemString(password
).getBytes(StandardCharsets
.US_ASCII
));
107 Path publicKeyPath
= privateKeyPath
.resolveSibling(privateKeyPath
.getFileName() + ".pub");
108 Files
.write(publicKeyPath
,
109 sshKeyPair
.getPublicKeyAsOpenSshString().getBytes(StandardCharsets
.US_ASCII
));
112 } catch (IOException e
) {
113 throw new RuntimeException("Cannot read or write private key " + privateKeyPath
, e
);
117 public static SshKeyPair
generate(int size
) {
118 return generate(RSA_KEY_TYPE
, size
);
121 public static SshKeyPair
generate(String keyType
, int size
) {
123 KeyPair keyPair
= KeyUtils
.generateKeyPair(keyType
, size
);
124 PublicKey publicKey
= keyPair
.getPublic();
125 PrivateKey privateKey
= keyPair
.getPrivate();
126 return new SshKeyPair(publicKey
, privateKey
);
127 } catch (GeneralSecurityException e
) {
128 throw new RuntimeException("Cannot generate SSH key", e
);
132 public static SshKeyPair
loadDefault(char[] password
) {
133 Path privateKeyPath
= Paths
.get(System
.getProperty("user.home") + "/.ssh/id_rsa");
134 // TODO try other formats
135 return load(privateKeyPath
, password
);
138 public static SshKeyPair
load(Path privateKeyPath
, char[] password
) {
139 try (Reader reader
= Files
.newBufferedReader(privateKeyPath
)) {
140 return load(reader
, password
);
141 } catch (IOException e
) {
142 throw new IllegalStateException("Cannot load private key from " + privateKeyPath
, e
);
147 public static SshKeyPair
load(Reader reader
, char[] password
) {
148 try (PEMParser pemParser
= new PEMParser(reader
)) {
149 Object object
= pemParser
.readObject();
150 JcaPEMKeyConverter converter
= new JcaPEMKeyConverter();// .setProvider("BC");
152 if (object
instanceof PEMEncryptedKeyPair
) {
153 PEMEncryptedKeyPair ekp
= (PEMEncryptedKeyPair
) object
;
154 JcePEMDecryptorProviderBuilder decryptorProviderBuilder
= new JcePEMDecryptorProviderBuilder();
155 decryptorProviderBuilder
.setProvider(BcUtils
.BC_SECURITY_PROVIDER
);
156 PEMDecryptorProvider decryptorProvider
= decryptorProviderBuilder
.build(password
);
157 PEMKeyPair pemKp
= ekp
.decryptKeyPair(decryptorProvider
);
158 kp
= converter
.getKeyPair(pemKp
);
159 } else if (object
instanceof PKCS8EncryptedPrivateKeyInfo
) {
160 // Encrypted key - we will use provided password
161 PKCS8EncryptedPrivateKeyInfo ckp
= (PKCS8EncryptedPrivateKeyInfo
) object
;
162 // PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password);
163 InputDecryptorProvider inputDecryptorProvider
= new JceOpenSSLPKCS8DecryptorProviderBuilder()
165 PrivateKeyInfo pkInfo
= ckp
.decryptPrivateKeyInfo(inputDecryptorProvider
);
166 PrivateKey privateKey
= converter
.getPrivateKey(pkInfo
);
168 // generate public key
169 RSAPrivateCrtKey privk
= (RSAPrivateCrtKey
) privateKey
;
170 RSAPublicKeySpec publicKeySpec
= new java
.security
.spec
.RSAPublicKeySpec(privk
.getModulus(),
171 privk
.getPublicExponent());
172 KeyFactory keyFactory
= KeyFactory
.getInstance("RSA");
173 PublicKey publicKey
= keyFactory
.generatePublic(publicKeySpec
);
175 kp
= new KeyPair(publicKey
, privateKey
);
177 // Unencrypted key - no password needed
178 // PKCS8EncryptedPrivateKeyInfo ukp = (PKCS8EncryptedPrivateKeyInfo) object;
179 PEMKeyPair pemKp
= (PEMKeyPair
) object
;
180 kp
= converter
.getKeyPair(pemKp
);
182 return new SshKeyPair(kp
);
183 } catch (Exception e
) {
184 throw new RuntimeException("Cannot load private key", e
);
188 public static void main(String args
[]) {
189 Path privateKeyPath
= Paths
.get(System
.getProperty("user.dir") + "/id_rsa");
190 SshKeyPair skp
= SshKeyPair
.loadOrGenerate(privateKeyPath
, 1024, null);
191 System
.out
.println("Public:\n" + skp
.getPublicKeyAsOpenSshString());
192 System
.out
.println("Private (plain):\n" + skp
.getPrivateKeyAsPemString(null));
193 System
.out
.println("Private (encrypted):\n" + skp
.getPrivateKeyAsPemString("demo".toCharArray()));
195 StringReader reader
= new StringReader(skp
.getPrivateKeyAsPemString(null));
196 skp
= SshKeyPair
.load(reader
, null);
197 System
.out
.println("Public:\n" + skp
.getPublicKeyAsOpenSshString());
198 System
.out
.println("Private (plain):\n" + skp
.getPrivateKeyAsPemString(null));
199 System
.out
.println("Private (encrypted):\n" + skp
.getPrivateKeyAsPemString("demo".toCharArray()));
201 reader
= new StringReader(skp
.getPrivateKeyAsPemString("demo".toCharArray()));
202 skp
= SshKeyPair
.load(reader
, "demo".toCharArray());
203 System
.out
.println("Public:\n" + skp
.getPublicKeyAsOpenSshString());
204 System
.out
.println("Private (plain):\n" + skp
.getPrivateKeyAsPemString(null));
205 System
.out
.println("Private (encrypted):\n" + skp
.getPrivateKeyAsPemString("demo".toCharArray()));