From 4c9fe8c72bbd59f2755ed62f1ce0ec034bc70928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Mon, 27 May 2024 22:36:49 +0200 Subject: [PATCH] fix Ed25519 key decoding --- gradle.properties | 2 +- .../xbib/net/security/PrivateKeyReader.java | 381 ++++++++++-------- 2 files changed, 203 insertions(+), 180 deletions(-) diff --git a/gradle.properties b/gradle.properties index 4c1562c..73fc6e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group = org.xbib name = net -version = 4.6.0 +version = 4.6.1 diff --git a/net-security/src/main/java/org/xbib/net/security/PrivateKeyReader.java b/net-security/src/main/java/org/xbib/net/security/PrivateKeyReader.java index f1830b6..dbacce6 100644 --- a/net-security/src/main/java/org/xbib/net/security/PrivateKeyReader.java +++ b/net-security/src/main/java/org/xbib/net/security/PrivateKeyReader.java @@ -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); + }