我如何从使用 android 中的 "PBKDF2withHmacSHA256" 生成的 secretKey 生成 JWT?

How do i generate JWT from a secretKey which was generated using "PBKDF2withHmacSHA256" in android?

所以我正在尝试从使用 PBKDF2 生成的密钥生成 JWT 令牌,代码在 android:

public SecretKey generateKey(String passphraseOrPin) throws NoSuchAlgorithmException, InvalidKeySpecException {

        final int iterations = 5000;

        // Generate a 256-bit key
        final int outputKeyLength = 256;
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA256");
        KeySpec keySpec = new PBEKeySpec(getSha256Hash(passphraseOrPin).toCharArray(), salt.getBytes(), iterations, outputKeyLength);
        SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);

        return secretKey;
    }

但是每当我尝试生成令牌时:

String jwtString = Jwts.builder().setSubject("sub").signWith(key, SignatureAlgorithm.HS256).compact();

我收到错误消息:

Process: com.android.gocontract, PID: 7434
    java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.
        at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:111)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: io.jsonwebtoken.security.InvalidKeyException: The signing key's algorithm 'PBKDF2WithHmacSHA256' does not equal a valid HmacSHA* algorithm name and cannot be used with HS256.
        at io.jsonwebtoken.SignatureAlgorithm.assertValid(SignatureAlgorithm.java:358)
        at io.jsonwebtoken.SignatureAlgorithm.assertValidSigningKey(SignatureAlgorithm.java:302)
        at io.jsonwebtoken.impl.DefaultJwtBuilder.signWith(DefaultJwtBuilder.java:123)
        at com.android.gocontract.Activity.LoginActivity.onNext(LoginActivity.java:192)
        at com.android.gocontract.Activity.LoginActivity.onNext(LoginActivity.java:163)
        at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:58)
        at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:200)
        at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
        at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
2019-09-23 18:38:37.036 7434-7434/com.android.gocontract I/Process: Sending signal. PID: 7434 SIG: 9

生成Sha256的方法:

public String getSha256Hash(String password) {
        try {
            MessageDigest digest = null;
            try {
                digest = MessageDigest.getInstance("SHA-256");
            } catch (NoSuchAlgorithmException e1) {
                e1.printStackTrace();
            }
            digest.reset();
            return bin2hex(digest.digest(password.getBytes()));
        } catch (Exception ignored) {
            return null;
        }
    }

jjwt的版本:

 api 'io.jsonwebtoken:jjwt-api:0.10.7'
    runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.10.7'
    runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.10.7') {
        exclude group: 'org.json', module: 'json' //provided by Android natively
    }

解决方法:使用其他加密算法,或者使用库Spongy Castle.

有用的库:Nimbus JOSE JWT Spongycastle,如果您需要帮助混合 jwt 和海绵城堡。

为什么它不适合您:这是您使用的设备的特殊性。有些设备支持 PBKDF2withHmacSHA256 算法,有些则不支持。

我是如何得出这个结论的: 起初我以为这可能是一个简单的错字,但我在 JJWT github that while the issue indeed existed, it was already fixed. Since the verification now uses equalsignorecase the typo wouldn't matter. You can confirm in the code.

中找到了

然后我想可能是android或java版本。但是我发现 since java 8 release 已经实现了。

我认为它可能是 android 版本,但它适用于 API 26

我也测试了你使用的代码,在这里运行得很好,所以不会出现这些问题。然后我研究了很多,看到了一些 other questions, people having the same problem with other libs and other algorithms.

额外: k3v 中的一段代码可能可以帮助您(使用 Spongy Castle):

PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password), salt, iterations); 
KeyParameter key = (KeyParameter)generator.generateDerivedMacParameters(keySizeInBits);