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