Skip to content

Commit 2c51f98

Browse files
committed
Translate ECDSA keys to X.509 instead of using COSE-Java
At this point this is the only thing we really use the COSE-Java library for. At the cost of this small bit of extra code we can eliminate the dependency, one benefit of which is that downstream security review gets easier.
1 parent a733a77 commit 2c51f98

File tree

1 file changed

+55
-4
lines changed

1 file changed

+55
-4
lines changed

webauthn-server-core/src/main/java/com/yubico/webauthn/WebAuthnCodecs.java

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
package com.yubico.webauthn;
2626

2727
import COSE.CoseException;
28-
import COSE.OneKey;
2928
import com.google.common.primitives.Bytes;
3029
import com.upokecenter.cbor.CBORObject;
3130
import com.yubico.webauthn.data.ByteArray;
@@ -46,6 +45,19 @@
4645

4746
final class WebAuthnCodecs {
4847

48+
private static final ByteArray EC_PUBLIC_KEY_OID =
49+
new ByteArray(
50+
new byte[] {
51+
0x2A, -122, 0x48, -50, 0x3D, 0x02, 0x01
52+
}); // OID 1.2.840.10045.2.1 ecPublicKey (ANSI X9.62 public key type)
53+
private static final ByteArray P256_CURVE_OID =
54+
new ByteArray(
55+
new byte[] {0x2A, -122, 0x48, -50, 0x3D, 0x03, 0x01, 7}); // OID 1.2.840.10045.3.1.7
56+
private static final ByteArray P384_CURVE_OID =
57+
new ByteArray(new byte[] {0x2B, -127, 0x04, 0, 34}); // OID 1.3.132.0.34
58+
private static final ByteArray P512_CURVE_OID =
59+
new ByteArray(new byte[] {0x2B, -127, 0x04, 0, 35}); // OID 1.3.132.0.35
60+
4961
private static final ByteArray ED25519_CURVE_OID =
5062
new ByteArray(new byte[] {0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70});
5163

@@ -130,7 +142,7 @@ static PublicKey importCosePublicKey(ByteArray key)
130142
// additional dependency to parse EdDSA keys via the OneKey constructor
131143
return importCoseEdDsaPublicKey(cose);
132144
case 2:
133-
return importCoseP256PublicKey(cose);
145+
return importCoseEcdsaPublicKey(cose);
134146
case 3:
135147
// COSE-JAVA supports RSA in v1.1.0 but not in v1.0.0
136148
return importCoseRsaPublicKey(cose);
@@ -148,8 +160,43 @@ private static PublicKey importCoseRsaPublicKey(CBORObject cose)
148160
return KeyFactory.getInstance("RSA").generatePublic(spec);
149161
}
150162

151-
private static ECPublicKey importCoseP256PublicKey(CBORObject cose) throws CoseException {
152-
return (ECPublicKey) new OneKey(cose).AsPublicKey();
163+
private static PublicKey importCoseEcdsaPublicKey(CBORObject cose)
164+
throws NoSuchAlgorithmException, InvalidKeySpecException {
165+
final int crv = cose.get(CBORObject.FromObject(-1)).AsInt32Value();
166+
final ByteArray x = new ByteArray(cose.get(CBORObject.FromObject(-2)).GetByteString());
167+
final ByteArray y = new ByteArray(cose.get(CBORObject.FromObject(-3)).GetByteString());
168+
169+
final ByteArray curveOid;
170+
switch (crv) {
171+
case 1:
172+
curveOid = P256_CURVE_OID;
173+
break;
174+
175+
case 2:
176+
curveOid = P384_CURVE_OID;
177+
break;
178+
179+
case 3:
180+
curveOid = P512_CURVE_OID;
181+
break;
182+
183+
default:
184+
throw new IllegalArgumentException("Unknown COSE EC2 curve: " + crv);
185+
}
186+
187+
final ByteArray algId =
188+
encodeDerSequence(encodeDerObjectId(EC_PUBLIC_KEY_OID), encodeDerObjectId(curveOid));
189+
190+
final ByteArray rawKey =
191+
encodeDerBitStringWithZeroUnused(
192+
new ByteArray(new byte[] {0x04}) // Raw EC public key with x and y
193+
.concat(x)
194+
.concat(y));
195+
196+
final ByteArray x509Key = encodeDerSequence(algId, rawKey);
197+
198+
KeyFactory kFact = KeyFactory.getInstance("EC");
199+
return kFact.generatePublic(new X509EncodedKeySpec(x509Key.getBytes()));
153200
}
154201

155202
private static ByteArray encodeDerLength(final int length) {
@@ -166,6 +213,10 @@ private static ByteArray encodeDerLength(final int length) {
166213
}
167214
}
168215

216+
private static ByteArray encodeDerObjectId(final ByteArray oid) {
217+
return new ByteArray(new byte[] {0x06, (byte) oid.size()}).concat(oid);
218+
}
219+
169220
private static ByteArray encodeDerBitStringWithZeroUnused(final ByteArray content) {
170221
return new ByteArray(new byte[] {0x03})
171222
.concat(encodeDerLength(1 + content.size()))

0 commit comments

Comments
 (0)