Generating and validating a signature with ED25519 expanded private key - java

I am building a encrypted messaging app over tor network and currently I'm struggling on using tor generated ed25519 private key to sign and verify any message.
Below piece of code works with a 32 bytes key however after skipping 32 header bytes of hs_ed25519_secret_key it fails to verify the signature on below cases:
1 - secret: left half of the remaining 64 bytes, public: right half
2 - secret: left half of the remaining 64 bytes, public: last 32 bytes of hs_ed25519_public_key after removing the header
3 - secret: all 64 bytes, public: last 32 bytes of hs_ed25519_public_key
I found a python library that seems to do this PyNaCl however i not familiar with py too much.
Is there something i am doing wrong or bouncycastle does not support expanded 64 bytes private keys
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import java.nio.charset.StandardCharsets;
public class ED25519 {
public static void main(String[] args) throws Exception {
byte[] message = "a msg to be signed".getBytes(StandardCharsets.UTF_8);
Signer signer = new Ed25519Signer();
signer.init(true, new Ed25519PrivateKeyParameters(KeysUtil.myPrivKey, 0));
signer.update(message, 0, message.length);
Signer verifier = new Ed25519Signer();
verifier.init(false, new Ed25519PublicKeyParameters(KeysUtil.myPubKey, 0));
verifier.update(message, 0, message.length);
boolean validSig = verifier.verifySignature(signer.generateSignature());
}
}

BouncyCastle uses the RFC 8032 definition of the private key, which is basically a 32 byte seed. That seed is input to SHA512, which produces 64 bytes consisting of an 'internal' 32 byte secret ("s") and an additional 32 bytes pseudo-random value ("h"). It looks like Tor treats this latter 64 bytes (the output of SHA512) as the secret key, so this is incompatible.
Of course it would be relatively straightforward to provide a way to work with these keys (at least in low-level utilities), but it doesn't exist yet.

Related

Why ssh-keygen does not output 32 bytes but instead 1612 chars?

My goal is to create my own TLS handshake server, so I can understand how encryption between server-client works. I am following this reference. In the Server Key Exchange Generation section from the reference, the private key length are said to be 32 bytes. But the example length is 64 bytes. Also, ssh-keygen output 1612 chars for private.key and 567 chars for private.pub.
My expected result: The private.pub should be used as is in Server Hello section of the reference which sends the public key with length 32 bytes to the client.
My actual result: The ssh-keygen or even keytool (I can't read the file with IntelliJ IDEA, so right now, I am avoiding it) output more than 32 bytes.
The cmd used
ssh-keygen
keytool -genkeypair -keyalg RSA -validity 7 -keystore keystore
test\experiment\tls\tls\TLSHandshake.java
class TLSHandshakeTest {
#Test
void doHandshake() {
String key = "MC4CAQAwBQYDK2VuBCIEIJCRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6v";
byte[] bytes = key.getBytes();
System.out.println(Arrays.toString(bytes));
// output: 64 instead of 32, does this mean that the `private.pub` file must be processed with an encoder?
System.out.println(bytes.length);
}
}
The 32 byte (128 bit) secret key produced in the "server key exchange generation" step is not the RSA private key. It is actually the key for the symmetric encryption algorithm that will be used to protect application data.
What happens is that the RSA asymmetric algorithm is used to establish identities, and to protect the negotiation. During this process the symmetric algorithm is agreed and 128 bit secret keys are generated and exchanged (securely). Once the session is established, the 128 bit keys are use to encrypt and decrypt the data sent over the connection.
Why don't they use RSA for everything?
Basically, it is too computationally expensive to use RSA (or any other asymmetric algorithm) for bulk data encryption / decryption. The symmetric (block cypher) algorithms are much more efficient. (So you could view the initial negotiation as a "key distribution mechanism" for the session private keys.)
As for the size of the private and public key files generated by ssh-keygen:
The files contain more than just one key:
The public key file contains the value of the modulus and the public exponent e
The private key file usually contains the values of the modulus and both e and the private exponent d and several more values (p, q, dp, dq, qinvp). This is part of why the private key file is bigger than the public key file!
The file contents are encoded in ASN.1 which adds some extra type headers.
The (binary) ASN.1 encoded data is then base64 encoded which uses roughly one ASCII character for each 6 bits of data (plus some padding).
Some additional stuff is added to that:
The private key has line breaks added, along with "PEM" header and trailer lines.
The public key has the name of the algorithm (e.g. "ssh-rsa") and a "comment" added which serves to identify the person the key belongs to. (The comment is largely ignored by SSH ... but it is useful when you are manually adding or removing keys from an "authorized keys" file.)
For later versions of OpenSSH, OpenSSH key formats may be used instead of OpenSSL key formats. These use XDR encoding in place of ASN.1. This may be relevant to you if you are trying to write your own SSH implementation1.
As we can see, the "private key" file generated by ssh-keygen actually contains both the public and private keys for the keypair. Indeed it is possible to recover the keypair's public key from a private key file; e.g.
ssh-keygen -f ~/.ssh/id_rsa -y > ~/.ssh/id_rsa.pub
Refer to Create a public ssh key from the private key for more details.
1 - To be honest, I can't see the point of doing that. It is a lot of effort ... just to learn a bunch of details that you most likely don't need to know.

Invalid key length: 16 bytes

I am getting Invalid key length: 16 bytes on netbeans ide keybyte length is 16
SecretKey deskey = new SecretKeySpec(keybyte, "DESede/ECB/NOPADDING");
//enter code here..
Cipher c1 = Cipher.getInstance("DESede/ECB/NOPADDING");
c1.init(Cipher.ENCRYPT_MODE, deskey);
but the same code works on android
Your cipher algorithm (triple DES) is expecting a 24 byte key but you only give it 16 bytes, it might help to show how you build the key.
Also, you should be aware of the limitations of ECB. As identical blocks are always encrypted to the same result, it can leave ciphertexts less opaque than you might assume. See here for more detail.

String.getBytes("UTF-32") returns different results on JVM and Dalvik VM

I have a 48 character AES-192 encryption key which I'm using to decrypt an encrypted database.
However, it tells me the key length is invalid, so I logged the results of getBytes().
When I execute:
final String string = "346a23652a46392b4d73257c67317e352e3372482177652c";
final byte[] utf32Bytes = string.getBytes("UTF-32");
System.out.println(utf32Bytes.length);
Using BlueJ on my mac (Java Virtual Machine), I get 192 as the output.
However, when I use:
Log.d(C.TAG, "Key Length: " + String.valueOf("346a23652a46392b4d73257c67317e352e3372482177652c".getBytes("UTF-32").length));
I get 196 as the output.
Does anybody know why this is happening, and where Dalvik is getting an additional 4 bytes from?
You should specify endianess on both machines
final byte[] utf32Bytes = string.getBytes("UTF-32BE");
Note that "UTF-32BE" is a different encoding, not special .getBytes parameter. It has fixed endianess and doesn't need BOM. More info: http://www.unicode.org/faq/utf_bom.html#gen6
Why would you UTF-32 encode plain a hexidecimal number. Thats 8x larger than it needs to be. :P
String s = "346a23652a46392b4d73257c67317e352e3372482177652c";
byte[] bytes = new BigInteger(s, 16).toByteArray();
String s2 = new BigInteger(1, bytes).toString(16);
System.out.println("Strings match is "+s.equals(s2)+" length "+bytes.length);
prints
Strings match is true length 24

RSA decryption of valid data padded data fails (BadPaddingException)

I am facing a very peculiar problem when using RSA encryption/decryption in Java.
Example code:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.genKeyPair();
Cipher enc = Cipher.getInstance("RSA");
enc.init(Cipher.ENCRYPT_MODE, kp.getPublic());
String CipherText = new String(enc.doFinal(PlainText.getBytes()));
System.out.println("CipherText: ") + CipherText);
Cipher dec = Cipher.getInstance("RSA");
dec.init(Cipher.DECRYPT_MODE, kp.getPrivate());
PlainText = new String(dec.doFinal(CipherText.getBytes()));
System.out.println("PlainText: " + PlainText);
As everyone can plainly see: I encrypt the plaintext using the public key, after which I decrypt the ciphertext using the private key.
This code crashes with the following message:
Exception in thread "main" javax.crypto.BadPaddingException: Data must start with zero
I also tried to explicitly use "RSA/ECB/NoPadding", and this fails on decoding period. (Eg the decoded ciphertext doesn't match the original plaintext).
Last but not least, I have tried to perform this when using my own PKCS1.5 padding function ala the PKCS1.5 specs:
EMB = 00 || 02 || RD || 00 || MD
EMB is encoded messageblock of length k
Where RD are 8 random nonzero bytes
MD is max length k = 11, and optionally padded with zero bytes to make EMB length k.
After two days of testing I can only conclude that the RSA algo in Java is flawed or simply not performing what I expect it to perform.
Any suggestions or fixes to the above code are very welcome, as I am completely stumped on why the above code will not simply work as expected.
Don't do this:
String CipherText = new String(enc.doFinal(PlainText.getBytes()));
Two reasons:
It's almost never a good idea to call String.getBytes() without specifying an encoding. Do you really want the result to depend on the system default encoding?
It's definitely never a good idea to treat the result of a binary encryption operation (i.e. opaque binary data) as an encoded string. Encode it in Base64 or hex instead.
You can use Apache Commons Codec to perform the base64 encode/decode operations, or this standalone public domain encoder/decoder.

invalid AES key length error

this code give invalid AES key length error. how can i correct it ? ( i want 128 bit key AES encryption )
package org.temp2.cod1;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
public class Code1 {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
String s = "9882623867";
byte[] plaintext = s.getBytes("UTF-16");
String s2 = "supernova";
byte[] key = s2.getBytes("UTF-16");
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(plaintext);
System.out.println(encryptedData);
}
}
any help appreciated
Use a SecretKeyFactory to derive key bytes from a password.You can see a detailed example here. Note that you'll need to specify a key length of 128 bits key instead of 256 bits as shown in that example.
The next problem that you will run into is that you have not specified a padding scheme. Unless your messages are a multiple of 16 bytes (the AES block size), that will raise an error. Use PKCS5Padding as shown in the example.
Use of CBC mode on the cipher will require a new initialization vector to be chosen for each message. This unique IV must be sent along with the encrypted message to the recipient.
Trying to perform cryptography without a thorough understanding of the concepts raised here (and a lot more) is likely to result in an insecure system.
You can't typically use any arbitrary key length (such as you're doing here with "supernova") for a block cipher like AES. You must use a supported key length (128, 192, 256, etc) appropriate for your algorithm of choice.
One common way to do this is to hash your passphrase (e.g., via SHA) and extract the first N bytes. This is better anyhow, as it allows you to "salt" your password with an initialization value such that no two users' "keys" are identical even if their passphrases are the same. If you're really interested in this stuff, the seminal work is Applied Cryptography by Bruce Schneier.
For practical implementation details, see
You can get this error when the key you're trying to use isn't the right length.
So in psuedocode, you're trying something like this:
String key = "123";
SecretKeySpec k = new SecretKeySpec(key, "AES");
but the key is too short - it needs to be something like, say 31 characters long.
So check your key value -> it's probably stored somewhere incorrectly.
Use a key Value string with 16 bytes for Smooth encryption e.g. The key "thebestsecretkey" will work on base64

Categories