Java server side Apple IAP and Google Play IAP verification class

Programming 2012.12.12 15:45



import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.net.URL;

import java.net.URLConnection;

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;


import org.apache.commons.codec.binary.Base64;

import org.apache.commons.lang.StringUtils;

import org.apache.log4j.Logger;

import org.codehaus.jackson.JsonNode;

import org.codehaus.jackson.map.ObjectMapper;


/**

 * Apple IAP and Google Play In-app Billing Verification Class

 *

 */

public class IapVerification {

    static Logger logger = Logger.getLogger(IapVerification.class);

    public static final String VERIFICATION_URL_REAL = "https://buy.itunes.apple.com/verifyReceipt";

    public static final String VERIFICATION_URL_SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt";

    /**

     * @return int

     * @param receipt

     * @param isTest

     * @return 0: fail, 1: success, -1 : urus hacking

     * @throws IOException

     */

    public static int verifyApplePurchase(String receipt, boolean isTest) throws IOException {

String returnedString ;

if (isTest)

{

    returnedString = verifyAppleReceiptData(VERIFICATION_URL_SANDBOX, receipt);

} else {

    returnedString = verifyAppleReceiptData(VERIFICATION_URL_REAL, receipt);

}


ObjectMapper mapper = new ObjectMapper();

JsonNode resultObject = mapper.readTree(returnedString);

int resultStatus = Integer.parseInt(resultObject.get("status").toString());


if (resultStatus != 0)

{

    // urus hacking check.

    try {

String decodedText = new String(Base64.decodeBase64(receipt

.getBytes()), "UTF-8");

if (decodedText != null && decodedText.startsWith("com.urus")) {

    return -1;

}

    } catch (Exception base64DecodeException) {

logger.error(base64DecodeException.getMessage());

    }

    return 0;


}

return 1;

    }

    /**

     * @author Kim Seong Su

     * @return String

     * @param urladdress

     * @param receiptData

     * @return result string

     * @throws IOException

     */

    private static String verifyAppleReceiptData(String urladdress, String receiptData) throws IOException  {

URL url = null;

URLConnection conn = null;

OutputStreamWriter osw = null;

BufferedReader br = null;

StringBuffer sb = new StringBuffer();


try {

    String jsonData = "{" +

                    "\"receipt-data\" : \"" + receiptData + "\"," +

                 "}";

    url = new URL(urladdress);

    conn = url.openConnection();

    conn.setDoOutput(true);

    osw = new OutputStreamWriter(conn.getOutputStream());

    osw.write(jsonData);

    osw.flush();


    // Get the response

    br = new BufferedReader(

    new InputStreamReader(conn.getInputStream()));

    String line;

    sb = new StringBuffer();

    while ((line = br.readLine()) != null) {

// Process line...

sb.append(line);

    }

} finally {

    if (osw != null)

osw.close();

    if (br != null)

br.close();

}


return sb.toString();

    }


    // --------------------- For Android --------------------------//


    private static final String KEY_FACTORY_ALGORITHM = "RSA";

    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";


    /**

     * Verifies that the data was signed with the given signature, and returns

     * the verified purchase. The data is in JSON format and signed

     * with a private key. The data also contains the {@link PurchaseState}

     * and product ID of the purchase.

     * @param base64PublicKey the base64-encoded public key to use for verifying.

     * @param signedData the signed JSON string (signed, not encrypted)

     * @param signature the signature for the data, signed with the private key

     */

    public static boolean verifyAndroidPurchase(String base64PublicKey, String signedData, String signature) {

        if (signedData == null) {

            return false;

        }


        boolean verified = false;

        if (!StringUtils.isEmpty(signature)) {

            PublicKey key = IapVerification.generatePublicKey(base64PublicKey);

            verified = IapVerification.verifyAndroidSignedData(key, signedData, signature);

            if (!verified) {

                return false;

            }

        }

        return true;

    }


    /**

     * Generates a PublicKey instance from a string containing the

     * Base64-encoded public key.

     *

     * @param encodedPublicKey Base64-encoded public key

     * @throws IllegalArgumentException if encodedPublicKey is invalid

     */

    public static PublicKey generatePublicKey(String encodedPublicKey) {

        try {

            byte[] decodedKey = Base64.decodeBase64(encodedPublicKey);

            KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);

            return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));

        } catch (NoSuchAlgorithmException e) {

            throw new RuntimeException(e);

        } catch (InvalidKeySpecException e) {

            throw new IllegalArgumentException(e);

        }

    }


    /**

     * Verifies that the signature from the server matches the computed

     * signature on the data.  Returns true if the data is correctly signed.

     *

     * @param publicKey public key associated with the developer account

     * @param signedData signed data from server

     * @param signature server signature

     * @return true if the data and signature match

     */

    public static boolean verifyAndroidSignedData(PublicKey publicKey, String signedData, String signature) {

        Signature sig;

        try {

            sig = Signature.getInstance(SIGNATURE_ALGORITHM);

            sig.initVerify(publicKey);

            sig.update(signedData.getBytes());

            if (!sig.verify(Base64.decodeBase64(signature))) {

                return false;

            }

            return true;

        } catch (NoSuchAlgorithmException e) {

            logger.error(e.getMessage());

        } catch (InvalidKeyException e) {

            logger.error(e.getMessage());

        } catch (SignatureException e) {

            logger.error(e.getMessage());

        }

        return false;

    }

}


저작자 표시 비영리 변경 금지
신고

AWS EC2 Instance를 AMI로 만드는 방법.

Programming 2012.11.26 22:57

AWS EC2에서 Instance를 AMI로 만드는 방법.


Developer Tools

AMI를 만들기위해서 사전에 필요한 것은 AWS계정과 Private Key, CERT파일입니다. 아마존에서 다양한 tools을 제공합니다. 그 중 AMI Tools와 EC2 Tools을 다운받아서 적절한 곳에 복사한 후 PATH 환경변수에 기록합니다.

EC2 Tools
AMI Tools


준비물.

 https://portal.aws.amazon.com/gp/aws/securityCredentials 페이지에 가서.. 

1. Private Key, Certification
X.509 Certifications 탭에 가서 없는 사람은 새로 Create a new Certificate 으로 만들면 Private Key가 자동으로 다운로드 될것이고 Public Key는 언제든 다운받을 수 있게 링크가 생길 것이다.
주의사항은 AWS는 PrivateKey를 저장하고 있지 않는다. private key를 따로 잘 저장해두길 바란다. (잃어버리면 새로 만들어야함~)

준비된 2개의 파일(pk-*****.pem, cert-*******.pem)을 해당 서버의 ~/.ec2/ 디렉토리로 복사하자.


2. Access Key, Secret Access Key

환경변수 잡기

export EC2_PRIVATE_KEY="$(/bin/ls "$HOME"/.ec2/pk-*.pem | /usr/bin/head -1)"
export EC2_CERT="$(/bin/ls "$HOME"/.ec2/cert-*.pem | /usr/bin/head -1)"
export EC2_HOME="/usr/local/ec2/apitools"
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."

Create Image

ec2-create-image instance_id -n "new instance name"



심각하게 주의사항!!

ec2-create-image를 실행하는 순간!!! 원본이 되는 instance가 리부팅 된다.
웹 콘솔에서 확인해보면 running으로 뜨는데.. 접속이 안된다.......-ㅅ-;;
만약 서비스중인 instance를 AMI로 등록하고자 한다면
http://lky1001.tistory.com/entry/ec2-ami-%EB%A7%8C%EB%93%A4%EA%B8%B0
위 방법대로 하자.



관련 링크 
- http://docs.amazonwebservices.com/AWSEC2/latest/CommandLineReference/ApiReference-cmd-CreateImage.html

- http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/creating-an-ami-ebs.html

- http://s3.amazonaws.com/awsVideos/CustomizeAnExistingAMI/Customize%20an%20Existing%20AMI.html

- http://geekdani.wordpress.com/2012/06/24/how-to-create-an-ec2-ami-amazon-machine-image/

- https://help.ubuntu.com/community/EC2StartersGuide

- http://lky1001.tistory.com/entry/ec2-ami-%EB%A7%8C%EB%93%A4%EA%B8%B0

저작자 표시 비영리 변경 금지
신고

Maven 사용시 외부 Jar를 maven pom.xml에 추가하는 방법.

Programming 2012.11.05 16:46

http://www.mkyong.com/maven/how-to-include-library-manully-into-maven-local-repository/


위 블로그를 참조하자.



쉽다.


일단 다운로드 받은 jar 파일을 Maven Install 해준다.

mvn install:install-file -Dfile=c:\kaptcha-2.3.jar -DgroupId=com.google.code -DartifactId=kaptcha -Dversion=2.3 -Dpackaging=jar

그다음.


pom.xml 에 추가만 해주면 끝!


<dependency>
      <groupId>com.google.code</groupId>
      <artifactId>kaptcha</artifactId>
      <version>2.3</version>
</dependency>



** 위 내용의 모든 저작권은  님께 있습니다.

저작자 표시 비영리 변경 금지
신고

Facebook API for Java Spring Framework (2013-10-08)

Programming 2012.10.08 21:06

페이스북 API들이 많은데.. 그중에 자바가 제대로 된게 없어서 고생을 하곤했는데..

최근에 다시 검색해보니..

으아니~! Springframework에서도 페이스북 뿐만 아니라 각종 Social용 API를 포함하는 라이브러리를 선보였넹!!


http://www.springsource.org/spring-social


일단 위 페이지가 공식 페이지!


여기서 나는 페이스북 API가 필요하다능!


자~ 그럼 한번 같이 만들어봅시다.


따라하기 전에 먼저...Facebook Developer 사이트에 가셔서 자신의 App을 만들어야 합니다! 이건 구글에 검색해보면 무지 많으니까 찾아보시오!

https://developers.facebook.com/apps


1. 필요한 라이브러리를 Maven으로 가져옵니다.

<properties>

...

<spring-social.version>1.0.2.RELEASE</spring-social.version>

<org.springframework.social-facebook-version>1.0.2.RELEASE</org.springframework.social-facebook-version>

</properties>


...


<dependency>

<groupId>org.springframework.social</groupId>

<artifactId>spring-social-core</artifactId>

<version>${spring-social.version}</version>

</dependency>

<dependency>

<groupId>org.springframework.social</groupId>

<artifactId>spring-social-web</artifactId>

<version>${spring-social.version}</version>

</dependency>

<dependency>

<groupId>org.springframework.social</groupId>

<artifactId>spring-social-facebook</artifactId>

<version>${org.springframework.social-facebook-version}</version>

</dependency>



2. 코드를 작성합니다.

( 공식 document에서는 - http://static.springsource.org/spring-social-facebook/docs/1.0.x/reference/html/connecting.html  Spring의 환경에 맞게 xml로 설정을 불러오게 되어있는데 저는 일단 만들어 테스트해보는게 목적이니까! JunitTest로 그냥 고고싱!)

import java.util.Calendar;

import java.util.List;


import org.junit.Before;

import org.junit.Test;

import org.springframework.social.connect.support.ConnectionFactoryRegistry;

import org.springframework.social.facebook.api.Facebook;

import org.springframework.social.facebook.api.FacebookProfile;

import org.springframework.social.facebook.api.impl.FacebookTemplate;

import org.springframework.social.facebook.connect.FacebookConnectionFactory;


public class FacebookAPITest {


    @Before

    public void setUp() throws Exception {

    }


    @Test

    public void test() {

Long st = Calendar.getInstance().getTimeInMillis();

ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();

registry.addConnectionFactory(new FacebookConnectionFactory("앱 ID/API 키", "앱 시크릿 코드"));

String accessToken = "액서스토큰"; // access token received from Facebook after OAuth authorization

Facebook facebook = new FacebookTemplate(accessToken);

FacebookProfile profile = facebook.userOperations().getUserProfile();

String myid = profile.getId();

String myname = profile.getName();

System.out.println("my id is '"+myid+"' and my name is '"+myname+".");

System.out.println("============= timestamp : "+(Calendar.getInstance().getTimeInMillis() - st));

st = Calendar.getInstance().getTimeInMillis();

List<FacebookProfile> friends = facebook.friendOperations().getFriendProfiles();

for(FacebookProfile fp : friends){

    System.out.println("Friend name => "+fp.getName());

}

System.out.println("============= timestamp : "+(Calendar.getInstance().getTimeInMillis() - st));

st = Calendar.getInstance().getTimeInMillis();

List<String> fids = facebook.friendOperations().getFriendIds();

for(String id : fids){

    System.out.println("friend id => "+id);

}

System.out.println("============= timestamp : "+(Calendar.getInstance().getTimeInMillis() - st));

st = Calendar.getInstance().getTimeInMillis();

    }


}


3. 실행~


my id is '1178*****' and my name is '*****'.

============= timestamp : 2156

Friend name => J***

Friend name => J****

Friend name => 김****

Friend name => Jon****

Friend name => Ch****

Friend name => Ha****

...

...

...



아...정말.정말.정말.정말 쉬워졌다....-_-!!

저작자 표시 비영리 변경 금지
신고

Proxy 설정시에 엄청난 트레픽과 이상한 요청!

Programming/Apache 2012.09.03 18:00

예전에 배포된 게임에 들어있는 주소로 뭔가 요청을 받던것을

새 도메인으로 옮기면서 기존것은 프록시를 걸었는데..


그 이후부터 서버에 엄청난 요청들이 쇄도 했다.


그 이유를 아래 블로그에서 상세히 밝혀주고있다.


너무 감사합니당.


http://theeye.pe.kr/entry/how-to-block-apache-with-proxy-remote-request


저작자 표시 비영리 변경 금지
신고

Mac 마운틴 라이언(Mountain Lion)에서 FTP 공유 켜기

Programming 2012.08.03 17:30

옛날엔 설정에 공유 메뉴에서 FTP를 켤 수 있었는데 어느순간 없어졌다..-_-


검색해본결과.. 터미널에서 직접 치면 된단다.


sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist


그럼 끄는건 어떻게하는거지?-_-?????

저작자 표시 비영리 변경 금지
신고

[MySql] Lock wait timeout exceeded; try restarting transaction

Programming 2012.07.27 12:34

출처 : http://blog.naver.com/w123654/10035121619

1205, 'Lock wait timeout exceeded; try restarting transaction'

 

mysql> show processlist;
+--------+------+--------------------+-------+---------+------+--------------+-+
| Id     | User | Host               | db    | Command | Time | State        | |
+--------+------+--------------------+-------+---------+------+--------------+-+
| 306835 | root | localhost          | dmsDB | Query   | 3651 | updating     | |
| 309090 | root | localhost          | dmsDB | Sleep   | 1018 |              | |
| 309199 | root | 192.168.1.231:2452 | dmsDB | Query   |   53 | Sending data | |
| 309288 | root | localhost          | NULL  | Query   |    0 | NULL         | |
+--------+------+--------------------+-------+---------+------+--------------+-+
4 rows in set (0.00 sec)

 

kill 306835
kill 309199

 

mysql> show processlist;
+--------+------+-----------+-------+---------+------+-------+-----------------+
| Id     | User | Host      | db    | Command | Time | State | Info            |
+--------+------+-----------+-------+---------+------+-------+-----------------+
| 306835 | root | localhost | dmsDB | Killed  | 4942 | end   | DELETE FROM t_do|
| 309090 | root | localhost | dmsDB | Sleep   | 2309 |       | NULL            |
| 309288 | root | localhost | NULL  | Query   |    0 | NULL  | show processlist|
+--------+------+-----------+-------+---------+------+-------+-----------------+

 

unlock tables;

저작자 표시 비영리 변경 금지
신고

Hive에서 Table Create시에 예약어에 걸릴때!!!

Programming 2012.07.23 17:21

보통 SQL에서 예약어와 컬럼명이 일치할때는 ' 으로 감싸준다.


구글 검색에도 HIVE에서도 동일 상황에서 ' 으로 감싸줘라고 되어있다.


하.지.만.


Java code에서 그렇게 넣어주면..


' 까지도 컬럼명으로 인식해 버린다.. 젝일슨.


그렇지만 ` 로 감싸주면 된다능!!!! -_-!!


그래서 예제는...


Create table `table` ( `timestamp` bigint, `string` string...) 


이러하다!

저작자 표시 비영리 변경 금지
신고

Unix timestamp를 formatted date로 변환해주는 간단한 페이지.

Programming 2012.07.19 17:56

Unix 시간 변환기.


http://www.onlineunitconversion.com/unix_time.html



 Time/Date 

Unix Time

Convert Unix timestamp to Readable Date/time
(based on seconds since standard epoch of 1/1/1970)
UNIX TimeStamp:
Convert a Date/Time to a Unix timestamp
(based on seconds since standard epoch of 1/1/1970)
Mon:Day:Year:Hr:Min:Sec:
//at::




저작자 표시 비영리 변경 금지
신고

drop all MongoDB MapReduce temp collections (tmp.mrs... collections)

Programming 2012.07.11 16:50

몽고디비에서 MapReduce 작업을 하다가 실패하는 경우 쓰래기 컬렉션들이 생성된채로 남아 있게 된다.


tmp.mrs.logs_1320387613_149
tmp.mrs.logs_1320387655_156
tmp.mrs.logs_1320390722_606
tmp.mrs.logs_1320395235_1164
tmp.mrs.logs_1320396089_1277
tmp.mrs.logs_1320405546_2308
tmp.mrs.logs_1320405750_2327
tmp.mrs.logs_1320405958_2352
... (you get the idea)


이런형태로..


이는 설정에서 쓰래기 컬렉션이 남지 않게 설정하면 되는것으로 알곤 있지만..


일단 생성된 녀석들을 한번에 drop 시키는 방법은..


db.system.namespaces.find({name: /tmp.mr/}).forEach(function(z) { try{ db.getMongo().getCollection( z.name ).drop(); } catch(err) {} });


이렇게 한줄로 샥~


몽고디비 역시 더러워...-_-;; 별루야 정말;


저작자 표시 비영리 변경 금지
신고


티스토리 툴바