SSH key pair management.
[lgpl/argeo-commons.git] / org.argeo.core / src / org / argeo / ssh / SshKeyPair.java
diff --git a/org.argeo.core/src/org/argeo/ssh/SshKeyPair.java b/org.argeo.core/src/org/argeo/ssh/SshKeyPair.java
new file mode 100644 (file)
index 0000000..8feca58
--- /dev/null
@@ -0,0 +1,146 @@
+package org.argeo.ssh;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.PKCS8Generator;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
+import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
+import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
+import org.bouncycastle.operator.InputDecryptorProvider;
+import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+
+@SuppressWarnings("restriction")
+public class SshKeyPair {
+       private PublicKey publicKey;
+       private PrivateKey privateKey;
+       private KeyPair keyPair;
+
+       public SshKeyPair(KeyPair keyPair) {
+               super();
+               this.publicKey = keyPair.getPublic();
+               this.privateKey = keyPair.getPrivate();
+               this.keyPair = keyPair;
+       }
+
+       public SshKeyPair(PublicKey publicKey, PrivateKey privateKey) {
+               super();
+               this.publicKey = publicKey;
+               this.privateKey = privateKey;
+               this.keyPair = new KeyPair(publicKey, privateKey);
+       }
+
+       public KeyPair asKeyPair() {
+               return keyPair;
+       }
+
+       public String getPublicKeyAsOpenSshString() {
+               return PublicKeyEntry.toString(publicKey);
+       }
+
+       public String getPrivateKeyAsString(char[] password) {
+               try {
+                       Object obj;
+
+                       if (password != null) {
+                               JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(
+                                               PKCS8Generator.PBE_SHA1_3DES);
+                               encryptorBuilder.setPasssword(password);
+                               OutputEncryptor oe = encryptorBuilder.build();
+                               JcaPKCS8Generator gen = new JcaPKCS8Generator(privateKey, oe);
+                               obj = gen.generate();
+                       } else {
+                               obj = privateKey;
+                       }
+
+                       StringWriter sw = new StringWriter();
+                       JcaPEMWriter pemWrt = new JcaPEMWriter(sw);
+                       pemWrt.writeObject(obj);
+                       pemWrt.close();
+                       return sw.toString();
+               } catch (Exception e) {
+                       throw new RuntimeException("Cannot convert private key", e);
+               }
+       }
+
+       public static SshKeyPair generate(int size) {
+               try {
+                       KeyPair keyPair = KeyUtils.generateKeyPair("ssh-rsa", size);
+                       PublicKey publicKey = keyPair.getPublic();
+                       PrivateKey privateKey = keyPair.getPrivate();
+                       return new SshKeyPair(publicKey, privateKey);
+               } catch (GeneralSecurityException e) {
+                       throw new RuntimeException("Cannot generate SSH key", e);
+               }
+       }
+
+       public static SshKeyPair load(Reader reader, char[] password) {
+               try (PEMParser pemParser = new PEMParser(reader)) {
+                       Object object = pemParser.readObject();
+                       JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+                       KeyPair kp;
+                       if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
+                               // Encrypted key - we will use provided password
+                               PKCS8EncryptedPrivateKeyInfo ckp = (PKCS8EncryptedPrivateKeyInfo) object;
+//                             PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password);
+                               InputDecryptorProvider inputDecryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder()
+                                               .build(password);
+                               PrivateKeyInfo pkInfo = ckp.decryptPrivateKeyInfo(inputDecryptorProvider);
+                               PrivateKey privateKey = converter.getPrivateKey(pkInfo);
+
+                               // generate public key
+                               RSAPrivateCrtKey privk = (RSAPrivateCrtKey) privateKey;
+                               RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(),
+                                               privk.getPublicExponent());
+                               KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+                               PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
+
+                               kp = new KeyPair(publicKey, privateKey);
+                       } else {
+                               // Unencrypted key - no password needed
+//                             PKCS8EncryptedPrivateKeyInfo ukp = (PKCS8EncryptedPrivateKeyInfo) object;
+                               PEMKeyPair pemKp = (PEMKeyPair) object;
+                               kp = converter.getKeyPair(pemKp);
+                       }
+                       return new SshKeyPair(kp);
+               } catch (Exception e) {
+                       throw new RuntimeException("Cannot load private key", e);
+               }
+       }
+
+       public static void main(String args[]) {
+               SshKeyPair okp = SshKeyPair.generate(1024);
+               System.out.println("Public:\n" + okp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + okp.getPrivateKeyAsString(null));
+               System.out.println("Private (encrypted):\n" + okp.getPrivateKeyAsString("demo".toCharArray()));
+
+               StringReader reader = new StringReader(okp.getPrivateKeyAsString(null));
+               okp = SshKeyPair.load(reader, null);
+               System.out.println("Public:\n" + okp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + okp.getPrivateKeyAsString(null));
+               System.out.println("Private (encrypted):\n" + okp.getPrivateKeyAsString("demo".toCharArray()));
+
+               reader = new StringReader(okp.getPrivateKeyAsString("demo".toCharArray()));
+               okp = SshKeyPair.load(reader, "demo".toCharArray());
+               System.out.println("Public:\n" + okp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + okp.getPrivateKeyAsString(null));
+               System.out.println("Private (encrypted):\n" + okp.getPrivateKeyAsString("demo".toCharArray()));
+       }
+
+}