I am trying to get RSA PublicKey for jwt token validation in Java but my attempts fail with this exception:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format.
My code:
String publicKeyString = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMk5pKytLeUpVaDhLYklLVU51OG0KUk5lRCtrU2tXZEh6MjBvSFZYK0g0cVd4WHlJSTk0MVdJUFU2WFpPc3lMeE9qZU1hb0ZRanJ6bDFwYnZQekUyRQpwMmhlK1BnQ1JteDNqOFlMVVd3dGpuQTVwTTFLWDhpNG5vTUw4RmlWY1U2NkE5RjRkZmRQR2MzY0tQQ2ZPbnorCmtBbW5qRllzajYzRCsrTThYSDRWaS9Vc0V3T1lzU05FR2RncUd2OTlTNHpVRzFzd2FqZ1NnODhvbTVaOC9Ja1AKY01LT3cvWkpvVHNDN3l1VlJnTC9xa3EwaDVkM2lXVXNNdXl1K0xoblRhTko4bW9WQmpJT2lQQkR0cEQyN1lzNgpCSGs1dEdBa3ZHZDg0N3c4SjVEeTFzYWlQS0pxelltcUx5akg3b3VlcERFczdEZ2UxZUlJeno5a1RnSkhKZHVzCnd3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
PublicKey publicKey = getPublicKey(Base64.getDecoder().decode(publicKeyString), "RSA");
And getPublicKey function:
private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) {
PublicKey publicKey = null;
try {
KeyFactory kf = KeyFactory.getInstance(algorithm);
EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
System.out.println(keySpec.getFormat());
publicKey = kf.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
System.out.println("Could not reconstruct the public key, the given algorithm could not be found.");
} catch (InvalidKeySpecException e) {
e.printStackTrace();
System.out.println("Could not reconstruct the public key");
}
return publicKey;
}
I know that there are many similar questions dealing with the same exception being thrown and I've read many of them and tried the solutions but nothing worked. I would appreciate any help.
Your data is not the base64 encoding of an actual X.509 publickey block (SubjectPublicKeyInfo) as required by Java crypto. It actually is the base64 encoding of a PEM file which is itself structured and encoded. Moreover this PEM file is labelled as containing 'RSA PUBLIC KEY' which would be the 'raw' PKCS1 form and wrong for Java, but fortunately whatever created this PEM file was defective and the body of the PEM file actually is an X.509 SubjectPublicKeyInfo which should have PEM label 'PUBLIC KEY' per RFC7468 #13. (RFC5280 is a profile of X.509.)
Thus you need to decode your string to PEM, then parse the PEM to get X.509:
String orig = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMk5pKytLeUpVaDhLYklLVU51OG0KUk5lRCtrU2tXZEh6MjBvSFZYK0g0cVd4WHlJSTk0MVdJUFU2WFpPc3lMeE9qZU1hb0ZRanJ6bDFwYnZQekUyRQpwMmhlK1BnQ1JteDNqOFlMVVd3dGpuQTVwTTFLWDhpNG5vTUw4RmlWY1U2NkE5RjRkZmRQR2MzY0tQQ2ZPbnorCmtBbW5qRllzajYzRCsrTThYSDRWaS9Vc0V3T1lzU05FR2RncUd2OTlTNHpVRzFzd2FqZ1NnODhvbTVaOC9Ja1AKY01LT3cvWkpvVHNDN3l1VlJnTC9xa3EwaDVkM2lXVXNNdXl1K0xoblRhTko4bW9WQmpJT2lQQkR0cEQyN1lzNgpCSGs1dEdBa3ZHZDg0N3c4SjVEeTFzYWlQS0pxelltcUx5akg3b3VlcERFczdEZ2UxZUlJeno5a1RnSkhKZHVzCnd3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
String pem = new String(Base64.getDecoder().decode(orig)); // default cs okay for PEM
String[] lines = pem.split("\n"); lines[0] = lines[lines.length-1] = ""; String body = String.join("", lines);
// in general split on "\r?\n" (or delete "\r" and split on "\n")
//or instead:
//String body = pem.replaceAll("-----(BEGIN|END) RSA PUBLIC KEY-----\n","").replaceAll("\n", ""); // or "\r?\n"
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey key = kf.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(body)));
// for test only:
System.out.println ( ((java.security.interfaces.RSAPublicKey)key).getModulus() );
Note your specific data has PEM using single-character (NL aka \n) linebreaks, but PEM in general can use either NL or CRLF (\r\n); see my comments.
Related
Generate some keys with OpenSSL, then encode them in Base64 and obtain them and try to generate them to validate the authentication with JWT. Here is the code and description of what happens to me
Generate with the following commands:
openssl req -x509 -newkey rsa:4096 -keyout private_key.pem -out public_key.der
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der
base64 –w 0 private_key.pem > private_key_base64_enc.txt
base64 –w 0 public_key.der > public_key_base64_enc.txt
I saved in my vault.keystore from wildfly: private_key_base64_enc.txt and public_key_base64_enc.txt
Then in my java class I write the following:
private void jwtSignedAuthentication(String token, PropName vaultBlockName) throws Exception
{
String rsa512Alias = vaultBlockName.getDefaultValue();
String rsa512pvt = VaultReader.getValue(rsa512Alias, "privateKey");
String rsa512pbc = VaultReader.getValue(rsa512Alias, "publicKey");
KeyFactory keyfatc = null;
PrivateKey privateKey = null;
PublicKey publicKey = null;
try {
keyfatc = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
logger.error(e);
}
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(new String(Base64.getDecoder().decode(rsa512pvt.getBytes()))));
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 ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\\s+","");
byte[] dataPvt = Base64.getDecoder().decode(pkcs8Pem.getBytes());
PKCS8EncodedKeySpec specPvt = new PKCS8EncodedKeySpec(dataPvt);
byte[] dataPbc = Base64.getDecoder().decode(rsa512pbc.getBytes());
StringBuilder publicLinesBuilder = new StringBuilder();
BufferedReader readerPlubKey = new BufferedReader(new StringReader(new String(dataPbc)));
String lineP;
while ((lineP = readerPlubKey.readLine()) != null) {
publicLinesBuilder.append(lineP);
}
String pubK = publicLinesBuilder.toString();
pubK = pubK.replace("-----BEGIN CERTIFICATE-----", "");
pubK = pubK.replace("-----END CERTIFICATE-----", "");
pubK = pubK.replaceAll("\\s+","");
X509EncodedKeySpec specPbc = new X509EncodedKeySpec(Base64.getDecoder().decode(pubK.getBytes()));
try {
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
} catch (InvalidKeySpecException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
// Creación de un verificador JWT
JWTVerifier verifier = JWT.require(algorithm).withIssuer(JWT_CLAIM_ISSUER).acceptLeeway(2).build();
UserContext userContext = new UserContext();
userContext.setUserName(JWT_CLAIM_ISSUER);
try {
// Decode JWT, verificación del token.
#SuppressWarnings("unused")
DecodedJWT decodeJwt = verifier.verify(token);
} catch (JWTDecodeException e) {
logger.error(e);
}
}
When I try to generate the keys I return null:
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
Anyone have any idea what happens with this. Thanks in advance
For generate my JWT:
public ResteasyWebTarget getClientWebAgent(String host, String blockName) throws KeyStoreException
{
ResteasyClient clientBuilder = new ResteasyClientBuilder().establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(5, TimeUnit.SECONDS).build();
ResteasyWebTarget target = clientBuilder.target(host);
KeyPair keys = null;
try {
keys = keyStore.getKeys();
/*logger.infov(new String(Base64.getEncoder().encode(keys.getPrivate().getEncoded())));
logger.infov("****PUBLIC KEY ******");
logger.infov(new String(keys.getPublic().getEncoded()));*/
} catch (IOException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) keys.getPublic(), (RSAPrivateKey) keys.getPrivate());
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", "RS512");
headerClaims.put("typ", "JWT");
JWTCreator.Builder jwtCreator = JWT.create();
jwtCreator.withHeader(headerClaims);
jwtCreator.withIssuer(JWT_CLAIM_ISSUER);
jwtCreator.withIssuedAt(LocalDate.now().toDate());
jwtCreator.withExpiresAt(LocalDate.now().toDateTimeAtCurrentTime().plusSeconds(30).toDate());
String jwtToken = jwtCreator.sign(algorithm);
target.register(new BearerAuthenticator(jwtToken));
target.register(new LanguageHeaderToken(Locale.getDefault()));
return target;
}
Your 'public key' is actually a certificate (specifically an X.509 v1 or v3 certificate, depending on your openssl config), which contains a publickey but is different from a publickey -- and is in PEM format even though you have misleadingly named it .der -- and your privatekey is encrypted.
In addition to the approach of using a PKCS12, as Roberto validly proposes and is usually the simplest because it's only one file to manage and is still encrypted and thus more secure:
Java can handle an X.509 certificate, but you use a CertificateFactory.getInstance("X.509") and give it an InputStream instead of a KeyFactory and an X509EncodedKeySpec. CertificateFactory can handle either PEM or DER, unlike KeyFactory which can handle only DER, so you don't need the de-PEM (strip BEGIN/END/EOL and decode base64) parts.
standard Java cannot handle encrypted PKCS8 keys directly. If you can add a thirdparty library, BouncyCastle's bcpkix can do so; search the dozen or so existing Qs that use PEMParser (not PEMReader, that's the older version) and JceOpenSSLPKCS8DecryptorBuilder. Otherwise, you can add -nodes to your req -newkey -x509 command to generate an unencrypted privatekey file, which after you de-PEM it does work in KeyFactory with PKCS8EncodedKeySpec. (It's still spelled -nodes even though the encryption used without it hasn't been plain aka single DES for decades.) Using an unencrypted privatekey file of course means that any intruder or malware on your system that can read that file can get your privatekey, which in many situations is a risk.
finally, if you really want only the keypair and not a certificate, don't bother with req -newkey -x509. Instead use openssl genpkey to generate the privatekey, or the older but simpler openssl genrsa -nodes followed by (or piped to) openssl pkcs8 -topk8 -nocrypt to convert it to PKCS8-unencrypted format. Then use openssl pkey -pubout or the older openssl rsa -pubout to make a separate file with the publickey. Those commands can write (and read back where applicable) DER format instead of PEM; if you do that, your code doesn't need the de-PEM steps, you can just pass the binary file contents to KeyFactory. The risks for an unencrypted file are the same as above.
Maybe you are generating the keystore without assigning a valid alias, looking at your command you are not using the -name option.
The command should be like this:
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der -name "alias"
A smarter way to use the keys in java is by creating a KeyPair:
KeyPair loadKeyPair() throws Exception {
// Read keystore from resource folder
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource("keyStore.p12");
File file = new File(Objects.requireNonNull(resource).toURI());
char[] keyPass = "1234".toCharArray();
String alias = "alias";
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream is = new FileInputStream(file)) {
keystore.load(is, keyPass);
}
Key key = keystore.getKey(alias, keyPass);
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keystore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
}
return null;
}
Then extract RSAPublicKey and RSAPrivateKey keys from the KeyPair:
void loadKeys() throws Exception{
KeyPair keyPair = loadKeyPair();
if (null != keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
}
}
Hope it can be helpful and good luck with your Json Web Tokens! :-p
I have the following public key, that is stored in the DB (PostgresSQL) as text. It's a String, in java:
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEA1ht0OqZpP7d/05373OE7pB7yCVGNGzkUEuCneyfOzps6iA03NbvI
1ZL0Jpp/N3AW73lGdhaoa3X3JE4GsI/bsToVLQwTKmIOC4yjTvBctmFEoyhhTfxW
s1UHZKl4XZ/7THbRlKHhRaTKyfDAbikkMAxNT/qutLAPjnN1qOwjb1oRq52NP6FJ
KWTTikz4UeOHroX+Xthn2fJSJDlQ4YMdBbgrZVx5JcHKNuPTKRf5gI8QQKMSA9Q9
QJRE5OGp7b6dG14ZmOUnUxb00Mp20LgcaGPcuWU+oFsbQaF6W4G4bdkSZRJJXhSg
d4Q7mahpar94/gnztJmth0GzqTWUYyZIWNqIFoMwuOgeaiDV43zb3uLsRVpRKYYy
esmzcOy/jTScVLRCD8QRyu9B2wgCkNAVztQOXPCOOa4O1LlVQWaecIs4WPhOqDhi
KTBhyVkpC1TrrBkp+QMqMqWll1OyVb6k/7uV0qE/i6rHJtjo5v9bcIgYzswyx9CD
9PKl2Q0L0Jg7TMG+yLDIrLfGeuSeEc4XYJzN7bJcCeiizzu5iU9dQUkrncOrq9jn
Ub2pM/+A+JqIsoPK3IY/pJKqH4JYpGKhO1iPQF6iXIZT1r3ZgJUSQtzSeyYqhkla
2uR2BsbPbDqebCuXm3lAsY5w+dujijcn96PKwYha1LsK5sACHuJ79AMCAwEAAQ==
-----END RSA PUBLIC KEY-----
I don't know how this key has been generated, I'm sorry. I have been told to take this key and verify the signature of another string that I'll call "object". I have been told that the algorithm that I have to use to verify "object" is SHA256withRSA.
So, I have written the following java method to read the key
private PublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
publicKey = publicKey.replaceAll("\\n", "");
publicKey = publicKey.replace("-----BEGIN RSA PUBLIC KEY-----", "");
publicKey = publicKey.replace("-----END RSA PUBLIC KEY-----", "");
publicKey = publicKey.trim();
byte[] keyDecoded = Base64.getDecoder().decode(publicKey.getBytes());
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(keyDecoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = kf.generatePublic(publicSpec);
return pubKey;
}
The point is that I get the following exception:
java.security.InvalidKeyException: IOException: algid parse error, not
a sequence
I have read plenty of qustions as mine in stackoverflow. The code written by other users is pretty similar (sometimes identical) to mine. So I definitely don't get why it doesn't work for me. Other developers (workmates) are doing the same in php and it works great, so I would discard the hypothesis of wrong public key. Maybe didn't I understood the process clearly? Do you have any clue, please?
I have also tried to cope with the problem using BouncyCastle library, as suggested here, but I get the same exception. The following is the code I have written:
private static PublicKey getPublicKey(String publicKey)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Security.addProvider(new BouncyCastleProvider());
PemReader pp = new PemReader(new StringReader(publicKey));
PemObject pem = pp.readPemObject();
byte[] content = pem.getContent();
pp.close();
X509EncodedKeySpec spec = new X509EncodedKeySpec(content);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
You can't load that key using an X509EncodedKeySpec. According to it's JavaDoc documentation it expects the following format:
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
Instead your key looks different. I used the data from your post, converted it to hex data and posted it into the online ASN.1 decoder.
The output is this:
SEQUENCE (2 elem)
INTEGER (4096 bit) 873481340827968071893572683200799871431146795599597693981565010037737…
INTEGER 65537
As you may recognize your key does not contain an AlgorithmIdentifier therefore it can not be loaded using X509EncodedKeySpec.
My suggestion would be to use the BouncyCastle library and it's PEMParser class for loading this key:
File pemFile = new File("test.pem");
try (PEMParser pp = new PEMParser(new InputStreamReader(new FileInputStream(pemFile)))) {
SubjectPublicKeyInfo subjPubKeyInfo = (SubjectPublicKeyInfo) pp.readObject();
RSAKeyParameters rsa = (RSAKeyParameters) PublicKeyFactory.createKey(subjPubKeyInfo);
RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsa.getModulus(), rsa.getExponent());
KeyFactory kf = KeyFactory.getInstance("RSA");
java.security.PublicKey publicKey = kf.generatePublic(rsaSpec);
System.out.println(publicKey);
}
Or you manually convert the key to PKCS#8 format via openssl.
I am encrypting a string using RSA algorithm and encryption and decryption logic is
public class RsaEncrypt {
private static final String ALGORITHM = "RSA";
public static void main(String[] args) {
String filePath = "/home/Desktop/abc.jks";
char[] password = "changeit".toCharArray();
String alias = "123";
KeyStore ks = null;
try {
//loading the keystore
ks = KeyStore.getInstance("JKS");
InputStream readStream = new FileInputStream(filePath);
ks.load(readStream, password);
Certificate cert = ks.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password);
RsaEncrypt e = new RsaEncrypt();
String result = e.encrypt("abvhdh", publicKey);
String decryptResult = e.decrypt(result.getBytes(), privateKey);
} catch (Exception e) {
e.printStackTrace();
}
}
//Encryption of a string
public String encrypt(String text,PublicKey publicKey) {
String retVal = null;
byte[] cipherText = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
cipherText = Base64.getEncoder().encode(cipherText);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return new String(cipherText) ;
}
// Decryption of a string
private String decrypt(byte[] text, PrivateKey privatekey) {
byte[] dectyptedText = null;
try {
final Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privatekey);
dectyptedText = cipher.doFinal(Base64.getDecoder().decode(text));
} catch (Exception ex) {
ex.printStackTrace();
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new String(dectyptedText);
}
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value.
But when i decrypt,The reuslt string contains some null characters
Example
input : abcd output : abcd \u0000 \u0000 \u0000 \u0000....
How can i resolve this problem and what is the best way to get same encryption value if we encrypt multiple times?????
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
That is correct and it is even required property of the RSA encryption. By default PKCS1.5 padding is used (RSA/ECB/PKCS1Padding) which contains some random bytes. Safer option is RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING mode which is even more random.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value. But when i decrypt,The reuslt string contains some null characters
Using RSA without padding (NoPadding) is very unsafe (it is called textbook RSA).
Padding extends the original encrypted value to full space length (e.g. 2048 bits) and then the RSA magic (exponentiation) will be executed. Using the NoPadding parameter you are telling the crypto library that you will do the padding yourself. In that case you are expected to remove the padding after decryption (in your case zero padding)
I hope you are doing that for learning / academic purposes, not some real security project. You may have a look at my blog about encryption to get some examples.
btw: you should not use RSA to encrypt the plaintext itself. Rather use symmetric encryption to encrypt the plaintext and then RSA to encrypt the symmetric encryption key.
For obvious security reasons i need to encrypt and decrypt User's PIN codes with RSA private and public key, I have found working solution, which looks like:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair rsaKeyPair = kpg.genKeyPair();
byte[] txt = "This is a secret message.".getBytes();
System.out.println("Original clear message: " + new String(txt));
// encrypt
Cipher cipher;
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic());
txt = cipher.doFinal(txt);
} catch (Throwable e) {
e.printStackTrace();
return;
}
System.out.println("Encrypted message: " + new String(txt));
// decrypt
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivate());
txt = cipher.doFinal(txt);
} catch (Throwable e) {
e.printStackTrace();
return;
}
System.out.println("Decrypted message: " + new String(txt));
}
everything works fine, but in this example key-pair is not static and generate new values everytime, but I need to use same keys, which are represented as String variables:
public static final String PrivateKey = "MIICXAIBAAKBgQDx0PSJr6zEP9914k1eM+sS8/eW+FenhBQI/jf6ARe8kZHFig9Y"
+ bla bla bla
+ "wdK3jBzObK319yNFr/2LukNZ9Bgv7fS78roBvxbe2gI=";
public static final String PublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDx0PSJr6zEP9914k1eM+sS8/eW"
+ bla bla bla
+ "jYo5w2Nhxe2cukCQMQIDAQAB";
Is there any way to cast these variables to PublicKey and PrivateKey Class?
If I understand what you want, to obtain PublicKey and PrivateKey instances from your static variables you can do, for example, this way:
private static final String privateKeyString = "...";
private static PrivateKey privateKey;
private static final String publicKeyString = "...";
private static PublicKey publicKey;
static {
KeyFactory kf;
try {
kf = KeyFactory.getInstance("RSA");
byte[] encodedPv = Base64.decodeBase64(privateKeyString);
PKCS8EncodedKeySpec keySpecPv = new PKCS8EncodedKeySpec(encodedPv);
privateKey = kf.generatePrivate(keySpecPv);
byte[] encodedPb = Base64.decodeBase64(publicKeyString);
X509EncodedKeySpec keySpecPb = new X509EncodedKeySpec(encodedPb);
publicKey = kf.generatePublic(keySpecPb);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
}
}
After (mostly) concurring with #JB that passwords (usually) shouldn't be encrypted, they should be "hashed" -- using a method specifically designed to "stretch" and salt such as scrypt, not a fast hash like SHA-1 -- and also noting that RSA-512 as used in your original code is broken and even RSA-1024 as apparently used in your modification is considered weak:
Your PrivateKey value appears (from its beginning) to be base64 of a plain PKCS#1 DER encoding, which basically is used only by OpenSSL and things that use OpenSSL (format) like older versions of OpenSSH. The Java standard "Sun" providers do not handle this, although I think BouncyCastle might if you want to explore that. For Sun you need to convert it to binary DER from base64; wrap it into PKCS#8 format (which in binary is just adding a header and maybe EOC trailers because the algorithm-specific part of PKCS#8 for RSA is PKCS#1); and put it in a PKCS8EncodedKeySpec and run it through generatePrivate of a KeyFactory of type RSA. See
http://docs.oracle.com/javase/8/docs/api/java/util/Base64.html (Java8 only)
http://docs.oracle.com/javase/8/docs/api/java/security/KeyFactory.html
https://www.rfc-editor.org/rfc/rfc5208#section-5 for the structure of unencrypted PKCS#8 (Java doesn't do the encrypted format in section 6) and look at the publickey form for the OID for RSA.
Alternatively add the header/trailer to make it proper PEM, use OpenSSL to convert it to PKCS#8 (unencrypted), and optionally binary at the same time, and run that through generatePrivate.
Your PublicKey similarly appears to be base64 of an X.509 SubjectPublicKeyInfo encoding, which OpenSSL (but not OpenSSH) uses and standard Java does support under the name "X.509". So just convert from base64 to binary, put in an X509EncodedKeySpec, and run through generatePublic of the RSA KeyFactory. Note if your encryption will be done remote or distributed, which is the usual scenario for publickey-encryption, the encryptor must be certain to use the correct publickey; if an attacker can substitute a wrong publickey they can decrypt and steal at least some of your supposedly secure data. That's why real PK systems don't use a plain publickey, they use a certificate, either X.509 like SSL/TLS and S/MIME, or web-of-trust like PGP.
I got this running doing the following:
public Key loadPrivateKey(String stored) throws GeneralSecurityException {
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8)));
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
public Key loadPublicKey(String stored) throws GeneralSecurityException {
byte[] data = Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePublic(spec);
}
You must also to remove -----BEGIN PRIVATE KEY-----, -----END PRIVATE KEY----- and all \n from the strings that contain you keys.
I'm writing a java program to import private keys from files within the file system and make a private key object, using java...
I could do it for files in .pem format but, with .der format, I had no idea what to do, since I couldnt firstly detect the algorithm used to generate the keys.
within .pem files I could determine the algorithm from the header for PKCS#1 which have a header like
-----BEGIN RSA PRIVATE KEY----
formats and used the bouncycastle pem reader for those in PKCS#8 which have a header
-----BEGIN PRIVATE KEY-----
but with those in .der format no idea :(
also if anyone have an idea about .key format tell me
thanx
If your DER files are in PKCS#8 format, you can use the Java KeyFactory and do something like this:
// Read file to a byte array.
String privateKeyFileName = "C:\\myPrivateKey.der";
Path path = Paths.get(privateKeyFileName);
byte[] privKeyByteArray = Files.readAllBytes(path);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privKeyByteArray);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey myPrivKey = keyFactory.generatePrivate(keySpec);
System.out.println("Algorithm: " + myPrivKey.getAlgorithm());
You mentioned that you may not know what algorithm the key is using. I'm sure there is a more elegant solution than this, but you could create several KeyFactory objects (one for each possible algorithm) and try to generatePrivate() on each one until you do not get an InvalidKeySpecException.
thanks #gtrig using ur idea and editing the code like this :
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(KeyBytes);
try
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
privateKey = keyFactory.generatePrivate(keySpec);
algorithm = keyFactory.getAlgorithm();
//algorithm = "RSA";
//publicKey = keyFactory.generatePublic(keySpec);
} catch (InvalidKeySpecException excep1) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
privateKey = keyFactory.generatePrivate(keySpec);
algorithm = keyFactory.getAlgorithm();
//publicKey = keyFactory.generatePublic(keySpec);
} catch (InvalidKeySpecException excep2) {
KeyFactory keyFactory = KeyFactory.getInstance("EC");
privateKey = keyFactory.generatePrivate(keySpec);
} // inner catch
}
the code is working well now