2017-08-14 26 views
0

我有一些說法,我想創建JWT並使用在指紋API中創建的PrivateKey簽名。從Android指紋API簽署JWT PrivateKey

這是智威湯遜要求 -

Header: 

{ 
    "alg": "RS256」, 
    「kid」: 「ABCDEDFkjsdfjaldfkjg」, 
     「auth_type」 : 「fingerprint」/"pin" 
} 

Payload: 
{ 
     「client_id」:」XXXXX-YYYYYY-ZZZZZZ」 
} 

創建密鑰對的指紋 -

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; 
    } 
} 

和創建JWT這樣 -

@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; 
} 

,當我在做這一點,我得到 -

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) 

我嘗試了許多其他方式來使用PrivateKey簽署JWT,到目前爲止我沒有找到解決方案。

任何幫助表示讚賞

回答

0

使用gradle這個依賴

compile group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '4.41.1' 

庫我能夠解決的問題,並利用簽署JWT AndroidKeyStoreRSAPrivateKey

這裏RSASSASigner構造這需要專用密鑰從Android KeyStore和此簽名者用於簽署JWSObject。

在尋找解決方案時,我沒有在Web上找到關於此的很多信息,因此在此處發佈解決方案,以瞭解如何使用來自android Fingerprint API的PrivateKey簽署JWT。感謝pedrofb您的幫助:)

@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; 
} 

雖然在這件事上我碰到的雨雲JOSE,智威湯遜舊版本https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169/android-m-support

1

您已經創建了而已,不用於簽名加密的密鑰。更改

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.initialize(
     new KeyGenParameterSpec.Builder(
        KEY_NAME, 
        KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 
        .setDigests(KeyProperties.DIGEST_SHA256) 
        .setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4)) 
        .build()); 
+0

報道了一些bug工作謝謝你的幫助@pedrofb。是的,改變KeyProperties是有意義的。 PURPOSE_SIGN從KeyProperties.PURPOSE_ENCRYPT但我仍然有問題 ClassCastException:android.security.keystore.AndroidKeyStoreRSAPrivateKey不能轉換爲java.security.interfaces.RSAPrivateKey – Manisha

+1

你可以發佈新的堆棧跟蹤?你有沒有檢查過jose4j是否與Android Keystore兼容?私鑰不受提取保護。如果圖書館試圖操縱它們,它會失敗。 – pedrofb

+0

感謝您的評論實際上幫助我,當我搜索與Android密鑰庫兼容的jose4j我發現他們知道錯誤,並且它已被修復在latest.https://bitbucket.org/b_c/jose4j/pull-requests/8/ fix-for-android-60-marshmallow/diff – Manisha