반응형

완성코드

public class SecurityManager {

    private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
    private static final String ALIAS = "yourSecretKey";//키값 숨겨야함..
    private static final String TAG = "[SecurityManager]";
    private static String crypto = "RSA/ECB/PKCS1Padding";


    private static SecurityManager sInstance;
    public static SecurityManager getInstance(Context context) {
        if (sInstance == null) {
           sInstance = new SecurityManager(context.getApplicationContext());
        } return sInstance;
    }
    private Context mContext;
    private SecurityManager(Context context) {
        mContext = context;
    }

    private KeyStore.Entry createKeys() throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableEntryException, NoSuchProviderException, InvalidAlgorithmParameterException {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
        keyStore.load(null);

        if (!keyStore.containsAlias(ALIAS)) {
            //키스토어 접근
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", ANDROID_KEY_STORE);
//            KeyPairGenerator kpg = KeyPairGenerator.getInstance("AES", "AndroidKeyStore");
            Calendar start = Calendar.getInstance(Locale.ENGLISH);
            Calendar end = Calendar.getInstance(Locale.ENGLISH);
            end.add(Calendar.YEAR, 25);

            //KeyPairGeneratorSpec deprecated
            //mContext.getApplicationContext() -> service , acirivity등을 벗어낫을때 context가 의미가 없어질 수 있음
            KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(mContext.getApplicationContext())
                    .setAlias(ALIAS)
                    .setKeySize(4096)// 512byte
                    .setSubject(new X500Principal("CN=" + ALIAS))
                    .setSerialNumber(BigInteger.valueOf(123456))
                    .setStartDate(start.getTime())
                    .setEndDate(end.getTime())
                    .build();

            kpg.initialize(spec);
            kpg.generateKeyPair();
        }

        return keyStore.getEntry(ALIAS, null);
    }


    private static byte[] encryptUsingKey(PublicKey publicKey, byte[] bytes)  throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        Cipher inCipher = Cipher.getInstance(crypto);
        inCipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return inCipher.doFinal(bytes);
    }

    private static byte[] decryptUsingKey(PrivateKey privateKey, byte[] bytes) throws NoSuchAlgorithmException,  NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException{
        Cipher inCipher = Cipher.getInstance(crypto);
        inCipher.init(Cipher.DECRYPT_MODE, privateKey);
        return inCipher.doFinal(bytes);
    }


    public String encrypt(String plainText) throws NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableEntryException, NoSuchProviderException, InvalidAlgorithmParameterException {
        KeyStore.Entry entry = createKeys();
        if (entry instanceof KeyStore.PrivateKeyEntry) {
            Certificate certificate = ((KeyStore.PrivateKeyEntry) entry).getCertificate();
            PublicKey publicKey = certificate.getPublicKey();
            byte[] bytes = plainText.getBytes("UTF-8");
            byte[] encryptedBytes = encryptUsingKey(publicKey, bytes);
            byte[] base64encryptedBytes = Base64.encode(encryptedBytes, Base64.DEFAULT);
            return new String(base64encryptedBytes);
        }
        return null;
    }
    public String decrypt(String cipherText) throws NoSuchAlgorithmException,  NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, KeyStoreException, CertificateException, IOException, UnrecoverableEntryException {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
        keyStore.load(null);

        KeyStore.Entry entry = keyStore.getEntry(ALIAS, null);
        if (entry instanceof KeyStore.PrivateKeyEntry) {
            PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
            byte[] bytes = cipherText.getBytes("UTF-8");
            byte[] base64encryptedBytes = Base64.decode(bytes, Base64.DEFAULT);
            byte[] decryptedBytes = decryptUsingKey(privateKey, base64encryptedBytes);
            return new String(decryptedBytes);
        }
        return null;
    }

}

 

사용

String cipherText  = SecurityManager.getInstance(getContext()).encrypt("암호화 문구");
String decryptText = SecurityManager.getInstance(getContext()).decrypt(cipherText));

 

 


뜯어보기

    private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
    private static final String ALIAS = "yourSecretKey";//키값 숨겨야함..
    private static final String TAG = "[SecurityManager]";
    private static String crypto = "RSA/ECB/PKCS1Padding";

주요하게 봐야할 부분은 ALIAS와 crypto부분입니다.

 

ALIAS

     암호화 키를 생성할때 중요한 암호화 값입니다. 자신만 아는곳에 값을 숨겨서 키를 생성해야합니다.

 

crypto

     RSA / ECB / PKCS1Padding 방식을 채택해 암호화합니다 Cipher객체를 생성할때 사용합니다.

 

RSA암호화 방식 : https://namu.wiki/w/RSA%20%EC%95%94%ED%98%B8%ED%99%94

 

RSA 암호화 - 나무위키

RSA 방식으로 암호화를 하기 위해선 먼저 키를 만들어야 한다. 그 과정은 다음과 같다. 두 소수 p,q p , q p,q 를 준비한다.[4]p−1, q−1 p - 1,\ q - 1 p−1, q−1과 각각 서로소인 정수 eee[5]를 준비한다.[6]ededed를 (p−1)(q−1)(p - 1)(q - 1)(p−1)(q−1)으로 나눈 나머지가 1이 되도록 하는 ddd[7]를 찾는다.[8][9]N=pqN = pqN=pq를 계산한 후, NNN와 eee를 공개한

namu.wiki

 

ECB 모드 : 블록형식으로 암호화하는 방법입니다.

 

pkcs1 padding : 데이터를 블럭으로 암호화 할때 평문이 항상 블럭 크기(일반적으로 64비트 또는 128비트)의 배수가 되지 않는다. 패딩은 어떻게 평문의 마지막 블록이 암호화 되기 전에 데이터로 채워지는가를 확실히 지정하는 방법이다. 복호화 과정에서는 패딩을 제거하고 평문의 실제 길이를 지정하게 된다.

 

 

 private KeyStore.Entry createKeys() throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableEntryException, NoSuchProviderException, InvalidAlgorithmParameterException {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
        keyStore.load(null);

        if (!keyStore.containsAlias(ALIAS)) {
            //키스토어 접근
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", ANDROID_KEY_STORE);
//            KeyPairGenerator kpg = KeyPairGenerator.getInstance("AES", "AndroidKeyStore");
            Calendar start = Calendar.getInstance(Locale.ENGLISH);
            Calendar end = Calendar.getInstance(Locale.ENGLISH);
            end.add(Calendar.YEAR, 25);

            //KeyPairGeneratorSpec deprecated
            //mContext.getApplicationContext() -> service , acirivity등을 벗어낫을때 context가 의미가 없어질 수 있음
            KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(mContext.getApplicationContext())
                    .setAlias(ALIAS)
                    .setKeySize(4096)// 512byte
                    .setSubject(new X500Principal("CN=" + ALIAS))
                    .setSerialNumber(BigInteger.valueOf(123456))
                    .setStartDate(start.getTime())
                    .setEndDate(end.getTime())
                    .build();

            kpg.initialize(spec);
            kpg.generateKeyPair();
        }

        return keyStore.getEntry(ALIAS, null);
    }

 

-  기본 설정햇을땐 256 바이트가 넘으면 오류가 낫다. 

   .setKeySize(4096)// 512byte 

    키 사이즈를 늘려 더 긴 택스트를 수용하도록 수정하였다.

 

   * RSA 암호화 방식은 키값보다 암호화하려는 text의 바이트값이 크면 암호화 할수 없다.

 

 

 

 

참고 : https://android.jlelse.eu/storing-data-securely-on-android-keystore-asymmetric-83b1dc5f47db

 

Storing data securely on Android-KeyStore Asymmetric

Saving data securely on Android devices is one of the most challenging problem. I will investigate some methods with their pros and cons.

android.jlelse.eu

 

반응형

+ Recent posts