java-jwt with public/private keys - java

Auth0 provides two JWT libraries, one for Node: node-jsonwebtoken, and one for Java: java-jwt.
I created the private/public key pair, and used it successfully in Node with node-jsonwebtoken:
var key = fs.readFileSync('private.key');
var pem = fs.readFileSync('public.pem');
var header = {...};
var payload = {...};
header.algorithm = "RS256";
var message = jsonwebtoken.sign(payload, key, header);
var decoded = jsonwebtoken.verify(message, pem, {algorithm: "RS256"});
But I found no way of doing the same in Java with java-jwt.
Anyone has a working example of how to use private/public keys for JWT in Java?

I used the following code for JWT in Java. Try it.
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class JWTJavaWithPublicPrivateKey {
public static void main(String[] args) {
System.out.println("generating keys");
Map<String, Object> rsaKeys = null;
try {
rsaKeys = getRSAKeys();
} catch (Exception e) {
e.printStackTrace();
}
PublicKey publicKey = (PublicKey) rsaKeys.get("public");
PrivateKey privateKey = (PrivateKey) rsaKeys.get("private");
System.out.println("generated keys");
String token = generateToken(privateKey);
System.out.println("Generated Token:\n" + token);
verifyToken(token, publicKey);
}
public static String generateToken(PrivateKey privateKey) {
String token = null;
try {
Map<String, Object> claims = new HashMap<String, Object>();
// put your information into claim
claims.put("id", "xxx");
claims.put("role", "user");
claims.put("created", new Date());
token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS512, privateKey).compact();
} catch (Exception e) {
e.printStackTrace();
}
return token;
}
// verify and get claims using public key
private static Claims verifyToken(String token, PublicKey publicKey) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
System.out.println(claims.get("id"));
System.out.println(claims.get("role"));
} catch (Exception e) {
claims = null;
}
return claims;
}
// Get RSA keys. Uses key size of 2048.
private static Map<String, Object> getRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
Map<String, Object> keys = new HashMap<String, Object>();
keys.put("private", privateKey);
keys.put("public", publicKey);
return keys;
}
}
Maven Dependency
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>

Recent versions (since 3.0.0) of the auth0 java-jwt library supports RSA and ECDSA for signing JWT tokens using a public/private key pair.
Example of signing a JWT using java-jwt (based on the documentation).
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Map;
class JwtPKSigningExample {
public static void main(String[] args) throws Exception {
Map<String, Object> keys = generateRSAKeys();
String token = null;
try {
RSAPrivateKey privateKey = (RSAPrivateKey) keys.get("private");
Algorithm algorithm = Algorithm.RSA256(null, privateKey);
token = JWT.create()
.withIssuer("pk-signing-example")
.sign(algorithm);
} catch (JWTCreationException x) {
throw x;
}
try {
RSAPublicKey publicKey = (RSAPublicKey) keys.get("public");
Algorithm algorithm = Algorithm.RSA256(publicKey, null);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("pk-signing-example")
.build();
DecodedJWT jwt = verifier.verify(token);
System.out.println(jwt.getToken());
} catch (JWTVerificationException x) {
throw x;
}
}
private static Map<String, Object> generateRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return Map.of("private", keyPair.getPrivate(), "public", keyPair.getPublic());
}
}

That particular library doesn't support it. But you can check others for Java that do. See here: https://jwt.io/

package com.java;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Enumeration;
import org.jose4j.json.internal.json_simple.parser.ParseException;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.keys.resolvers.JwksVerificationKeyResolver;
import org.jose4j.keys.resolvers.VerificationKeyResolver;
import org.jose4j.lang.JoseException;
public class JWTSigningAndVerification {
public static void main(String args[]) throws Exception {
String jwt = generateJWT();
validateJWTwithJWKS(jwt);
}
private static String generateJWT() throws FileNotFoundException, KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, JoseException {
JwtClaims jwt_claims = new JwtClaims();
jwt_claims.setSubject("sub");
jwt_claims.setIssuer("https://domain");
jwt_claims.setIssuedAtToNow();
jwt_claims.setExpirationTimeMinutesInTheFuture(1000000);
jwt_claims.setGeneratedJwtId();
jwt_claims.setClaim("sid", "sessionid");
jwt_claims.setClaim("email", "test#mail.com");
jwt_claims.setClaim("given_name", "first");
jwt_claims.setClaim("family_name", "last");
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(jwt_claims.toJson());
String KeyPassword = "p12-key-password";
File file = new File("path-to-key.p12");
InputStream stream = new FileInputStream(file);
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(stream, KeyPassword.toCharArray());
Enumeration e = store.aliases();
String alias = (String) e.nextElement();
PrivateKey key = (PrivateKey) store.getKey(alias, KeyPassword.toCharArray());
jws.setKey(key);
jws.setKeyIdHeaderValue("1");
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_PSS_USING_SHA512);
jws.setHeader("typ", "JWT");
String jwt = jws.getCompactSerialization();
System.out.println(jwt);
return jwt;
}
private static void validateJWTwithJWKS(String jwt) throws JoseException, FileNotFoundException, IOException,
ParseException, InvalidJwtException, MalformedClaimException {
JsonWebKeySet jsonWebKeySet = new JsonWebKeySet("json-jwks-escaped");
VerificationKeyResolver verificationKeyResolver = new JwksVerificationKeyResolver(jsonWebKeySet.getJsonWebKeys());
JwtConsumer jwtConsumer = new JwtConsumerBuilder().setVerificationKeyResolver(verificationKeyResolver).build();
JwtClaims claims = jwtConsumer.processToClaims(jwt);
System.out.println("sub:- " + claims.getSubject());
}
}

Related

JWT, How can we use same RSA key pairs for both Java & NodeJS

I am trying to understand Jason Web Token with RSA Public Key & Private Key pairs.
I generated a RSA key pairs using this websites - https://travistidwell.com/jsencrypt/demo/
, and saved those in two different files-
private key "my_key" is as
-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgFrSlpibGae4QZjcQp20b9+go22JF9WHJU4TLfdwJbumIgHlukD9
S/W7Dr1j4AXtJ0yJopdnf0bY4294SjjMO4DFHrLDEAB8ZeZkMhfyparYWKHECV5e
szvR6dIHGeDc1CoJolAsPmpO+0qfK4LfkesmLu+diIy9I+B2KJAAka6hAgMBAAEC
gYAlsQuqnYOSJVej1pUW2dEr34CzbpejmAiVVERZUgN20sV+QBaB7hzeCBlf49kO
3JLYoq4FY4BgqJYKpsM2uxteIC8dLJdayyNze1mnPiPW3wLR2bzedOJdjcY0H3Ju
M7lZlk9NVnBI7PNXw4hanifCGY2XPKyrOwrqrMgFsC382QJBAKlKAErRU9N8LZgq
TNAhMTaTPvpZuK4LqqV8RCqPFuNz1tVsO/cjGHfrU3R1pM619K6xCUjvwYvXL0MM
6vm/CKsCQQCJV6/bFuc4L+Uyik0Q+zaRz3fKi5h/AWoN1vmBcXQ01F5GtTAlZDc6
lk/rBk4M69k899I95ReMlIfc68S51P3jAkAp5eLEoaI5iVZPfsicClr/wtmnZGVM
zh5h7quATQHBMX5OPAdrVwhLRbbV7/fmISp5wd8mahBg59UOpzfQr/MZAkAtgC9y
lhPkOXnlVIxTo+ZgSCuXnsfWy9Em5KGkkMG+/tx88GoS+TCS6Flxs5UIEtrVqASv
HMbAfDTGrBVwu2+hAkAnrAHRhFlC8O9zAhoFkWcaYeFu+y1pBcF3pzFfQBpn6uK8
Xd0Ln+eMNaYl6lAECG36jtUvnKLkyfWMHzqdkERj
-----END RSA PRIVATE KEY-----
public key "my_key.pub" is as
-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgFrSlpibGae4QZjcQp20b9+go22J
F9WHJU4TLfdwJbumIgHlukD9S/W7Dr1j4AXtJ0yJopdnf0bY4294SjjMO4DFHrLD
EAB8ZeZkMhfyparYWKHECV5eszvR6dIHGeDc1CoJolAsPmpO+0qfK4LfkesmLu+d
iIy9I+B2KJAAka6hAgMBAAE=
-----END PUBLIC KEY-----
When I am using this key in NodeJS as -
var fs = require('fs');
var JWT = require('jsonwebtoken');
var privateKey = fs.readFileSync('./my_key', 'utf8');
var publicKey = fs.readFileSync('./my_key.pub', 'utf8');
var jwtOptions = {
issuer : 'Chittaranjan Sardar',
subject: 'JWT Cross Platforms',
audience: 'https://github.com/crsardar',
expiresIn: '5m',
algorithm: "RS256"
};
let token = JWT.sign({user: 'CRSARDAR'}, privateKey, jwtOptions);
console.log("generated token = " + token);
var verifyResult = JWT.verify(token, publicKey, jwtOptions);
console.log("Verification has passed : " + JSON.stringify(verifyResult));
It is Working Fine.
But, when I am trying to use in Java, as follows -
package com.crsardar.handson.java.springboot.jwt.controller;
import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class JWTJavaWithPublicPrivateKey
{
public static void main(String[] args)
{
System.out.println("generating keys");
Map<String, Object> rsaKeys = null;
try
{
rsaKeys = getRSAKeys();
}
catch (Exception e)
{
e.printStackTrace();
}
PublicKey publicKey = (PublicKey) rsaKeys.get("public");
PrivateKey privateKey = (PrivateKey) rsaKeys.get("private");
System.out.println("generated keys");
String token = generateToken(privateKey);
System.out.println("Generated Token:\n" + token);
verifyToken(token, publicKey);
}
public static String generateToken(PrivateKey privateKey)
{
String token = null;
try
{
Instant now = Instant.now();
Instant after = now.plus(Duration.ofMinutes(1));
Date date = Date.from(after);
Claims claims = Jwts.claims();
claims.put("user", "CRSARDAR");
claims.put("issuer", "Chittaranjan Sardar");
claims.put("subject", "JWT Cross Platforms");
claims.put("audience", "https://github.com/crsardar");
claims.put("created", new Date());
JwtBuilder jwtBuilder = Jwts.builder();
jwtBuilder.setClaims(claims);
jwtBuilder.setExpiration(date);
jwtBuilder.signWith(SignatureAlgorithm.RS512, privateKey);
token = jwtBuilder.compact();
}
catch (Exception e)
{
e.printStackTrace();
}
return token;
}
// verify and get claims using public key
private static Claims verifyToken(String token, PublicKey publicKey)
{
Claims claims;
try
{
JwtParser jwtParser = Jwts.parser();
jwtParser.setSigningKey(publicKey);
Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token);
claims = claimsJws.getBody();
System.out.println("verifyToken : issuer = " + claims.get("issuer"));
}
catch (Exception e)
{
claims = null;
}
return claims;
}
private static Map<String, Object> getRSAKeys() throws Exception
{
Map<String, Object> keys = new HashMap();
PrivateKey privateKey = getPrivateKey();
PublicKey publicKey = getPublicKey();
keys.put("private", privateKey);
keys.put("public", publicKey);
return keys;
}
private static PrivateKey getPrivateKey() throws Exception
{
ClassLoader classLoader = JWTJavaWithPublicPrivateKey.class.getClassLoader();
URL resource = classLoader.getResource("my_key");
File file = new File(resource.getFile());
byte[] keyBytes = Files.readAllBytes(Paths.get(file.toURI()));
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(spec);
}
private static PublicKey getPublicKey() throws Exception
{
ClassLoader classLoader = JWTJavaWithPublicPrivateKey.class.getClassLoader();
URL resource = classLoader.getResource("my_key.pub");
File file = new File(resource.getFile());
byte[] keyBytes = Files.readAllBytes(Paths.get(file.toURI()));
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
}
It is giving me following errors -
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)
at com.crsardar.handson.java.springboot.jwt.controller.JWTJavaWithPublicPrivateKey.getPrivateKey(JWTJavaWithPublicPrivateKey.java:126)
at com.crsardar.handson.java.springboot.jwt.controller.JWTJavaWithPublicPrivateKey.getRSAKeys(JWTJavaWithPublicPrivateKey.java:110)
at com.crsardar.handson.java.springboot.jwt.controller.JWTJavaWithPublicPrivateKey.main(JWTJavaWithPublicPrivateKey.java:35)
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:331)
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:357)
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)

RSA Signature having public key as text not verified in java

This issue is generated in continuation of past question How to RSA verify a signature in java that was generated in php . That code work for simple text. But Now I have requirement for signing and verifying the text which also have a public key ( other than verification key ) in format.
text1:text2:exported-public-key
Example :
53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:MIIBCgKCAQEAxWg6ErfkN3xu8rk9WsdzjL5GpjAucMmOAQNeZcgMBxN+VmU43EnvsDLSxUZD1e/cvfP2t2/dzhtV6N2IvT7hveuo/zm3+bUK6AnAfo6pM1Ho0z4WetoYOrHdOVNMMPaytXiVkNlXyeWRF6rl9JOe94mMYWRJzygntiD44+MXsB6agsvQmB1l8thg/8+QHNOBBU1yC4pLQwwO2cb1+oIl0svESkGpzHk8xJUl5jL6dDnhqp8+01KE7AGHwvufrsw9TfVSAPH73lwo3mBMVXE4sfXBzC0/YwZ/8pz13ToYiN88DoqzcfD3+dtrjmpoMpymAA5FBc5c6xhPRcrn24KaiwIDAQAB
PHP Code :
$rsa = new Crypt_RSA();
$keysize=2048;
$pubformat = "CRYPT_RSA_PUBLIC_FORMAT_PKCS1";
$privformat = "CRYPT_RSA_PRIVATE_FORMAT_PKCS8";
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kp = $d['publickey'];
$Ks = $d['privatekey'];
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kver = $d['publickey'];
$KSign = $d['privatekey'];
$plainText = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:".$Kp;
// Signing
$hash = new Crypt_Hash('sha256');
$rsa = new Crypt_RSA();
$rsa->loadKey($KSign);
$rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setHash('sha256');
$signature = $rsa->sign($plainText);
$signedHS = base64_encode($signature);
// Verification
$signature = base64_decode($signedHS);
$rsa->loadKey($Kver);
$status = $rsa->verify($plainText, $signature);
var_dump($status);
JAVA Code
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
//import java.util.Base64;
//import java.util.Base64.Decoder;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class VerifySig {
public static RSAPublicKey fromPKCS1Encoding(byte[] pkcs1EncodedPublicKey) {
// --- parse public key ---
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
try {
pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
.getInstance(pkcs1EncodedPublicKey);
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not parse BER PKCS#1 public key structure", e);
}
// --- convert to JCE RSAPublicKey
RSAPublicKeySpec spec = new RSAPublicKeySpec(
pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
KeyFactory rsaKeyFact;
try {
rsaKeyFact = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("RSA KeyFactory should be available", e);
}
try {
return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new IllegalArgumentException(
"Invalid RSA public key, modulus and/or exponent invalid", e);
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String pkey = "MIIBCgKCAQEA+8fKYCT4QiFUdsJ7VdF4xCkVmq/Kwc/10Jl3ie6mvn8hEsC3NAtMJu+Od12gyWYsS0zBDiQ8h2pGZ7p4uWqenc01dRRrq+g968zmoCKPUllPUuR6v9o+wYTX/os4hgaQSBg7DQn4g3BEekcvyk6e6zAMvuhHjeqnrinhCMFgJUhFL8zFNoyaH559C0TNbR6BTKzOoikah8cKhu4UOga0tWDC0I2Ifus/sHOwVaOBkDFIzD6jBxDH/QF8FsrLLTocuIb7Y6lVxFPPtgiUJku6b7wKExV0bPJvm6/Xhv1GX1FpMrA0Ylzj5IFviuviwgo534EcZQ/Hx3aIf4oPG8jVTQIDAQAB";
byte[] dpkey = Base64.decodeBase64(pkey);
RSAPublicKey publicKey = fromPKCS1Encoding(dpkey);
String plainData = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:MIIBCgKCAQEArszIunGg3ievJOpgesYQsp3nPGgrW+3VwkivkkktOXUBRzb3G3mZzidEjG6LxNe/rrNe0UczmnSHQoSBxJCHyUnCWNfScBD66CFG4hLo5Z1gxrP8D2M2lCa6ap2PWcsKiWqlu38EinMeBjBvB4aYpF7+FkFy64ObxR4pfVZxnxradkD0HvvMPLMbyeHxeGqYf8orERf9jfuKTdY8V44rxht2D2fg2WhB1+XL0JulsPvgOaSK3RPnwi+RQAJbihCIh5Zznn0KQCs5pIWoT3XKe1DMpQuEmphSOY9ZUg3AwlOrpRV+565x6GCSc615/6nowmqKzE4T7qT5nbH+ctiEHQIDAQAB";
String data = "iD96rNeR51BF2TUZSaw+QhW8SnsMXE5AdJiDVmJk6LL55jC26PBCnqXrFo2lsQt8aWRsZc0bHFGCcuIbhHA+Duo1/PwrxTqC5BZFL/frqsRSVa+vpvGEnj3xe4iImTEasMicQzzaAG9IWIgkRZ272lUZ8PqdtTuqAsRIwir6fEsfVs5uIErEWM18R4JxlFBc3LDIjFOFemEPSVIEBHwWht1c/CrdTtxPRIiugEb1jdofEBUNcWPZgfvApVx5+0aS9WTl31AY+RMlvp+13P/FQgAMnH9rvBdopRIVsZUNlMf8AOE2afhLPfOgx+41rzCB2wGCrRGELbml466WJ3wYNQ==";
byte[] ciphertext = Base64.decodeBase64(data);
System.out.println(new String(plainData.getBytes(), UTF_8));
verifyBC(publicKey, plainData, ciphertext);
System.out.flush();
}
private static void verifyBC(PublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
// what should work (for PKCS#1 v1.5 signatures), requires Bouncy Castle provider
//Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
Signature sig = Signature.getInstance( "SHA256withRSA");
sig.initVerify(publicKey);
sig.update(plainData.getBytes(UTF_8));
System.out.println(sig.verify(ciphertext));
}
}
It not gave any error but just return false when using public key in plainText. If try after removing with public key, It works and return true.
PHP is working fine and signature is verified in all cases.
I suspecting if java is unable to verify data having base 64 text/public key as text ?
UPDATE : I compare binary bytes of both two times and result show minor difference.
First Case
PHP -> ��#C:���sQ
JAVA -> ��/#C:���sQ
Second Case
PHP -> ��]Q0l�O+
JAVA -> ��]Q0l�
If php base64 is not compatible with apache base 64 ?
When I run the PHP code, I'm noticing that the $Kp variable contains a key in the wrong format:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAqCJ/2E+YZvXJyabQmi0zZlaXXGbfXHt8KYS27i+PAJKBODmevTrS
w59S5AOy2l7lB4z5mYHuwdT6bm6YYXgE0gnoX/b2L65xdD9XtlenS4Zm15TVTdR5
zde4nBa0QPKfhFvthOmdPr9xDhDb8Rojy/phX+Ftva33ceTXoB+CtLyidMWbQmUh
ZufnI7MwIOPAIzXNJJ85eyUjBdoNMwlAPZo9vYQWeiwYGyP1fjQwEWZgjCH/LJjl
sNR1X9vp5oi8/4omdnFRvKLpkd5R7WMmMfAyAXe7tcfMSXuVAgMWEj9ZG0ELpXbG
S3CK6nvOp2gFF+AjHo9bCrh397jYotE3HQIDAQAB
-----END RSA PUBLIC KEY-----
When I strip out all the extra formatting and export the key as a single line of base64, it works.
PHP code:
function extract_key($pkcs1) {
# strip out -----BEGIN/END RSA PUBLIC KEY-----, line endings, etc
$temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $pkcs1, 1);
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
return str_replace(array("\r", "\n", ' '), '', $temp);
}
$rsa = new Crypt_RSA();
$keysize=2048;
$pubformat = "CRYPT_RSA_PUBLIC_FORMAT_PKCS1";
$privformat = "CRYPT_RSA_PRIVATE_FORMAT_PKCS8";
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kp = $d['publickey'];
$Ks = $d['privatekey'];
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kver = $d['publickey'];
$KSign = $d['privatekey'];
file_put_contents("pub_verify_key.txt",extract_key($Kver));
$plainText = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:".extract_key($Kp);
file_put_contents("plain.txt",$plainText);
// Signing
$hash = new Crypt_Hash('sha256');
$rsa = new Crypt_RSA();
$rsa->loadKey($KSign);
$rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setHash('sha256');
$signature = $rsa->sign($plainText);
$signedHS = base64_encode($signature);
file_put_contents("signedkey.txt", $signedHS);
// Verification
$signature = base64_decode($signedHS);
$rsa->loadKey($Kver);
$status = $rsa->verify($plainText, $signature);
var_dump($status);
Java code:
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.File;
import java.io.FileReader;
import java.math.BigInteger;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class VerifySig {
public static RSAPublicKey fromPKCS1Encoding(byte[] pkcs1EncodedPublicKey) {
// --- parse public key ---
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
try {
pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
.getInstance(pkcs1EncodedPublicKey);
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not parse BER PKCS#1 public key structure", e);
}
// --- convert to JCE RSAPublicKey
RSAPublicKeySpec spec = new RSAPublicKeySpec(
pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
KeyFactory rsaKeyFact;
try {
rsaKeyFact = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("RSA KeyFactory should be available", e);
}
try {
return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new IllegalArgumentException(
"Invalid RSA public key, modulus and/or exponent invalid", e);
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String pkey = fromFile("pub_verify_key.txt");
byte[] dpkey = Base64.decodeBase64(pkey);
RSAPublicKey publicKey = fromPKCS1Encoding(dpkey);
String plainData = fromFile("plain.txt");
String data = fromFile("signedkey.txt");
byte[] ciphertext = Base64.decodeBase64(data);
System.out.println(new String(plainData.getBytes(), UTF_8));
verifyBC(publicKey, plainData, ciphertext);
System.out.flush();
}
private static void verifyBC(PublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
// what should work (for PKCS#1 v1.5 signatures), requires Bouncy Castle provider
//Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
Signature sig = Signature.getInstance( "SHA256withRSA");
sig.initVerify(publicKey);
sig.update(plainData.getBytes(UTF_8));
System.out.println(sig.verify(ciphertext));
}
private static String fromFile(String filename) {
StringBuilder builder = new StringBuilder(8000);
try {
FileReader reader = new FileReader(new File(filename));
int c;
while((c = reader.read()) != -1) {
builder.append((char)c);
}
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
return builder.toString();
}
}

Easy way to generate a self-signed certificate for java.security.Keystore using BouncyCastle

I have an application that uses BouncyCastle to generate an RSA key pair. I want to store that key pair in a java.security.Keystore. For that I need a certificate (the only reason I need one!).
I'm using bouncycastle version 1.51.
All the examples I have found on this are either using a *CertificateGenerator (deprecated in 1.51) or very complex and without any meaningful explanations.
Whats the easiest way to generate a basically meaningless self-signed certificate for this purpose?
Or is there an alternative to using a keystore?
To be clear: input is a KeyPair holding an RSAPrivateKey and an RSAPublicKey, output should be a java.security.cert.Certificate.
The solution I ended up using looks mostly like the following code (not pretty but it works):
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.sql.Date;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v1CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
public class SelfSignedCertificateGenerator {
private static final JcaX509CertificateConverter CONVERTER = new JcaX509CertificateConverter()
.setProvider(new BouncyCastleProvider());
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
private static final X500Name ISSUER = new X500Name(new X500Principal("CN=Stupid CA Certificate").getName());
private static final X500Name SUBJECT = ISSUER;
private static final Date NOT_AFTER = Date.valueOf("3000-01-01");
private static final Date NOT_BEFORE = Date.valueOf("2000-01-01");
private static final BigInteger SERIAL = new BigInteger("1");
public static Certificate[] getCerts(KeyPair keys) {
return new Certificate[] { getCertificate(keys) };
}
private static X509Certificate getCertificate(KeyPair keys) {
try {
X509v1CertificateBuilder certificateBuilder = getCertificateBuilder(keys.getPublic());
X509CertificateHolder certificateHolder = certificateBuilder.build(getSigner(keys));
return CONVERTER.getCertificate(certificateHolder);
} catch (CertificateException e) {
throw new RuntimeException(e);
}
}
private static X509v1CertificateBuilder getCertificateBuilder(PublicKey publicKey) {
return new X509v1CertificateBuilder(ISSUER, SERIAL, NOT_BEFORE, NOT_AFTER, SUBJECT, getPublicKeyInfo(publicKey));
}
private static SubjectPublicKeyInfo getPublicKeyInfo(PublicKey publicKey) {
if (!(publicKey instanceof RSAPublicKey))
throw new RuntimeException("publicKey is not an RSAPublicKey");
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
try {
return SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(new RSAKeyParameters(false, rsaPublicKey
.getModulus(), rsaPublicKey.getPublicExponent()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static ContentSigner getSigner(KeyPair keys) {
try {
return new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).setProvider(new BouncyCastleProvider()).build(
keys.getPrivate());
} catch (OperatorCreationException e) {
throw new RuntimeException(e);
}
}
}

How to verify signature & decrypt a ; Encrypted & Signed content

I have a bouncy castle code that encrypts and sign a message(listed below) ; now I need to find the code for opposite end ; Verify the signature & decrypt the message. My search to find a solution for the past 2 days has been futile, as I could only find example for either verifying the signature (or) for decryption ; but not both. Please find below the encryption & signing code ; would greatly appreciate any hint or direction to Verify the signature & decrypt the message
Note : The method encryptAndSign() is called to encrypt and sign the Content/data
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.naming.NamingException;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public final class EncryptorNsigner {
public byte[] encryptAndSign(final String content, final String selfSignedCert,
final String encryptionCert) throws CertificateException, IOException,
KeyStoreException, Exception {
byte[] signedBytes;
//Get key store
KeyStore keyStore = this.getKeyStore();
//Setup provider
CMSSignedDataGenerator signatureGenerator = this.setUpProvider(
keyStore, selfSignedCert);
//Encrypt data
byte[] encryptBytes = this.encryptData(content.getBytes("UTF-8"),
getEncyptionCertFromKeystore(keyStore, encryptionCert), null);
//Sign the encrypted data
signedBytes = this.signPkcs7(encryptBytes, signatureGenerator);
return Base64.encode(signedBytes);
}
private KeyStore getKeyStore() throws NamingException, KeyStoreException,
FileNotFoundException, CertificateException, IOException,
NoSuchAlgorithmException{
...
return keystore;
}
private X509Certificate getEncyptionCertFromKeystore(final KeyStore keystore, final String encryptionCertName)
throws CertificateException, IOException, KeyStoreException{
Certificate c = keystore.getCertificate(encryptionCertName);
X509CertificateHolder certHolder = new X509CertificateHolder(c.getEncoded());
return new JcaX509CertificateConverter().setProvider( "BC" )
.getCertificate(certHolder );
}
private CMSSignedDataGenerator setUpProvider(final KeyStore keystore, String signCertName) throws Exception {
Security.addProvider(new BouncyCastleProvider());
//Get certificate chain
Certificate[] certchain = (Certificate[]) keystore.getCertificateChain(signCertName);
final List<Certificate> certlist = new ArrayList<Certificate>();
//Add the certificates in Certificate chain to the Certificate list
for (int i = 0, length = certchain == null ? 0 : certchain.length; i < length; i++) {
certlist.add(certchain[i]);
}
//Class for storing certificates for later lookup
Store certstore = new JcaCertStore(certlist);
//Get certificate
Certificate cert = keystore.getCertificate(signCertName);
String algorithm = "SHA1withRSA";
String keyStorePwd = "....";
ContentSigner signer = new JcaContentSignerBuilder(algorithm).setProvider("BC").
build((PrivateKey) (keystore.getKey(signCertName, keyStorePwd.toCharArray())));
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").
build()).build(signer, (X509Certificate) cert));
generator.addCertificates(certstore);
return generator;
}
private byte[] signPkcs7(final byte[] content, final CMSSignedDataGenerator generator)
throws CMSException, IOException {
CMSTypedData cmsdata = new CMSProcessableByteArray(content);
CMSSignedData signeddata = generator.generate(cmsdata, true);
return signeddata.getEncoded();
}
private byte[] encryptData(byte[] dataToEncrypt, X509Certificate recipientCert, String algorithmName) throws IOException{
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
CMSEnvelopedData ed;
AlgorithmIdentifier digestAlgorithm;
if (algorithmName!=null && !algorithmName.isEmpty()){
digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(new DefaultSignatureAlgorithmIdentifierFinder().find(algorithmName));
try {
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
ed = edGen.generate(new CMSProcessableByteArray(dataToEncrypt),
new JceCMSContentEncryptorBuilder(digestAlgorithm.getAlgorithm())
.setProvider("BC").build());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
else{
try {
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
ed = edGen.generate(new CMSProcessableByteArray(dataToEncrypt),
new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC) //Default algorithmName = DES_EDE3_CBC
.setProvider("BC").build());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
return ed.getEncoded();
}
}
2 years waiting for an answer. I hope you find the solution.
To help others, here is a sample for reading SMIME emails and decrypt attachments. It's based on the fact that the sender used your public key to encrypt and you got your key and password to decrypt the message.
Security.addProvider(new BouncyCastleProvider());
// IMAP encrypt
final KeyStore ks = KeyStore.getInstance("PKCS12");
final String password = CERTIFICAT_SMIME_PWD;
ks.load(this.class.getClassLoader()
.getResourceAsStream(CERTIFICAT_SMIME_PFX), password.toCharArray());
final String alias = ks.aliases().nextElement();
final PrivateKey pKey = (PrivateKey) ks.getKey(alias, password.toCharArray());
final SMIMEEnveloped m = new SMIMEEnveloped((MimeMessage) message);
final RecipientInformationStore recipients = m.getRecipientInfos();
final X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
final RecipientId recId = new JceKeyTransRecipientId(cert);
final RecipientInformation recipient = recipients.get(recId);
final byte[] content = recipient.getContent(new JceKeyTransEnvelopedRecipient(pKey)
.setProvider(BouncyCastleProvider.PROVIDER_NAME));
final String providerName = BouncyCastleProvider.PROVIDER_NAME;
final MimeBodyPart res = SMIMEUtil.toMimeBodyPart(content);
final MimeMultipart parts = (MimeMultipart) res.getContent();
for (int i = 0; i < parts.getCount(); i++) {
final BodyPart part = parts.getBodyPart(i);
if (part.getContent() instanceof Multipart) {
multipart = (Multipart) part.getContent();
}
}

How to get private key from browser keystore?

I read certificates from keystore browser, there is some problem with getting private key, but public key get perfect. Below is code:
KeyStore keystore1 = KeyStore.getInstance("Windows-MY");
keystore1.load(null, null);
if (keystore1 != null) {
Enumeration<String> enumeration = keystore1.aliases();
while (enumeration.hasMoreElements()) {
String alias = enumeration.nextElement();
if (alias.equals("myalias")) {
char[] keypwd = "123456".toCharArray();
KeyStore.PrivateKeyEntry keyEnt = (KeyStore.PrivateKeyEntry) keystore1.getEntry(alias, new KeyStore.PasswordProtection(keypwd));
System.out.println("getPublicKey: " + keyEnt.getCertificate().getPublicKey().getEncoded());
//show RSAPrivateKey [size=2048 bits, type=Exchange, container={5089EC94-FF45-4339-ACCF-E6ECCCB16899}]
System.out.println("privateKey111: " + keyEnt.getPrivateKey());
}
}
}
Public key output is correct, but private key looks like this:
RSAPrivateKey [size=2048 bits, type=Exchange, container={5089EC94-FF45-4339-ACCF-E6ECCCB16899}]
Password is correct. How can I get private key?
Here is a private key exporter I have used, it reads JKS keystore maybe you could somehow convert keystore first or modify your code accordingly.
c:\test>java -classes ./classes ExportPrivateKey mystore.ks JKS mystorepwd myalias mycert_priv.crt
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import sun.misc.BASE64Encoder;
public class ExportPrivateKey {
private File keystoreFile;
private String keyStoreType;
private char[] password;
private String alias;
private File exportedFile;
public static KeyPair getPrivateKey(KeyStore keystore, String alias, char[] password) {
try {
Key key=keystore.getKey(alias,password);
if(key instanceof PrivateKey) {
Certificate cert=keystore.getCertificate(alias);
PublicKey publicKey=cert.getPublicKey();
return new KeyPair(publicKey,(PrivateKey)key);
}
} catch (UnrecoverableKeyException e) {
} catch (NoSuchAlgorithmException e) {
} catch (KeyStoreException e) { }
return null;
}
public void export() throws Exception{
KeyStore keystore=KeyStore.getInstance(keyStoreType);
BASE64Encoder encoder=new BASE64Encoder();
keystore.load(new FileInputStream(keystoreFile),password);
KeyPair keyPair=getPrivateKey(keystore,alias,password);
PrivateKey privateKey=keyPair.getPrivate();
String encoded=encoder.encode(privateKey.getEncoded());
FileWriter fw=new FileWriter(exportedFile);
fw.write(“—–BEGIN PRIVATE KEY—–\n“);
fw.write(encoded);
fw.write(“\n“);
fw.write(“—–END PRIVATE KEY—–”);
fw.close();
}
public static void main(String args[]) throws Exception{
ExportPrivateKey export=new ExportPrivateKey();
export.keystoreFile=new File(args[0]);
export.keyStoreType=args[1];
export.password=args[2].toCharArray();
export.alias=args[3];
export.exportedFile=new File(args[4]);
export.export();
}
}

Categories