Implementing SHA1withRSA in Java - java

I'm trying to port a C++ program to Java, but not having much luck. The algorithm being used is RSASSA-PKCS1-v1_5. I know Java behaves differently from many other languages when it comes to cryptographic stuff.
This is known to be working:
bssl::UniquePtr<RSA> rsa(RSA_new());
rsa->n = BN_bin2bn(server_key, sizeof(server_key), nullptr);
rsa->e = BN_new();
BN_set_word(rsa->e, 65537);
std::uint8_t gs_hash[20];
SHA1(gs.data(), gs.size(), gs_hash);
if (1 != RSA_verify(NID_sha1, gs_hash, sizeof(gs_hash), gs_sig.data(), gs_sig.size(), rsa.get())) {
// failed
}
My current implementation:
Security.addProvider(new BouncyCastleProvider());
byte[] serverKey = new byte[] {...};
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(new RSAPublicKeySpec(new BigInteger(serverKey), BigInteger.valueOf(65537)));
MessageDigest digest = MessageDigest.getInstance("SHA1");
byte[] gs_hash = digest.digest(gs);
Signature sig = Signature.getInstance("SHA1withRSA/PSS", "BC");
sig.initVerify(publicKey);
sig.update(gs_hash);
if (sig.verify(gs_sig)) System.out.println("ALL GOOD");
else System.out.println("FAIL");
I've also tried using SHA1withRSA for the signature. gs and gs_sig are known and respectively 96 and 256 bytes long.
Test values:
serverKey: ace0460bffc230aff46bfec3bfbf863da191c6cc336c93a14fb3b01612acac6af180e7f614d9429dbe2e346643e362d2327a1a0d923baedd1402b18155056104d52c96a44c1ecc024ad4b20c001f17edc22fc43521c8f0cbaed2add72b0f9db3c5321a2afe59f35a0dac68f1fa621efb2c8d0cb7392d9247e3d7351a6dbd24c2ae255b88ffab73298a0bcccd0c58673189e8bd3480784a5fc96b899d956bfc86d74f33a6781796c9c32d0d32a5abcd0527e2f710a39613c42f99c027bfed049c3c275804b6b219f9c12f02e94863eca1b642a09d4825f8b39dd0e86af9484da1c2ba863042ea9db3086c190e48b39d66eb0006a25aeea11b13873cd719e655bd
gs: 4f2d7c6e76ccb6400ae1ff560d55a8084d98563ae03ac109d899fde735f6490935383cd1a97aa1fbff12e646f837194e9c6e57e1c5f956fcfde446a387c6be9a35c3225475f86df5a2c9b94626a2f90da3673af9861e33e8851a9a0ae20b9809
gs_signature: 35d1e685382f6d75bc5991f8ca1d252d70851c7bf66aa332bc6fc37bdb95da40c77c3cb452e6a70feda2bd6f63036d4b7a6d3f205789cf23a5777bcbd917a803ec2f3fb47fb6bdb911dd7d40dbe8425ad0d1905f1edb1bbc037ef4e259fa9c5dacdf9d58db8a18baf593d4e1c8055f51acffdaeeb10b4cee79f8b2421cfc28bdc9513859f76b101c965427334927207b575b9c29c581dfcc6fa4f135a1b04cfda4363e589f0510407faec4041b142b0ebbb9acd26dc2ed54fb28c38f560e05f8048c08c3574a4865f903192636987021eb72598ee822544026756ca294a150eb6642e65a860bac31fbba5ca0d800d030a52b515a7b8768def7de8e9f5dccdebe
gs_hash: 41aed19785bc5f4ffaa7eb30a29b1a39d52a225a

There are two things wrong:
You don't need to hash yourself when using "SHA1withRSA" (the signature will do the hashing for you, just feed it the data) and
if you add "/PSS" then you're using a different scheme than PKCS#1 v1.5.
BigInteger(byte[]) will create a negative value if used with the modulus. You need to use BigInteger(1, byte[]) to make it a positive value. However, both Bouncy as the JCE should throw an exception on negative exponents, so I don't see how you make your code run anyway.

Related

Nodejs Crypto ECDH PublicKey Hex as X.509

I am using the prime256v1 curves for generating key pairs and sign using nodejs with the default crypto module.
Using crypto
let crypto = require('crypto');
let e = crypto.createECDH('prime256v1');
e.generateKeys();
privateKey = e.getPrivateKey();
privateKeyHex = privateKey.toString('hex');
publicKey = e.getPublicKey();
publicKeyHex = publicKey.toString('hex');
I obtain a publickey which looks like the hex string below:
'049a6b0ac242afe41128cf59736412686ca83c9e902ee3fa0f13810b9d59ebfe5e49204427c23b630be12ae33815b0bda6ed8d0603386c6ea5f1906cdb0e731286'
Usign jsrsasign
let jsrsa = require('jsrsasign');
let KEYUTIL = jsrsa.KEYUTIL;
let kp = KEYUTIL.generateKeypair("EC", "prime256v1");
let pkHex = kp.pubKeyObj.pubKeyHex
which returns
'04f36e41189420db05dd8a73e3cb310b0c55809190bdedd89bf19769ac8df3cd06c1380f646e9e65e31c24affff79e43516b37e0186c3753cfdfd29894c2becc84'
Converting the PublicKey Hex to PublicKey object in Java
I want to use these publicKeys and convert it into a PublicKey object in java. Using the EC KeyFactory, I convert the hex to a byte[] and try to construct the PublicKey object in java which expects a X.509 format encoding.
public PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey pub = kf.generatePublic(publicKeySpec);
return pub;
}
To convert the hex string to a byte[] I use the following:
public byte[] hexStringToByteArray(String hexString) {
byte[] bytes = new byte[hexString.length() / 2];
for(int i = 0; i < hexString.length(); i += 2) {
String sub = hexString.substring(i, i + 2);
Integer intVal = Integer.parseInt(sub, 16);
bytes[i / 2] = intVal.byteValue();
String hex = "".format("0x%x", bytes[i / 2]);
}
return bytes;
}
Trying to do the same using the test case as follows results in an InvalidKeySpecException:
#Test
public void pkConversionTest() throws NoSuchAlgorithmException, InvalidKeySpecException {
ECDSA.setDebug(true);
byte[] pk = hexStringToByteArray("049a6b0ac242afe41128cf59736412686ca83c9e902ee3fa0f13810b9d59ebfe5e49204427c23b630be12ae33815b0bda6ed8d0603386c6ea5f1906cdb0e731286");
PublicKey pub = ECDSA.getPublicKey(pk);
System.out.println(pub);
}
returns
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=26, too big.
I am however able to generate a KeyPair using java and use the publicKey hex obtained with nodejs to perform signature verify. A sample publickey hex generated from java looks as follows:
3059301306072a8648ce3d020106082a8648ce3d0301070342000425a321d5a1a74e6c04a6e3cab030401f3dbc04d5242f9bc629175c3d3988799175eb80cd96d7e76ea924630a8d86b93c54dec7cb965b58de31705eb3343846a1
How do I format the publicKey generated by nodejs in an X.509 format to be used on the java's side?
Edit:
3059301306072a8648ce3d020106082a8648ce3d030107034200 seems to be a common prefix for the publicKey hexes generated using java. By Prefixing this to the hex values of the PublicKey obtained using nodejs since the length is smaller seems to solve the problem. But can someone explain why?
Thank you.
But can someone explain why?
Java encodes public keys in "X.509" format or more exactly the SubjectPublicKeyInfo structure (SPKI) defined by X.509/PKIX; see rfc5280, rfc3279, and for ECC specifically rfc5480. That's why the data you pass to the key factory is in a class named X509EncodedKeySpec. This ASN.1 structure contains an AlgorithmIdentifier which identifies the algorithm used and its parameters (which for ECC is the curve/group used, in your case an OID identifying prime256 aka P-256 aka secp256r1) plus a BIT STRING type containing the actual encoded publickey value (which for ECC is the point in X9.62 format, which has several variants; here you are using uncompressed; according to the doc nodejs.crypto also supports compressed).
Your "prefix" is the DER encoding of the ASN.1 outer SEQUENCE, AlgorithmIdentifier, and tag length and padcount which begin the BIT STRING to contain the publickey point.
Basically dupe:
* How can I get a PublicKey object from EC public key bytes?
* Loading raw 64-byte long ECDSA public key in Java (Maarten's answer is effectively what you did)
* How can I generate a valid ECDSA EC key pair? (disclosure: mine)
FYI: Effectively the same issue also occurs for RSA and there are more Qs on that.
And there are also similar issues with privatekeys in generic PKCS8 format compared to algorithm-specific formats, but since publickeys are usually exchanged with other systems and/or programs while privatekeys usually aren't interoperability of privatekey encoding is less often a concern.

Translating C# RSACryptoServiceProvider code to Java

I need to encrypt String for project related purpose and was given the below code for the same by vendor.
public static string EncryptString(string StringToEncrypt)
{
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
string xmlString = "<RSAKeyValue><Modulus>qqoWhMwGrrEBRr92VYud3j+iIEm7652Fs20HvNckH3tRDJIL465TLy7Cil8VYxJre69zwny1aUAPYItybg5pSbSORmP+hMp6Jhs+mg3qRPvHfNIl23zynb4kAi4Mx/yEkGwsa6L946lZKY8f9UjDkLJY7yXevMML1LT+h/a0a38=</Modulus><Exponent>AQAB</Exponent><P>20PwC7nSsfrfA9pzwSOnRYdbhOYivFSuERxvXHvNjCll5XdmFYYp1d2evXcXbyj3E1k8azce1avQ9njH85NMNQ==</P><Q>x0G0lWcQ13NDhEcWbA7R2W5LPUmRqcjQXo8qFIaHk7LZ7ps9fAk/kOxaCR6hvfczgut1xSpXv6rnQ5IGvxaHYw==</Q><DP>lyybF2qSEvYVxvFZt8MeM/jkJ5gIQPLdZJzHRutwx39PastMjfCHbZW0OYsflBuZZjSzTHSfhNBGbXjO22gmNQ==</DP><DQ>NJVLYa4MTL83Tx4vdZ7HlFi99FOI5ESBcKLZWQdTmg+14XkIVcZfBxDIheWWi3pEFsWqk7ij5Ynlc/iCXUVFvw==</DQ><InverseQ>X5Aw9YSQLSfTSXEykTt7QZe6SUA0QwGph3mUae6A2SaSTmIZTcmSUsJwhL7PLNZKbMKSWXfWoemj0EVUpZbZ3Q==</InverseQ><D>jQL4lEUYCGNMUK6GEezIRgiB5vfFg8ql3DjsOcXxnOmBcEeD913kcYnLSBWEUFW55Xp0xW/RXOOHURgnNnRF3Ty5UR73jPN3/8QgMSxV8OXFo3+QvX+KHNHzf2cjKQDVObJTKxHsHKy+L2qjfULA4e+1cSDNn5zIln2ov51Ou3E=</D></RSAKeyValue>";
provider.FromXmlString(xmlString);
return Convert.ToBase64String(provider.Encrypt(Encoding.ASCII.GetBytes(StringToEncrypt), false));
}
However I need to modify or translate it to JAVA. I have wrote the below method for the same purpose.
public static String EncryptString(String strToBeEncrypted) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException
{
String modulusString = "qqoWhMwGrrEBRr92VYud3j+iIEm7652Fs20HvNckH3tRDJIL465TLy7Cil8VYxJre69zwny1aUAPYItybg5pSbSORmP+hMp6Jhs+mg3qRPvHfNIl23zynb4kAi4Mx/yEkGwsa6L946lZKY8f9UjDkLJY7yXevMML1LT+h/a0a38=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decodeBase64(modulusString);
byte[] exponentBytes = Base64.decodeBase64(publicExponentString);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = strToBeEncrypted.getBytes("US-ASCII");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.encodeBase64String(cipherData);
return encryptedStringBase64;
}
But the sample results do not match.
String is "4111111111111111" and encrypted result should be:
PfU31ai9dSwWX4Im19TlikfO9JetkJbUE+btuvpBuNHTnnfrt4XdM4PmGA19z8rF+lPUC/kcOEXciUSxFrAPyuRJHifIDqWFbbJvPhatbf269BXUiAW31UBX3X5bBOqNWjh4LDitYY0BtarlTU4xzOFyb7vLpLJe9aHGWhzs6q0=
But the result from Java code is
Cxp5AIzTHEkrU6YWwYo5yYvpED2qg9IC/0ct+tRgDZi9fJb8LAk+E1l9ljEt7MFQ2KB/exo4NYwijnBKYPeLStXyfVO1Bj6S76zMeKygAlCtDukq1UhJaJKaCXY94wi9Kel09VTmj+VByIYvAGUFqZGaK1CyLnd8QXMcdcWi3sA=
Every encryption algorithm needs to be randomized in order to provide semantic security. Otherwise, an attacker might notice that you've sent the same message again, just by observing ciphertexts. In symmetric ciphers, this property is achieved by a random IV. In RSA, this is achieved by a randomized padding (PKCS#1 v1.5 type 2 and PKCS#1 v2.x OAEP are randomized).
You can check whether the padding is randomized by running the encryption again with the same key and plaintext, and comparing the ciphertexts to previous ciphertexts. If the ciphertexts change in either C# or Java between executions, then you will not be able to tell whether the encryption is compatible, just by looking at the ciphertexts.
The proper way to check this, would be to encrypt something in one language and then decrypt in the other. For full compatibility, you should also try it the other way around.
Looking at your code, both seem equivalent, because false is passed as the second parameter into RSACryptoServiceProvider#Encrypt to use PKCS#1 v1.5 padding, and Cipher.getInstance("RSA/ECB/PKCS1PADDING") requests the same padding. The input/output encodings also seem equivalent. So, yes this code will be equivalent.
PKCS#1 v1.5 padding should not be used nowadays, because it is vulnerable against a Bleichenbacher attack (reference). You should use OAEP for encryption and PSS for signing, which are considered secure. C# and Java both support OAEP, but there may be differences in the default hash functions that are used (hash and MGF1).

Calculating ECDSA signature in Java as per an RFC test vector

I am writing a test harness in java for a program relating to the ikev2 protocol. As part of this i need to be able to calculate an ECDSA signature (specifically using the NIST P-256 curve).
RFC 4754 Describes the the use of ECDSA in IKEv2 and helpfully provides a set of test vectors (Including for the p256 curve that i need).
I am trying to run the ECDSA-256 Test Vector values (Section 8.1 in the RFC) through java's ECDSA signature implementation using the following code:
//"abc" for the input
byte[] input = { 0x61, 0x62, 0x63 };
//Ugly way of getting the ECParameterSpec for the P-256 curve by name as opposed to specifying all the parameters manually.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec kpgparams = new ECGenParameterSpec("secp256r1");
kpg.initialize(kpgparams);
ECParameterSpec params = ((ECPublicKey) kpg.generateKeyPair().getPublic()).getParams();
//Create the static private key W from the Test Vector
ECPrivateKeySpec static_privates = new ECPrivateKeySpec(new BigInteger("DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", 16), params);
KeyFactory kf = KeyFactory.getInstance("EC");
ECPrivateKey spriv = (ECPrivateKey) kf.generatePrivate(static_privates);
//Perfrom ECDSA signature of the data with SHA-256 as the hash algorithm
Signature dsa = Signature.getInstance("SHA256withECDSA");
dsa.initSign(spriv);
dsa.update(input);
byte[] output = dsa.sign();
System.out.println("Result: " + new BigInteger(1, output).toString(16));
The result should be:
CB28E099 9B9C7715 FD0A80D8 E47A7707 9716CBBF 917DD72E
97566EA1 C066957C 86FA3BB4 E26CAD5B F90B7F81 899256CE 7594BB1E A0C89212
748BFF3B 3D5B0315
Instead I get:
30460221 00dd9131 edeb5efd c5e718df c8a7ab2d 5532b85b 7d4c012a e5a4e90c 3b824ab5 d7022100 9a8a2b12 9e10a2ff 7066ff79 89aa73d5 ba37c868 5ec36517 216e2e43 ffa876d7
I know that the length difference is due to Java ASN.1 Encoding the signature. However, the rest of it is completely wrong and I'm stumped as to why.
Any help or advice would be greatly appreciated!
P.S I am not a ECDSA or Java crypto expert so it is probably a stupid mistake I am making
I'm guessing that each time you run your program, you get a different signature value for the same plaintext (to-be-signed) input.
ECDSA specifies that a random ephemeral ECDSA private key be generated per signature. To that end, Signature.getInstance("SHA256withECDSA") doesn't let you specify an ephemeral key (this is a good thing, to prevent many a self shot in the foot!). Instead, it gets its own SecureRandom instance that will make your output nondeterministic.
This probably means you can't use JCE (Signature.getInstance()) for test vector validation.
What you could do is extend SecureRandom in a way that it returns deterministic data. Obviously you shouldn't use this in a real deployment:
public class FixedSecureRandom extends SecureRandom {
private static boolean debug = false;
private static final long serialVersionUID = 1L;
public FixedSecureRandom() { }
private int nextBytesIndex = 0;
private byte[] nextBytesValues = null;
public void setBytes(byte[] values) {
this.nextBytesValues = values;
}
public void nextBytes(byte[] b) {
if (nextBytesValues==null) {
super.nextBytes(b);
} else if (nextBytesValues.length==0) {
super.nextBytes(b);
} else {
for (int i=0; i<b.length; i++) {
b[i] = nextBytesValues[nextBytesIndex];
nextBytesIndex = (nextBytesIndex + 1) % nextBytesValues.length;
}
}
}
}
Phew. Ok now you have a SecureRandom class that returns you some number of known bytes, then falls back to a real SecureRandom after that. I'll say it again (excuse the shouting) - DO NOT USE THIS IN PRODUCTION!
Next you'll need to use a ECDSA implementation that lets you specify your own SecureRandom. You can use BouncyCastle's ECDSASigner for this purpose. Except here you're going to give it your own bootlegged FixedSecureRandom, so that when it calls secureRandom.getBytes(), it gets the bytes you want it to. This lets you control the ephemeral key to match that specified in the test vectors. You may need to massage the actual bytes (eg. add zero pre-padding) to match what ECDSASigner is going to request.
ECPrivateKeyParameters ecPriv = ...; // this is the user's EC private key (not ephemeral)
FixedSecureRandom fsr_k = new FixedSecureRandom();
fsr_k.setBytes(tempKeyK);
ECDSASigner signer = new ECDSASigner();
ParametersWithRandom ecdsaprivrand = new ParametersWithRandom(ecPriv, fsr_k);
signer.init(true, ecdsaprivrand);
Note that BC's ECDSASigner implements only the EC signature part, not the hashing. You'll still need to do your own hashing (assuming your input data is in data):
Digest md = new SHA256Digest()
md.reset();
md.update(data, 0, data.length);
byte[] hash = new byte[md.getDigestSize()];
md.doFinal(hash, 0);
before you create the ECDSA signature:
BigInteger[] sig = signer.generateSignature(hash);
Finally, this BigInteger[] (should be length==2) are the (r,s) values. You'll need to ASN.1 DER-encode it, which should give you the droids bytes you're looking for.
here's my complete test following tsechin's solution using BouncyCastle but sticking to good old JCA API:
byte[] input = { 0x61, 0x62, 0x63 };
//Create the static private key W from the Test Vector
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
org.bouncycastle.jce.spec.ECPrivateKeySpec privateKeySpec = new org.bouncycastle.jce.spec.ECPrivateKeySpec(new BigInteger("DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", 16), parameterSpec);
KeyFactory kf = KeyFactory.getInstance("EC");
ECPrivateKey spriv = (ECPrivateKey) kf.generatePrivate(privateKeySpec);
//Perfrom ECDSA signature of the data with SHA-256 as the hash algorithm
Signature dsa = Signature.getInstance("SHA256withECDSA", "BC");
FixedSecureRandom random = new FixedSecureRandom();
random.setBytes(Hex.decode("9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE"));
dsa.initSign(spriv, random);
dsa.update(input);
byte[] output = dsa.sign();
// compare the signature with the expected reference values
ASN1Sequence sequence = ASN1Sequence.getInstance(output);
DERInteger r = (DERInteger) sequence.getObjectAt(0);
DERInteger s = (DERInteger) sequence.getObjectAt(1);
Assert.assertEquals(r.getValue(), new BigInteger("CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", 16));
Assert.assertEquals(s.getValue(), new BigInteger("86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", 16));

Encryption in Android using RSA algorithm with given modulus and exponent

I have previously used a RSACryptoServiceProvider in C# to encrypt some data, and now I have to replicate this encryption in an Android program. I want my Android program to generate the same result as I got in my C# program.
Public Key:
<RSAKeyValue>
<Modulus>zz4qdc39y1BHyJgVXUkINJSbsUd1ZJPISyE9nNGjqgR+ZO1a4cE3ViVCSZCw+6dBdVMFNjzZPBxl0mT57GIq7rcuoT0scesZgxOftbMasPbxp0BGrh3HTpbBMJdCopgcYV98CZERakb8Pgbb0ne/DiW9Aq0kfTBE02/iEHRNuqMNfo1GFo55m0OKbxVoM6UBb8AITQ6lbdvfCgeIvMzRlVrHCwxUNrrX5cS6gurEfJ8Da+prKQmwWpFCkwDkPWje2W+bTSPUc9l6Ads0UimYE5sGs4Zsfz6Eocz4rJjR+qCiB8qt6HtdyjKo0auqYzyXIjdRv2950flc9tOh5bRlQQ==
</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Java Encryption Program:
byte[] modulusBytes = Base64.decode(Modoutput.getBytes("UTF-8"),
Base64.DEFAULT);
byte[] exponentBytes = Base64.decode(Expoutput.getBytes("UTF-8"),
Base64.DEFAULT);
BigInteger e = new BigInteger(1, exponentBytes);
BigInteger m = new BigInteger(1, modulusBytes);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKeyn = fact.generatePublic(keySpec);
Log.i("Publickey", pubKeyn.toString());
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, pubKeyn);
byte[] encryptedByteData = cipher.doFinal(byteData);
String outputEncrypted = Base64.encodeToString(encryptedByteData,
Base64.NO_WRAP);
Log.i("Encrypteddata", outputEncrypted);
I tried the above code but it gives an entirely different output from C#. Can anyone tell me what is wrong with my code? Thanks in advance.
Edit: As requested, here is the C# code for which I am trying to replicate the encrypted output in Java:
public static string EncryptText(string text, int keySize,
string publicKeyXml) {
var encrypted = Encrypt(Encoding.UTF8.GetBytes(text), keySize,
publicKeyXml);
return Convert.ToBase64String(encrypted);
}
public static byte[] Encrypt(byte[] data, int keySize, string publicKeyXml) {
if (data == null || data.Length == 0)
throw new ArgumentException("Data are empty", "data");
int maxLength = GetMaxDataLength(keySize);
if (data.Length > maxLength)
throw new ArgumentException(String.Format(
"Maximum data length is {0}", maxLength), "data");
if (!IsKeySizeValid(keySize))
throw new ArgumentException("Key size is not valid", "keySize");
if (String.IsNullOrEmpty(publicKeyXml))
throw new ArgumentException("Key is null or empty", "publicKeyXml");
using (var provider = new RSACryptoServiceProvider(keySize)) {
provider.FromXmlString(publicKeyXml);
return provider.Encrypt(data, _optimalAsymmetricEncryptionPadding);
}
}
Encryption by definition tries to hide all information about the plain text. This includes information about identical plain text. To do this it uses some kind of random within the various padding modes (e.g. PKCS#1 v1.5 compatible padding or OAEP padding for RSA). So speaking from a cryptographic standpoint, the implementation is broken if you ever get an identical result.
The method to check if the ciphertext is correct is by decrypting it using the private key. If that results in the plaintext you started with then your implementation is correct.
[EDIT] Note that you are using OAEP encryption in the C# code, while Java uses the PKCS#1 v1.5 compatible scheme by default. You should use "RSA/None/OAEPWithSHA1AndMGF1Padding" or "RSA/ECB/OAEPWithSHA1AndMGF1Padding". If it is not available add the Bouncy Castle provider.

How Do i compress or encode the Elliptic curve public key and put it over the network?

I am developing Distributed digital signature that signs a document and send it through network to the Application Server.I am using socket programming in java to do it. I think the public key should be encoded or compressed i.e the x and y values are somehow represented as a single binary data and saved in a public registry or network.But i don't know how to do it in java.
// I have class like this
public class CryptoSystem{
EllipticCurve ec = new EllipticCurve(new P192());
//-------------------
//--------------------
public ECKeyPair generatekeyPair()
{
return ECKeyPair(ec);
}
}
// i don't think i have problem in the above
CryptoSystem crypto = new CryptoSystem();
ECKeyPair keyPair = crypto.generateKeyPair();
BigInteger prvKey = keyPair.getPrivateKey();
ECPoint pubKey = keyPair.getPublicKey();
// recommend me here to compress and send it the public key to a shared network.
I want to know how to encode the public key and domain parameters, so that the verifier of the signature will decode it to use it.because when you send them over the network to the verifier u gonna have to encode the as a single byte array.i am no using Bouncy Castle Provider. The whole implementation of ECDSA algorithm is my project
Elliptic curve points are almost always encoded using the encoding specified in X9.62.
It is optional to use point compression. It is trivial to encode using point compression, but decoding a compressed point needs a bit more work, so unless you really need to save the extra bytes, I would not bother. Let me know if you need it, and I will add the details. You can recognize X9.62 encoded points with point compression by the first byte, which will be 0x02 or 0x03.
Encoding without point compression is really simple: start with a 0x04 (to indicate no compression). Then follow with first the x coordinate, then the y coordinate, both zero-padded on the left up to the size in bytes of the field:
int qLength = (q.bitLength()+7)/8;
byte[] xArr = toUnsignedByteArray(x);
byte[] yArr = toUnsignedByteArray(y);
byte[] res = new byte[1+2*qLength];
res[0] = 0x04;
System.arraycopy(xArr, 0, res, qLength - xArr.length, xArr.length);
System.arraycopy(yArr, 0, res, 2* qLength - yArr.length, nLength);
Decoding this is of course trivial.
I'm pretty sure that the BC implementation uses X9.63 encoding, so these would be rather standardized encodings. You will need to add the Bouncy Castle provider to your JRE (Security.addProvider(new BouncyCastleProvider()), see the bouncy documentation.
public static void showECKeyEncodings() {
try {
KeyPairGenerator kp = KeyPairGenerator.getInstance("ECDSA");
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable
.getParameterSpec("prime192v1");
kp.initialize(ecSpec);
KeyPair keyPair = kp.generateKeyPair();
PrivateKey privKey = keyPair.getPrivate();
byte[] encodedPrivKey = privKey.getEncoded();
System.out.println(toHex(encodedPrivKey));
PublicKey pubKey = keyPair.getPublic();
byte[] encodedPubKey = pubKey.getEncoded();
System.out.println(toHex(encodedPubKey));
KeyFactory kf = KeyFactory.getInstance("ECDSA");
PublicKey pubKey2 = kf.generatePublic(new X509EncodedKeySpec(encodedPubKey));
if (Arrays.equals(pubKey2.getEncoded(), encodedPubKey)) {
System.out.println("That worked for the public key");
}
PrivateKey privKey2 = kf.generatePrivate(new PKCS8EncodedKeySpec(encodedPrivKey));
if (Arrays.equals(privKey2.getEncoded(), encodedPrivKey)) {
System.out.println("That worked for the private key");
}
} catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
}
}

Categories