Bug 980478 - Generate assertions with no iat and exp: 9999999999999L. r=rnewman

This commit is contained in:
Nick Alexander 2014-03-10 21:04:25 -07:00
parent c0e309ea5c
commit f8696e1593
5 changed files with 74 additions and 51 deletions

View File

@ -9,7 +9,9 @@ import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.apache.commons.codec.binary.StringUtils;
@ -28,6 +30,7 @@ import org.mozilla.gecko.sync.Utils;
public class JSONWebTokenUtils {
public static final long DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS = 60 * 60 * 1000;
public static final long DEFAULT_ASSERTION_DURATION_IN_MILLISECONDS = 60 * 60 * 1000;
public static final long DEFAULT_FUTURE_EXPIRES_AT_IN_MILLISECONDS = 9999999999999L;
public static final String DEFAULT_CERTIFICATE_ISSUER = "127.0.0.1";
public static final String DEFAULT_ASSERTION_ISSUER = "127.0.0.1";
@ -70,8 +73,12 @@ public class JSONWebTokenUtils {
return payload;
}
protected static String getPayloadString(String payloadString, String issuer,
long issuedAt, String audience, long expiresAt) throws NonObjectJSONException,
/**
* Public for testing.
*/
@SuppressWarnings("unchecked")
public static String getPayloadString(String payloadString, String audience, String issuer,
Long issuedAt, long expiresAt) throws NonObjectJSONException,
IOException, ParseException {
ExtendedJSONObject payload;
if (payloadString != null) {
@ -79,13 +86,16 @@ public class JSONWebTokenUtils {
} else {
payload = new ExtendedJSONObject();
}
payload.put("iss", issuer);
payload.put("iat", issuedAt);
if (audience != null) {
payload.put("aud", audience);
}
payload.put("iss", issuer);
if (issuedAt != null) {
payload.put("iat", issuedAt);
}
payload.put("exp", expiresAt);
return payload.toJSONString();
// TreeMap so that keys are sorted. A small attempt to keep output stable over time.
return JSONObject.toJSONString(new TreeMap<Object, Object>(payload.object));
}
protected static String getCertificatePayloadString(VerifyingPublicKey publicKeyToSign, String email) throws NonObjectJSONException, IOException, ParseException {
@ -100,33 +110,42 @@ public class JSONWebTokenUtils {
public static String createCertificate(VerifyingPublicKey publicKeyToSign, String email,
String issuer, long issuedAt, long expiresAt, SigningPrivateKey privateKey) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String certificatePayloadString = getCertificatePayloadString(publicKeyToSign, email);
String payloadString = getPayloadString(certificatePayloadString, issuer, issuedAt, null, expiresAt);
String payloadString = getPayloadString(certificatePayloadString, null, issuer, issuedAt, expiresAt);
return JSONWebTokenUtils.encode(payloadString, privateKey);
}
public static String createCertificate(VerifyingPublicKey publicKeyToSign, String email, SigningPrivateKey privateKey) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String issuer = DEFAULT_CERTIFICATE_ISSUER;
long issuedAt = System.currentTimeMillis();
long durationInMilliseconds = DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS;
return createCertificate(publicKeyToSign, email, issuer, issuedAt, issuedAt + durationInMilliseconds, privateKey);
}
/**
* Create a Browser ID assertion.
*
* @param privateKeyToSignWith
* private key to sign assertion with.
* @param certificate
* to include in assertion; no attempt is made to ensure the
* certificate is valid, or corresponds to the private key, or any
* other condition.
* @param audience
* to produce assertion for.
* @param issuer
* to produce assertion for.
* @param issuedAt
* timestamp for assertion, in milliseconds since the epoch; if null,
* no timestamp is included.
* @param expiresAt
* expiration timestamp for assertion, in milliseconds since the epoch.
* @return assertion.
* @throws NonObjectJSONException
* @throws IOException
* @throws ParseException
* @throws GeneralSecurityException
*/
public static String createAssertion(SigningPrivateKey privateKeyToSignWith, String certificate, String audience,
String issuer, long issuedAt, long durationInMilliseconds) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
long expiresAt = issuedAt + durationInMilliseconds;
String issuer, Long issuedAt, long expiresAt) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String emptyAssertionPayloadString = "{}";
String payloadString = getPayloadString(emptyAssertionPayloadString, issuer, issuedAt, audience, expiresAt);
String payloadString = getPayloadString(emptyAssertionPayloadString, audience, issuer, issuedAt, expiresAt);
String signature = JSONWebTokenUtils.encode(payloadString, privateKeyToSignWith);
return certificate + "~" + signature;
}
public static String createAssertion(SigningPrivateKey privateKeyToSignWith, String certificate, String audience) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String issuer = DEFAULT_ASSERTION_ISSUER;
long issuedAt = System.currentTimeMillis();
long durationInMilliseconds = DEFAULT_ASSERTION_DURATION_IN_MILLISECONDS;
return createAssertion(privateKeyToSignWith, certificate, audience, issuer, issuedAt, durationInMilliseconds);
}
/**
* For debugging only!
*

View File

@ -40,18 +40,17 @@ public class MockMyIDTokenFactory {
* sign username@mockmyid.com
* @param issuedAt
* timestamp for certificate, in milliseconds since the epoch.
* @param durationInMilliseconds
* lifespan of certificate, in milliseconds.
* @param expiresAt
* expiration timestamp for certificate, in milliseconds since the epoch.
* @return encoded certificate string.
* @throws Exception
*/
public String createMockMyIDCertificate(final VerifyingPublicKey publicKeyToSign, String username,
final long issuedAt, final long durationInMilliseconds)
final long issuedAt, final long expiresAt)
throws Exception {
if (!username.endsWith("@mockmyid.com")) {
username = username + "@mockmyid.com";
}
long expiresAt = issuedAt + durationInMilliseconds;
SigningPrivateKey mockMyIdPrivateKey = getMockMyIDPrivateKey();
return JSONWebTokenUtils.createCertificate(publicKeyToSign, username, "mockmyid.com", issuedAt, expiresAt, mockMyIdPrivateKey);
}
@ -69,8 +68,9 @@ public class MockMyIDTokenFactory {
*/
public String createMockMyIDCertificate(final VerifyingPublicKey publicKeyToSign, final String username)
throws Exception {
return createMockMyIDCertificate(publicKeyToSign, username,
System.currentTimeMillis(), JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS );
long ciat = System.currentTimeMillis();
long cexp = ciat + JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS;
return createMockMyIDCertificate(publicKeyToSign, username, ciat, cexp);
}
/**
@ -84,23 +84,24 @@ public class MockMyIDTokenFactory {
* sign username@mockmyid.com.
* @param certificateIssuedAt
* timestamp for certificate, in milliseconds since the epoch.
* @param certificateDurationInMilliseconds
* lifespan of certificate, in milliseconds.
* @param certificateExpiresAt
* expiration timestamp for certificate, in milliseconds since the epoch.
* @param assertionIssuedAt
* timestamp for assertion, in milliseconds since the epoch.
* @param assertionDurationInMilliseconds
* lifespan of assertion, in milliseconds.
* timestamp for assertion, in milliseconds since the epoch; if null,
* no timestamp is included.
* @param assertionExpiresAt
* expiration timestamp for assertion, in milliseconds since the epoch.
* @return encoded assertion string.
* @throws Exception
*/
public String createMockMyIDAssertion(BrowserIDKeyPair keyPair, String username, String audience,
long certificateIssuedAt, long certificateDurationInMilliseconds,
long assertionIssuedAt, long assertionDurationInMilliseconds)
long certificateIssuedAt, long certificateExpiresAt,
Long assertionIssuedAt, long assertionExpiresAt)
throws Exception {
String certificate = createMockMyIDCertificate(keyPair.getPublic(), username,
certificateIssuedAt, certificateDurationInMilliseconds);
certificateIssuedAt, certificateExpiresAt);
return JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience,
JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER, assertionIssuedAt, assertionDurationInMilliseconds);
JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER, assertionIssuedAt, assertionExpiresAt);
}
/**
@ -117,9 +118,11 @@ public class MockMyIDTokenFactory {
*/
public String createMockMyIDAssertion(BrowserIDKeyPair keyPair, String username, String audience)
throws Exception {
long now = System.currentTimeMillis();
long ciat = System.currentTimeMillis();
long cexp = ciat + JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS;
long aiat = ciat + 1;
long aexp = aiat + JSONWebTokenUtils.DEFAULT_ASSERTION_DURATION_IN_MILLISECONDS;
return createMockMyIDAssertion(keyPair, username, audience,
now, JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS,
now + 1, JSONWebTokenUtils.DEFAULT_ASSERTION_DURATION_IN_MILLISECONDS);
ciat, cexp, aiat, aexp);
}
}

View File

@ -14,6 +14,8 @@ public class FxAccountConstants {
public static final String DEFAULT_AUTH_SERVER_ENDPOINT = "https://api.accounts.firefox.com/v1";
public static final String DEFAULT_TOKEN_SERVER_ENDPOINT = "https://token.services.mozilla.com/1.0/sync/1.5";
public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5";
// For extra debugging. Not final so it can be changed from Fennec, or from
// an add-on.
public static boolean LOG_PERSONAL_INFORMATION = false;

View File

@ -56,8 +56,11 @@ public class Married extends TokensAndKeysState {
delegate.handleTransition(new LogMessage("staying married"), this);
}
public String generateAssertion(String audience, String issuer, long issuedAt, long durationInMilliseconds) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String assertion = JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience, issuer, issuedAt, durationInMilliseconds);
public String generateAssertion(String audience, String issuer) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
// We generate assertions with no iat and an exp after 2050 to avoid
// invalid-timestamp errors from the token server.
final long expiresAt = JSONWebTokenUtils.DEFAULT_FUTURE_EXPIRES_AT_IN_MILLISECONDS;
String assertion = JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience, issuer, null, expiresAt);
if (!FxAccountConstants.LOG_PERSONAL_INFORMATION) {
return assertion;
}

View File

@ -347,9 +347,9 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
// skew adjustment that the HawkAuthHeaderProvider uses to adjust its
// timestamps. Eventually we might want this to adapt within the scope of a
// global session.
final SkewHandler tokenServerSkewHandler = SkewHandler.getSkewHandlerForHostname(storageHostname);
final long tokenServerSkew = tokenServerSkewHandler.getSkewInSeconds();
final AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false, tokenServerSkew);
final SkewHandler storageServerSkewHandler = SkewHandler.getSkewHandlerForHostname(storageHostname);
final long storageServerSkew = storageServerSkewHandler.getSkewInSeconds();
final AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false, storageServerSkew);
final Context context = getContext();
final SyncConfiguration syncConfig = new SyncConfiguration(token.uid, authHeaderProvider, sharedPrefs, syncKeyBundle);
@ -503,11 +503,7 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
}
final Married married = (Married) state;
SkewHandler skewHandler = SkewHandler.getSkewHandlerFromEndpointString(tokenServerEndpoint);
final long now = System.currentTimeMillis();
final long issuedAtMillis = now + skewHandler.getSkewInMillis();
final long assertionDurationMillis = this.getAssertionDurationInMilliseconds();
final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER, issuedAtMillis, assertionDurationMillis);
final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER);
/*
* At this point we're in the correct state to sync, and we're ready to fetch