I have a little problem with the ECDH (Elliptic-curve Diffie-Hellman) encryption.
I use the BouncyCastle library.
Here is my function to generate keys :
public static KeyPair generateECKeys() {
try {
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC");
keyPairGenerator.initialize(parameterSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException e) {
Log.d("Error - ",e.getMessage());
e.printStackTrace();
return null;
}
}
I encode my public key in base64 with :
String keyPairA_public_base64 = Base64.getEncoder().encodeToString(keyPairA.getPublic().getEncoded());
Here is an example of the key received :
keyPairA_public_base64 = "MFowFAYHKoZIzj0CAQYJKyQDAwIIAQEHA0IABGuSxmgwVGLHwcVhSf7C4/BfxfL4pGixHht8rWjPMBMTH5Vav1RQnf/Ucv9rLpD3M6ad8hHotwP5IpFsQT3hRkg="
Now, I need to generate an ECPublicKey object with the public key (String).
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
KeyFactory kf = KeyFactory.getInstance("ECDH", new BouncyCastleProvider());
ECNamedCurveSpec params = new ECNamedCurveSpec("brainpoolp256r1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = ECPointUtil.decodePoint(params.getCurve(), keyPairA.getPublic().getEncoded()); // Error here : Invalid point encoding 0x30
ECPublicKeySpec pubKeySpec = new java.security.spec.ECPublicKeySpec(point, params);
ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);
But, I have an error : Invalid point encoding 0x30 when I use ECPointUtil.decodePoint()
I don't understand how I can solve this error and if I use the right way to create an ECPublicKey object from a string.
Can you help me please ? :)
ECPointUtil.decodePoint() expects a raw public key. keyPairA_public_base64 on the other hand is a Base64 encoded public key in X.509/SPKI format (i.e. not a raw public key) and can be imported as follows:
import java.security.spec.X509EncodedKeySpec;
import java.security.interfaces.ECPublicKey;
import java.security.KeyFactory;
...
X509EncodedKeySpec x509EncodedKeySpecA = new X509EncodedKeySpec(Base64.getDecoder().decode(keyPairA_public_base64));
KeyFactory keyFactoryA = KeyFactory.getInstance("ECDH");
ECPublicKey publicKeyA = (ECPublicKey)keyFactoryA.generatePublic(x509EncodedKeySpecA);
Related
My aim is to take in a message containing a challenge and an origin. On receiving this msg a rsa keypair must be generated which is then used to manipulate the data as shown below. Certain part of the data is encrypted using the generated public key. During authorization, that data must be decrypted with the private key. However, when i try to decrypt it is shows a decryption error. I have even printed different parts of the code just to check if the desired output is achieved which is why i know the private key taken from file is correct. I am unable to solve the decryption error. The specifications for the task require the use of rsa and not hybrid encryption. i have tried padding but that didnt help. please advice on how to solve this problem
package pam;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.xml.bind.DatatypeConverter;
import com.sun.jersey.core.util.Base64;
class Test
{
public static void kpgen(int numBits, String s) throws Exception
{
if(s.length()!=64)
{
System.out.println("invalid entry");
}
else
{
KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = keygen.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
System.out.println("pk: "+privateKey);
System.out.println("pubk: "+publicKey);
String fileBase = "f:\\tempKey"; //WRITING PVT KEY TO FILE
try (FileOutputStream out = new FileOutputStream(fileBase + ".key"))
{
out.write(keyPair.getPrivate().getEncoded());
}
try (FileOutputStream out = new FileOutputStream(fileBase + ".pub"))
{
out.write(keyPair.getPublic().getEncoded());
}
System.out.println("Key pair : " + Base64.encode(String.valueOf(keyPair)));
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(keyPair.toString().getBytes(StandardCharsets.UTF_8));
String sha256 = DatatypeConverter.printHexBinary(digest).toLowerCase();
System.out.println("Hash value: "+sha256);
String ch = s.substring(0,32);
String or = s.substring(32,64);
System.out.println("Challenge: "+ch);
System.out.println("Origin: "+or);
MessageDigest md1 = MessageDigest.getInstance("SHA-256");
byte[] digest1 = md1.digest(privateKey.toString().getBytes(StandardCharsets.UTF_8));
String sha = DatatypeConverter.printHexBinary(digest1).toLowerCase();
or = or + sha;
System.out.println("String kh: "+or);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] keyHandle = cipher.doFinal(or.getBytes());
System.out.println("Key Handle: "+keyHandle);
String f = "f:\\keyList.pub";
Key pub = getKeyFromFile(f);
System.out.println("Attestation Public Key: "+pub);
PrivateKey pk = (PrivateKey) getPvtKey("f:\\keyList.key");
Signature rsa = Signature.getInstance("SHA1withRSA");
rsa.initSign(pk);
rsa.update(ch.getBytes());
byte[] sc = rsa.sign();
System.out.println("Signed challenge: "+sc);
String rm = publicKey.toString() + pub + sc + keyHandle;
System.out.println("Response Msg: " +rm);
}
}
public static Key getKeyFromFile(String fileName) throws Exception
{
byte[] bytes = Files.readAllBytes(Paths.get(fileName));
X509EncodedKeySpec ks = new X509EncodedKeySpec(bytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pub = kf.generatePublic(ks);
return pub;
}
public static PrivateKey getPvtKey(String s) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
byte[] bytes = Files.readAllBytes(Paths.get(s));
PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(bytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey pvt = kf.generatePrivate(ks);
return pvt;
}
public static void auth(String s) throws NoSuchAlgorithmException, Exception, IOException
{
String chal = s.substring(0, 32);
String origin = s.substring(32,64);
String kh = s.substring(64);
byte[] kh1 = kh.getBytes();
PrivateKey pvtKey = getPvtKey("f:\\tempKey.key"); //READING THE PRIVATE KEY MADE IN KPGEN
System.out.println("pk: "+pvtKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pvtKey);
byte[] keyHandle = cipher.doFinal(kh1);
String or = keyHandle.toString().substring(0, (kh.length()/2));
String pk = keyHandle.toString().substring(kh.length()/2);
int c = 0;
if(or.equals(origin))
{
c++;
}
else
{
System.out.println("Bad Key Handle: Invalid Origin");
}
if(pk.equals(pvtKey.toString()))
{
c++;
}
else
{
System.out.println("Bad Key Handle: invalid private key");
}
if(c==2)
{
Signature rsa = Signature.getInstance("SHA1withRSA");
rsa.initSign((PrivateKey) pvtKey);
rsa.update(chal.getBytes());
byte[] sc = rsa.sign();
System.out.println("Signed Challenge: "+sc);
}
else
System.out.println("error");
}
}
You have multiple (many) issues in your code with the encryption
First - encode properly your data, String in Java is to represent printable characters. As soon as you work with encryption (working on byte[] level), you need to encode or decode the values.
Example - your code will print the "keyHandle", it's a byte array object hash, not really the encrypted data itself
byte[] keyHandle = cipher.doFinal(or.getBytes());
System.out.println("Key Handle: "+keyHandle);
...
String rm = publicKey.toString() + pub + sc + keyHandle;
Use at hex or base64 encoding to print out the output. The same applies to the signature.
I am unable to solve the decryption error.
String kh = s.substring(64);
byte[] kh1 = kh.getBytes();
..
byte[] keyHandle = cipher.doFinal(kh1);
And you simply assume you can decrypt some random substring? Encrypting using RSA will produce output of size of the key (e.g. 2048 bits) and you have to store and decrypt as whole, not any substring.
As a learning exercise - try to simply encrypt and decrypt, encode, decode to learn the primitives you can (re)use.
This question already has answers here:
Decrypting an OpenSSL PEM Encoded RSA private key with Java?
(2 answers)
Closed 5 years ago.
Given this .pem file (generated with openssl and encrypted with a password):
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,AC009672952033EB
2wegzxf3MtncXS1CY3c.....
....
....
-----END RSA PRIVATE KEY-----
How do I get a PrivateKey object in Java? I wrote the following code but I cannot find the right way to get a KeySpec:
PrivateKey readFromPem(File keyFile, String password){
PemReader r = new PemReader(new InputStreamReader(new FileInputStream(keyFile)));
PemObject pemObject = r.readPemObject();
byte[] encodedKey = pemObject.getContent();
KeySpec keySpec = ???? // how to get this?
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey key = keyFactory.generatePrivate(keySpec);
return key;
}
I guess I should build a RSAPrivateKeySpec, but I don't know how. I tried the method from this answer and this other answer, but they both result in errors when parsing the byte array.
I'm using BouncyCastle 1.57 (bcprov-jdk15on, bcmail-jdk15on and bcpkix-jdk15on) and Java 7.
You can read the private key using the JcaPEMKeyConverter class.
The code below works for keys with and without a password:
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
// don't forget to add the provider
Security.addProvider(new BouncyCastleProvider());
String password = "your password";
// reads your key file
PEMParser pemParser = new PEMParser(new FileReader(keyFile));
Object object = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair kp;
if (object instanceof PEMEncryptedKeyPair) {
// Encrypted key - we will use provided password
PEMEncryptedKeyPair ckp = (PEMEncryptedKeyPair) object;
// uses the password to decrypt the key
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
kp = converter.getKeyPair(ckp.decryptKeyPair(decProv));
} else {
// Unencrypted key - no password needed
PEMKeyPair ukp = (PEMKeyPair) object;
kp = converter.getKeyPair(ukp);
}
// RSA
KeyFactory keyFac = KeyFactory.getInstance("RSA");
RSAPrivateCrtKeySpec privateKey = keyFac.getKeySpec(kp.getPrivate(), RSAPrivateCrtKeySpec.class);
System.out.println(privateKey.getClass());
The privateKey's class will be java.security.spec.RSAPrivateCrtKeySpec (which extends RSAPrivateKeySpec).
Use Bouncy Castle's bcpkix dependency which knows how to handle OpenSSL keys.
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk14</artifactId>
<version>1.57</version>
</dependency>
and try it like this:
private PrivateKey readFromPem(File keyFile, String password) throws IOException {
Security.addProvider(new BouncyCastleProvider());
PEMParser pemParser = new PEMParser(new InputStreamReader(new FileInputStream(keyFile)));
PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) pemParser.readObject();
PEMDecryptorProvider decryptorProvider = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
PEMKeyPair pemKeyPair = encryptedKeyPair.decryptKeyPair(decryptorProvider);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
return converter.getPrivateKey(pemKeyPair.getPrivateKeyInfo());
}
I have Java code for generating keypair using BC as follows:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
PrivateKey priv = key.getPrivate();
PublicKey pub = key.getPublic();
String privateKey = new String(Base64.encode(priv.getEncoded(), 0,priv.getEncoded().length, Base64.NO_WRAP));
String publicKey1 = new String(Base64.encode(pub.getEncoded(), 0,pub.getEncoded().length, Base64.NO_WRAP));
String publicKey = new String(Base64.encode(publicKey1.getBytes(),0, publicKey1.getBytes().length, Base64.NO_WRAP));
Now I want to do same in C# using BC. I have downloaded WP8BouncyCastle library via nuget package manager. I have written as:
var kpgen = new RsaKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));
var keyPair = kpgen.GenerateKeyPair();
AsymmetricKeyParameter privateKey = keyPair.Private;
AsymmetricKeyParameter publicKey = keyPair.Public;
string prvKey = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(privateKey.ToString()));
string pubKey = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(publicKey.ToString()));
string pubKey1 = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(pubKey.ToString()));
But I need getEncoded() method available in Java which is not available in BC library in C#. This getEncoded() method is used to convert given key into X.509 encoded key.In case of Java, public key getting twice converted (getencoded() and getBytes()) ,I am not able to do same in C#.
Is there any solution to it?
Use the following code for private key:
PrivateKeyInfo pkInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
String privateKey = Convert.ToBase64String(pkInfo.GetDerEncoded());
and following for public:
SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
String publicKey = Convert.ToBase64String(info.GetDerEncoded());
I'm trying to read RSA public key shown below, but I get an exception at line 6: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
My code:
String rsaPublicKey = rsaPublicKeyString.replace(
"-----BEGIN RSA PUBLIC KEY-----\n", "");
rsaPublicKey = rsaPublicKey.replace("\n-----END RSA PUBLIC KEY-----", "");
byte[] bytes = EncryptionUtils.decodeBase64(rsaPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
pubKey = (RSAPublicKey)keyFactory.generatePublic(keySpec);
RSA public key:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
-----END RSA PUBLIC KEY-----
What am I doing wrong?
UPD:
public static byte[] decodeBase64(String data) throws EncryptionException {
try {
BASE64Decoder decoder = new BASE64Decoder();
return decoder.decodeBuffer(data);
} catch (Exception e) {
throw new EncryptionException(e);
}
}
For me, I was missing the OID in the public key. I had to correct that on the iOS side using help from here: http://blog.wingsofhermes.org/?p=42
Also, my public key didn't have to be casted to an RSAPublicKey, the standard worked just fine:
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Your problem is caused because your public key is an RSAPublicKey object rather than a SubjectPublicKeyInfo object (see this answer for a good description of the difference). You will need to convert from one to the other before your code will work.
BouncyCastle can do the conversion for you. The following code snippet will work, although I dislike it for two reasons:
It uses a deprecated class PEMReader.
It requires the BouncyCastle provider to be loaded.
Code:
Security.addProvider(new BouncyCastleProvider());
PEMReader reader = new PEMReader(new StringReader(rsaPublicKeyString));
BCRSAPublicKey key = (BCRSAPublicKey) reader.readObject();
bytes[] = key.getEncoded(); // now in SubjectPublicKeyInfo format.
// as before...
With BouncyCastle, there is always many ways to skin a cat. Perhaps someone can find a more elegant solution than the one above?
Cannot match up the size of key generated using public/private keys for licensing application. Ive written a self contained example that creates public/private key, create a license by signing user emailaddress with public key, and then check using public key, license and email address that the license indeed was encoded using private key (Obviously this wouldn't all be in one class usually).
This all works but the hex version of the license key is 96 characters (i.e representing 48 bytes/384 bits) which is a little longer than I wanted (In contrast the length of public/private keys is not a problem and the longer the better). What could I use to generate a 32 (64 hex chars) byte or maybe 16 byte (32 hex chars), and would the security of this be reasonable ?
Picking another algorithm is somewhat hard as I do not understand the the interaction between the algorithm picked for generating the keys
KeyPairGenerator.getInstance("DSA");
and the algorithm for signing
Signature.getInstance("SHA/DSA");
and I cant find a list for either.
One other point when I generate a public/private key pairs I specify key size of
keyGen.initialize(1024, new SecureRandom());
yet neither the public key (443 bytes) or the private key (335 bytes) or the sum of both (778 bytes) match this number.
import org.apache.commons.codec.binary.Hex;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
*
*/
public class CreateLicense
{
private String PUBLIC_KEY;
private String PRIVATE_KEY;
public static void main(final String[] args)
{
try
{
String email = args[0];
System.out.println("Creating license for:"+email);
CreateLicense cl = new CreateLicense();
cl.generatePublicPrivateKeyPair();
String license = cl.createLicense(email);
cl.checkLicense(email, license);
}
catch(Throwable t)
{
t.printStackTrace();
}
}
//Would only be done once on server
private void generatePublicPrivateKeyPair() throws Exception
{
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
keyGen.initialize(1024, new SecureRandom());
final KeyPair pair = keyGen.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PRIVATE_KEY=Hex.encodeHexString(privateKey.getEncoded());
PublicKey publicKey = pair.getPublic();
PUBLIC_KEY=Hex.encodeHexString(publicKey.getEncoded());
System.out.println("PrivateKeyHexLength:"+privateKey.getEncoded().length);
System.out.println("PublicKeyHexLength:"+publicKey.getEncoded().length);
}
private PrivateKey reinflatePrivateKey(String keyAsHexString) throws Exception
{
byte[] keyBytes = Hex.decodeHex(keyAsHexString.toCharArray());
final PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(keyBytes);
final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
final PrivateKey privateKey = keyFactory.generatePrivate(privKeySpec);
return privateKey;
}
private PublicKey reinflatePublicKey(String keyAsHexString) throws Exception
{
byte[] keyBytes = Hex.decodeHex(keyAsHexString.toCharArray());
final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyBytes);
final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
final PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
return publicKey;
}
//License Create on server based on email address
private String createLicense(String emailAddress) throws Exception
{
String message=emailAddress;
PrivateKey privateKey = reinflatePrivateKey(PRIVATE_KEY);
final Signature dsa = Signature.getInstance("SHA/DSA");
dsa.initSign(privateKey);
dsa.update(message.getBytes());
final byte[] m1 = dsa.sign();
String license = Hex.encodeHexString(m1);
System.out.println("CreateLicense:"+license+":Size:"+license.length());
return license;
}
//Client checks that given known emailaddress and public key that a if a license was derived from
//that and corresponding privatekey it would match license.
private boolean checkLicense(String emailAddress, String license) throws Exception
{
String message=emailAddress;
PublicKey publicKey = reinflatePublicKey(PUBLIC_KEY);
final Signature dsa = Signature.getInstance("SHA/DSA");
dsa.initVerify(publicKey);
dsa.update(message.getBytes());
boolean result = dsa.verify(Hex.decodeHex(license.toCharArray()));
System.out.println("Result"+result);
return result;
}
}
gives output like
Creating license for:testuser#nowhere.com
PrivateKeyHexLength:335
PublicKeyHexLength:443
CreateLicense:302c021425f7ad7289b073f82a1d808838f43e0134c5591402140d2a7a4e3967706d4659dc73ace6455040a5fc6b:Size:92
Resulttrue
#Paul - I think your solution here would be to use ECDSA.
Change your line of code
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
to
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA");
The keys are much shorter than DSA - and I'm sure hex version signature would be shorter. I suggest you use a prime ECC curve of say 256 or 128 bits.
Please let us know if this solves the problem.