fix Ed25519 key decoding
This commit is contained in:
parent
dea43eee45
commit
4c9fe8c72b
2 changed files with 203 additions and 180 deletions
|
@ -1,3 +1,3 @@
|
|||
group = org.xbib
|
||||
name = net
|
||||
version = 4.6.0
|
||||
version = 4.6.1
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package org.xbib.net.security;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
@ -16,13 +13,9 @@ import java.security.PrivateKey;
|
|||
import java.security.PublicKey;
|
||||
import java.security.interfaces.DSAParams;
|
||||
import java.security.interfaces.DSAPrivateKey;
|
||||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.EdECPrivateKey;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.DSAPrivateKeySpec;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.ECField;
|
||||
|
@ -46,9 +39,6 @@ import java.security.spec.RSAPublicKeySpec;
|
|||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.EncryptedPrivateKeyInfo;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
@ -60,64 +50,35 @@ import org.xbib.net.security.util.DerParser;
|
|||
import org.xbib.net.security.util.DerUtils;
|
||||
|
||||
/**
|
||||
* Key reader for reading private keys from inputstreams, PEM formatted text etc. with PKCS#8 or PKCS#1 encodings.
|
||||
* Best effort key reader for reading private keys from inputstreams,
|
||||
* PEM formatted text etc. with PKCS#8 or PKCS#1 encodings.
|
||||
* It doesn't support encrypted PEM files.
|
||||
*/
|
||||
public class PrivateKeyReader {
|
||||
|
||||
private static final byte[] BEGIN_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_PRIVATE_KEY = "-----END PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] BEGIN_RSA_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_RSA_PRIVATE_KEY = "-----END RSA PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] BEGIN_DSA_PRIVATE_KEY = "-----BEGIN DSA PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_DSA_PRIVATE_KEY = "-----END DSA PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] BEGIN_EC_PRIVATE_KEY = "-----BEGIN EC PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_EC_PRIVATE_KEY = "-----END EC PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] BEGIN_OPENSSH_PRIVATE_KEY = "-----BEGIN OPENSSH PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_OPENSSH_PRIVATE_KEY = "-----END OPENSSH PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
public PrivateKeyReader() {
|
||||
}
|
||||
|
||||
public KeySpec parse(InputStream inputStream,
|
||||
String password)
|
||||
public KeySpec readPrivateKeySpec(InputStream inputStream,
|
||||
String password)
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException,
|
||||
InvalidAlgorithmParameterException, NoSuchPaddingException, InvalidKeyException {
|
||||
Objects.requireNonNull(inputStream);
|
||||
byte[] key = inputStream.readAllBytes();
|
||||
if (indexOf(key, BEGIN_PRIVATE_KEY,0, key.length) >= 0) {
|
||||
byte[] keyBytes = extract(key, BEGIN_PRIVATE_KEY, END_PRIVATE_KEY);
|
||||
return getKeySpec(keyBytes, password != null ? password.toCharArray() : null);
|
||||
} else if (indexOf(key, BEGIN_RSA_PRIVATE_KEY,0, key.length) >= 0) {
|
||||
byte[] keyBytes = extract(key, BEGIN_RSA_PRIVATE_KEY, END_RSA_PRIVATE_KEY);
|
||||
return getRSAKeySpec(keyBytes);
|
||||
} else if (indexOf(key, BEGIN_DSA_PRIVATE_KEY,0, key.length) >= 0) {
|
||||
byte[] keyBytes = extract(key, BEGIN_DSA_PRIVATE_KEY, END_DSA_PRIVATE_KEY);
|
||||
return getDSAKeySpec(keyBytes);
|
||||
} else if (indexOf(key, BEGIN_EC_PRIVATE_KEY,0, key.length) >= 0) {
|
||||
byte[] keyBytes = extract(key, BEGIN_EC_PRIVATE_KEY, END_EC_PRIVATE_KEY);
|
||||
return getECKeySpec(keyBytes);
|
||||
} else if (indexOf(key, BEGIN_OPENSSH_PRIVATE_KEY,0, key.length) >= 0) {
|
||||
byte[] keyBytes = extract(key, BEGIN_OPENSSH_PRIVATE_KEY, END_OPENSSH_PRIVATE_KEY);
|
||||
return getEdECKeySpec(keyBytes);
|
||||
}
|
||||
throw new IOException("invalid PEM input stream");
|
||||
RawKey rawKey = readKeyBytes(inputStream);
|
||||
return switch (rawKey.keyType) {
|
||||
case PKCS8 -> getPkcs8KeySpec(rawKey.raw, password != null ? password.toCharArray() : null);
|
||||
case RSA -> getRSAKeySpec(rawKey.raw);
|
||||
case DSA -> getDSAKeySpec(rawKey.raw);
|
||||
case EC -> getECKeySpec(rawKey.raw);
|
||||
case ED25519 -> getEdECKeySpec(rawKey.raw);
|
||||
case UNKNOWN -> throw new InvalidKeySpecException();
|
||||
};
|
||||
}
|
||||
|
||||
public PrivateKey readPrivateKey(InputStream inputStream,
|
||||
String password)
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException,
|
||||
InvalidAlgorithmParameterException, NoSuchPaddingException, InvalidKeyException {
|
||||
KeySpec keySpec = parse(inputStream, password);
|
||||
KeySpec keySpec = readPrivateKeySpec(inputStream, password);
|
||||
if (keySpec instanceof EncodedKeySpec) {
|
||||
return KeyFactory.getInstance("RSA").generatePrivate(keySpec);
|
||||
} else if (keySpec instanceof RSAPrivateCrtKeySpec) {
|
||||
|
@ -136,29 +97,37 @@ public class PrivateKeyReader {
|
|||
String password)
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException,
|
||||
InvalidAlgorithmParameterException, NoSuchPaddingException, InvalidKeyException {
|
||||
KeySpec keySpec = parse(inputStream, password);
|
||||
RawKey rawKey = readKeyBytes(inputStream);
|
||||
KeySpec keySpec = switch (rawKey.keyType) {
|
||||
case PKCS8 -> getPkcs8KeySpec(rawKey.raw, password != null ? password.toCharArray() : null);
|
||||
case RSA -> getRSAKeySpec(rawKey.raw);
|
||||
case DSA -> getDSAKeySpec(rawKey.raw);
|
||||
case EC -> getECKeySpec(rawKey.raw);
|
||||
case ED25519 -> getEdECKeySpec(rawKey.raw);
|
||||
case UNKNOWN -> throw new InvalidKeySpecException();
|
||||
};
|
||||
PrivateKey privateKey = null;
|
||||
PublicKey publicKey = null;
|
||||
if (keySpec instanceof EncodedKeySpec) {
|
||||
if (keySpec instanceof DSAPrivateKeySpec) {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
||||
privateKey = keyFactory.generatePrivate(keySpec);
|
||||
publicKey = keyFactory.generatePublic(getDSAPublicKeySpec(privateKey));
|
||||
} else if (keySpec instanceof EncodedKeySpec) {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
privateKey = keyFactory.generatePrivate(keySpec);
|
||||
publicKey = getRSAPublicKey(keyFactory, privateKey);
|
||||
publicKey = keyFactory.generatePublic(getRSAPublicKeySpec(keyFactory, privateKey));
|
||||
} else if (keySpec instanceof RSAPrivateCrtKeySpec) {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
privateKey = keyFactory.generatePrivate(keySpec);
|
||||
publicKey = getRSAPublicKey(keyFactory, privateKey);
|
||||
publicKey = keyFactory.generatePublic(getRSAPublicKeySpec(keyFactory, privateKey));
|
||||
} else if (keySpec instanceof ECPrivateKeySpec) {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
||||
privateKey = keyFactory.generatePrivate(keySpec);
|
||||
publicKey = getECPublicKey(keyFactory, privateKey);
|
||||
} else if (keySpec instanceof EdECPrivateKeySpec) {
|
||||
publicKey = keyFactory.generatePublic(getECPublicKeySpec(privateKey));
|
||||
} else {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EdDSA");
|
||||
privateKey = keyFactory.generatePrivate(keySpec);
|
||||
publicKey = getEdECPublicKey(keyFactory, privateKey);
|
||||
} else if (keySpec instanceof DSAPrivateKeySpec) {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
||||
privateKey = keyFactory.generatePrivate(keySpec);
|
||||
publicKey = getDSAPublicKey(keyFactory, privateKey);
|
||||
publicKey = keyFactory.generatePublic(getEdECPublicKeySpec(privateKey, rawKey.raw));
|
||||
}
|
||||
if (publicKey != null) {
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
|
@ -166,30 +135,19 @@ public class PrivateKeyReader {
|
|||
throw new IOException("invalid PEM");
|
||||
}
|
||||
|
||||
public static PrivateKey toPrivateKey(InputStream keyInputStream,
|
||||
String keyPassword)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
||||
InvalidAlgorithmParameterException, KeyException, IOException {
|
||||
if (keyInputStream == null) {
|
||||
return null;
|
||||
private static PKCS8EncodedKeySpec getPkcs8KeySpec(byte[] key, char[] password)
|
||||
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
||||
InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
if (password == null) {
|
||||
return new PKCS8EncodedKeySpec(key);
|
||||
}
|
||||
return getPrivateKey(readPrivateKey(keyInputStream), keyPassword);
|
||||
}
|
||||
|
||||
public static PrivateKey getPrivateKey(byte[] key,
|
||||
String keyPassword)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
||||
InvalidAlgorithmParameterException, KeyException, IOException {
|
||||
PKCS8EncodedKeySpec encodedKeySpec =
|
||||
getKeySpec(key, keyPassword == null ? null : keyPassword.toCharArray());
|
||||
for (String keyType : KEY_TYPES) {
|
||||
try {
|
||||
return KeyFactory.getInstance(keyType).generatePrivate(encodedKeySpec);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
// ignore exception
|
||||
}
|
||||
}
|
||||
throw new InvalidKeySpecException("no key worked: " + Arrays.asList(KEY_TYPES));
|
||||
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
|
||||
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
|
||||
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
|
||||
return encryptedPrivateKeyInfo.getKeySpec(cipher);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,6 +194,15 @@ public class PrivateKeyReader {
|
|||
return new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp, prime1, prime2, exp1, exp2, crtCoef);
|
||||
}
|
||||
|
||||
private static RSAPublicKeySpec getRSAPublicKeySpec(KeyFactory keyFactory, PrivateKey key)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
if (!(key instanceof RSAPrivateKey)) {
|
||||
throw new InvalidKeyException("Private key is not RSA private key");
|
||||
}
|
||||
RSAPrivateKeySpec rsaPrivateKeySpec = keyFactory.getKeySpec(key, RSAPrivateKeySpec.class);
|
||||
return new RSAPublicKeySpec(rsaPrivateKeySpec.getModulus(), BigInteger.valueOf(65537));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read DSA key in PKCS#1 spec.
|
||||
*
|
||||
|
@ -259,6 +226,19 @@ public class PrivateKeyReader {
|
|||
return new DSAPrivateKeySpec(prv, p, q, g);
|
||||
}
|
||||
|
||||
private static DSAPublicKeySpec getDSAPublicKeySpec(PrivateKey key) throws InvalidKeyException {
|
||||
if (!(key instanceof DSAPrivateKey dsaPrivateKey)) {
|
||||
throw new InvalidKeyException("Private key is not DSA private key");
|
||||
}
|
||||
DSAParams dsaParams = dsaPrivateKey.getParams();
|
||||
BigInteger g = dsaParams.getG();
|
||||
BigInteger p = dsaParams.getP();
|
||||
BigInteger q = dsaParams.getQ();
|
||||
BigInteger y = dsaParams.getG().modPow(dsaPrivateKey.getX(), dsaParams.getP());
|
||||
return new DSAPublicKeySpec(y, p, q, g);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read EC private key in PKCS#1 format.
|
||||
*
|
||||
|
@ -311,6 +291,39 @@ public class PrivateKeyReader {
|
|||
}
|
||||
}
|
||||
|
||||
private static ECPublicKeySpec getECPublicKeySpec(PrivateKey key)
|
||||
throws InvalidKeyException {
|
||||
if (!(key instanceof ECPrivateKey ecPrivateKey)) {
|
||||
throw new InvalidKeyException("Private key is not EC private key");
|
||||
}
|
||||
ECParameterSpec ecParameterSpec = ecPrivateKey.getParams();
|
||||
ECPoint ecPoint = scalarPointMultiplication(ecParameterSpec.getCurve(),
|
||||
ecParameterSpec.getOrder(), ecParameterSpec.getGenerator(), ecPrivateKey.getS());
|
||||
return new ECPublicKeySpec(ecPoint, ecParameterSpec);
|
||||
}
|
||||
|
||||
private static EdECPrivateKeySpec getEdECKeySpec(byte[] keyBytes) throws InvalidKeySpecException {
|
||||
OpenSsh openSsh = decodeFromOpenSsh(keyBytes);
|
||||
NamedParameterSpec params = NamedParameterSpec.ED25519;
|
||||
return new EdECPrivateKeySpec(params, openSsh.privatekey);
|
||||
}
|
||||
|
||||
private static EdECPublicKeySpec getEdECPublicKeySpec(PrivateKey key, byte[] keyBytes)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
if (!(key instanceof EdECPrivateKey edECPrivateKey)) {
|
||||
throw new InvalidKeyException("Private key is not EdEC private key");
|
||||
}
|
||||
OpenSsh openSsh = decodeFromOpenSsh(keyBytes);
|
||||
byte[] arr = openSsh.publickey;
|
||||
byte msb = arr[arr.length - 1];
|
||||
boolean xOdd = (msb & 0x80) != 0;
|
||||
arr[arr.length - 1] &= (byte) 0x7F;
|
||||
reverse(arr);
|
||||
BigInteger y = new BigInteger(1, arr);
|
||||
EdECPoint edECPoint = new EdECPoint(xOdd, y);
|
||||
return new EdECPublicKeySpec(edECPrivateKey.getParams(), edECPoint);
|
||||
}
|
||||
|
||||
private static final Curve SECP256R1 = initializeCurve(
|
||||
"secp256r1 [NIST P-256, X9.62 prime256v1]",
|
||||
"1.2.840.10045.3.1.7",
|
||||
|
@ -378,79 +391,6 @@ public class PrivateKeyReader {
|
|||
return new BigInteger(s, 16);
|
||||
}
|
||||
|
||||
private EdECPrivateKeySpec getEdECKeySpec(byte[] keySpec) {
|
||||
NamedParameterSpec params = NamedParameterSpec.ED25519;
|
||||
byte[] sk = Arrays.copyOfRange(keySpec, 0, 32);
|
||||
return new EdECPrivateKeySpec(params, sk);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec getKeySpec(byte[] key, char[] password)
|
||||
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
||||
InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
if (password == null) {
|
||||
return new PKCS8EncodedKeySpec(key);
|
||||
}
|
||||
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
|
||||
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
|
||||
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
|
||||
return encryptedPrivateKeyInfo.getKeySpec(cipher);
|
||||
}
|
||||
|
||||
private static DSAPublicKey getDSAPublicKey(KeyFactory keyFactory, PrivateKey key)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
if (!(key instanceof DSAPrivateKey dsaPrivateKey)) {
|
||||
throw new InvalidKeyException("Private key is not DSA private key");
|
||||
}
|
||||
DSAParams dsaParams = dsaPrivateKey.getParams();
|
||||
BigInteger g = dsaParams.getG();
|
||||
BigInteger p = dsaParams.getP();
|
||||
BigInteger q = dsaParams.getQ();
|
||||
BigInteger y = dsaParams.getG().modPow(dsaPrivateKey.getX(), dsaParams.getP());
|
||||
DSAPublicKeySpec spec = new DSAPublicKeySpec(y, p, q, g);
|
||||
return DSAPublicKey.class.cast(keyFactory.generatePublic(spec));
|
||||
}
|
||||
|
||||
private static RSAPublicKey getRSAPublicKey(KeyFactory keyFactory, PrivateKey key)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
if (!(key instanceof RSAPrivateKey)) {
|
||||
throw new InvalidKeyException("Private key is not RSA private key");
|
||||
}
|
||||
RSAPrivateKeySpec rsaPrivateKeySpec = keyFactory.getKeySpec(key, RSAPrivateKeySpec.class);
|
||||
RSAPublicKeySpec spec = new RSAPublicKeySpec(rsaPrivateKeySpec.getModulus(), BigInteger.valueOf(65537));
|
||||
return RSAPublicKey.class.cast(keyFactory.generatePublic(spec));
|
||||
}
|
||||
|
||||
private static ECPublicKey getECPublicKey(KeyFactory keyFactory, PrivateKey key)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
if (!(key instanceof ECPrivateKey ecPrivateKey)) {
|
||||
throw new InvalidKeyException("Private key is not EC private key");
|
||||
}
|
||||
ECParameterSpec ecParameterSpec = ecPrivateKey.getParams();
|
||||
ECPoint ecPoint = scalarPointMultiplication(ecParameterSpec.getCurve(),
|
||||
ecParameterSpec.getOrder(), ecParameterSpec.getGenerator(), ecPrivateKey.getS());
|
||||
ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
|
||||
return ECPublicKey.class.cast(keyFactory.generatePublic(spec));
|
||||
}
|
||||
|
||||
private static EdECPublicKey getEdECPublicKey(KeyFactory keyFactory, PrivateKey key)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
if (!(key instanceof EdECPrivateKey edECPrivateKey)) {
|
||||
throw new InvalidKeyException("Private key is not EdEC private key");
|
||||
}
|
||||
byte[] arr = edECPrivateKey.getEncoded();
|
||||
byte msb = arr[arr.length - 1];
|
||||
boolean xOdd = (msb & 0x80) != 0;
|
||||
arr[arr.length - 1] &= (byte) 0x7F;
|
||||
reverse(arr);
|
||||
BigInteger y = new BigInteger(1, arr);
|
||||
EdECPoint edECPoint = new EdECPoint(xOdd, y);
|
||||
EdECPublicKeySpec publicKeySpec = new EdECPublicKeySpec(edECPrivateKey.getParams(), edECPoint);
|
||||
return EdECPublicKey.class.cast(keyFactory.generatePublic(publicKeySpec));
|
||||
}
|
||||
|
||||
private static void reverse(byte [] arr) {
|
||||
int i = 0;
|
||||
int j = arr.length - 1;
|
||||
|
@ -467,6 +407,33 @@ public class PrivateKeyReader {
|
|||
arr[j] = tmp;
|
||||
}
|
||||
|
||||
private static class RawKey {
|
||||
enum KeyType { UNKNOWN, PKCS8, DSA, RSA, EC, ED25519 };
|
||||
KeyType keyType;
|
||||
byte[] raw;
|
||||
RawKey(KeyType keyType, byte[] raw) {
|
||||
this.keyType = keyType;
|
||||
this.raw = raw;
|
||||
}
|
||||
}
|
||||
|
||||
private RawKey readKeyBytes(InputStream inputStream) throws IOException {
|
||||
Objects.requireNonNull(inputStream);
|
||||
byte[] rawByteArray = inputStream.readAllBytes();
|
||||
if (indexOf(rawByteArray, BEGIN_PRIVATE_KEY,0, rawByteArray.length) >= 0) {
|
||||
return new RawKey(RawKey.KeyType.PKCS8, decodeFromBase64(rawByteArray, BEGIN_PRIVATE_KEY, END_PRIVATE_KEY));
|
||||
} else if (indexOf(rawByteArray, BEGIN_RSA_PRIVATE_KEY,0, rawByteArray.length) >= 0) {
|
||||
return new RawKey(RawKey.KeyType.RSA, decodeFromBase64(rawByteArray, BEGIN_RSA_PRIVATE_KEY, END_RSA_PRIVATE_KEY));
|
||||
} else if (indexOf(rawByteArray, BEGIN_DSA_PRIVATE_KEY,0, rawByteArray.length) >= 0) {
|
||||
return new RawKey(RawKey.KeyType.DSA, decodeFromBase64(rawByteArray, BEGIN_DSA_PRIVATE_KEY, END_DSA_PRIVATE_KEY));
|
||||
} else if (indexOf(rawByteArray, BEGIN_EC_PRIVATE_KEY,0, rawByteArray.length) >= 0) {
|
||||
return new RawKey(RawKey.KeyType.EC, decodeFromBase64(rawByteArray, BEGIN_EC_PRIVATE_KEY, END_EC_PRIVATE_KEY));
|
||||
} else if (indexOf(rawByteArray, BEGIN_OPENSSH_PRIVATE_KEY,0, rawByteArray.length) >= 0) {
|
||||
return new RawKey(RawKey.KeyType.ED25519, decodeFromBase64(rawByteArray, BEGIN_OPENSSH_PRIVATE_KEY, END_OPENSSH_PRIVATE_KEY));
|
||||
}
|
||||
return new RawKey(RawKey.KeyType.UNKNOWN, rawByteArray);
|
||||
}
|
||||
|
||||
private static int indexOf(byte[] array, byte[] target, int start, int end) {
|
||||
if (target.length == 0) {
|
||||
return 0;
|
||||
|
@ -483,7 +450,7 @@ public class PrivateKeyReader {
|
|||
return -1;
|
||||
}
|
||||
|
||||
private static byte[] extract(byte[] array, byte[] b1, byte[] b2) {
|
||||
private static byte[] decodeFromBase64(byte[] array, byte[] b1, byte[] b2) {
|
||||
int i1 = indexOf(array, b1, 0, array.length);
|
||||
if (i1 < 0) {
|
||||
throw new IllegalArgumentException("unable to extract: not found");
|
||||
|
@ -498,23 +465,58 @@ public class PrivateKeyReader {
|
|||
return Base64.getMimeDecoder().decode(b);
|
||||
}
|
||||
|
||||
private static final String[] KEY_TYPES = {
|
||||
"RSA", "DSA", "EC", "EdDSA"
|
||||
};
|
||||
|
||||
private static final Pattern KEY_PATTERN =
|
||||
Pattern.compile("-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" +
|
||||
"([a-z0-9+/=\\r\\n]+)" + "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static byte[] readPrivateKey(InputStream inputStream) throws KeyException, IOException {
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.US_ASCII))) {
|
||||
String string = bufferedReader.lines().collect(Collectors.joining(System.lineSeparator()));
|
||||
Matcher m = KEY_PATTERN.matcher(string);
|
||||
if (!m.find()) {
|
||||
throw new KeyException("could not find a PKCS #8 private key in input stream");
|
||||
}
|
||||
return Base64.getMimeDecoder().decode(m.group(1));
|
||||
private static OpenSsh decodeFromOpenSsh(byte[] rawKey) throws InvalidKeySpecException {
|
||||
OpenSsh openSsh = new OpenSsh();
|
||||
openSsh.verify = Arrays.copyOfRange(rawKey, 0, OPENSSH_KEY_V1.length());
|
||||
if (!new String(openSsh.verify).equals(OPENSSH_KEY_V1)) {
|
||||
throw new InvalidKeySpecException("invalid OpenSSH key file");
|
||||
}
|
||||
boolean occurred = false;
|
||||
int index = 0;
|
||||
for (int i = 0; i < rawKey.length; i++) {
|
||||
if (rawKey[i] == 's'
|
||||
&& rawKey[i + 1] == 's'
|
||||
&& rawKey[i + 2] == 'h'
|
||||
&& rawKey[i + 3] == '-'
|
||||
&& rawKey[i + 4] == 'e'
|
||||
&& rawKey[i + 5] == 'd'
|
||||
&& rawKey[i + 6] == '2'
|
||||
&& rawKey[i + 7] == '5'
|
||||
&& rawKey[i + 8] == '5'
|
||||
&& rawKey[i + 9] == '1'
|
||||
&& rawKey[i + 10] == '9'
|
||||
&& rawKey[i + 11] == 0x00
|
||||
&& rawKey[i + 12] == 0x00
|
||||
&& rawKey[i + 13] == 0x00
|
||||
&& rawKey[i + 14] == ' ') {
|
||||
index = i + 15;
|
||||
if (occurred) {
|
||||
break;
|
||||
}
|
||||
occurred = true;
|
||||
}
|
||||
}
|
||||
openSsh.publickey = Arrays.copyOfRange(rawKey, index, index + 32);
|
||||
index += 32;
|
||||
for (int i = index; i < rawKey.length; i++) {
|
||||
if (rawKey[i] == 0x00
|
||||
&& rawKey[i + 1] == 0x00
|
||||
&& rawKey[i + 2] == 0x00
|
||||
&& rawKey[i + 3] == '@') {
|
||||
index = i + 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
openSsh.privatekey = Arrays.copyOfRange(rawKey, index, index + 32);
|
||||
return openSsh;
|
||||
}
|
||||
|
||||
private static final String OPENSSH_KEY_V1 = "openssh-key-v1";
|
||||
|
||||
private static class OpenSsh {
|
||||
byte[] verify;
|
||||
byte[] publickey;
|
||||
byte[] privatekey;
|
||||
}
|
||||
|
||||
private static ECPoint scalarPointMultiplication(EllipticCurve curve,
|
||||
|
@ -531,7 +533,7 @@ public class PrivateKeyReader {
|
|||
return ecPointP;
|
||||
}
|
||||
ECPoint ecPointR = ECPoint.POINT_INFINITY;
|
||||
for (int i = (scalar.bitLength()) - 1; i >= 0; i--) {
|
||||
for (int i = scalar.bitLength() - 1; i >= 0; i--) {
|
||||
ecPointR = doublePoint(curve, ecPointR);
|
||||
if (scalar.testBit(i)) {
|
||||
ecPointR = addPoint(curve, ecPointR, ecPointP);
|
||||
|
@ -587,9 +589,30 @@ public class PrivateKeyReader {
|
|||
return ((lambda.multiply(xp.subtract(xr)).mod(p)).subtract(yp)).mod(p);
|
||||
}
|
||||
|
||||
private static final BigInteger ZERO = BigInteger.ZERO;
|
||||
private static final BigInteger ONE = BigInteger.ONE;
|
||||
|
||||
private static final BigInteger TWO = ONE.add(ONE);
|
||||
|
||||
private static final BigInteger THREE = TWO.add(ONE);
|
||||
|
||||
private static final byte[] BEGIN_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_PRIVATE_KEY = "-----END PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] BEGIN_RSA_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_RSA_PRIVATE_KEY = "-----END RSA PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] BEGIN_DSA_PRIVATE_KEY = "-----BEGIN DSA PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_DSA_PRIVATE_KEY = "-----END DSA PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] BEGIN_EC_PRIVATE_KEY = "-----BEGIN EC PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_EC_PRIVATE_KEY = "-----END EC PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] BEGIN_OPENSSH_PRIVATE_KEY = "-----BEGIN OPENSSH PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final byte[] END_OPENSSH_PRIVATE_KEY = "-----END OPENSSH PRIVATE KEY-----".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue