使用openssh公钥(ecdsa-sha2-nistp256)和Java Security

时间:2017-06-28 16:34:47

标签: java openssh jce elliptic-curve ecdsa

是否有Java库/示例在Java中读取jss PublicKey的openssh格式ecdsa公钥?我想将EC用于JWT

我正在尝试阅读的格式符合authorized_keys或Github API(例如https://api.github.com/users/davidcarboni/keys):ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK8hPtB72/sfYgNw1WTska2DNOJFx+QhUxuV6OLINSD2ty+6gxcM8yZrvMqWdMePGRb2cGh8L/0bGOk+64IQ/pM=

我找到了这个答案,这对RSA和DSS来说很好: Using public key from authorized_keys with Java security,以及对ECDSA的openssh格式的讨论:https://security.stackexchange.com/questions/129910/ecdsa-why-do-ssh-keygen-and-java-generated-public-keys-have-different-sizes

然而,我迷失了尝试调整ECDSA的RSS / DSA代码 - 我不知道如何设置ECPublicKeySpec。它需要ECPointEllipticCurveECParameterSpecECField。 openssh格式只包含两个整数,这对ECPoint有意义,但我不知道如何设置其余的。

我一直在寻找一堆图书馆,包括jschsshjssh-tools和古老的Bouncycastle。我最接近的是:

com.jcraft.jsch.KeyPair load = com.jcraft.jsch.KeyPair.load(jsch, null, bytes[openSshKey]);

加载密钥很好,但不会让我进入JCE PublicKey - 只是byte[] getPublicKeyBlob()方法。

我错过了一些明显的东西吗?

2 个答案:

答案 0 :(得分:1)

为了完整性,这是我已经使用过的代码。它几乎是纯粹的JCE,在帮助器方法中添加了Bouncycastle(这更新了Using public key from authorized_keys with Java security中的示例代码):

...
        } else if (type.startsWith("ecdsa-sha2-") &&
                (type.endsWith("nistp256") || type.endsWith("nistp384") || type.endsWith("nistp521"))) {
            // Based on RFC 5656, section 3.1 (https://tools.ietf.org/html/rfc5656#section-3.1)
            String identifier = decodeType();
            BigInteger q = decodeBigInt();
            ECPoint ecPoint = getECPoint(q, identifier);
            ECParameterSpec ecParameterSpec = getECParameterSpec(identifier);
            ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
            return KeyFactory.getInstance("EC").generatePublic(spec);
        } ...

/**
 * Provides a means to get from a parsed Q value to the X and Y point values.
 * that can be used to create and ECPoint compatible with ECPublicKeySpec.
 *
 * @param q          According to RFC 5656:
 *                   "Q is the public key encoded from an elliptic curve point into an octet string"
 * @param identifier According to RFC 5656:
 *                   "The string [identifier] is the identifier of the elliptic curve domain parameters."
 * @return An ECPoint suitable for creating a JCE ECPublicKeySpec.
 */
ECPoint getECPoint(BigInteger q, String identifier) {
    String name = identifier.replace("nist", "sec") + "r1";
    ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(name);
    org.bouncycastle.math.ec.ECPoint point = ecSpec.getCurve().decodePoint(q.toByteArray());
    BigInteger x = point.getAffineXCoord().toBigInteger();
    BigInteger y = point.getAffineYCoord().toBigInteger();
    System.out.println("BC x = " + x);
    System.out.println("BC y = " + y);
    return new ECPoint(x, y);
}

/**
 * Gets the curve parameters for the given key type identifier.
 *
 * @param identifier According to RFC 5656:
 *                   "The string [identifier] is the identifier of the elliptic curve domain parameters."
 * @return An ECParameterSpec suitable for creating a JCE ECPublicKeySpec.
 */
ECParameterSpec getECParameterSpec(String identifier) {
    try {
        // http://www.bouncycastle.org/wiki/pages/viewpage.action?pageId=362269#SupportedCurves(ECDSAandECGOST)-NIST(aliasesforSECcurves)
        String name = identifier.replace("nist", "sec") + "r1";
        AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
        parameters.init(new ECGenParameterSpec(name));
        return parameters.getParameterSpec(ECParameterSpec.class);
    } catch (InvalidParameterSpecException | NoSuchAlgorithmException e) {
        throw new IllegalArgumentException("Unable to get parameter spec for identifier " + identifier, e);
    }
}

答案 1 :(得分:0)

我已经找到了一种使用Bouncycastle的方法(但希望找到一种JCE方式)。

调整Using public key from authorized_keys with Java security中的代码并引用RFC 5656, section 3.1,添加到decodePublicKey的以下块将解析单个BigInt值Q,即#34;公钥编码从椭圆曲线点":

if (type.startsWith("ecdsa-sha2-") &&
            (type.endsWith("nistp256") || type.endsWith("nistp384") || type.endsWith("nistp521"))) {

        // Based on RFC 5656, section 3.1 (https://tools.ietf.org/html/rfc5656#section-3.1)

        // The string [identifier] is the identifier of the elliptic curve
        // domain parameters.  The format of this string is specified in
        // Section 6.1 (https://tools.ietf.org/html/rfc5656#section-6.1).
        // Information on the REQUIRED and RECOMMENDED sets of
        // elliptic curve domain parameters for use with this algorithm can be
        // found in Section 10 (https://tools.ietf.org/html/rfc5656#section-10).
        String identifier = decodeType();
        if (!type.endsWith(identifier)) {
            throw new IllegalArgumentException("Invalid identifier " + identifier + " for key type " + type + ".");
        }

        // Q is the public key encoded from an elliptic curve point into an
        // octet string as defined in Section 2.3.3 of [SEC1];
        // (https://tools.ietf.org/html/rfc5656#ref-SEC1)
        // point compression MAY be used.
        BigInteger q = decodeBigInt();

        ECPublicKey keyBC = getKeyBC(q, identifier);
        return keyBC;
    }

我发现从Q到ECPublicKey的解决方案如下,使用Bouncycastle API(归功于Generate ECPublicKey from ECPrivateKey提供起点):

ECPublicKey getKeyBC(BigInteger q, String identifier) {
    // https://stackoverflow.com/questions/42639620/generate-ecpublickey-from-ecprivatekey
    try {
        // This only works with the Bouncycastle library:
        Security.addProvider(new BouncyCastleProvider());
        // http://www.bouncycastle.org/wiki/pages/viewpage.action?pageId=362269#SupportedCurves(ECDSAandECGOST)-NIST(aliasesforSECcurves)
        String name = identifier.replace("nist", "sec") + "r1";
        KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC");
        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(name);
        ECPoint point = ecSpec.getCurve().decodePoint(q.toByteArray());
        ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec);
        ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(pubSpec);
        return publicKey;
    } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) {
        throw new RuntimeException(e);
    }
}

这会让你从openssh格式的椭圆曲线公钥(ssh-keygen -t ecdsa -b [256|384|521])转到JCE ECPublicKey