From 90558f6062edd1f57d4491cf4071c7461c71c667 Mon Sep 17 00:00:00 2001 From: Hatem Date: Mon, 29 Apr 2019 10:33:21 +0200 Subject: [PATCH] getASN1FormattedPublicKey --- README.md | 8 +++++ ios/RNRSA.m | 7 ++++ ios/RSAFormatter.h | 1 + ios/RSAFormatter.m | 79 ++++++++++++++++++++++++++++++++++++++++++++++ ios/RSANative.h | 1 + ios/RSANative.m | 20 ++++++++++++ 6 files changed, 116 insertions(+) diff --git a/README.md b/README.md index fe087b0..00dcc86 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,14 @@ Property | Description --|-- `public : string` | The RSA public key. +### Do you have a Java Backened ? +It might require OID padding in the keys encoding to get it interfacing with iOS. +An accessor for public key for java servers with OID in keys + +```typescript +RSAKeychain.getASN1FormattedPublicKey(keyTag); +``` + ## Credit * Originally based on https://github.com/SamSaffron/react-native-key-pair diff --git a/ios/RNRSA.m b/ios/RNRSA.m index 45c63ab..41b6885 100644 --- a/ios/RNRSA.m +++ b/ios/RNRSA.m @@ -187,4 +187,11 @@ - (dispatch_queue_t)methodQueue { resolve(key); } +RCT_EXPORT_METHOD(getASN1FormattedPublicKey:(NSString *)keyTag resolve:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + RSANative *rsa = [[RSANative alloc] initWithKeyTag:keyTag]; + NSString *key = [rsa encodedASN1PublicKey]; + resolve(key); +} + @end diff --git a/ios/RSAFormatter.h b/ios/RSAFormatter.h index 1d0cd8f..207d72f 100644 --- a/ios/RSAFormatter.h +++ b/ios/RSAFormatter.h @@ -13,5 +13,6 @@ + (NSString *)PEMFormattedPublicKey:(NSData *)publicKeyData; + (NSString *)PEMFormattedPrivateKey:(NSData *)privateKeyData; + (NSString *)stripHeaders:(NSString *)pemString; ++ (NSString *)ASN1FormattedPublicKey:(NSData*)keyBits; @end diff --git a/ios/RSAFormatter.m b/ios/RSAFormatter.m index 9e5195b..1b820b3 100644 --- a/ios/RSAFormatter.m +++ b/ios/RSAFormatter.m @@ -69,4 +69,83 @@ + (NSString *)stripHeaders:(NSString *)pemString { return pemString; } +#pragma mark - Java Helpers + +// Java helpers to remove and add extra bits needed for java based backends +// Once it’s base 64 decoded it strips the ASN.1 encoding associated with the OID +// and sequence encoding that generally prepends the RSA key data. That leaves it +// with just the large numbers that make up the public key. +// Read this for a clear understanding of ANS.1, BER AND PCKS encodings +// https://stackoverflow.com/a/29707204/1460582 + ++ (NSString *)ASN1FormattedPublicKey:(NSData*)keyBits { + + static const unsigned char _encodedRSAEncryptionOID[15] = { + + /* Sequence of length 0xd made up of OID followed by NULL */ + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00 + + }; + + // That gives us the "BITSTRING component of a full DER + // encoded RSA public key - We now need to build the rest + + unsigned char builder[15]; + NSMutableData * encKey = [[NSMutableData alloc] init]; + int bitstringEncLength; + + // When we get to the bitstring - how will we encode it? + + if ([keyBits length ] + 1 < 128 ) + bitstringEncLength = 1 ; + else + bitstringEncLength = (int)(([keyBits length] + 1 ) / 256 ) + 2; + + // Overall we have a sequence of a certain length + builder[0] = 0x30; // ASN.1 encoding representing a SEQUENCE + // Build up overall size made up of - + // size of OID + size of bitstring encoding + size of actual key + size_t i = sizeof(_encodedRSAEncryptionOID) + 2 + bitstringEncLength + + [keyBits length]; + size_t j = encodeLength(&builder[1], i); + [encKey appendBytes:builder length:j +1]; + + // First part of the sequence is the OID + [encKey appendBytes:_encodedRSAEncryptionOID + length:sizeof(_encodedRSAEncryptionOID)]; + + // Now add the bitstring + builder[0] = 0x03; + j = encodeLength(&builder[1], [keyBits length] + 1); + builder[j+1] = 0x00; + [encKey appendBytes:builder length:j + 2]; + + // Now the actual key + [encKey appendData:keyBits]; + + // base64 encode encKey and return + return [encKey base64EncodedStringWithOptions:0]; + +} + +size_t encodeLength(unsigned char * buf, size_t length) { + + // encode length in ASN.1 DER format + if (length < 128) { + buf[0] = length; + return 1; + } + + size_t i = (length / 256) + 1; + buf[0] = i + 0x80; + for (size_t j = 0 ; j < i; ++j) { + buf[i - j] = length & 0xFF; + length = length >> 8; + } + + return i + 1; +} + + @end diff --git a/ios/RSANative.h b/ios/RSANative.h index fec0281..e511c2a 100644 --- a/ios/RSANative.h +++ b/ios/RSANative.h @@ -20,6 +20,7 @@ - (NSString *)encodedPublicKey; - (NSString *)encodedPrivateKey; +- (NSString *)encodedASN1PublicKey; - (NSString *)encrypt:(NSString *)message; - (NSString *)decrypt:(NSString *)encodedMessage; diff --git a/ios/RSANative.m b/ios/RSANative.m index e8dd07f..6505e17 100644 --- a/ios/RSANative.m +++ b/ios/RSANative.m @@ -91,6 +91,21 @@ - (NSString *)encodedPrivateKey { return [self externalRepresentationForPrivateKey:self.privateKeyRef]; } +- (NSString *)encodedASN1PublicKey { + if (self.keyTag) { + __block NSString *encodedPublicKey = nil; + + [self performWithPublicKeyTag:self.keyTag block:^(SecKeyRef publicKey) { + encodedPublicKey = [self externalASN1Representation:publicKey]; + }]; + + return encodedPublicKey; + } + + return [self externalASN1Representation:self.publicKeyRef]; +} + + - (void)setPublicKey:(NSString *)publicKey { publicKey = [RSAFormatter stripHeaders: publicKey]; NSDictionary* options = @{(id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA, @@ -345,6 +360,11 @@ - (NSString *) externalRepresentationForPrivateKey:(SecKeyRef)key { return [RSAFormatter PEMFormattedPrivateKey:keyData]; } +- (NSString *) externalASN1Representation:(SecKeyRef)key { + NSData *keyData = [self dataForKey:key]; + return [RSAFormatter ASN1FormattedPublicKey:keyData]; +} + - (NSData *)dataForKey:(SecKeyRef)key { CFErrorRef error = NULL;