Generate token with RS256 and Jwts.builder().signWith() produces invalid signature - java

Using the JWT Java library and producing a token with the RS256 algorithm, I always get an invalid signature with the jwt.io debugger. Here is my example code, I tried to make it as simple as possible to start with my current project:
// Create a legitimate RSA public and private key pair:
KeyPair kp = RsaProvider.generateKeyPair();
PublicKey publicKey = kp.getPublic();
PrivateKey privateKey = kp.getPrivate();
String jwt = Jwts.builder().setSubject("Joe").signWith(SignatureAlgorithm.RS256, privateKey).compact();
This code is inspired from the test class here.
Any idea what I could be missing?

The jwt.io debugger expects that you provide the public key associated with the private key used to sign the token encoded in the Public Key file (PKCS#8) format.
Ensure that you specify it using exactly that format, an example follows:
-----BEGIN PUBLIC KEY-----
BASE64 DATA
-----END PUBLIC KEY-----

Related

JWT implementation in Java using .pem file

Would like to know if JWT implementation (generating token) in Java is only possible only via .jks file in keystore of we can do it using a .pem (certificate and key) file as well ?
I would like to store the .jks file in a secured location offered by the PaaS provider on which my application is running and this service does not allow a physical file to be stored but rather the encoded format of the binary contents can be stored in key value pair which we have to refer by decoding. However when i do so , I get a Invalid keystore error.Hence i would like to know if .jks is a mandate for JWT token generation ?
As per your requirement, you don't want to use the Keystore & instead of that you directly wants to read the private & public keys (or public certificate), you can easily achieve it by the following. I've added the apache codec library dependency import org.apache.commons.codec.binary.Base64; for the Base64 decoding purpose.
public static RSAPrivateKey readPrivateKey(File file) throws Exception {
String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());
String privateKeyPEM = key.replace("-----BEGIN PRIVATE KEY-----", "")
.replaceAll(System.lineSeparator(), "").replace("-----END PRIVATE KEY-----", "");
byte[] encoded = Base64.decodeBase64(privateKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
// to call above code
// private.key in PEM format
File file = new File("private.key");
PrivateKey key = readPrivateKey(file);
Similar things can be done for reading the public key and public certificate as per the requirement.

RSA Public Key from Java into NodeJS

I'm having some trouble with using a public key from Java to encrypt within NodeJS.
I've created an RSA key within keytool and output the public key as a base64 encoded string.
Base64.Encoder encoder = Base64.getEncoder();
byte[] pubKeyBytes = publicKey.getEncoded();
String pubKeyBase64 = encoder.encodeToString(pubKeyBytes);
I then took the the base64 encoded public key, wrapped it in -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- and used it to encrypt, then base64 encode a string within node.
const encryptedBuffer = crypto.publicEncrypt(encodedPublicKey, buffer);
const encryptedEncodedText = encryptedBuffer.toString("base64");
When I try to decrypt the encryptedEncodedText (base64 decoding first) within Java, it throws a BadPadding exception. Interestingly, if I export the private key to a PEM, Node can't decrypt with it either.
Any help would be appreciated!
After much searching and testing, it turned out to be a padding issue.
The public key provided to Node did was not being passed a passphrase, so it defaulted to RSA_PKCS1_OAEP_PADDING as per the documentation:
crypto.publicEncrypt
When decrypting on the Java side, it is necessary to inform Java of that padding as follows:
Cipher cipher = Cipher.getInstance("RSA/NONE/OAEPPadding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
This fixed the issue, and allowed messages to be exchanged between Java and NodeJS.
I tried to reproduce your problem. For me, it works fine. I suspect the only problematic step can be when you are creating the PEM file manually. Be sure to:
If you are copying the key string from any console, remove unwanted characters from the beginning and/or end.
And a new line character (\n) after -----BEGIN PUBLIC KEY----- and before -----END PUBLIC KEY-----.
like below:
var key = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqwEldwNI5s1LkUzwyQZkPQQFpgtj29W7RcHgdwAbUOe31Q8bybAgzg5cUMqdIQlQHq6S5dxsSJBTDCZozSu+pxtKqRLz0JjtCTZD5gS+CJR9DlXH5GgJt+KDiO6olbKiVsP/tsMPgRCFKUMiMKU+dA06dwrUqJlC1k/JzuYVrbwIDAQAB\n-----END PUBLIC KEY-----"
var plaintext = "hello world"
var buffer = new Buffer(plaintext, "utf8")
console.log(crypto.publicEncrypt(key, buffer))
To avoid all these issues, you can directly generate PEM file from Java using the bouncycastle library.

How can I create a Java KeyStore from an existing set of certificates and keys without using command line tools?

I obtain a randomly generated RSA certificate and private key as strings from another service, and I would like to use those strings to create a Java KeyStore. All of the examples I see involve saving these strings to files, using the openssl and keytool command line tools to create the keystore on disk, and then loading the resulting KeyStore into memory (like here). However, it makes more sense for my purposes to create the KeyStore entirely in memory.
To that end, I am trying to use the Java Security API. I am able to convert the certificate string into an instance of the java.security.cert.Certificate class, but I am unable to convert the private key into an instance of java.security.PrivateKey. Here's method I am trying to create:
private PrivateKey generatePrivateKey (String newKey)
throws NoSuchAlgorithmException,
InvalidKeySpecException, IOException {
//Configuring the KeyFactory to use RSA
KeyFactory kf = KeyFactory.getInstance("RSA");
//Convert the key string to a byte array
byte[] keyBytes = newKey.getBytes();
KeySpec ks = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey key = kf.generatePrivate(ks);
return key;
}
Where the value of newKey is something like "-----BEGIN RSA PRIVATE KEY-----\nMIIEow...gNK3x\n-----END RSA PRIVATE KEY-----".
When I run the code, I receive the following exception:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:217)
at java.security.KeyFactory.generatePrivate(KeyFactory.java:372)
... 30 more
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:341)
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:367)
at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(RSAPrivateCrtKeyImpl.java:91)
at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)
at sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:316)
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:213)
... 32 more
This particular error is very similar to this stackoverflow question, and I would be grateful if that question were resolved, but I also want to know if my higher level goal (creating a JKS programmatically and solely in memory using Java) is feasible, and if so, whether I am on the right path.
you need to decode base64 if your key is in the base64 representation:
KeySpec ks = new PKCS8EncodedKeySpec(Base64.decodeBase64(newKey));
you can use org.apache.commons.codec.binary.Base64 to do this.
if you want to generate keyPair you can you this code:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// extract the encoded private key, this is an unencrypted PKCS#8 private key
byte[] encodedprivkey = keyPair.getPrivate().getEncoded();
System.out.println(Base64.encodeBase64String(encodedprivkey));

Bouncycastle: how to create an encrypted PKCS8 representation of an RSA private key?

In my application I have a AsymmetricCipherKeyPair keyPair and KeyParameter key. From that, I need to generate a PKCS#8 representation of the private key in the form
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
Can anybody give me a hint on how I could accomplish that?
You can use org.bouncycastle.crypto.util.PrivateKeyInfoFactory to create a PrivateKeyInfo object from the private KeyParameter. getEncoded() on that will return the bytes that represent the PKCS#8 version of the key.
The PEM headings you give do suggest you might want an OpenSSL encoding instead - in that case you should be able to use the BC PEMWriter in conjunction with the PrivateKeyInfo object, or in conjunction with a PrivateKey object generated using KeyFactory with the encoding of PrivateKeyInfo passed in as a PKCS8EncodedKeySpec (sort of depends on what version of BC you are using).
Regards,
David

BouncyCastle signature value does not match with dotNET signature value

I have a problem with the "SHA1withRSA" signature using Bouncy Castle on Android: Bouncy Castle signature value does not match with .NET signature value.
I tried to use many algorithms like "SHA1withRSA", "SHA1withRSAAndMGF1" or "SHA1withRSA/ISO9796-2" with no satisfactory results.
If I use the same message, same algorithm and the same private key, the result must be identical in .NET and Bouncy Castle... shouldn't it?
Whats wrong in my code???
Getting the private key:
...
ks = KeyStore.getInstance("PKCS12");
...
key = ks.getKey(alias, senha.toCharArray());
if (key instanceof PrivateKey) {
privateKey = (PrivateKey) key;
}
Signature method:
public String signer(String txt, String alg) throws Exception {
Signature signer = Signature.getInstance(alg, new BouncyCastleProvider());
signer.initSign(privateKey);
signer.update(txt.getBytes("UTF-8"));
return Base64.encodeToString(signer.sign(), Base64.NO_WRAP);
}
My app must use XMLDSIG protocol to send XML to a government's web service.
Value being signed can have some random padding data (is your signature is always the same on one side?). You should generate signature on one side and try to verify on the other.
This is especially the case for the following algorithms:
"SHA1WITHRSAANDMGF1"
"SHA224WITHRSAANDMGF1"
"SHA256WITHRSAANDMGF1"
"SHA384WITHRSAANDMGF1"
"SHA512WITHRSAANDMGF1"
Install the Bouncy Castle provider and see if the signature can be verified with the public key.

Categories

Resources