Does somebody know how I can create an RSA key in C++ from an encoded byte array?
My problem is that I try to develop a C++ client that is interacting with a server which is coded in Java.
Well in Java the client receives the rsa key encoded as an byte array, decodes it to a RSA RSAPublicKey and encrypts a message with this key.
The java server/client code:
public static PublicKey decodePublicKey(byte[] p_75896_0_)
{
try
{
X509EncodedKeySpec var1 = new X509EncodedKeySpec(p_75896_0_);
KeyFactory var2 = KeyFactory.getInstance("RSA");
return var2.generatePublic(var1);
}
catch (NoSuchAlgorithmException var3)
{
;
}
catch (InvalidKeySpecException var4)
{
;
}
field_180198_a.error("Public key reconstitute failed!");
return null;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.publicKey = CryptManager.decodePublicKey(data.readByteArray());
After that the client is doing some encrypting stuff with his key.
The key gets sent like this:
public static final KeyPair keys;
static
{
try
{
KeyPairGenerator generator = KeyPairGenerator.getInstance( "RSA" );
generator.initialize( 1024 );
keys = generator.generateKeyPair();
} catch ( NoSuchAlgorithmException ex )
{
throw new ExceptionInInitializerError( ex );
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
byte[] pubKey = keys.getPublic().getEncoded();
writeBytes(pubKey);
My problem is how to get the key from the byte array in C++.
Update:
Im currently working on this code:
char* publicKey = ...
int publicKeyLength = 162;
EVP_PKEY* key = EVP_PKEY_new();
if(d2i_PUBKEY(&key, (const unsigned char**) &publicKey, publicKeyLength) != 0){
logError("Problem!");
}
logMessage("Key: "+to_string((uint64_t) (void*) key));
Well my problem now is that i have an SIGSEGV error on the third line and dont know what this course. Well the key should be valid.
What Java returns for the public key is a SubjectPublicKeyInfo structure, which doesn't just contain the (PKCS#1 encoded) values for the public key, but also the key identifier etc.
So to decode this you have to type "decode SubjectPublicKeyInfo openssl" in your favorite search engine. Then you'll find (after some scrolling) the following information from here:
d2i_PUBKEY() and i2d_PUBKEY() decode and encode an EVP_PKEY structure
using SubjectPublicKeyInfo format. They otherwise follow the conventions
of other ASN.1 functions such as d2i_X509().
Obviously you'd need the decoding algorithm.
Note that openssl is C so beware of buffer overruns when decoding stuff. I'd rather have a 1024 bit RSA key that is used with secure software than a 2048 bit key with software full of buffer overruns.
Needless to say you need to trust the public key before importing it. There is a reason why it is called the public key infrastructure (PKI).
Related
I've generated this test public key using 1024 RSA and then encoded it to DER and Base64 in another coding platform. I copied the key into a string in Android/Eclipse and I am trying to turn it into a public key using KeyFactory. It just keeps giving me an InvalidKeySpecException no matter what I try. Any advice at all would be appreciated.
private void prepKeys() {
String AppKeyPub = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5" +
"5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ" +
"OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// create the key factory
try {
KeyFactory kFactory = KeyFactory.getInstance("RSA");
// decode base64 of your key
byte yourKey[] = Base64.decode(AppKeyPub,0);
// generate the public key
X509EncodedKeySpec spec = new X509EncodedKeySpec(yourKey);
PublicKey publicKey = (PublicKey) kFactory.generatePublic(spec);
System.out.println("Public Key: " + publicKey);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The key you have is in PKCS#1 format instead of SubjectPublicKeyInfo structure that Java accepts. PKCS#1 is the encoding of the RSA parameters only and lacks things such as an algorithm identifier. SubjectPublicKeyInfo uses PKCS#1 internally - for RSA public keys anyway.
As the PKCS#1 public key is at the end of the SubjectPublicKeyInfo structure it is possible to simply prefix the bytes so that they become an RSA SubjectPublicKeyInfo. That solution is easier to perform without additional libraries such as Bouncy Castle. So if you need to go without an external library then you may have a look at my answer here.
Alternatively a simple BER decoder could be written to decode the structure into the two BigInteger values. The structure itself is not that complicated but the BER/DER length encoding takes some getting used to.
However, you can also use Bouncy Castle (lightweight API) to solve your issues:
String publicKeyB64 = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5"
+ "5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ"
+ "OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// ok, you may need to use the Base64 decoder of bouncy or Android instead
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(decoded);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey generatedPublic = kf.generatePublic(keySpec);
System.out.printf("Modulus: %X%n", modulus);
System.out.printf("Public exponent: %d ... 17? Why?%n", publicExponent); // 17? OK.
System.out.printf("See, Java class result: %s, is RSAPublicKey: %b%n", generatedPublic.getClass().getName(), generatedPublic instanceof RSAPublicKey);
As you can see it actually only requires a single class as interface, although that is of course backed up with the entire ASN.1/BER decoder functionality within Bouncy Castle.
Note that it may be required to change the Base 64 decoder to the Android specific one (android.util.Base64). This code was tested on an equivalent Java runtime.
For those who dont want to use Bouncy Castle
public class RSAKeySeperation {
public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException {
String publicKeyB64 = "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBV8xakN/wOsB6qHpyMigk/5PrSxxd6tKTJsyMIq5f9npzZue0mI4H2o8toYImtRk6VHhcldo0t7UwsQXmFMk7D"
+ "i3C53Xwfk7yEFSkXGpdtp/7fbqNnjVoJl/EPcgoDsTPrHYF/HgtmbhzuYvYeY1zpV0d2uYpFxAuqkE9FreuuH0iI8xODFe5NzRevXH116elwdCGINeAecHKgiWe"
+ "bGpRPml0lagrfi0qoQvNScmi/WIN2nFcI3sQFCq3HNYDBKDhO0AEKPB2FjvoEheJJwTs5URCYsJglYyxEUon3w6KuhVa+hzYJUAgNTCsrAhQCUlX4+5LOGlwI5gonm1DYvJJZAgMBAAEB";
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
X509EncodedKeySpec spec =
new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey generatePublic = (RSAPublicKey) kf.generatePublic(spec);
BigInteger modulus = generatePublic.getModulus();
System.out.println(modulus);
BigInteger exponent = generatePublic.getPublicExponent();
System.out.println(exponent);
}
}
Encryption and Decryption successful when encrypt with public key and decrypt with private key :
C# encryption with public key(Successful)
public string EncryptData(string data) {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xml); //public key
var cipher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);
return Convert.ToBase64String(cipher );
}
Java decryption with private key(Successful)
public static void decrypt() throws Exception{
byte[] modulusBytes = Base64.getDecoder().decode(mod);
byte[] dByte = Base64.getDecoder().decode(d);
BigInteger modulus = new BigInteger(1, (modulusBytes));
BigInteger exponent = new BigInteger(1, (dByte));
RSAPrivateKeySpec rsaPrivKey = new RSAPrivateKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privKey = fact.generatePrivate(rsaPrivKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] cipherData = Base64.getDecoder().decode(cipherByte);
byte[] plainBytes = cipher.doFinal(cipherData);
System.out.println(new String(plainBytes));
}
Problem is Here
When c# encrypt with private key and java decrypt with public key bad padding error occur:
C# encryption with private key(Fail)
public stringEncryptData(string data) {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xml); //private key
var cypher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);
return Convert.ToBase64String(cypher);
}
java decryption with public key (Fail)
public static void decryptPublic() throws Exception{
byte[] modulusBytes = Base64.getDecoder().decode(mod);
byte[] expBytes = Base64.getDecoder().decode(exp);
BigInteger modulus = new BigInteger(1, (modulusBytes));
BigInteger exponent = new BigInteger(1, (expBytes));
RSAPublicKeySpec pubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey publicKey = fact.generatePublic(pubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, publicKey );
byte[] cipherData = Base64.getDecoder().decode(cipherByte);
byte[] plainBytes = cipher.doFinal(cipherData);
System.out.println(new String(plainBytes));
}
I understand public key should use to do encryption and private key for decryption.But in my situation, i need to sent out public key to mutiple clients for decryption on a text encrypted by its private key. Text should be non readable by others except client.
Can anyone see what problem on my code, or suggest a better solution to my problem.
RSA encryption is only secure if a (secure) padding scheme is being used. RSA encryption schemes have been specified in PKCS#1 standards by RSA laboratories (now part of EMC2). These have been copied into RFC, such as RFC 3447: Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1.
For the purposes of this document, an encryption scheme consists of
an encryption operation and a decryption operation, where the
encryption operation produces a ciphertext from a message with a
recipient's RSA public key, and the decryption operation recovers the
message from the ciphertext with the recipient's corresponding RSA
private key.
So encryption with a private key is an undefined operation.
So what to do now:
securely distribute private keys instead of public keys
generate key pairs and securely transport the public key to the sender
if you require authentication/integrity instead of confidentiality, use signature generation instead of encryption
And, whatever you do, read into Public Key Infrastructure (PKI). It's a far stretching subject that you need to understand before you can apply it.
Encrypting with the private key/decrypting with the public key is a legitimate operation in RSA, however it is not used to protect data, it is instead used to authenticate the source of the data and its integrity. In this context the encryption operation is more usually called "signing".
Encrypting using the private key to protect data as you describe is insecure and so the fact that it is not easily done is likely intentional and intended to prevent incorrect use of the algorithm.
Distributing your private key to clients as suggested in the comments is also unwise since you have no control over who they may pass the key onto (accidentally or otherwise).
If you wish to encrypt data so that it can be decrypted by multiple distinct parties, then you should have each of them provide you with their own public key, and use that to encrypt the data separately for each client.
For obvious security reasons i need to encrypt and decrypt User's PIN codes with RSA private and public key, I have found working solution, which looks like:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair rsaKeyPair = kpg.genKeyPair();
byte[] txt = "This is a secret message.".getBytes();
System.out.println("Original clear message: " + new String(txt));
// encrypt
Cipher cipher;
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic());
txt = cipher.doFinal(txt);
} catch (Throwable e) {
e.printStackTrace();
return;
}
System.out.println("Encrypted message: " + new String(txt));
// decrypt
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivate());
txt = cipher.doFinal(txt);
} catch (Throwable e) {
e.printStackTrace();
return;
}
System.out.println("Decrypted message: " + new String(txt));
}
everything works fine, but in this example key-pair is not static and generate new values everytime, but I need to use same keys, which are represented as String variables:
public static final String PrivateKey = "MIICXAIBAAKBgQDx0PSJr6zEP9914k1eM+sS8/eW+FenhBQI/jf6ARe8kZHFig9Y"
+ bla bla bla
+ "wdK3jBzObK319yNFr/2LukNZ9Bgv7fS78roBvxbe2gI=";
public static final String PublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDx0PSJr6zEP9914k1eM+sS8/eW"
+ bla bla bla
+ "jYo5w2Nhxe2cukCQMQIDAQAB";
Is there any way to cast these variables to PublicKey and PrivateKey Class?
If I understand what you want, to obtain PublicKey and PrivateKey instances from your static variables you can do, for example, this way:
private static final String privateKeyString = "...";
private static PrivateKey privateKey;
private static final String publicKeyString = "...";
private static PublicKey publicKey;
static {
KeyFactory kf;
try {
kf = KeyFactory.getInstance("RSA");
byte[] encodedPv = Base64.decodeBase64(privateKeyString);
PKCS8EncodedKeySpec keySpecPv = new PKCS8EncodedKeySpec(encodedPv);
privateKey = kf.generatePrivate(keySpecPv);
byte[] encodedPb = Base64.decodeBase64(publicKeyString);
X509EncodedKeySpec keySpecPb = new X509EncodedKeySpec(encodedPb);
publicKey = kf.generatePublic(keySpecPb);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
}
}
After (mostly) concurring with #JB that passwords (usually) shouldn't be encrypted, they should be "hashed" -- using a method specifically designed to "stretch" and salt such as scrypt, not a fast hash like SHA-1 -- and also noting that RSA-512 as used in your original code is broken and even RSA-1024 as apparently used in your modification is considered weak:
Your PrivateKey value appears (from its beginning) to be base64 of a plain PKCS#1 DER encoding, which basically is used only by OpenSSL and things that use OpenSSL (format) like older versions of OpenSSH. The Java standard "Sun" providers do not handle this, although I think BouncyCastle might if you want to explore that. For Sun you need to convert it to binary DER from base64; wrap it into PKCS#8 format (which in binary is just adding a header and maybe EOC trailers because the algorithm-specific part of PKCS#8 for RSA is PKCS#1); and put it in a PKCS8EncodedKeySpec and run it through generatePrivate of a KeyFactory of type RSA. See
http://docs.oracle.com/javase/8/docs/api/java/util/Base64.html (Java8 only)
http://docs.oracle.com/javase/8/docs/api/java/security/KeyFactory.html
https://www.rfc-editor.org/rfc/rfc5208#section-5 for the structure of unencrypted PKCS#8 (Java doesn't do the encrypted format in section 6) and look at the publickey form for the OID for RSA.
Alternatively add the header/trailer to make it proper PEM, use OpenSSL to convert it to PKCS#8 (unencrypted), and optionally binary at the same time, and run that through generatePrivate.
Your PublicKey similarly appears to be base64 of an X.509 SubjectPublicKeyInfo encoding, which OpenSSL (but not OpenSSH) uses and standard Java does support under the name "X.509". So just convert from base64 to binary, put in an X509EncodedKeySpec, and run through generatePublic of the RSA KeyFactory. Note if your encryption will be done remote or distributed, which is the usual scenario for publickey-encryption, the encryptor must be certain to use the correct publickey; if an attacker can substitute a wrong publickey they can decrypt and steal at least some of your supposedly secure data. That's why real PK systems don't use a plain publickey, they use a certificate, either X.509 like SSL/TLS and S/MIME, or web-of-trust like PGP.
I got this running doing the following:
public Key loadPrivateKey(String stored) throws GeneralSecurityException {
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8)));
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
public Key loadPublicKey(String stored) throws GeneralSecurityException {
byte[] data = Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePublic(spec);
}
You must also to remove -----BEGIN PRIVATE KEY-----, -----END PRIVATE KEY----- and all \n from the strings that contain you keys.
I'm desperately trying to encrypt a message using asymmetric public / private key cryptography on an Android.
I'm on Windows and I've generated a public and private key using puttygen. I'm not sure what difference it makes but I've selected SSH-2 RSA. Here is the public key:
AAAAB3NzaC1yc2EAAAABJQAAAQEAh63orUzl0UTd7jj0KNYJg1+kNnty0QHyJu0r
Cajf5Kl7qWJaGXPfwsG8Qt3teafs5sv0JBSinab0s/5wfQmd1QPpXTMP93Wc4ucp
1VC/9B2o8XVi4fKoGTehB48yrSfI6KF2AIeASM1jUswydKxsuS4AS2mLGV/HuoKD
huMfCsRc8qK5zGQfVCoZTbQ66Z1yKdAzxMUuGmiTp7pVsle/P/UGbm6yFiee5r1/
dOR2CDyR6CP09Jaj7KSGfGuwPryCXPjEce1oCbN/FlLHVb7T1B5f6xhq+oY+Ij13
1IZPfShV8cs2kYKjsle2s23V5urSdWFv2tEcSJcpkUm2FlPdQw==
I've copied this to a text file in my main/assets folder. I read this in like so:
InputStream input = context.getAssets().open(filename);
This is then read in to a byte array through a fairly standard ByteArrayOutputStream method.
I then try and convert that to a public key as such:
public static PublicKey getPublicKey(byte[] keyBytes){
PublicKey publicKey = null;
if(keyBytes != null) {
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = null;
try {
kf = KeyFactory.getInstance("RSA");
publicKey = kf.generatePublic(spec);
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException");
e.printStackTrace();
} catch (InvalidKeySpecException e) {
Log.e(TAG, "InvalidKeySpecException " + e.getMessage());
e.printStackTrace();
}
}
return publicKey;
}
Problem is I keep getting this error:
InvalidKeySpecException java.lang.RuntimeException: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
I've been attacking this for hours, and can't seem to get around it. Please please any suggestions welcome.
I've tried Base64 as such:
byte[] tempNewKey = Base64.decode(keyBytes, Base64.DEFAULT);
Which makes no difference and I've also tried using
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(module), new BigInteger(exponent));
However putty doesn't tell me anything about an exponent? If I go ahead with this method I don't get the same error, but if I try and decrypt with my private key I just get gibberish.
Really hope you can help. Many Thanks
SSH keys are not X509 compatible keys. They are stored in a SSH proprietary format. You'll need a SSH capable libary to retrieve the key value.
If SSH functionality is not required then it is possible to generate keys in Java (using the keytool command line or KeyPairGenerator.
Alternatively it is also possible to use external applications or libraries such as the openssl command line. In the case of OpenSSL specify DER as output. Java expects a DER encoded SubjectPublicKeyInfo structure for X509EncodedKeySpec.
I'm trying to create a very simple key exchange in Java. After the code and output there are questions:
public class Blergh {
public static KeyPair genKeyPair512() {
try {
AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator
.getInstance("DH");
paramGen.init(512);
BigInteger g = new BigInteger(
"7961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20",
16);
BigInteger p = new BigInteger(
"00AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC5",
16);
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
final DHParameterSpec dhSpec = new DHParameterSpec(p, g, 511);
keyGen.initialize(dhSpec);
return keyGen.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
public static byte[] genSharedSecretKey(KeyPair keyPair,
byte[] bytesPeerPublicKey) {
PrivateKey privateKey = keyPair.getPrivate();
try {
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(
bytesPeerPublicKey);
KeyFactory keyFact = KeyFactory.getInstance("DH");
PublicKey peerPublicKey = keyFact.generatePublic(x509KeySpec);
KeyAgreement ka;
ka = KeyAgreement.getInstance("DH");
ka.init(privateKey);
ka.doPhase(peerPublicKey, true);
String algorithm = "AES";
SecretKey secretKey = ka.generateSecret(algorithm);
return secretKey.getEncoded();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
public static void main(String argv[]) {
KeyPair akp = genKeyPair512();
KeyPair bkp = genKeyPair512();
System.out.println("Ali pub key: "
+ toRawHex(akp.getPublic().getEncoded()));
System.out.println("Bob pub key: "
+ toRawHex(bkp.getPublic().getEncoded()));
System.out.println("Ali pri key: "
+ toRawHex(akp.getPrivate().getEncoded()));
System.out.println("Bob pri key: "
+ toRawHex(bkp.getPrivate().getEncoded()));
byte[] apk = akp.getPublic().getEncoded();
byte[] bpk = bkp.getPublic().getEncoded();
byte[] as = genSharedSecretKey(akp, bpk);
byte[] bs = genSharedSecretKey(bkp, apk);
}
}
It generates, for example, the output:
Ali pub key: 3081DF30819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF03430002403BBCBF4052CD1CEF7A580A919AF75186CE0A624BC93AA47922C3822CE60A8CD10CE98550ABCA2D39DA2F09903C3D761B9A1C4AED185934FE5D08AD0CD097AA86
Bob pub key: 3081DF30819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF03430002400F119BC06E53F8C33D3F7C16473D1F9E001FABF4D619930C34945AA2C6D0A00CB9B332CEAF2C0C2FB61D3F568B9263B69A152410237F4D793F8B571C34AB37B7
Ali pri key: 3081E102010030819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF0442024043BA0B3C73EB7482B80DE98FA81A7E50B0DC2F5786CA62285655BD36CE012C056545DE5EED65736D9135EC9CD5148F8D68FF3C7B5CC62B2A1F7649698B26D1BE
Bob pri key: 3081E102010030819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF04420240485DDD7F5BDECA92FEE30D9D15211D274BC0FF7838B8B51E7894263CA65DB4E394033CE3E2146C0CD0CA74E2DB0EF95D01EE0DC4011A3EC6A8EC61CC2FDC5A44
So, I have a main question and two additional ones:
Why more than half of the bytes of the keys (both private and public) between Alice and Bob are equal? For instance, Alice's private starts with 3081E102010030819706092A... and Bob's private with 3081E102010030819706092A... as well.
Why is DHParameterSpec created with 511 and not 512 (at least in most examples on the web)?
Assuming there's nothing wrong with the generated keys, is there something else that I'm missing here, or this code should be safe when adpated to exchange the public keys through the internet?
Any help is welcome. Thanks in advance.
EDIT: The third question applies to the genSharedSecretKey() as well (that is, the whole code), though I'm not calling/showing output because it would be irrelevant.
Why more than half of the bytes of the keys (both private and public) between Alice and Bob are equal? For instance, Alice's private starts with 3081E102010030819706092A... and Bob's private with 3081E102010030819706092A... as well.
When you call getEncoded() you receive a copy of the key data in ASN.1 format. Depending upon the objects being represented, there is often a certain amount of duplicate data present in any two structures. Somewhere within the structure will be the key data, which will be different for Alice and Bob.
Indeed, as CodesInChaos stated in the comments, the first part of the ASN.1 structure contains the group parameter which is identical between both parties.
The reason why you receive keys with much duplicate that is - as Duncan suggested - that it is encoded in an ASN.1 format. And that does explain the amount of duplicate data as the contains the parameters p and g you've specified yourself - look for the values in the hexadecimal string. If you want to take a look at the contents, simply paste the hexadecimal output into an online ASN.1 decoder, such as the one found here.
There is a lot to be said about DH and (key) sizes. Some interesting discussions can be found on security.stackexchange.com and crypto.stackexchange.com. But in the end, we don't know; you typed in the code, so we should ask you how and why the Diffie-Hellman parameters were chosen.
As for the third question; to distribute public keys on the internet you need to establish trust. You need to trust the other party. Diffie Hellman key agreement by itself does not establish trust; only a shared secret with some other party. So you need a secure protocol (such as TLS) with trusted certificates or keys that contains an authentication component. A lot of knowledge is required to create such a protocol however; if you do not fully understand the code you just showed us, you will fail to create such a protocol yourself.
Note that if both parties have agreed upon a set of parameters, that it may not be necessary to send the whole encoded public key; you may just have to send the values that the other party does not know so the other party can reconstruct the public key.