/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.vending.licensing;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.vending.licensing.util.Base64;
import com.google.android.vending.licensing.util.Base64DecoderException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
/**
* Contains data related to a licensing request and methods to verify
* and process the response.
*/
public class LicenseValidator {
// Server response codes.
public static final int LICENSED = 0x0;
public static final int NOT_LICENSED = 0x1;
public static final int LICENSED_OLD_KEY = 0x2;
public static final int ERROR_NOT_MARKET_MANAGED = 0x3;
public static final int ERROR_SERVER_FAILURE = 0x4;
public static final int ERROR_OVER_QUOTA = 0x5;
public static final int ERROR_CONTACTING_SERVER = 0x101;
public static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
public static final int ERROR_NON_MATCHING_UID = 0x103;
public static final int ERROR_SIGNATURE_FAILED = 0x104;
public static final int ERROR_INVALID_PUBLIC_KEY = 5;
public static final int ERROR_INVALID_RESPONSE = 7;
private static final String TAG = "LicenseValidator";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
private final int mNonce;
private final String mPackageName;
private final String mVersionCode;
public LicenseValidator(int nonce, String packageName, String versionCode) {
mNonce = nonce;
mPackageName = packageName;
mVersionCode = versionCode;
}
public int getNonce() {
return mNonce;
}
public String getPackageName() {
return mPackageName;
}
/**
* Verifies the response from server and calls appropriate callback method.
*
* @param publicKey public key associated with the developer account
* @param responseCode server response code
* @param signedData signed data from server
* @param signature server signature
*/
public int verify(PublicKey publicKey, int responseCode, String signedData, String signature) {
String userId;
// Skip signature check for unsuccessful requests
ResponseData data;
if (responseCode == LICENSED || responseCode == NOT_LICENSED ||
responseCode == LICENSED_OLD_KEY) {
// Verify signature.
try {
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(Base64.decode(signature))) {
Log.e(TAG, "Signature verification failed.");
return ERROR_SIGNATURE_FAILED;
}
} catch (NoSuchAlgorithmException e) {
// This can't happen on an Android compatible device.
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
return ERROR_INVALID_PUBLIC_KEY;
} catch (SignatureException e) {
throw new RuntimeException(e);
} catch (Base64DecoderException e) {
Log.e(TAG, "Could not Base64-decode signature.");
return ERROR_INVALID_RESPONSE;
}
// Parse and validate response.
try {
data = ResponseData.parse(signedData);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Could not parse response.");
return ERROR_INVALID_RESPONSE;
}
if (data.responseCode != responseCode) {
Log.e(TAG, "Response codes don't match.");
return ERROR_INVALID_RESPONSE;
}
if (data.nonce != mNonce) {
Log.e(TAG, "Nonce doesn't match.");
return ERROR_INVALID_RESPONSE;
}
if (!data.packageName.equals(mPackageName)) {
Log.e(TAG, "Package name doesn't match.");
return ERROR_INVALID_RESPONSE;
}
if (!data.versionCode.equals(mVersionCode)) {
Log.e(TAG, "Version codes don't match.");
return ERROR_INVALID_RESPONSE;
}
// Application-specific user identifier.
userId = data.userId;
if (TextUtils.isEmpty(userId)) {
Log.e(TAG, "User identifier is empty.");
return ERROR_INVALID_RESPONSE;
}
}
return responseCode;
}
}