Following this discussion it's a simple tutorial how to sign a string by using ECDSA algorithm in java without using any third-party libraries. But the question is:
How can i convert the public and the private key into a string ? (Because i want to send them into a database).
Can somebody help me create a simple tutorial of how to verify the message by using ECDSA algorithm in java ? at this point i need to include the signature and public key as the verification method.
Here's my scenario in my java code, assume that there's a sender side and the recipient side:
Sender side
package sender;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
public class Sign {
public static void main(String[] args) throws Exception {
/*
* Generate a key pair
*/
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(256, random);
KeyPair pair = keyGen.generateKeyPair();
/*
Generate the private and the public key
*/
PrivateKey priv = pair.getPrivate();
/*
*and then Convert the priv key into a String;
*HOW can i do that ? this what i'm asking
*/
PublicKey pub = pair.getPublic();
/*
Convert the pub key into a String;
HOW can i do that ? this what i'm asking
*/
/*
-------Encrypt the pub and the priv key, i do with my own code
-------Store the enrypted pub & priv key into the database
-------I'm doing this with my own code
*/
/*
* Create a Signature object and initialize it with the private key
*/
Signature dsa = Signature.getInstance("SHA1withECDSA");
dsa.initSign(priv);
String str = "This is string to sign";
byte[] strByte = str.getBytes("UTF-8");
dsa.update(strByte);
/*
* Now that all the data to be signed has been read in, generate a
* signature for it
*/
byte[] realSig = dsa.sign();
System.out.println("Signature: " +
new BigInteger(1, realSig).toString(16));
/*
and Then i'm storing this signature into my database.
i have done with this
*/
}
}
Recipient side
package recipient;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
public class Verify {
public static void main(String[] args) throws Exception {
/*
Step one, taking public key from the database.
Step two, receive the message + signature.
Step three, split the message and signature into an "array[0]" for message,
and "array[1] for the signature"
Verify the signature <--- Here's what im asking to anybody,
how can i do, i mean the sample code ?
*/
}
}
Sorry for my bad English :D
You're asking a lot of different questions about dealing with ECDSA. I will address your first question about database storage here. I recommend you do some additional research on the mechanics of ECDSA if you want to learn about how to properly use it. Examples given here would be hard to follow out of context anyway.
To store keys as a string, you must first retrieve the byte array representing the key in its encoded format (note: encoded not encrypted). This can be done by using the getEncoded() method from class Key which is the superinterface of both PublicKey and PrivateKey.
Example:
PrivateKey key = // ...
byte[] enc_key = key.getEncoded();
// Byte array to string
StringBuilder key_builder = new StringBuilder();
for(byte b : enc_key){
key_builder.append(String.format("%02x", b));
}
String serialized_key = key_builder.toString();
To load the key again from a database you parse the string to a byte array, pass it into the appropriate key specification and then retrieve it by using a key factory.
Example:
String serialzed_key = // ...
byte[] encoded_key = // serialzed_key -> byte array conversion
// If key is private, use PKCS #8
PKCS8EncodedKeySpec formatted_private = new PKCS8EncodedKeySpec(encoded_key);
// or, if key is public, use X.509
X509EncodedKeySpec formatted_public = new X509EncodedKeySpec(encoded_key);
// Retrieve key using KeyFactory
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey pub = kf.generatePublic(formatted_public);
PrivateKey priv = kf.generatePrivate(formatted_private);
If all you mean to do is to use ECDSA as a signature algorithm, verification is identical to signing using using the verify methods instead of the sign methods, as follows:
byte[] message_hash = // ...
byte[] candidate_message = // ...
PublicKey pub = // ...
Signature dsa = Signature.getInstance("SHA1withECDSA");
dsa.initVerify(pub);
dsa.update(candidate_message);
boolean success = dsa.verify(message_hash);
Related
I have a situation where I have to store the encrypted from of a text in an excel sheet and my java code should read that encrypted text from a excel sheet and convert it back to original text and hit the server with original plain text.
I'm trying to achieve this with AES encryption/decryption logic but I'm not able to achieve it as every time I convert the plain text into encrypted format for it to be stored in the excel sheet it results in a different encrypted string each time so I'm not able to decrypt it back to original, what I want is for a same static string/text my AES encrypted text should also be static.
I know even if my encrypted string is dynamic I can convert it back to decrypted format but for my case what I want is my encrypted text should also remain static.
How can I achieve this?
Ex. If my plain text is "This is a question" my encrypted string is different each time like below
Enter
This is a question
Encrypted Data : mFsue8JGwLcJQTiBzM0HLVvdDXKPNGsG/O7N60joH+Ozgg==
Decrypted Data : This is a question
Enter
This is a question
Encrypted Data : FdBz3cGS4NphK14Fw8Me4daM4lVzdrK47WUMSRiUVe+juQ==
Decrypted Data : This is a question
See the encrypted data's are different each time. I want that to be static
The classes which I'm using are as below (please note these are not my exact classes but they implement the same logic which I have used. These example classes may have some non used variables and methods, I have mentioned just for reference)
Method.java
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class Method {
private static SecretKey key;
private final int KEY_SIZE = 128;
private final int DATA_LENGTH = 128;
private Cipher encryptionCipher;
/*
* public void init() throws Exception { KeyGenerator keyGenerator =
* KeyGenerator.getInstance("AES"); keyGenerator.init(KEY_SIZE); key =
* keyGenerator.generateKey();
*
*
* }
*/
// String to Key
public static void getKeyFromPassword(String toEnc, String salt) throws
NoSuchAlgorithmException, InvalidKeySpecException { SecretKeyFactory factory
= SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); KeySpec spec = new
PBEKeySpec(toEnc.toCharArray(), salt.getBytes(), 65536, 128); SecretKey
originalKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(),"AES");
key= originalKey; }
/*
* public static void convertStringToSecretKeyto(String string) {
*
*
* byte[] bytesEncoded = Base64.getEncoder().encode(string.getBytes()); byte[]
* decodedKey = Base64.getDecoder().decode(string); key = new
* SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
* System.out.println(bytesEncoded); System.out.println(decodedKey);
*
* }
*/
public String encrypt(String data) throws Exception {
byte[] dataInBytes = data.getBytes();
encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = encryptionCipher.doFinal(dataInBytes);
return encode(encryptedBytes);
}
public String decrypt(String encryptedData) throws Exception {
byte[] dataInBytes = decode(encryptedData);
Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(DATA_LENGTH,
encryptionCipher.getIV());
decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] decryptedBytes = decryptionCipher.doFinal(dataInBytes);
return new String(decryptedBytes);
}
private String encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
private byte[] decode(String data) {
return Base64.getDecoder().decode(data);
}
}
Main.class
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.util.Base64;
import java.util.Scanner;
public class Cypher {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
Method aes_encryption = new Method();
// aes_encryption.init();
Method.getKeyFromPassword("Texty text","Salty salt");
System.out.println("Enter");
Scanner sc= new Scanner(System.in);
String s= sc.nextLine();
String encryptedData = aes_encryption.encrypt(s);
String decryptedData = aes_encryption.decrypt(encryptedData);
System.out.println("Encrypted Data : " + encryptedData);
System.out.println("Decrypted Data : " + decryptedData);
} catch (Exception ignored) {
}
}
}
This is not really an implementation issue; your issue is with the used encryption scheme.
The nonce or IV are used to randomize your ciphertext. This gives you an important property: identical plaintext will encrypt to randomized ciphertext. This is required for a cipher to be secure, as you could otherwise identify identical plaintext messages.
Now you could just fix the IV or nonce, but that means that you will leak information early: if the first blocks of plaintext are identical then those blocks will result in identical ciphertext. Instead you can use an encryption scheme where each of the ciphertext bits depend on all of the plaintext bits.
There are several schemes that offer this kind of property:
biIGE mode: the bi-directional infinite garble extension mode is an encryption mode that runs both forward and then backwards. It is an odd mode that isn't used or implemented much;
FPE modes: format preserving encryption can be used to encrypt X plaintext bits into exactly X ciphertext bits, it is mainly used for smaller plaintext messages such as credit cards (so that there aren't any additional storage requirements for encrypted credit card numbers);
AES-SIV mode: synthetic IV mode is probably the best option, it can encrypt large(r) messages, and offers authenticity as well as the MAC / authentication tag doubles as IV. It has the drawback that it does require additional room for the synthetic IV / authentication tag. It is also a two-pass mode (all plaintext bytes are visited twice), which means that it won't be as performant as other generic modes.
There is AES-GCM-SIV for a performant option of the latter.
Of course, as indicated by others, you really want to make sure that you need this kind of functionality: not having messages encrypt to the same ciphertext is a rather fundamental security property of a cipher.
I am loading a public key in java using bouncy castle library but always getting error Invalid point encoding 0x45.
The public key is generated at client side using C# CNG APIs.
Java method 1:
public PublicKey loadPublicKey(String encodedPublicKey)
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
byte[] keybytes = java.util.Base64.getDecoder().decode(encodedPublicKey);
Security.addProvider(new BouncyCastleProvider());
ECNamedCurveParameterSpec params = ECNamedCurveTable.getParameterSpec("P-256");
ECPublicKeySpec keySpec = new ECPublicKeySpec(params.getCurve().decodePoint(keybytes), params);
return new BCECPublicKey("ECDH", keySpec, BouncyCastleProvider.CONFIGURATION);
}
Method 2
public PublicKey loadPublicKey(String pKey) throws Exception {
byte[] keybytes = java.util.Base64.getDecoder().decode(pKey);
Security.addProvider(new BouncyCastleProvider());
ECParameterSpec params = ECNamedCurveTable.getParameterSpec("P-256");
ECPublicKeySpec pubKey = new ECPublicKeySpec(params.getCurve().decodePoint(keybytes), params);
KeyFactory kf = KeyFactory.getInstance("ECDH", "BC");
return kf.generatePublic(pubKey);
}
Exception
java.lang.IllegalArgumentException: Invalid point encoding 0x45
at org.bouncycastle.math.ec.ECCurve.decodePoint(ECCurve.java:443)
Below method to create public key
public static (byte[] publicKey, byte[] privateKey) CreateKeyPair()
{
using (ECDiffieHellmanCng cng = new ECDiffieHellmanCng(
// need to do this to be able to export private key
CngKey.Create(
CngAlgorithm.ECDiffieHellmanP256,
null,
new CngKeyCreationParameters
{ ExportPolicy = CngExportPolicies.AllowPlaintextExport })))
{
cng.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
cng.HashAlgorithm = CngAlgorithm.Sha256;
// export both private and public keys and return
var pr = cng.Key.Export(CngKeyBlobFormat.EccPrivateBlob);
var pub = cng.PublicKey.ToByteArray();
return (pub, pr);
}
}
Public Key generated RUNLMSAAAAHddHI6TOEDG/Ka7naBbLQH0u/DSFfbKJI2w0WSoxrmFkwKm1tktz4wD0rqnwkZp8FwdHJ+8OVrTcpDMmxrwvS6
The key which I am receiving at java is of 72 bytes. But I think bouncy castle java supports 64 bytes of key.
I was also looking into this but did not get any help
The C# code exports the public key as a Base64 encoded EccPublicBlob whose format is described in the link given in the question:
The first 4 bytes 0x45434B31 denote in little endian order a public ECDH key for curve P-256, the following 4 bytes are in little endian order the key length in bytes (0x20000000 = 32), the rest are the x and y coordinates of the EC point i.e. the public key, 32 bytes each.
It is striking that in the key you posted, the second 4 bytes are 0x20000001, but the x and y coordinates are 32 bytes each. Possibly there is a copy/paste error here. Anyway, with the posted C# code, I cannot reproduce a key that has a value other than 0x20000000 in the second 4 bytes.
Java/BC does not directly support importing an EccPublicBlob (which is MS proprietary), but it does support importing an uncompressed public key. This results when the x and y coordinates are concatenated and 0x04 is used as prefix. The import with Java/BC is then possible as follows:
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.ECPointUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
...
public static PublicKey getPubKeyFromCurve(byte[] uncompRawPubKey, String curveName) throws Exception {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(curveName);
ECNamedCurveSpec params = new ECNamedCurveSpec(spec.getName(), spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = ECPointUtil.decodePoint(params.getCurve(), uncompRawPubKey);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
KeyFactory kf = KeyFactory.getInstance("ECDH", new BouncyCastleProvider());
ECPublicKey pubKey = (ECPublicKey) kf.generatePublic(pubKeySpec);
return pubKey;
}
Test (assuming EccPublicBlob is Base64 encoded like the posted one):
import java.util.Base64;
...
String publicKeyBlob = "RUNLMSAAAAAFzw4IGY4N8PKVt0MGF38SAKU5ixJhptVUdrWzuPhFDOcj/2k4SlGRN1RpRMbar9Iu7Uvcx7Vtm8Wa0HSzWJdE";
byte[] rawPublic = new byte[65];
rawPublic[0] = 0x04;
System.arraycopy(Base64.getDecoder().decode(publicKeyBlob), 8, rawPublic, 1, 64);
PublicKey pub = getPubKeyFromCurve(rawPublic, "P-256");
System.out.println(Base64.getEncoder().encodeToString(pub.getEncoded())); // MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBc8OCBmODfDylbdDBhd/EgClOYsSYabVVHa1s7j4RQznI/9pOEpRkTdUaUTG2q/SLu1L3Me1bZvFmtB0s1iXRA==
The test imports the EccPublicBlob and exports it as a Base64 encoded DER key in X.509/SPKI format. This can be read with an ASN.1 parser, e.g. https://lapo.it/asn1js/, and thus be verified.
Note that C# also supports the export of other formats. However, this depends on the version. E.g. as of .NET Core 3.0 there is the method ExportSubjectPublicKeyInfo() that exports the public key in X.509/SPKI format, DER encoded. This format and encoding can be imported directly into Java using X509EncodedKeySpec (even without BouncyCastle).
In other versions of C#, BouncyCastle for C# can be used for the export, which also supports the X.509/SPKI format.
Since you didn't post your .NET version, it's unclear what specific alternatives exist for you.
Keep in mind that an ECDH key for P-256 can also be created more simply with:
ECDiffieHellmanCng cng = new ECDiffieHellmanCng(ECCurve.NamedCurves.nistP256)
or cross-platform with
ECDiffieHellman ecdh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256)
Suppose I have the following JWK as the deserialised body of some JWS (RFC7515), where the modulus n is partially omitted for display purposes
{
"kty": "RSA",
"e": "AQAB",
"kid": "KAgNpWbRyy9Mf2rikl498LThMrvkbZWHVSQOBC4VHU4",
"n": "llWmHF8XA2KNLdmxOP3kxD9OY76p0Sr37j..."
}
And the JWS header specifies both the alg and kid fields required for signature verification.
How do I construct an RSA public key from this JWK so that I can verify the signature? Having looked at some related questions, I have the following Java implementation that attempts to build an RSA public key from the n and e fields within the JWK
public void someMethod(){
String exjws ="eyJhbGciOiJSUzI1NiIsImtpZCI6IktBZ05wV2JSeXk5TWYycmlrbDQ5OExUaE1ydmtiWldIVlNRT0JDNFZIVTQiL"
+ "CJodG0iOiJwb3N0IiwiaHR1IjoiL3R4IiwidHMiOjE2MDM4MDA3ODN9.eyJjYXBhYmlsaXRpZXMiOltdLCJjbGllbnQiOnsia2V5Ijp7Imp3ayI6eyJrdHkiOiJSU0EiLCJ"
+ "hbGciOiJSUzI1NiIsImUiOiJBUUFCIiwia2lkIjoiS0FnTnBXYlJ5eTlNZjJyaW"
+ "tsNDk4TFRoTXJ2a2JaV0hWU1FPQkM0VkhVNCIsIm4iOiJsbFdtSEY4WEEyS05MZG14T1Aza3hEOU9ZNzZwMFNyMzdqZmh6OTRhOTN4bTJGTnFvU1BjUlpBUGQwbHFEUzhO"
+ "M1VpYTUzZEIyM1o1OU93WTRicE1fVmY4R0p2dnB0TFdueG8xUHlobVByIC0gZWNkU0NSUWRUY19aY01GNGhSVjQ4cXFsdnVEMG1xdGNEYklrU0JEdmNjSm1aSHdmVHBESG"
+ "luVDh0dHZjVlA4VmtBTUFxNGtWYXp4T3BNb0lSc295RXBfZUNlNXBTd3FIbzBkYUNXTktSI"
+ "C0gRXBLbTZOaU90ZWRGNE91bXQ4TkxLVFZqZllnRkhlQkRk"
+ "Q2JyckVUZDR2Qk13RHRBbmpQcjNDVkN3d3gyYkFRVDZTbHhGSjNmajJoaHlJcHE3cGM4clppYjVqTnlYS3dmQnVrVFZZWm96a3NodCAtIExvaHlBU2FLcFlUcDhMdE5aIC0gdyAifSw"
+ "icHJvb2YiOiJqd3MifSwibmFtZSI6Ik15IEZpcnN0IENsaWVu"
+ "dCIsInVyaSI6Imh0dHA6XC9cL2xvY2FsaG9zdFwvY2xpZW50XC9jbGllbnRJRCJ9LCJpbnRlcmFjdCI6eyJzdGFydCI6WyJyZWRpcmVjdCJ"
+ "dLCJmaW5pc2giOnsibWV0aG9kIjoicmVkaXJlY3QiLCJub25jZSI6ImQ5MDIxMzg4NGI4NDA5MjA1MzhiNWM1MSIsInVyaSI6Imh0dHA6XC9cL2xvY2FsaG9zdFwvY2xpZW50"
+ "XC9yZXF1ZXN0LWRvbmUifX0sImFjY2Vzc190b2tlbiI6eyJhY2Nlc3MiOlt7ImFjdGlvbnMiOlsicmVhZCIsInByaW50Il0sImxvY2F0aW9ucyI6WyJodHRwOlwvXC9sb2Nhb"
+ "Ghvc3RcL3Bob3RvcyJdLCJkYXRhdHlwZXMiOlsibWV0YWRhdGEiLCJpbWFnZXMiXSwidHlwZSI6InBob3RvLWFwaSJ9XX0sInN1YmplY3QiOnsic3ViX2lkcyI6WyJpc3Nfc3"
+ "ViIiwiZW1haWwiXX19.LUyZ8_fERmxbYARq8kBYMwzcd8GnCAKAlo2ZSYLRRNAYWPrp2XGLJOvg97WK1idf_LB08OJmLVsCXxCvn9mgaAkYNL_ZjHcusBvY1mNo0E1sdTEr31"
+ "CVKfC-6WrZCscb8YqE4Ayhh0Te8kzSng3OkLdy7xN4xeKuHzpF7yGsM52JZ0cBcTo6WrYEfGdr08AWQJ59ht72n3jTsmYNy9A6I4Wrvfgj3TNxmwYojpBAi"
+ "cfjnzA1UVcNm9F_xiSz1_y2tdH7j5rVqBMQife-k9Ewk95vr3lurthenliYSNiUinVfoW1ybnaIBcTtP1_YCxg_h1y-B5uZEvYNGCuoCqa6IQ";
String[] parts = exjws.split("\\.");
String payload = new Base64URL(parts[1]).decodeToString();
JsonObject jwk = JsonParser.parseString(payload).getAsJsonObject().get("client")
.getAsJsonObject().get("key").getAsJsonObject().get("jwk").getAsJsonObject();
BigInteger modulus = new BigInteger(1, new Base64URL(jwk.get("n").getAsString()).decode());
BigInteger exponent = new BigInteger(1, new Base64URL(jwk.get("e").getAsString()).decode());
byte[] signingInfo = String.join(".",parts[0],parts[1]).getBytes(StandardCharsets.UTF_8);
byte[] b64DecodedSig = new Base64(parts[2]).decode();
PublicKey pub = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent));
Signature verifier = Signature.getInstance("SHA256withRSA");
verifier.initVerify(pub);
verifier.update(signingInfo);
boolean okay = verifier.verify(b64DecodedSig);
System.out.println(okay);
}
The result of verify() is currently returning false.
I have tried generating RSA key pairs, signing and verifying using the generate keys and that worked. I suspect that my issue is that the constructed RSA key in the code above is wrong somehow. Any help appreciated.
EDIT
JSON library is Gson
JWK library is nismus-jose-jwt which provides Base64 and Base64URl
From the discussion with #Topaco in the comments:
The code in the question does successfully construct the RSA public key from the modulus n and the exponent e.
However, the Signature.verify() invocation returns false because the public key specified within the JWK in the request does not match the key used to sign the request.
I'm using a JWT library for this task (I know it's a bit of overkill, but it's working...): https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/
They provide an easy-to-use interface and below you find a sample code that generates an RSA key pair and print out the public key, then convert this public key to JWK format (print out as well) followed by the "final" conversion from JWK format to Java's RSAPublicKey format - the original public key is identical to the "double converted" new public key:
RSA converting between Java keys and JWK keys
rsaPublicKey:
Sun RSA public key, 2048 bits
params: null
modulus: 26357316308141920246706187189832816184464669247574329274151600014987833498613081305553907167489639882497901020042668019684731733203493602029515963993706600847721534104752032126130361576446376774646308346723928903197131878300000630951097323650413651136361398382777541880437222482333326912353931641531474275115618239345544686220478026629436520040030688170796270228708165193211856330191604982765859609032534442818720461696078063893165568447273933782242398761845509532495844704423556107073518195030616464416564865911759432179943444938978123330642161124144169230685337930276039065398676755273689018037129036026967769360801
public exponent: 65537
jwkRsaPublicKey (JWK-Format)
{"kty":"RSA","e":"AQAB","n":"0MpIJE0koFXx5sZOOI-XsEMMQfvwHkizj1jaYGATZEz0YTdf-WUDrO2JeELP1UvwHRbD5Mt0y0IvSYEjG4btVoZWjoJwEIz-bT7rtJNnZ9bjY8vMYloCUM81nTLve0sVRqkjw3S7IFXsTXx05vkY7oV25Z9YeZH2f5b1ph3JGcTrQF8d3XZy6XAM_KaWWOPTwzoNtr3JQQzUJ2vS_BGCJyiVU1cEB0RlRu1Gd9EPqDcMGAN2nMoHUuQw0qNTd-ms0Du0RGnktRDpcm3SXLsUt2J4adbPp02eXjn-TDTISzR6FywC0sAL6ED0EqWhOgqEf7EftctSJGGdgLOkmL4poQ"}
rsaPublicKeyFromJwk:
Sun RSA public key, 2048 bits
params: null
modulus: 26357316308141920246706187189832816184464669247574329274151600014987833498613081305553907167489639882497901020042668019684731733203493602029515963993706600847721534104752032126130361576446376774646308346723928903197131878300000630951097323650413651136361398382777541880437222482333326912353931641531474275115618239345544686220478026629436520040030688170796270228708165193211856330191604982765859609032534442818720461696078063893165568447273933782242398761845509532495844704423556107073518195030616464416564865911759432179943444938978123330642161124144169230685337930276039065398676755273689018037129036026967769360801
public exponent: 65537
This code has no exception handling and is for educational purpose only:
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.RSAKey;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
public class ConvertRsaKeysJavaJwk {
public static void main(String[] args) throws NoSuchAlgorithmException, JOSEException {
System.out.println("RSA converting between Java keys and JWK keys");
// generate a RSA key pair
KeyPair rsaKeyPair = generateRsaKeyPair(2048);
RSAPublicKey rsaPublicKey = (RSAPublicKey) rsaKeyPair.getPublic();
System.out.println("rsaPublicKey:\n" + rsaPublicKey);
// import the ecdsaPublicKey to JWK
// usage of nimbus-jose-jwt
// https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/
RSAKey jwkRsaPublicKey = new RSAKey.Builder(rsaPublicKey).build();
System.out.println("\njwkRsaPublicKey (JWK-Format)\n" + jwkRsaPublicKey);
// convert jwk to java
RSAPublicKey rsaPublicKeyFromJwk = jwkRsaPublicKey.toRSAPublicKey();
System.out.println("\nrsaPublicKeyFromJwk:\n" + rsaPublicKeyFromJwk);
}
public static KeyPair generateRsaKeyPair(int keylengthInt) throws NoSuchAlgorithmException {
KeyPairGenerator keypairGenerator = KeyPairGenerator.getInstance("RSA");
keypairGenerator.initialize(keylengthInt, new SecureRandom());
return keypairGenerator.generateKeyPair();
}
}
I am trying to achieve encryption using PGP and my encrypt method successfully encrypts the input string but when I try to decrypt it to verify if the encryption is properly done, the string doesn't get decrypted.
I tried 2 approaches:
1st approach uses FileOutputStream to write encrypted string & 2nd approach uses ByteArrayOutputStream.
FileOutputStream creates a file and I am able to decrypt it using Kleopatra. However my requirement is to just get an encrypted string (not written in a file). So when I try to decrypt the encrypted string (received after using ByteArrayOutputStream) its not working. I tried copying the string and decrypting it through tools>>clipboard in Kleopatra, but the decrypt/verify option is disabled. I tried writing the string on a file manually & through FileWriter class, but decryption fails with the error that File contains certificate & cannot be decrypted or verified.
I assume only files created directly by OutputStream gets decrypted successfully.
But I have to really check the encrypted string.
Any help would be highly appreciated.
The following full example is taken from the source code of the book "Java Cryptography: Tools and Techniques by David Hook & Jon Eaves".
The complete source code with all examples is available here: https://www.bouncycastle.org/java-crypto-tools-src.zip
The examples are showing a private-/public key creation with El Gamal or Elliptic Curves and encryption with AES-256.
In the ecExample-method I added two lines to save the encrypted string to the file "pgp-encrypted-string.dat" and then
reload the data to decypt the file and show the decrypted string.
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.io.Streams;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.util.Date;
public class PGPEncryptionExampleForSO
{
/**
* Create an encrypted data blob using an AES-256 session key and the
* passed in public key.
*
* #param encryptionKey the public key to use.
* #param data the data to be encrypted.
* #return a PGP binary encoded version of the encrypted data.
*/
public static byte[] createEncryptedData(
PGPPublicKey encryptionKey,
byte[] data)
throws PGPException, IOException
{
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom()).setProvider("BC"));
encGen.addMethod(
new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey)
.setProvider("BC"));
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
// create an indefinite length encrypted stream
OutputStream cOut = encGen.open(encOut, new byte[4096]);
// write out the literal data
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(
cOut, PGPLiteralData.BINARY,
PGPLiteralData.CONSOLE, data.length, new Date());
pOut.write(data);
pOut.close();
// finish the encryption
cOut.close();
return encOut.toByteArray();
}
/**
* Extract the plain text data from the passed in encoding of PGP
* encrypted data. The routine assumes the passed in private key
* is the one that matches the first encrypted data object in the
* encoding.
*
* #param privateKey the private key to decrypt the session key with.
* #param pgpEncryptedData the encoding of the PGP encrypted data.
* #return a byte array containing the decrypted data.
*/
public static byte[] extractPlainTextData(
PGPPrivateKey privateKey,
byte[] pgpEncryptedData)
throws PGPException, IOException
{
PGPObjectFactory pgpFact = new JcaPGPObjectFactory(pgpEncryptedData);
PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpFact.nextObject();
// find the matching public key encrypted data packet.
PGPPublicKeyEncryptedData encData = null;
for (PGPEncryptedData pgpEnc: encList)
{
PGPPublicKeyEncryptedData pkEnc
= (PGPPublicKeyEncryptedData)pgpEnc;
if (pkEnc.getKeyID() == privateKey.getKeyID())
{
encData = pkEnc;
break;
}
}
if (encData == null)
{
throw new IllegalStateException("matching encrypted data not found");
}
// build decryptor factory
PublicKeyDataDecryptorFactory dataDecryptorFactory =
new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider("BC")
.build(privateKey);
InputStream clear = encData.getDataStream(dataDecryptorFactory);
byte[] literalData = Streams.readAll(clear);
clear.close();
// check data decrypts okay
if (encData.verify())
{
// parse out literal data
PGPObjectFactory litFact = new JcaPGPObjectFactory(literalData);
PGPLiteralData litData = (PGPLiteralData)litFact.nextObject();
byte[] data = Streams.readAll(litData.getInputStream());
return data;
}
throw new IllegalStateException("modification check failed");
}
private static void elgamalExample()
throws Exception
{
byte[] msg = Strings.toByteArray("Hello, world!");
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH", "BC");
kpGen.initialize(2048);
KeyPair kp = kpGen.generateKeyPair();
PGPKeyPair elgKp = new JcaPGPKeyPair(
PGPPublicKey.ELGAMAL_ENCRYPT, kp, new Date());
byte[] encData = createEncryptedData(elgKp.getPublicKey(), msg);
byte[] decData = extractPlainTextData(elgKp.getPrivateKey(), encData);
System.out.println("elgamal encryption msg length: " + msg.length + " enc.length: " + encData.length + " dec.length: " + decData.length);
System.out.println(Strings.fromByteArray(decData));
}
private static void ecExample()
throws Exception
{
byte[] msg = Strings.toByteArray("Hello, world!");
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
kpGen.initialize(new ECGenParameterSpec("P-256"));
KeyPair kp = kpGen.generateKeyPair();
PGPKeyPair ecdhKp = new JcaPGPKeyPair(PGPPublicKey.ECDH, kp, new Date());
byte[] encData = createEncryptedData(ecdhKp.getPublicKey(), msg);
// save encrypted string
Files.write(Paths.get("pgp-encrypted-string.dat"), encData);
// load encrypted string
byte[] encDataLoad = Files.readAllBytes(Paths.get("pgp-encrypted-string.dat"));
byte[] decData = extractPlainTextData(ecdhKp.getPrivateKey(), encDataLoad);
System.out.println("ec encryption msg length: " + msg.length + " enc.length: " + encData.length + " dec.length: " + decData.length);
System.out.println(Strings.fromByteArray(decData));
}
public static void main(String[] args)
throws Exception
{
Security.addProvider(new BouncyCastleProvider());
// you need the two files bcpg-jdk15on-165.jar and bcprov-jdk15to18-165.jar to run the example
System.out.println("Example from Java Cryptography: Tools and Techniques by David Hook & Jon Eaves");
System.out.println("get source files: https://www.bouncycastle.org/java-crypto-tools-src.zip");
elgamalExample();
ecExample();
}
}
This is the short output:
Example from Java Cryptography: Tools and Techniques by David Hook & Jon Eaves
get source files: https://www.bouncycastle.org/java-crypto-tools-src.zip
elgamal encryption msg length: 13 enc.length: 601 dec.length: 13
Hello, world!
ec encryption msg length: 13 enc.length: 200 dec.length: 13
Hello, world!
Added: As I now understand you're having a String like
This string needs an encryption
and you want to encrypt it with a rsa pgp public key:
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.65
mI0EXr/nDgEEAKhB6ufAB954aBIlNjPCsryzUVLu0qkC/1RtnFHf+J6IVegV8Wi7
28V074inQcw6o6FTLtFTaLRP4+3eXNATdjGSjrvcP7k+nu50vydugHv43fPuCiZ7
6gbbMTE9gPiLPA2pS+SmQJnr9hOrD5rzwYP1yNNIsRJ9qmU5NeZyu+szABEBAAG0
DHRlc3RpZGVudGl0eYicBBABAgAGBQJev+cOAAoJEPBDuyqTbz/gY0YD/R+gDkfe
qPgNuk6iI2wLSGEeZRXr6Ru1cyG73CRvz7BjCpwWx039AdQzP9gkeo6MEj8Z0c73
obqEP8NtvvOcwC7+/QiGLTR2mgCsNhk54+iCGsvNbkpkr/rRoYZGyvb+rxui0A61
DCB1w5hdnyMg2OglFNrkaPfpNjMsTebfF5eS
=h1+m
-----END PGP PUBLIC KEY BLOCK-----
and get the encrypted string
-----BEGIN PGP MESSAGE-----
Version: BCPG v1.65
hIwD8EO7KpNvP+ABA/9JkOE9PDyS/kr/lZ1Uz+NCSe1JiNcKCXjbsUbvP8CT7Tf1
cKlgzIz1mQjdpkBtVpVhEnEjmUzFy2UCRKr4b4Wx7/1UL+370CICW5HgMoi5TgTg
MYRy5I9Uba/+JxcusjWB1JJHP4ofULziXRKLWAoSPLlglZDzSmV88hNo19rl39JZ
AbMhIS2edM9hHICefL/Yaiq90hGjKMRReVopu2tPUjNLGYP7QABAvWb3WQJMZoYT
HEsyjHxeyYQylAdYB7pWQA0++Z803iclvM3skN8FBt64ebDkqfxgbhs=
=je0r
-----END PGP MESSAGE-----
Now you'd like to decrypt this message with Kleoptatra, online (e.g. https://sela.io/pgp-en/) or in Java with the RSA pgp private key and the password 123456:
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: BCPG v1.65
lQH+BF6/5w4BBACoQernwAfeeGgSJTYzwrK8s1FS7tKpAv9UbZxR3/ieiFXoFfFo
u9vFdO+Ip0HMOqOhUy7RU2i0T+Pt3lzQE3Yxko673D+5Pp7udL8nboB7+N3z7gom
e+oG2zExPYD4izwNqUvkpkCZ6/YTqw+a88GD9cjTSLESfaplOTXmcrvrMwARAQAB
/gMDAhhcE1oF/u8YYExKGLgriK5JpUUSsMFU0AOHP9/zZQr09437V0f/F4J87+9s
G30lDRikGwynEGRnAvIVwqq2F+iarKGGHCZCRgbyufXS7VK6wE/43lR0kSwA2VIM
ll/KbQKP1cSZv0rqtJ1tGL7cDHFEwq10gM4Bn75HOKyBzE9oERRKz37noAECsAZn
xuXGlEB5noqTT00RxsHjBA5Os04CtEz9N+OMrg47IR7AzSQUe90lG2F6W71dhJ6V
jQaf7D6JFU3dOWPW1eBb5FQhgYF92CFRizJ42lDCiTfl2FQU49MlwLd2ofNneuPo
aVuPoYUNKwbasyx4fo2vh6rrMyxmncCizMExvh6GIVgYd7EK9s6Gxq/duuOvly4O
ZAyIY2MOon0bDXxAYR2q/wdQLamnP7rAR4uMu24m/iOuBj6wwTR8v8hhsFFTp/4u
tebwWzLnPyyBYStnTF5IZ9ZJeVl5S3zdzNcrP9g8yXtItAx0ZXN0aWRlbnRpdHmI
nAQQAQIABgUCXr/nDgAKCRDwQ7sqk28/4GNGA/0foA5H3qj4DbpOoiNsC0hhHmUV
6+kbtXMhu9wkb8+wYwqcFsdN/QHUMz/YJHqOjBI/GdHO96G6hD/Dbb7znMAu/v0I
hi00dpoArDYZOePoghrLzW5KZK/60aGGRsr2/q8botAOtQwgdcOYXZ8jINjoJRTa
5Gj36TYzLE3m3xeXkg==
=y/tQ
-----END PGP PRIVATE KEY BLOCK-----
and get the decrypted string:
This string needs an encryption
To encrypt/decrypt in Java fortunately there are sample files available in the BouncyCastle Github-Repo: https://github.com/bcgit/bc-java/blob/master/pg/src/main/java/org/bouncycastle/openpgp/examples/. You may need to create a new PGP-keypair using RSA (RSAKeyPairGenerator.java) or ElGamal
(DSAElGamalKeyRingGenerator.java). With the generated keys you can encrypt or decrypt using KeyBasedFileProcessor.java and neccessary PGPExampleUtil.java.
I created the RSA key files with "-a testidentity 123456" as arguments, the encryption is done with "-e -ai plaintext.txt rsa_pub.asc" and the decryption goes with "-d plaintext.txt.asc rsa_secret.asc 123456".
I am new to the EC-encryption and have some struggle with it.
I am using Java 8 and the BouncyCatle provider.
The Question I have now is:
when I generate an EC-KeyPair with the folloing code:
ECGenParameterSpec spec = new ECGenParameterSpec("secp521r1");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
kpg.initialize(spec, new SecureRandom());
return kpg.generateKeyPair();
and try to get the byte array of the public key to send it to another person, the encoded key is 158 bytes long and in the X.509 format. But I expected the X9.62 format and a keysize between 65 and 66 bytes.
Why is the public key this large and how can I encode it with the expected keysize? (I expected the keysize because I expect the key to be 521 bits long)
An ECC publickey is semantically a point on a curve; if the curve you name is implied, a point in X9.62 format is either 67 octets (Java bytes) if compressed or 133 octets if uncompressed, never any other length.
If you mean java.security.PublicKey.getEncoded() that is always in what Java calls "X.509" encoding which is actually the ASN.1 structure SubjectPublicKeyInfo (SPKI) defined in X.509 and more conveniently available in rfc5280 sec 4.1, encoded as DER. An ECC publickey on that curve in this format is is 90 or 158 octets, exactly, for uncompressed or compressed, and the Java providers (at least currently) produce the uncompressed form (although they can parse compressed).
It sounds like you may want the X9.62 compressed format, which as I said is 67 bytes (not 65 or 66). If so, you can't control point compression in the standard Java API, but the BouncyCastle implementation classes do support it, given you have key objects created by the BC provider.
First cast keypair.getPublicKey() to (corr) org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey (before 1.47 was org.bouncycastle.jce.provider.JCEECPublicKey) and then getQ() returns an org.bouncycastle.math.ec.ECPoint which has an (overloaded) getEncoded(boolean compressed) which produces what you apparently want.
For your additional but not (yet?) official question, to re-create a PublicKey object from an encoded point (compressed or not), you have two or three options depending how you count:
construct an ASN.1/DER-encoded SubjectPublicKeyInfo structure (which Java calls "X.509" format) for this curve and point, put it in X509EncodedKeySpec, and run that through an appropriate KeyFactory. Either the standard SunEC provider (assuming j7+, and not a RedHat-crippled version) or the BC provider can be used. Constructing ASN.1 encodings like SPKI by hand is difficult in general but not bad in this specific case; or given you have BC you can use its ASN.1 functionality
call the BC routines directly to do what the EC KeyFactory would do for the above input
Example code for creating a point and then using it all three ways:
// as needed in addition to standard java.security and javax.xml
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(new ECGenParameterSpec("secp521r1"));
org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey ku =
(org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey)kpg.generateKeyPair().getPublic();
byte[] encodedpoint = ku.getQ().getEncoded(true/*compressed*/);
{ // construct SPKI by hand, this curve only
byte[] hdr = DatatypeConverter.parseHexBinary("3058301006072a8648ce3d020106052b81040023034400");
// could also write out byte[] hdr = {0x30,0x58,0x30,0x10... but items with 0x80 set need casts
if( 0x44 /*hdr[0x15]*/ -1 != encodedpoint.length ) throw new Exception ("BAD COMPRESSED POINT FOR secp521r1!");
byte[] spki = Arrays.copyOf(hdr,90); System.arraycopy(encodedpoint,0, spki,0x17, 0x43);
PublicKey k2 = KeyFactory.getInstance("EC" /*,provider?*/).generatePublic(new X509EncodedKeySpec(spki));
Signature.getInstance("ECDSA").initVerify(k2); // sanity check
}
{ // construct SPKI with BC
AlgorithmIdentifier algid = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey,SECObjectIdentifiers.secp521r1);
ASN1EncodableVector vec = new ASN1EncodableVector();
vec.add(algid); vec.add(new DERBitString(encodedpoint));
byte[] spki = new DERSequence(vec).getEncoded();
PublicKey k2 = KeyFactory.getInstance("EC" /*,provider*/).generatePublic(new X509EncodedKeySpec(spki));
Signature.getInstance("ECDSA").initVerify(k2); // sanity check
}
{ // call BC directly
ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION;
X962Parameters params = X962Parameters.getInstance(org.bouncycastle.asn1.sec.SECObjectIdentifiers.secp521r1);
ECCurve curve = EC5Util.getCurve(configuration, params);
/*ECParameterSpec ecSpec = EC5Util.convertToSpec(params, curve);*/
ECPoint point = curve.decodePoint(encodedpoint).normalize();
ECPublicKeyParameters kparams = new ECPublicKeyParameters(point, ECUtil.getDomainParameters(configuration, params));
PublicKey k2 = new BCECPublicKey ("EC"/* or "ECDH" etc*/, kparams, configuration);
Signature.getInstance("ECDSA").initVerify(k2); // sanity check
}
Related Loading raw 64-byte long ECDSA public key in Java which is for P256 uncompressed.
The code (modified from BouncyCastle) below can work with any public key (not only secp521r1)
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.math.ec.ECCurve;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.Security;
import java.security.spec.ECParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class TestCompressionEncoded {
static X962Parameters getDomainParametersFromName(ECParameterSpec ecSpec, boolean compress) {
X962Parameters x962Param;
if (ecSpec instanceof ECNamedCurveSpec) {
ASN1ObjectIdentifier var3 = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
if (var3 == null) {
var3 = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
}
x962Param = new X962Parameters(var3);
} else if (ecSpec == null) {
x962Param = new X962Parameters(DERNull.INSTANCE);
} else {
ECCurve var5 = EC5Util.convertCurve(ecSpec.getCurve());
X9ECParameters var4 = new X9ECParameters(var5, new X9ECPoint(EC5Util.convertPoint(var5, ecSpec.getGenerator()), compress), ecSpec.getOrder(), BigInteger.valueOf((long)ecSpec.getCofactor()), ecSpec.getCurve().getSeed());
x962Param = new X962Parameters(var4);
}
return x962Param;
}
static byte[] encodeKeyWithCompression(BCECPublicKey x) throws Exception {
AlgorithmIdentifier var1 = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, getDomainParametersFromName(x.getParams(), true));
byte[] var2 = x.getQ().getEncoded(true);
return KeyUtil.getEncodedSubjectPublicKeyInfo(var1, var2);
}
public static void main(String...args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String publicKey = "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagGFnfckwVFpKg10+S2ttJYVUB4q+kPpnJg/YHV5xMnSLA==";
KeyFactory fact = KeyFactory.getInstance("ECDSA", "BC");
BCECPublicKey bcePubKey = (BCECPublicKey) fact.generatePublic(new X509EncodedKeySpec( Base64.getDecoder().decode(publicKey)));
System.out.println("Uncompressed encoded value: " + publicKey);
System.out.println("Compressed encoded value: " + Base64.getEncoder().encodeToString(encodeKeyWithCompression(bcePubKey)));
}
}
The output (for prime256v1)
Uncompressed encoded value: MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagGFnfckwVFpKg10+S2ttJYVUB4q+kPpnJg/YHV5xMnSLA==
Compressed encoded value: MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgACLPqrW2JAXKTbjfh9M3X3b85Uje7T0r2gu7qKPmmyagE=