Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export public key for external usage: ASN1 (Java supported key) #51

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions ios/RNRSA.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions ios/RSAFormatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
+ (NSString *)PEMFormattedPublicKey:(NSData *)publicKeyData;
+ (NSString *)PEMFormattedPrivateKey:(NSData *)privateKeyData;
+ (NSString *)stripHeaders:(NSString *)pemString;
+ (NSString *)ASN1FormattedPublicKey:(NSData*)keyBits;

@end
79 changes: 79 additions & 0 deletions ios/RSAFormatter.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions ios/RSANative.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

- (NSString *)encodedPublicKey;
- (NSString *)encodedPrivateKey;
- (NSString *)encodedASN1PublicKey;

- (NSString *)encrypt:(NSString *)message;
- (NSString *)decrypt:(NSString *)encodedMessage;
Expand Down
20 changes: 20 additions & 0 deletions ios/RSANative.m
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down