(Android / Java) Create a RSA private key instance - java

I am trying to create a PrivateKey instance in an Android app from a pem file to decrypt some data but I am getting the following error:
java.lang.RuntimeException: error:0c0890ba:ASN.1 encoding routines:asn1_check_tlen:WRONG_TAG
The code:
// Read private key.
InputStream is = context.getResources().openRawResource(R.raw.private_key);
br = new BufferedReader(new InputStreamReader(is));
List<String> lines = new ArrayList<String>();
line = null;
while ((line = br.readLine()) != null)
lines.add(line);
// Removes the first and last lines of the file (comments).
if (lines.size() > 1 && lines.get(0).startsWith("-----") &&
lines.get(lines.size()-1).startsWith("-----")) {
lines.remove(0);
lines.remove(lines.size()-1);
}
// Concats the remaining lines to a single String.
StringBuilder sb = new StringBuilder();
for (String aLine: lines)
sb.append(aLine);
String keyString = sb.toString();
// Converts the String to a PublicKey instance
byte[] keyBytes = Base64.decode(keyString, Base64.DEFAULT);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
mKey = keyFactory.generatePrivate(spec);
Any help?

Seems your key is not PKCS8 format. Java does not support loading keys in PKCS#1 format. Check your key is in PKCS#8 format verifying that it starts with -----BEGIN PRIVATE KEY----- If it starts with ----BEGIN RSA PRIVATE KEY----- then you need to convert it to PKCS#8. See Convert PEM traditional private key to PKCS8 private key

use this lines:
if (privateKeyString.contains("-----BEGIN PRIVATE KEY-----") || privateKeyString.contains("-----END PRIVATE KEY-----"))
privateKeyString = privateKeyString.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "");
if (privateKeyString.contains("-----BEGIN RSA PRIVATE KEY-----") || privateKeyString.contains("-----END RSA PRIVATE KEY-----"))
privateKeyString = privateKeyString.replace("-----BEGIN RSA PRIVATE KEY-----", "").replace("-----END RSA PRIVATE KEY-----", "");

Related

RSA signature Java to C#

I have a tool which creates a signature. I am looking for equivalent code in dot net core 2.1.
Java Code: sign(currentdate,'some text','SHA1WithRSA')
public String sign(String date, String subjectId, String algorithm){
Signature rsa = Signature.getInstance(algorithm);
rsa.initSign(this.getPrivate());
rsa.update(subjectId.getBytes());
rsa.update(date.getBytes());
String signature = new String(Base64.getEncoder().encode(rsa.sign()));
return signature;
}
public PrivateKey getPrivate() {
String privateKeyPEM = "<This is the private key string";
byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
return kf.generatePrivate(spec);
}
Suggest a way to generate dot net core 2.1 equivalent code for the above with or without 3rd party tool.
This is work for me using Portable.BouncyCastle library.
private string GetSign(string data)
{
using (var rsa = new RSACryptoServiceProvider())
{
var privateKey = new StringBuilder();
privateKey.AppendLine("-----BEGIN RSA PRIVATE KEY-----");
privateKey.AppendLine("private key as string");
privateKey.AppendLine("-----END RSA PRIVATE KEY-----");
var pem = new PemReader(new StringReader(privateKey.ToString()));
var keyPair = (AsymmetricCipherKeyPair)pem.ReadObject();
var privateKeyParameters = (RsaPrivateCrtKeyParameters)keyPair.Private;
var rsaParameters = DotNetUtilities.ToRSAParameters(privateKeyParameters);
rsa.ImportParameters(rsaParameters);
var sign = rsa.SignData(Encoding.UTF8.GetBytes(data), new HashAlgorithmName("SHA1") /*pass your algorithm*/,
RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(sign);
}
}

SHA1withRSA NoSuchAlgorithmException

Hi I have the following function to convert a string into a PrivateKey in my application:
public static PrivateKey main() throws Exception {
// Read in the key into a String
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(PRIVATE_KEY));
String line;
while ((line = rdr.readLine()) != null) {
pkcs8Lines.append(line);
}
// Remove the "BEGIN" and "END" lines, as well as any whitespace
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replaceAll("\\n+","");
// Base64 decode the result
byte [] pkcs8EncodedBytes = Base64.decode(pkcs8Pem, Base64.DEFAULT);
// extract the private key
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
KeyFactory kf = KeyFactory.getInstance("SHA1WITHRSA");
PrivateKey privKey = kf.generatePrivate(keySpec);
return privKey;
}
and I get the following exception:
W/System.err: java.security.NoSuchAlgorithmException: SHA1withRSA KeyFactory not available
W/System.err: at java.security.KeyFactory.(KeyFactory.java:161)
at java.security.KeyFactory.getInstance(KeyFactory.java:195)
so I have tried to find all the alghoritms I can use through this code:
TreeSet<String> algorithms = new TreeSet<>();
for (Provider provider : Security.getProviders())
for (Provider.Service service : provider.getServices())
if (service.getType().equals("Signature"))
algorithms.add(service.getAlgorithm());
for (String algorithm : algorithms)
System.out.println(algorithm);
and in the response "SHA1withRSA" is included, do you know where the problem is?
SHA1withRSA is a signature type, you see that in the list because you have
if (service.getType().equals("Signature"))
If you edit that to
if (service.getType().equals("KeyFactory"))
you should see a list that looks something like this
DSA
EC
RSA
RSASSA-PSS
X25519
X448
XDH

How to generate Private key from string using BouncyCastle

I have a String stored in a variable:
-----BEGIN RSA PUBLIC KEY-----
MIGHAoGBANAahj75ZIz9nXqW2H83nGcUao4wNyYZ9Z1kiNTUYQl7ob/RBmDzs5rY
mUahXAg0qyS7+a55eU/csShf5ATGzAXv+DDPcz8HrSTcHMEFpuyYooX6PrIZ07Ma
XtsJ2J4mhlySI5uOZVRDoaFY53MPQx5gud2quDz759IN/0gnDEEVAgED
-----END RSA PUBLIC KEY-----
I generate public key as:
public static PublicKey getFromString(String keystr) throws Exception
{
//String S1= asciiToHex(keystr);
byte[] keyBytes = new sun.misc.BASE64Decoder().decodeBuffer(keystr);
ASN1InputStream in = new ASN1InputStream(keyBytes);
DERObject obj = in.readObject();
RSAPublicKeyStructure pStruct = RSAPublicKeyStructure.getInstance(obj);
RSAPublicKeySpec spec = new RSAPublicKeySpec(pStrcut.getModulus(), pStruct.getPublicExponent());
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
How to generate the PrivateKey using bouncy castle in android
{Edit}
Without using bouncy castle i am generating private key like this:
public static PrivateKey getKey(String mKey){
try{
// Read in the key into a String
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(mKey));
String line;
while ((line = rdr.readLine()) != null) {
pkcs8Lines.append(line);
}
// Remove the "BEGIN" and "END" lines, as well as any whitespace
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replace("-----BEGIN RSA PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END RSA PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\\s+","");
// Base64 decode the result
byte [] pkcs8EncodedBytes = Base64.decode(pkcs8Pem, Base64.DEFAULT);
// extract the private key
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(keySpec);
System.out.println(privKey);
return privKey;
}catch (Exception ex){
ex.printStackTrace();
}
return null;
}
I want to achieve the same using Bouncy Castle
I'm a little confused on why you insist on using bouncycastle, but if you really want to use bouncycastle then the CMS/PKIX library has a nice helper class called PEMParser that will shorten the code needed, e.g:
public static PrivateKey getPemPrivateKey(String mKey) throws Exception {
PEMParser pemParser = new PEMParser(new StringReader(mKey));
final PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
final byte[] encoded = pemKeyPair.getPrivateKeyInfo().getEncoded();
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
}

How to read a rsa public key file in java?

I have a RSA public key file like this:
-----BEGIN RSA PUBLIC KEY-----
this is content
-----END RSA PUBLIC KEY-----
and i use java to read it:
KeyFactory factory = KeyFactory.getInstance("RSA");
KeySpec spec = new X509EncodedKeySpec(bytesFromThisFile); // bytesFromThisFile is created and filled correctly
PublicKey publicKey = factory.generatePublic(spec);
then i get an exception:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
How to read the file properly? Is there a way to convert this rsa public key file to a java-readable format?
Try this method:
/**
* reads a public key from a file
* #param filename name of the file to read
* #param algorithm is usually RSA
* #return the read public key
* #throws Exception
*/
public PublicKey getPemPublicKey(String filename, String algorithm) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String publicKeyPEM = temp.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
BASE64Decoder b64 = new BASE64Decoder();
byte[] decoded = b64.decodeBuffer(publicKeyPEM);
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePublic(spec);
}
source: Load RSA public key from file
This is my implementation; can read PEM with key or certificate.
Tested only in java11.
/**
* reads a public key from a file
* #param f file to read
* #return the read public key
* #throws Exception
*/
public static PublicKey getPublicKeyFromPem(File f)
throws Exception
{
byte[] keyBytes = Files.readAllBytes(f.toPath());
String temp = new String(keyBytes);
String publicKeyPEM = temp;
if(temp.contains("-----BEGIN PUBLIC KEY-----"))
{
publicKeyPEM = temp
.replace("-----BEGIN PUBLIC KEY-----\n", "")
.replace("-----END PUBLIC KEY-----", "")
.trim();
}
else if(temp.contains("-----BEGIN RSA PUBLIC KEY-----"))
{
publicKeyPEM = temp
.replace("-----BEGIN RSA PUBLIC KEY-----\n", "")
.replace("-----END RSA PUBLIC KEY-----", "")
.trim();
}
else if(temp.contains("-----BEGIN CERTIFICATE-----"))
{
CertificateFactory fact = CertificateFactory.getInstance("X.509");
try (FileInputStream is = new FileInputStream(f))
{
X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
return cer.getPublicKey();
}
}
Base64.Decoder b64 = Base64.getDecoder();
byte[] decoded = b64.decode(publicKeyPEM);
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}

InvalidKeyException: invalid key format when reading EC Private Key from PEM file in Java

I'm trying to create a private key object from a given .pem file. The file has this structure:
-----BEGIN EC PRIVATE KEY-----
...............................
...............................
...............................
-----END EC PRIVATE KEY-----
I am attempting to create the private key object with this code:
public static String getKeyFromFile(String filename) throws IOException {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String key = new String(keyBytes);
return key;
}
public static PrivateKey getPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, NoSuchProviderException {
String privateKeyPEM = getKeyFromFile("MY_FILE.pem");
privateKeyPEM = privateKeyPEM.replace("-----BEGIN EC PRIVATE KEY-----\n", "");
privateKeyPEM = privateKeyPEM.replace("-----END EC PRIVATE KEY-----", "");
privateKeyPEM = privateKeyPEM.replaceAll("\n", "");
privateKeyPEM = privateKeyPEM.replaceAll(" ", "");
byte[] privateKeyBytes = privateKeyPEM.getBytes();
String encodedString = Base64.getEncoder().encodeToString(privateKeyBytes);
byte[] decodedString = Base64.getDecoder().decode(encodedString);
EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(decodedString);
KeyFactory kf = KeyFactory.getInstance("EC");
PrivateKey privKey = kf.generatePrivate(privKeySpec);
return privKey;
Upon running this method, I receive this error:
java.security.InvalidKeyException: invalid key format
I am able to parse the text and strip away any unwanted characters just fine, but I'm not able to create the private key object. I am able to generate a public key object from a similar .crt file using very similar methods. I want to be able to do this solely within Java and no openssl. Any help would be greatly appreciated.
Your code does not properly decode the base64 data:
privateKeyPEM contains the String data between the BEGIN and END data (which is base64 encoded).
Your code does the following:
byte[] privateKeyBytes = privateKeyPEM.getBytes();
// privateKeyBytes now contains the base64 encoded key data
String encodedString = Base64.getEncoder().encodeToString(privateKeyBytes);
// encoded String contains now the base64 encoded data of the base64 encoded key data
byte[] decodedString = Base64.getDecoder().decode(encodedString);
// decodedString is not the base64 encoded data of your key data
Why are you encoding the data base64 and then in the next line decoding it - both steps together are just useless.
What you really need is to apply the base64 decode one time onto privateKeyPEM:
byte[] keyData = Base64.getDecoder().decode(privateKeyPEM);
EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(keyData);
If the base64 decode fails then your base64 data is invalid - most likely because of contained spaces or \r.

Categories