I generated a RSA key using keytool.
keytool -genkeypair -alias myAlias -keyalg RSA -dname ...
creates a jks.
I am using this to sign..
String sig = Jwts.builder().setClaims(claims).setIssuedAt(new
Date(now)).setExpiration(expires.getTime()).signWith(
SignatureAlgorithm.RS256, myKey).compact();
and for verifying..
Certificate cert = keystore.getCertificate(myAlias);
PublicKey publicKey = cert.getPublicKey();
Jwt jwt = Jwts.parser().setSigningKey(publicKey).require("user",
"me").require("iotDevice", "123456789").parse(signature);
verifies fine.
I created a public key for this cert..
keytool -export -alias myAlias -keystore myKeyStore.jks -file myPem.pem
openssl rsa -in myPem.pem -pubout > myApp.pub
I am loading this public key to verify again..
RSAPublicKey getPublicKeyFromString(String key) throws IOException,
GeneralSecurityException {
String publicKey = key;
publicKey = publicKey.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKey = publicKey.replace("-----END PUBLIC KEY-----", "");
publicKey = publicKey.replace("\n", "");
byte[] encoded = Base64.getDecoder().decode(publicKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new
X509EncodedKeySpec(encoded));
return pubKey;
}
PublicKey publicKey2 = getPublicKeyFromString(keyString); // keyString has myApp.pub
Jwt jwt = Jwts.parser().setSigningKey(publicKey2).require("user",
"me").require("iotDevice", "123456789").parse(signature);
// fails with message
JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
Any help is appreciated.
Thanks in advance
Related
I am trying to generate a JWT token that has to be given to another system.
I have followed the below steps for doing so:
Created a Connect App. I have got consumer key using this App.
Created a self signed certificate. Downloaded the certificate(crt) file. Converted the crt file into key file using openSSL. I used command:
openssl req -newkey rsa:2048 -nodes -keyout SSO_Self_Signed.key -out SSO_Self_Signed.csr
SSO_Self_Signed is a label name of my certificate. The generated key file has private key in between -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----
3. I have created an APEX class that will generate JWT token using the creds we obtained in STEP 1 and STEP 2.
The code snippet of my code is also mentioned below:
jwtToken is generated on line 46.
When I am trying to validate the JWT in https://jwt.io/, I am getting invalid signature and I am not able to figure out the exact cause of the issue.
The inputs passed to the class includes - iss:mailaddress; sub:other system url; aud: consumer key from step1; privateKey: key from step2.
public class SSOJWTGenerator {
public static String generateJWT(String iss, String sub, String aud, String privateKey) {
String alg = 'RS256';
String typ = 'JWT';
// Create the JWT header
Map<String, Object> header = new Map<String, Object>{
'alg' => alg,
'typ' => typ
};
String encodedHeader = base64UrlEncode(JSON.serialize(header));
// Create the JWT claim set
Map<String, Object> claimSet = new Map<String, Object>{
'iss' => iss,
'sub' => sub,
'aud' => aud,
'exp' => String.valueOf(DateTime.now().getTime()/1000 + 300),
'iat' => String.valueOf(DateTime.now().getTime()/1000)
};
String encodedClaimSet = base64UrlEncode(JSON.serialize(claimSet));
// Create the signature
String input = encodedHeader + '.' + encodedClaimSet;
privateKey = privateKey.replace('-----BEGIN PRIVATE KEY-----', '');
privateKey = privateKey.replace('-----END PRIVATE KEY-----', '');
privateKey = privateKey.deleteWhitespace();
Blob privateKeyBlob = EncodingUtil.base64Decode(privateKey);
Blob signatureBlob = Crypto.sign('RSA-SHA256', Blob.valueOf(input), privateKeyBlob);
String signature = base64UrlEncode(signatureBlob);
// Combine the header, claim set, and signature to create the JWT token
String jwtToken = encodedHeader + '.' + encodedClaimSet + '.' + signature;
System.debug('jwtToken'+ jwtToken);
return jwtToken;
}
private static String base64UrlEncode(String input) {
// Replace + with -, / with _, and remove any trailing = signs
String base64 = EncodingUtil.base64Encode(Blob.valueOf(input));
base64 = base64.replace('+', '-').replace('/', '_').replaceAll('\\=+$', '');
return base64;
}
private static String base64UrlEncode(Blob input) {
// Replace + with -, / with _, and remove any trailing = signs
String base64 = EncodingUtil.base64Encode(input);
base64 = base64.replace('+', '-').replace('/', '_').replaceAll('\\=+$', '');
return base64;
}
}
I want to sign a PDF with a self generated certificate. In this process I need a keystore and private key. The signing will be made with PDFBox by using the class CreateSignature()
To generate a keyStore with a self generated certificate I am using this:
public KeyStore generateSampleKeyStoreWith509Certificate() throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableEntryException {
X509Certificate cert;
PrivateKey caKey;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(1024, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
caKey = keyPair.getPrivate();
Date notBefore = new Date();
Date notAfter = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 365);
SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
X509v3CertificateBuilder newGen = new X509v3CertificateBuilder(new X500Name(issuer), serial, notBefore,
notAfter, new X500Name(subject), spkInfo);
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider("BC")
.build(caKey);
X509CertificateHolder certHolder = newGen.build(sigGen);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream is1 = new ByteArrayInputStream(certHolder.getEncoded());
cert = (X509Certificate) cf.generateCertificate(is1);
is1.close();
} catch (OperatorCreationException | CertificateException | IOException | NoSuchProviderException
| NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
keyStore.setCertificateEntry("SelfSigned", cert);
return keyStore;
}
The certificate is entered correctly, but shouldn't there be a key, too? Or am I wrong that the keystore should be holding a key?
I'm just figuring this sibject, so I'm thankful of every bit of help.
X509Certificate[] certChain = new X509Certificate[1];
certChain[0] = cert;
keyStore.setKeyEntry("SelfSigned",caKey, password, certChain);
Adding the above code, at the bottom, enters the privateKey previously created to the keystore. It seems most examples on the internet assume loading a keystore with privatekey already entered before.
I have one small doubt as i am new to AES.
I encrypted a string using one certificate with some password lets say , 'xxx'.
Now i duplicated the certificate by changing the password of it.
When i try to decrypt the encrypted string with the duplicated cert, it says Bad padding exception.Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
However, when i use the original cert, it decrypts properly.
Could anyone please guide me on it?
public SecretKey retrieveKey(String password, byte[] certFile) throws Exception {
try {
String alias = null;
certPass = password;
char[] pass = certPass.toCharArray();
KeyStore keyStore = KeyStore.getInstance("jceks");
InputStream inputStream = new ByteArrayInputStream(certFile);
keyStore.load(inputStream, pass);
Enumeration enumeration = keyStore.aliases();
while (enumeration.hasMoreElements()) {
alias = (String) enumeration.nextElement();
}
Certificate cert = keyStore.getCertificate(alias);
Key key = cert.getPublicKey();
aesSecretKey = new SecretKeySpec(key.getEncoded(), algorithm);
byte[] encoded = aesSecretKey.getEncoded();
byte[] encryptionKey = Arrays.copyOfRange(encoded, encoded.length - 16, encoded.length);
aesSecretKey = new SecretKeySpec(encryptionKey, algorithm);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw e;
}
return aesSecretKey;
}
You should use RSA to wrap / unwrap the AES key. The public key is not identical to the AES key, so the following code is certainly incorrect:
Key key = cert.getPublicKey();
aesSecretKey = new SecretKeySpec(key.getEncoded(), algorithm);
I'm trying to create a JWT with JWT Scala using:
import pdi.jwt._
...
val claim = s"""{ \"exp\": $oneMinuteFromNow }"""
Jwt.encode(claim, key, JwtAlgorithm.ES512)
key is a String containing a private key in the form of
-----BEGIN EC PRIVATE KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAmG8JrpLz14+qUs7oxFX0pCoe90Ah
MMB/9ZENy8KZ+us26i/6PiBBc7XaiEi6Q8Icz2tiazwSpyLPeBrFVPFkPgIADyLa
T0fp7D2JKHWpdrWQvGLLMwGqYCaaDi79KugPo6V4bnpLBlVtbH4ogg0Hqv89BVyI
ZfwWPCBH+Zssei1VlgM=
-----END EC PRIVATE KEY-----
But I'm getting:
java.security.spec.InvalidKeySpecException: encoded key spec not recognized: wrong version for private key info
at org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePrivate(Unknown Source)
at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.engineGeneratePrivate(Unknown Source)
at java.security.KeyFactory.generatePrivate(KeyFactory.java:366)
at pdi.jwt.JwtUtils$.parsePrivateKey(JwtUtils.scala:109)
at pdi.jwt.JwtUtils$.sign(JwtUtils.scala:152)
at pdi.jwt.JwtUtils$.sign(JwtUtils.scala:159)
at pdi.jwt.JwtCore$class.encode(Jwt.scala:80)
at pdi.jwt.Jwt$.encode(Jwt.scala:23)
at pdi.jwt.JwtCore$class.encode(Jwt.scala:109)
at pdi.jwt.Jwt$.encode(Jwt.scala:23)
I've Googled everything I can think of, but I'm not sure what I'm doing wrong. Any help appreciated!
(above key is not a real key BTW)
I finally got this working.
openssl ecparam -genkey -name secp521r1 -noout -out ec512-key-pair.pem
Is the command I ued to generate the key. The code to sign the JWT:
object JwtGen {
def generateToken(keyPath: String) : String = {
if (keyPath.isEmpty) throw new java.io.IOException("keyPath is empty!")
val claim = s"""{ \"exp\": $oneMinuteFromNow }"""
Jwt.encode(claim, encodeKey(keyPath), JwtAlgorithm.ES512)
}
private def oneMinuteFromNow: Long = {
(System.currentTimeMillis / 1000) + 60
}
private def encodeKey(keyPath: String): PrivateKey = {
Security.addProvider(new BouncyCastleProvider)
val parser = new PEMParser(new InputStreamReader(new FileInputStream(keyPath)))
val pemObject = Option(parser.readObject)
pemObject match {
case Some(kp: PEMKeyPair) => convertFormat(kp)
case _ => throw new java.io.IOException(s"cannot parse pem file $keyPath")
}
}
private def convertFormat(pemKeyPair: PEMKeyPair): PrivateKey = {
val converter = new JcaPEMKeyConverter
val keyPair = converter.getKeyPair(pemKeyPair)
keyPair.getPrivate
}
}
A CA gives me a P12 which I want to convert to PEM inside a jvm.
Yes using openssl command works:
openssl pkcs12 -in jack.p12 -out jack.pem -nodes -clcerts
But then it gets ugly getting key and keystore passwords into the openssl program from java not to mention unsecure.
so BouncyCastle seems to be best crypto API for java...
How could it be done (convert P12 to PEM) using bouncy...
Here is an example:
private File createPem(final Certificate certP12, final String name) {
File file = new File(getFileName(name, "pem"));
FileWriter fileWriter;
try {
fileWriter = new FileWriter(file);
PEMWriter pemWriter = new PEMWriter(fileWriter);
pemWriter.writeObject(certP12);
pemWriter.flush();
pemWriter.close();
fileWriter.close();
} catch (IOException e) {
log.error("", e);
}
return file;
}
Load the certificate as Org.BouncyCastle.X509.X509Certificate
Convert to pem.
public static Org.BouncyCastle.X509.X509Certificate ImportCertFromPfx(string path, string password)
{
Pkcs12Store store = new Pkcs12StoreBuilder().Build();
store.Load(File.OpenRead(path), password.ToCharArray());
string alias = null;
foreach (string str in store.Aliases)
{
if (store.IsKeyEntry(str))
alias = str;
}
if (alias == null)
{
Console.WriteLine("alias is null");
}
else
Console.WriteLine(alias);
X509CertificateEntry certEntry = store.GetCertificate(alias);
Org.BouncyCastle.X509.X509Certificate x509cert = certEntry.Certificate;
return x509cert;
}
Org.BouncyCastle.X509.X509Certificate x509cert = ImportCertFromPfx(p12path, p12password);
StringBuilder CertPem = new StringBuilder();
PemWriter CSRPemWriter = new PemWriter(new StringWriter(CertPem));
CSRPemWriter.WriteObject(x509cert);
CSRPemWriter.Writer.Flush();
//get Cert text
var CertPemText = CertPem.ToString();
Console.WriteLine(CertPemText);