diff --git a/.gitignore b/.gitignore index 520e0b1..6199292 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ target/ .settings/ .classpath .project +src/main/java/META-INF/ diff --git a/src/main/java/com/google/webauthn/gaedemo/server/U2fServer.java b/src/main/java/com/google/webauthn/gaedemo/server/U2fServer.java index db79da6..31f0a65 100644 --- a/src/main/java/com/google/webauthn/gaedemo/server/U2fServer.java +++ b/src/main/java/com/google/webauthn/gaedemo/server/U2fServer.java @@ -38,7 +38,6 @@ import com.google.webauthn.gaedemo.objects.PublicKeyCredential; import com.google.webauthn.gaedemo.storage.Credential; - public class U2fServer extends Server { private static final Logger Log = Logger.getLogger(U2fServer.class.getName()); @@ -51,24 +50,20 @@ public class U2fServer extends Server { */ public static void verifyAssertion(PublicKeyCredential cred, String currentUser, String sessionId, Credential savedCredential) throws ServletException { - AuthenticatorAssertionResponse assertionResponse = - (AuthenticatorAssertionResponse) cred.getResponse(); + AuthenticatorAssertionResponse assertionResponse = (AuthenticatorAssertionResponse) cred.getResponse(); Log.info("-- Verifying signature --"); - if (!(savedCredential.getCredential() - .getResponse() instanceof AuthenticatorAttestationResponse)) { + if (!(savedCredential.getCredential().getResponse() instanceof AuthenticatorAttestationResponse)) { throw new ServletException("Stored attestation missing"); } - AuthenticatorAttestationResponse storedAttData = - (AuthenticatorAttestationResponse) savedCredential.getCredential().getResponse(); + AuthenticatorAttestationResponse storedAttData = (AuthenticatorAttestationResponse) savedCredential.getCredential() + .getResponse(); - if (!(storedAttData.decodedObject.getAuthenticatorData().getAttData() - .getPublicKey() instanceof EccKey)) { + if (!(storedAttData.decodedObject.getAuthenticatorData().getAttData().getPublicKey() instanceof EccKey)) { throw new ServletException("U2f-capable key not provided"); } - EccKey publicKey = - (EccKey) storedAttData.decodedObject.getAuthenticatorData().getAttData().getPublicKey(); + EccKey publicKey = (EccKey) storedAttData.decodedObject.getAuthenticatorData().getAttData().getPublicKey(); try { /* * U2F authentication signatures are signed over the concatenation of @@ -84,15 +79,12 @@ public static void verifyAssertion(PublicKeyCredential cred, String currentUser, String clientDataJson = assertionResponse.getClientDataString(); byte[] clientDataHash = Crypto.sha256Digest(clientDataJson.getBytes()); - byte[] signedBytes = Bytes.concat( - storedAttData.getAttestationObject().getAuthenticatorData().getRpIdHash(), - new byte[] { - (assertionResponse.getAuthenticatorData().isUP() == true ? (byte) 1 : (byte) 0)}, - ByteBuffer.allocate(4).putInt(assertionResponse.getAuthenticatorData().getSignCount()) - .array(), + byte[] signedBytes = Bytes.concat(storedAttData.getAttestationObject().getAuthenticatorData().getRpIdHash(), + new byte[] { (assertionResponse.getAuthenticatorData().isUP() == true ? (byte) 1 : (byte) 0) }, + ByteBuffer.allocate(4).putInt(assertionResponse.getAuthenticatorData().getSignCount()).array(), clientDataHash); - if (!Crypto.verifySignature(Crypto.decodePublicKey(publicKey.getX(), publicKey.getY()), - signedBytes, assertionResponse.getSignature())) { + if (!Crypto.verifySignature(Crypto.decodePublicKey(publicKey.getX(), publicKey.getY()), signedBytes, + assertionResponse.getSignature())) { throw new ServletException("Signature invalid"); } } catch (WebAuthnException e) { @@ -112,18 +104,17 @@ public static void verifyAssertion(PublicKeyCredential cred, String currentUser, * @param cred * @param currentUser * @param session - * @param origin + * @param originString * @throws ServletException */ - public static void registerCredential(PublicKeyCredential cred, String currentUser, - String session, String origin) throws ServletException { + public static void registerCredential(PublicKeyCredential cred, String currentUser, String session, + String originString, String rpId) throws ServletException { if (!(cred.getResponse() instanceof AuthenticatorAttestationResponse)) { throw new ServletException("Invalid response structure"); } - AuthenticatorAttestationResponse attResponse = - (AuthenticatorAttestationResponse) cred.getResponse(); + AuthenticatorAttestationResponse attResponse = (AuthenticatorAttestationResponse) cred.getResponse(); List savedCreds = Credential.load(currentUser); for (Credential c : savedCreds) { @@ -138,31 +129,28 @@ public static void registerCredential(PublicKeyCredential cred, String currentUs throw new ServletException("Unable to verify session and challenge data", e1); } - if (!attResponse.getClientData().getOrigin().equals(origin)) { - throw new ServletException("Couldn't verify client data"); + if (!attResponse.getClientData().getOrigin().equals(originString)) { + throw new ServletException("Client data origin: " + attResponse.getClientData().getOrigin() + + " does not match server origin:" + originString); } - Gson gson = new Gson(); String clientDataJson = attResponse.getClientDataString(); System.out.println(clientDataJson); byte[] clientDataHash = Crypto.sha256Digest(clientDataJson.getBytes()); - byte[] rpIdHash = Crypto.sha256Digest(origin.getBytes()); - if (!Arrays.equals(attResponse.getAttestationObject().getAuthenticatorData().getRpIdHash(), - rpIdHash)) { + byte[] rpIdHash = Crypto.sha256Digest(rpId.getBytes()); + if (!Arrays.equals(attResponse.getAttestationObject().getAuthenticatorData().getRpIdHash(), rpIdHash)) { throw new ServletException("RPID hash incorrect"); } - if (!(attResponse.decodedObject.getAuthenticatorData().getAttData() - .getPublicKey() instanceof EccKey)) { + if (!(attResponse.decodedObject.getAuthenticatorData().getAttData().getPublicKey() instanceof EccKey)) { throw new ServletException("U2f-capable key not provided"); } - FidoU2fAttestationStatement attStmt = - (FidoU2fAttestationStatement) attResponse.decodedObject.getAttestationStatement(); + FidoU2fAttestationStatement attStmt = (FidoU2fAttestationStatement) attResponse.decodedObject + .getAttestationStatement(); - EccKey publicKey = - (EccKey) attResponse.decodedObject.getAuthenticatorData().getAttData().getPublicKey(); + EccKey publicKey = (EccKey) attResponse.decodedObject.getAuthenticatorData().getAttData().getPublicKey(); try { /* @@ -178,22 +166,19 @@ public static void registerCredential(PublicKeyCredential cred, String currentUs * * 65 byte user public key represented as {0x4, X, Y} */ - byte[] signedBytes = Bytes.concat(new byte[] {0}, rpIdHash, - clientDataHash, cred.rawId, new byte[] {0x04}, + byte[] signedBytes = Bytes.concat(new byte[] { 0 }, rpIdHash, clientDataHash, cred.rawId, new byte[] { 0x04 }, publicKey.getX(), publicKey.getY()); - // TODO Make attStmt.attestnCert an X509Certificate right off the bat. - DataInputStream inputStream = new DataInputStream( - new ByteArrayInputStream(attStmt.attestnCert)); - X509Certificate attestationCertificate = (X509Certificate) - CertificateFactory.getInstance("X.509"). - generateCertificate(inputStream); - if (!Crypto.verifySignature(attestationCertificate, signedBytes, - attStmt.sig)) { + // TODO Make attStmt.attestnCert an X509Certificate right off the + // bat. + DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(attStmt.attestnCert)); + X509Certificate attestationCertificate = (X509Certificate) CertificateFactory.getInstance("X.509") + .generateCertificate(inputStream); + if (!Crypto.verifySignature(attestationCertificate, signedBytes, attStmt.sig)) { throw new ServletException("Signature invalid"); } } catch (CertificateException e) { - throw new ServletException("Error when parsing attestationCertificate"); + throw new ServletException("Error when parsing attestationCertificate"); } catch (WebAuthnException e) { throw new ServletException("Failure while verifying signature", e); } diff --git a/src/main/java/com/google/webauthn/gaedemo/servlets/FinishMakeCredential.java b/src/main/java/com/google/webauthn/gaedemo/servlets/FinishMakeCredential.java index 1f80662..52b5709 100644 --- a/src/main/java/com/google/webauthn/gaedemo/servlets/FinishMakeCredential.java +++ b/src/main/java/com/google/webauthn/gaedemo/servlets/FinishMakeCredential.java @@ -40,17 +40,16 @@ public class FinishMakeCredential extends HttpServlet { private static final long serialVersionUID = 1L; private final UserService userService = UserServiceFactory.getUserService(); - public FinishMakeCredential() {} + public FinishMakeCredential() { + } @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String currentUser = userService.getCurrentUser().getUserId(); String data = request.getParameter("data"); String session = request.getParameter("session"); @@ -88,25 +87,24 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) PublicKeyCredential cred = new PublicKeyCredential(credentialId, type, BaseEncoding.base64Url().decode(credentialId), attestation); - //String rpId = (request.isSecure() ? "https://" : "http://") + request.getHeader("Host"); + String domain = (request.isSecure() ? "https://" : "http://") + request.getHeader("Host"); String rpId = request.getHeader("Host").split(":")[0]; switch (cred.getAttestationType()) { - case FIDOU2F: - U2fServer.registerCredential(cred, currentUser, session, rpId); - break; - case ANDROIDSAFETYNET: - AndroidSafetyNetServer.registerCredential(cred, currentUser, session, rpId); - break; - case PACKED: - PackedServer.registerCredential(cred, currentUser, session, rpId); - break; + case FIDOU2F: + U2fServer.registerCredential(cred, currentUser, session, domain, rpId); + break; + case ANDROIDSAFETYNET: + AndroidSafetyNetServer.registerCredential(cred, currentUser, session, rpId); + break; + case PACKED: + PackedServer.registerCredential(cred, currentUser, session, rpId); + break; } Credential credential = new Credential(cred); credential.save(currentUser); - PublicKeyCredentialResponse rsp = - new PublicKeyCredentialResponse(true, "Successfully created credential"); + PublicKeyCredentialResponse rsp = new PublicKeyCredentialResponse(true, "Successfully created credential"); response.setContentType("application/json"); response.getWriter().println(rsp.toJson());