Skip to content

Commit

Permalink
Add support for RSA keys
Browse files Browse the repository at this point in the history
  • Loading branch information
emlun committed Apr 17, 2019
1 parent 181f969 commit a71a9cd
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 5 deletions.
6 changes: 5 additions & 1 deletion NEWS
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
== Version 1.1.1 (unreleased) ==
== Version 1.2.0 (unreleased) ==

New features:

* RSA keys are now supported.

Bug fixes:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,17 @@
import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.COSEAlgorithmIdentifier;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.jce.provider.BouncyCastleProvider;


public final class WebAuthnCodecs {
Expand Down Expand Up @@ -123,16 +129,25 @@ public static ByteArray ecPublicKeyToCose(ECPublicKey key) {
return rawEcdaKeyToCose(ecPublicKeyToRaw(key));
}

public static PublicKey importCosePublicKey(ByteArray key) throws CoseException, IOException {
public static PublicKey importCosePublicKey(ByteArray key) throws CoseException, IOException, InvalidKeySpecException, NoSuchAlgorithmException {
CBORObject cose = CBORObject.DecodeFromBytes(key.getBytes());
final int kty = cose.get(CBORObject.FromObject(1)).AsInt32();
switch (kty) {
case 2: return importCoseP256PublicKey(cose);
case 3: return importCoseRsaPublicKey(cose);
default:
throw new IllegalArgumentException("Unsupported key type: " + kty);
}
}

private static PublicKey importCoseRsaPublicKey(CBORObject cose) throws NoSuchAlgorithmException, InvalidKeySpecException {
RSAPublicKeySpec spec = new RSAPublicKeySpec(
new BigInteger(1, cose.get(CBORObject.FromObject(-1)).GetByteString()),
new BigInteger(1, cose.get(CBORObject.FromObject(-2)).GetByteString())
);
return KeyFactory.getInstance("RSA", new BouncyCastleProvider()).generatePublic(spec);
}

private static ECPublicKey importCoseP256PublicKey(CBORObject cose) throws CoseException, IOException {
return new COSE.ECPublicKey(new OneKey(cose));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@

import COSE.CoseException;
import com.fasterxml.jackson.databind.JsonNode;
import com.yubico.internal.util.ExceptionUtil;
import com.yubico.internal.util.WebAuthnCodecs;
import com.yubico.webauthn.data.AttestationObject;
import com.yubico.webauthn.data.AttestationType;
import com.yubico.webauthn.data.AttestedCredentialData;
import com.yubico.webauthn.data.ByteArray;
import java.io.IOException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Objects;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -83,7 +86,12 @@ private static boolean validSelfSignature(X509Certificate cert) {

private static ByteArray getRawUserPublicKey(AttestationObject attestationObject) throws IOException, CoseException {
final ByteArray pubkeyCose = attestationObject.getAuthenticatorData().getAttestedCredentialData().get().getCredentialPublicKey();
final PublicKey pubkey = WebAuthnCodecs.importCosePublicKey(pubkeyCose);
final PublicKey pubkey;
try {
pubkey = WebAuthnCodecs.importCosePublicKey(pubkeyCose);
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
throw ExceptionUtil.wrapAndLog(log, "Failed to decode public key: " + pubkeyCose.getHex(), e);
}

final ECPublicKey ecPubkey;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
import com.yubico.webauthn.exception.InvalidSignatureCountException;
import com.yubico.webauthn.extension.appid.AppId;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
Expand Down Expand Up @@ -547,7 +549,7 @@ public void validate() {

try {
key = WebAuthnCodecs.importCosePublicKey(cose);
} catch (CoseException | IOException e) {
} catch (CoseException | IOException | InvalidKeySpecException e) {
throw new IllegalArgumentException(
String.format(
"Failed to decode public key: Credential ID: %s COSE: %s",
Expand All @@ -556,6 +558,8 @@ public void validate() {
),
e
);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}

if (!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
Expand Down Expand Up @@ -101,12 +102,14 @@ private boolean verifySelfAttestationSignature(AttestationObject attestationObje
pubkey = WebAuthnCodecs.importCosePublicKey(
attestationObject.getAuthenticatorData().getAttestedCredentialData().get().getCredentialPublicKey()
);
} catch (IOException | CoseException e) {
} catch (IOException | CoseException | InvalidKeySpecException e) {
throw ExceptionUtil.wrapAndLog(
log,
String.format("Failed to parse public key from attestation data %s", attestationObject.getAuthenticatorData().getAttestedCredentialData()),
e
);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}

final Long keyAlgId = CBORObject.DecodeFromBytes(attestationObject.getAuthenticatorData().getAttestedCredentialData().get().getCredentialPublicKey().getBytes())
Expand Down

0 comments on commit a71a9cd

Please sign in to comment.