[openssl-dev] [openssl.org #3887] PATCH: rsautl and intelligent retry for Public Key parse after Traditional/Subject Public Key Info parse fails

noloader@gmail.com via RT rt at openssl.org
Sun May 31 07:46:41 UTC 2015


apps.c has a couple of parsing routines called load_pubkey and
load_key. rsautl uses those routines.

However, there's no option in rsautil to use anything other than a
ASN.1/DER or PEM encoded traditional key (or subject public key info).
The code paths are present, we just can't seem to get to them.

Folks in the field have problem with it on occasion. See, for example,
"Can't load programmatically generated public key,"
http://stackoverflow.com/q/30547646.

This patch adds an intelligent fallback:

    /* rsautl does not offer a way to specify just a public key. It
requires a */
    /*   subjectPublicKeyInfo, and there's no argument or option to
switch to  */
    /*   the other type with either ASN.1/DER or PEM. This attempts to
make an */
    /*   intelligent retry. */
    if(keyformat == FORMAT_PEM) {
        next_format = FORMAT_PEMRSA;
    } if(keyformat == FORMAT_ASN1) {
        next_format = FORMAT_ASN1RSA;
    } else {
        next_format = -1;
    }

    switch (key_type) {
    case KEY_PRIVKEY:
        pkey = load_key(keyfile, keyformat, 0, passin, e, "Private
Key", 0 /*last_try*/);
        if(!pkey && next_format != -1) { pkey = load_key(keyfile,
next_format, 0, passin, e, "Private Key", 1 /*last_try*/); }
        break;

    case KEY_PUBKEY:
        pkey = load_pubkey(keyfile, keyformat, 0, NULL, e, "Public
Key", 0 /*last_try*/);
        if(!pkey && next_format != -1) { pkey = load_pubkey(keyfile,
next_format, 0, NULL, e, "Public Key", 1 /*last_try*/); }
        break;

Then, functions load_key and load_pubkey suppress the error messages
if last_try is 0:

    if (pkey == NULL && last_try) {
        BIO_printf(bio_err, "unable to load %s\n", key_descrip);
    }

All in all, existing behavior and error messages are maintained. The
subcommand is just a little more robust.

Related, we might be able to make similar changes to rsa.c. But
there's some extra special sauce present, so it was not a clear
cut-in. I think the special sauce kid of attempts to do the same thing
as above. (Maybe this patch should act more like rsa.c?)

-------------- next part --------------
diff --git a/apps/apps.c b/apps/apps.c
index 60f71c3..f80767a 100644
--- a/apps/apps.c
+++ b/apps/apps.c
@@ -763,7 +763,7 @@ X509_CRL *load_crl(const char *infile, int format)
 }
 
 EVP_PKEY *load_key(const char *file, int format, int maybe_stdin,
-                   const char *pass, ENGINE *e, const char *key_descrip)
+                   const char *pass, ENGINE *e, const char *key_descrip, int last_try)
 {
     BIO *key = NULL;
     EVP_PKEY *pkey = NULL;
@@ -773,7 +773,7 @@ EVP_PKEY *load_key(const char *file, int format, int maybe_stdin,
     cb_data.prompt_info = file;
 
     if (file == NULL && (!maybe_stdin || format == FORMAT_ENGINE)) {
-        BIO_printf(bio_err, "no keyfile specified\n");
+        if(last_try) { BIO_printf(bio_err, "no keyfile specified\n"); }
         goto end;
     }
 #ifndef OPENSSL_NO_ENGINE
@@ -827,7 +827,7 @@ EVP_PKEY *load_key(const char *file, int format, int maybe_stdin,
     }
  end:
     BIO_free(key);
-    if (pkey == NULL) {
+    if (pkey == NULL && last_try) {
         BIO_printf(bio_err, "unable to load %s\n", key_descrip);
         ERR_print_errors(bio_err);
     }
@@ -842,7 +842,7 @@ static const char *key_file_format(int format)
 }
 
 EVP_PKEY *load_pubkey(const char *file, int format, int maybe_stdin,
-                      const char *pass, ENGINE *e, const char *key_descrip)
+                      const char *pass, ENGINE *e, const char *key_descrip, int last_try)
 {
     BIO *key = NULL;
     EVP_PKEY *pkey = NULL;
@@ -852,7 +852,7 @@ EVP_PKEY *load_pubkey(const char *file, int format, int maybe_stdin,
     cb_data.prompt_info = file;
 
     if (file == NULL && (!maybe_stdin || format == FORMAT_ENGINE)) {
-        BIO_printf(bio_err, "no keyfile specified\n");
+        if(last_try) { BIO_printf(bio_err, "no keyfile specified\n"); }
         goto end;
     }
 #ifndef OPENSSL_NO_ENGINE
@@ -914,8 +914,9 @@ EVP_PKEY *load_pubkey(const char *file, int format, int maybe_stdin,
 #endif
  end:
     BIO_free(key);
-    if (pkey == NULL)
+    if (pkey == NULL && last_try) {
         BIO_printf(bio_err, "unable to load %s\n", key_descrip);
+    }
     return (pkey);
 }
 
diff --git a/apps/apps.h b/apps/apps.h
index a8652a1..0cd563a 100644
--- a/apps/apps.h
+++ b/apps/apps.h
@@ -427,9 +427,9 @@ X509 *load_cert(const char *file, int format,
 X509_CRL *load_crl(const char *infile, int format);
 int load_cert_crl_http(const char *url, X509 **pcert, X509_CRL **pcrl);
 EVP_PKEY *load_key(const char *file, int format, int maybe_stdin,
-                   const char *pass, ENGINE *e, const char *key_descrip);
+                   const char *pass, ENGINE *e, const char *key_descrip, int last_try);
 EVP_PKEY *load_pubkey(const char *file, int format, int maybe_stdin,
-                      const char *pass, ENGINE *e, const char *key_descrip);
+                      const char *pass, ENGINE *e, const char *key_descrip, int last_try);
 STACK_OF(X509) *load_certs(const char *file, int format,
                            const char *pass, ENGINE *e,
                            const char *cert_descrip);
diff --git a/apps/ca.c b/apps/ca.c
index 4dc9176..8c03efd 100644
--- a/apps/ca.c
+++ b/apps/ca.c
@@ -587,7 +587,7 @@ end_of_options:
             goto end;
         }
     }
-    pkey = load_key(keyfile, keyformat, 0, key, e, "CA private key");
+    pkey = load_key(keyfile, keyformat, 0, key, e, "CA private key", 1 /*last_try*/);
     if (key)
         OPENSSL_cleanse(key, strlen(key));
     if (pkey == NULL) {
diff --git a/apps/cms.c b/apps/cms.c
index 7ccca5b..5966873 100644
--- a/apps/cms.c
+++ b/apps/cms.c
@@ -762,7 +762,7 @@ int cms_main(int argc, char **argv)
         keyfile = NULL;
 
     if (keyfile) {
-        key = load_key(keyfile, keyform, 0, passin, e, "signing key file");
+        key = load_key(keyfile, keyform, 0, passin, e, "signing key file", 1 /*last_try*/);
         if (!key)
             goto end;
     }
@@ -966,7 +966,7 @@ int cms_main(int argc, char **argv)
                                e, "signer certificate");
             if (!signer)
                 goto end;
-            key = load_key(keyfile, keyform, 0, passin, e, "signing key file");
+            key = load_key(keyfile, keyform, 0, passin, e, "signing key file", 1 /*last_try*/);
             if (!key)
                 goto end;
             for (kparam = key_first; kparam; kparam = kparam->next) {
diff --git a/apps/crl.c b/apps/crl.c
index 17391e2..cc82ac2 100644
--- a/apps/crl.c
+++ b/apps/crl.c
@@ -266,7 +266,7 @@ int crl_main(int argc, char **argv)
         newcrl = load_crl(crldiff, informat);
         if (!newcrl)
             goto end;
-        pkey = load_key(keyfile, keyformat, 0, NULL, NULL, "CRL signing key");
+        pkey = load_key(keyfile, keyformat, 0, NULL, NULL, "CRL signing key", 1 /*last_try*/);
         if (!pkey) {
             X509_CRL_free(newcrl);
             goto end;
diff --git a/apps/dgst.c b/apps/dgst.c
index 308555c..03f0f3f 100644
--- a/apps/dgst.c
+++ b/apps/dgst.c
@@ -286,9 +286,9 @@ int dgst_main(int argc, char **argv)
 
     if (keyfile) {
         if (want_pub)
-            sigkey = load_pubkey(keyfile, keyform, 0, NULL, e, "key file");
+            sigkey = load_pubkey(keyfile, keyform, 0, NULL, e, "key file", 1 /*last_try*/);
         else
-            sigkey = load_key(keyfile, keyform, 0, passin, e, "key file");
+            sigkey = load_key(keyfile, keyform, 0, passin, e, "key file", 1 /*last_try*/);
         if (!sigkey) {
             /*
              * load_[pub]key() has already printed an appropriate message
diff --git a/apps/dsa.c b/apps/dsa.c
index f02f293..34d8e0b 100644
--- a/apps/dsa.c
+++ b/apps/dsa.c
@@ -204,9 +204,9 @@ int dsa_main(int argc, char **argv)
         EVP_PKEY *pkey;
 
         if (pubin)
-            pkey = load_pubkey(infile, informat, 1, passin, e, "Public Key");
+            pkey = load_pubkey(infile, informat, 1, passin, e, "Public Key", 1 /*last_try*/);
         else
-            pkey = load_key(infile, informat, 1, passin, e, "Private Key");
+            pkey = load_key(infile, informat, 1, passin, e, "Private Key", 1 /*last_try*/);
 
         if (pkey) {
             dsa = EVP_PKEY_get1_DSA(pkey);
diff --git a/apps/ocsp.c b/apps/ocsp.c
index 4c3aa39..944b14c 100644
--- a/apps/ocsp.c
+++ b/apps/ocsp.c
@@ -528,7 +528,7 @@ int ocsp_main(int argc, char **argv)
                 goto end;
         }
         rkey = load_key(rkeyfile, FORMAT_PEM, 0, NULL, NULL,
-                        "responder private key");
+                        "responder private key", 1 /*last_try*/);
         if (!rkey)
             goto end;
     }
@@ -573,7 +573,7 @@ int ocsp_main(int argc, char **argv)
                 goto end;
         }
         key = load_key(keyfile, FORMAT_PEM, 0, NULL, NULL,
-                       "signer private key");
+                       "signer private key", 1 /*last_try*/);
         if (!key)
             goto end;
 
diff --git a/apps/pkcs12.c b/apps/pkcs12.c
index 82131e8..b603794 100644
--- a/apps/pkcs12.c
+++ b/apps/pkcs12.c
@@ -386,7 +386,7 @@ int pkcs12_main(int argc, char **argv)
 
         if (!(options & NOKEYS)) {
             key = load_key(keyname ? keyname : infile,
-                           FORMAT_PEM, 1, passin, e, "private key");
+                           FORMAT_PEM, 1, passin, e, "private key", 1 /*last_try*/);
             if (!key)
                 goto export_end;
         }
diff --git a/apps/pkcs8.c b/apps/pkcs8.c
index f8a340e..ddc2062 100644
--- a/apps/pkcs8.c
+++ b/apps/pkcs8.c
@@ -236,7 +236,7 @@ int pkcs8_main(int argc, char **argv)
     if (out == NULL)
         goto end;
     if (topk8) {
-        pkey = load_key(infile, informat, 1, passin, e, "key");
+        pkey = load_key(infile, informat, 1, passin, e, "key", 1 /*last_try*/);
         if (!pkey)
             goto end;
         if ((p8inf = EVP_PKEY2PKCS8_broken(pkey, p8_broken)) == NULL) {
diff --git a/apps/pkey.c b/apps/pkey.c
index 875087f..cde83ec 100644
--- a/apps/pkey.c
+++ b/apps/pkey.c
@@ -173,9 +173,9 @@ int pkey_main(int argc, char **argv)
         goto end;
 
     if (pubin)
-        pkey = load_pubkey(infile, informat, 1, passin, e, "Public Key");
+        pkey = load_pubkey(infile, informat, 1, passin, e, "Public Key", 1 /*last_try*/);
     else
-        pkey = load_key(infile, informat, 1, passin, e, "key");
+        pkey = load_key(infile, informat, 1, passin, e, "key", 1 /*last_try*/);
     if (!pkey)
         goto end;
 
diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c
index 4c267c1..d6c7e19 100644
--- a/apps/pkeyutl.c
+++ b/apps/pkeyutl.c
@@ -352,11 +352,11 @@ static EVP_PKEY_CTX *init_ctx(int *pkeysize,
     }
     switch (key_type) {
     case KEY_PRIVKEY:
-        pkey = load_key(keyfile, keyform, 0, passin, e, "Private Key");
+        pkey = load_key(keyfile, keyform, 0, passin, e, "Private Key", 1 /*last_try*/);
         break;
 
     case KEY_PUBKEY:
-        pkey = load_pubkey(keyfile, keyform, 0, NULL, e, "Public Key");
+        pkey = load_pubkey(keyfile, keyform, 0, NULL, e, "Public Key", 1 /*last_try*/);
         break;
 
     case KEY_CERT:
@@ -427,7 +427,7 @@ static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file)
         return 0;
     }
 
-    peer = load_pubkey(file, peerform, 0, NULL, NULL, "Peer Key");
+    peer = load_pubkey(file, peerform, 0, NULL, NULL, "Peer Key", 1 /*last_try*/);
 
     if (!peer) {
         BIO_printf(bio_err, "Error reading peer key %s\n", file);
diff --git a/apps/req.c b/apps/req.c
index 3bae59e..dd3c629 100644
--- a/apps/req.c
+++ b/apps/req.c
@@ -481,7 +481,7 @@ int req_main(int argc, char **argv)
     }
 
     if (keyfile != NULL) {
-        pkey = load_key(keyfile, keyform, 0, passin, e, "Private Key");
+        pkey = load_key(keyfile, keyform, 0, passin, e, "Private Key", 1 /*last_try*/);
         if (!pkey) {
             /* load_key() has already printed an appropriate message */
             goto end;
diff --git a/apps/rsa.c b/apps/rsa.c
index 87cb702..dae59f2 100644
--- a/apps/rsa.c
+++ b/apps/rsa.c
@@ -275,9 +275,9 @@ int rsa_main(int argc, char **argv)
             } else
                 tmpformat = informat;
 
-            pkey = load_pubkey(infile, tmpformat, 1, passin, e, "Public Key");
+            pkey = load_pubkey(infile, tmpformat, 1, passin, e, "Public Key", 1 /*last_try*/);
         } else
-            pkey = load_key(infile, informat, 1, passin, e, "Private Key");
+            pkey = load_key(infile, informat, 1, passin, e, "Private Key", 1 /*last_try*/);
 
         if (pkey != NULL)
             rsa = EVP_PKEY_get1_RSA(pkey);
diff --git a/apps/rsautl.c b/apps/rsautl.c
index 8ba838b..9706923 100644
--- a/apps/rsautl.c
+++ b/apps/rsautl.c
@@ -120,7 +120,7 @@ int rsautl_main(int argc, char **argv)
     char *passinarg = NULL, *passin = NULL, *prog;
     char rsa_mode = RSA_VERIFY, key_type = KEY_PRIVKEY;
     unsigned char *rsa_in = NULL, *rsa_out = NULL, pad = RSA_PKCS1_PADDING;
-    int rsa_inlen, keyformat = FORMAT_PEM, keysize, ret = 1;
+    int rsa_inlen, keyformat = FORMAT_PEM, next_format, keysize, ret = 1;
     int rsa_outlen = 0, hexdump = 0, asn1parse = 0, need_priv = 0, rev = 0;
     OPTION_CHOICE o;
 
@@ -217,16 +217,30 @@ int rsautl_main(int argc, char **argv)
     if (!app_load_modules(NULL))
         goto end;
 
-/* FIXME: seed PRNG only if needed */
+    /* FIXME: seed PRNG only if needed */
     app_RAND_load_file(NULL, 0);
+    
+    /* rsautl does not offer a way to specify just a public key. It requires a */
+    /*   subjectPublicKeyInfo, and there's no argument or option to switch to  */
+    /*   the other type with either ASN.1/DER or PEM. This attempts to make an */
+    /*   intelligent retry. */
+    if(keyformat == FORMAT_PEM) {
+        next_format = FORMAT_PEMRSA;
+    } if(keyformat == FORMAT_ASN1) {
+        next_format = FORMAT_ASN1RSA;
+    } else {
+        next_format = -1;
+    }
 
     switch (key_type) {
     case KEY_PRIVKEY:
-        pkey = load_key(keyfile, keyformat, 0, passin, e, "Private Key");
+        pkey = load_key(keyfile, keyformat, 0, passin, e, "Private Key", 0 /*last_try*/);
+        if(!pkey && next_format != -1) { pkey = load_key(keyfile, next_format, 0, passin, e, "Private Key", 1 /*last_try*/); }
         break;
 
     case KEY_PUBKEY:
-        pkey = load_pubkey(keyfile, keyformat, 0, NULL, e, "Public Key");
+        pkey = load_pubkey(keyfile, keyformat, 0, NULL, e, "Public Key", 0 /*last_try*/);
+        if(!pkey && next_format != -1) { pkey = load_pubkey(keyfile, next_format, 0, NULL, e, "Public Key", 1 /*last_try*/); }
         break;
 
     case KEY_CERT:
diff --git a/apps/s_cb.c b/apps/s_cb.c
index 35366c5..10e6c0a 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -1050,10 +1050,10 @@ int load_excert(SSL_EXCERT **pexc)
             return 0;
         if (exc->keyfile) {
             exc->key = load_key(exc->keyfile, exc->keyform,
-                                0, NULL, NULL, "Server Key");
+                                0, NULL, NULL, "Server Key", 1 /*last_try*/);
         } else {
             exc->key = load_key(exc->certfile, exc->certform,
-                                0, NULL, NULL, "Server Key");
+                                0, NULL, NULL, "Server Key", 1 /*last_try*/);
         }
         if (!exc->key)
             return 0;
diff --git a/apps/s_client.c b/apps/s_client.c
index 009e5fe..85a9e39 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -1112,7 +1112,7 @@ int s_client_main(int argc, char **argv)
 
     if (key_file) {
         key = load_key(key_file, key_format, 0, pass, e,
-                       "client certificate private key file");
+                       "client certificate private key file", 1 /*last_try*/);
         if (key == NULL) {
             ERR_print_errors(bio_err);
             goto end;
diff --git a/apps/s_server.c b/apps/s_server.c
index 189019d..3469add 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -1452,7 +1452,7 @@ int s_server_main(int argc, char *argv[])
 
     if (nocert == 0) {
         s_key = load_key(s_key_file, s_key_format, 0, pass, e,
-                         "server certificate private key file");
+                         "server certificate private key file", 1 /*last_try*/);
         if (!s_key) {
             ERR_print_errors(bio_err);
             goto end;
@@ -1474,7 +1474,7 @@ int s_server_main(int argc, char *argv[])
 
         if (tlsextcbp.servername) {
             s_key2 = load_key(s_key_file2, s_key_format, 0, pass, e,
-                              "second server certificate private key file");
+                              "second server certificate private key file", 1 /*last_try*/);
             if (!s_key2) {
                 ERR_print_errors(bio_err);
                 goto end;
@@ -1532,7 +1532,7 @@ int s_server_main(int argc, char *argv[])
             s_dkey_file = s_dcert_file;
 
         s_dkey = load_key(s_dkey_file, s_dkey_format,
-                          0, dpass, e, "second certificate private key file");
+                          0, dpass, e, "second certificate private key file", 1 /*last_try*/);
         if (!s_dkey) {
             ERR_print_errors(bio_err);
             goto end;
diff --git a/apps/smime.c b/apps/smime.c
index 45898de..f74105f 100644
--- a/apps/smime.c
+++ b/apps/smime.c
@@ -491,7 +491,7 @@ int smime_main(int argc, char **argv)
         keyfile = NULL;
 
     if (keyfile) {
-        key = load_key(keyfile, keyform, 0, passin, e, "signing key file");
+        key = load_key(keyfile, keyform, 0, passin, e, "signing key file", 1 /*last_try*/);
         if (!key)
             goto end;
     }
@@ -574,7 +574,7 @@ int smime_main(int argc, char **argv)
                                e, "signer certificate");
             if (!signer)
                 goto end;
-            key = load_key(keyfile, keyform, 0, passin, e, "signing key file");
+            key = load_key(keyfile, keyform, 0, passin, e, "signing key file", 1 /*last_try*/);
             if (!key)
                 goto end;
             if (!PKCS7_sign_add_signer(p7, signer, key, sign_md, flags))
diff --git a/apps/spkac.c b/apps/spkac.c
index d41331c..72f8b3a 100644
--- a/apps/spkac.c
+++ b/apps/spkac.c
@@ -163,7 +163,7 @@ int spkac_main(int argc, char **argv)
 
     if (keyfile) {
         pkey = load_key(strcmp(keyfile, "-") ? keyfile : NULL,
-                        FORMAT_PEM, 1, passin, e, "private key");
+                        FORMAT_PEM, 1, passin, e, "private key", 1 /*last_try*/);
         if (!pkey) {
             goto end;
         }
diff --git a/apps/x509.c b/apps/x509.c
index 77a2a6b..0c7a810 100644
--- a/apps/x509.c
+++ b/apps/x509.c
@@ -510,7 +510,7 @@ int x509_main(int argc, char **argv)
     }
 
     if (fkeyfile) {
-        fkey = load_pubkey(fkeyfile, keyformat, 0, NULL, e, "Forced key");
+        fkey = load_pubkey(fkeyfile, keyformat, 0, NULL, e, "Forced key", 1 /*last_try*/);
         if (fkey == NULL)
             goto end;
     }
@@ -823,7 +823,7 @@ int x509_main(int argc, char **argv)
                 BIO_printf(bio_err, "Getting Private key\n");
                 if (Upkey == NULL) {
                     Upkey = load_key(keyfile, keyformat, 0,
-                                     passin, e, "Private key");
+                                     passin, e, "Private key", 1 /*last_try*/);
                     if (Upkey == NULL)
                         goto end;
                 }
@@ -835,7 +835,7 @@ int x509_main(int argc, char **argv)
                 BIO_printf(bio_err, "Getting CA Private Key\n");
                 if (CAkeyfile != NULL) {
                     CApkey = load_key(CAkeyfile, CAkeyformat,
-                                      0, passin, e, "CA Private Key");
+                                      0, passin, e, "CA Private Key", 1 /*last_try*/);
                     if (CApkey == NULL)
                         goto end;
                 }
@@ -855,7 +855,7 @@ int x509_main(int argc, char **argv)
                     goto end;
                 } else {
                     pk = load_key(keyfile, keyformat, 0,
-                                  passin, e, "request key");
+                                  passin, e, "request key", 1 /*last_try*/);
                     if (pk == NULL)
                         goto end;
                 }


More information about the openssl-dev mailing list