使用具有 Java 安全性的 openssh public 密钥 (ecdsa-sha2-nistp256)
Using openssh public key (ecdsa-sha2-nistp256) with Java Security
是否有 Java library/example 来读取 Java 中 JCE PublicKey
的 openssh 格式 ecdsa public 密钥?我想将 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, and this discussion of the openssh format for ECDSA: https://security.stackexchange.com/questions/129910/ecdsa-why-do-ssh-keygen-and-java-generated-public-keys-have-different-sizes
但是我在尝试为 ECDSA 调整 RSS/DSA 代码时迷路了 - 我不确定如何设置 ECPublicKeySpec
。它需要 ECPoint
、EllipticCurve
、ECParameterSpec
、ECField
。 openssh格式只包含两个整数,这对ECPoint
有意义,但我不知道如何设置其余的
我一直在浏览一堆库,包括 jsch, sshj, ssh-tools and good old Bouncycastle。我最接近的是:
com.jcraft.jsch.KeyPair load = com.jcraft.jsch.KeyPair.load(jsch, null, bytes[openSshKey]);
它可以很好地加载密钥,但不会让我进入 JCE PublicKey
- 只是一个 byte[] getPublicKeyBlob()
方法。
我是不是遗漏了什么明显的东西?
我找到了一种使用 Bouncycastle 来执行此操作的方法(但我想找到一种 JCE 方法)。
调整来自 Using public key from authorized_keys with Java security, and refering to RFC 5656, section 3.1 的代码,添加到 decodePublicKey
的以下块将解析单个 BigInt 值 Q,即“从椭圆曲线点编码的 public 密钥” :
if (type.startsWith("ecdsa-sha2-") &&
(type.endsWith("nistp256") || type.endsWith("nistp384") || type.endsWith("nistp521"))) {
// Based on RFC 5656, section 3.1 (https://www.rfc-editor.org/rfc/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://www.rfc-editor.org/rfc/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://www.rfc-editor.org/rfc/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://www.rfc-editor.org/rfc/rfc5656#ref-SEC1)
// point compression MAY be used.
BigInteger q = decodeBigInt();
ECPublicKey keyBC = getKeyBC(q, identifier);
return keyBC;
}
我找到的从 Q 到 ECPublicKey
的解决方案如下,使用 Bouncycastle API(感谢 提供起点):
ECPublicKey getKeyBC(BigInteger q, String identifier) {
//
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 格式的椭圆曲线 public 密钥 (ssh-keygen -t ecdsa -b [256|384|521]
) 到一个 JCE ECPublicKey
.
为了完整起见,这里是我使用的代码。它几乎是纯 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);
}
}
是否有 Java library/example 来读取 Java 中 JCE PublicKey
的 openssh 格式 ecdsa public 密钥?我想将 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, and this discussion of the openssh format for ECDSA: https://security.stackexchange.com/questions/129910/ecdsa-why-do-ssh-keygen-and-java-generated-public-keys-have-different-sizes
但是我在尝试为 ECDSA 调整 RSS/DSA 代码时迷路了 - 我不确定如何设置 ECPublicKeySpec
。它需要 ECPoint
、EllipticCurve
、ECParameterSpec
、ECField
。 openssh格式只包含两个整数,这对ECPoint
有意义,但我不知道如何设置其余的
我一直在浏览一堆库,包括 jsch, sshj, ssh-tools and good old Bouncycastle。我最接近的是:
com.jcraft.jsch.KeyPair load = com.jcraft.jsch.KeyPair.load(jsch, null, bytes[openSshKey]);
它可以很好地加载密钥,但不会让我进入 JCE PublicKey
- 只是一个 byte[] getPublicKeyBlob()
方法。
我是不是遗漏了什么明显的东西?
我找到了一种使用 Bouncycastle 来执行此操作的方法(但我想找到一种 JCE 方法)。
调整来自 Using public key from authorized_keys with Java security, and refering to RFC 5656, section 3.1 的代码,添加到 decodePublicKey
的以下块将解析单个 BigInt 值 Q,即“从椭圆曲线点编码的 public 密钥” :
if (type.startsWith("ecdsa-sha2-") &&
(type.endsWith("nistp256") || type.endsWith("nistp384") || type.endsWith("nistp521"))) {
// Based on RFC 5656, section 3.1 (https://www.rfc-editor.org/rfc/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://www.rfc-editor.org/rfc/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://www.rfc-editor.org/rfc/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://www.rfc-editor.org/rfc/rfc5656#ref-SEC1)
// point compression MAY be used.
BigInteger q = decodeBigInt();
ECPublicKey keyBC = getKeyBC(q, identifier);
return keyBC;
}
我找到的从 Q 到 ECPublicKey
的解决方案如下,使用 Bouncycastle API(感谢
ECPublicKey getKeyBC(BigInteger q, String identifier) {
//
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 格式的椭圆曲线 public 密钥 (ssh-keygen -t ecdsa -b [256|384|521]
) 到一个 JCE ECPublicKey
.
为了完整起见,这里是我使用的代码。它几乎是纯 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);
}
}