]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/SshKeyPair.java
Self-signed certificate with RSA 3072
[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.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;
38
39 @SuppressWarnings("restriction")
40 public class SshKeyPair {
41 public final static String RSA_KEY_TYPE = "ssh-rsa";
42
43 private PublicKey publicKey;
44 private PrivateKey privateKey;
45 private KeyPair keyPair;
46
47 public SshKeyPair(KeyPair keyPair) {
48 super();
49 this.publicKey = keyPair.getPublic();
50 this.privateKey = keyPair.getPrivate();
51 this.keyPair = keyPair;
52 }
53
54 public SshKeyPair(PublicKey publicKey, PrivateKey privateKey) {
55 super();
56 this.publicKey = publicKey;
57 this.privateKey = privateKey;
58 this.keyPair = new KeyPair(publicKey, privateKey);
59 }
60
61 public KeyPair asKeyPair() {
62 return keyPair;
63 }
64
65 public String getPublicKeyAsOpenSshString() {
66 return PublicKeyEntry.toString(publicKey);
67 }
68
69 public String getPrivateKeyAsPemString(char[] password) {
70 try {
71 Object obj;
72
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);
79 obj = gen.generate();
80 } else {
81 obj = privateKey;
82 }
83
84 StringWriter sw = new StringWriter();
85 JcaPEMWriter pemWrt = new JcaPEMWriter(sw);
86 pemWrt.writeObject(obj);
87 pemWrt.close();
88 return sw.toString();
89 } catch (Exception e) {
90 throw new RuntimeException("Cannot convert private key", e);
91 }
92 }
93
94 public static SshKeyPair loadOrGenerate(Path privateKeyPath, int size, char[] password) {
95 try {
96 SshKeyPair sshKeyPair;
97 if (Files.exists(privateKeyPath)) {
98 // String privateKeyStr = new String(Files.readAllBytes(privateKeyPath), StandardCharsets.US_ASCII);
99 sshKeyPair = load(
100 new InputStreamReader(Files.newInputStream(privateKeyPath), StandardCharsets.US_ASCII),
101 password);
102 // TOD make sure public key is consistemt
103 } else {
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));
110 }
111 return sshKeyPair;
112 } catch (IOException e) {
113 throw new RuntimeException("Cannot read or write private key " + privateKeyPath, e);
114 }
115 }
116
117 public static SshKeyPair generate(int size) {
118 return generate(RSA_KEY_TYPE, size);
119 }
120
121 public static SshKeyPair generate(String keyType, int size) {
122 try {
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);
129 }
130 }
131
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);
136 }
137
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);
143 }
144
145 }
146
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");
151 KeyPair kp;
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()
164 .build(password);
165 PrivateKeyInfo pkInfo = ckp.decryptPrivateKeyInfo(inputDecryptorProvider);
166 PrivateKey privateKey = converter.getPrivateKey(pkInfo);
167
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);
174
175 kp = new KeyPair(publicKey, privateKey);
176 } else {
177 // Unencrypted key - no password needed
178 // PKCS8EncryptedPrivateKeyInfo ukp = (PKCS8EncryptedPrivateKeyInfo) object;
179 PEMKeyPair pemKp = (PEMKeyPair) object;
180 kp = converter.getKeyPair(pemKp);
181 }
182 return new SshKeyPair(kp);
183 } catch (Exception e) {
184 throw new RuntimeException("Cannot load private key", e);
185 }
186 }
187
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()));
194
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()));
200
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()));
206 }
207
208 }