I have a PEM encoded Elliptic Curve public key that I'm trying to load into Bouncy Castle and everything I tried so far is failing. This is an example of the key I'm trying to load:
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBhsFCcWY2GaiN1BjPEd1v+ESKO6/0
D0sUR4y1amHnOr3FZx6TdqdoSBqxownQrnAKGCwagGxUb7BWwPFgHqKQJHgBq+J7
F+6m5SKAEL1wS5pqya91N7oudF3yFW8oZRE4RQRdSLl3fV2aVXKwGDXciwhUhw8k
x5OS4iZpMAY+LI4WVGU=
-----END PUBLIC KEY-----
It is generated by NodeJS Crypto module and the curve name is secp521r1. It's later on encoded into PEM by the npm package key-encoder. I already used it in JavaScript (ClojureScript actually) to verify a signature and now I need to verify the signature on the server with Java (Clojure actually).
I tried removing the guards from the key, coverting to a byte[] and creating a X509EncodedKeySpec. That didn't work. It crashed with:
InvalidKeySpecException encoded key spec not recognised org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePublic (:-1)
The code to I'm using to load the key:
KeyFactory.
getInstance("ECDSA", "BC").
generatePublic(new X509EncodedKeySpec(publicKey.getBytes()))
Just in case, this is my Clojure code:
(-> (KeyFactory/getInstance "ECDSA")
(.generatePublic (X509EncodedKeySpec. (.getBytes public-key))))
I also tried PKCS8EncodedKeySpec but I got the error:
InvalidKeySpecException key spec not recognised org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePublic (:-1)
I also tried this method here: https://gist.github.com/wuyongzheng/0e2ed6d8a075153efcd3#file-ecdh_bc-java-L47-L50 but when running decodePoint I get the error:
IllegalArgumentException Invalid point encoding 0x4d org.bouncycastle.math.ec.ECCurve.decodePoint (:-1)
when I removed the guards and:
IllegalArgumentException Invalid point encoding 0x2d org.bouncycastle.math.ec.ECCurve.decodePoint (:-1)
with the guards on.
Any ideas what I'm doing wrong or how to fix it?
Also, in case it helps, this is the private key:
-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEEjNeo52qeffbIQvSxRcWAPlyJjeEOov2JNxxwWKCtlowi07HsYNNyE
jFDdSn8tSYAGx0rROrgpGuuJoG0zarPKz6AHBgUrgQQAI6GBiQOBhgAEAYbBQnFm
NhmojdQYzxHdb/hEijuv9A9LFEeMtWph5zq9xWcek3anaEgasaMJ0K5wChgsGoBs
VG+wVsDxYB6ikCR4AaviexfupuUigBC9cEuaasmvdTe6LnRd8hVvKGUROEUEXUi5
d31dmlVysBg13IsIVIcPJMeTkuImaTAGPiyOFlRl
-----END EC PRIVATE KEY-----
and everything seems to be valid:
$ openssl ec -in private.pem -pubout
read EC key
writing EC key
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBhsFCcWY2GaiN1BjPEd1v+ESKO6/0
D0sUR4y1amHnOr3FZx6TdqdoSBqxownQrnAKGCwagGxUb7BWwPFgHqKQJHgBq+J7
F+6m5SKAEL1wS5pqya91N7oudF3yFW8oZRE4RQRdSLl3fV2aVXKwGDXciwhUhw8k
x5OS4iZpMAY+LI4WVGU=
-----END PUBLIC KEY-----
Doing a bit of massaging I finally managed to load it:
(require '[clojure.string :as s])
(import '[java.security KeyFactory]
'[java.security.spec X509EncodedKeySpec]
'[java.util Base64])
(def public-key "-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBhsFCcWY2GaiN1BjPEd1v+ESKO6/0
D0sUR4y1amHnOr3FZx6TdqdoSBqxownQrnAKGCwagGxUb7BWwPFgHqKQJHgBq+J7
F+6m5SKAEL1wS5pqya91N7oudF3yFW8oZRE4RQRdSLl3fV2aVXKwGDXciwhUhw8k
x5OS4iZpMAY+LI4WVGU=
-----END PUBLIC KEY-----")
(as-> public-key key
(s/replace key "-----BEGIN PUBLIC KEY-----" "")
(s/replace key "-----END PUBLIC KEY-----" "")
(s/replace key #"\s" "")
(.decode (Base64/getDecoder) key)
(X509EncodedKeySpec. key)
(.generatePublic (KeyFactory/getInstance "ECDSA" "BC") key))
Since you have BC, it can dePEMify instead of doing it 'by hand' (I only do plain Java):
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Reader rdr = new StringReader("-----BEGIN PUBLIC KEY-----\n"
+"MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBhsFCcWY2GaiN1BjPEd1v+ESKO6/0\n"
+"D0sUR4y1amHnOr3FZx6TdqdoSBqxownQrnAKGCwagGxUb7BWwPFgHqKQJHgBq+J7\n"
+"F+6m5SKAEL1wS5pqya91N7oudF3yFW8oZRE4RQRdSLl3fV2aVXKwGDXciwhUhw8k\n"
+"x5OS4iZpMAY+LI4WVGU=\n" +"-----END PUBLIC KEY-----\n"); // or from file etc.
org.bouncycastle.util.io.pem.PemObject spki = new org.bouncycastle.util.io.pem.PemReader(rdr).readPemObject();
PublicKey key = KeyFactory.getInstance("EC","BC").generatePublic(new X509EncodedKeySpec(spki.getContent()));
System.out.println (key.getAlgorithm() + " " + ((ECPublicKey)key).getW().toString());
Example output:
EC java.security.spec.ECPoint#47244700
FYI, PKCS8 encoding is only for private keys; see javadoc for java.security.Key.getFormat()
Related
This question already has answers here:
"Invalid privatekey" when using JSch
(9 answers)
Closed 3 years ago.
I'm setting up SFTP delivery of files to a server using public/private keys generated with ssh-keygen on mac. The private key I generated looks different from other private keys already in the codebase (it doesn't have headers and it's an "OPENSSH PRIVATE KEY" instead of "RSA PRIVATE KEY".
My java FtpUtil is throwing invalid privatekey error.
Is this because the key I generated is not correctly formatted in some way? Is there a way to change this private key to use headers?
This is what other private keys in the codebase look like:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,E26BE59A622AEDE6A899FE18AD369AA0
<key text>
-----END RSA PRIVATE KEY-----
This is what my private key looks like:
-----BEGIN OPENSSH PRIVATE KEY-----
<key text>
-----END OPENSSH PRIVATE KEY-----
I'm getting the following exception thrown:
Caused by: com.jcraft.jsch.JSchException: invalid privatekey: [B#5c0f79f0
at com.jcraft.jsch.KeyPair.load(KeyPair.java:664)
at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:46)
at com.jcraft.jsch.JSch.addIdentity(JSch.java:442)
at org.springframework.integration.sftp.session.DefaultSftpSessionFactory.initJschSession(DefaultSftpSessionFactory.java:410)
at org.springframework.integration.sftp.session.DefaultSftpSessionFactory.getSession(DefaultSftpSessionFactory.java:385)
... 46 more
It looks like this private key is not correctly formatted indeed.
Maybe it would be better to describe how did you generate private key and how is it processed within code?
Anyway if it's possible you can try to generate encrypted RSA private key using following:
openssl genrsa -des3 -out private_key.pem 2048
I'm having some trouble with using a public key from Java to encrypt within NodeJS.
I've created an RSA key within keytool and output the public key as a base64 encoded string.
Base64.Encoder encoder = Base64.getEncoder();
byte[] pubKeyBytes = publicKey.getEncoded();
String pubKeyBase64 = encoder.encodeToString(pubKeyBytes);
I then took the the base64 encoded public key, wrapped it in -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- and used it to encrypt, then base64 encode a string within node.
const encryptedBuffer = crypto.publicEncrypt(encodedPublicKey, buffer);
const encryptedEncodedText = encryptedBuffer.toString("base64");
When I try to decrypt the encryptedEncodedText (base64 decoding first) within Java, it throws a BadPadding exception. Interestingly, if I export the private key to a PEM, Node can't decrypt with it either.
Any help would be appreciated!
After much searching and testing, it turned out to be a padding issue.
The public key provided to Node did was not being passed a passphrase, so it defaulted to RSA_PKCS1_OAEP_PADDING as per the documentation:
crypto.publicEncrypt
When decrypting on the Java side, it is necessary to inform Java of that padding as follows:
Cipher cipher = Cipher.getInstance("RSA/NONE/OAEPPadding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
This fixed the issue, and allowed messages to be exchanged between Java and NodeJS.
I tried to reproduce your problem. For me, it works fine. I suspect the only problematic step can be when you are creating the PEM file manually. Be sure to:
If you are copying the key string from any console, remove unwanted characters from the beginning and/or end.
And a new line character (\n) after -----BEGIN PUBLIC KEY----- and before -----END PUBLIC KEY-----.
like below:
var key = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqwEldwNI5s1LkUzwyQZkPQQFpgtj29W7RcHgdwAbUOe31Q8bybAgzg5cUMqdIQlQHq6S5dxsSJBTDCZozSu+pxtKqRLz0JjtCTZD5gS+CJR9DlXH5GgJt+KDiO6olbKiVsP/tsMPgRCFKUMiMKU+dA06dwrUqJlC1k/JzuYVrbwIDAQAB\n-----END PUBLIC KEY-----"
var plaintext = "hello world"
var buffer = new Buffer(plaintext, "utf8")
console.log(crypto.publicEncrypt(key, buffer))
To avoid all these issues, you can directly generate PEM file from Java using the bouncycastle library.
Using the JWT Java library and producing a token with the RS256 algorithm, I always get an invalid signature with the jwt.io debugger. Here is my example code, I tried to make it as simple as possible to start with my current project:
// Create a legitimate RSA public and private key pair:
KeyPair kp = RsaProvider.generateKeyPair();
PublicKey publicKey = kp.getPublic();
PrivateKey privateKey = kp.getPrivate();
String jwt = Jwts.builder().setSubject("Joe").signWith(SignatureAlgorithm.RS256, privateKey).compact();
This code is inspired from the test class here.
Any idea what I could be missing?
The jwt.io debugger expects that you provide the public key associated with the private key used to sign the token encoded in the Public Key file (PKCS#8) format.
Ensure that you specify it using exactly that format, an example follows:
-----BEGIN PUBLIC KEY-----
BASE64 DATA
-----END PUBLIC KEY-----
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)
In my application I have a AsymmetricCipherKeyPair keyPair and KeyParameter key. From that, I need to generate a PKCS#8 representation of the private key in the form
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
Can anybody give me a hint on how I could accomplish that?
You can use org.bouncycastle.crypto.util.PrivateKeyInfoFactory to create a PrivateKeyInfo object from the private KeyParameter. getEncoded() on that will return the bytes that represent the PKCS#8 version of the key.
The PEM headings you give do suggest you might want an OpenSSL encoding instead - in that case you should be able to use the BC PEMWriter in conjunction with the PrivateKeyInfo object, or in conjunction with a PrivateKey object generated using KeyFactory with the encoding of PrivateKeyInfo passed in as a PKCS8EncodedKeySpec (sort of depends on what version of BC you are using).
Regards,
David