Related
I found on Google this code for encrypt/decrypt a string in Java:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] input = "test".getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
System.out.println(new String(input));
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
System.out.println(new String(cipherText));
System.out.println(ctLength);
// decryption pass
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
ptLength += cipher.doFinal(plainText, ptLength);
System.out.println(new String(plainText));
System.out.println(ptLength);
And this is the output (screenshot because I can't copy-paste some characters):
output screenshot
My question is:
Why the first input "test" is different from the second (decrypted) "test"?
I need this code to encrypt a password and save it on a TXT file and then read this encrypted password from the TXT file and decrypt it..
But if these two outputs are different I can't do this.
Second question:
Is it possible to exclude ";" from the encrypted text?
Can someone help me, please? Thanks!
If you read the documentation for getOutputSize() then you will see that it returns the maximum amount of plaintext to expect. The cipher instance cannot know how much padding is added, so it guesses high. You will have to resize the byte array when you are using ECB or CBC mode (or any other non-streaming mode).
System.out.println(ctLength);
As you can see, ctLength does have the correct size. Use Arrays.copyOf(plainText, ptLength) to get the right number of bytes, or use the four parameter String constructor (new String(plainText, 0, ptLength, StandardCharsets.UTF_8)) in case you're just interested in the string.
The ciphertext consists of random characters. It actually depends on your standard character set what you see on the screen. If you really need text, then you can base 64 encode the ciphertext.
ECB mode encryption is not suitable to encrypt strings. You should try and use a different mode that includes setting / storing an IV.
I'd use new String(StandardCharsets.UTF_8) and String#getBytes(StandardCharsets.UTF_8) to convert to and from strings. If you don't specify the character set then it uses the system default character set, and that means decrypting your passwords won't work on all systems. The allowed characters also differ with Linux & Android defaulting on UTF-8 while Java SE on Windows (still?) defaults to the Windows-1252 (extended Western-Latin) character set.
There is absolutely no need to use the Bouncy Castle provider for AES encryption (the compatible padding string is "PKCS5Padding").
Please don't grab random code samples from Google. You need to understand cryptography before you start implementing it. The chances that you grab a secure code sample is practically zero unfortunately.
I have some server that provides access to data by cryptographic API. I need to write client in C# that can create requests to server and read responses from.
For doing it I need to create public and private RSA keys and convert them to bytes array. I had the working example in java:
java.security.KeyPairjava.security.KeyPair keypair = keyGen.genKeyPair();
byte[] pubKeyBytes = keypair.getPublic().getEncoded();
byte[] privKeyBytes = keypair.getPrivate().getEncoded();
I tried to do the same with C# in .NET:
RSACryptoServiceProvider keyPair = new RSACryptoServiceProvider(2048);
var publicKey = keyPair.ExportParameters(false);
var privateKey = keyPair.ExportParameters(true);
And I don't know how to do it. I have D, Dp, DQ, InverseQ, Modulus, Exponent as properties of publicKey and privateKey, but in java sample those key looks like single united keys. Which one of D, Dp, DQ, InverseQ, Modulus, Exponent I should use for my task? What the way to do the same as in java example, but in C#?
According to https://docs.oracle.com/javase/7/docs/api/java/security/Key.html#getFormat() the default for a public key encoding is X.509 SubjectPublicKeyInfo and for a private key is PKCS#8 PrivateKeyInfo.
There are a number of questions (like Correctly create RSACryptoServiceProvider from public key) on creating RSAParameters from a SubjectPublicKeyInfo, but not as many for the reverse.
If you're creating your key via RSACryptoServiceProvider then the new key will always have an exponent value of 0x010001, which means the only variable sized piece of data that you have to contend with is the modulus value. The reason that this is important is that a SubjectPublicKeyInfo is (almost always) encoded in DER (defined by ITU-T X.690), which uses length-prefixed values. The ASN.1 (ITU-T X.680) is defined in RFC 5280 as
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
The encoded value for the AlgorithmIdentifier for RSA is
30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00
(aka SEQUENCE(OID("1.2.840.113549.1.1.1"), NULL))
The value for subjectPublicKey depends on the algorithm. For RSA it's RSAPublicKey, defined in RFC 3447 as
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e }
The encoding for an INTEGER is 02 (then the length) then the signed big-endian value. So, assuming that your Exponent value is 01 00 01 the encoded value is 02 03 01 00 01. The modulus length depends on the size of your key.
int modulusBytes = parameters.Modulus.Length;
if (parameters.Modulus[0] >= 0x80)
modulusBytes++;
RSACryptoServiceProvider should always create keys that need the extra byte, but technically keys could exist which don't. The reason we need it is that parameters.Modulus is an UNsigned big-endian encoding, and if the high bit is set then we would be encoding a negative number into the RSAPublicKey. We fix that by inserting an 00 byte to keep the sign bit clear.
The length bytes for the modulus are slightly tricky. If the modulus is representable in 127 bytes or fewer (RSA-1015 or smaller) then you just use one byte for that value. Otherwise you need the smallest number of bytes to report the number, plus one. That extra byte (the first one, actually) says how many bytes the length is. So 128-255 is one byte, 81. 256-65535 is two, so 82.
We then need to wrap that into a BIT STRING value, which is easy (if we ignore the hard parts, since they're not relevant here). And then wrap everything else up in a SEQUENCE, which is easy.
Quick and dirty, only works on a 2048-bit key with exponent=0x010001:
private static byte[] s_prefix =
{
0x30, 0x82, 0x01, 0x22,
0x30, 0x0D,
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
0x05, 0x00,
0x03, 0x82, 0x01, 0x0F,
0x00,
0x30, 0x82, 0x01, 0x0A,
0x02, 0x82, 0x01, 0x01, 0x00
};
private static byte[] s_suffix = { 0x02, 0x03, 0x01, 0x00, 0x01 };
private static byte[] MakeSubjectPublicInfoEasy2048(RSA rsa)
{
if (rsa.KeySize != 2048)
throw new ArgumentException(nameof(rsa));
RSAParameters rsaParameters = rsa.ExportParameters(false);
if (Convert.ToBase64String(rsaParameters.Exponent) != "AQAB")
{
throw new ArgumentException(nameof(rsa));
}
return s_prefix.Concat(rsaParameters.Modulus).Concat(s_suffix).ToArray();
}
Or, for a general-purpose response (that creates a lot of temporary byte[]s):
private static byte[] MakeTagLengthValue(byte tag, byte[] value, int index = 0, int length = -1)
{
if (length == -1)
{
length = value.Length - index;
}
byte[] data;
if (length < 0x80)
{
data = new byte[length + 2];
data[1] = (byte)length;
}
else if (length <= 0xFF)
{
data = new byte[length + 3];
data[1] = 0x81;
data[2] = (byte)length;
}
else if (length <= 0xFFFF)
{
data = new byte[length + 4];
data[1] = 0x82;
data[2] = (byte)(length >> 8);
data[3] = unchecked((byte)length);
}
else
{
throw new InvalidOperationException("Continue the pattern");
}
data[0] = tag;
int dataOffset = data.Length - length;
Buffer.BlockCopy(value, index, data, dataOffset, length);
return data;
}
private static byte[] MakeInteger(byte[] unsignedBigEndianValue)
{
if (unsignedBigEndianValue[0] >= 0x80)
{
byte[] tmp = new byte[unsignedBigEndianValue.Length + 1];
Buffer.BlockCopy(unsignedBigEndianValue, 0, tmp, 1, unsignedBigEndianValue.Length);
return MakeTagLengthValue(0x02, tmp);
}
for (int i = 0; i < unsignedBigEndianValue.Length; i++)
{
if (unsignedBigEndianValue[i] != 0)
{
if (unsignedBigEndianValue[i] >= 0x80)
{
i--;
}
return MakeTagLengthValue(0x02, unsignedBigEndianValue, i);
}
}
// All bytes were 0, encode 0.
return MakeTagLengthValue(0x02, unsignedBigEndianValue, 0, 1);
}
private static byte[] MakeSequence(params byte[][] data)
{
return MakeTagLengthValue(0x30, data.SelectMany(a => a).ToArray());
}
private static byte[] MakeBitString(byte[] data)
{
byte[] tmp = new byte[data.Length + 1];
// Insert a 0x00 byte for the unused bit count value
Buffer.BlockCopy(data, 0, tmp, 1, data.Length);
return MakeTagLengthValue(0x03, tmp);
}
private static byte[] s_rsaAlgorithmId = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
private static byte[] ExportSubjectPublicKeyInfo(RSA rsa)
{
RSAParameters parameters = rsa.ExportParameters(false);
return MakeSequence(
s_rsaAlgorithmId,
MakeBitString(
MakeSequence(
MakeInteger(parameters.Modulus),
MakeInteger(parameters.Exponent))));
}
You shouldn't really need the encoded private key. But, if you really do, you need the general-purpose approach because there's a lot of room for variability in the private key data.
PrivateKeyInfo is defined in RFC 5208 as
PrivateKeyInfo ::= SEQUENCE {
version Version,
privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
privateKey PrivateKey,
attributes [0] IMPLICIT Attributes OPTIONAL }
Version ::= INTEGER
PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
PrivateKey ::= OCTET STRING
Attributes ::= SET OF Attribute
It also says the current version number is 0.
The octet string of the private key is defined by the algorithm. For RSA we see in RFC 3447, along with RSAPublicKey:
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL }
Ignore otherPrimeInfos. It doesn't, and shouldn't ever, apply. Therefore the version number to use is 0.
Taking utility methods already defined, we get the rest by
private static byte[] MakeOctetString(byte[] data)
{
return MakeTagLengthValue(0x04, data);
}
private static byte[] s_integerZero = new byte[] { 0x02, 0x01, 0x00 };
private static byte[] ExportPrivateKeyInfo(RSA rsa)
{
RSAParameters parameters = rsa.ExportParameters(true);
return MakeSequence(
s_integerZero,
s_rsaAlgorithmId,
MakeOctetString(
MakeSequence(
s_integerZero,
MakeInteger(parameters.Modulus),
MakeInteger(parameters.Exponent),
MakeInteger(parameters.D),
MakeInteger(parameters.P),
MakeInteger(parameters.Q),
MakeInteger(parameters.DP),
MakeInteger(parameters.DQ),
MakeInteger(parameters.InverseQ))));
}
Making all of this easier is on the feature roadmap for .NET Core (https://github.com/dotnet/corefx/issues/20414 - doesn't say export, but where there's an import there's usually an export :))
Save your output to a file and you can check it with openssl rsa -inform der -pubin -text -in pub.key and openssl rsa -inform der -text -in priv.key
You need to use the ExportCspBlob method:
RSACryptoServiceProvider keyPair = new RSACryptoServiceProvider(2048);
var publicKey = keyPair.ExportCspBlob(false);
var privateKey = keyPair.ExportCspBlob(true);
ExportParameters exports the specific parameters from which the keys themselves can be calculated. For more information about those parameters, see the wiki article.
I have a requirement to use AES encryption with specific parameters but the only example provided is in Java. I need to move everything to PHP and I'm not sure how to do it exactly.
In Java the encryption class takes the iv/salt parameters as an array of bytes directly. Something in the likes of:
byte[] iv = {(byte) 0xCB, (byte) 0x35, (byte) 0xF3, (byte) 0x52, (byte) 0x1A, (byte) 0xF7, (byte) 0x38, (byte) 0x0B, (byte) 0x75, (byte) 0x03, (byte) 0x8E, (byte) 0xE0, (byte) 0xEF, (byte) 0x39, (byte) 0x98, (byte) 0xC7};
AlgorithmParameterSpec params = new IvParameterSpec(iv);
but PHP expects a string for input, so I tried to do something like:
private $salt = ['a7', '70', '1f', 'f6', '5e', 'd3', '29', '8f'];
private $iv = ['cb', '35', 'f1', '52', '1b', 'f7', '33', '0b', '75', '03', '8e', 'e0', 'cf', '39', '98', 'c7'];
public function __construct()
{
$iv = implode(array_map("hex2bin", $this->iv));
$this->iv = $iv;
$salt = implode(array_map("hex2bin", $this->salt));
$this->salt = $salt;
}
public function encrypt($unencryptedString)
{
$key = hash_pbkdf2('sha1', $this->passPhrase, $this->salt, $this->iterationCount, $this->keyLen, true);
var_dump($key);
$hash = openssl_encrypt($unencryptedString, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $this->iv);
$encoded = base64_encode($hash);
return $encoded;
}
I imagine I'm not using the iv/salt parameters the same way its used on Java, thats why it doesn't produce the same thing. Suggestions?
Basics (You did good!)
You should always Use a standard library for PHP encryption.
I know we shouldn't be making recommendations but encryption is in my opinion somewhat of an exception and you should utilise tried and tested libraries.
Use either the openssl extensions or libsodium
Answers on using both are linked here.
How to encrypt plaintext with AES-256 CBC in PHP using openssl()?
How to encrypt / decrypt AES with Libsodium-PHP
Problems?
In terms of your implemtation I think the $iv and $key aren't well formed inputs for hex2bin()
$iv = ['0xA7', '0x71', '0x1F', '0xF5', '0x5D', '0xD2', '0x28', '0x8F'];
array_map("hex2bin", $iv);
// outputs Warning: hex2bin(): Input string must be hexadecimal string
Following #Luke's suggestion:
Removing 0x and using lower case in all iv/salt values seems to do it.
private $salt = ['a7', '71', '1f', 'f5', '5d', 'd2', '28', '8f'];
I'm trying to make run example from IBM website.
I wrote this method:
public static byte[] cipher(byte[] inputData) {
Cipher cipher
= Cipher.getInstance(
Cipher.ALG_DES_CBC_NOPAD, true);
DESKey desKey = (DESKey) KeyBuilder.buildKey(
KeyBuilder.TYPE_DES,
KeyBuilder.LENGTH_DES,
false);
byte[] keyBytes = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04};
desKey.setKey(keyBytes, (short) 0);
cipher.init(desKey, Cipher.MODE_ENCRYPT);
byte[] outputData = new byte[8];
cipher.doFinal(inputData, (short) 0, (short) inputData.length, outputData, (short) 0);
return outputData;
}
And call this method cipher("test".getBytes());. When I call this servlet server gives me Internal server error and javacard.security.CryptoException.
I tried ALG_DES_CBC_ISO9797_M1, ALG_DES_CBC_ISO9797_M2 (and others) and got the same exception.
How to make run simple example of cipher on Java Card Connected?
UPDATE
As #vojta said, key must be 8 bytes long. So it must be something like this:
byte[] keyBytes = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04};
I don't know why, but it works only if replace
Cipher cipher = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, true);
with
Cipher cipher = Cipher.getInstance(Cipher.ALG_DES_CBC_ISO9797_M2, false);
I could not find anything about it in documentation.
These lines seem to be wrong:
byte[] keyBytes = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04};
desKey.setKey(keyBytes, (short) 0);
DES key should be longer than 4 bytes, right? Standard DES key is 8 bytes long (with strength of 56 bits).
In addition to #vojta's answer, the input data should be block aligned.
Your input data "test".getBytes() have length 4 which is not valid for Cipher.ALG_DES_CBC_NOPAD (but valid for Cipher.ALG_DES_CBC_ISO9797_M2).
Strange is that this should cause CryptoException.ILLEGAL_USE reason (which is 5 opposed to 3 you are getting)...
I have a file encrypted using the following code in c:
unsigned char ckey[] = "0123456789ABCDEF";
unsigned char iv[8] = {0};
AES_set_encrypt_key(ckey, 128, &key);
AES_ctr128_encrypt(indata, outdata, 16, &key, aesstate.ivec, aesstate.ecount, &aesstate.num);
I have to decrypt this file using java so I was using the code below to do it:
private static final byte[] encryptionKey = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
IvParameterSpec ips = new IvParameterSpec(iv);
Cipher aesCipher = Cipher.getInstance("AES/CTR/NoPadding");
SecretKeySpec aeskeySpec = new SecretKeySpec(encryptionKey, "AES");
aesCipher.init(Cipher.DECRYPT_MODE, aeskeySpec, ips);
FileInputStream is = new FileInputStream(in);
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), aesCipher);
copy(is, os);
os.close();
The JAVA code doesn't give me any error but the output is not correct.
What am I doing wrong?
My main doubts are if i'm using the correct padding (also tried PKCS5Padding without success) and if the key and iv are correct (don't know what the function AES_set_encrypt_key really does...).
** EDIT **
I think I have an answer to my own question, but I still have some doubts.
CTR means counter mode. The function AES_ctr128_encrypt receives as parameters the actual counter (ecount) and the number of blocks used (num).
The file is being encrypted in blocks of 16 bytes, like this:
for(int i = 0; i < length; i+=16)
{
// .. buffer processing here
init_ctr(&aesstate, iv); //Counter call
AES_ctr128_encrypt(indata, outdata, 16, &key, aesstate.ivec, aesstate.ecount, &aesstate.num);
}
the function init_ctr does this:
int init_ctr(struct ctr_state *state, const unsigned char iv[8])
{
state->num = 0;
memset(state->ecount, 0, 16);
memset(state->ivec + 8, 0, 8);
memcpy(state->ivec, iv, 8);
return 0;
}
This means that before every encryption/decryption the C code is resetting the counter and the ivec.
I am trying to decrypt the file as a whole in java. This probably means Java is using the counter correctly but the C code is not as it is resetting the counter at each block.
Is my investigation correct?
I have absolutely NO CONTROL over the C code that is calling openssl. Is there a way of doing the same in JAVA, i.e. resetting the counter at each block of 16? (The API only requests the key, algorithm, mode and IV)
My only other option is to use openssl via JNI but I was trying to avoid it...
Thank you!
I did not try it, but you should be able to effectively emulate what is done there on the C side - decrypt each 16-byte (=128 bit) block separately, and reset the cipher between two calls.
Please note that using CTR mode for just one block, with a zero initialization vector and counter, defeats the goal of CTR mode - it is worse than ECB.
If I see this right, you could try to encrypt some blocks of zeros with your C function (or the equivalent Java version) - these should come out as the same block each time. XOR this block with any ciphertext to get your plaintext back.
This is the equivalent to a Caesar cipher on a 128-bit alphabet (e.g. the 16-byte blocks), the block cipher adds no security here to a simple 128-bit XOR cipher. Guessing one block of plaintext (or more generally, guessing 128 bits at the right positions, not necessary all in the same block) allows getting the effective key, which allows getting all the remaining plaintext blocks.
Your encryption keys are different.
The C code uses the ASCII character codes for 0 through F, whereas the Javacode uses the actual bytes 0 through 16.
There are numerous serious problems with that C code:
As already noted, it is reinitialising the counter on every block. This makes the encryption completely insecure. This can be fixed by calling init_ctr() once only, prior to encrypting the first block.
It is setting the IV statically to zero. A fresh IV should be generated randomly, for example if (!RAND_bytes(iv, 8)) { /* handle error */ }.
The code appears to be directly using a password string as the key. Instead, a key should be generated from the password using a key derivation function like PBKDF2 (implemented in OpenSSL by PKCS5_PBKDF2_HMAC_SHA1()).