Authenticating signature created on c# by public key on java - java

I am new to verification and certificates etc ..
I am facing an issue , that I need to sign a message on c# then verify the signature on java , the issue I ma facing that I am unable to load the public key on java on a (PublicKey) object using the Base64 string generated on c# , I used the following code to generate the private and public key on c# side
CspParameters cspParams = new CspParameters { ProviderType = 1 };
cspParams.KeyContainerName = "MyKeyContainer";
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
System.Diagnostics.Debug.WriteLine("pub:" + publicKey);
System.Diagnostics.Debug.WriteLine("pri:" + privateKey);
Console.WriteLine("Key added to container: \n {0}", rsaProvider.ToXmlString(true));
then I used the following code to create a public key on Java side :
X509EncodedKeySpec specc = new X509EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64("BgIAAACkAABSU0ExAAQAAA......"));
KeyFactory xx = KeyFactory .getInstance("RSA");
PublicKey ssx= xx.generatePublic(specc);
note that I copied the base64 public key string from the c# console .
When I try to run the code on java side the I get the following exception :
java.security.spec.InvalidKeySpecException: Inappropriate key specification: invalid key format
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(Unknown Source)
I need to find a way to generate private and public key on c# (and generate a .cer file for the public key) to load it on java side , or find a way to load the base64 public key string into a (Publickey) object on java side . please help !

Option 1: Same data, different format.
The easiest way to transmit a public RSA key from .NET is to check that the public exponent value is { 01 00 01 } and then send the modulus value. On the receiver side you accept the modulus and assert the public exponent.
RSAParameters keyParams = rsa.ExportParameters(false);
if (!keyParams.Exponent.SequenceEqual(new byte[] { 0x01, 0x00, 0x01 }))
throw new InvalidOperationException();
Send(keyParams.Modulus);
Then Creating RSA keys from known parameters in Java says you can straightforwardly recover it on the Java side.
Option 2: Same format, different parser.
The next option you have is to keep using the CSP blob, but writing a parser in Java. The data is the result of calling CryptExportKey with PUBLICKEYBLOB, making your data layout as described at https://msdn.microsoft.com/en-us/library/ee442238.aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx#pub_BLOB.
In summary:
A header (which you could decide to skip, or just test it for equal to the fixed value(s) that you expect):
A byte, value 0x06 (PUBLICKEYBLOB)
A byte, value 0x02 (blob v2)
A short, value 0x0000 (reserved)
An integer (stored as little-endian) identifying the key as RSA (0x0000A400 or 0x00002400)
An integer (stored as little-endian) identifying the next segment as an RSA public key (a bit redundant, but technically a different structure now): 0x31415352
After all that comes the relevant data:
The bit-length of the modulus stored as a little-endian unsigned integer. For your 1024-bit example this will be 1024, aka 0x00000400, aka { 00 04 00 00 }(LE).
The public exponent, stored as a little-endian unsigned integer. This is almost always 0x00010001 (aka { 01 00 01 00 }), but since it's there you should respect it.
The next bitLen/8 bytes represent the modulus value. Since this is the public key that should be "the rest of the bytes in this array".
Option 3: Build a certificate
.NET Framework doesn't have this capability built-in (as of the current version, 4.7). You can P/Invoke to CertCreateSelfSignCertificate, but that would involve quite a lot of change (since RSACryptoServiceProvider won't let you get at the key handle, so you'll have to P/Invoke all of that, too).
You could "bit bang" out the DER-encoded certificate yourself. While fun, this is hard to get right, and probably not a viable path.
If you can move to .NET Core, the ability to create certificates has been added to .NET Core 2.0 via the CertificateRequest class. For a very simple certificate from your key:
var certReq = new CertificateRequest(
"CN=SubjectCN",
rsaProvider,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
// add any extensions you want. I'm not adding any because I said "simple".
DateTimeOffset now = DateTimeOffset.UtcNow;
X509Certificate2 cert = certReq.CreateSelfSigned(now, now.AddMinutes(90));
byte[] xfer = cert.RawData;

Related

Bouncy Castle Curve25519 private key from Scrypt output

I am trying to achieve ECIES encryption, for which below code is working.
X9ECParameters ecP = CustomNamedCurves.getByName("curve25519");
ECParameterSpec ecSpec = EC5Util.convertToSpec(ecP);
BigInteger d = new BigInteger("145642755521911534651321230007534120304391871461646461466464667494947990");
ECPrivateKeySpec priKeySpec = new ECPrivateKeySpec(
d, // d
ecSpec);
ECPoint Q = new FixedPointCombMultiplier().multiply(params.getG(), d.multiply(BigInteger.valueOf(-1)));
Q = Q.normalize();
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(
new ECPoint(Q.getAffineXCoord().toBigInteger(), Q.getAffineYCoord().toBigInteger()), // Q
ecSpec);
KeyFactory factTrial = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
BCECPrivateKey sKey = (BCECPrivateKey) factTrial.generatePrivate(priKeySpec);
PublicKey vKey = factTrial.generatePublic(pubKeySpec);
Cipher c = Cipher.getInstance("ECIESwithAES-CBC",BouncyCastleProvider.PROVIDER_NAME);
byte[] encodeBytes = c.doFinal(data.getBytes());
String encrypt = Base64.getEncoder().encodeToString(encodeBytes);
Cipher c2 = Cipher.getInstance("ECIESwithAES-CBC",BouncyCastleProvider.PROVIDER_NAME);
c2.init(Cipher.DECRYPT_MODE,sKey, c.getParameters());
byte[] decodeBytes = c2.doFinal(encodeBytes);
String deCrypt = new String(decodeBytes,"UTF-8");
Issue is the private key element 'd'. If I try to replace it with output of scrypt hash, private key fails to be converted in PrivateKey instance.
I have gone through net resources https://github.com/bcgit/bc-java/issues/251, https://crypto.stackexchange.com/questions/51703/how-to-convert-from-curve25519-33-byte-to-32-byte-representation, https://crypto.stackexchange.com/questions/72134/raw-curve25519-public-key-points.
Above resources suggest the way Bouncy Castle for Curve25519 interprets private key is different from the ways some internet resource suggest. In post https://crypto.stackexchange.com/questions/51703/how-to-convert-from-curve25519-33-byte-to-32-byte-representation there is mention as follows.
According to the curve25519 paper a x25519 public key can be represented in 32 bytes.
The x25519 library I'm using (bouncycastle) however gives me a 33 byte representation according to this standard.
I am very new to ECC, these resources are confusing me, the difference between lengths, the style of encoding big vs. little.
I have tried libSodium 'crypto_box_easy' and 'crypto_box_open_easy'
via its Java binding and it works all fine. The 32 byte scrypt output
is used by 'crypto_box_seed_keypair' to generate key pair which is
used for encryption process.
As I see some maths is involved here which I lack at present or I am failing to see the conversion.
I have to go this route Scrypt output -> key pair -> use for encryption
Using directly KeyGenerator from BC is working, but that utilises SecureRandom, but I need the output of Scrypt to behave as private key.
Questions:
I'll really appreciate someone helps me understand the difference between libSodium and Bouncy Castle approach. libSodium mentions it uses X25519. When I try to create X25519 key from 32 bytes, but BC Cipher(ECIESwithAES-CBC) then complaints it is not a EC Point, from this resource 'https://github.com/bcgit/bc-java/issues/251' it seems there are differences in that too (Curve25519 vs X25519).
The private key 'd', how to interpret it. I have seen these random values in Bouncy Castle documentation and test cases, is this simply a number in the prescribed range for valid keys? This number is treated (little vs. big endian) before creating BigInteger instance. I mean the raw value of 'd' in the my code example was converted from some other number?
The struggle between understanding different mechanism of Curve25519 and BC API itself, I am really confused.
Some pointers to further my research would be of great help.

how to specify signature length for java.security.Signature sign method

For my application, I'm trying to sign some byte contents using java.security.Signature class. The problem I'm having is that signature is never generated at a fixed length. For instance, sometimes it is generated at a length of 135, 136 or 137 bytes. Is there a way to specify the length or some padding at the end? Any other ideas or comments are appreciated.
private byte[] ecdsaSign(ECPrivateKey key, byte[] content) throws Exception {
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA", "SC");
ecdsaSign.initSign(key);
ecdsaSign.update(content);
byte[] signature = ecdsaSign.sign();
return signature;
}
For ECDSA Java crypto uses the ASN.1 DER encoding standardized by X9.62, SEC1 and rfc 3279 sec 2.2.3, which varies slightly in length. This is covered in more detail on another Stack: https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-ASN.1 and https://crypto.stackexchange.com/questions/33095/shouldnt-a-signature-using-ecdsa-be-exactly-96-bytes-not-102-or-103 and https://crypto.stackexchange.com/questions/37528/why-do-openssl-elliptic-curve-digital-signatures-differ-by-one-byte
This is also true for DSA, but not RSA, where signatures (and cryptograms since RSA supports both signature and encryption) are fixed length for a given key, as defined by I2OS and OS2I in PKCS1.
If you want a different encoding, such as the fixed-length one used by PKCS11 (and your provider name "SC" suggests that possibility), you must convert it.
Added 2019-10: you no longer have to do it yourself in Java; BouncyCastle since 1.61 (2019-02) correctly supports this, as does SunEC in Java 9 up (2018-12). See later near-dupe Java ECDSAwithSHA256 signature with inconsistent length .

Hashing using md5 and sign using private certificate

I have a requirement wherein I have to generate a URL where one of the parameter is signature and signature has to be generated using below requirement in a Java Application:
The other 4 URL parameter values should be hashed (in the order specified below) using MD5 and sign using the private certificate. (The signature will be DER-encoded PKCS #1 block as defined in RSA Laboratory's Public Key Cryptography Standards Note #1.) The resulting digest should be converted to ASCII character set using base64 and then encoded to comply with HTTP URL character set limitations.
Order Parameter
1 [queryparameter1]
2.. [queryparameter …] *
3 Expiration
The final url should look something like
https://<ServerName>:<Port>/imageRet/pod?ID=123456789&build=XHB&date=201102151326&expiration=20110218155523&signature=H767dhghjKJ#23mxi
I have never worked on Cryptography before and hence don't know how to start.
Can somebody help how can this be achived.
This will be the signature code
Signature sig = Signature.getInstance("MD5withRSA");
sig.initSign(privateKey);
sig.update(canonicalize(params));
byte signature[] = sig.sign();
String signatureB64UrlEncoded = Base64.getUrlEncoder().encodeToString(signature);
Where canonicalize(params) means converting the String parameters of the url to byte[] the way your service specified. You have not given details. This step is not trivial at all because equivalent urls may generate different signatures.
For example
q=hello%20world --> Qazz_tVB-guYai5oW0Eef6BbVP ...
q=hello world --> JJWDEPMQDmffcsjR0dP3vnrkFT ...
An example implementation, but surely not valid...
//Convert params[] to byte[] converting each String to byte with default charset and concatenating results
public byte[] canonicalize(String params[] ) throws IOException{
final ByteArrayOutputStream out = new ByteArrayOutputStream();
for (String param: params){
out.write(param.getBytes());
}
return out.toByteArray();
}
Take a look at Amazon AWS to see an example of how canonicalize a URL
If you finally decide to use a more secure algorithm, simply replace MD5withRSA with for example SHA256withRSA

ColdFusion or Java equivalent to PHP encryption/decryption

I have an API spec that reads:
Encryption Algorithm
The API utilizes the AES-128 (also known as Rijndael-128) algorithm with a 192-bit key in
CBC mode for encryption and decryption of sensitive pieces of information – the password parameter in the user/signin and user/signup methods, the authentication token, etc. The steps of the algorithm are listed below:
Encryption
Pad the input data so that its size is a multiple of the encryption algorithm block size – 16 bytes. In case the length of input data is a multiple of 16, a block of additional 16 bytes needs to be appended. The value of each pad byte is the number of pad bytes as an 'unsigned char'. That is, the last byte of the padded data should always be between 0x01 and 0x10.
Generate a 16-byte long initialization vector (IV) for the encryption algorithm.
Encrypt the padded data using AES-128 with the EK and the generated IV.
Combine the IV with the encrypted data.
Encode the result with urlsafe Base64. The urlsafe Base46 alphabet uses '–' instead of '+' and '_' instead of '/'.
Decryption
Base64-decode the input data.
Extract the first 16 bytes – these are the IV for the AES algorithm.
Decrypt the data using AES-128 with the EK and IV.
Read the value of the last byte of the decrypted data and remove that many bytes off its tail.
The only example provided by the supplier of this API is in PHP, using mcrypt. I know absolutely nothing about PHP, and am not an encryption expert. I need to be able to represent the above algorithm using ColdFusion 10.
I started by trying to take the example PHP files and find equivalents in either the ColdFusion tag or function library, and then by looking for a Java library with the same interface. I just don't know enough to make this happen.
Is there someone here that can point me in the right direction, or work with me offline to assist?
EDIT:
Here's the example given, for the basic task of doing a "check" on the keys (partner key and encryption key) provided to me for use with the API.
Object Client.php, has this constructor:
public function __construct($hostApiUrl, $partnerKey, $encryptionKey, $token = null)
{
$this->_pk = $partnerKey;
$this->_ek = $encryptionKey;
$this->_crypt = new Crypt($encryptionKey);
$this->_url = rtrim($hostApiUrl, '/') . self::BASE_URL;
if ($token) {
$this->setUserSession($token);
}
}
and this is the function I'm attempting to use:
public function checkKeys()
{
$secret = $this->_encodeParam($this->_ek);
$result = $this->call('partner/checkkeys', array(
'secret' => $secret
));
if (!$result || !$this->_isCodeOk($result->code)) {
return false;
}
return true;
}
So the client object already has the partner key and encryption key when this method is called, obviously.
so the "secret" is created by "encoding" the encryption key provided, using _encodeParam() method. that looks like this:
protected function _encodeParam($secret)
{
$secret = "{$secret}:{$this->_pk}";
return $this->_crypt->encrypt($secret);
}
so the secret is appended with the partner key. and then encrypted using this method in the crypt object (AES_BLOCK_SIZE is set as 16):
public function encrypt($data)
{
$pad = self::AES_BLOCK_SIZE - strlen($data) % self::AES_BLOCK_SIZE;
$data .= str_repeat(chr($pad), $pad);
if (stristr(PHP_OS, 'win') !== false) {
$random_source = MCRYPT_RAND;
} else {
$random_source = MCRYPT_DEV_URANDOM;
}
$iv = mcrypt_create_iv(self::AES_BLOCK_SIZE, $random_source);
mcrypt_generic_init($this->_td, $this->_key, $iv);
$data = $iv . mcrypt_generic($this->_td, $data);
mcrypt_generic_deinit($this->_td);
return self::urlsafe_b64encode($data);
}
this is returned back to the above checkKeys() function which sends the request to the API, which then returns a response. That actual API call is a POST which is easy enough to generate of course, but all those encryption hoops, including the MCRYPT library calls, are where I get stuck trying to determine the equivalent in CF10 or Java.
If I were to get an example thus far, I think I'd stand a chance of replicating the other functions in the crypt object (the ones that are even necessary, which may not be, since some may be built right into the CF encrypt() and decrypt() functions). This seems like a reasonable starting point, however.

RSA encryption in Java, decrypt in PHP

Assume I have the following Java code to generate a Public-private keypair:
KeyPairGenerator generator = KeyPairGenerator.getInstance ("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
generator.initialize (1024, random);
KeyPair pair = generator.generateKeyPair();
RSAPrivateKey priv = (RSAPrivateKey)pair.getPrivate();
RSAPublicKey pub = (RSAPublicKey)pair.getPublic();
// Sign a message
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initSign (priv);
dsa.update ("Hello, World".getBytes(), 0, "Hello, World".length());
byte[] out = dsa.sign();
/* save the signature in a file */
FileOutputStream sigfos = new FileOutputStream("sig");
sigfos.write(out);
sigfos.close();
How would one go about and decrypt the file "sig" in PHP? I've read the post: https://stackoverflow.com/a/1662887/414414 which supplies a function to convert a DER file to PEM (Assume I also save the public key from Java).
I have tried something like:
$key = openssl_pkey_get_public ("file://pub_key.pem");
$data = null;
openssl_public_decrypt ( file_get_contents ("sig"), $data, $key);
echo $data, "\n";
It successfully decrypts the message, but it is many weird characters.
Our scenario is a Java client that is sending messages to a PHP server, but encrypts the data with a private key. PHP knows about the public key, which it should use to decrypt and validate the message.
I've read a lot of posts regarding this issue here on SO, but I've come to realize that this is a bit specific issue, especially if there's different algorithms in use, etc. So sorry if this may be a duplicate.
Any feedbacks are greatly appreciated!
an "RSA signature" is usually more than just "encrypt with private key, decrypt with public key", since Public key protocols like PKCS#1 also specify padding schemes, and all signature schemes will encrypt a digest of the message, instead of the full message. I cannot find any documentation if java's signature scheme uses the signature padding scheme specified in PKCS#1, but my suspicion is that it is.
If it is, you will instead want to use the openssl_verify method in PHP, documented here. This will return a 0 or 1 if the signature is invalid or valid, respectively.
In the event that Java does not use a padding scheme, your issue is that the data encrypted in the signature is a hash of the message, instead of the message itself (you can see in the Java code that it uses the SHA-1 hash algorithm). So on the PHP side, you will need to take the sha1 hash of your message using the sha1 method with $raw_output set to true, and compare those strings to ensure your message is valid.
From the snippet
$key = openssl_pkey_get_public ("file://pub_key.pem");
It looks like you're referencing the public key, which would be the wrong one to decrypt. Double check ?

Categories

Resources