How to convert from String to PublicKey? - java

I've used the following code to convert the public and private key to a string
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair keyPair = keyPairGen.genKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String publicK = Base64.encodeBase64String(publicKey.getEncoded());
String privateK = Base64.encodeBase64String(privateKey.getEncoded());
Now I'm trying to convert it back to public ad private key
PublicKey publicDecoded = Base64.decodeBase64(publicK);
I'm getting error of cannot convert from byte[] to public key. So I tried like this
PublicKey publicDecoded = new SecretKeySpec(Base64.decodeBase64(publicK),"RSA");
This leads to error like below
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: Neither a public nor a private key
Looks like I'm doing wrong key conversion here. Any help would be appreciated.

I don't think you can use the SecretKeySpec with RSA.
This should do:
byte[] publicBytes = Base64.decodeBase64(publicK);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
And to decode the private use PKCS8EncodedKeySpec

Related

JWT signature exception occured after Base64 encoding/decoding Secret and Public keys

JWT signature exception occured after Base64 encoding/decoding Secret and Public keys.
I generated a key pair like this:
KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
Them I incoded keys this way:
PrivateKey aPrivate = keyPair.getPrivate();
String encodePrivate = Encoders.BASE64URL.encode(aPrivate.getEncoded());
PublicKey aPublic = keyPair.getPublic();
String encodePublic = Encoders.BASE64URL.encode(aPublic.getEncoded());
System.out.println("encodePrivate: " + encodePrivate);
System.out.println("encodePublic: " + encodePublic);
After that I decode keys and try to generate and verify JWTs:
SecretKey privateKey = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(encodePrivate));
SecretKey publicKey = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(encodePublic));
String jwtStr = Jwts.builder()
.setId(id)
.setIssuedAt(now)
.setSubject(subject)
.setIssuer(issuer)
.claim("dhdfh", "dfhd")
.claim("cvbcv", "wrwerew")
.signWith(privateKey)
.compact();
Jwts.parserBuilder()
.setSigningKey(publicKey)
.build()
.parseClaimsJws(jwt).getBody();
And suddenly I get an error
If I use the keys directly like this
PrivateKey aPrivate = keyPair.getPrivate(); PublicKey aPublic = keyPair.getPublic(); - no error occurs
encodePrivate: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDTnkqggdMFu5O58pB8U0f8D4pab_7j5T8jNZfEep23DbvoMCjR6X1cFe4qCsvaY4aDF6d6Vn3TVY2INHMuyOTXqe5vhBWiRgaI3TIPkGjgHZ1f6Up_ZPQFJCGTo2b3OiXBq3LTK7PXvMj2EIQPJrHuX099ACDvO-9F8T_nyLA68JLfS-V6OmH-nvaCjqndbVSUxOE69OncpU3kPUpdzPPdi7jhEphTOcKdSE8YvPyAjj8RREgZsWgYn10qql_GEZ8Lh15KuBRwJAJQYeOYU_thC6pObazr7NWbpk1e0_0tDuMrfunbKCca1Wz_xv9M7bn1BD92122wuPkenQg-s0ohAgMBAAECggEAA3vssJ3SkpqXAQ28UT-xxLWgyoJjiO8CThsYx5RZOmVQfa8lTOdyN-zogeqxloPi-A-Qo1P_OFaFQQPUDaYjFmXm1hEvpf9PJju2EkmHYIE8URLvNg-8cMU6hErBbDqZ2olvF4j1qgyipmJ5OiKh9VG-Zkl1QVsUQmuJaNCGDkJWDgCAAnJz-dwjhTV0J_RCeOcDhc5YTik14lVUxFsWm5F0bFbvh_x-ThhgFYwoUZ-ZWIQjPHD2_VraxX76BqOOa1B56p1xeKsz6sEv_jR7G3fSay8mgMxkAakCYoXANedcdtXpBZx8Ad1QBFUOFBGX67hfD_2PydAu5mA9S18_zQKBgQDixlZw1EbzwAVZ-VxURBZ8-Iw0GGYtOYqCJqB771cLuRrCiYfMrUqWE9A3AqtxhasaBYZ5K8m0abET4oZ7QOOajVhBG1N6v1ilQiUsxcYZVqdEhfH_T1olx_wNWT5GqbUb-sUXr4AmtirWypqdGTIixVLUyQkRhtSbJiDLf6bR0wKBgQDu4-tAOiBotp_tW6ZELrZbAxNTfqUDODd-RBYFT-OHL5PXT2PqgpPC0W8JKEerTIXo_hplr-70YOkJYGZwHk84Ptx-cFDjNq86bR10xaFDkLI-omMT4aPQyx67gaNMx07G6AlV9idC-sYi_Db2dUsiJPJ6A8g-aug7MtFA3y_HuwKBgDf0rqGaj4NXrzpbQD_-qPnfLmEwYA3qs9WXiGPsU7Mt0n-MBfkoDU5oxyi7vOf_DpAWKu89McEVjz8T1xEUmtSo6czu3DNegZYNczTP_CiGbDGJR4Qy5VCLPxNgIPC4sVqdDwLgKCPlMT7csTfwXqGbxOuCS32Kom1CBDeSYOxHAoGAXq2qMRxYd-fZXoMyVFeHIm8Hm9HXqH0BUWO-roBJFuz-VRk76leyJEZJEYILVZLQh9UdtSuTMvutoG-6abk5gHs5fEsbY9HqhOd2Ay_IiDSy0CwfxGNrP3chSQNKK0XarO6NtKoISX2GRZtcVTWLf47RIxaYqFRKkhvD30gVcb0CgYB_Bc5sImIZpyVNJ48b9v6u6s0rPQW0q7pI4GstuhVeTlsukf_p76xf6V50F3mbWE7nb-SbVenJxn0naTWPW9oWmBrJX3eYft_HE4OjURQc6aaOuP-1OSPnioVnfYxQ52e5uHa6cTQvaAOcy3vrFjw7VYTp0W95L5ZYTLaa83nxIw
encodePublic: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA055KoIHTBbuTufKQfFNH_A-KWm_-4-U_IzWXxHqdtw276DAo0el9XBXuKgrL2mOGgxenelZ901WNiDRzLsjk16nub4QVokYGiN0yD5Bo4B2dX-lKf2T0BSQhk6Nm9zolwaty0yuz17zI9hCEDyax7l9PfQAg7zvvRfE_58iwOvCS30vlejph_p72go6p3W1UlMThOvTp3KVN5D1KXczz3Yu44RKYUznCnUhPGLz8gI4_EURIGbFoGJ9dKqpfxhGfC4deSrgUcCQCUGHjmFP7YQuqTm2s6-zVm6ZNXtP9LQ7jK37p2ygnGtVs_8b_TO259QQ_dtdtsLj5Hp0IPrNKIQIDAQAB
JWT!!! eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIxMjM0NTYiLCJpYXQiOjE2NzA1ODIyMjEsInN1YiI6InN1YmplY3RfMiIsImlzcyI6Imlzc3Vlcl8xIiwiZGhkZmgiOiJkZmhkIiwiY3ZiY3YiOiJ3cndlcmV3IiwiZXhwIjoxNjcwNTgyMzIxfQ.V0y0-l7ySBEdmc4X7eRmLlSoxXv5QdpEPedICZOR6zlMRgjFR63mPMn64yqfz8FAvUOaFjyZr-FRgCEFhTk8CA
Exception in thread "main" io.jsonwebtoken.security.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:399)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:529)
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:589)
at io.jsonwebtoken.impl.ImmutableJwtParser.parseClaimsJws(ImmutableJwtParser.java:173)
at Runner.decodeJWT(Runner.java:64)
at Runner.main(Runner.java:83)
I think i'm doing smthng wrong with publicKey. I need string representation of keys to keep them in configuration file. Help me to solve this problem please. Thanks in advance!
I made a mistake using this:
SecretKey privateKey = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(encodePrivate));
SecretKey publicKey = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(encodePublic));
here is what to use instead:
PKCS8EncodedKeySpec keySpecPriv = new PKCS8EncodedKeySpec(prvKeyBytes);
X509EncodedKeySpec keySpecPubl = new X509EncodedKeySpec(pblcKeyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey prvKey = kf.generatePrivate(keySpecPriv);
PublicKey pblKey = kf.generatePublic(keySpecPubl);

Generating keyPair using Bouncy Castle

I have Java code for generating keypair using BC as follows:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
PrivateKey priv = key.getPrivate();
PublicKey pub = key.getPublic();
String privateKey = new String(Base64.encode(priv.getEncoded(), 0,priv.getEncoded().length, Base64.NO_WRAP));
String publicKey1 = new String(Base64.encode(pub.getEncoded(), 0,pub.getEncoded().length, Base64.NO_WRAP));
String publicKey = new String(Base64.encode(publicKey1.getBytes(),0, publicKey1.getBytes().length, Base64.NO_WRAP));
Now I want to do same in C# using BC. I have downloaded WP8BouncyCastle library via nuget package manager. I have written as:
var kpgen = new RsaKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));
var keyPair = kpgen.GenerateKeyPair();
AsymmetricKeyParameter privateKey = keyPair.Private;
AsymmetricKeyParameter publicKey = keyPair.Public;
string prvKey = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(privateKey.ToString()));
string pubKey = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(publicKey.ToString()));
string pubKey1 = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(pubKey.ToString()));
But I need getEncoded() method available in Java which is not available in BC library in C#. This getEncoded() method is used to convert given key into X.509 encoded key.In case of Java, public key getting twice converted (getencoded() and getBytes()) ,I am not able to do same in C#.
Is there any solution to it?
Use the following code for private key:
PrivateKeyInfo pkInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
String privateKey = Convert.ToBase64String(pkInfo.GetDerEncoded());
and following for public:
SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
String publicKey = Convert.ToBase64String(info.GetDerEncoded());

Unable to load RSA public key

I'm trying to read RSA public key shown below, but I get an exception at line 6: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
My code:
String rsaPublicKey = rsaPublicKeyString.replace(
"-----BEGIN RSA PUBLIC KEY-----\n", "");
rsaPublicKey = rsaPublicKey.replace("\n-----END RSA PUBLIC KEY-----", "");
byte[] bytes = EncryptionUtils.decodeBase64(rsaPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
pubKey = (RSAPublicKey)keyFactory.generatePublic(keySpec);
RSA public key:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
-----END RSA PUBLIC KEY-----
What am I doing wrong?
UPD:
public static byte[] decodeBase64(String data) throws EncryptionException {
try {
BASE64Decoder decoder = new BASE64Decoder();
return decoder.decodeBuffer(data);
} catch (Exception e) {
throw new EncryptionException(e);
}
}
For me, I was missing the OID in the public key. I had to correct that on the iOS side using help from here: http://blog.wingsofhermes.org/?p=42
Also, my public key didn't have to be casted to an RSAPublicKey, the standard worked just fine:
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Your problem is caused because your public key is an RSAPublicKey object rather than a SubjectPublicKeyInfo object (see this answer for a good description of the difference). You will need to convert from one to the other before your code will work.
BouncyCastle can do the conversion for you. The following code snippet will work, although I dislike it for two reasons:
It uses a deprecated class PEMReader.
It requires the BouncyCastle provider to be loaded.
Code:
Security.addProvider(new BouncyCastleProvider());
PEMReader reader = new PEMReader(new StringReader(rsaPublicKeyString));
BCRSAPublicKey key = (BCRSAPublicKey) reader.readObject();
bytes[] = key.getEncoded(); // now in SubjectPublicKeyInfo format.
// as before...
With BouncyCastle, there is always many ways to skin a cat. Perhaps someone can find a more elegant solution than the one above?

Using RSA encryption in Java without BouncyCastle

In my program, I'm trying to encrypt some plaintext with RSA using the following code:
static String RSAEncrypt(String pubkey, String plain){
return encrypt(pubkey,plain,"RSA");
}
static String encrypt(String stringKey, String plain, String algo){
String enc="failed";
try{
byte[] byteKey = new BASE64Decoder().decodeBuffer(stringKey);
Key key = new SecretKeySpec(byteKey,algo);
byte[] data = plain.getBytes();
Cipher c = Cipher.getInstance(algo);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(data);
enc = new BASE64Encoder().encode(encVal);
}catch(Exception e){e.printStackTrace();}
return enc;
}
However, when it runs, it shows the following error:
java.security.InvalidKeyException: No installed provider supports this key: javax.crypto.spec.SecretKeySpec
at javax.crypto.Cipher.chooseProvider(Cipher.java:877)
at javax.crypto.Cipher.init(Cipher.java:1212)
at javax.crypto.Cipher.init(Cipher.java:1152)
at Crypto.encrypt(Crypto.java:37)
at Crypto.RSAEncrypt(Crypto.java:62)
I have tried changing it to RSA/None/PKCS1Padding and RSA/ECB/PKCS1Padding to no avail.. I know that installing BouncyCastle may help but I'd like to avoid it (I'd like to avoid more dependencies and I've been having some issues installing it anyway). Thanks in advance for any ideas.
As was said in the comments, SecretKeySpec is for symmetric algorithms only. You mentioned that you got your byte[] containing the key by calling getEncoded.
There are two possibilities and two resulting formats:
Encoding of an RSA PrivateKey
Calling PrivateKey#getEncoded on an instance of an RSA private key will result in a PKCS#8 encoding for private keys, and it can be restored with the help of PKCS8EncodedKeySpec.
Encoding of an RSA PublicKey
PublicKey#getEncoded on an RSA public key results in the generic X.509 public key encoding, and can be restored with X509EncodedKeySpec.
Example Usage
byte[] data = "test".getBytes("UTF8");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair keyPair = kpg.genKeyPair();
byte[] pk = keyPair.getPublic().getEncoded();
X509EncodedKeySpec spec = new X509EncodedKeySpec(pk);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = cipher.doFinal(data);
byte[] priv = keyPair.getPrivate().getEncoded();
PKCS8EncodedKeySpec spec2 = new PKCS8EncodedKeySpec(priv);
PrivateKey privKey = keyFactory.generatePrivate(spec2);
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] plain = cipher.doFinal(encrypted);
System.out.println(new String(plain, "UTF8")); //=> "test"

RSA encryption :InvalidKeyException: invalid key format

I have to read pem key files to get RSA Public key,and then use them to encrypt.
I can do this using openssl and convert pem file to der file.
and then load my key using X509EncodedKeySpec and PKCS8EncodedKeySpec.
But I don't want to do this because pem is the user key exchange format.
user can register it's own key can like this :
--BEGIN PUBLIC KEY-- MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGi0/vKrSIIQMOm4atiw+2s8tSojOKHsWJU3oPTm
b1a5UQIH7CM3NgtLvUF5DqhsP2jTqgYSsZSl+W2RtqCFTavZTWvmc0UsuK8tTzvnCXETsnpjeL13
Hul9JIpxZVej7b6KxgyxFAhuz2AGscvCXnepElkVh7oGOqkUKL7gZSD7AgMBAAE=
--END PUBLIC KEY--
and this key is store in a database in this format...
Here is the code I have tried..
File pubKeyFile=new File("D:/public_key.pem");
DataInputStream dis = new DataInputStream(new FileInputStream(pubKeyFile));
byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
dis.readFully(pubKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubSpec);
I am getting exception as
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
As I am completely new to encryption concepts can anyone please help me to solve this exception?
Many thanks.
With bouncycastle, it would be done this way:
CertificateFactory cf = CertificateFactory.getInstance("X509", "BC");
InputStream is = new FileInputStream("D:/public_key.pem");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(is);
is.close();
RSAPublicKey pubKey = (RSAPublicKey)certificate.getPublicKey();
You were almost there, with the standard provider. You just need to strip the header and footer lines:
List<String> lines = Files.readAllLines(Paths.get(path), StandardCharsets.US_ASCII);
if (lines.size() < 2)
throw new IllegalArgumentException("Insufficient input");
if (!lines.remove(0).startsWith("--"))
throw new IllegalArgumentException("Expected header");
if (!lines.remove(lines.size() - 1).startsWith("--"))
throw new IllegalArgumentException("Expected footer");
byte[] raw = Base64.getDecoder().decode(String.join("", lines));
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey pub = factory.generatePublic(new X509EncodedKeySpec(raw));
try using bouncycastele's PemReader .
PublicKey getPublicKey(String pubKeyStr) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PemObject pem = new PemReader(new StringReader(pubKeyStr)).readPemObject();
byte[] pubKeyBytes = pem.getContent();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubSpec);
return pubKey;
}

Categories

Resources