Java to Objective-C RSA Implementation

2019-04-02 17:19发布

问题:

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;

}

回答1:

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.



回答2:

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 :).