한이음 공모전에 나가기 위해 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을 사용한다거나, 암호화 복호화를 한다거나 하는 내용이 낯설고 어렵게만 느껴져서 못할 것이라는 부정적인 생각이 강했지만, 하다보면서 얼마든지 할 수 있다는 것을 깨닫게 되었다.
이제 도전하는데 있어 겁먹지 말아야겠다는 생각을 하기도 했다.
'side project' 카테고리의 다른 글
[준비] Kotlin으로 Vue.js 와 통신하기 (0) | 2021.12.31 |
---|---|
[준비]하이브리드앱 환경 설정 With JAVA (4) | 2021.12.29 |
2020 한이음 공모전 후기 (0) | 2021.01.19 |