How to verify ECDSA with Public Key? - android

I have the following methods for generating keys and signing the password message.
public void generateKeys() {
try {
keyStore = KeyStore.getInstance(KEYSTORE_NAME);
keyStore.load(null);
if (!keyStore.containsAlias(KEY_NAME)) {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, KEYSTORE_NAME);
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA256)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
// Require the user to authenticate with a fingerprint to authorize
// every use of the private key
.setUserAuthenticationRequired(true)
.build());
keyPairGenerator.generateKeyPair();
}
loadKeys();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public BiometricPrompt.CryptoObject getCryptoObject() {
cryptoObject = new BiometricPrompt.CryptoObject(signature);
return cryptoObject;
}
private void loadKeys() {
try {
keyStore = KeyStore.getInstance(KEYSTORE_NAME);
keyStore.load(null);
if (keyStore.containsAlias(KEY_NAME)) {
publicKey = keyStore.getCertificate(KEY_NAME).getPublicKey();
privateKey = (PrivateKey) keyStore.getKey(KEY_NAME, null);
signature = Signature.getInstance(Constants.SIGNATURE);
signature.initSign(privateKey);
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
public String sign(String inputStr) {
try {
Signature signature = cryptoObject.getSignature();
signature.update(inputStr.getBytes());
byte[] signedBytes = signature.sign();
String result = HexManager.bytesToHex(signedBytes);
Log.d("TAG", result);
return result;
} catch (SignatureException e) {
e.printStackTrace();
}
return null;
}
After that, I saved the signed password in Shared Preferences. Later, I want to verify the saved password with a new password that is verified with the fingerprint.
Here is my method to verify:
public boolean verify(String inputStr, String savedStr) {
try {
Signature signature = cryptoObject.getSignature();
signature.initVerify(publicKey);
signature.update(inputStr.getBytes());
boolean isVerified = signature.verify(savedStr.getBytes());
return isVerified;
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
return false;
}
But it always return false.
Does anybody knows why?

It looks like in your sign() method you're returning the Hex of the byte[] obtained from the signature.sign() method. If this is what you're saving as savedStr. Then the verification method should be changed to convert the Hex encoded string to a byte[]. You could use HexManager.hexToBytes() (or equivalent) if it exists to convert the savedStr to a byte[] savedStrBytes.
public boolean verify(String inputStr, String savedStr) {
try {
byte[] savedStrBytes = HexManager.hexToByes(savedStr);
Signature signature = cryptoObject.getSignature();
signature.initVerify(publicKey);
signature.update(inputStr.getBytes());
boolean isVerified = signature.verify(savedStrBytes);
return isVerified;
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
return false;
}

Related

Decrypting Encrypted data that has been encrypted as followed by the official google documentation

I encrypted data successfully using the biometric manager flow as shown by google but on trying the decrypt the encrypted data, I keep hitting the IllegalBlockSizeException clause. I need help please.
This is my class that handles all the fingerprint encryption( which works well) but decryption doesn't work, since it keeps hitting the IllegalBlockSizeException clause
public class FingerPrintEncrypter {
private static FingerPrintEncrypter fingerPrintEncrypter;
private static final String ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
private static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
private static final String ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
byte[] initVector;
private FingerPrintEncrypter(){
}
public static synchronized FingerPrintEncrypter getInstance(){
if(fingerPrintEncrypter == null){
fingerPrintEncrypter = new FingerPrintEncrypter();
}
return fingerPrintEncrypter;
}
/*
gets the Cipher obj in encryptionMode for encryption tasks.
*/
public Cipher getCipherForEncryption(String aliasKeyName){
try {
Cipher cipher = getCipher();
SecretKey secretKey = getOrCreateSecreteKey(aliasKeyName);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
initVector = cipher.getIV();
return cipher;
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
/*
gets the Cipher obj in decryptionMode for decryption tasks.
*/
public Cipher getCipherForDecryption(String aliasKeyName){
try{
Cipher cipher = getCipher();
SecretKey secretKey = getOrCreateSecreteKey(aliasKeyName);
IvParameterSpec decryptParamSpec = new IvParameterSpec(initVector);
cipher.init(Cipher.DECRYPT_MODE, secretKey, decryptParamSpec);
return cipher;
}catch (InvalidKeyException e){
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
private SecretKey getOrCreateSecreteKey(String aliasKeyName) {
try {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
SecretKey key = ((SecretKey)keyStore.getKey(aliasKeyName, null));
if(key == null){
// key previously create so just grab it
generateSecretKey(aliasKeyName);
}
return key;
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
return null;
}
private void generateSecretKey(String aliasKeyName){
try {
// key not create create a new one
KeyGenParameterSpec parameterSpec = new KeyGenParameterSpec.Builder(aliasKeyName,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(ENCRYPTION_BLOCK_MODE)
.setEncryptionPaddings(ENCRYPTION_PADDING)
.setUserAuthenticationRequired(true)
.build();
KeyGenerator keyGenerator = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM, ANDROID_KEY_STORE);
keyGenerator.init(parameterSpec);
keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
private Cipher getCipher() {
try {
return Cipher.getInstance(ENCRYPTION_ALGORITHM + "/"
+ ENCRYPTION_BLOCK_MODE + "/"
+ ENCRYPTION_PADDING);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
return null;
}
public String encryptData(String info, Cipher cipher){
try {
byte[] encryptedData = cipher.doFinal(info.getBytes("UTF-8"));
return Base64.encodeToString(encryptedData, Base64.NO_WRAP);
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public String decryptData(String stringToDecrypt, Cipher cipher){
try {
return new String(cipher.doFinal(Base64.decode(stringToDecrypt, Base64.NO_WRAP)));
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
}

AES/GCM/NoPadding decryption need TAG How to find Tag while encryption

Here is my encryption method also Decryption method its working fine with Tag Basically server need TAG is it possible server find Tag by my Hex String because every String i send Tag to the server and also with Hex, is it have any other optimal solution
aesKey=context.getString(R.string.aes_key).getBytes();
iv=context.getString(R.string.input_vector).getBytes();
public Map encrypt(String string){
byte[] plainText= new byte[0];
try {
plainText = string.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/GCM/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new IvParameterSpec(iv));
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] cipherText = new byte[0];
byte[] tag = new byte[0];
try {
cipherText = cipher.doFinal(plainText);
tag= Arrays.copyOfRange(cipherText, cipherText.length -16, cipherText.length);
cipherText= Arrays.copyOfRange(cipherText, 0, cipherText.length -16);
Log.d("testing", "encrypt: "+byteArrayToHexString(tag));
Map<String, Object> map=new HashMap();
map.put("content",byteArrayToHexString(cipherText));
map.put("tag",tag);
Log.d("testing", "encrypt: "+new Gson().toJson(map));
return map;
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return new HashMap();
}
Server need Hex so i convert it into Hex
public String byteArrayToHexString(final byte[] bytes) {
StringBuilder sb = new StringBuilder();
for(byte b : bytes){
sb.append(String.format("%02x", b&0xff));
}
return sb.toString();
}
My Decryption Function is that return me the String i receive Tag and Hex String from the server
public String decrypt(String content,byte[] tag){
byte[] cipherText = hexStringToByteArray(content+byteArrayToHexString(tag));
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/GCM/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new IvParameterSpec(iv));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
byte[] plainText = new byte[0];
try {
plainText = cipher.doFinal(cipherText);
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
try {
Log.d("testing", "encrypt: plain text:"+new String(plainText,"UTF-8"));
return new String(plainText,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}

ECDSA Android verify with Public Key always return false

I have some token and I need first to sign it with SHA256 and ECDSA based on private and public key from KeyStore.
Every time when I try to verify value I get false as result. I do not know why.
Does someone have any idea how to solve this?
Here are my functions to generate and load keys:
private void generateKeys(){
try {
keyStore = KeyStore.getInstance(KEYSTORE_NAME);
keyStore.load(null);
if(!keyStore.containsAlias(KEY_NAME)) {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, KEYSTORE_NAME);
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA512)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
.setUserAuthenticationRequired(false)
.build());
keyPairGenerator.generateKeyPair();
setRegistred(true);
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void loadKeys(){
try {
keyStore = KeyStore.getInstance(KEYSTORE_NAME);
keyStore.load(null);
if(keyStore.containsAlias(KEY_NAME)) {
publicKey = keyStore.getCertificate(KEY_NAME).getPublicKey();
privateKey = (PrivateKey) keyStore.getKey(KEY_NAME, null);
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
}
This is how sign value:
public String sign(String inputStr, FingerprintManager.CryptoObject cryptoObject){
try {
Signature signature = Signature.getInstance(SecurityConstants.SIGNATURE);
signature.initSign(privateKey);
signature.update(inputStr.getBytes());
byte[] signedBytes = signature.sign();
String result = Base64.encodeToString(signedBytes, Base64.DEFAULT);
Log.d("TAG", result);
return result;
} catch (SignatureException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
Here is my try to verify with Public Key:
public boolean verifyWithPublicKey(String input, FingerprintManager.CryptoObject cryptoObject){
try {
Signature signature = Signature.getInstance(SecurityConstants.SIGNATURE);
keyStore = KeyStore.getInstance(KEYSTORE_NAME);
keyStore.load(null);
PublicKey pk = getPublicKeyForVerification();
signature.initVerify(pk);
signature.update(input.getBytes());
boolean isVerifed = signature.verify(input.getBytes());
Log.d("TAG", String.valueOf(isVerifed));
return isVerifed;
} catch (SignatureException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
The error is here, when you write the following code to verify the signature:
signature.update(input.getBytes());
boolean isVerifed = signature.verify(input.getBytes());
With this code, you try to verify that the signature has been signed with itself!
You should have:
signature.update(MY_BYTES_ARRAY_OF_DATA);
boolean isVerifed = signature.verify(MY_SIGNATURE);
Do not forget that the signature does not encapsulate the signed data by default.
If you want to have a format that includes signed data and associated signature, use S/MIME, OpenPGP, etc.

Is it safe using AndroidKeyStore to generate a password of a database?

My app have a database, and I need a password to encrypt it.
If I store the hardcode password in java or in JNI, it is easy to hack.
Is it safer using AndroidKeyStore to generate a password? (I will recreate a copied db when the password changed.) So the hacker cannot easily to get the password.
Here is my code. I use public key of a KayPair to be the password:
private String generateAcStr() {
String keyAlias = "DBAlias";
KeyStore keystore = null;
try {
keystore = KeyStore.getInstance(ANDROID_KEY_STORE);
keystore.load(null);
if(!keystore.containsAlias(keyAlias)) {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", ANDROID_KEY_STORE);
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.MONTH, 1);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
.setAlias(keyAlias)
.setSubject(new X500Principal("CN=Custer Name, O=Android Authority"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.setKeySize(1024)
.build();
generator.initialize(spec);
generator.generateKeyPair();
}
KeyStore.PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keystore.getEntry(keyAlias, null);
// use the public key to be the password
return Base64.encodeToString(privateKeyEntry.getCertificate().getPublicKey().getEncoded(), Base64.DEFAULT);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
}
return null;
}
Thanks for helping me.

AES with CFB Mode

I want to encode my Url-params with Base64 and crypt(AES with CFB Mode), but it will not working and I can't get the error. Can someone help me? Here is my code:
private String toBase64Crypt(String cryptString) {
try {
SecretKeySpec key = new SecretKeySpec(pwd.getBytes("UTF8"), "AES");
byte[] cryptByte = cryptString.getBytes("UTF8");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
cryptString = Base64.encodeToString(cipher.doFinal(cryptByte),Base64.DEFAULT);
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return cryptString;
}

Categories

Resources