Crypt-OpenSSL-SignCSR
view release on metacpan or search on metacpan
if (enddate == NULL) {
#if OPENSSL_API_COMPAT <= 10100
if (X509_time_adj_ex(X509_get_notAfter(x), days, 0, NULL)
#else
if (X509_time_adj_ex(X509_getm_notAfter(x), days, 0, NULL)
#endif
== NULL)
return 0;
#if OPENSSL_API_COMPAT <= 11000
} else if (!ASN1_TIME_set_string(X509_get_notAfter(x), enddate)) {
#else
} else if (!ASN1_TIME_set_string_X509(X509_getm_notAfter(x), enddate)) {
#endif
return 0;
}
return 1;
}
int copy_extensions(X509 *x, X509_REQ *req, int copy_type)
{
STACK_OF(X509_EXTENSION) *exts;
int i, ret = 0;
if (x == NULL || req == NULL)
return 0;
if (copy_type == EXT_COPY_NONE)
return 1;
exts = X509_REQ_get_extensions(req);
for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
ASN1_OBJECT *obj = X509_EXTENSION_get_object(ext);
int idx = X509_get_ext_by_OBJ(x, obj, -1);
/* Does extension exist in target? */
if (idx != -1) {
/* If normal copy don't override existing extension */
if (copy_type == EXT_COPY_ADD)
continue;
/* Delete all extensions of same type */
do {
X509_EXTENSION_free(X509_delete_ext(x, idx));
idx = X509_get_ext_by_OBJ(x, obj, -1);
} while (idx != -1);
}
if (!X509_add_ext(x, ext, -1))
goto end;
}
ret = 1;
end:
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
return ret;
}
int cert_matches_key(const X509 *cert, const EVP_PKEY *pkey)
{
int match;
ERR_set_mark();
match = X509_check_private_key((X509 *) cert, (EVP_PKEY *) pkey);
ERR_pop_to_mark();
return match;
}
static int do_x509_req_init(X509_REQ *x, STACK_OF(OPENSSL_STRING) *opts)
{
//int i;
opts = NULL;
if (opts == NULL)
return 1;
//for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) {
// char *opt = sk_OPENSSL_STRING_value(opts, i);
// if (x509_req_ctrl_string(x, opt) <= 0) {
// croak("parameter error "); //$, n", opt);
// ERR_print_errors(bio_err);
// return 0;
// }
//}
return 1;
}
/*
* do_X509_REQ_verify returns 1 if the signature is valid,
* 0 if the signature check fails, or -1 if error occurs.
*/
int do_X509_REQ_verify(X509_REQ *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts)
{
int rv = 0;
if (do_x509_req_init(x, vfyopts) > 0){
#if OPENSSL_API_COMPAT >= 30000
rv = X509_REQ_verify_ex(x, pkey, libctx, propq);
#else
rv = X509_REQ_verify(x, pkey);
#endif
}
else
rv = -1;
return rv;
}
void croakSsl(char* p_file, int p_line)
{
const char* errorReason;
/* Just return the top error on the stack */
errorReason = ERR_reason_error_string(ERR_get_error());
ERR_clear_error();
croak("%s:%d: OpenSSL error: %s", p_file, p_line, errorReason);
}
SV* extractBioString(pTHX_ BIO* p_stringBio)
{
SV* sv;
BUF_MEM* bptr;
CHECK_OPEN_SSL(BIO_flush(p_stringBio) == 1);
#endif
if (ctx == NULL)
return 0;
/*
* EVP_PKEY_get_default_digest_name() returns 2 if the digest is mandatory
* for this algorithm.
*/
#if OPENSSL_API_COMPAT >= 30101
if (EVP_PKEY_get_default_digest_name(pkey, def_md, sizeof(def_md)) == 2
&& strcmp(def_md, "UNDEF") == 0) {
#else
if (EVP_PKEY_get_default_digest_nid(pkey, &def_nid) == 2
&& def_nid == NID_undef) {
#endif
/* The signing algorithm requires there to be no digest */
md = NULL;
}
#if OPENSSL_API_COMPAT >= 30101
int val = EVP_DigestSignInit_ex(ctx, &pkctx, md, libctx,
propq, pkey, NULL);
#else
int val = EVP_DigestSignInit(ctx, &pkctx, md, NULL, pkey);
#endif
return val
&& do_pkey_ctx_init(pkctx, sigopts);
}
static int key_destroy(pTHX_ SV* var, MAGIC* magic) {
EVP_PKEY * key;
key = (EVP_PKEY *) magic->mg_ptr;
if (!key)
return 0;
EVP_PKEY_free(key);
return 1;
}
static const MGVTBL key_magic = { NULL, NULL, NULL, NULL, key_destroy };
MODULE = Crypt::OpenSSL::SignCSR PACKAGE = Crypt::OpenSSL::SignCSR PREFIX = signcsr_
BOOT:
ERR_load_crypto_strings();
#if OPENSSL_API_COMPAT <= 10100
ERR_load_ERR_strings();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
#endif
PROTOTYPES: DISABLE
SV * new(class, ...)
const char * class
PREINIT:
SV * private_key = NULL;
HV * options = newHV();
CODE:
STRLEN keyStringLength;
char* keyString;
BIO *bio;
SV * key = newSV(0);
SV **svp; // Temporary storage of options
SV *digest = NULL;
SV *format = newSVpv("pem", 3);
IV days = 365;
if (items > 1) {
if (ST(1) != NULL) {
// TODO: ensure_string_sv
private_key = ST(1);
if (strlen(SvPV_nolen(private_key)) == 0) {
private_key = NULL;
}
}
if (items > 2)
options = ensure_hv(ST(2), "options");
}
// Get the number of days for specified - default 365
if (hv_exists(options, "days", strlen("days"))) {
svp = hv_fetch(options, "days", strlen("days"), 0);
if (SvIOKp(*svp)) {
days = SvIV(*svp);
}
}
// Get the digest format - default NULL uses the openssl default
if (hv_exists(options, "digest", strlen("digest"))) {
svp = hv_fetch(options, "digest", strlen("digest"), 0);
if (SvPOKp(*svp)) {
digest = *svp;
}
} // No defaut value - sign will use openssl default
// Get the output format - default is pem format
if (hv_exists(options, "format", strlen("format"))) {
svp = hv_fetch(options, "format", strlen("format"), 0);
if (SvPOKp(*svp)) {
format = *svp;
}
}
// Get the private key and save it in memory
keyString = SvPV(private_key, keyStringLength);
bio = BIO_new_mem_buf(keyString, keyStringLength);
if (bio == NULL) {
croak ("Bio is null **** \n");
}
// Create the PrivateKey as EVP_PKEY
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, 0, NULL);
if (pkey == NULL) {
croak("Failed operation error code %d\n", errno);
}
// This uses "Magic" to hold the private key object
// so it can be accessed later
HV * attributes = newHV();
sv_magicext(key, NULL, PERL_MAGIC_ext,
&key_magic, (const char *)pkey, 0);
if((hv_store(attributes, "privkey", 7, key, 0)) == NULL)
croak("unable to init privkey store");
if (format != NULL)
if((hv_store(attributes, "format", 6, newRV_inc(format), 0)) == NULL)
croak("unable to init format store");
if (digest != NULL)
if((hv_store(attributes, "digest", 6, newRV_inc(digest), 0)) == NULL)
croak("unable to init digest store");
if((hv_store(attributes, "days", 4, newSViv(days), 0)) == NULL)
croak("unable to init days store");
SV *const self = newRV_noinc( (SV *)attributes );
RETVAL = sv_bless( self, gv_stashpv( class, 0 ) );
OUTPUT:
RETVAL
char * get_digest(self)
HV * self;
CODE:
SV **svp;
RETVAL = SvPV_nolen(newSVpv("",0));
// Get the output format - default is pem format
if (hv_exists(self, "digest", strlen("digest"))) {
svp = hv_fetch(self, "digest", strlen("digest"), 0);
if (SvROK(*svp)) {
RETVAL = SvPV_nolen(SvRV(*svp));
}
}
OUTPUT:
RETVAL
if (sv_cmp(format, newSVpv("pem", 0)) == 0 ||
sv_cmp(format, newSVpv("text", 0)) == 0 )
{
if((hv_store(self, "format", 6, newRV_inc(format), 0)) == NULL)
RETVAL = 0;
else
RETVAL = 1;
} else {
RETVAL = 0;
}
OUTPUT:
RETVAL
IV get_days(self)
HV * self;
CODE:
SV **svp;
RETVAL = -1;
// Get the number of days for specified - default 365
if (hv_exists(self, "days", strlen("days"))) {
svp = hv_fetch(self, "days", strlen("days"), 0);
if (SvIOKp(*svp)) {
RETVAL = SvIV(*svp);
}
}
OUTPUT:
RETVAL
IV set_days(self, IV days)
HV * self;
CODE:
RETVAL = 0;
if((hv_store(self, "days", 4, newSViv(days), 0)) == NULL)
RETVAL = 0;
else
RETVAL = 1;
OUTPUT:
RETVAL
SV * sign(self, request_SV)
HV * self;
SV * request_SV;
PREINIT:
EVP_MD_CTX *mctx;
CODE:
SV **svp;
MAGIC* mg;
EVP_PKEY *private_key;
X509_REQ * csr;
int rv = 0;
STRLEN request_length;
unsigned char* request;
BIO *csrbio;
const char * digestname;
STRLEN digestname_length;
IV days;
SV * digest = NULL;
SV * format = NULL;
if (!hv_exists(self, "privkey", strlen("privkey")))
croak("privkey not found in self!\n");
svp = hv_fetch(self, "privkey", strlen("privkey"), 0);
if (!SvMAGICAL(*svp) || (mg = mg_findext(*svp, PERL_MAGIC_ext, &key_magic)) == NULL)
croak("privkey is invalid");
if (!hv_exists(self, "days", strlen("days")))
croak("days not found in self!\n");
svp = hv_fetch(self, "days", strlen("days"), 0);
if (SvIOKp(*svp)) {
days = SvIV(*svp);
}
else {
days = 365;
}
if (hv_exists(self, "digest", strlen("digest"))) {
svp = hv_fetch(self, "digest", strlen("digest"), 0);
if (SvROK(*svp)) {
digest = SvRV(*svp);
}
}
//printf("Digest: %s\n", (char *) SvPV_nolen(digest));
if (!hv_exists(self, "format", strlen("format")))
croak("format not found in self!\n");
svp = hv_fetch(self, "format", strlen("format"), 0);
if (SvROK(*svp)) {
format = SvRV(*svp);
}
private_key = (EVP_PKEY *) mg->mg_ptr;
// Get the request that was passed into the sign function
request = (unsigned char*) SvPV(request_SV, request_length);
// Create the X509_REQ from the request
csrbio = BIO_new_mem_buf(request, request_length);
if (csrbio == NULL) {
croak ("Bio for CRS Request is null **** \n");
}
csr = PEM_read_bio_X509_REQ(csrbio, NULL, NULL, NULL);
if (csr == NULL) {
croak ("PEM_read_bio_X509_REQ failed **** \n");
}
// Verify the CSR is properly signed
EVP_PKEY *pkey;
if (csr != NULL) {
#if OPENSSL_API_COMPAT <= 10100
pkey = X509_REQ_get_pubkey(csr);
#else
pkey = X509_REQ_get0_pubkey(csr);
#endif
if (pkey == NULL)
croak ("Warning: unable to get public key from CSR\n");
int ret = do_X509_REQ_verify(csr, pkey, NULL);
if (ret == 0)
croak ("Verification of CSR failed\n");
if ( ret < 0)
croak ("Warning: error while verifying CSR self-signature\n");
}
else
croak("Unable to properly parse the Certificate Signing Request\n");
// Create a new certificate store
X509 * x;
#if OPENSSL_API_COMPAT >= 30101
if ((x = X509_new_ex(libctx, propq)) == NULL)
#else
if ((x = X509_new()) == NULL)
#endif
croak("X509_new_ex failed ...\n");
// FIXME need to look at this
int ext_copy = EXT_COPY_UNSET;
if (!copy_extensions(x, csr, ext_copy))
croak("Unable to copy extensions\n");
// Update the certificate with the CSR's subject name
if (!X509_set_subject_name(x, X509_REQ_get_subject_name(csr)))
croak("X509_set_subject_name cannot set subject name\n");
// Update the certificate with the CSR's public key
#if OPENSSL_API_COMPAT <= 10100
if (!X509_set_pubkey(x, X509_REQ_get_pubkey(csr)))
#else
if (!X509_set_pubkey(x, X509_REQ_get0_pubkey(csr)))
#endif
croak("X509_set_pubkey cannot set public key\n");
// FIXME need to look at this
//for (int i = X509_get_ext_count(x) - 1; i >= 0; i--) {
// X509_EXTENSION *ex = X509_get_ext(x, i);
// const char *sn = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ex)));
// if (clrext || (ext_names != NULL && strstr(ext_names, sn) == NULL))
// X509_EXTENSION_free(X509_delete_ext(x, i));
//}
// FIXME - this may need to change to support signing by different certificates
if (private_key != NULL && !cert_matches_key(x, private_key))
croak("cert_matches_key: signature key and public key of cert do not match\n");
// Generate a serial number and update the certificate
ASN1_INTEGER *sno = ASN1_INTEGER_new();
if (sno == NULL || !rand_serial(NULL, sno))
croak ("Unable to get ASN1INTEGER or random_serial\n");
if (sno != NULL && !X509_set_serialNumber(x, sno))
croak("X509_set_serialNumber cannot set serial number\n");
set_cert_times(x, NULL, NULL, (int) days);
// Set the certificate's issuer based on the issuer's certificate
// In self-signed certificates it is the same issuer
// FIXME this needs to be fixed to support non-self-signed certificate
X509 * issuer_cert = x;
if (!X509_set_issuer_name(x, X509_get_subject_name(issuer_cert)))
croak("X509_set_issuer_name cannot set issuer name\n");
// Create the X509 v3 extensions for the certificate
X509V3_CTX ext_ctx;
// Set the certificate issuer from the private key
#if OPENSSL_API_COMPAT >= 30000
X509V3_set_ctx(&ext_ctx, issuer_cert, x, NULL, NULL, X509V3_CTX_REPLACE);
if (!X509V3_set_issuer_pkey(&ext_ctx, private_key))
croak("X509V3_set_issuer_pkey cannot set issuer private key\n");
#elseif OPENSSL_API_COMPAT >=10010
X509V3_set_ctx(&ext_ctx, issuer_cert, x, csr, NULL, X509V3_CTX_REPLACE);
#else
X509V3_set_ctx(&ext_ctx, issuer_cert, x, csr, NULL, 0);
#endif
// Set the X509 version of the certificate
#if OPENSSL_API_COMPAT >= 30000
if (!X509_set_version(x, X509_VERSION_3))
#else
if (!X509_set_version(x, 2))
#endif
croak("X509_set_version cannot set version 3\n");
// Get digestname parameter - verify that it is valid
#if OPENSSL_API_COMPAT >= 30101
const EVP_MD *dgst;
#else
EVP_MD * md = NULL;
#endif
if (digest != NULL) {
digestname = (const char*) SvPV(digest, digestname_length);
md = (EVP_MD *)EVP_get_digestbyname(digestname);
}
if (md != NULL)
digestname = (const char *) digestname;
else {
digestname = NULL;
printf("Failed to set the digest md = Null\n");
}
//printf ("DIGEST NAME = %s\n", digestname);
// Allocate and a new digest context for certificate signing
#if OPENSSL_API_COMPAT <= 10100
mctx = EVP_MD_CTX_create();
#else
mctx = EVP_MD_CTX_new();
#endif
// Sign the new certificate
#if OPENSSL_API_COMPAT >= 30101
if (mctx != NULL && do_sign_init(mctx, private_key, digestname, NULL) > 0)
#else
if (mctx != NULL && do_sign_init(mctx, private_key, md, NULL) > 0)
#endif
rv = (X509_sign_ctx(x, mctx) > 0);
if (rv == 0)
croak("X509_sign_ctx cannot sign the new certificate\n");
// Prepare to output new certificate
BIO * out = BIO_new(BIO_s_mem());
int i;
if (sv_cmp(format, newSVpv("pem", 0)) == 0){
// Output the PEM encoded certificate
i = PEM_write_bio_X509(out, x);}
else
// Output the text format of the certificate
i = X509_print_ex(out, x, get_nameopt(), 0);
if (!i)
croak("unable to output certificate data\n");
RETVAL = extractBioString(aTHX_ out);
OUTPUT:
RETVAL
#if OPENSSL_API_COMPAT > 10200
void signcsr_DESTROY(void)
CODE:
/* deinitialisation is done automatically */
#else
void signcsr_DESTROY(void)
CODE:
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
#if OPENSSL_API_COMPAT < 10000
ERR_remove_state(0);
#endif
EVP_cleanup();
#endif
( run in 0.510 second using v1.01-cache-2.11-cpan-39bf76dae61 )