]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/SshKeyPair.java
Workaround Krb5LoginModule printing to System.out when tryFirstPass is
[lgpl/argeo-commons.git] / org.argeo.cms.lib.sshd / src / org / argeo / cms / ssh / SshKeyPair.java
1 package org.argeo.cms.ssh;
2
3 import java.io.IOException;
4 import java.io.InputStreamReader;
5 import java.io.Reader;
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;
19
20 import org.apache.sshd.common.config.keys.KeyUtils;
21 import org.apache.sshd.common.config.keys.PublicKeyEntry;
22 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
23 import org.bouncycastle.openssl.PEMDecryptorProvider;
24 import org.bouncycastle.openssl.PEMEncryptedKeyPair;
25 import org.bouncycastle.openssl.PEMKeyPair;
26 import org.bouncycastle.openssl.PEMParser;
27 import org.bouncycastle.openssl.PKCS8Generator;
28 import org.bouncycastle.openssl.bc.BcPEMDecryptorProvider;
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.operator.InputDecryptorProvider;
35 import org.bouncycastle.operator.OutputEncryptor;
36 import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
37
38 @SuppressWarnings("restriction")
39 public class SshKeyPair {
40 public final static String RSA_KEY_TYPE = "ssh-rsa";
41
42 private PublicKey publicKey;
43 private PrivateKey privateKey;
44 private KeyPair keyPair;
45
46 public SshKeyPair(KeyPair keyPair) {
47 super();
48 this.publicKey = keyPair.getPublic();
49 this.privateKey = keyPair.getPrivate();
50 this.keyPair = keyPair;
51 }
52
53 public SshKeyPair(PublicKey publicKey, PrivateKey privateKey) {
54 super();
55 this.publicKey = publicKey;
56 this.privateKey = privateKey;
57 this.keyPair = new KeyPair(publicKey, privateKey);
58 }
59
60 public KeyPair asKeyPair() {
61 return keyPair;
62 }
63
64 public String getPublicKeyAsOpenSshString() {
65 return PublicKeyEntry.toString(publicKey);
66 }
67
68 public String getPrivateKeyAsPemString(char[] password) {
69 try {
70 Object obj;
71
72 if (password != null) {
73 JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(
74 PKCS8Generator.PBE_SHA1_3DES);
75 encryptorBuilder.setPasssword(password);
76 OutputEncryptor oe = encryptorBuilder.build();
77 JcaPKCS8Generator gen = new JcaPKCS8Generator(privateKey, oe);
78 obj = gen.generate();
79 } else {
80 obj = privateKey;
81 }
82
83 StringWriter sw = new StringWriter();
84 JcaPEMWriter pemWrt = new JcaPEMWriter(sw);
85 pemWrt.writeObject(obj);
86 pemWrt.close();
87 return sw.toString();
88 } catch (Exception e) {
89 throw new RuntimeException("Cannot convert private key", e);
90 }
91 }
92
93 public static SshKeyPair loadOrGenerate(Path privateKeyPath, int size, char[] password) {
94 try {
95 SshKeyPair sshKeyPair;
96 if (Files.exists(privateKeyPath)) {
97 // String privateKeyStr = new String(Files.readAllBytes(privateKeyPath), StandardCharsets.US_ASCII);
98 sshKeyPair = load(
99 new InputStreamReader(Files.newInputStream(privateKeyPath), StandardCharsets.US_ASCII),
100 password);
101 // TOD make sure public key is consistemt
102 } else {
103 sshKeyPair = generate(size);
104 Files.write(privateKeyPath,
105 sshKeyPair.getPrivateKeyAsPemString(password).getBytes(StandardCharsets.US_ASCII));
106 Path publicKeyPath = privateKeyPath.resolveSibling(privateKeyPath.getFileName() + ".pub");
107 Files.write(publicKeyPath,
108 sshKeyPair.getPublicKeyAsOpenSshString().getBytes(StandardCharsets.US_ASCII));
109 }
110 return sshKeyPair;
111 } catch (IOException e) {
112 throw new RuntimeException("Cannot read or write private key " + privateKeyPath, e);
113 }
114 }
115
116 public static SshKeyPair generate(int size) {
117 return generate(RSA_KEY_TYPE, size);
118 }
119
120 public static SshKeyPair generate(String keyType, int size) {
121 try {
122 KeyPair keyPair = KeyUtils.generateKeyPair(keyType, size);
123 PublicKey publicKey = keyPair.getPublic();
124 PrivateKey privateKey = keyPair.getPrivate();
125 return new SshKeyPair(publicKey, privateKey);
126 } catch (GeneralSecurityException e) {
127 throw new RuntimeException("Cannot generate SSH key", e);
128 }
129 }
130
131 public static SshKeyPair loadDefault(char[] password) {
132 Path privateKeyPath = Paths.get(System.getProperty("user.home") + "/.ssh/id_rsa");
133 // TODO try other formats
134 return load(privateKeyPath, password);
135 }
136
137 public static SshKeyPair load(Path privateKeyPath, char[] password) {
138 try (Reader reader = Files.newBufferedReader(privateKeyPath)) {
139 return load(reader, password);
140 } catch (IOException e) {
141 throw new IllegalStateException("Cannot load private key from " + privateKeyPath, e);
142 }
143
144 }
145
146 public static SshKeyPair load(Reader reader, char[] password) {
147 try (PEMParser pemParser = new PEMParser(reader)) {
148 Object object = pemParser.readObject();
149 JcaPEMKeyConverter converter = new JcaPEMKeyConverter();// .setProvider("BC");
150 KeyPair kp;
151 if (object instanceof PEMEncryptedKeyPair) {
152 PEMEncryptedKeyPair ekp = (PEMEncryptedKeyPair) object;
153 PEMDecryptorProvider decryptorProvider = new BcPEMDecryptorProvider(password);
154 PEMKeyPair pemKp = ekp.decryptKeyPair(decryptorProvider);
155 kp = converter.getKeyPair(pemKp);
156 } else if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
157 // Encrypted key - we will use provided password
158 PKCS8EncryptedPrivateKeyInfo ckp = (PKCS8EncryptedPrivateKeyInfo) object;
159 // PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password);
160 InputDecryptorProvider inputDecryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder()
161 .build(password);
162 PrivateKeyInfo pkInfo = ckp.decryptPrivateKeyInfo(inputDecryptorProvider);
163 PrivateKey privateKey = converter.getPrivateKey(pkInfo);
164
165 // generate public key
166 RSAPrivateCrtKey privk = (RSAPrivateCrtKey) privateKey;
167 RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(),
168 privk.getPublicExponent());
169 KeyFactory keyFactory = KeyFactory.getInstance("RSA");
170 PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
171
172 kp = new KeyPair(publicKey, privateKey);
173 } else {
174 // Unencrypted key - no password needed
175 // PKCS8EncryptedPrivateKeyInfo ukp = (PKCS8EncryptedPrivateKeyInfo) object;
176 PEMKeyPair pemKp = (PEMKeyPair) object;
177 kp = converter.getKeyPair(pemKp);
178 }
179 return new SshKeyPair(kp);
180 } catch (Exception e) {
181 throw new RuntimeException("Cannot load private key", e);
182 }
183 }
184
185 public static void main(String args[]) {
186 Path privateKeyPath = Paths.get(System.getProperty("user.dir") + "/id_rsa");
187 SshKeyPair skp = SshKeyPair.loadOrGenerate(privateKeyPath, 1024, null);
188 System.out.println("Public:\n" + skp.getPublicKeyAsOpenSshString());
189 System.out.println("Private (plain):\n" + skp.getPrivateKeyAsPemString(null));
190 System.out.println("Private (encrypted):\n" + skp.getPrivateKeyAsPemString("demo".toCharArray()));
191
192 StringReader reader = new StringReader(skp.getPrivateKeyAsPemString(null));
193 skp = SshKeyPair.load(reader, null);
194 System.out.println("Public:\n" + skp.getPublicKeyAsOpenSshString());
195 System.out.println("Private (plain):\n" + skp.getPrivateKeyAsPemString(null));
196 System.out.println("Private (encrypted):\n" + skp.getPrivateKeyAsPemString("demo".toCharArray()));
197
198 reader = new StringReader(skp.getPrivateKeyAsPemString("demo".toCharArray()));
199 skp = SshKeyPair.load(reader, "demo".toCharArray());
200 System.out.println("Public:\n" + skp.getPublicKeyAsOpenSshString());
201 System.out.println("Private (plain):\n" + skp.getPrivateKeyAsPemString(null));
202 System.out.println("Private (encrypted):\n" + skp.getPrivateKeyAsPemString("demo".toCharArray()));
203 }
204
205 }