PuTTYgen: add an extra button to save a public key into a file
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 27 Aug 2001 17:40:03 +0000 (17:40 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 27 Aug 2001 17:40:03 +0000 (17:40 +0000)
(as well as showing it for cut and paste). For SSH1, this feature is
largely cosmetic and added for orthogonality; it comes into its own
in SSH2, where it saves the Official One True Public Key Format as
specified in the draft spec, and more particularly as used by
ssh.com's product for authentication. Now that ssh-3.0.1 supports
RSA user keys, this is suddenly actually useful.

git-svn-id: svn://svn.tartarus.org/sgt/putty@1217 cda61777-01e9-0310-a592-d414129be87e

puttygen.c
winctrls.c
winstuff.h

index b9edf0d..3a02abf 100644 (file)
@@ -328,7 +328,7 @@ static void hidemany(HWND hwnd, const int *ids, int hideit)
     }
 }
 
-static void setupbigedit1(HWND hwnd, int id, struct RSAKey *key)
+static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
 {
     char *buffer;
     char *dec1, *dec2;
@@ -340,12 +340,15 @@ static void setupbigedit1(HWND hwnd, int id, struct RSAKey *key)
     sprintf(buffer, "%d %s %s %s",
            bignum_bitcount(key->modulus), dec1, dec2, key->comment);
     SetDlgItemText(hwnd, id, buffer);
+    SetDlgItemText(hwnd, idstatic,
+                  "&Public key for pasting into authorized_keys file:");
     sfree(dec1);
     sfree(dec2);
     sfree(buffer);
 }
 
-static void setupbigedit2(HWND hwnd, int id, struct ssh2_userkey *key)
+static void setupbigedit2(HWND hwnd, int id, int idstatic,
+                         struct ssh2_userkey *key)
 {
     unsigned char *pub_blob;
     char *buffer, *p;
@@ -368,10 +371,77 @@ static void setupbigedit2(HWND hwnd, int id, struct ssh2_userkey *key)
     *p++ = ' ';
     strcpy(p, key->comment);
     SetDlgItemText(hwnd, id, buffer);
+    SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
+                  "OpenSSH authorized_keys2 file:");
     sfree(pub_blob);
     sfree(buffer);
 }
 
+static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
+{
+    char *dec1, *dec2;
+    FILE *fp;
+
+    dec1 = bignum_decimal(key->exponent);
+    dec2 = bignum_decimal(key->modulus);
+    fp = fopen(filename, "wb");
+    if (!fp)
+       return 0;
+    fprintf(fp, "%d %s %s %s\n",
+           bignum_bitcount(key->modulus), dec1, dec2, key->comment);
+    fclose(fp);
+    sfree(dec1);
+    sfree(dec2);
+    return 1;
+}
+
+static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
+{
+    unsigned char *pub_blob;
+    char *p;
+    int pub_len;
+    int i, column;
+    FILE *fp;
+
+    pub_blob = key->alg->public_blob(key->data, &pub_len);
+
+    fp = fopen(filename, "wb");
+    if (!fp)
+       return 0;
+
+    fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
+
+    fprintf(fp, "Comment: \"");
+    for (p = key->comment; *p; p++) {
+       if (*p == '\\' || *p == '\"')
+           fputc('\\', fp);
+       fputc(*p, fp);
+    }
+    fprintf(fp, "\"\n");
+
+    i = 0;
+    column = 0;
+    while (i < pub_len) {
+       char buf[5];
+       int n = (pub_len - i < 3 ? pub_len - i : 3);
+       base64_encode_atom(pub_blob + i, n, buf);
+       i += n;
+       buf[4] = '\0';
+       fputs(buf, fp);
+       if (++column >= 16) {
+           fputc('\n', fp);
+           column = 0;
+       }
+    }
+    if (column > 0)
+       fputc('\n', fp);
+    
+    fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
+    fclose(fp);
+    sfree(pub_blob);
+    return 1;
+}
+
 /*
  * Dialog-box function for the main PuTTYgen dialog box.
  */
@@ -393,7 +463,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        IDC_BOX_ACTIONS,
        IDC_GENSTATIC, IDC_GENERATE,
        IDC_LOADSTATIC, IDC_LOAD,
-       IDC_SAVESTATIC, IDC_SAVE,
+       IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
        IDC_BOX_PARAMS,
        IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA,
        IDC_BITSSTATIC, IDC_BITS,
@@ -441,7 +511,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        {
            struct ctlpos cp, cp2;
 
-           /* Accelerators used: acglops */
+           /* Accelerators used: acglops1rb */
 
            ctlposinit(&cp, hwnd, 10, 10, 10);
            bartitle(&cp, "Public and private key generation for PuTTY",
@@ -472,8 +542,9 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                      IDC_GENSTATIC, "&Generate", IDC_GENERATE);
            staticbtn(&cp, "Load an existing private key file",
                      IDC_LOADSTATIC, "&Load", IDC_LOAD);
-           staticbtn(&cp, "Save the generated key to a new file",
-                     IDC_SAVESTATIC, "&Save", IDC_SAVE);
+           static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
+                      "Save p&ublic key", IDC_SAVEPUB,
+                      "&Save private key", IDC_SAVE);
            endbox(&cp);
            beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
            radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 2,
@@ -489,13 +560,14 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        /*
         * Initially, hide the progress bar and the key display,
         * and show the no-key display. Also disable the Save
-        * button, because with no key we obviously can't save
+        * buttons, because with no key we obviously can't save
         * anything.
         */
        hidemany(hwnd, nokey_ids, FALSE);
        hidemany(hwnd, generating_ids, TRUE);
        hidemany(hwnd, gotkey_ids, TRUE);
        EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
+       EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
 
        return 1;
       case WM_MOUSEMOVE:
@@ -555,10 +627,11 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                    *state->commentptr = smalloc(len + 1);
                    GetWindowText(editctl, *state->commentptr, len + 1);
                    if (state->ssh2) {
-                       setupbigedit2(hwnd, IDC_KEYDISPLAY,
+                       setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
                                      &state->ssh2key);
                    } else {
-                       setupbigedit1(hwnd, IDC_KEYDISPLAY, &state->key);
+                       setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
+                                     &state->key);
                    }
                }
            }
@@ -597,6 +670,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
                EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
                EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
+               EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
+               EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
+               EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
+               EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
                state->key_exists = FALSE;
                SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
                state->collecting_entropy = TRUE;
@@ -681,6 +758,37 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                }
            }
            break;
+         case IDC_SAVEPUB:
+           state =
+               (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
+           if (state->key_exists) {
+               char filename[FILENAME_MAX];
+               if (prompt_keyfile(hwnd, "Save public key as:",
+                                  filename, 1)) {
+                   int ret;
+                   FILE *fp = fopen(filename, "r");
+                   if (fp) {
+                       char buffer[FILENAME_MAX + 80];
+                       fclose(fp);
+                       sprintf(buffer, "Overwrite existing file\n%.*s?",
+                               FILENAME_MAX, filename);
+                       ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
+                                        MB_YESNO | MB_ICONWARNING);
+                       if (ret != IDYES)
+                           break;
+                   }
+                   if (state->ssh2) {
+                       ret = save_ssh2_pubkey(filename, &state->ssh2key);
+                   } else {
+                       ret = save_ssh1_pubkey(filename, &state->key);
+                   }
+                   if (ret <= 0) {
+                       MessageBox(hwnd, "Unable to save key file",
+                                  "PuTTYgen Error", MB_OK | MB_ICONERROR);
+                   }
+               }
+           }
+           break;
          case IDC_LOAD:
            state =
                (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
@@ -747,6 +855,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                        EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
                        EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
                        EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
+                       EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
+                       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
+                       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
+                       EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
                        /*
                         * Now update the key controls with all the
                         * key data.
@@ -780,7 +892,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                                 * .ssh/authorized_keys on a Unix box.
                                 */
                                setupbigedit1(hwnd, IDC_KEYDISPLAY,
-                                             &state->key);
+                                             IDC_PKSTATIC, &state->key);
                            } else {
                                char *fp;
                                char *savecomment;
@@ -802,7 +914,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                                sfree(fp);
 
                                setupbigedit2(hwnd, IDC_KEYDISPLAY,
-                                             &state->ssh2key);
+                                             IDC_PKSTATIC, &state->ssh2key);
                            }
                            SetDlgItemText(hwnd, IDC_COMMENTEDIT,
                                           *state->commentptr);
@@ -830,6 +942,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
+       EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
+       EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
        if (state->ssh2) {
            state->ssh2key.data = &state->key;
            state->ssh2key.alg = &ssh_rsa;
@@ -886,12 +1002,15 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            *state->commentptr = savecomment;
            /*
             * Construct a decimal representation of the key, for
-            * pasting into .ssh/authorized_keys on a Unix box.
+            * pasting into .ssh/authorized_keys or
+            * .ssh/authorized_keys2 on a Unix box.
             */
            if (state->ssh2) {
-               setupbigedit2(hwnd, IDC_KEYDISPLAY, &state->ssh2key);
+               setupbigedit2(hwnd, IDC_KEYDISPLAY,
+                             IDC_PKSTATIC, &state->ssh2key);
            } else {
-               setupbigedit1(hwnd, IDC_KEYDISPLAY, &state->key);
+               setupbigedit1(hwnd, IDC_KEYDISPLAY,
+                             IDC_PKSTATIC, &state->key);
            }
        }
        /*
index 9800c3e..61821c3 100644 (file)
@@ -362,6 +362,48 @@ void staticbtn(struct ctlpos *cp, char *stext, int sid,
 }
 
 /*
+ * Like staticbtn, but two buttons.
+ */
+void static2btn(struct ctlpos *cp, char *stext, int sid,
+               char *btext1, int bid1, char *btext2, int bid2)
+{
+    const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
+                       PUSHBTNHEIGHT : STATICHEIGHT);
+    RECT r;
+    int lwid, rwid1, rwid2, rpos1, rpos2;
+
+    rpos1 = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2;
+    rpos2 = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
+    lwid = rpos1 - 2 * GAPBETWEEN;
+    rwid1 = rpos2 - rpos1 - GAPBETWEEN;
+    rwid2 = cp->width + GAPBETWEEN - rpos2;
+
+    r.left = GAPBETWEEN;
+    r.top = cp->ypos + (height - STATICHEIGHT) / 2;
+    r.right = lwid;
+    r.bottom = STATICHEIGHT;
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
+
+    r.left = rpos1;
+    r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
+    r.right = rwid1;
+    r.bottom = PUSHBTNHEIGHT;
+    doctl(cp, r, "BUTTON",
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+         0, btext1, bid1);
+
+    r.left = rpos2;
+    r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
+    r.right = rwid2;
+    r.bottom = PUSHBTNHEIGHT;
+    doctl(cp, r, "BUTTON",
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+         0, btext2, bid2);
+
+    cp->ypos += height + GAPBETWEEN;
+}
+
+/*
  * An edit control on the right hand side, with a static to its left.
  */
 static void staticedit_internal(struct ctlpos *cp, char *stext,
index 6dcf7de..a8d696b 100644 (file)
@@ -55,6 +55,8 @@ void checkbox(struct ctlpos *cp, char *text, int id);
 void statictext(struct ctlpos *cp, char *text, int id);
 void staticbtn(struct ctlpos *cp, char *stext, int sid,
               char *btext, int bid);
+void static2btn(struct ctlpos *cp, char *stext, int sid,
+               char *btext1, int bid1, char *btext2, int bid2);
 void staticedit(struct ctlpos *cp, char *stext,
                int sid, int eid, int percentedit);
 void dropdownlist(struct ctlpos *cp, char *text, int staticid, int listid);