반응형
완성코드
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
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
반응형
'AOS' 카테고리의 다른 글
(안드로이드)manifest에서 activity list 가저오기 (0) | 2020.05.03 |
---|---|
(안드로이드) program type already present:BuildConfig (0) | 2020.04.14 |
(안드로이드)recycleview 만들기 1편 (기본) (0) | 2020.02.27 |
(안드로이드) webview 관련 정리2 (0) | 2020.02.14 |
(안드로이드) interface를 이용한 ADID 가저오기 (0) | 2020.02.03 |