/** * Verify authorization header: * * <pre> * Authorization: Airship fingerprint:signature * fingerprint = hex md5 of private key * signature = base64 signature of [ts, method, uri, bodyMd5] * </pre> */ @Override public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { if (!enabled) { chain.doFilter(servletRequest, servletResponse); return; } HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // get authorization headers ArrayList<String> authorizations = Collections.list(request.getHeaders("Authorization")); if (authorizations.isEmpty()) { sendError(response, BAD_REQUEST, "Missing Authorization header"); return; } // // Generate message // // get unix timestamp from request time long millis; try { millis = request.getDateHeader("Date"); } catch (IllegalArgumentException e) { sendError(response, BAD_REQUEST, "Invalid Date header"); return; } if (millis == -1) { sendError(response, BAD_REQUEST, "Missing Date header"); return; } long serverTime = currentTimeMillis(); if (abs(serverTime - millis) > MAX_REQUEST_TIME_SKEW.toMillis()) { sendError( response, BAD_REQUEST, format("Request time too skewed (server time: %s)", serverTime / 1000)); return; } long timestamp = millis / 1000; // get method and uri with query parameters String method = request.getMethod(); String uri = getRequestUri(request); // wrap request to allow reading body RequestWrapper requestWrapper = new RequestWrapper(request); String bodyMd5 = md5Hex(requestWrapper.getRequestBody()); // compute signature payload String stringToSign = Joiner.on('\n').join(timestamp, method, uri, bodyMd5); byte[] bytesToSign = stringToSign.getBytes(Charsets.UTF_8); // // try each authorization header // for (String authorization : authorizations) { // parse authorization header List<String> authTokens = ImmutableList.copyOf(Splitter.on(' ').omitEmptyStrings().split(authorization)); if ((authTokens.size() != 2) || (!authTokens.get(0).equals("Airship"))) { sendError(response, BAD_REQUEST, "Invalid Authorization header"); return; } List<String> authParts = ImmutableList.copyOf(Splitter.on(':').split(authTokens.get(1))); if (authParts.size() != 2) { sendError(response, BAD_REQUEST, "Invalid Authorization token"); return; } // parse authorization token String hexFingerprint = authParts.get(0); String base64Signature = authParts.get(1); Fingerprint fingerprint; try { fingerprint = Fingerprint.valueOf(hexFingerprint); } catch (IllegalArgumentException e) { sendError(response, BAD_REQUEST, "Invalid Authorization fingerprint"); return; } byte[] signature; try { signature = Base64.decodeBase64(base64Signature); } catch (Exception e) { sendError(response, BAD_REQUEST, "Invalid Authorization signature encoding"); return; } // verify signature AuthorizedKey authorizedKey = verifier.verify(fingerprint, signature, bytesToSign); if (authorizedKey == null) { continue; } request.setAttribute(AUTHORIZED_KEY_ATTRIBUTE, authorizedKey); chain.doFilter(requestWrapper, response); return; } sendError(response, FORBIDDEN, "Signature verification failed"); }