OpenSsl generates a private key in DER format with 118 bytes length. (openssl ecparam -genkey -name secp256k1 and so on).
In android KeyPairGenerator initialized like:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA", "SC");
ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
keyPairGenerator.initialize(spec, new SecureRandom());
return keyPairGenerator.generateKeyPair();
returns a private key with a 144 length. But i need 118. Where is a difference? Is smh added in android implementation? How could i get 118 key length? Cannot find implementation to figure out.
Found a way to do this.
Generate key pair:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA", "SC");
ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
keyPairGenerator.initialize(spec, new SecureRandom());
KeyPair kp = keyPairGenerator.generateKeyPair();
Get private key bytes array:
byte[] privateK = kp.getPrivate();
Then convert private key to PKCS1:
byte[] privBytes = privateKey.getEncoded();
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes);
ASN1Encodable encodable = pkInfo.parsePrivateKey();
ASN1Primitive primitive = encodable.toASN1Primitive();
byte[] privBytesEncoded = primitive.getEncoded();
It's a little bit of good news/bad news. The good news is that the bytes you want are a subsequence of the bytes returned by PrivateKey.getEncoded(). The bad news is that there's no good way that I'm aware of to get at them. Well, there is one easy way: the bytes you want are always at the end of PrivateKey.getEncoded(), so if you know the length of the byte sequence is n (e.g. 118 in your example) then just take the last n bytes of PrivateKey.getEncoded().
A slightly harder way is to parse through the encoding using the Spongycastle/Bouncycastle ASN1 routines, as in the following snippet:
private static byte[] encodePrivateKey(PrivateKey privateKey) throws Exception{
ASN1InputStream asn1InputStream = new ASN1InputStream(privateKey.getEncoded());
ASN1Primitive asn1Primitive = asn1InputStream.readObject();
DLSequence seq = (DLSequence) asn1Primitive;
ASN1OctetString octetString = (ASN1OctetString) seq.getObjectAt(2);
return octetString.getOctets();
}
I offer this as an example but I have to warn you that this is brittle: I haven't made any real effort to follow the PKCS#8 specification, I just eyeballed the ASN.1 structure to grab the bytes I knew were needed.
Related
I have an issue with my java code. I'm trying to encrypt a file. However, when I run my java code I get "java.security.InvalidKeyException: Invalid AES key length: 162 bytes".
Here is the code:
byte[] rawFile;
File f = new File("./src/wonkybox.stl");
FileInputStream fileReader = new FileInputStream(f);
rawFile = new byte[(int)f.length()];
fileReader.read(rawFile);
/***** Encrypt the file (CAN DO THIS ONCE!) ***********/
//Generate the public/private keys
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
keyGen.initialize(1024, random);
KeyPair key = keyGen.generateKeyPair();
PrivateKey privKey = key.getPrivate();
PublicKey pubKey = key.getPublic();
//Store the keys
byte[] pkey = pubKey.getEncoded();
FileOutputStream keyfos = new FileOutputStream("./CloudStore/keys/pubkey");
keyfos.write(pkey);
keyfos.close();
pkey = privKey.getEncoded();
keyfos = new FileOutputStream("./CloudStore/keys/privkey");
keyfos.write(pkey);
keyfos.close();
//Read public/private keys
KeyFactory keyFactory = KeyFactory.getInstance("AES");
FileInputStream keyfis = new FileInputStream("./CloudStore/keys/pubkey");
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
PublicKey pub1Key = keyFactory.generatePublic(pubKeySpec);
keyfis = new FileInputStream("./CloudStore/keys/privkey");
encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
PrivateKey priv1key = keyFactory.generatePrivate(privKeySpec);
//Encrypt file using public key
Cipher cipher = Cipher.getInstance("AES");
System.out.println("provider= " + cipher.getProvider());
cipher.init(Cipher.ENCRYPT_MODE, pub1Key);
byte[] encryptedFile;
encryptedFile = cipher.doFinal(rawFile);
//Write encrypted file to 'CloudStore' folder
FileOutputStream fileEncryptOutput = new FileOutputStream(new File("./CloudStore/encrypted.txt"));
fileEncryptOutput.write(encryptedFile);
fileEncryptOutput.close();
The error occurs at the line "KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");".
AES is a symmetric algorithm, hence they use of KeyPairGenerator is not supported. To generate a key with AES you call KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); //set keysize, can be 128, 192, and 256
By looking at the rest of your code, it looks like you are trying to achive asymmetric encryption (since you call getPublic() and getPrivate() etc), so I advice you to switch to using RSA or any other asymmetric algorithm that java supports. You will most likley only need to replace AES with RSA in your getInstance(); calls, and pherhaps some fine-tuning. Good luck
As far as I know, AES is symmetric encryption algorithm i.e. it needs only one key for encryption/decryption.
From the JavaDoc of java.security.KeyPairGenerator:
The KeyPairGenerator class is used to generate pairs of public and private keys.
Meaning that it should be used for asymmetric encryption algorithms. For symmetric encryption algorithms one should use javax.crypto.KeyGenerator.
However, I advise simply mimicking some tutorial on how to encrypt / decrypt byte array in Java using AES like this one.
It uses sun.misc.Base64Encoder / Base64Decoder classes to encode / decode byte array to / from String, however you may skip this step.
Hope this helps
How can you use a keypair generator for AES? AES is a symmetric key algorithm. Refer this link. That means if you encrypt data using a key "k", then you will have to decrypt it also using the same key "k". But when you generate key pair, as the name suggests, two keys are generated and if you encrypt using one of the keys, you can decrypt only using the other key. This is the base for PKI.
If you want to use keypair generator use an algorithm like "rsa" or "dsa" in the getInstance() method like this :
KeyPairGenerator keygen=KeyPairGenerator.getInstance("rsa");
I think your code should now work fine after making the above change.
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 need to save my RSA private and public key. Here I create it.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
Now I want to use both keys the next time. So they should be stored on the device.
byte[] publicKeyBytes = publicKey.getEncoded();
byte[] privateKeyBytes = privateKey.getEncoded();
So I can get the Byte Arrays and then I save them as a text file.
Then the next time i read it and convert it back to the byte Array.
Now my question: How can i convert a byte array back to a key?
This does not really work:
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes);
publicKey = keyFactory.generatePublic(spec);
spec = new X509EncodedKeySpec(privateKeyBytes);
privateKey = keyFactory.generatePrivate(spec);
On LogCat, this is going to be display:
Must use RSAPublicKeySpec or PKCS8EncodedKeySpec; was
java.security.spec.X509EncodedKeySpec
Any ideas whats wrong with this code?
Thanks for your help.
Try using PKCS8EncodedKeySpec for the private key.
RSAPublicKeySpec for the public key, instead of X509EncodedKeySpec for both.
Please also note that saving keys to a text file might not be the most secure idea. Android provides a nice KeyStore Api, which is easy to use.
You try encode the keys in Base64 and stored like a String, when you retrieved only need decoded the keys from Base64 to Byte arrays.
I am generating a keypair and converting one of the same into string which later is inserted into the database using the following code:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair generatedKeyPair = keyGen.genKeyPair();
PublicKey pubkey = generatedKeyPair.getPublic();
PrivateKey prvkey = generatedKeyPair.getPrivate();
System.out.println("My Public Key>>>>>>>>>>>"+pubkey);
System.out.println("My Private Key>>>>>>>>>>>"+prvkey);
String keyAsString = new BigInteger(prvkey.getEncoded()).toString(64);
I then retrieve the string from the database and convert it back to the original key using the following code (where rst is my ResultSet):
String keyAsString = rst.getString("privateKey").toString();
byte[] bytes = new BigInteger(keyAsString, 64).toByteArray();
//byte k[] = "HignDlPs".getBytes();
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(bytes);
KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
PrivateKey privKey = rsaKeyFac.generatePrivate(encodedKeySpec);
On using the privKey for RSA decryption, I get the following exception
java.lang.NumberFormatException: Radix out of range
at java.math.BigInteger.<init>(BigInteger.java:294)
at com.util.SimpleFTPClient.downloadFile(SimpleFTPClient.java:176)
at com.Action.FileDownload.processRequest(FileDownload.java:64)
at com.Action.FileDownload.doGet(FileDownload.java:94)
Please guide.
You're getting an exception because the radix you're providing is greater than Character.MAX_RADIX (which is 36). In other words, it's entirely predictable.
Don't use BigInteger as an encoding class. That's not what it's there for. There are plenty of decent ways of performing base64 encoding. Personally I like this public domain library.
String keyAsString = Base64.encode(prvkey.getEncoded());
Then later:
byte[] bytes = Base64.decode(keyAsString);
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
Key publicKey = kp.getPublic();
Key privateKey = kp.getPrivate();
I want to create just the public key from a byte[].
I have tried this as an experiment:
publicKey = new SecretKeySpec(publicKey.getEncoded(), publicKey.getAlgorithm());
But decryption using that key then fails.
I have also tried serializing the key with ObjectOutputStream, but serialization fails.
java.io.NotSerializableException: org.apache.harmony.xnet.provider.jsse.OpenSSLKey
I read here that I can't use SecretKeySpec with RSA.
so as long as you are talking of a SecretKey and not an RSA or DSA key then you don't have to go through any contortions involving KeyGenerator or the like.
Anyone know how to perform these contortions or a way of doing this.
Asymmetric keys like those from RSA are usually stored in X509 format. Therefor you can use X509EncodedKeySpecinstead.
A simple example is already in the Java 7 JavaDoc (just replace DSA with RSA):
http://docs.oracle.com/javase/7/docs/api/java/security/KeyFactory.html
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);
If you need to deserialize the private from byte[], I've found that you must use PKCS8EncodedKeySpec.
Depends on what do you want to do with the serialized representation. If the consumer is no one but your own program, feel free to roll your own implementation. A public RSA key consists of two integers - exponent and modulus. Modulus is large - around 1024 bits, exponent is typically on the order of 17 bits. Both are available as BigInteger objects if you cast your public key object to RSAPublicKey.
So, to recap:
RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic();
return publicKey.getModulus().toString() + "|" +
publicKey.getPublicExponent().toString();
That's sufficient to restore the key. To deserialize:
String []Parts = MyKeyString.split("\\|");
RSAPublicKeySpec Spec = new RSAPublicKeySpec(
new BigInteger(Parts[0]),
new BigInteger(Parts[1]));
return KeyFactory.getInstance("RSA").generatePublic(Spec);
If the key needs to be passed to third party software, you better serialize to a standard format - PEM or DER.
I use the following code to convert a PubliKey to PEM Base64 format
String publicKeyString = javax.xml.bind.DatatypeConverter.printBase64Binary(publicKey.getEncoded());
I hope it helps.