This question already has answers here:
"Your app contains unsafe cryptographic encryption patterns" - How I can get rid of this warning?
(2 answers)
Closed 3 years ago.
I'm encrypting the password for firebase sign in, it's working well but I received a warning in google play console that your app contains unsafe cryptographic encryption patterns how can I get rid of it ??
I'm trying it on android studio.
public static class AESCrypt
{
private static final String ALGORITHM = "AES";
private static final String KEY = "1Hbfh667adfDEJ78";
public static String encrypt(String value) throws Exception
{
Key key = generateKey();
Cipher cipher = Cipher.getInstance(AESCrypt.ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte [] encryptedByteValue = cipher.doFinal(value.getBytes("utf-8"));
String encryptedValue64 = Base64.encodeToString(encryptedByteValue, Base64.DEFAULT);
return encryptedValue64;
}
public static String decrypt(String value) throws Exception
{
Key key = generateKey();
Cipher cipher = Cipher.getInstance(AESCrypt.ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedValue64 = Base64.decode(value, Base64.DEFAULT);
byte [] decryptedByteValue = cipher.doFinal(decryptedValue64);
String decryptedValue = new String(decryptedByteValue,"utf-8");
return decryptedValue;
}
private static Key generateKey() throws Exception
{
Key key = new SecretKeySpec(AESCrypt.KEY.getBytes(),AESCrypt.ALGORITHM);
return key;
}
The main issues are that you use a cipher with no integrity and a hard coded cryptographic key. If you analyse your source with Find Security Bugs you get CIPHER_INTEGRITY and HARD_CODE_KEY warning:
The cipher does not provide data integrity [com.lloyds.keystorage.AESCrypt] At AESCrypt.java:[line 25] CIPHER_INTEGRITY
The cipher does not provide data integrity [com.lloyds.keystorage.AESCrypt] At AESCrypt.java:[line 15] CIPHER_INTEGRITY
Hard coded cryptographic key found [com.lloyds.keystorage.AESCrypt] At AESCrypt.java:[line 35] HARD_CODE_KEY
The solution is to use a cipher that includes a Hash based Message Authentication Code (HMAC) to sign the data:
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
And to store the secret key in separate configuration files or keystores.
Below is the whole class after a full refactoring:
import android.util.Base64
import static java.nio.charset.StandardCharsets.UTF_8;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AESCrypt {
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
public static String encrypt(String value) throws Exception {
Key key = generateKey();
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedByteValue = cipher.doFinal(value.getBytes(UTF_8));
return Base64.encodeToString(encryptedByteValue, Base64.DEFAULT);
}
public static String decrypt(String value) throws Exception {
Key key = generateKey();
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedValue64 = Base64.decode(value, Base64.DEFAULT);
byte[] decryptedByteValue = cipher.doFinal(decryptedValue64);
return new String(decryptedByteValue, UTF_8);
}
private static Key generateKey() {
return new SecretKeySpec(Configuration.getKey().getBytes(UTF_8), TRANSFORMATION);
}
}
Related
I receive this error:
com.nimbusds.jose.JOSEException: Invalid private RSA key: Keystore operation failed
And my java.class:
public class MyClass {
private static final String ANDROID_PROVIDER = "AndroidKeyStore";
#RequiresApi(api = Build.VERSION_CODES.M)
public static PublicKey getPublicKeyAndCreateKeypairIfNotExists(String alias) throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(ANDROID_PROVIDER);
keyStore.load(null);
if (!keyStore.containsAlias(alias) || keyStore.getCertificate(alias) == null) {
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_PROVIDER);
generator.initialize(spec);
generator.generateKeyPair();
}
return keyStore.getCertificate(alias).getPublicKey();
}
#RequiresApi(api = Build.VERSION_CODES.M)
public static PrivateKey getSecretKey(String alias) throws GeneralSecurityException, IOException {
KeyStore.Entry entry;
//Get Android KeyStore
KeyStore ks = KeyStore.getInstance(ANDROID_PROVIDER);
// Weird artifact of Java API. If you don't have an InputStream to load, you still need to call "load", or it'll crash.
ks.load(null);
// Load the key pair from the Android Key Store
entry = ks.getEntry(alias, null);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)entry;
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
return privateKey;
}
public static String createJWT(String alias) throws JSONException, GeneralSecurityException, IOException, JOSEException {
//
RSASSASigner signer = new RSASSASigner(getSecretKey(alias));
//signer.getJCAContext().setProvider(hsmProvider);
//
SignedJWT jwt = new SignedJWT(
new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(alias).build(),
new JWTClaimsSet.Builder().subject("alice").build());
jwt.sign(signer);
String jwtString = jwt.serialize();
return jwtString;
}
}
The error appears specifically when executing the createJWT method on the line:
jwt.sign(signer);
Apparently the creation of the Keypair is satisfactory, since I have previously tested it and it does not give any error, even with ENCRYPT and DECRYPT it works perfectly. The problem is in the createJWT() method. I really don't know what I'm doing wrong.
Thanks in advance
I ran into this same issue. when i looked into the end of stack trace, the exception was
Caused by: android.security.KeyStoreException: **Incompatible padding mode**
And fixed by setting Signature mode for it, while generating the key
setSignaturePaddings(SIGNATURE_PADDING_RSA_PKCS1)
I used the DES algorithm to encrypt the username and password in jdbc.properties, and used the PropertyPlaceholderConfigurer for decryption, but found that this class has been deprecated. So use PropertySourcesPlaceholderConfigurer to replace.
The bean has been added in spring-dao.xml, and the class is filled with the class containing the decryption method inherited from PropertySourcesPlaceholderConfigurer.
I put a breakpoint on the first line of the decryption method, and then started tomcat to send an access request. At this point, the backend should call the database. But if the decryption class inherits from PropertySourcesPlaceholderConfigurer, the first line of the decryption method will not be executed. If the decryption class inherits from PropertyPlaceholderConfigurer, the first line of the decryption method is executed. I don't know why this is the case, should I use the deprecated PropertyPlaceholderConfigurer?
spring.version: 5.2.0.RELEASE
Part of spring-dao.xml
<!--<context:property-placeholder location="classpath:jdbc.properties" />-->
<bean class="com.imooc.o2o.util.EncryptPropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
<property name="fileEncoding" value="UTF-8" />
</bean>
EncryptPropertySourcesPlaceholderConfigurer.java (My decryption algorithm class)
package com.imooc.o2o.util;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
public class EncryptPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer {
// Fields to be encrypted
private String[] encryptPropNames = {"jdbc.username", "jdbc.password"};
/**
* Transform key attributes
* #param propertyName
* #param propertyValue
* #return
*/
#Override
protected String convertProperty(String propertyName, String propertyValue) {
if (isEncryptProp(propertyName)) {
// Decrypting encrypted fields
String decryptValue = DESUtil.getDecryptString(propertyValue);
return decryptValue;
} else {
return propertyValue;
}
}
/**
* Whether the attribute is encrypted
* #param propertyName
* #return
*/
private boolean isEncryptProp(String propertyName) {
// If it is equal to the field to be encrypted, it has been encrypted
for (String encryptPropertyName : encryptPropNames) {
if (encryptPropertyName.equals(propertyName)) {
return true;
}
}
return false;
}
}
DESUtil.java
package com.imooc.o2o.util;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Base64;
/**
* DES is a symmetric encryption algorithm. The so-called symmetric encryption algorithm is an algorithm that uses the same key for encryption and decryption.
*/
public class DESUtil {
private static Key key;
private static String KEY_STR = "myKey";
private static String CHAR_SET_NAME = "UTF-8";
private static String ALGORITHM = "DES";
static {
try {
// Generate DES Algorithm Object
KeyGenerator generator = KeyGenerator.getInstance(ALGORITHM);
// Apply SHA1 security policy
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
// Setting the key seed
secureRandom.setSeed(KEY_STR.getBytes());
// Initialize SHA1-based algorithm objects
generator.init(secureRandom);
// Generate a key object
key = generator.generateKey();
generator = null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// Get encrypted information
public static String getEncryptString(String str) {
// Based on BASE64 encoding, receive byte [] and convert to String
Base64.Encoder encoder = Base64.getEncoder();
try {
// Encoded as UTF-8
byte[] bytes = str.getBytes(CHAR_SET_NAME);
// Get the encrypted object
Cipher cipher = Cipher.getInstance(ALGORITHM);
// Initialize password information
cipher.init(Cipher.ENCRYPT_MODE, key);
// encryption
byte[] doFinal = cipher.doFinal(bytes);
// byte[] to encode a good String and return
return encoder.encodeToString(doFinal);
} catch (Exception e) {
throw new RuntimeException();
}
}
// Get the decrypted information
public static String getDecryptString(String str) {
// Based on BASE64 encoding, receive byte[] and convert to String
Base64.Decoder decoder = Base64.getDecoder();
try {
// Decode string into byte []
byte[] bytes = decoder.decode(str);
// Get the decrypted object
Cipher cipher = Cipher.getInstance(ALGORITHM);
// Initialize decryption information
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] doFinal = cipher.doFinal(bytes);
// Returns the decrypted information
return new String(doFinal, CHAR_SET_NAME);
} catch (Exception e) {
throw new RuntimeException();
}
}
/**
* Get the string to be encrypted
* #param args
*/
public static void main(String[] args) {
System.out.println(getEncryptString(""));
System.out.println(getEncryptString(""));
}
}
Answer my own question, maybe because I did not use Spring Boot.
The implementation logic of these two classes is different. Spring Boot use PropertySourcesPlaceholderConfigurer.
This answer is not accurate. I am a beginner and I have not figured it out in many places. Please understand if the deviation is large.
I'm writing PGP server to generate keys for users.
I want to sign any generated PGP public key with my private key in Java with Bouncy Castle, to make it trusted for my users.
I have no problems with generating key pair - it works perfect.
I tried to sign by gpg --sign-key command line and it works good.
But I can't get the same in Java. I tried many solutions like this: java sign public pgp key with bouncycastle
and DirectKeySignature class from BC examples package, but nothing works for my.
Key is signed but gpg --check-sigs tell that signature is incorrect:
gpg --check-sigs "Adrian (test) <al#p-c.pl>"
pub 4096R/9D5D4AB8 2018-06-20
sig- E9798A8A 2018-08-04 Test (test) <test#sample.com>
uid Adrian (test) <al#p-c.pl>
sig! 9D5D4AB8 2018-06-20 Adrian (test) <al#p-c.pl>
1 incorrect signature
Source code in two versions:
version 1:
public void signKey(String id, PGPSecretKey mySecretKey, PGPPublicKey publicKeyToBeSigned, char[] passPhrase, OutputStream out) throws PGPException, IOException {
PGPPrivateKey pgpPrivKey = mySecretKey.extractPrivateKey(
new JcePBESecretKeyDecryptorBuilder().setProvider(provider).build(passPhrase));
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
new JcaPGPContentSignerBuilder(mySecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1));
signatureGenerator.init(PGPSignature.DIRECT_KEY, pgpPrivKey);
PGPSignature signature = signatureGenerator.generateCertification(id, publicKeyToBeSigned);
PGPPublicKey result = PGPPublicKey.addCertification(publicKeyToBeSigned, signature);
out = new ArmoredOutputStream(out);
result.encode(out);
out.close();
}
version 2 (baset on org.bouncycastle.openpgp.examples.DirectKeySignature):
public void signPublicKey(PGPSecretKey secretKey, String secretKeyPass, PGPPublicKey keyToBeSigned, String notationName, String notationValue, OutputStream out) throws Exception {
PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(
new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(secretKeyPass.toCharArray()));
PGPSignatureGenerator sGen = new PGPSignatureGenerator(
new JcaPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("BC"));
sGen.init(PGPSignature.DIRECT_KEY, pgpPrivKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
boolean isHumanReadable = true;
spGen.setNotationData(true, isHumanReadable, notationName, notationValue);
PGPSignatureSubpacketVector packetVector = spGen.generate();
sGen.setHashedSubpackets(packetVector);
PGPPublicKey result = PGPPublicKey.addCertification(keyToBeSigned, sGen.generate());
out = new ArmoredOutputStream(out);
result.encode(out);
out.close();
}
Test:
#Test
public void signKey() throws Exception {
FileInputStream in = new FileInputStream("src/test/resources/secret.dat");
PgpServiceImpl pgp = new PgpServiceImpl();
PGPSecretKey pgpSecretKey = pgp.readSecretKey(in, "..........".toCharArray());
PGPPublicKey pubKey = pgp.readPublicKey(new FileInputStream("src/test/resources/pub_other.dat"));
ByteArrayOutputStream res = new ByteArrayOutputStream();
pgp.signPublicKey(pgpSecretKey, "..........", pubKey, "Test (test) <test#sample.com>", "Adrian (test) <al#p-c.pl>", res);
System.out.println(new String(res.toByteArray()));
}
result:
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.60
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
snGdpaUOpLJZRe2zI/i59UIHi1OoKERxYw==
=9lxJ
-----END PGP PUBLIC KEY BLOCK-----
this works perfectly:
public void signPublicKey(PGPSecretKey secretKey, char[] passPhrase, PGPPublicKey keyToBeSigned, OutputStream out) throws PGPException, IOException {
PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(
new JcePBESecretKeyDecryptorBuilder().setProvider( provider )
.build(passPhrase));
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
new JcaPGPContentSignerBuilder( secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1 ) );
signatureGenerator.init( PGPSignature.DEFAULT_CERTIFICATION, pgpPrivKey );
Iterator<String> ids = keyToBeSigned.getUserIDs();
if(!ids.hasNext()) throw new IllegalArgumentException("klucz nie posiada żadnego User ID");
String id = ids.next();
PGPSignature signature = signatureGenerator.generateCertification(id, keyToBeSigned);
PGPPublicKey newKey = PGPPublicKey.addCertification(keyToBeSigned, id, signature);
out = new ArmoredOutputStream(out);
newKey.encode(out);
out.close();
}
My Java application is receiving a JWT. I have the public key in PEM format:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAixn0CGu8/M4txn4pdp8K
m8RQfVa+cHX25/a5sPmzP49u7YlQsRvtOexzgdwDcfUJm3hHMZcbZBtrHKsS8q4Q
QtGQioyVml8EaLuFNFYisaIEldVyRbXFG54FNp03vSU9ImS/cOiM9swo+1w5JgWO
F9efy7JO40LA9E7lv64COUYjFhrn+HRZuKoblL19+Sj49FyXexAUS29UM9PfIdY6
ar1FA8cxzPqW7EkXZ0Mua3IzNnYcjMvUL9TJwoLAAz9S1Tv4Is5jupy9UXkuJ4r8
Jx9DqI3Q3ur0VekYSd5tnTI4K+no9ABCFVv7+6Q45Ec2eB0xMwlqI+phcGhGMVCX
1QIDAQAB
-----END PUBLIC KEY-----
I know I can use a JwtConsumer to verify and decode the JWT:
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setVerificationKey(publicKey) // what do I pass here?
.build();
But how do I convert my PEM file into a format understood by .setVerificationKey()?
I had to first Base64 decode the public key to its constituent bytes, then encode the bytes in X509 format.
Here is a complete example showing this, and how to read some claims from the decoded JWT:
package jwt;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import sun.misc.BASE64Decoder;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Map;
public class JwtExample {
public void decodeJwt(String jwt) throws Exception {
// read public key from a file or config or something
String publicKeyPEM =
"-----BEGIN PUBLIC KEY-----\n" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAixn0CGu8/M4txn4pdp8K\n" +
"m8RQfVa+cHX25/a5sPmzP49u7YlQsRvtOexzgdwDcfUJm3hHMZcbZBtrHKsS8q4Q\n" +
"QtGQioyVml8EaLuFNFYisaIEldVyRbXFG54FNp03vSU9ImS/cOiM9swo+1w5JgWO\n" +
"F9efy7JO40LA9E7lv64COUYjFhrn+HRZuKoblL19+Sj49FyXexAUS29UM9PfIdY6\n" +
"ar1FA8cxzPqW7EkXZ0Mua3IzNnYcjMvUL9TJwoLAAz9S1Tv4Is5jupy9UXkuJ4r8\n" +
"Jx9DqI3Q3ur0VekYSd5tnTI4K+no9ABCFVv7+6Q45Ec2eB0xMwlqI+phcGhGMVCX\n" +
"1QIDAQAB\n" +
"-----END PUBLIC KEY-----";
// decode to its constituent bytes
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] publicKeyBytes = base64Decoder.decodeBuffer(publicKeyPEM);
// create a key object from the bytes
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
// create a JWT consumer
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setVerificationKey(publicKey)
.build();
// validate and decode the jwt
// eg: jwt = "eyJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6Ik1DaGFtYmU0IiwiZXhwIjoxNDU2OTEwODgzLCJzY29wZSI6WyJvcGVuaWQiLCJwMnAiLCJociIsImRhcyIsIm1lIl0sImNsaWVudF9pZCI6Im1vYmlsZSIsImp0aSI6ImNZcHBMYXltVzlmNXFBZk4ifQ.QqZI9vV8IznTjN-GtUSCri9-6HH6Yl1Oae6K8-d2yjQ4fysF5d3wStdL2kMazl7xeqbtSIsw-F5Aol9eHdGAu54b9IyBEM_QIasy0lnT8xFk0Zi36NJ-7yhl_89f6SB6TGimM59xUvzXxuAw3FzWM6TbiptInrCL2TXkhS69Gng-ANPeiSITUX5A1TDInssds6ZoSb7IOUMtxPGfrbO9sBjx8aJlIu9igkqk4OX5xBmxLp3icoo98I5v9Wt_Huu7eWKBfOskMSEav4X_m5_phbAZJ_F8nWRmcxk6O7hCQdawzegnhMxP2IPIhwlWRNX_8WxkNErq2fJgdazDf8pS_Q";
JwtClaims jwtDecoded = jwtConsumer.processToClaims(jwt);
Map<String, Object> jwtClaims = jwtDecoded.getClaimsMap();
String username = (String) jwtClaims.get("username"); // "MChambe4"
// ensure the required scope is claimed
String requiredScope = "das";
ArrayList scopes = (ArrayList) jwtClaims.get("scope");
// ensure claims contains the required scope
if (!scopes.stream().anyMatch(scope -> scope == requiredScope)) {
throw new Exception("Required scope is not claimed: " + requiredScope);
}
}
}
With v0.5.0 there is some utility support for dealing with the PEM encoded public keys, RsaKeyUtil.fromPemEncoded(String pem), which can maybe simplify things for you a bit. You can also get claim values directly from the JwtClaims object, which might also simplify. Here's your example with those slight modifications:
String jwt = "eyJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6Ik1DaGFtYmU0IiwiZXhwIjoxNDU2OTEwODgzLCJzY29wZSI6WyJvcGVuaWQiLCJwMnAiLCJociIsImRhcyIsIm1lIl0sImNsaWVudF9pZCI6Im1vYmlsZSIsImp0aSI6ImNZcHBMYXltVzlmNXFBZk4ifQ.QqZI9vV8IznTjN-GtUSCri9-6HH6Yl1Oae6K8-d2yjQ4fysF5d3wStdL2kMazl7xeqbtSIsw-F5Aol9eHdGAu54b9IyBEM_QIasy0lnT8xFk0Zi36NJ-7yhl_89f6SB6TGimM59xUvzXxuAw3FzWM6TbiptInrCL2TXkhS69Gng-ANPeiSITUX5A1TDInssds6ZoSb7IOUMtxPGfrbO9sBjx8aJlIu9igkqk4OX5xBmxLp3icoo98I5v9Wt_Huu7eWKBfOskMSEav4X_m5_phbAZJ_F8nWRmcxk6O7hCQdawzegnhMxP2IPIhwlWRNX_8WxkNErq2fJgdazDf8pS_Q";
// read public key from a file or config or something
String publicKeyPEM =
"-----BEGIN PUBLIC KEY-----\n" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAixn0CGu8/M4txn4pdp8K\n" +
"m8RQfVa+cHX25/a5sPmzP49u7YlQsRvtOexzgdwDcfUJm3hHMZcbZBtrHKsS8q4Q\n" +
"QtGQioyVml8EaLuFNFYisaIEldVyRbXFG54FNp03vSU9ImS/cOiM9swo+1w5JgWO\n" +
"F9efy7JO40LA9E7lv64COUYjFhrn+HRZuKoblL19+Sj49FyXexAUS29UM9PfIdY6\n" +
"ar1FA8cxzPqW7EkXZ0Mua3IzNnYcjMvUL9TJwoLAAz9S1Tv4Is5jupy9UXkuJ4r8\n" +
"Jx9DqI3Q3ur0VekYSd5tnTI4K+no9ABCFVv7+6Q45Ec2eB0xMwlqI+phcGhGMVCX\n" +
"1QIDAQAB\n" +
"-----END PUBLIC KEY-----";
RsaKeyUtil rsaKeyUtil = new RsaKeyUtil();
PublicKey publicKey = rsaKeyUtil.fromPemEncoded(publicKeyPEM);
// create a JWT consumer
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setVerificationKey(publicKey)
.build();
// validate and decode the jwt
JwtClaims jwtDecoded = jwtConsumer.processToClaims(jwt);
String username = jwtDecoded.getStringClaimValue("username"); // "MChambe4"
// ensure the required scope is claimed
String requiredScope = "das";
List<String> scopes = jwtDecoded.getStringListClaimValue("scope");
if (!scopes.stream().anyMatch(scope -> scope.equals(requiredScope))) {
throw new Exception("Required scope is not claimed: " + requiredScope);
}
}
I keep on getting java.security.NoSuchAlgorithmException: no such algorithm: AES for provider BC with my code below. It looks like I have included all the things I need.
My class is as follows:
....
import org.bouncycastle.jce.provider.BouncyCastleProvider;
class ... {
static
{
Security.addProvider(new BouncyCastleProvider());
}
public CryptSession(String _algo, String _provider, String _keyAlgo, int _keySize)
throws
NoSuchAlgorithmException,
NoSuchProviderException
{
KeyGenerator generator = KeyGenerator.getInstance("AES", "BC"); // KeyGenerator.getInstance(_algo, _provider);
generator.init(256); //generator.init(_keySize);
this._algo = _algo;
this._provider = _provider;
this._keyAlgo = _keyAlgo;
this._keySize = _keySize;
this._key = generator.generateKey();
}
...
You need to edit java security policy file add bauntycasle provider to policy file located in jdk/bin/lib directory