[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
Mon Jun 1 03:07:08 UTC 2015


I submitted this earlier, but I forgot to tweak the docs. The docs
were missing the -keyform option, and they needed the behavior change
documented.

I also fixed a typo in the patch. The following was missing an 'else if':

    if(keyformat == FORMAT_PEM) {
        next_format = FORMAT_PEMRSA;
    } else if(keyformat == FORMAT_ASN1) {
        next_format = FORMAT_ASN1RSA;
    } else {
        next_format = -1;
    }

Attached is the updated patch.

If the patch is rejected, you might consider taking the documentation
updates (sans the behavioral changes).

On Sat, May 30, 2015 at 6:17 PM, Jeffrey Walton <noloader at gmail.com> wrote:
> 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.
>
> ...

-------------- 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..4d029be 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 a non-traditional key in either ASN.1/DER or PEM format. This     */
+    /*   attempts to make an intelligent retry.                                */
+    if(keyformat == FORMAT_PEM) {
+        next_format = FORMAT_PEMRSA;
+    } else 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;
                 }
diff --git a/doc/apps/rsautl.pod b/doc/apps/rsautl.pod
index 1a498c2..5699af5 100644
--- a/doc/apps/rsautl.pod
+++ b/doc/apps/rsautl.pod
@@ -10,6 +10,7 @@ B<openssl> B<rsautl>
 [B<-in file>]
 [B<-out file>]
 [B<-inkey file>]
+[B<-keyform format>]
 [B<-pubin>]
 [B<-certin>]
 [B<-sign>]
@@ -33,17 +34,21 @@ data using the RSA algorithm.
 
 =item B<-in filename>
 
-This specifies the input filename to read data from or standard input
+the input filename to read data from or standard input
 if this option is not specified.
 
 =item B<-out filename>
 
-specifies the output filename to write to or standard output by
+the output filename to write to or standard output by
 default.
 
 =item B<-inkey file>
 
-the input key file, by default it should be an RSA private key.
+input key file, and by default it should be a RSA private key. The key can be in any supported format, including PEM and DER (ASN.1). The key should be in traditional format.
+
+=item B<-keyform format>
+
+the format of the key files, by default PEM.
 
 =item B<-pubin>
 
@@ -55,12 +60,13 @@ the input is a certificate containing an RSA public key.
 
 =item B<-sign>
 
-sign the input data and output the signed result. This requires
-and RSA private key.
+sign the input data and output the signed result. This operation requires
+a RSA private key.
 
 =item B<-verify>
 
-verify the input data and output the recovered data.
+verify the input data and output the recovered data. This operation can
+use either a RSA public key or private key.
 
 =item B<-encrypt>
 
@@ -93,6 +99,14 @@ B<-verify> option.
 B<rsautl> because it uses the RSA algorithm directly can only be
 used to sign or verify small pieces of data.
 
+Prior to OpenSSL 1.0.2c, the keys had to be in traditional format
+in a supported encoding, like PEM or DER. OpenSSL 1.0.2c added an
+intelligent fallback so that both traditional and non-traditional
+format can be utilized without command line arguments (the command
+line options were never present). Traditional format means
+subjectPublicKeyInfo with an algorithm OID and public key; while
+non-traditional means just a public key.
+
 =head1 EXAMPLES
 
 Sign some data using a private key:


More information about the openssl-dev mailing list