side project

한이음 개발완료 후기

UR'im 2019. 10. 29. 23:16

한이음 공모전에 나가기 위해 2019년 1월부터 준비해서 6월 본격적인 개발에 들어갔다.

우리팀은 암호화된 정보를 QR로 만들어 특정 택배 기사만이 그 QR을 인식할 수 있는 기능을 만들었다.

개인정보를 QR로 넣어 외부 침입자가 볼 수 없게 하고, 택배기사님들은 빠르게 연락을 취할 수 있도록 했다.

 

여기서 내가 맡은 역할은 안드로이드 개발이었는데, 

나는 QR을 인식 할 수 있게 바코드 라이브러리를 사용하여 QR인식

//qr 인식
        surfaceView = (SurfaceView) view.findViewById(R.id.barcodeScanner);
        barcodeDetector = new BarcodeDetector.Builder(getContext()).setBarcodeFormats(Barcode.QR_CODE).build();

        cameraSource = new CameraSource.Builder(context, barcodeDetector).setRequestedPreviewSize(640, 480).build();
        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                    return;
                }
                try {
                    cameraSource.start(holder);
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                cameraSource.stop();
            }
        });
        barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
            @Override
            public void release() {

            }

            @Override
            public void receiveDetections(Detector.Detections<Barcode> detections) {
                final SparseArray<Barcode> qrBarcode = detections.getDetectedItems();
                final Intent intent = new Intent(getContext(), CustomerInformationActivity.class);
                if(qrReadChecker == false){
                    if(qrBarcode.size() > 0){
                        qrReadChecker = true;
                        try{
                            Log.e("bar code", qrBarcode.valueAt(0).displayValue.toString()+"");
                            //복호화
                            String text = decrypt(qrBarcode.valueAt(0).displayValue);

                            //json 형식으로 변환
                            startActivityForResult(intent, REQUEST_ACT);
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

을 만들고,

택배 기사들이 회원 가입시 사용할 수 있는 RSA알고리즘을 사용한 키생성, keyStore를 사용해서 안드로이드 앱에 키를 자동 저장하여 필요시 사용할 수 있게 하는 것, 처음 코드를 작성했을 때 암호화 복호화가 안되는 것을 보고 다양한 시도 끝에 멘토님께서 알려주셔서 다행이 해결 할 수 있었다.

 

내가 작성했던 코드의 문제는 키 생성시

// key 생성
                try {
                    keyPairGenerator = KeyPairGenerator.getInstance(
                            KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
                    keyPairGenerator.initialize(
                            new KeyGenParameterSpec.Builder(
                                    "....",
                                    KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT)
                                    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                                    .setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
                                    .setDigests(KeyProperties.DIGEST_SHA256 , KeyProperties.DIGEST_SHA512)
                                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
                                    .build());
                } catch (Exception e) {
                    Log.e("catch error",e.getMessage()+"");
                    Log.e("##############키생성실패", "키생성실패");
                }

패딩 맞춰 주는 것과 Digests를 맞춰주는 것 등등이 부족했기 때문이었다. 멘토님 덕분에 몰랐던 것을 알 수 있었다.

 

마지막으로 정상적인 키 생성후 서버에서 암호화한 것을 복호화 하는 코드를 작성했다. 안드로이드 코드에는 문제가 없었지만, 파이썬을 사용하는 서버에서 암호화 하는 부분에서 문제가 생겨 정상적으로 복호화 하는것에 어려움이있었다. 하지만 이 문제 또한 멘토님의 도움으로 해결 할 수 있었다.

암호화 복호화 라이브러리를 사용해서 서버에서 암호화 한 것을 안드로이드 단에서 복호화 하여 사용할 수 있게 했다.

public String decrypt(String encryptedText){
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            PrivateKey privateKey = (PrivateKey) keyStore.getKey("....", null);
            Log.e("pk", privateKey+"");
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            Log.e("encryptedText", encryptedText);
            byte[] base64Decrypte = android.util.Base64.decode(encryptedText.getBytes("UTF-8"), android.util.Base64.DEFAULT);
            byte[] decryptedText = cipher.doFinal(base64Decrypte);
            finalText = new String(decryptedText);
            Log.e("decryted text", ""+finalText);
            return finalText;
        } catch (UnrecoverableKeyException | IOException | KeyStoreException | CertificateException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
            Log.e("catch error ",Log.getStackTraceString(e)+"");
            return "실패";
        }
    }

키스토어 라이브러리를 사용하여 저장해 둔 개안키를 추출하여 Cipher 객체를 사용하여 내가 키 생성시 주었던 옵션과 맞는 알고리즘으로 설정하고, 입력받은 String 타입의 텍스트를 한글에서 사용할 수 있도록 "UTF-8"의 바이트로 받고 그 바이트 배열을 Base64를 사용하여  인코딩, 디코딩 해야만 정상적으로 한글을 사용할 수 있었다...

 

아래의 코드는 만약 키스토어를 사용한 키생성및 암호화가 어려울 경우 라이브러리가 아닌 알고리즘 소스를 만든 것이다..

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import java.util.Random;
import java.util.Scanner;
 
public class RSA
{
    private BigInteger p;
    private BigInteger q;
    private BigInteger N;
    private BigInteger phi;
    private BigInteger e;
    private BigInteger d;
    private int        bitlength = 2048;
    private Random     r;
    public RSA()
    {
        r = new Random();
        p = BigInteger.probablePrime(bitlength, r);
        q = BigInteger.probablePrime(bitlength, r);
        N = p.multiply(q);
        phi = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
        e = BigInteger.probablePrime(bitlength / 2, r);
        while (phi.gcd(e).compareTo(BigInteger.ONE) > 0 && e.compareTo(phi) < 0)
        {
            e.add(BigInteger.ONE);
        }
        d = e.modInverse(phi);
    }
 
    public RSA(BigInteger e, BigInteger d, BigInteger N)
    {
        this.e = e;
        this.d = d;
        this.N = N;
    }
 
    @SuppressWarnings("deprecation")
    public static void main(String[] args) throws IOException
    {

        Encoder encoder = Base64.getEncoder();
        Decoder decoder = Base64.getDecoder();
        RSA rsa = new RSA();
        String teststring = "{pid: 1, toname: \"고유림\", tophone: \"010-1111-1111\"}";
        byte[] byteTypeData = teststring.getBytes("UTF-8");
        byte[] encodeByteData = encoder.encode(byteTypeData);
        System.out.println("String in Bytes: "
                + bytesToString(encodeByteData));
        byte[] encrypted = rsa.encrypt(encodeByteData);
        String strEncrypted =  new String(encodeByteData, "UTF-8");
        System.out.println("Encrypted Bytes: " + bytesToString(encodeByteData));
        System.out.println("Encrypted String:  "+ bytesToString(strEncrypted.getBytes("UTF-8")));
        // decrypt
        byte[] decrypted = rsa.decrypt(encrypted);
        byte[] base64Decode = decoder.decode(decrypted);
        System.out.println("Decrypted String: " + new String(base64Decode, "UTF-8"));
    }
 
    private static String bytesToString(byte[] encrypted) throws UnsupportedEncodingException
    {
        String test = "";
        for (byte b : encrypted)
        {
            test += Byte.toString(b)+",";
        }
        return test;
    }
 
    // Encrypt message
    public byte[] encrypt(byte[] message){
        return (new BigInteger(message)).modPow(e, N).toByteArray();
    }
 
    // Decrypt message
    public byte[] decrypt(byte[] message){
        return (new BigInteger(message)).modPow(d, N).toByteArray();
    }
}

이를 통해 잘 알지 못했던 RSA알고리즘에 대하여 자세히 알게 되었고, 부족했던 부분을 채워나갈 수 있었던 것 같다.

 

물론 나의 능력으로는 부족한 부분이 많아 멘토님과, 다양한 커뮤니티 사람들의 도움을 받았고, 덕분에 다양한 많은 것을 알고 배울 수 있었다.

 

처음 QR을 사용한다거나, 암호화 복호화를 한다거나 하는 내용이 낯설고 어렵게만 느껴져서 못할 것이라는 부정적인 생각이 강했지만, 하다보면서 얼마든지 할 수 있다는 것을 깨닫게 되었다.

 

이제 도전하는데 있어 겁먹지 말아야겠다는 생각을 하기도 했다.