Sign JWT with PrivateKey from android Fingerprint API - android

I have some claims and I want to create JWT and sign it with a PrivateKey created in Fingerprint API.
This is the JWT claim -
Header:
{
"alg": "RS256”,
“kid”: “ABCDEDFkjsdfjaldfkjg”,
“auth_type” : “fingerprint” / "pin"
}
Payload:
{
“client_id”:”XXXXX-YYYYYY-ZZZZZZ”
}
Creating KeyPair for fingerprint -
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.support.annotation.RequiresApi;
import android.util.Log;
import com.yourmobileid.mobileid.library.common.MIDCommons;
import org.jose4j.base64url.Base64;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.spec.RSAKeyGenParameterSpec;
#RequiresApi(api = Build.VERSION_CODES.M)
public class BiometricHelper {
public static final String KEY_NAME = "my_key";
static KeyPairGenerator mKeyPairGenerator;
private static String mKid;
private static KeyStore keyStore;
public static void init() {
try {
mKeyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new RuntimeException("Failed to get an instance of KeyPairGenerator", e);
}
mKid = MIDCommons.generateRandomString();
keyStore = null;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (KeyStoreException e) {
throw new RuntimeException("Failed to get an instance of KeyStore", e);
}
createKeyPair();
}
/**
* Generates an asymmetric key pair in the Android Keystore. Every use of the private key must
* be authorized by the user authenticating with fingerprint. Public key use is unrestricted.
*/
public static void createKeyPair() {
try {
mKeyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
.build());
mKeyPairGenerator.generateKeyPair();
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
}
public static PrivateKey getPrivateKey() {
PrivateKey privateKey = null;
try {
keyStore.load(null);
privateKey = (PrivateKey) keyStore.getKey(KEY_NAME, null);
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
return privateKey;
}
public static PublicKey getPublicKey() {
PublicKey publicKey = null;
try {
keyStore.load(null);
publicKey = keyStore.getCertificate(KEY_NAME).getPublicKey();
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
return publicKey;
}
public static KeyStore getKeyStore(){
return keyStore;
}
public static String getPublicKeyStr() {
StringBuilder publicKey = new StringBuilder("-----BEGIN PUBLIC KEY-----\n");
publicKey.append(Base64.encode((getPublicKey().getEncoded())).replace("==",""));
publicKey.append("\n-----END PUBLIC KEY-----");
Log.d("Key==","\n"+publicKey);
return publicKey.toString();
}
public static String getKid() {
Log.d("mKid==","\n"+mKid);
return mKid;
}
}
And creating JWT this way -
#RequiresApi(api = Build.VERSION_CODES.M)
private String createJWT(){
JwtClaims claims = new JwtClaims();
claims.setClaim("client_id","”XXXXX-YYYYYY-ZZZZZZ”");
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(BiometricHelper.getPrivateKey());
jws.setKeyIdHeaderValue(BiometricHelper.getKid());
jws.setHeader("auth_type","fingerprint");
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
String jwt = null;
try {
jwt = jws.getCompactSerialization();
} catch (JoseException e) {
e.printStackTrace();
}
System.out.println("JWT: " + jwt);
return jwt;
}
When i am doing this it am getting -
W/System.err: org.jose4j.lang.InvalidKeyException: The given key (algorithm=RSA) is not valid for SHA256withRSA
W/System.err: at org.jose4j.jws.BaseSignatureAlgorithm.initForSign(BaseSignatureAlgorithm.java:97)
W/System.err: at org.jose4j.jws.BaseSignatureAlgorithm.sign(BaseSignatureAlgorithm.java:68)
W/System.err: at org.jose4j.jws.JsonWebSignature.sign(JsonWebSignature.java:101)
I tried many other way for signing JWT with PrivateKey so far i did not find solution.
Any help is appreciated

You have created a key for encryption only, not for signing. Change
mKeyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
.build());
With
mKeyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
.build());

Using gradle dependency
compile group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '4.41.1'
library I am able to fix the issue and sign JWT using AndroidKeyStoreRSAPrivateKey
Here RSASSASigner constructor which takes PrivateKey from Android KeyStore and this signer is used to sign JWSObject.
While looking for solution I did not find much information on this on Web so posting solution here for how to Sign JWT using PrivateKey from android Fingerprint API. Thanks pedrofb for you help :)
#RequiresApi(api = Build.VERSION_CODES.M)
private String createJWT(){
RSASSASigner signer = new RSASSASigner(BiometricHelper.getPrivateKey());
JSONObject message = new JSONObject();
message.put("client_id",mConfiguration.getClientID());
JWSObject jwsObject = new JWSObject(
new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(BiometricHelper.getKid())
.customParam("auth_type","touchid").build(),new Payload(message ));
try {
jwsObject.sign(signer);
} catch (JOSEException e) {
e.printStackTrace();
}
String jwt = jwsObject.serialize();
Log.d("JWT============","\n"+jwt);
return jwt;
}
While working on this thing i came across some bug reported in Nimbus-JOSE-JWT older version https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169/android-m-support

For anyone reading finding this question and the answers, it's worth mentioning that this key is NOT finger print protected - (setUserAuthenticationRequired(true) is not set on the key, and BiometricPrompt is not being used to approve the signing operation.
To do this correctly with jose4j you need to use it's jws.prepareSigningPrimitive() method - https://bitbucket.org/b_c/jose4j/issues/176/signing-not-possible-with-an has a discussion and a link to a full example.

Related

Android Java Paho MQTT connect with Google Iot Core attach Google root CA certification package

I am trying to connect with google iot core from android by paho mqtt library. I am able to connect and initiate publish. But just after connect its got disconnected and the publish failed with the exception that connection lost. After some googling i found that i have to attach Google root CA certification package with the mqtt client. After some research i found a way to mention custom socket factory(which takes the certificate) to MqttConnectOptions. Now the problem is i got the following exception:
java.security.cert.CertificateException:
com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException:
com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException:
java.lang.RuntimeException: error:0c0000be:ASN.1 encoding
routines:OPENSSL_internal:WRONG_TAG
Please help me.
Here is the android code details:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mqttapp">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.MQTTApp">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="org.eclipse.paho.android.service.MqttService"/>
</application>
</manifest>
Project level build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.3"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
module level build.gradle
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.mqttapp"
minSdkVersion 26
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
maven {
url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
implementation 'io.jsonwebtoken:jjwt:0.7.0'
implementation 'net.danlew:android.joda:2.10.9.1'
implementation 'commons-cli:commons-cli:1.4'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.56'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
MainActivity.java
package com.example.mqttapp;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import java.io.InputStream;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String clientId = "projects/<project_id>/locations/<region>/registries/<registry>/devices/<device>";
String topic = "/devices/<device>/<topics>";
String content = "Message from MqttPublishSample";
int qos = 1;
String broker = "ssl://mqtt.googleapis.com:8883";
MemoryPersistence persistence = new MemoryPersistence();
try {
IotCorePasswordGenerator passwordGenerator = new IotCorePasswordGenerator("project_id", getResources(), R.raw.private_key);
MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setUserName("unused");
InputStream inputStream = getResources().openRawResource(R.raw.roots);
SocketFactory.SocketFactoryOptions socketFactoryOptions = new SocketFactory.SocketFactoryOptions();
try {
socketFactoryOptions.withCaInputStream(getResources().openRawResource(R.raw.roots));
connOpts.setSocketFactory(new SocketFactory(socketFactoryOptions));
} catch (Exception e) {
*e.printStackTrace();*//exception occurs here
}
connOpts.setPassword(passwordGenerator.createJwtRsaPassword());
connOpts.setCleanSession(true);
connOpts.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
Log.d("tag_bug_bug","Connecting to broker: "+broker);
sampleClient.connect(connOpts);
Log.d("tag_bug_bug","Connected");
Log.d("tag_bug_bug","Publishing message: "+content);
MqttMessage message = new MqttMessage(content.getBytes());
message.setQos(qos);
sampleClient.publish(topic, message);
Log.d("tag_bug_bug","Message published");
//sampleClient.disconnect();
Log.d("tag_bug_bug","Disconnected");
//System.exit(0);
} catch(Exception me) {
Log.d("tag_bug_bug","msg "+me.getMessage());
Log.d("tag_bug_bug","loc "+me.getLocalizedMessage());
Log.d("tag_bug_bug","cause "+me.getCause());
Log.d("tag_bug_bug","excep "+me);
me.printStackTrace();
}
}
}
IotCorePasswordGenerator.java
package com.example.mqttapp;
import android.content.res.Resources;
import android.util.Base64;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.security.cert.X509Certificate;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
class IotCorePasswordGenerator {
private final String projectId;
private final Resources resources;
private final int privateKeyRawFileId;
IotCorePasswordGenerator(String projectId, Resources resources, int privateKeyRawFileId) {
this.projectId = projectId;
this.resources = resources;
this.privateKeyRawFileId = privateKeyRawFileId;
}
char[] createJwtRsaPassword() {
try {
byte[] privateKeyBytes = decodePrivateKey(resources, privateKeyRawFileId);
return createJwtRsaPassword(projectId, privateKeyBytes).toCharArray();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Algorithm not supported. (developer error)", e);
} catch (InvalidKeySpecException e) {
throw new IllegalStateException("Invalid Key spec. (developer error)", e);
} catch (IOException e) {
throw new IllegalStateException("Cannot read private key file.", e);
}
}
private static byte[] decodePrivateKey(Resources resources, int privateKeyRawFileId) throws IOException {
try(InputStream inStream = resources.openRawResource(privateKeyRawFileId)) {
return Base64.decode(inputToString(inStream), Base64.DEFAULT);
}
}
private static String inputToString(InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
private static String createJwtRsaPassword(String projectId, byte[] privateKeyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
try {
CertificateFactory fact = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
e.printStackTrace();
}
return createPassword(projectId, privateKeyBytes, "RSA", SignatureAlgorithm.RS256);
}
private static String createPassword(String projectId, byte[] privateKeyBytes, String algorithmName, SignatureAlgorithm signatureAlgorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
Instant now = Instant.now();
// The device will be disconnected after the token expires, and will have to reconnect with a new token.
// The audience field should always be set to the GCP project id.
JwtBuilder jwtBuilder =
Jwts.builder()
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(now.plus(Duration.ofMinutes(20))))
.setAudience(projectId);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory kf = KeyFactory.getInstance(algorithmName);
return jwtBuilder.signWith(signatureAlgorithm, kf.generatePrivate(spec)).compact();
}
}
SocketFactory.java
package com.example.mqttapp;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.security.cert.CertificateException;
/**
* Original SocketFactory file taken from https://github.com/owntracks/android
*/
public class SocketFactory extends javax.net.ssl.SSLSocketFactory{
private javax.net.ssl.SSLSocketFactory factory;
public static class SocketFactoryOptions {
private InputStream caCrtInputStream;
private InputStream caClientP12InputStream;
private String caClientP12Password;
public SocketFactoryOptions withCaInputStream(InputStream stream) {
this.caCrtInputStream = stream;
return this;
}
public SocketFactoryOptions withClientP12InputStream(InputStream stream) {
this.caClientP12InputStream = stream;
return this;
}
public SocketFactoryOptions withClientP12Password(String password) {
this.caClientP12Password = password;
return this;
}
public boolean hasCaCrt() {
return caCrtInputStream != null;
}
public boolean hasClientP12Crt() {
return caClientP12Password != null;
}
public InputStream getCaCrtInputStream() {
return caCrtInputStream;
}
public InputStream getCaClientP12InputStream() {
return caClientP12InputStream;
}
public String getCaClientP12Password() {
return caClientP12Password;
}
public boolean hasClientP12Password() {
return (caClientP12Password != null) && !caClientP12Password.equals("");
}
}
public SocketFactory() throws CertificateException, KeyStoreException, NoSuchAlgorithmException, IOException, KeyManagementException, java.security.cert.CertificateException, UnrecoverableKeyException {
this(new SocketFactoryOptions());
}
private TrustManagerFactory tmf;
public SocketFactory(SocketFactoryOptions options) throws KeyStoreException, NoSuchAlgorithmException, IOException, KeyManagementException, java.security.cert.CertificateException, UnrecoverableKeyException {
Log.v(this.toString(), "initializing CustomSocketFactory");
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyManagerFactory kmf = null;
kmf = KeyManagerFactory.getInstance("X509");
if(options.hasCaCrt()) {
Log.v(this.toString(), "MQTT_CONNECTION_OPTIONS.hasCaCrt(): true");
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
caKeyStore.load(null, null);
CertificateFactory caCF = CertificateFactory.getInstance("X.509");
X509Certificate ca = (X509Certificate) caCF.generateCertificate(options.getCaCrtInputStream());
String alias = ca.getSubjectX500Principal().getName();
// Set propper alias name
caKeyStore.setCertificateEntry(alias, ca);
tmf.init(caKeyStore);
Enumeration<String> aliasesCA = caKeyStore.aliases();
for (; aliasesCA.hasMoreElements(); ) {
String o = aliasesCA.nextElement();
}
} else {
KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
keyStore.load(null);
tmf.init(keyStore);
}
if (options.hasClientP12Crt()) {
Log.v(this.toString(), "MQTT_CONNECTION_OPTIONS.hasClientP12Crt(): true");
KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
clientKeyStore.load(options.getCaClientP12InputStream(), options.hasClientP12Password() ? options.getCaClientP12Password().toCharArray() : new char[0]);
kmf.init(clientKeyStore, options.hasClientP12Password() ? options.getCaClientP12Password().toCharArray() : new char[0]);
Log.v(this.toString(), "Client .p12 Keystore content: ");
Enumeration<String> aliasesClientCert = clientKeyStore.aliases();
for (; aliasesClientCert.hasMoreElements(); ) {
String o = aliasesClientCert.nextElement();
}
} else {
Log.v(this.toString(), "Client .p12 sideload: false, using null CLIENT cert");
kmf.init(null,null);
}
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(kmf.getKeyManagers(), getTrustManagers(), null);
this.factory= context.getSocketFactory();
}
public TrustManager[] getTrustManagers() {
return tmf.getTrustManagers();
}
#Override
public String[] getDefaultCipherSuites() {
return this.factory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return this.factory.getSupportedCipherSuites();
}
#Override
public Socket createSocket() throws IOException {
SSLSocket r = (SSLSocket)this.factory.createSocket();
r.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
return r;
}
#Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
SSLSocket r = (SSLSocket)this.factory.createSocket(s, host, port, autoClose);
r.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
return r;
}
#Override
public Socket createSocket(String host, int port) throws IOException {
SSLSocket r = (SSLSocket)this.factory.createSocket(host, port);
r.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
return r;
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
SSLSocket r = (SSLSocket)this.factory.createSocket(host, port, localHost, localPort);
r.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
return r;
}
#Override
public Socket createSocket(InetAddress host, int port) throws IOException {
SSLSocket r = (SSLSocket)this.factory.createSocket(host, port);
r.setEnabledProtocols(new String[]{ "TLSv1", "TLSv1.1", "TLSv1.2"});
return r;
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
SSLSocket r = (SSLSocket)this.factory.createSocket(address, port, localAddress,localPort);
r.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
return r;
}
}

Unable to verify signature when keys are strongbox backed in Android Keystore

My requirement is to sign sha256 hash using keys generated in AndroidKeystore. but when I verify the signature validity it fails here are my observations using different scenarios.
we have tested this in the following scenarios using Google PIXEL 3A as strongbox enabled device
IsStrongBoxEnabled Algorithm Verification
TRUE NONEWithRSA FALSE
TRUE SHA256WithRSA TRUE
FALSE NONEWithRSA TRUE
FALSE SHA256WithRSA TRUE
TRUE NONEWithECDSA TRUE
TRUE SHA256WithECDSA TRUE
FALSE NONEWithECDSA TRUE
FALSE SHA256WithECDSA TRUE
Here is my sample test code
package com.company.samplekeystoreapp;
import android.os.Build;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.StrongBoxUnavailableException;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import org.bouncycastle.util.encoders.Hex;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.Signature;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private KeyPair keyPair;
private KeyStore keyStore;
private String aliasName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
UUID uuid = UUID.randomUUID();
aliasName = "keyPrefix_" + uuid.toString();
keyPair = generateKeyPair(true);
KeyStore.Entry entry = keyStore.getEntry(aliasName, null);
if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
Log.w("SampleApp", "Not an instance of a PrivateKeyEntry");
}
Signature s = Signature.getInstance("NONEwithRSA");
if (entry instanceof KeyStore.PrivateKeyEntry) {
s.initSign(((KeyStore.PrivateKeyEntry) entry).getPrivateKey());
}
byte[] data = Hex.decode("d56ca469cd129c015f682724560baaf653914ef21fff1817512186d67b18c7f5");
s.update(data);
byte[] signature = s.sign();
if (entry instanceof KeyStore.PrivateKeyEntry) {
s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate());
}
s.update(data);
boolean valid = s.verify(signature);
Toast.makeText(MainActivity.this, "Status : " + valid, Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
}
private KeyPair generateKeyPair(boolean isStrongBoxEnabled) throws Exception {
try{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxEnabled) {
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
aliasName, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512, KeyProperties.DIGEST_NONE, KeyProperties.DIGEST_SHA1)
.setIsStrongBoxBacked(true)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.build());
} else {
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
aliasName, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512, KeyProperties.DIGEST_NONE, KeyProperties.DIGEST_SHA1)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.build());
}
return keyPairGenerator.generateKeyPair();
}catch (Exception e) {
if (e instanceof StrongBoxUnavailableException) {
try {
return keyPair = generateKeyPair(false);
} catch (Exception ex) {
throw e;
}
}else {
throw e;
}
}
}

How to send data from Raspberry pi to Google Cloud IoT Core, using Android Studio?

I'm doing Android Things project.
I want to publish a String message to Google Cloud IoT Core, but there are errors shown.
I'm using Raspberry Pi 3 with Android Things OS and and programming it using Android Studio.
Errors screenshot:
This is the whole code:
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cacaosd.com.sample1">
<!-- PAHO Permissions -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- PAHO Permissions -->
<application>
<uses-library android:name="com.google.android.things"/>
<!-- Mqtt Service -->
<service android:name="org.eclipse.paho.android.service.MqttService" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.IOT_LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
IotCoreCommunicator Class
package cacaosd.com.sample1;
import android.content.Context;
import android.util.Log;
import java.util.concurrent.TimeUnit;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
public class IotCoreCommunicator {
private static final String SERVER_URI = "ssl://mqtt.googleapis.com:8883";
public static class Builder {
private Context context;
private String projectId;
private String cloudRegion;
private String registryId;
private String deviceId;
private int privateKeyRawFileId;
public Builder withContext(Context context) {
this.context = context;
return this;
}
public Builder withProjectId(String projectId) {
this.projectId = projectId;
return this;
}
public Builder withCloudRegion(String cloudRegion) {
this.cloudRegion = cloudRegion;
return this;
}
public Builder withRegistryId(String registryId) {
this.registryId = registryId;
return this;
}
public Builder withDeviceId(String deviceId) {
this.deviceId = deviceId;
return this;
}
public Builder withPrivateKeyRawFileId(int privateKeyRawFileId) {
this.privateKeyRawFileId = privateKeyRawFileId;
return this;
}
public IotCoreCommunicator build() {
if (context == null) {
throw new IllegalStateException("context must not be null");
}
if (projectId == null) {
throw new IllegalStateException("projectId must not be null");
}
if (cloudRegion == null) {
throw new IllegalStateException("cloudRegion must not be null");
}
if (registryId == null) {
throw new IllegalStateException("registryId must not be null");
}
if (deviceId == null) {
throw new IllegalStateException("deviceId must not be null");
}
String clientId = "projects/" + projectId + "/locations/" + cloudRegion + "/registries/" + registryId + "/devices/" + deviceId;
if (privateKeyRawFileId == 0) {
throw new IllegalStateException("privateKeyRawFileId must not be 0");
}
MqttAndroidClient client = new MqttAndroidClient(context, SERVER_URI, clientId);
IotCorePasswordGenerator passwordGenerator = new IotCorePasswordGenerator(projectId, context.getResources(), privateKeyRawFileId);
return new IotCoreCommunicator(client, deviceId, passwordGenerator);
}
}
private final MqttAndroidClient client;
private final String deviceId;
private final IotCorePasswordGenerator passwordGenerator;
IotCoreCommunicator(MqttAndroidClient client, String deviceId, IotCorePasswordGenerator passwordGenerator) {
this.client = client;
this.deviceId = deviceId;
this.passwordGenerator = passwordGenerator;
}
public void connect() {
monitorConnection();
clientConnect();
subscribeToConfigChanges();
}
private void monitorConnection() {
client.setCallback(new MqttCallback() {
#Override
public void connectionLost(Throwable cause) {
Log.e("TUT", "connection lost", cause);
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
Log.d("TUT", "message arrived " + topic + " MSG " + message);
// You need to do something with messages when they arrive
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
Log.d("TUT", "delivery complete " + token);
}
});
}
private void clientConnect() {
try {
MqttConnectOptions connectOptions = new MqttConnectOptions();
// Note that the the Google Cloud IoT Core only supports MQTT 3.1.1, and Paho requires that we explicitly set this.
// If you don't, the server will immediately close its connection to your device.
connectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
// With Google Cloud IoT Core, the username field is ignored, however it must be set for the
// Paho client library to send the password field. The password field is used to transmit a JWT to authorize the device.
connectOptions.setUserName("unused-but-necessary");
connectOptions.setPassword(passwordGenerator.createJwtRsaPassword());
IMqttToken iMqttToken = client.connect(connectOptions);
iMqttToken.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d("TUT", "success, connected");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.e("TUT", "failure, not connected", exception);
}
});
iMqttToken.waitForCompletion(TimeUnit.SECONDS.toMillis(30));
Log.d("TUT", "IoT Core connection established.");
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
/**
* Configuration is managed and sent from the IoT Core Platform
*/
private void subscribeToConfigChanges() {
try {
client.subscribe("/devices/" + deviceId + "/config", 1);
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
public void publishMessage(String subtopic, String message) {
String topic = "/devices/" + deviceId + "/" + subtopic;
String payload = "{msg:\"" + message + "\"}";
MqttMessage mqttMessage = new MqttMessage(payload.getBytes());
mqttMessage.setQos(1);
try {
client.publish(topic, mqttMessage);
Log.d("TUT", "IoT Core message published. To topic: " + topic);
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
public void disconnect() {
try {
Log.d("TUT", "IoT Core connection disconnected.");
client.disconnect();
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
}
IotCorePasswordGenerator Class
package cacaosd.com.sample1;
import android.content.res.Resources;
import android.util.Base64;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
class IotCorePasswordGenerator {
private final String projectId;
private final Resources resources;
private final int privateKeyRawFileId;
IotCorePasswordGenerator(String projectId, Resources resources, int privateKeyRawFileId) {
this.projectId = projectId;
this.resources = resources;
this.privateKeyRawFileId = privateKeyRawFileId;
}
char[] createJwtRsaPassword() {
try {
byte[] privateKeyBytes = decodePrivateKey(resources, privateKeyRawFileId);
return createJwtRsaPassword(projectId, privateKeyBytes).toCharArray();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Algorithm not supported. (developer error)", e);
} catch (InvalidKeySpecException e) {
throw new IllegalStateException("Invalid Key spec. (developer error)", e);
} catch (IOException e) {
throw new IllegalStateException("Cannot read private key file.", e);
}
}
private static byte[] decodePrivateKey(Resources resources, int privateKeyRawFileId) throws IOException {
try(InputStream inStream = resources.openRawResource(privateKeyRawFileId)) {
return Base64.decode(inputToString(inStream), Base64.DEFAULT);
}
}
private static String inputToString(InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
private static String createJwtRsaPassword(String projectId, byte[] privateKeyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
return createPassword(projectId, privateKeyBytes, "RSA", SignatureAlgorithm.RS256);
}
private static String createPassword(String projectId, byte[] privateKeyBytes, String algorithmName, SignatureAlgorithm signatureAlgorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
Instant now = Instant.now();
// Create a JWT to authenticate this device. The device will be disconnected after the token
// expires, and will have to reconnect with a new token. The audience field should always be set
// to the GCP project id.
JwtBuilder jwtBuilder =
Jwts.builder()
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(now.plus(Duration.ofMinutes(20))))
.setAudience(projectId);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory kf = KeyFactory.getInstance(algorithmName);
return jwtBuilder.signWith(signatureAlgorithm, kf.generatePrivate(spec)).compact();
}
}
MainActivity Class:
package cacaosd.com.sample1;
import android.app.Activity;
import android.hardware.SensorEvent;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import cacaosd.com.sample1.R;
import cacaosd.com.sample1.IotCoreCommunicator;
import com.google.android.things.pio.Gpio;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class MainActivity extends Activity {
private IotCoreCommunicator communicator;
private Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup the communication with your Google IoT Core details
communicator = new IotCoreCommunicator.Builder()
.withContext(this)
.withCloudRegion("us-central1") // ex: europe-west1
.withProjectId("my-first-project-198704") // ex: supercoolproject23236
.withRegistryId("vibration") // ex: my-devices
.withDeviceId("my-device") // ex: my-test-raspberry-pi
.withPrivateKeyRawFileId(R.raw.rsa_private)
.build();
HandlerThread thread = new HandlerThread("MyBackgroundThread");
thread.start();
handler = new Handler(thread.getLooper());
handler.post(connectOffTheMainThread); // Use whatever threading mechanism you want
}
private final Runnable connectOffTheMainThread = new Runnable() {
#Override
public void run() {
communicator.connect();
handler.post(sendMqttMessage);
}
};
private final Runnable sendMqttMessage = new Runnable() {
private int i;
/**
* We post 100 messages as an example, 1 a second
*/
#Override
public void run() {
if (i == 100) {
return;
}
// events is the default topic for MQTT communication
String subtopic = "events";
// Your message you want to send
String message = "Hello World " + i++;
communicator.publishMessage(subtopic, message);
handler.postDelayed(this, TimeUnit.SECONDS.toMillis(1));
}
};
#Override
protected void onDestroy() {
communicator.disconnect();
super.onDestroy();
}
}
Update:
I converted the private key from "pem" format to "pkcs8" format by following this documentation and this demo, then the error "Invalid key spec" is gone, but still there is a "FATAL EXCEPTION" and "java.lang.IllegalArgumentException: bad base-64" as shown in the image below:
[![enter image description here][4]][4]
It says that these are the related codes that caused the error (which shown in blue color in the previous image:
IotCorePasswordGenerator.java:47
return Base64.decode(inputToString(inStream), Base64.DEFAULT);
IotCorePasswordGenerator.java:34
byte[] privateKeyBytes = decodePrivateKey(resources, privateKeyRawFileId);
IotCoreCommunicator.java:135
connectOptions.setPassword(passwordGenerator.createJwtRsaPassword());
IotCoreCommunicator.java:101
clientConnect();
MainActivity.java:58
communicator.connect();
Update 2
I deleted the statement "-----BEGIN PRIVATE KEY-----" and the statement "------END PRIVATE KEY-----" and the error "bad base 64" is gone, Now there is another error which is "broken pipe" as shown in image below, when I reopen Android Studio and rebuild the project this error "broken pipe" removed, and when I run the project again it comes back again.
The error (first image)
Private key with start and end statements(second image)
Private key without start and end statements(third image)
From the error, it looks like you registered the device with the wrong type of SSL key. Verify that you created an SSL key that matches the format you specified in IoT Core. I.e. if you created an RSA key with the x509 certificate wrapper, be sure that your device is registered with that type and not just the RSA key.
Also be sure that the private key is actually on the device, and that it's not corrupted.
Edit: Problem may have been the roots.pem wasn't on device to handle the TLS handshake with IoT Core. We shall see... to get it, run: wget https://pki.google.com/roots.pem and put the roots.pem in the same directory as the private key on device.
I strongly recommend that you take a look at the Android Things connector for Cloud IoT core. This project makes it much easier to access Cloud IoT core from Android Things and handles various best practices such as token refresh.

FingerPrint Authentication in my app

Im trying to implement fingerprint authentication inside app. while initializing KeyGenerator .it showing this error.
protected void generateKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (Exception e) {
e.printStackTrace();
}
try {
keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore");
} catch (NoSuchAlgorithmException |
NoSuchProviderException e) {
throw new RuntimeException(
"Failed to get KeyGenerator instance", e);
}
try {
keyStore.load(null);
keyGenerator.init(new
KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();
} catch (NoSuchAlgorithmException |
InvalidAlgorithmParameterException
| CertificateException | IOException e) {
throw new RuntimeException(e);
}
Error:'KeyGenParameterSpec()' is not public in 'android.security.keystore.KeyGenParameterSpec'. Cannot be accessed from outside package
Below are two classes which you can use to authenticate with finger print. Their will be minor issues like permsission handling or any import. but this is working code of finger print authentication.
FingerPrintHandler class
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.Manifest;
import android.os.Build;
import android.os.CancellationSignal;
import android.support.v4.app.ActivityCompat;
import yasiriqbal.ethereum.R;
import yasiriqbal.ethereum.util.SessionManager;
#TargetApi(Build.VERSION_CODES.M)
public class FingerprintHandler extends FingerprintManager.AuthenticationCallback {
// You should use the CancellationSignal method whenever your app can no longer process user input, for example when your app goes
// into the background. If you don’t use this method, then other apps will be unable to access the touch sensor, including the lockscreen!//
private CancellationSignal cancellationSignal;
private Context context;
public FingerprintHandler(Context mContext) {
context = mContext;
}
//Implement the startAuth method, which is responsible for starting the fingerprint authentication process//
public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) {
cancellationSignal = new CancellationSignal();
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
return;
}
manager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
}
public void StopListener() {
try {
if (cancellationSignal != null)
cancellationSignal.cancel();
cancellationSignal = null;
}catch (Exception e)
{}
}
#Override
//onAuthenticationError is called when a fatal error has occurred. It provides the error code and error message as its parameters//
public void onAuthenticationError(int errMsgId, CharSequence errString) {
//I’m going to display the results of fingerprint authentication as a series of toasts.
//Here, I’m creating the message that’ll be displayed if an error occurs//
}
#Override
//onAuthenticationFailed is called when the fingerprint doesn’t match with any of the fingerprints registered on the device//
public void onAuthenticationFailed() {
}
#Override
//onAuthenticationHelp is called when a non-fatal error has occurred. This method provides additional information about the error,
//so to provide the user with as much feedback as possible I’m incorporating this information into my toast//
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
}
#Override
//onAuthenticationSucceeded is called when a fingerprint has been successfully matched to one of the fingerprints stored on the user’s device//
public void onAuthenticationSucceeded(
FingerprintManager.AuthenticationResult result) {
if (new SessionManager(context).getUsername() != null && !new SessionManager(context).getUsername().isEmpty()) {
String userName = new SessionManager(context).getUsername();
String pass = new SessionManager(context).getPass();
if (pass != null) {
((SigninActivity) context).requestLoginToServer(userName, pass);
((SigninActivity) context).isSignInButtonClicked = false;
}
}
//context.startActivity(new Intent(context, DashboardActivity.class));
}
}
Signin Class
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.annotation.TargetApi;
import android.app.KeyguardManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.Handler;
import android.provider.ContactsContract;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v7.widget.AppCompatTextView;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
import android.text.style.ForegroundColorSpan;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.gson.Gson;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import yasiriqbal.ethereum.EthereumApplication;
import yasiriqbal.ethereum.PermissionActivity;
import yasiriqbal.ethereum.R;
import yasiriqbal.ethereum.dashboad.DashboardActivity;
import yasiriqbal.ethereum.models.EthUserModel;
import yasiriqbal.ethereum.models.ServerMessage;
import yasiriqbal.ethereum.network.VolleyRequestHelper;
import yasiriqbal.ethereum.settings.ChangePasswordActivity;
import yasiriqbal.ethereum.signup.MobileCodeVerifyActivity;
import yasiriqbal.ethereum.signup.MobileNumberActivity;
import yasiriqbal.ethereum.signup.SignUpActivity;
import yasiriqbal.ethereum.util.Constants;
import yasiriqbal.ethereum.util.DialogErrorFragment;
import yasiriqbal.ethereum.util.DialogHelper;
import yasiriqbal.ethereum.util.Fonts;
import yasiriqbal.ethereum.util.MyUtils;
import yasiriqbal.ethereum.util.PermissionHandler;
import yasiriqbal.ethereum.util.SessionManager;
public class SigninActivity extends AccountAuthenticatorActivity implements View.OnClickListener, Response.Listener<JSONObject>, Response.ErrorListener {
public static final String PARAM_USER_PASS = "userpassword";
private static final int CODE_EMAIL_NOT_VERIFIED = 408;
private AppCompatTextView txt_loginscreen_not_register_member, txt_loginscreen_header,
txt_loginscreen_forgetpassword, txt_loginscreen_touchid;
private TextInputEditText edit_loginscreen_username, edit_loginscreen_password;
Button btn_loginscreen_signin;
PermissionHandler permissionHandler;
private TextInputLayout layout_loginscreen_password, layout_loginscreen_username;
private KeyStore keyStore;
private Cipher cipher;
private final int CODE_WRONG_PASSWORD = 401;
private final int CODE_INVALID_USER = 400;
private final int CODE_LOGIN_SUCCESS = 200;
public final int CODE_PROFILE_STATUS_COMPLETE = 2;
public final int CODE_MOBILE_NOT_VERIFY = 1;
private DialogHelper dialogHelper;
private final int ACCOUNT_PERMISSION_ID = 1234;
boolean isFromSetting = false;
private static final String KEY_NAME = "ethereum";
private Typeface qanelasRegularTypeFace;
private Typeface qanelasSemiBoldTypeFace;
private String previousUserName = "";
FingerprintHandler handler;
FingerprintManager mFingerprintManager;
public boolean isSignInButtonClicked = false;
FingerprintManager.CryptoObject cryptoObject;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_in);
if (permissionHandler.isMarshMallow()) {
authenticateWithFingerPrint();
if (permissionHandler.isPermissionAvailable(Manifest.permission.USE_FINGERPRINT)) {
setFingerprintConfig();
} else {
permissionHandler.requestPermission(Manifest.permission.USE_FINGERPRINT);
}
}//end of if is marshmallow
//dialogErrorFragment.show(getFragmentManager(),"dialog");
}//end of onCreate
#TargetApi(Build.VERSION_CODES.M)
private void authenticateWithFingerPrint() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
return;
}
mFingerprintManager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
if (mFingerprintManager.isHardwareDetected()) {
KeyguardManager mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
if (mKeyguardManager.isKeyguardSecure()) {
generateKey();
if (initCipher()) {
cryptoObject = new FingerprintManager.CryptoObject(cipher);
handler = new FingerprintHandler(this);
handler.startAuth(mFingerprintManager, cryptoObject);
}
} else {
Toast.makeText(this, "Lock Screen Security not enabled in Settings.", Toast.LENGTH_LONG).show();
}
}
}
#TargetApi(Build.VERSION_CODES.M)
protected void generateKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (Exception e) {
e.printStackTrace();
}
KeyGenerator keyGenerator;
try {
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new RuntimeException("Failed to get KeyGenerator instance", e);
}
try {
keyStore.load(null);
keyGenerator.init(new
KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();
} catch (NoSuchAlgorithmException |
InvalidAlgorithmParameterException
| CertificateException | IOException e) {
throw new RuntimeException(e);
}
}
//Create a new method that we’ll use to initialize our cipher//
#TargetApi(Build.VERSION_CODES.M)
public boolean initCipher() {
try {
//Obtain a cipher instance and configure it with the properties required for fingerprint authentication//
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException |
NoSuchPaddingException e) {
throw new RuntimeException("Failed to get Cipher", e);
}
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
null);
cipher.init(Cipher.ENCRYPT_MODE, key);
//Return true if the cipher has been initialized successfully//
return true;
} catch (KeyPermanentlyInvalidatedException e) {
//Return false if cipher initialization failed//
return false;
} catch (KeyStoreException | CertificateException
| UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
}
//xml initalization
private void initResources() {
//textViews initalize
}//end of initResources
#Override
protected void onResume() {
super.onResume();
}//end of onResume
//here to discover fingerprint hardware and set views
#TargetApi(Build.VERSION_CODES.M)
#SuppressWarnings("MissingPermission")
private void setFingerprintConfig() {
FingerprintManager mFingerprintManager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
if (mFingerprintManager.isHardwareDetected()) {
if (getPreviousUsername() != null && !getPreviousUsername().isEmpty() && mFingerprintManager.hasEnrolledFingerprints()) {
//TODO show finger print icon that this device support
} else {
//here means user is not enrolled for fingerpring
//TODO handle case when fingerprint not supported
}
}
}//end of setFingerprint
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (permissionHandler.PERMISSIONS_REQUEST == requestCode && grantResults.length > 0 && grantResults[0] > -1) {
setFingerprintConfig();
}
}
//onclick reciver for signin activity
#Override
public void onClick(View v) {
switch (v.getId()) {
default:
throw new IllegalArgumentException(v.getId() + " is not handle in onclick of signing activity");
}//end f switch
}//end of onClick
#Override
protected void onPause() {
super.onPause();
}
//here check if user profile complete then redirect to dashboard or then mobile verify
private void redirectUserAfterSuccessLogin(String data) throws JSONException {
if (!data.isEmpty()) {
Gson gsonForUser = new Gson();
new SessionManager(this).adToken(new JSONObject(data).getString("authenticationToken"));
EthUserModel ethUserModel = gsonForUser.fromJson(data, EthUserModel.class);
EthereumApplication.getInstance().setEthUserLoginObj(ethUserModel);
insertEthUserToContentProvider(ethUserModel);
if (!previousUserName.equals(ethUserModel.getEthUserName()) && !removeAccountFromAccountManager(ethUserModel, edit_loginscreen_password.getText().toString())) {
addAccountToAuthenticator(ethUserModel, edit_loginscreen_password.getText().toString());
startOtherActivity(ethUserModel);
//startSyncAdapter();
}//end of if for removeAccount return false
else {
startOtherActivity(ethUserModel);
//startSyncAdapter();
}//end of else
} else {
//here to show retry login
}
}//end of function
private void startOtherActivity(EthUserModel ethUserModel) {
if (isSignInButtonClicked)
new SessionManager(this).addData(edit_loginscreen_username.getText().toString(), edit_loginscreen_password.getText().toString());
if (txt_loginscreen_touchid.getVisibility() == View.VISIBLE)
try {
handler.StopListener();
} catch (Exception e) {
}
if (ethUserModel.getProfileStatus() == CODE_MOBILE_NOT_VERIFY) {
//redirect to mobile screen
startActivity(new Intent(this, MobileNumberActivity.class));
} else {
//redirect to dashboard if user not from setting
if (!getIntent().getBooleanExtra(Constants.ARG_IS_USER_FROM_SETTING, false)) {
if (EthereumApplication.getInstance().getEthUserLoginObj().getEthUserDoubleAuthenticationStatus() == Constants.ResultCode.CODE_DOUBLE_AUTHENTICATION_TRUE)
startActivity(new Intent(this, MobileCodeVerifyActivity.class)
.putExtra(Constants.MobileCodeVerifyActivity.IS_FROM_SIGNIN, true)
.putExtra(Constants.MobileNumberActivity.EXTRA_MOBILE_NUMBER, EthereumApplication.getInstance().getEthUserLoginObj().getMobileNum()));
else
startActivity(new Intent(this, DashboardActivity.class));
finish();
}
}//end of else
}//end of function
#Override
protected void onStop() {
super.onStop();
}
}//end of activity

Android Fingerprint API and Private/Public keys

When I enrol first and only fingerprint and generate KeyPair the PrivateKey gets invalidated when I use it for the second time. This happens only once. Am I the only one having this issue? Is there something wrong with my code?
I cannot use any other key as I'm using PrivateKey to sign data.
Steps:
Wipe all fingerprints
Enrol one fingerprint
Generate KeyPair and use FingerprintManager :: authenticate
During next use of FingerprintManager :: authenticate PrivateKey gets permanently invalidated. This happens only for the first time
Below the code where I generate the KeyPair
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(null);
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
generator.initialize(new KeyGenParameterSpec.Builder("key_name", KeyProperties.PURPOSE_SIGN)
.setDigests(digest) // I have defined digest before
.setSignaturePaddings(paddings) // I have defined paddings before
.setUserAuthenticationRequired(true)
.build());
generator.generateKeyPair();
And here is the code where I invoke fingerprint authentication for data signing:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
Signature signature = Signature.getInstance("signing_algorithm");
PrivateKey privateKey = (PrivateKey) keyStore.getKey("key_name", null);
signature.initSign(privateKey); // Here I get KeyPermanentlyInvalidatedException
CryptoObject crypto = new CryptoObject(signature);
FingerprintManager fingerprintManager = context.getSystemService(FingerprintManager.class);
CancellationSignal cancellationSignal = new CancellationSignal();
AuthenticationCallback authenticationCallback = new AuthenticationCallback() {
...
};
fingerprintManager.authenticate(crypto, cancelationSignal, 0, authenticationCallback, null);
i try this link and work perfectly .
First you need to set Minimum sdk look like the Picture
Second set Permission in Mainfest
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
Third
generateKey() function which generates an encryption key which is then stored securely on the device.
cipherInit() function that initializes the cipher that will be used to create the encrypted FingerprintManager.
CryptoObject instance and various other checks before initiating the authentication process which is implemented inside onCreate() method.
FingerPrintActivty.java
import android.Manifest;
import android.annotation.TargetApi;
import android.app.KeyguardManager;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
public class FingerprintActivity extends AppCompatActivity {
private KeyStore keyStore;
// Variable used for storing the key in the Android Keystore container
private static final String KEY_NAME = "androidHive";
private Cipher cipher;
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fingerprint);
// Initializing both Android Keyguard Manager and Fingerprint Manager
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
FingerprintManager fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
textView = (TextView) findViewById(R.id.errorText);
// Check whether the device has a Fingerprint sensor.
if(!fingerprintManager.isHardwareDetected()){
/**
* An error message will be displayed if the device does not contain the fingerprint hardware.
* However if you plan to implement a default authentication method,
* you can redirect the user to a default authentication activity from here.
* Example:
* Intent intent = new Intent(this, DefaultAuthenticationActivity.class);
* startActivity(intent);
*/
textView.setText("Your Device does not have a Fingerprint Sensor");
}else {
// Checks whether fingerprint permission is set on manifest
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
textView.setText("Fingerprint authentication permission not enabled");
}else{
// Check whether at least one fingerprint is registered
if (!fingerprintManager.hasEnrolledFingerprints()) {
textView.setText("Register at least one fingerprint in Settings");
}else{
// Checks whether lock screen security is enabled or not
if (!keyguardManager.isKeyguardSecure()) {
textView.setText("Lock screen security not enabled in Settings");
}else{
generateKey();
if (cipherInit()) {
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
FingerprintHandler helper = new FingerprintHandler(this);
helper.startAuth(fingerprintManager, cryptoObject);
}
}
}
}
}
}
#TargetApi(Build.VERSION_CODES.M)
protected void generateKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (Exception e) {
e.printStackTrace();
}
KeyGenerator keyGenerator;
try {
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new RuntimeException("Failed to get KeyGenerator instance", e);
}
try {
keyStore.load(null);
keyGenerator.init(new
KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();
} catch (NoSuchAlgorithmException |
InvalidAlgorithmParameterException
| CertificateException | IOException e) {
throw new RuntimeException(e);
}
}
#TargetApi(Build.VERSION_CODES.M)
public boolean cipherInit() {
try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("Failed to get Cipher", e);
}
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException | NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
}
}
FingerprintAuthenticationHandler.Class
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.widget.TextView;
/**
* Created by whit3hawks on 11/16/16.
*/
public class FingerprintHandler extends FingerprintManager.AuthenticationCallback {
private Context context;
// Constructor
public FingerprintHandler(Context mContext) {
context = mContext;
}
public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) {
CancellationSignal cancellationSignal = new CancellationSignal();
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
return;
}
manager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
}
#Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
this.update("Fingerprint Authentication error\n" + errString, false);
}
#Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
this.update("Fingerprint Authentication help\n" + helpString, false);
}
#Override
public void onAuthenticationFailed() {
this.update("Fingerprint Authentication failed.", false);
}
#Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
this.update("Fingerprint Authentication succeeded.", true);
}
public void update(String e, Boolean success){
TextView textView = (TextView) ((Activity)context).findViewById(R.id.errorText);
textView.setText(e);
if(success){
textView.setTextColor(ContextCompat.getColor(context,R.color.colorPrimaryDark));
}
}
}
Hope it help.
you can see this one on github: hope it will help you : Confirm Credentials

Categories

Resources