|
|
@ -1,5 +1,29 @@ |
|
|
|
package com.breadwallet.tools.security; |
|
|
|
|
|
|
|
import android.app.AlertDialog; |
|
|
|
import android.content.Context; |
|
|
|
import android.content.DialogInterface; |
|
|
|
import android.security.KeyPairGeneratorSpec; |
|
|
|
import android.util.Base64; |
|
|
|
import android.util.Log; |
|
|
|
import android.widget.Toast; |
|
|
|
import java.io.ByteArrayInputStream; |
|
|
|
import java.io.ByteArrayOutputStream; |
|
|
|
import java.math.BigInteger; |
|
|
|
import java.security.KeyPair; |
|
|
|
import java.security.KeyPairGenerator; |
|
|
|
import java.security.KeyStore; |
|
|
|
import java.security.KeyStoreException; |
|
|
|
import java.security.interfaces.RSAPrivateKey; |
|
|
|
import java.security.interfaces.RSAPublicKey; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Calendar; |
|
|
|
import java.util.List; |
|
|
|
import javax.crypto.Cipher; |
|
|
|
import javax.crypto.CipherInputStream; |
|
|
|
import javax.crypto.CipherOutputStream; |
|
|
|
import javax.security.auth.x500.X500Principal; |
|
|
|
|
|
|
|
/** |
|
|
|
* BreadWallet |
|
|
|
* <p/> |
|
|
@ -24,7 +48,141 @@ package com.breadwallet.tools.security; |
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
|
|
* THE SOFTWARE. |
|
|
|
*/ |
|
|
|
|
|
|
|
public class KeyStoreManager { |
|
|
|
public static final String TAG = KeyStoreManager.class.getName(); |
|
|
|
|
|
|
|
static final String CIPHER_TYPE = "RSA/ECB/PKCS1Padding"; |
|
|
|
static final String CIPHER_PROVIDER = "AndroidOpenSSL"; |
|
|
|
static List<String> keyAliases; |
|
|
|
private static KeyStoreManager instance; |
|
|
|
public static final String ALIAS = "phrase"; |
|
|
|
public static final String ANDROID_KEY_STORE = "AndroidKeyStore"; |
|
|
|
|
|
|
|
private static KeyStore keyStore; |
|
|
|
|
|
|
|
private KeyStoreManager(Context context) { |
|
|
|
try { |
|
|
|
keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); |
|
|
|
keyStore.load(null); |
|
|
|
creteKey(context); |
|
|
|
} catch (Exception e) { |
|
|
|
Log.e(TAG, "Cannot access keystore!", e); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public static synchronized KeyStoreManager getInstance(Context context) { |
|
|
|
if (instance == null) { |
|
|
|
instance = new KeyStoreManager(context); |
|
|
|
} |
|
|
|
return instance; |
|
|
|
} |
|
|
|
|
|
|
|
private void creteKey(Context context) { |
|
|
|
try { |
|
|
|
// Create new key if needed
|
|
|
|
if (!keyStore.containsAlias(ALIAS)) { |
|
|
|
Calendar start = Calendar.getInstance(); |
|
|
|
Calendar end = Calendar.getInstance(); |
|
|
|
end.add(Calendar.YEAR, 1); |
|
|
|
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context) |
|
|
|
.setAlias(ALIAS) |
|
|
|
.setSubject(new X500Principal("CN=Mihail Gutan, O=BreadWallet")) |
|
|
|
.setSerialNumber(BigInteger.ONE) |
|
|
|
.setStartDate(start.getTime()) |
|
|
|
.setEndDate(end.getTime()) |
|
|
|
.build(); |
|
|
|
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", ANDROID_KEY_STORE); |
|
|
|
generator.initialize(spec); |
|
|
|
|
|
|
|
KeyPair keyPair = generator.generateKeyPair(); |
|
|
|
} |
|
|
|
} catch (Exception e) { |
|
|
|
Toast.makeText(context, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show(); |
|
|
|
Log.e(TAG, Log.getStackTraceString(e)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void deleteKey(final Context context) { |
|
|
|
AlertDialog alertDialog = new AlertDialog.Builder(context) |
|
|
|
.setTitle("Delete Key") |
|
|
|
.setMessage("Do you want to delete the key \"" + ALIAS + "\" from the keystore?") |
|
|
|
.setPositiveButton("Yes", new DialogInterface.OnClickListener() { |
|
|
|
public void onClick(DialogInterface dialog, int which) { |
|
|
|
try { |
|
|
|
keyStore.deleteEntry(ALIAS); |
|
|
|
} catch (KeyStoreException e) { |
|
|
|
Toast.makeText(context, "Exception " + e.getMessage() + " occured", |
|
|
|
Toast.LENGTH_LONG).show(); |
|
|
|
Log.e(TAG, Log.getStackTraceString(e)); |
|
|
|
} |
|
|
|
dialog.dismiss(); |
|
|
|
} |
|
|
|
}) |
|
|
|
.setNegativeButton("No", new DialogInterface.OnClickListener() { |
|
|
|
public void onClick(DialogInterface dialog, int which) { |
|
|
|
dialog.dismiss(); |
|
|
|
} |
|
|
|
}) |
|
|
|
.create(); |
|
|
|
alertDialog.show(); |
|
|
|
} |
|
|
|
|
|
|
|
public void encryptString(String stringToEncrypt,Context context) { |
|
|
|
try { |
|
|
|
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(ALIAS, null); |
|
|
|
RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey(); |
|
|
|
|
|
|
|
if (stringToEncrypt.isEmpty()) { |
|
|
|
Toast.makeText(context, "Enter text in the 'Initial Text' widget", Toast.LENGTH_LONG).show(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL"); |
|
|
|
inCipher.init(Cipher.ENCRYPT_MODE, publicKey); |
|
|
|
|
|
|
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
|
|
|
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inCipher); |
|
|
|
cipherOutputStream.write(stringToEncrypt.getBytes("UTF-8")); |
|
|
|
cipherOutputStream.close(); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
Toast.makeText(context, "Exception " + e.getMessage() + " occurred", Toast.LENGTH_LONG).show(); |
|
|
|
Log.e(TAG, Log.getStackTraceString(e)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public String decryptString(Context context) { |
|
|
|
try { |
|
|
|
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(ALIAS, null); |
|
|
|
RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey(); |
|
|
|
|
|
|
|
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL"); |
|
|
|
output.init(Cipher.DECRYPT_MODE, privateKey); |
|
|
|
|
|
|
|
String cipherText = "Some string to decode"; |
|
|
|
CipherInputStream cipherInputStream = new CipherInputStream( |
|
|
|
new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), output); |
|
|
|
ArrayList<Byte> values = new ArrayList<>(); |
|
|
|
int nextByte; |
|
|
|
while ((nextByte = cipherInputStream.read()) != -1) { |
|
|
|
values.add((byte) nextByte); |
|
|
|
} |
|
|
|
|
|
|
|
byte[] bytes = new byte[values.size()]; |
|
|
|
for (int i = 0; i < bytes.length; i++) { |
|
|
|
bytes[i] = values.get(i).byteValue(); |
|
|
|
} |
|
|
|
|
|
|
|
String finalText = new String(bytes, 0, bytes.length, "UTF-8"); |
|
|
|
return finalText; |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
Toast.makeText(context, "Exception " + e.getMessage() + " occurred", Toast.LENGTH_LONG).show(); |
|
|
|
Log.e(TAG, Log.getStackTraceString(e)); |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|