So I made a program based off this tut:
http://www.macs.hw.ac.uk/~ml355/lore/pkencryption.htm
It works perfectly fine, but as you can see at the bottom of the page it requires OpenSSL commands. I used OpenSSL and generated my keys in the correct format to test my program, works perfectly. But I was wondering how I would generate those keys just in Java. I have tried countless times but encryption is so hard to understand for me and to work with. So I have no code to show at the moment, how would I start this or if anyone has a good method for doing so. I don't mind using bouncy castle if it is possible.
TL;DR: I would like to generate a unique Private and Public key in the correct format (PKCS#8, DER) using java (with or without a public library).
Just need these commands in java (not running through OpenSSL):
To generate a private key of length 2048 bits:
openssl genrsa -out private.pem 2048
To get it into the required (PKCS#8, DER) format:
openssl pkcs8 -topk8 -in private.pem -outform DER -out private.der -nocrypt
To generate a public key from the private key:
openssl rsa -in private.pem -pubout -outform DER -out public.der
Generating an RSA keypair is easy in Java. For example:
import java.security.*;
import java.security.interfaces.*;
public class RSACreate {
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048, new SecureRandom());
KeyPair kp = kpg.generateKeyPair();
RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) kp.getPrivate();
RSAPublicKey rsaPub = (RSAPublicKey) kp.getPublic();
}
}
Related
I have got a passphrase protected PEM key file generated by OpenSSL
openssl genrsa -aes128 -passout stdin -out testfile.pem
I have also generated a public key file using OpenSSL
openssl rsa -in testfile.pem -out testfile_pub.pub ( propts for password)
I would like to be able to use this private key to sign my claims etc. and then send requests. What I am struggling to understand (or more like confirming my understanding about) are the following:
1) My private key is password protected, does it mean no one can actually generate the public key without unlocking it first? i.e. that's where the protection is?
2) If I was to read this encrypted private key PEM file in Java, I would have to do something similar to:
\\ 1. Read file as string
\\ 2. Replace all boring bits e.g. begin/end/rsa/private/public/key/enc/--
\\ 3. decode using Base64
\\ 4. read as PKCS8 keyspec and generate PrivateKey object
but doesn't this mean that no one is actually stopping me from reading the keyspecs ? I guess what I am trying to compare with is how we generate JKS keys with optional keypass/storepass. But may be I am not supposed to compare this.
Could anyone help me understand?
Thanks,
openssl rsa -in testfile.pem -out testfile_pub.pub does not export the public key, it actually exports the private key out in clear text (if you provided the correct password). To export the public key use the -pubout option.
Yes you will need the password to export the public key.
To import the private key in Java you will need to convert it to PKCS8 first:
openssl pkcs8 -topk8 -in testfile.pem -inform pem -out testfile_pkcs8.pem -outform pem
Then you can import it in Java like:
String encrypted = new String(Files.readAllBytes(Paths.get("testfile_pkcs8.pem")));
encrypted = encrypted.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
encrypted = encrypted.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo(Base64.decode(encrypted));
PBEKeySpec keySpec = new PBEKeySpec("mypassword".toCharArray()); // password
SecretKeyFactory pbeKeyFactory = SecretKeyFactory.getInstance(pkInfo.getAlgName());
PKCS8EncodedKeySpec encodedKeySpec = pkInfo.getKeySpec(pbeKeyFactory.generateSecret(keySpec));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey encryptedPrivateKey = keyFactory.generatePrivate(encodedKeySpec);
No, it doesn't mean that anyone can read the key, because you still have to provide the password.
I have gone through many similar threads but no luck!!
I want to generate public and private keys using a PEM file. Following is the code I am using for the same:
String pemFileNme = "C:\\Users\\amitmm\\Desktop\\clean\\key.pem";
File pubKeyFile = new File(pemFileNme);
File privKeyFile = new File(pemFileNme);
// read public key DER file
DataInputStream dis = new DataInputStream(new
FileInputStream(pubKeyFile));
byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
dis.readFully(pubKeyBytes);
dis.close();
// read private key DER file
dis = new DataInputStream(new FileInputStream(privKeyFile));
byte[] privKeyBytes = new byte[(int)privKeyFile.length()];
dis.read(privKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// decode public key
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey)
keyFactory.generatePublic(pubSpec);
// decode private key
PKCS8EncodedKeySpec privSpec = new
PKCS8EncodedKeySpec(privKeyBytes);
RSAPrivateKey privKey = (RSAPrivateKey)
keyFactory.generatePrivate(privSpec);
Exception:
Exception in thread "main" java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: invalid key format
at
sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at main.java.me.txedo.security.Main2.f1(Main2.java:47)
at main.java.me.txedo.security.Main2.main(Main2.java:20)
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(X509Key.java:387)
PEM File Content:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwnEEodFEf86+Ae+wYyI//u1kekIWnA3RfzbAwWD77uG7D9Ci
9vVNbPO4XT2hKL03/q7d7KTgrA1sjBltfaOzVfA56x1S/0cYVk4xI440dpLo0F+m
RIqRw5fh8IuUlUIr3I4A7ESkDQQsZbDpdgCiNbrlADqLotcZyB4rU4uURW8QUI/W
eqsD6TOQs4bI+3o3xAKkky2kXujSaaa3tDxgUPTmSQ0Buk7Hx/IVzwyV7qjWiR4U
C46rHpnWxfF0DWuJUOYgJmBQ8xFOQwt4Ec/u+0m8top8cqQF+gpBn9iLXpbtahA3
pqyvLuNXRH9yn8mlEneBrjjl6U0H3W/AV7/dGwIBAwKCAQEAgaCtwTYtqonUAUp1
l2wqqfOYUYFkaAk2VM8rK5X9SevSCosXT04znffQPikWGyjP/x8+ncNAcrOdsrue
U8J3jqAmnL43VNoQOYl2F7Qi+bdF4D/ELbG2gmVBSwe4Y4FykwlV8thtXgLIQ8tG
TqsWznyYqtGybI9mhWlyN7Ji2POMDZP5Lwx7M01pMezwpnsZSmPVL9TgVrtWv4xt
C0vPyuy9THlFWtkOdHItNK+vOTcpuHn29rFUJI/D3R+SQjcdqj3aaqljOtdeBxgd
yDl2/Z4rUyetgzcZMfNTt/NRT0hOJ6R6/2S7gFCTtxMHBh3vVCH+pLLnQyJvcPQu
AsORSwKBgQDhOPr1x/8BioqaasoXvO9NsGktCgPDjbC4d3jR8n6lCa42X/eIahaD
xi1VGWyQhdO7aMXiDmzOtox7xHcMRh+a5ySIs9gTsHkMB2hqwIUNg25INRkQ3Vr3
eWnoTBGsfJqC1TEME3ocKwmyz57ZAe4yyR/ZRdDX5DUt9qCCFeA8uQKBgQDdAzbq
7BlJkbTYfdlIRNJEJAO3wWqQTx8X0ttCMMwDluOT9l+RR/KuUxl85ph+kwJci6E/
ixfeMTW1NcsMY/lB6mTP0oooalU1MP7gpPSu+24zhLXnUHZotbNbv9nk6w/1WWhz
FBt5w2DG4kQPFK6LSySqcVuzIGQyvWD5PbpGcwKBgQCWJfyj2qoBBwcRnIa6ffTe
dZtzXAKCXnXQT6XhTFRuBnQkP/pa8WRX2XOOEPMLA+J88IPsCZ3fJF2n2E9dhBUR
722wd+VidaYIBPBHKwNeV57azhC16OdPpkaa3WEdqGcB43YIDPwSx1vMimnmAUl3
ML/mLos6mCNz+cBWuUAoewKBgQCTV3nx8ruGYSM6/pDa2IwtbVfP1kcK32oP4eeB
dd1Xue0NTupg2qHJjLuombr/DKw9smt/sg/pdiPOI9yy7VDWnEM1NwbFnDjOIKnr
GKMfUkl3rc6aNaRFzneSf+aYnLVOO5r3Yrz715XZ7C1fYx8Hh23G9j0iFZgh05X7
fnwu9wKBgHyC0X26KZQ0ukan5jDSiz4dapUp2d3F+vnRzZa2AOsmo995gsXLdfsJ
n0o4Z3LsQJUDRI3tQ4dXe/5jS4oFrOdxALOAw6YmvEv/3oHwsCYPDhqLNfIJ9I6m
Dt3yG61pUJiCArhPaYG17NQoCxF6Xi6GUajRsECbr8DdyGMAu5eE
-----END RSA PRIVATE KEY-----
I have tried removing file header and footer manually. I tried code from bouncycastle, no luck, same error.
Python code which works with this file:
def t2e_enc(plaintext, pk_pem_file = './2017-12-04T062008Z.pem'):
'''
Function for encryption of Track2 credit card data.
This function uses private key to derivate public part used for encryption
'''
with open(pk_pem_file, 'rb') as pk:
private_key = serialization.load_pem_private_key(pk.read(),
password=None, backend=default_backend())
public_key = serialization.load_pem_public_key(
private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo),
backend=default_backend()
)
ciphertext = public_key.encrypt(
plaintext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
)
)
b64ciphertext=base64.b64encode(ciphertext)
return b64ciphertext
I am doing this for the first time, so bear with me if there is any silly mistake.
Partial dupe Load RSA public key from file
So, you 'wrote' (I assume, copied) code that clearly says you need two files, in DER form, containing PKCS8 and 'X509' encodings. (What Java calls X.509 here is really the SubjectPublicKeyInfo component of X.509.) You give it one file, in PEM form not DER, containing a PKCS1 encoding not PKCS8 or X509 -- and you're surprised it doesn't work? Python works because it calls OpenSSL, and OpenSSL supports over a dozen encodings and formats for privatekeys, including this one; Java supports only one (outside of keystores) which isn't this one. (Bare) publickeys are a little better; internally libcrypto supports multiple forms, but in practice only two of them are used, and one of them matches Java -- although many publickeys are distributed, stored, and used in the form of X.509 certificates, which provides several more forms to worry about.
There are approximately 7 solutions to your situation:
the simplest is to use OpenSSL commandline to convert your one file to the two files Java wants:
# (corrected! pkey is inconsistent!)
openssl pkcs8 -topk8 -nocrypt -in input.pem -outform der -out private.der
openssl pkey -in input.pem -pubout -outform der -out public.der
# or for very old versions (should not be needed now)
openssl rsa -in input.pem -pubout -outform der -out public.der
Those files can now be read by the code you posted (except with the filenames separated). Note this conversion doesn't have to be done on the same system; if necessary you can do it elsewhere and copy the files, if you use a method that works for binary files i.e. NOT cut&paste.
if you really want only one file, but it can be converted, create the private.der file as above and read it with only the privatekey-related parts of your code, then do:
RSAPrivateCrtKey priv2 = (RSAPrivateCrtKey)privKey;
PublicKey pubkey = keyFactory.generatePublic(new RSAPublicKeySpec(priv2.getModulus(), priv2.getPublicExponent()));
you could convert the files to PKCS8 and 'X509' PEM by omitting -outform der from the above conversions, then read those files and manually 'de-PEM' by removing the header and trailer lines and converting the base64 to binary (removing or skipping the linebreaks); this results in binary PKCS8 and X509 encodings you can run through your existing code. This is as much work on the openssl side and more work on the Java side so there is no apparent advantage, except that PEM files are valid text and can be cut&pasted if necessary.
combining these, you could convert to PKCS8 PEM only, read that per bullet 3 (de-PEM then the privatekey parts of your code), then extract publickey from privatekey per bullet 2
one way to use the format you have (unconverted) in plain Java is to de-PEM per bullet 3 giving you a PKCS1 encoding, then manually construct the PKCS8 encoding, then proceed as before to run the PKCS8 through the KeyFactory and extract publickey per bullet 2. See my answer at Java: Convert DKIM private key from RSA to DER for JavaMail for a really ugly way to do this (including one de-PEM method). There is a better way if you use BouncyCastle (which has a class for this ASN.1 type), but if you use BouncyCastle it's better not to use this method at all, see below.
another way to use the unconverted format in plain Java is to de-PEM per bullet 3, then parse the ASN.1 structure of PKCS1 and construct an RSAPrivateCrtKeySpec which you can run through your KeyFactory instead of a PKCS8 encoding then extract publickey per bullet 2. This is even more complicated, although I think I have seen it somewhere; will add if I find it. Again BouncyCastle can improve this method, but doesn't need to, see below.
finally, if you have BouncyCastle it's dead easy. You don't say what you tried with 'no luck', but the following BouncyCastle code is all you need and does work:
try( Reader r = new FileReader(filename) ){
KeyPair pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)new PEMParser(r).readObject());
}
Note this gives you a KeyPair with both privatekey and publickey objects from the one file.
I'm trying to convert a .pub file's contents to a PublicKey and then convert the PublicKey back into a String in order to determine if the conversion is working and does not change the key in the process.
id_rsa.pub:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0zszKhcZTC8xJidUszmRn4Tr/FxPs04wpCzEstebfTW7Bvqgtt+OdvxoNyYM0LAEnxEF4XhAWcsX7VJJqstZLpDqlKDXFr2d0aVIjksCpZt+ftVRwYHRoERhEOP/UmPFb5rKIkhQbED2kTWg11mW9soc6BhwB3THn/Cyo3t1u2vWjEySgPhKeA3Xzh+5eqV7CUD8V6S7OAT7T9ijf7sRV0R8rwHgTLWJ8+dETnY3L3N0fEaNuaayeNblHqrL53/1+tsBBUF3bAS+1GE6oniSeM/yhtfzf2x+O5MDlVVMbOCC/v+FnfIIEKLA+v1xDSAha7C5cHh82TxToWXsbjqGD me#mail
Converter.java
public static final synchronized PublicKey base64ToPublicKey(final String algorithm, final String base64) throws GeneralSecurityException, IOException {
BASE64Decoder decoder = new BASE64Decoder();
byte[] sigBytes2 = decoder.decodeBuffer(base64);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(sigBytes2);
KeyFactory keyFact = KeyFactory.getInstance(algorithm, "BC");
return keyFact.generatePublic(x509KeySpec);
}
public static final synchronized String publicKeyToBase64(final PublicKey publicKey) throws GeneralSecurityException, IOException {
byte[] publicKeyBytes = publicKey.getEncoded();
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(publicKeyBytes);
}
When I run:
PublicKey test1 = base64ToPublicKey("RSA", "AAAAB3NzaC1yc2EAAAADAQABAAABAQC0zszKhcZTC8xJidUszmRn4Tr/FxPs04wpCzEstebfTW7Bvqgtt+OdvxoNyYM0LAEnxEF4XhAWcsX7VJJqstZLpDqlKDXFr2d0aVIjksCpZt+ftVRwYHRoERhEOP/UmPFb5rKIkhQbED2kTWg11mW9soc6BhwB3THn/Cyo3t1u2vWjEySgPhKeA3Xzh+5eqV7CUD8V6S7OAT7T9ijf7sRV0R8rwHgTLWJ8+dETnY3L3N0fEaNuaayeNblHqrL53/1+tsBBUF3bAS+1GE6oniSeM/yhtfzf2x+O5MDlVVMbOCC/v+FnfIIEKLA+v1xDSAha7C5cHh82TxToWXsbjqGD");
I get back:
java.security.spec.InvalidKeySpecException: java.io.IOException: unexpected end-of-contents marker
at org.bouncycastle.jce.provider.JDKKeyFactory.engineGeneratePublic(Unknown Source)
at org.bouncycastle.jce.provider.JDKKeyFactory$RSA.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(KeyFactory.java:328)
at base64ToPublicKey(Converter.java:216)
at main(Converter.java:283)
OpenSSH public key files (id_*.pub also the entries in known_hosts and authorized_keys) for SSH2 use an OpenSSH-specific variant of an SSH-specific format, see rfc4716 which is in turn based on the SSH2 wire format (as linked) rfc4253 6.6, which is not the 'X.509' format Java crypto uses. (OpenSSH file formats for SSH1 were different, but SSH1 is long broken and should not be used.)
To convert this in Java see convert openSSH rsa key to javax.crypto.Cipher compatible format .
It's easier to avoid the problem.
Bypass 1: If you have reasonably recent OpenSSH (6.0 is okay, not sure for earlier), use
ssh-keygen -e -m PKCS8 -f id_rsa.pub >pub.pem # change filename as needed
to convert to 'X.509' (really SubjectPublicKeyInfo aka SPKI) in PEM form. (Yes they do use the name PKCS8 to mean SPKI; it's crazy.) Then read this in Java by discarding the BEGIN and END lines, decode everything in between (less the line breaks) from base64 to byte[], and put that in X509EncodedKeySpec as you have now. Or if you have OpenSSL you can convert to DER form
openssl rsa -pubin -in pub.pem -out pub.der -outform der # any version
openssl pkey -pubin -in pub.pem -out pub.der -outform der # 1.0.0 up
and then read the DER file with no change at all into an X509EncodedKeySpec.
Bypass 2: if you have the private key, and it is NOT OpenSSH's 'new' format (optional since 6.5 (edit) and default since 7.8), and you have OpenSSL, get the public key in SPKI (Java-friendly) format with one of
openssl rsa -in id_rsa -pubout -out pub.pem # default PEM
openssl rsa -in id_rsa -pubout -out pub.der -outform der # DER
openssl pkey -in id_rsa -pubout -out pub.pem # default PEM, 1.0.0 up
openssl pkey -in id_rsa -pubout -out pub.der -outform der # DER, 1.0.0
InvalidKeyException is throw while trying to read SSLeay Format private key.
Please find below the details:-
I have a SSLeay Format private key.The pem format begins with the below file
-----BEGIN RSA PRIVATE KEY-----
I am writing the code to get the private key saved in a byte format and convert the same to PrivateKey.
Variable privateKeyBytes contains the private key in byte format/
String pkStrFormat = new String(privateKeyBytes, "UTF-8");
pkStrFormat = pkStrFormat.replaceAll("(-----BEGIN RSA PRIVATE KEY-----\\r?\\n|-----END RSA PRIVATE KEY-----+\\r?\\n?)","");
byte[] keyBytesOfPrivateKey = org.bouncycastle.util.encoders.Base64
.decode(pkStrFormat.getBytes());
KeyFactory ecKeyFac = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytesOfPrivateKey);
PrivateKey priKey = ecKeyFac.generatePrivate(keySpec);
I am getting the below exception:-
Caused by: java.security.InvalidKeyException: IOException : version mismatch: (supported: 00, parsed: 01
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:350)
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:356)
The code works fine when PKCS8 Format keys are used.
PEM files starting with BEGIN RSA PRIVATE KEY are PKCS#1, not PKCS#8. PKCS#1 is essentially PKCS#8 for fixed algorithm RSA and therefore with algorithm identifier removed. Either convert your key like in https://superuser.com/questions/606215/openssl-pkcs8-default-format-gives-rsa-private-key:
openssl pkcs8 -topk8 -inform pem -in file.key -outform pem -nocrypt -out file.pem
or use PEMParser similar to Bouncy Castle : PEMReader => PEMParser (without the password stuff)
I am getting InvalidKeyException trying to initialize a Signature object:
java.security.InvalidKeyException: Key is too short for this signature algorithm
The code:
String pkcs8 = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA34N+ujANvgJ0vc696v2T/L3QUxwNf5VEf9sO/NESOBx9ZNhTHKtmY3vdmW1LVmT07vxVlaMgRhxG90h/HKCD7wIDAQABAkB2kN2PzN/tVIYzDdGnLz7qipJRFAeBD2CX5k9sA0gD5PLtpV0IVxYvSw7rUAOR/GywklF+QWKYwfCqkhMkEJMRAiEA+8fQcNEajDWB/R2VgPPWA8indGQdZT8m9lvo0xYD97kCIQDjQmkd82+UPlRB+g7GwTJw9GIiRvdps3yIKZlCKfHc5wIhAJCDb7BRVNuFGscdY+JQEla5pOO5UuX6CXL97fS6fiyBAiBRFKKYUwAeLda161dWRhuO/UH95L/k8Gqf0eeiGYD3RQIgEiAhiX1quSuBL7LrLGISGyJVy0dw+IXosqFHYeutmEI='
KeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8.decodeBase64())
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SunRsaSign")
PrivateKey pk = keyFactory.generatePrivate(keySpec)
Signature signature = Signature.getInstance("SHA512withRSA", "SunRsaSign")
signature.initSign(pk) // <--- InvalidKeyException
This is how I got the private key pkcs8:
# generate a private key just for this example
openssl genrsa 512 > mykey.pem
# convert it into pkcs8 format to be able to read it from Java later
openssl pkcs8 -topk8 -inform pem -in mykey.pem -outform pem -nocrypt -out file.pkcs8
This is how the PrivateKey looks stdout'ed:
Sun RSA private CRT key, 512 bits
modulus: 11706359850928035656926954612512379852454997399434114135854653766733637189933721115314465909375387122765789791657314272666480346477870633114913813167113199
public exponent: 65537
private exponent: 6209799048133316441293705496192881663344339603450371209133573984169170039947484349841188666943972061768383840284881642579217732240489331444594222111429393
prime p: 113883566165066111166981826386356612269934395331161452768365784963361173403577
prime q: 102792354025518497728065227780488381725246951885773034739853555051227644026087
prime exponent p: 65365278008836639419826790688453702902877034572485301544697611535190715149441
prime exponent q: 36673799866101187327427577642604625501620828371654868216232903920042186438469
crt coefficient: 8198401844921780663468999895368137692410993828212557924743840907863587133506
How to get that signing working?
JDK 1.6
This was a known bug. Descriptions says:
Signature algorithms, such as "SHA384withRSA" and "SHA512withRSA", require that the hash length should be less than the key size. If the RSA key size is 512 bits, it will not be able to use with the SHA384 and SHA512.
Though it was reported for JDK 7, I suspect you could stumble over this bug too. Try to generate a key of a bigger size (1024 and more).