I am trying to run the code in this pdf. For example, for ECCKeyGeneration, I get the following output instead of Figure 1 in the pdf:
sun.security.ec.ECPrivateKeyImpl#58b6
Sun EC public key, 192 bits
public x coord: 4812174841545539052388802454891896756539688756781766645984
public y coord: 1161396487043052042009627836016170768650083444786081272028
parameters: secp192r1 [NIST P-192, X9.62 prime192v1] (1.2.840.10045.3.1.1)
The private key doesn't printed to console/screen. Instead, it says "sun.security.ec.ECPrivateKeyImpl#blabla" as you can see above. What could be the reason for this situation?
Here is the code if you like to test:
import java.security.*;
import java.security.spec.*;
public class ECCKeyGeneration {
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg;
kpg = KeyPairGenerator.getInstance("EC","SunEC");
ECGenParameterSpec ecsp;
ecsp = new ECGenParameterSpec("secp192r1");
kpg.initialize(ecsp);
KeyPair kp = kpg.genKeyPair();
PrivateKey privKey = kp.getPrivate();
PublicKey pubKey = kp.getPublic();
System.out.println(privKey.toString());
System.out.println(pubKey.toString());
}
}
Normally private key values are not printed to screen. Hence there is little sense to provide a toString() for ECPrivateKey (a sub-class of PrivateKey).Printing out private key values is of course not safe.
It is of course possible to print out the secret part of the private key; printing out the other parameters of secp192r1 makes little sense. You can easily retrieve them from standard documents from NIST or Certicom if required.
ECPrivateKey ecPrivKey = (ECPrivateKey) eckp.getPrivate();
System.out.println(ecPrivKey.getS().toString(16));
Note that you are better off printing out a hash over the private key value S if you just need this for verifying that the right private key is used.
Your Problem is with the toString() methode of the java PrivateKey class.
toString() does not necessarily returns all the data in your object, just a string representation.
So in your case you could use the getEncoded() methode.
Note that your byte[] might contain unprintable characters, so you may need to convert it in a readable form f.e. Base64.
PrivateKey privKey = kp.getPrivate();
byte[] data = privKey.getEncoded();
//first methode, convert to string
String privKeyString = new String(data);
System.out.println(privKeyString);
//second methode, print all byte values
for(byte value : data)
System.out.println(value);
See also print byte array
Also you can use:
System.out.println(Arrays.toString(kp.getPrivate().getEncoded()));
Related
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)
I have generated a key pair on iOS and created a data representation using the following code:
var publicKey, privateKey: SecKey?
let keyattribute = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String : 256
] as CFDictionary
SecKeyGeneratePair(keyattribute, &publicKey, &privateKey)
var error: Unmanaged<CFError>?
let pubkeyRep = SecKeyCopyExternalRepresentation(publicKey!, &error) as Data?
let prikeyRep = SecKeyCopyExternalRepresentation(privateKey!, &error) as Data?
According to the documentation from Apple, the SecKeyCopyExternalRepresentation function encodes these keys using uncompressed ANSI X9.63 format
I want to transform these byte arrays into PublicKey and PrivateKey objects in Java.
A few examples that I've found here (using SunJCE) and here (using BouncyCastle) work for the public key, but they don't describe a way to import the private key.
Notice in the Apple documentation how the first 65-bytes are the uncompressed public key (04 || X || Y) concatenated with the private scalar (|| K). Take these bytes off and you can create the private key. I hope this helps somebody.
/*
* For an elliptic curve private key, the output is formatted as the public key
* concatenated with the big endian encoding of the secret scalar, or 04 || X || Y || K.
*/
private PrivateKey createECPrivateKey(byte[] rawBytes) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidParameterSpecException {
KeyFactory kf = KeyFactory.getInstance("EC");
BigInteger s = new BigInteger(Arrays.copyOfRange(rawBytes, 65, rawBytes.length));
return kf.generatePrivate(new ECPrivateKeySpec(s, ecParameterSpecForCurve("secp256r1")));
}
I am trying to implement Diffie-Hellman key exchange to generate the symmetric key for encryption/decryption using JAVA cryptography packages. This requires a public key exchange between the two parties.
The public key shared by the client is 1024 bit hexadecimal string, which should be used to calculate the shared secret key. How to convert this string into an encoded key format (ASN.1 SubjectPublicKeyInfo structure) to create a PublicKey object.
Considering a sample public key string. Parameters p and g are fed into inputDHParameterSpec object.
Sample implementation:
AutoGen keypair:
KeyPairGenerator clientKpairGen = keyPairGenerator.getInstance("DiffieHellman");
clientKpairGen.initialize(inputDHParameterSpec);
KeyPair clientKpair = clientKpairGen.generateKeyPair();
byte[] clientPubKeyEnc = clientKpair.getPublic().getEncoded();
X509EncodedKeySpec testPubKeySpec = new X509EncodedKeySpec(clientPubKeyEnc);
KeyFactory keyFactory = KeyFactory.getInstance("DiffieHellman");
PublicKey clientPubKey = keyFactory.generatePublic(testPubKeySpec);
Hex PublicKey - failing:
String testPublicKey = "85f04dd00345642ad12b65bd1a7c38728bff0b8e281ddb6ac4f2739e82a02145daabf23d173c933913b1f844059710e9125591569de427eae1d269accbfa3305069deb7622d1da3ad9820d11bd24fdcce5381d2df99bda314394738dfcbe210eae247b1303e79297ff746cd919e189f6a5776e6ecc24c8900de0f38f159072de";
X509EncodedKeySpec testPubKeySpec = new X509EncodedKeySpec(hexStringToByteArray(testPublicKey));
KeyFactory keyFactory = KeyFactory.getInstance("DiffieHellman");
PublicKey clientPubKey = keyFactory.generatePublic(testPubKeySpec);//Failing here
byte[] created in first code block has public key in ASN.1 encoded format, but hexStringToByteArray(testPublicKey) merely converts the hex to byte[]. Getting the below error on the marked line, due to this.
Exception in thread "main" java.security.spec.InvalidKeySpecException: Inappropriate key specification
at com.sun.crypto.provider.DHKeyFactory.engineGeneratePublic(DHKeyFactory.java:85)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at MWK_DHGen.main(MWK_DHGen.java:87)
Caused by: java.security.InvalidKeyException: Error parsing key encoding
at com.sun.crypto.provider.DHPublicKey.<init>(DHPublicKey.java:178)
at com.sun.crypto.provider.DHKeyFactory.engineGeneratePublic(DHKeyFactory.java:78)
... 2 more
Can someone help on how to convert this hex to the requried format here? A different implementation that would use this hex string to arrive at the secret key is also encouraged.
If you already have the domain parameters (p, g) and just the integer value of the public key then a DHPublicKeySpec rather than an X509EncodedKeySpec is the way to go:
String testPublicKey = "85f04dd00345642ad12b65bd1a7c38728bff0b8e281ddb6ac4f2739e82a02145daabf23d173c933913b1f844059710e9125591569de427eae1d269accbfa3305069deb7622d1da3ad9820d11bd24fdcce5381d2df99bda314394738dfcbe210eae247b1303e79297ff746cd919e189f6a5776e6ecc24c8900de0f38f159072de";
BigInteger publicKeyInteger = new BigInteger(testPublicKey, 16);
KeyFactory keyFactory = KeyFactory.getInstance("DiffieHellman");
PublicKey clientPubKey = keyFactory.generatePublic(new DHPublicKeySpec(publicKeyInteger, g, p));
I need to use ECDSA algorithm to sign a message and send to receiver in java. Then, receiver should verify sender's signature.
So, for this, receiver has sender's public key but in byte array format after converting java.security.PublicKey to byte array by the command bellow:
byte[] byteArrayPublicKey = publickey.getEncoded();
The format of public key in ECDSA algorithm (before converting it to byte array) is as follow:
Public Key:
X: 8a83c389e7bb817c17bf2db4ed71055f18342b630221b2a3a1ca752502dc2e21
Y: 3eaf48c9ab1700fe0966a0cde196b85af66bb8f0bacef711c9dca2368f9d8470
But, the problem is to convert this byte array to usable format to verify the signature that is java.security.PublicKey by receiver.
In general, is there any solution to verify the signature without converting it to byte array? In the other word, the problem is to verify the signature by sender's public key, using any method.
But, the problem is to convert this byte array to usable format to verify the signature that is java.security.PublicKey by receiver.
You can solve the problem like this way:
public static ECPublicKey genEcPubKey() throws Exception {
KeyFactory factory = KeyFactory.getInstance("ECDSA", "BC");
java.security.PublicKey ecPublicKey = (ECPublicKey) factory
.generatePublic(new X509EncodedKeySpec(Helper
.toByte(ecRemotePubKey))); // Helper.toByte(ecRemotePubKey)) is java.security.PublicKey#getEncoded()
return (ECPublicKey) ecPublicKey;
}
Note that, you need BouncyCastle provider to do that.
But question remains, how you generate the private key?
public KeyPair ecKeyPairGenerator(String curveName) throws Exception {
KeyPair keyPair;
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
"ECDSA", "BC");
ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(
curveName);
keyPairGenerator.initialize(ecGenParameterSpec, new SecureRandom());
keyPair = keyPairGenerator.generateKeyPair();
java.security.PublicKey ecPublicKey = (ECPublicKey) keyPair.getPublic();
System.out.println("JAVA EC PublicKey: "
+ Helper.toHex(ecPublicKey.getEncoded()));
// write private key into a file. Just for testing purpose
FileOutputStream fileOutputStream = new FileOutputStream(
"ECPrivateKey.key");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(
fileOutputStream);
objectOutputStream.writeObject(keyPair.getPrivate());
objectOutputStream.close();
return keyPair;
}
I have the full running code for EC sign/verify in github. You can take a look for better understanding.
I remember do this long time ago with OpenSSL, but I want to know if it's possible and how, I've never used Cryptography on java.
The assumption is that we are talking about RSA private and Public keys. Then, if you are working from a PEM format file, then first you need to read the private key from the file into a PrivateKey object:
public PrivateKey readPemRsaPrivateKey(String pemFilename) throws
java.io.IOException,
java.security.NoSuchAlgorithmException,
java.security.spec.InvalidKeySpecException
{
String pemString = File2String(pemFilename);
pemString = pemString.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
pemString = pemString.replace("-----END RSA PRIVATE KEY-----", "");
byte[] decoded = Base64.decodeBase64(pemString);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
where File2String is something like:
private static String File2String(String fileName) throws
java.io.FileNotFoundException, java.io.IOException
{
File file = new File(fileName);
char[] buffer = null;
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
buffer = new char[(int)file.length()];
int i = 0;
int c = bufferedReader.read();
while (c != -1) {
buffer[i++] = (char)c;
c = bufferedReader.read();
}
return new String(buffer);
}
Now you can generate the corresponding PublicKey with code like this:
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.RSAPublicKeySpec;
...
PrivateKey myPrivateKey = readPemRsaPrivateKey(myPrivateKeyPemFileName);
RSAPrivateCrtKey privk = (RSAPrivateCrtKey)myPrivateKey;
RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(), privk.getPublicExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey myPublicKey = keyFactory.generatePublic(publicKeySpec);
Credits: How to get a RSA PublicKey by giving a PrivateKey?
Please make sure that Eli Rosencruft answer is basically correct, but the order of the modulus and the public exponent are incorrect! This is the correct statement:
RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(), privk.getPublicExponent());
You cannot generate either key directly from the other. It is mathematically impossible. If you had a key blob that contained both the public and private keys, you could extract either one of them with relative ease.
EDIT, 2017: Many years and a much better understanding of crypto later, and it's now clear to me that this answer isn't really correct.
To quote Wikipedia:
The public key consists of the modulus n and the public (or encryption) exponent e. The private key consists of the modulus n and the private (or decryption) exponent d, which must be kept secret. p, q, and λ(n) must also be kept secret because they can be used to calculate d.
The public modulus n can be computed as p × q. The only thing missing from a raw private key is e, but this value is usually selected as 65537, and if not you can still compute e from d and λ(n).
However, many private key storage formats actually contain the public modulus n alongside the other components, so you can just do a direct extraction of the values.
EDIT, 2018: Still getting downvotes for this, and rightly so! I'm leaving this answer up so people can see why I was originally wrong, and to remind myself not to be wrong in future.