44import java .io .BufferedReader ;
55import java .io .File ;
66import java .io .PrintWriter ;
7+ import java .io .OutputStreamWriter ;
78import java .io .FileNotFoundException ;
89import java .io .IOException ;
910import java .security .Key ;
1011import java .security .KeyPair ;
1112import java .security .KeyPairGenerator ;
1213import java .security .NoSuchAlgorithmException ;
1314import java .security .PublicKey ;
15+ import java .security .NoSuchProviderException ;
16+
17+ import org .bouncycastle .crypto .params .AsymmetricKeyParameter ;
18+ import org .bouncycastle .crypto .util .OpenSSHPrivateKeyUtil ;
19+ import org .bouncycastle .crypto .util .OpenSSHPublicKeyUtil ;
20+ import org .bouncycastle .crypto .util .PrivateKeyFactory ;
21+ import org .bouncycastle .crypto .util .PublicKeyFactory ;
1422
1523/* This file is part of SFTP-SAF, an Android app to access sftp servers via Storage access framework
1624 Copyright (C) 2025,2026 Alain Knaff
2230You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
2331*/
2432
25- import java .security .interfaces .RSAPublicKey ;
26- import java .nio .charset .StandardCharsets ;
2733import java .nio .ByteOrder ;
2834import java .nio .ByteBuffer ;
2935
@@ -43,62 +49,75 @@ public class Keygen {
4349 public static final String PRIVATE_KEY_FILE ="privateKey.pem" ;
4450 public static final String PUBLIC_KEY_FILE ="publicKey.txt" ;
4551
46- public static void genKey (Context ctx ) {
52+ public static void genKey (Context ctx , String algo ) {
4753 try {
48- // confirmation dialog if it already exists:
49- // https://stackoverflow.com/questions/5127407/how-to-implement-a-confirmation-yes-no-dialogpreference
54+ BouncyCastle .trigger ();
5055
51- KeyPairGenerator keyGen = KeyPairGenerator .getInstance ("RSA" );
52- keyGen .initialize (2048 );
56+ KeyPairGenerator keyGen = KeyPairGenerator .getInstance (algo ,"BC" );
5357 KeyPair keyPair = keyGen .generateKeyPair ();
54- saveKeyToFile (ctx , PRIVATE_KEY_FILE , convertToPEM (keyPair .getPrivate (), "PRIVATE" ));
55- saveKeyToFile (ctx , PUBLIC_KEY_FILE , getEncodedSshPublicKey ( (PublicKey ) keyPair .getPublic ()));
5658
59+ saveKeyToFile (ctx , PRIVATE_KEY_FILE ,
60+ convertToPEM (keyPair .getPrivate (),
61+ "RSA" .equals (algo )));
62+
63+ saveKeyToFile (ctx , PUBLIC_KEY_FILE ,
64+ getEncodedSshPublicKey (keyPair .getPublic ()));
65+ } catch (IOException e ) {
66+ Log .e (TAG , "Error generating keys: " + e .getMessage ());
67+ } catch (NoSuchProviderException e ) {
68+ Log .e (TAG , "Error generating keys: " + e .getMessage ());
5769 } catch (NoSuchAlgorithmException e ) {
5870 Log .e (TAG , "Error generating keys: " + e .getMessage ());
5971 }
6072 }
6173
62- private static void saveKeyToFile (Context ctx , String fileName , String key ) {
74+ private static void saveKeyToFile (Context ctx , String fileName , String key )
75+ throws IOException
76+ {
6377 try (PrintWriter pw = new PrintWriter (ctx .openFileOutput (fileName , 0 ))) {
6478 pw .println (key );
6579 System .out .println (fileName + " saved successfully." );
6680 } catch (IOException e ) {
6781 Log .e (TAG , "Error saving key to file: " + e .getMessage ());
82+ throw e ;
6883 }
6984 }
7085
7186 private static String toBase64 (byte [] bin ) {
7287 return Base64 .encodeToString (bin , Base64 .NO_WRAP );
7388 }
7489
75- public static String convertToPEM (Key key , String type ) {
76- byte [] encodedKey = key .getEncoded ();
90+ public static String convertToPEM (Key key , boolean isRsa )
91+ throws IOException
92+ {
93+ byte [] encodedKey ;
94+ String type ;
95+ if (isRsa ) {
96+ encodedKey = key .getEncoded ();
97+ type = "" ;
98+ } else {
99+ AsymmetricKeyParameter bprv =
100+ PrivateKeyFactory .createKey (key .getEncoded ());
101+ encodedKey = OpenSSHPrivateKeyUtil .encodePrivateKey (bprv );
102+ type ="OPENSSH " ;
103+ }
77104 String base64Key = toBase64 (encodedKey );
78- return "-----BEGIN " +type +" KEY-----\n " + base64Key + "\n -----END " +type +" KEY-----" ;
105+ return "-----BEGIN " +type +"PRIVATE KEY-----\n " + base64Key + "\n -----END " +type +"PRIVATE KEY-----" ;
79106 }
80107
81108 // see https://linuxtut.com/en/ee3c7d0ba7d4610a9d21/ for
82109 // outputting public key
83- public static String getEncodedSshPublicKey (final PublicKey pKey ) {
84- final String sig = "ssh-rsa" ;
85-
86- RSAPublicKey publicKey = (RSAPublicKey ) pKey ;
87- final byte [] sigBytes = sig .getBytes (StandardCharsets .US_ASCII );
88- final byte [] eBytes = publicKey .getPublicExponent ().toByteArray ();
89- final byte [] nBytes = publicKey .getModulus ().toByteArray ();
90-
91- final int size = 4 + sigBytes .length
92- + 4 + eBytes .length
93- + 4 + nBytes .length ;
94-
95- final byte [] publicKeyBytes = ByteBuffer .allocate (size )
96- .order (ByteOrder .BIG_ENDIAN )
97- .putInt (sigBytes .length ).put (sigBytes )
98- .putInt (eBytes .length ).put (eBytes )
99- .putInt (nBytes .length ).put (nBytes )
100- .array ();
101-
110+ public static String getEncodedSshPublicKey (final PublicKey pKey )
111+ throws IOException
112+ {
113+ AsymmetricKeyParameter bpub =
114+ PublicKeyFactory .createKey (pKey .getEncoded ());
115+ byte [] publicKeyBytes = OpenSSHPublicKeyUtil .encodePublicKey (bpub );
116+ int sigLen = ByteBuffer
117+ .wrap (publicKeyBytes )
118+ .order (ByteOrder .BIG_ENDIAN )
119+ .getInt ();
120+ final String sig = new String (publicKeyBytes , 4 , sigLen );
102121 final String publicKeyBase64 = toBase64 (publicKeyBytes );
103122
104123 final String publicKeyEncoded = sig + " " + publicKeyBase64 + " user@sftpprovider" ;
0 commit comments