-
-
Save 801sanae/088ac6d6743d0a98df4fc7a4fb32655b to your computer and use it in GitHub Desktop.
Objective-C code to generate a CSR via OpenSSL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CertificateSigningRequest.h | |
// Cloak | |
// | |
// Created by Dave Peck on 10/31/16. | |
// Copyright © 2016 Bourgeois Bits LLC. All rights reserved. | |
// Licensed under the MIT license | |
// | |
#import <Foundation/Foundation.h> | |
@interface CertificateSigningRequest: NSObject | |
+ (nullable NSData *)csrWithPublicKey:(nonnull NSData *)publicKey privateKey:(nonnull NSData *)privateKey; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CertificateSigningRequest.m | |
// Cloak | |
// | |
// Created by Dave Peck on 10/31/16. | |
// Copyright © 2016 Bourgeois Bits LLC. All rights reserved. | |
// Licensed under the MIT license | |
// | |
#import "CertificateSigningRequest.h" | |
#import <openssl/x509.h> | |
@interface CertificateSigningRequest () | |
@property (nonatomic, readwrite) NSData *publicKey; | |
@property (nonatomic, readwrite) NSData *privateKey; | |
// Intermediate structures. | |
@property (nonatomic) EVP_PKEY *evpKeyPair; | |
@property (nonatomic) X509_REQ *request; | |
@property (nonatomic) BIO *basicOutput; | |
@end | |
@implementation CertificateSigningRequest | |
+ (nullable NSData *)csrWithPublicKey:(nonnull NSData *)publicKey privateKey:(nonnull NSData *)privateKey; | |
{ | |
return [[[CertificateSigningRequest alloc] initWithPublicKey:publicKey privateKey:privateKey] generate]; | |
} | |
- (id)initWithPublicKey:(nonnull NSData *)publicKey privateKey:(nonnull NSData *)privateKey | |
{ | |
self = [super init]; | |
if (self != nil) | |
{ | |
_publicKey = publicKey; | |
_privateKey = privateKey; | |
} | |
return self; | |
} | |
- (void)dealloc | |
{ | |
if (self.evpKeyPair != NULL) { | |
EVP_PKEY_free(self.evpKeyPair); | |
} | |
if (self.request != NULL) { | |
X509_REQ_free(self.request); | |
} | |
if (self.basicOutput != NULL) { | |
BIO_free(self.basicOutput); | |
} | |
} | |
- (nullable NSData *)generate | |
{ | |
NSData *data = nil; | |
@try | |
{ | |
[self importKeys]; | |
[self createRequest]; | |
data = [self exportRequest]; | |
} | |
@catch (NSException *exception) | |
{ | |
NSLog(@"Error generating CSR: %@", exception.reason); | |
} | |
return data; | |
} | |
- (void)importKeys | |
{ | |
EVP_PKEY *evpKeyPair = NULL; | |
// OpenSSL has terrifying interfaces. Read the implementations of d2i_*Key and cry. | |
const unsigned char *letOpenSSLOverwriteMe = NULL; | |
// Wrap the key in an EVP_PKEY. | |
letOpenSSLOverwriteMe = (const unsigned char *)self.publicKey.bytes; | |
evpKeyPair = d2i_PublicKey(EVP_PKEY_RSA, NULL, &letOpenSSLOverwriteMe, self.publicKey.length); | |
if (evpKeyPair == NULL) { | |
[self failWithReason:@"Failed to add public key data to EVP_PKEY object."]; | |
} | |
letOpenSSLOverwriteMe = (const unsigned char *)self.privateKey.bytes; | |
evpKeyPair = d2i_PrivateKey(EVP_PKEY_RSA, &evpKeyPair, &letOpenSSLOverwriteMe, self.privateKey.length); | |
if (evpKeyPair == NULL) { | |
[self failWithReason:@"Failed to add private key data to EVP_PKEY object."]; | |
} | |
self.evpKeyPair = evpKeyPair; | |
} | |
- (void)createRequest | |
{ | |
int success = 0; | |
self.request = X509_REQ_new(); | |
if (self.request == NULL) { | |
[self failWithReason:@"Failed to allocate X509_REQ object."]; | |
} | |
success = X509_REQ_set_pubkey(self.request, self.evpKeyPair); | |
if (!success) { | |
[self failWithReason:@"Failed to add keys to the X509_REQ."]; | |
} | |
success = X509_REQ_sign(self.request, self.evpKeyPair, EVP_sha256()); | |
if (!success) { | |
[self failWithReason:@"Failed to sign X509_REQ."]; | |
} | |
} | |
- (nonnull NSData *)exportRequest | |
{ | |
BUF_MEM *bufferMemory = NULL; | |
NSData *data = nil; | |
int success = 0; | |
self.basicOutput = BIO_new(BIO_s_mem()); | |
if (self.basicOutput == NULL) { | |
[self failWithReason:@"Failed to allocate mem BIO."]; | |
} | |
success = i2d_X509_REQ_bio(self.basicOutput, self.request); | |
if (!success) { | |
[self failWithReason:@"Failed to export X509_REQ to BIO."]; | |
} | |
BIO_get_mem_ptr(self.basicOutput, &bufferMemory); | |
if (bufferMemory == NULL) { | |
[self failWithReason:@"Can't get BIO buffer."]; | |
} | |
data = [NSData dataWithBytes:bufferMemory->data length:bufferMemory->length]; | |
return data; | |
} | |
#pragma mark - Utils | |
- (void)failWithReason:(nonnull NSString *)reason | |
{ | |
@throw [NSException exceptionWithName:NSGenericException reason:reason userInfo:nil]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment