2
  1. I generated an RSA key pair with SecKeyGeneratePair. The key size in bits is 2048.

    NSDictionary *privateAttributes = @{(NSString *)kSecAttrIsPermanent: @YES, (NSString *)kSecAttrApplicationTag: PrivTag};
    NSDictionary *publicAttributes = @{(NSString *)kSecAttrIsPermanent: @YES, (NSString *)kSecAttrApplicationTag: PubTag};
    
    NSDictionary *pairAttributes = @{(NSString *)kSecAttrKeyType: (NSString *)kSecAttrKeyTypeRSA, (NSString *)kSecAttrKeySizeInBits: @2048, (NSString *)kSecPublicKeyAttrs: publicAttributes, (NSString *)kSecPrivateKeyAttrs: privateAttributes};
    
    SecKeyRef publicKeyRef;
    SecKeyRef privateKeyRef;
    OSStatus osStatus = SecKeyGeneratePair((CFDictionaryRef)pairAttributes, &publicKeyRef, &privateKeyRef);
    switch (osStatus) {
    case noErr:
        break;
    default:
        break;
    }
    
  2. Create the X.509 format of the public key and send it to the server.

  3. Create the SHA256 digest of the custom string with CC_SHA256.

    NSMutableData *hash = [NSMutableData dataWithLength:(NSUInteger)CC_SHA256_DIGEST_LENGTH];
    NSData *data = [stringToSign dataUsingEncoding:NSUTF8StringEncoding];
    CC_SHA256(data.bytes, (CC_LONG)data.length, hash.mutableBytes);
    
  4. Sign the string with SecKeyRawSign method using kSecPaddingPKCS1SHA256.

    // Sign the hash with the private key
    size_t blockSize = SecKeyGetBlockSize(privateKeyRef);
    
    NSUInteger hashDataLength = hash.length;
    const unsigned char *hashData = (const unsigned char *)hash.bytes;
    
    NSMutableData *result = [NSMutableData dataWithLength:blockSize];
    
    uint8_t *signedHashBytes = malloc(blockSize * sizeof(uint8_t));
    memset((void *) signedHashBytes, 0x0, blockSize);
    size_t encryptedDataLength = blockSize;
    
    OSStatus status = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1SHA256, hashData, hashDataLength, signedHashBytes, &encryptedDataLength);
    
    NSData *signedHash = [NSData dataWithBytes:(const void *) signedHashBytes length:(NSUInteger) encryptedDataLength];
    
  5. Apply base64 on the signed data and send it to the server.

  6. The java server cannot verify it with the public key.

I have the same above code in Swift. As a debug step, I've exported my private key too and tried to follow the exact same steps in java. Until step 3 everything is the same. So, the iOS creates the same digest as the java app. The fourth step, the signing creates a different output than the java code.

Here's the java code:

final Signature instance = Signature.getInstance("SHA256withRSA");
instance.initSign(privateKey);
instance.update(MessageDigest.getInstance("SHA-256").digest(rawString.toString().getBytes("UTF-8")));
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Infinite Possibilities
  • 7,415
  • 13
  • 55
  • 118
  • 1
    iOS and Java API for digital signature are different. `kSecPaddingPKCS1SHA256` uses a SHA256 digest, but in Java it is needed to use the raw data. `Signature.sign()` do the digest. Use `instance.update(rawString.toString().getBytes("UTF-8"));` – pedrofb Mar 09 '17 at 08:26
  • may be the problem is step 2, exporting public key. Because iOS do not work with standard formats like PEM/DER. Take a look to my [answer here](http://stackoverflow.com/questions/37735486/generate-a-csr-in-ios-library/37736209#37736209). It shows how to export the public key to DER. This key can be loaded by Java. – pedrofb Mar 09 '17 at 08:30
  • pedrofb yes, this is how I do it. – Infinite Possibilities Mar 09 '17 at 09:41
  • pedrofb your first comment is really solved the issue, but then this means, the iOS doesn't do the digest part. I have to verify it why does this happen. – Infinite Possibilities Mar 09 '17 at 09:42
  • I think it is just a difference in the API but the final result is the same . Java `Signature.sign` makes digest+pkcs1 and iOS `SecKeyRawSign` makes only the pcks1 signature using a provided digest. – pedrofb Mar 09 '17 at 09:57
  • Then, is your issue is completely solved? – pedrofb Mar 10 '17 at 07:10
  • Yes, the issues is solved and please put this as an answer, so I can accept it. Thank you for your help. – Infinite Possibilities Mar 10 '17 at 20:44

1 Answers1

2

Digital signature API for iOS and Java is different but the result is the same.

iOS SecKeyRawSign with kSecPaddingPKCS1SHA256 uses a SHA256 digest, but in Java Signature.sign requires the raw data and it makes digest+pkcs1. Use

instance.update(rawString.toString().getBytes("UTF-8"));
pedrofb
  • 37,271
  • 5
  • 94
  • 142