I'm in trouble in implementing RSA encryption and decryption in Objective-C, I made it in Java very simply and now I tried to translate this java code in objc.
Here is my java code:
public static byte[] encryptRSA(byte[] text, PublicKey key) throws Exception {
byte[] cipherText = null;
// get an RSA cipher object and print the provider
Cipher cipher = Cipher.getInstance("RSA");
// encrypt the plaintext using the public key
cipher.init(Cipher.ENCRYPT_MODE, key);
cipherText = cipher.doFinal(text);
return cipherText;
}
public static byte[] decryptRSA(byte[] text, PrivateKey key) throws Exception {
byte[] dectyptedText = null;
// decrypt the text using the private key
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
return dectyptedText;
}
and this is how i generate the key pair
String seed = "SOMERANDOMSEED"+Long.toString(System.currentTimeMillis());
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom rand = new SecureRandom(seed.getBytes());
keyGen.initialize(4096,rand);
KeyPair keyPair = keyGen.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
now in objC I have writed some code that sems to work, but I don't know hot to generate the rsa they from a seed, like i do in java, and how to import the key that i save in java with this method
//for import
public static byte[] hexStringToByteArray(String s) {
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
//for export
public static String byteArrayToHexString(byte[] b) {
StringBuilder sb = new StringBuilder(b.length * 2);
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
here is my objc code
//this works properly
+(NSString *)decryptRSA:(NSString *)cipherString key:(SecKeyRef) privateKey {
size_t plainBufferSize = SecKeyGetBlockSize(privateKey);
uint8_t *plainBuffer = malloc(plainBufferSize);
NSData *incomingData = [cipherString decodeFromHexidecimal];
uint8_t *cipherBuffer = (uint8_t*)[incomingData bytes];
size_t cipherBufferSize = SecKeyGetBlockSize(privateKey);
SecKeyDecrypt(privateKey,
kSecPaddingOAEPKey,
cipherBuffer,
cipherBufferSize,
plainBuffer,
&plainBufferSize);
NSData *decryptedData = [NSData dataWithBytes:plainBuffer length:plainBufferSize];
NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
return decryptedString;
}
//this works properly
+(NSString *)encryptRSA:(NSString *)plainTextString key:(SecKeyRef)publicKey {
size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
uint8_t *cipherBuffer = malloc(cipherBufferSize);
uint8_t *nonce = (uint8_t *)[plainTextString UTF8String];
SecKeyEncrypt(publicKey,
kSecPaddingOAEPKey,
nonce,
strlen( (char*)nonce ),
&cipherBuffer[0],
&cipherBufferSize);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
return [encryptedData hexadecimalString];
}
//here i generate the key pair
#define kPublicKeyTag "com.apple.sample.publickey"
#define kPrivateKeyTag "com.apple.sample.privatekey"
//i should use these as seed!?!!?
- (void)generateKeyPair:(NSUInteger)keySize {
OSStatus sanityCheck = noErr;
publicKeyRef = NULL;
privateKeyRef = NULL;
// Container dictionaries.
NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init];
// Set top level dictionary for the keypair.
[keyPairAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(id)kSecAttrKeySizeInBits];
// Set the private key dictionary.
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set the public key dictionary.
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[publicKeyAttr setObject:publicTag forKey:(id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set attributes to top level dictionary.
[keyPairAttr setObject:privateKeyAttr forKey:(id)#kSecPrivateKeyAttrs];
[keyPairAttr setObject:publicKeyAttr forKey:(id)#kSecPublicKeyAttrs];
// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
}
this is the method i use to export keys in objc, it seems work just like the java method
+ (NSString *)fromPrivateKeyToString: (SecKeyRef) privateKey {
size_t pkeySize = SecKeyGetBlockSize(privateKey);
NSData* pkeyData = [NSData dataWithBytes:privateKey length:pkeySize];
NSString* pkeyString = [pkeyData hexadecimalString];
return pkeyString;
}
Although it is not impossible to create the same key pair from a seed, you need to make sure that both the RNG and the key pair generation are exactly identical. Furthermore, the seed to be put in the generator needs to be used in the same way. Neither the RNG or the key pair generation is usually created with compatibility in mind. Actually, the default "SHA1PRNG" has even changed between versions of Java, and the algorithm is not described.
If you want to use the same private key then it is better to generate it and to transport it to the other runtime. There are multiple ways, but one method is to use a (password) encrypted PKCS#8 or PKCS#12 format. Of course the key or password needs to be kept secret, but that's also the case with your seed value.
For more information, see this Q/A. Don't forget to vote up the question and answer over there, I can use some more points on crypto :).
As I explained in my other answer, it is very tricky to generate the same key pair using the same value of the PRNG. But that does not seem to be what you are after. It seems that you want to use your own seeded PRNG to generate the key pair.
In general, the default SecureRandom in Java is seeded by the operating system. The idea that you can supply your own random number generator is that you may get "better" results using for instance your own entropy pool (e.g. from a a hardware random number generator). Normally the default Java PRNG seeded by the operating system would however provide enough random.
As you are using the SecureRandom class, you supplant the operating system provided seed with your own relatively weakly seeded PRNG. currentTimeMilis certainly does not give you much entropy, and the password seems to be static. This is generally not thought to be enough for generating RSA key pairs.
If you really want to you can add some entropy to the pool instead:
// create runtime default PRNG
SecureRandom rng = new SecureRandom();
// make sure that the rng is seeded by the operating system
rng.nextInt();
// add secret to the pool
rng.setSeed("SOME_SECRET".getBytes(StandardCharsets.UTF_8));
// add time information to the pool
rng.setSeed(System.currentTimeMillis());
// use for e.g. RSA key pair generation
There seems to be no method of injecting your own random number generator in Apple's OS X libraries. As indicated, usually the OS provided random number generator is good enough. If you really want to you can write your additional seeds to /dev/random though.
Related
I have a code from creating base 64 hashes
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class ApiSecurityExample {
public static void main(String[] args) {
try {
String secret = "secret";
String message = "Message";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
String hash = Base64.encodeBase64String(sha256_HMAC.doFinal(message.getBytes()));
System.out.println(hash);
}
catch (Exception e){
System.out.println("Error");
}
}
}
there is secret_key in sha256_HMAC.init(secret_key);
when I read, it tells use Key key an Interface.
how to use it?
The example is doing it wrong, as strings should not be used to store keys.
A secret key should consist of bytes that are unpredictable to an adversary. The most logical method to generate those is to use a random number generator, but you can also generate them from key establishment (Diffie-Hellman), using a key derivation function upon another key, ratchets and many other ways.
A somewhat dangerous method is to generate them from a password. For that you normally use a Password Based Key Derivation Function or PBKDF. Java has direct support for PBKDF2 which can be used for this.
So you could create a HMAC key in the following way:
Mac mac = Mac.getInstance("HMACSHA256");
SecureRandom rng = new SecureRandom();
// key size can be anything but should default to the hash / MAC output size for HMAC
byte[] hmacKeyData = new byte[mac.getMacLength()];
rng.nextBytes(hmacKeyData);
SecretKey hmacKey = new SecretKeySpec(hmacKeyData, "HMACSHA256");
Arrays.fill(hmacKeyData, (byte) 0x00);
However, the following code is shorter, probably more descriptive. It also allows hardware devices to be used later on to implement the Mac, although that might be a bit out of your territory.
KeyGenerator kg = KeyGenerator.getInstance("HMACSHA256");
SecretKey hmacKey = kg.generateKey();
Finally, if you still want to use a password, then use PKBDF2 and don't forget to store the salt:
// you don't want to use a string, as you cannot delete strings in Java
char[] password = {'p', 'a', 's', 's' };
SecureRandom rng = new SecureRandom();
byte[] salt = new byte[128 / Byte.SIZE];
rng.nextBytes(salt);
int iterations = 1_000_000;
Mac mac = Mac.getInstance("HMACSHA256");
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, mac.getMacLength() * Byte.SIZE);
SecretKeyFactory pbkdf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hmacKeyData = pbkdf.generateSecret(spec).getEncoded();
SecretKey hmacKey = new SecretKeySpec(hmacKeyData, "HMACSHA256");
// clean up secret material
Arrays.fill(password, (char) 0x0000);
spec.clearPassword();
Arrays.fill(hmacKeyData, (byte) 0x00);
As an attacker may have forever to try passwords if he has a MAC to compare the result with, it would be a very good idea to choose a very complex password though; this is why password based encryption generally is not a good idea.
Key is a generic parent interface used for both SecretKey, PublicKey and PrivateKey. It is used in many classes that represent crypto algorithms as they may be used with any kind of key. For instance Cipher can be used for RSA but also for AES. So the implementation just checks at runtime if the correct key is given.
For Mac it might as well have been SecretKey as a Mac is really always a symmetrical algorithm (an asymmetric form of a Mac is called a Signature after all). Just a HMAC key would not be enough though, as there are also Mac algorithms based on block ciphers such as AES (thus requiring a SecretKey with algorithm "AES").
For convenience, SecretKeySpec also implements SecretKey; that way you don't need the SecretKeyFactory to create a SecretKey. The Java designers kind of forgot about hardware support that does require such as factory, but here we are.
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));
I found a link in stackoverflow here use-3des-encryption-decryption-in-java,but in fact the method uses only two parameter:HG58YZ3CR9" and the "IvParameterSpec iv = new IvParameterSpec(new byte[8]);"
But the most strong option of triple des could use three different key to encrypt the message.So how to do that? I find a mehond in Cipher, which use "SecureRandom" as another parameter.So is this the right way?
The first method code is below:
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class TripleDESTest {
public static void main(String[] args) throws Exception {
String text = "kyle boon";
byte[] codedtext = new TripleDESTest().encrypt(text);
String decodedtext = new TripleDESTest().decrypt(codedtext);
System.out.println(codedtext); // this is a byte array, you'll just see a reference to an array
System.out.println(decodedtext); // This correctly shows "kyle boon"
}
public byte[] encrypt(String message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9"
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
// final String encodedCipherText = new sun.misc.BASE64Encoder()
// .encode(cipherText);
return cipherText;
}
public String decrypt(byte[] message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9"
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
// final byte[] encData = new
// sun.misc.BASE64Decoder().decodeBuffer(message);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, "UTF-8");
}
}
As per this document, simply pass the cipher a key that is 168 bits long.
Keysize must be equal to 112 or 168.
A keysize of 112 will generate a Triple DES key with 2 intermediate keys, and a keysize of 168 will generate a Triple DES key with 3 intermediate keys.
Your code seems to do something questionable to make up for the fact that the output of MD5 is only 128 bits long.
Copy-pasting cryptographic code off the internet will not produce secure applications. Using a static IV compromises several reasons why CBC mode is better than ECB. If you are using a static key, you should probably consider generating random bytes using a secure random number generator instead of deriving the key from a short ASCII string. Also, there is absolutely no reason to use Triple DES instead of AES in new applications.
In principle, the for-next loop to generate the DES ABA key does seem correct. Note that you can provide DESede with a 16 byte key from Java 7 onwards, which amounts to the same thing.
That said, the code you've shown leaves a lot to be desired:
I is not secure:
the key is not generated by a Password Based Key Derivation Function (PBKDF) using the (password?) string
the key is composed of two keys instead of three (using a triple DES or TDEA with an ABA key)
the IV is set to all zero's instead of being randomized
the "password" string is too short
Furthermore the following code mistakes can be seen:
using new sun.misc.BASE64Encoder() which is in the Sun proprietary packages (which can be removed or changed during any upgrade of the runtime)
throwing Exception for platform exceptions and runtime exceptions (not being able to decrypt is handled the same way as not being able to instantiate the Cipher)
requesting 24 bytes instead of 16 within the Arrays.copyOf() call (which seems to return 24 SHA-1 output while there are only 20 bytes)
To generate a 3DES 24 byte (168 bits used) DES ABC key from a password (like) String you should use PBKDF-2. Adding an authentication tag is also very important if man-in-the-middle attacks or padding oracle apply. It would be much secure and much more practical to upgrade to AES if you can control the algorithms being used as well.
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.
I'm making an app that encrypts some files. I want to use gnu's cryptix library. It says it is no longer developed since 2005, but I guess it has everything I need... should I use something else?
And I have a question about encrypting a single file. Right now I do it with a loop like this:
for(int i=0; i+block_size < bdata.length; i += block_size)
cipher.encryptBlock(bdata, i, cdata, i);
So my question is how to encrypt the last block that may not have the same size as the block_size. I was thinking maybe a should add some extra data to the last block, but than I don't know how to decrypt that...
I would strongly suggest using AES encryption and it too comes with the JAVA SDK. Have a look at: Using AES with Java Technology which will give you some great example. To read up more on AES see: Advanced Encryption Standard - Wikipedia.
Never use your own encryption scheme or an older form of an encryption scheme. AES has been tried and tested by people with far greater knowledge in that field then us, so you know it will work. Where as with your own or an old encryption scheme we might miss a fatal loop hole that will leave our data open to attacks.
See this question here to see the difference in the encryption schemes: Comparison of DES, Triple DES, AES, blowfish encryption for data
Addendum:
AES in java will work flawlessly for 192 and 256bit keys but you will have to install the newer JCE Policy Files. See here and here. You should also place the files in your JDK or else it wont work when executed from your IDE.
Note: Make sure you download the correct JCE policy files, depending on your Java version i.e 1.4, 1.5 1.6 or 7.
However if you use 128bit keys no need to install the newer JCE files.
Here is a template of some secure AES usage in java it use CBC/AES/PKCS5Padding and a random IV using RandomSecure.
Note you need both the key and IV for decrypting:
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* This program generates a AES key, retrieves its raw bytes, and then
* reinstantiates a AES key from the key bytes. The reinstantiated key is used
* to initialize a AES cipher for encryption and decryption.
*/
public class AES {
/**
* Encrypt a sample message using AES in CBC mode with a random IV genrated
* using SecyreRandom.
*
*/
public static void main(String[] args) {
try {
String message = "This string contains a secret message.";
System.out.println("Plaintext: " + message + "\n");
// generate a key
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(128); // To use 256 bit keys, you need the "unlimited strength" encryption policy files from Sun.
byte[] key = keygen.generateKey().getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
// build the initialization vector (randomly).
SecureRandom random = new SecureRandom();
byte iv[] = new byte[16];//generate random 16 byte IV AES is always 16bytes
random.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
// initialize the cipher for encrypt mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
System.out.println("Key: " + new String(key, "utf-8") + " This is important when decrypting");
System.out.println("IV: " + new String(iv, "utf-8") + " This is important when decrypting");
System.out.println();
// encrypt the message
byte[] encrypted = cipher.doFinal(message.getBytes());
System.out.println("Ciphertext: " + asHex(encrypted) + "\n");
// reinitialize the cipher for decryption
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
// decrypt the message
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("Plaintext: " + new String(decrypted) + "\n");
} catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
}
/**
* Turns array of bytes into string
*
* #param buf Array of bytes to convert to hex string
* #return Generated hex string
*/
public static String asHex(byte buf[]) {
StringBuilder strbuf = new StringBuilder(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strbuf.append("0");
}
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
}
I always use BouncyCastle
I also use the streaming framework instead of the for loop you were describing: it deals with the issue raised. Mostly I use that because when it comes to cryptography (and threading) I rarely trust my own code, I trust the people that live eat and breath it. Here is the code I use when I want "gash" cryptography. i.e. I have no particular threat model, and just want something "a little secure".
The hex encoding of the keys makes them much easier to manipulate / store and so on. I use "makeKey" to ... well ... make a key, then I can use the key in the encrypt and decrypt methods. You can obviously go back to using byte[] instead of hex strings for the keys.
private static boolean initialised;
private static void init() {
if (initialised)
return;
Security.addProvider(new BouncyCastleProvider());
initialised = true;
}
public static String makeKey() {
init();
KeyGenerator generator = KeyGenerator.getInstance(algorithm, provider);
generator.init(keySize);
Key key = generator.generateKey();
byte[] encoded = key.getEncoded();
return Strings.toHex(encoded);
}
public static String aesDecrypt(String hexKey, String hexCoded) {
init();
SecretKeySpec key = new SecretKeySpec(Strings.fromHex(hexKey), algorithm);
Cipher cipher = Cipher.getInstance(algorithm + "/ECB/PKCS5Padding", provider);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] codedBytes = Strings.fromHex(hexCoded);
CipherInputStream inputStream = new CipherInputStream(new ByteArrayInputStream(codedBytes), cipher);
byte[] bytes = getBytes(inputStream, 256);
String result = new String(bytes, "UTF-8");
return result;
}
public static String aesEncrypt(String hexKey, String input) {
init();
SecretKeySpec key = new SecretKeySpec(Strings.fromHex(hexKey), algorithm);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(input.length());
CipherOutputStream outputStream = new CipherOutputStream(byteArrayOutputStream, cipher);
setText(outputStream, input);
byte[] outputBytes = byteArrayOutputStream.toByteArray();
String output = new String(Strings.toHex(outputBytes));
return output;
}
public static void setText(OutputStream outputStream, String text, String encoding) {
try {
outputStream.write(text.getBytes(encoding));
outputStream.flush();
} finally {
outputStream.close();
}
}
public static byte[] getBytes(InputStream inputStream, int bufferSize) {
try {
List<ByteArrayAndLength> list = Lists.newList();
while (true) {
byte[] buffer = new byte[bufferSize];
int count = inputStream.read(buffer);
if (count == -1) {
byte[] result = new byte[ByteArrayAndLength.length(list)];
int index = 0;
for (ByteArrayAndLength byteArrayAndLength : list) {
System.arraycopy(byteArrayAndLength.bytes, 0, result, index, byteArrayAndLength.length);
index += byteArrayAndLength.length;
}
assert index == result.length;
return result;
}
list.add(new ByteArrayAndLength(buffer, count));
}
} finally {
inputStream.close();
}
}
static class ByteArrayAndLength {
byte[] bytes;
int length;
public ByteArrayAndLength(byte[] bytes, int length) {
super();
this.bytes = bytes;
this.length = length;
}
static int length(List<ByteArrayAndLength> list) {
int result = 0;
for (ByteArrayAndLength byteArrayAndLength : list) {
result += byteArrayAndLength.length;
}
return result;
}
}
I've taken out some of the exception catching to reduce the size of the code, and Strings.fromHex turns the string back into a byte[]
Maybe you should consider using a javax.crypto package.
Here is an example of how to use Ciphers:
DES encryption
Hope this helps
I would seriously think twice before going this route. The development of the software was halted because standard alternatives exist, and have a look at the mailing list, there's been no significant activity since 2009. In my book that means that the software is abandoned, and abandoned software means you're more or less on your own.
Have a look here on SO, there are several questions and answers that may help you like this one. An at first sight interesting package that could simplify things for you (but still using the standard JCE infrastructure) is jasypt