while trying to convert byte[] array to PublicKey I'm getting an error:
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: short read of DER octet string
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(Unknown Source)
This is part of code that gives me this error:
byte[] publicKeyBytes = keystring.getBytes();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey2 = keyFactory.generatePublic(publicKeySpec);
Checked before, byte[] array that i'm getting from string is the same as the original one.
Thanks
According to your comments you convert the bytes of the encoded public key directly to String using the default encoding, which will for sure drop/replace some of the bytes. See the JavaDoc for new String(bytes[]).
So the content of pair.getPublic().getEncoded() and keystring.getBytes() won't be the same.
Use a suitable transport encoding, like Base64. On Java 8, you can use java.util.Base64 for that, on older platforms the Apache Commons Codec's Base64 class can be used.
Related
In an Android app, I am trying to consume a WebAPI with an encrypted value in the JSON. I have a C# script which handles the encryption and I have adapted it to Android. The encryption method in short is adding a byte array a few variables, randomly generated IV, randomly generated salt and hmacsalt, adding the ciphertext which is generated from a Json String using AES/CBC/Pkcs7Padding and generating hmac and also adding that to the byte array. Converting this byte array to base64 gives me the encrypted string. I think everything works fine in this process since the WebAPI is able to decrypt my string. What the problem is the string is decrypted mostly correct but there is a date field and the date appears to be completely wrong.
I've been trying to adapt the C# to Android and what I am not sure about is how to specify the key length.
The key generators in both C# and Android are as follows.
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, pbkdf2_iterations).GetBytes(pbkdf2_keyLength);
Here the pbkdf2_iterations = 100 and pbkdf2_keyLength =32;
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 100, 32);
byte[] key = factory.generateSecret(pbeKeySpec).getEncoded();
Here when initiating the PBEKeySpec, when I pass 32 for the key length, the byte[]key becomes 4 bytes and there is an exception saying unsupported key size 4 bytes but the WebAPI is able to decrypt my string with the date error. When I pass 256 for the key length, to make the byte[] 32 bytes, the WebAPI is not able to decrypt the string.
I am quite confused about what is the right way to do this.
To give an example about the faulty decrypted string, my plaintext to be encrypted is;
{"Username":"username","Password":"password","DateCreated":"2019-10-25T14:46:01.441Z"}
but the decrypted string is;
{"Username":"username","Password":"password","DateCreated":"2019-03-04T09:29:54.3516562Z"}
In this case, I suppose the encryption is correct but have no clue how the date value and format changes.
Any opinions about what may be wrong would be greatly appreciated.
Edit:
JSonObject Creation code
The Json data is created statically.
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
String dateString = formatter.format(new java.util.Date());
JsonObject o = new JsonObject();
o.addProperty("Username", "username");
o.addProperty("Password","password");
o.addProperty("DateCreated", dateString);
String jsonString = o.toString();
And jsonString is passed for the encryption. I can see that its value is just the way I want.
I am trying to get the same amount of bytes of the below snippet in Java. I am working with decryption of encrypted strings with known key. However, I am unable to reproduce these two lines of code in Java.
let usrKey: NSData! = (usrKey as NSString).data(using: String.Encoding.utf8.rawValue) as NSData!
let keyBytes = UnsafeMutableRawPointer(mutating: usrData!.bytes)
How can I get the same keyBytes in Java?
What will the equivalent expression of the above in Java language?
What is the behavior of mutating ?
Java code I tried working with and the error below that.
byte [] keyBytes = userKey.getBytes("UTF-8");
SecretKeySpec secret = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, newIvParameterSpec(ivBytes));
byte[] outputBytes = cipher.doFinal(inputBytes);
input 16 bytes
iv 16 bytes
key 32 bytes
ALGORITHM - AES128/CBC/PKCS5Padding
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:991)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
The issue is to find the right way to get the user key (could be short so padded) and create similar 32 bytes (required by the decryption algo) between Swift and Java code.
The Java line
byte [] keyBytes = userKey.getBytes("UTF-8");
should have the same effect as the two Swift lines. The problem is likely outside of the code that you showed in the question. You may want to debug both Swift and Java code to make sure the usrKey matches userKey and that the resulting keyBytes are the same in both Swift and Java.
Please see Apple documentation at https://developer.apple.com/documentation/swift/unsafemutablerawpointer/2427850-init for the meaning of mutating. Also consult Apple documentation for any other API-related questions.
I need to convert an RSA private key located in memory in PEM format into a PrivateKey on Android.
The problem seems to have been solved for public keys but I'm struggling to get it to work for a private key. I'm trying the following code:
String pemkey = "MIICWwIBAAKBgQDIuL0SzG1+wgyaoyDyHvYIaG10ePXBHqaKTnYyZfY5RzaEFLE/vBdFN2Di7AMH3/5iN/YFQqLsVjKqzX3E3LM2dJOZ9qSWeYArSyQPbhy0eM/3amwchvtLvhVLm2UqVFLjiPGlyYX3D75ETD5tmgulAc5ZDRtGqYVoLKPmZ0USPwIDAQABAoGAErfDjf65UUJISZ1fw6Rmfic62csz47P3hNtHQ3Dlsra020FQvChOpTpCUzb+G1xkjQU58Iijx9VL+Uiba2HHZmiJX2LgS3KKqKFZKmbKZnZQTiw+2o+4AXhtcAYfSAJE9TgRPEhwhZmzV2cvfUk5AjnOghSn2gGjdD1g4xtH22ECQQD/ZbfEd2HEGqHf6j/AVMW+N/Q1xtYIB8r0CWxF6cNw5iq/8Ce9ujpnAFi0vgtojyKDlgwBp4XMU2C4is49EkFhAkEAyTH96mS8dExAAmi3Mm2seUIEOtKwuLD6BEECecPyZSIOd24tfNbmA7Ri6MpGjyLZoNoJQ0AJGcnWU1tnc8bXnwJAS+jYyP1OwrHDwUDnt+u6ZoJNBJrXzMU8LnKKivEjFPBkbm4b8cljSHAS7Y266FX6xS+Y2/kFzKhPjCo9iGtfoQJAOv39hYyj9TWmTw6FKLQfri49L0I3ru+1Xynwn+NkX2Ls+vfDPqeEKfHqTneA2NdPGGrV7HIKORWFUkuqubfD4QJAK60RuhDSeH+ZljcYLhbHLoTnja/uTcvDAd0M4ll2HUNId4jPbYl1qw7OQwfg8apKmGwp7HGW50o/EItvvJrR7w==";
byte[] encoded = Base64.decode(pemkey, Base64.DEFAULT);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey sessionkey = kf.generatePrivate(keySpec);
(The key is not the same as for my Bitcoin wallet so don't bother ^^)
The last line gives the error
java.security.spec.InvalidKeySpecException:
Must use RSAPublicKeySpec or PKCS8EncodedKeySpec;
was java.security.spec.X509EncodedKeySpec
I tested the pemkey string to be ok in other languages (e.g. Python RSA.importkey) and it works fine.
Edit:
On a suggestion by a comment (and the answer to the question linked as doublicate), I also tried with X509EncodedKeySpec replaced by PKCS8EncodedKeySpec. Then the new error I get is
java.security.spec.InvalidKeySpecException:
java.lang.RuntimeException:
error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
Thanks to comments by Greggz and James I was able to get it to work. There were two problems:
X509EncodedKeySpec had to be replaced by PKCS8EncodedKeySpec
The provided pemkey was PKCS#1 (to be recognized by 'BEGIN RSA PRIVATE KEY') but needs to be PKCS#8 (to be recognized by 'BEGIN PRIVATE KEY').
I am new to cryptography and am learning how to use Bouncy Castle in Java for crypto purposes.
I know that Python has Crypto-Charm which I have used
import charm.toolbox.ecgroup
serializedKey = charm.toolbox.ecgroup(prime192v1).deserialize(keyInBytes)
How can I do the same for Java?
Try this:
Deserialize:
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
Serialize:
byte[] keyBytes = privateKey.getEncoded();
Maybe you can need this call (depending on your JDK) previous to KeyFactory.getInstance:
Security.addProvider(new BouncyCastleProvider());
This reference could be useful https://www.bouncycastle.org/fips-java/BCFipsIn100.pdf to understand different examples of code enconding.
In chapter
Password Based Encryption and Key Storage
on this section
Encoding Public and Private Keys
there are some examples to get ideas.
Is it possible decrypt an encrypted RSA (or others, shouldn't matter) private keys using JCE and/or BouncyCastle provider (not using openssl bundle)?
I can read unencrypted keys just fine using PrivateKeyFactory.
Googling this gets me through examples of using PEMReader (from BC openssl bundle) that has a password applied to it, but - don't want to use openssl bundle, don't necessarily want to use PEM format, and I can decode PEM using PemReader (from provider bundle). It's what can I do with it afterwards is the question.
I'm looking for some mega-function, or a series thereof that can do it, i.e. I am not looking into parsing the ASN1 of the encrypted key, figuring out the encryption method, passing the input through the cipher, etc.
If you have an encrypted PKCS#8 key in binary format (i.e. not in PEM format) the following code shows how to retrieve the private key:
public PrivateKey decryptKey(byte[] pkcs8Data, char[] password) throws Exception {
PBEKeySpec pbeSpec = new PBEKeySpec(password);
EncryptedPrivateKeyInfo pkinfo = new EncryptedPrivateKeyInfo(pkcs8Data);
SecretKeyFactory skf = SecretKeyFactory.getInstance(pkinfo.getAlgName());
Key secret = skf.generateSecret(pbeSpec);
PKCS8EncodedKeySpec keySpec = pkinfo.getKeySpec(secret);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
If you have a PEM format, remove the header (first line), the footer(last line) et convert the remaining content from base64 to regular byte array.