/* Copyright (c) 2012 Google Inc. * * 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.github.jberkel.pay.me.validator; import android.text.TextUtils; import android.util.Base64; import android.util.Log; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; /** * A signature validator using a Base64 encoded public key. * For a secure implementation, all of this code * should be implemented on a server that communicates with the * application on the device. For the sake of simplicity and clarity of this * example, this code is included here and is executed on the device. If you * must verify the purchases on the phone, you should obfuscate this code to * make it harder for an attacker to replace the code with stubs that treat all * purchases as verified. */ public class DefaultSignatureValidator implements SignatureValidator { private static final String TAG = "IAB/DefaultSignatureValidator"; private static final String KEY_FACTORY_ALGORITHM = "RSA"; private static final String SIGNATURE_ALGORITHM = "SHA1withRSA"; private final PublicKey mPublicKey; public DefaultSignatureValidator(String base64EncodedKey) { mPublicKey = generatePublicKey(base64EncodedKey); } @Override public boolean validate(String signedData, String signature) { if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(signature)) { Log.e(TAG, "Purchase verification failed: missing data."); return false; } else if (!verify(signedData, signature)) { Log.w(TAG, "signature does not match data."); return false; } else { return true; } } private boolean verify(String signedData, String signature) { final byte[] decodedSig; try { decodedSig = Base64.decode(signature, Base64.DEFAULT); } catch (IllegalArgumentException e) { Log.e(TAG, "Error decoding signature."); return false; } try { Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM); sig.initVerify(mPublicKey); sig.update(signedData.getBytes()); if (!sig.verify(decodedSig)) { Log.e(TAG, "Signature verification failed."); return false; } else { return true; } } catch (NoSuchAlgorithmException e) { Log.e(TAG, "NoSuchAlgorithmException."); } catch (InvalidKeyException e) { Log.e(TAG, "Invalid key specification."); } catch (SignatureException e) { Log.e(TAG, "Signature exception."); } return false; } private static PublicKey generatePublicKey(String encodedPublicKey) { try { byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT); KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM); return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey)); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (InvalidKeySpecException e) { Log.e(TAG, "Invalid key specification."); throw new IllegalArgumentException(e); } } }