비밀키/공개키를 이용한 서명 및 검증을 활용한 Google in-app 서버사이드 검증

String keystore = “keystore.jks”; String alias = “test”; String passphrase = “password”; KeyStore ks = KeyStore.getInstance(“JKS”); InputStream readStream = new FileInputStream(keystore); ks.load(readStream, passphrase.toCharArray()); PrivateKey privateKey = (PrivateKey)ks.getKey(alias, passphrase.toCharArray()); Certificate cert = ks.getCertificate(alias); PublicKey publicKey = cert.getPublicKey(); readStream.close(); // 서명할 데이터 원본 String data = “this is sample text.”; Signature sig = Signature.getInstance(“SHA1withRSA”); // 서명하기 sig.initSign(privateKey); sig.update(data.getBytes()); String signedValue = new String(Base64.encodeBase64(sig.sign())); System.out.println(signedValue); // 검증하기 sig.initVerify(publicKey); sig.update(data.getBytes()); System.out.println(“verified => ” + sig.verify(Base64.decodeBase64(signedValue.getBytes()))); Google Play의 in-app결제에서 서버검증할때는 위의 원리를 활용합니다. 개발콘솔에서 app을 업로드하면 콘솔에 공개키 정보가 표시됩니다. 이 정보는 app을 서명할때 작성한 키정보가 아니라 Google이 자동으로 생성해주는 비밀키/공개키의 키페어중에서 공개키의 내용입니다. (비밀키는 공개되지 않습니다.) 원리는 이렇습니다. 1. 결제를 하게 되면 Google에서 보관하고 있는 비밀키로 서명을 합니다. 2. app에서는 아래와 같이 데이터를 가져옵니다. String purchaseData = data.getStringExtra(“INAPP_PURCHASE_DATA”); String dataSignature = data.getStringExtra(“INAPP_DATA_SIGNATURE”); 3. 물론 app내부에서도 서명을 검증합니다. 이때 검증은 공개키를 이용합니다. 4. 서버측에서 포인트를 부여하는 경우는 좀 더 고려해야 하며 가장 좋은 방법은 서버측에서도 검증을 하면 좋습니다. 5. purchaseData와 dataSignature를 서버에 송신합니다. 6. 서버측에서도 공개키를 활용하여 서명을 검증한 후에 포인트를 부여합니다. 7. 단, 이미 처리된 orderId에 대해서는 에러처리 합니다. 공격자는 purchaseData를 수정하여 서버에 보내더라도 (비밀키가 없으므로) dataSignature값을 계산할 수 없습니다. 다만 본인의 결제처리된 이미 검증된 dataSignature값을 이용한 재송신이 있을 수 있으므로 이를 위해 이미 처리된 orderId를 체크하게 됩니다. 다른 사람의 정상처리된 purchaseData를 보낼 수도 있으므로 developerPayload에 본인의 User Id등을 넣어두면 좋겠네요. ]]>

Related Posts