Skip to content

Commit 4e802ce

Browse files
Make KeyType compatible with Android Keystore (hierynomus#586)
* Make KeyType compatible with Android Keystore Android Keystore private keys do not implement PrivateKey since the raw key material is not available to applications. With this commit, sshj's KeyType correctly detects the algorithm associated with Android Keystore keys, which makes them usable for SSH authentication. * Extract RSA, DSA, ECDSA and EC into constants * Fix license lint issue Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
1 parent dfdc464 commit 4e802ce

12 files changed

Lines changed: 63 additions & 23 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (C)2009 - SSHJ Contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.hierynomus.sshj.common;
17+
18+
public class KeyAlgorithm {
19+
20+
public static final String RSA = "RSA";
21+
public static final String DSA = "DSA";
22+
23+
/** Elliptic curve signature key algorithm for use with BouncyCastle **/
24+
public static final String ECDSA = "ECDSA";
25+
26+
/** General elliptic curve algorithm identifier for use with BouncyCastle **/
27+
public static final String EC_BC = "EC";
28+
29+
/** General elliptic curve algorithm identifier for use with the Android Keystore **/
30+
public static final String EC_KEYSTORE = "EC";
31+
}

src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.hierynomus.sshj.userauth.keyprovider;
1717

18+
import com.hierynomus.sshj.common.KeyAlgorithm;
1819
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
1920
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
2021
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
@@ -216,7 +217,7 @@ private KeyPair readUnencrypted(final PlainBuffer keyBuffer, final PublicKey pub
216217
keyBuffer.readMPInt(); // iqmp (q^-1 mod p)
217218
keyBuffer.readMPInt(); // p (Prime 1)
218219
keyBuffer.readMPInt(); // q (Prime 2)
219-
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec(n, d)));
220+
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePrivate(new RSAPrivateKeySpec(n, d)));
220221
break;
221222
case ECDSA256:
222223
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-256"));
@@ -248,7 +249,7 @@ private PrivateKey createECDSAPrivateKey(KeyType kt, PlainBuffer buffer, String
248249
X9ECParameters ecParams = NISTNamedCurves.getByName(name);
249250
ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN());
250251
ECPrivateKeySpec pks = new ECPrivateKeySpec(s, ecCurveSpec);
251-
return SecurityUtils.getKeyFactory("ECDSA").generatePrivate(pks);
252+
return SecurityUtils.getKeyFactory(KeyAlgorithm.ECDSA).generatePrivate(pks);
252253

253254
}
254255
}

src/main/java/net/schmizz/sshj/common/ECDSAVariationsAdapter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package net.schmizz.sshj.common;
1717

18+
import com.hierynomus.sshj.common.KeyAlgorithm;
1819
import com.hierynomus.sshj.secg.SecgUtils;
1920
import org.bouncycastle.asn1.nist.NISTNamedCurves;
2021
import org.bouncycastle.asn1.x9.X9ECParameters;
@@ -87,7 +88,7 @@ static PublicKey readPubKeyFromBuffer(Buffer<?> buf, String variation) throws Ge
8788
ECPoint p = new ECPoint(bigX, bigY);
8889
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec);
8990

90-
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
91+
KeyFactory keyFactory = KeyFactory.getInstance(KeyAlgorithm.ECDSA);
9192
return keyFactory.generatePublic(publicKeySpec);
9293
} catch (Exception ex) {
9394
throw new GeneralSecurityException(ex);
@@ -103,7 +104,7 @@ static void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
103104
}
104105

105106
static boolean isECKeyWithFieldSize(Key key, int fieldSize) {
106-
return "ECDSA".equals(key.getAlgorithm())
107+
return (KeyAlgorithm.ECDSA.equals(key.getAlgorithm()) || KeyAlgorithm.EC_KEYSTORE.equals(key.getAlgorithm()))
107108
&& fieldSizeFromKey((ECKey) key) == fieldSize;
108109
}
109110

src/main/java/net/schmizz/sshj/common/KeyType.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package net.schmizz.sshj.common;
1717

18+
import com.hierynomus.sshj.common.KeyAlgorithm;
1819
import com.hierynomus.sshj.signature.Ed25519PublicKey;
1920
import com.hierynomus.sshj.userauth.certificate.Certificate;
2021
import net.i2p.crypto.eddsa.EdDSAPublicKey;
@@ -30,9 +31,7 @@
3031
import java.security.Key;
3132
import java.security.KeyFactory;
3233
import java.security.PublicKey;
33-
import java.security.interfaces.DSAPrivateKey;
3434
import java.security.interfaces.DSAPublicKey;
35-
import java.security.interfaces.RSAPrivateKey;
3635
import java.security.interfaces.RSAPublicKey;
3736
import java.security.spec.DSAPublicKeySpec;
3837
import java.security.spec.RSAPublicKeySpec;
@@ -53,7 +52,7 @@ public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
5352
} catch (Buffer.BufferException be) {
5453
throw new GeneralSecurityException(be);
5554
}
56-
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
55+
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
5756
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
5857
}
5958

@@ -66,7 +65,7 @@ protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
6665

6766
@Override
6867
protected boolean isMyType(Key key) {
69-
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
68+
return KeyAlgorithm.RSA.equals(key.getAlgorithm());
7069
}
7170
},
7271

@@ -84,7 +83,7 @@ public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
8483
} catch (Buffer.BufferException be) {
8584
throw new GeneralSecurityException(be);
8685
}
87-
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
86+
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.DSA);
8887
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
8988
}
9089

@@ -99,7 +98,7 @@ protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
9998

10099
@Override
101100
protected boolean isMyType(Key key) {
102-
return (key instanceof DSAPublicKey || key instanceof DSAPrivateKey);
101+
return KeyAlgorithm.DSA.equals(key.getAlgorithm());
103102
}
104103

105104
},

src/main/java/net/schmizz/sshj/transport/kex/Curve25519DH.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package net.schmizz.sshj.transport.kex;
1717

18+
import com.hierynomus.sshj.common.KeyAlgorithm;
1819
import net.schmizz.sshj.common.Factory;
1920
import net.schmizz.sshj.transport.random.Random;
2021
import org.bouncycastle.asn1.x9.X9ECParameters;
@@ -31,7 +32,7 @@ public class Curve25519DH extends DHBase {
3132
private byte[] secretKey;
3233

3334
public Curve25519DH() {
34-
super("ECDSA", "ECDH");
35+
super(KeyAlgorithm.ECDSA, "ECDH");
3536
}
3637

3738
@Override

src/main/java/net/schmizz/sshj/transport/kex/ECDH.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package net.schmizz.sshj.transport.kex;
1717

18+
import com.hierynomus.sshj.common.KeyAlgorithm;
1819
import net.schmizz.sshj.common.Factory;
1920
import net.schmizz.sshj.common.SecurityUtils;
2021
import net.schmizz.sshj.transport.random.Random;
@@ -54,7 +55,7 @@ public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) t
5455

5556
@Override
5657
public void computeK(byte[] f) throws GeneralSecurityException {
57-
KeyFactory keyFactory = SecurityUtils.getKeyFactory("EC");
58+
KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.EC_BC);
5859
ECPublicKeySpec keySpec = new ECPublicKeySpec(getDecoded(f, ecParameterSpec.getCurve()), ecParameterSpec);
5960
PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
6061
agreement.doPhase(yourPubKey, true);

src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package net.schmizz.sshj.transport.verification;
1717

18+
import com.hierynomus.sshj.common.KeyAlgorithm;
1819
import com.hierynomus.sshj.transport.verification.KnownHostMatchers;
1920
import net.schmizz.sshj.common.*;
2021
import org.slf4j.Logger;
@@ -239,7 +240,7 @@ public KnownHostEntry parseEntry(String line)
239240
final BigInteger e = new BigInteger(split[i++]);
240241
final BigInteger n = new BigInteger(split[i++]);
241242
try {
242-
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
243+
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
243244
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
244245
} catch (Exception ex) {
245246
log.error("Error reading entry `{}`, could not create key", line, ex);

src/main/java/net/schmizz/sshj/userauth/keyprovider/PKCS5KeyFile.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package net.schmizz.sshj.userauth.keyprovider;
1717

18+
import com.hierynomus.sshj.common.KeyAlgorithm;
1819
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
1920
import net.schmizz.sshj.common.Base64;
2021
import net.schmizz.sshj.common.ByteArrayUtils;
@@ -140,7 +141,7 @@ protected KeyPair readKeyPair()
140141
ASN1Data asn = new ASN1Data(data = decrypt(Base64.decode(sb.toString()), cipher, iv));
141142
switch (type) {
142143
case RSA: {
143-
KeyFactory factory = KeyFactory.getInstance("RSA");
144+
KeyFactory factory = KeyFactory.getInstance(KeyAlgorithm.RSA);
144145
asn.readNext();
145146
BigInteger modulus = asn.readNext();
146147
BigInteger pubExp = asn.readNext();
@@ -150,7 +151,7 @@ protected KeyPair readKeyPair()
150151
return new KeyPair(pubKey, prvKey);
151152
}
152153
case DSA: {
153-
KeyFactory factory = KeyFactory.getInstance("DSA");
154+
KeyFactory factory = KeyFactory.getInstance(KeyAlgorithm.DSA);
154155
asn.readNext();
155156
BigInteger p = asn.readNext();
156157
BigInteger q = asn.readNext();

src/main/java/net/schmizz/sshj/userauth/keyprovider/PuTTYKeyFile.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package net.schmizz.sshj.userauth.keyprovider;
1717

18+
import com.hierynomus.sshj.common.KeyAlgorithm;
1819
import net.schmizz.sshj.common.Base64;
1920
import net.schmizz.sshj.common.KeyType;
2021
import net.schmizz.sshj.userauth.password.PasswordUtils;
@@ -114,7 +115,7 @@ protected KeyPair readKeyPair() throws IOException {
114115

115116
final KeyFactory factory;
116117
try {
117-
factory = KeyFactory.getInstance("RSA");
118+
factory = KeyFactory.getInstance(KeyAlgorithm.RSA);
118119
} catch (NoSuchAlgorithmException s) {
119120
throw new IOException(s.getMessage(), s);
120121
}
@@ -141,7 +142,7 @@ protected KeyPair readKeyPair() throws IOException {
141142

142143
final KeyFactory factory;
143144
try {
144-
factory = KeyFactory.getInstance("DSA");
145+
factory = KeyFactory.getInstance(KeyAlgorithm.DSA);
145146
} catch (NoSuchAlgorithmException s) {
146147
throw new IOException(s.getMessage(), s);
147148
}

src/test/groovy/net/schmizz/sshj/signature/SignatureDSASpec.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
*/
3131
package net.schmizz.sshj.signature
3232

33+
import com.hierynomus.sshj.common.KeyAlgorithm
3334
import spock.lang.Unroll;
3435

3536
import java.math.BigInteger;
@@ -47,7 +48,7 @@ import spock.lang.Specification
4748

4849
class SignatureDSASpec extends Specification {
4950

50-
def keyFactory = KeyFactory.getInstance("DSA")
51+
def keyFactory = KeyFactory.getInstance(KeyAlgorithm.DSA)
5152

5253
private PublicKey createPublicKey(final byte[] y, final byte[] p, final byte[] q, final byte[] g) throws Exception {
5354
final BigInteger publicKey = new BigInteger(y);

0 commit comments

Comments
 (0)