]>
git.saurik.com Git - apple/security.git/blob - SecurityTests/cspxutils/sshKey/sshKey.cpp
6b181224ea050f357daa497cfc4c6d8a576fd53a
2 * sshKey.cpp - Standalone SSH key parser and converter. Uses libcrypto for
3 * representing and storing RSA and DSA keys and for
4 * writing and reading BIGNUMS to/from memory.
11 #include <CommonCrypto/CommonDigest.h>
12 #include <CommonCrypto/CommonCryptor.h>
13 #include <security_cdsa_utils/cuFileIo.h>
14 #include <security_cdsa_utils/cuEnc64.h>
15 #include <openssl/rsa.h>
16 #include <openssl/dsa.h>
17 #include <CoreFoundation/CoreFoundation.h>
18 #include <security_utilities/devrandom.h>
21 #define dprintf(s...) printf(s)
23 static void usage(char **argv
)
25 printf("usage: %s [options]\n", argv
[0]);
27 printf(" -i inFile\n");
28 printf(" -o outFile\n");
29 printf(" -v -- private key input; default is public\n");
30 printf(" -V -- private key output; default is public\n");
31 printf(" -d -- DSA; default is RSA\n");
32 printf(" -r -- parse & print inFile\n");
33 printf(" -f ssh1|ssh2 -- input format; default = ssh2\n");
34 printf(" -F ssh1|ssh2 -- output format; default = ssh2\n");
35 printf(" -p password\n");
36 printf(" -P -- no password; private keys in the clear\n");
37 printf(" -c comment\n");
41 static const char *authfile_id_string
= "SSH PRIVATE KEY FILE FORMAT 1.1\n";
43 /* from openssh cipher.h */
44 #define SSH_CIPHER_NONE 0 /* no encryption */
45 #define SSH_CIPHER_IDEA 1 /* IDEA CFB */
46 #define SSH_CIPHER_DES 2 /* DES CBC */
47 #define SSH_CIPHER_3DES 3 /* 3DES CBC */
48 #define SSH_CIPHER_BROKEN_TSS 4 /* TRI's Simple Stream encryption CBC */
49 #define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */
50 #define SSH_CIPHER_BLOWFISH 6
51 #define SSH_CIPHER_RESERVED 7
53 #define SSH2_RSA_HEADER "ssh-rsa"
54 #define SSH2_DSA_HEADER "ssh-dss"
56 #pragma mark --- commmon code ---
58 static uint32_t readUint32(
59 const unsigned char *&cp
, // IN/OUT
60 unsigned &len
) // IN/OUT
64 for(unsigned dex
=0; dex
<sizeof(uint32_t); dex
++) {
72 static uint16_t readUint16(
73 const unsigned char *&cp
, // IN/OUT
74 unsigned &len
) // IN/OUT
83 static void appendUint32(
84 CFMutableDataRef cfOut
,
87 UInt8 buf
[sizeof(uint32_t)];
89 for(int dex
=(sizeof(uint32_t) - 1); dex
>=0; dex
--) {
93 CFDataAppendBytes(cfOut
, buf
, sizeof(uint32_t));
96 static void appendUint16(
97 CFMutableDataRef cfOut
,
100 UInt8 buf
[sizeof(uint16_t)];
105 CFDataAppendBytes(cfOut
, buf
, sizeof(uint16_t));
108 /* parse text as decimal, return BIGNUM */
109 static BIGNUM
*parseDecimalBn(
110 const unsigned char *cp
,
113 for(unsigned dex
=0; dex
<len
; dex
++) {
115 if((c
< '0') || (c
> '9')) {
119 char *str
= (char *)malloc(len
+ 1);
120 memmove(str
, cp
, len
);
128 /* Read BIGNUM, OpenSSH-1 version */
129 static BIGNUM
*readBigNum(
130 const unsigned char *&cp
, // IN/OUT
131 unsigned &remLen
) // IN/OUT
133 if(remLen
< sizeof(uint16_t)) {
134 dprintf("readBigNum: short record(1)\n");
137 uint16_t numBits
= readUint16(cp
, remLen
);
138 unsigned bytes
= (numBits
+ 7) / 8;
140 dprintf("readBigNum: short record(2)\n");
143 BIGNUM
*bn
= BN_bin2bn(cp
, bytes
, NULL
);
145 dprintf("readBigNum: BN_bin2bn error\n");
153 /* Write BIGNUM, OpenSSH-1 version */
154 static int appendBigNum(
155 CFMutableDataRef cfOut
,
158 /* 16 bits of numbits */
159 unsigned numBits
= BN_num_bits(bn
);
160 appendUint16(cfOut
, numBits
);
162 /* serialize the bytes */
163 int numBytes
= (numBits
+ 7) / 8;
164 unsigned char outBytes
[numBytes
]; // gcc is so cool...
165 int moved
= BN_bn2bin(bn
, outBytes
);
166 if(moved
!= numBytes
) {
167 dprintf("appendBigNum: BN_bn2bin() screwup\n");
170 CFDataAppendBytes(cfOut
, (UInt8
*)outBytes
, numBytes
);
174 /* read BIGNUM, OpenSSH-2 mpint version */
175 static BIGNUM
*readBigNum2(
176 const unsigned char *&cp
, // IN/OUT
177 unsigned &remLen
) // IN/OUT
180 dprintf("readBigNum2: short record(1)\n");
183 uint32_t bytes
= readUint32(cp
, remLen
);
185 dprintf("readBigNum2: short record(2)\n");
188 BIGNUM
*bn
= BN_bin2bn(cp
, bytes
, NULL
);
190 dprintf("readBigNum2: BN_bin2bn error\n");
198 /* write BIGNUM, OpenSSH v2 format (with a 4-byte byte count) */
199 static int appendBigNum2(
200 CFMutableDataRef cfOut
,
204 dprintf("appendBigNum2: NULL bn");
207 if (BN_is_zero(bn
)) {
208 appendUint32(cfOut
, 0);
212 dprintf("appendBigNum2: negative numbers not supported\n");
215 int numBytes
= BN_num_bytes(bn
);
216 unsigned char buf
[numBytes
];
217 int moved
= BN_bn2bin(bn
, buf
);
218 if(moved
!= numBytes
) {
219 dprintf("appendBigNum: BN_bn2bin() screwup\n");
222 bool appendZero
= false;
224 /* prepend leading zero to make it positive */
226 numBytes
++; // to encode the correct 4-byte length
228 appendUint32(cfOut
, (uint32_t)numBytes
);
231 CFDataAppendBytes(cfOut
, &z
, 1);
232 numBytes
--; // to append the correct number of bytes
234 CFDataAppendBytes(cfOut
, buf
, numBytes
);
235 memset(buf
, 0, numBytes
);
239 /* Write BIGNUM, OpenSSH-1 decimal (public key) version */
240 static int appendBigNumDec(
241 CFMutableDataRef cfOut
,
244 char *buf
= BN_bn2dec(bn
);
246 dprintf("appendBigNumDec: BN_bn2dec() error");
249 CFDataAppendBytes(cfOut
, (const UInt8
*)buf
, strlen(buf
));
254 /* write string, OpenSSH v2 format (with a 4-byte byte count) */
255 static void appendString(
256 CFMutableDataRef cfOut
,
260 appendUint32(cfOut
, (uint32_t)strLen
);
261 CFDataAppendBytes(cfOut
, (UInt8
*)str
, strLen
);
264 /* skip whitespace */
265 static void skipWhite(
266 const unsigned char *&cp
,
269 while(bytesLeft
!= 0) {
270 if(isspace((int)(*cp
))) {
280 /* find next whitespace or EOF - if EOF, rtn pointer points to one past EOF */
281 static const unsigned char *findNextWhite(
282 const unsigned char *cp
,
285 while(bytesLeft
!= 0) {
286 if(isspace((int)(*cp
))) {
297 * Calculate d mod{p-1,q-1}
298 * Used when decoding OpenSSH-1 private RSA key.
301 rsa_generate_additional_parameters(RSA
*rsa
)
306 if((rsa
->dmq1
= BN_new()) == NULL
) {
307 dprintf("rsa_generate_additional_parameters: BN_new failed");
310 if((rsa
->dmp1
= BN_new()) == NULL
) {
311 dprintf("rsa_generate_additional_parameters: BN_new failed");
314 if ((aux
= BN_new()) == NULL
) {
315 dprintf("rsa_generate_additional_parameters: BN_new failed");
318 if ((ctx
= BN_CTX_new()) == NULL
) {
319 dprintf("rsa_generate_additional_parameters: BN_CTX_new failed");
324 BN_sub(aux
, rsa
->q
, BN_value_one());
325 BN_mod(rsa
->dmq1
, rsa
->d
, aux
, ctx
);
327 BN_sub(aux
, rsa
->p
, BN_value_one());
328 BN_mod(rsa
->dmp1
, rsa
->d
, aux
, ctx
);
335 #pragma mark --- OpenSSH-1 crypto ---
337 static int ssh1DES3Crypt(
338 unsigned char cipher
,
340 const unsigned char *inText
,
342 const char *password
, // C string
343 unsigned char *outText
, // data RETURNED here, caller mallocs
344 unsigned *outTextLen
) // RETURNED
347 case SSH_CIPHER_3DES
:
349 case SSH_CIPHER_NONE
:
350 /* cleartext RSA private key, e.g. host key. */
351 memmove(outText
, inText
, inTextLen
);
352 *outTextLen
= inTextLen
;
355 /* who knows how we're going to figure these out */
356 printf("***Unsupported cipher (%u)\n", cipher
);
360 /* key starts with MD5(password) */
361 unsigned char pwdDigest
[CC_MD5_DIGEST_LENGTH
];
362 CC_MD5(password
, strlen(password
), pwdDigest
);
364 /* three keys from that, like so: */
365 unsigned char k1
[kCCKeySizeDES
];
366 unsigned char k2
[kCCKeySizeDES
];
367 unsigned char k3
[kCCKeySizeDES
];
368 memmove(k1
, pwdDigest
, kCCKeySizeDES
);
369 memmove(k2
, pwdDigest
+ kCCKeySizeDES
, kCCKeySizeDES
);
370 memmove(k3
, pwdDigest
, kCCKeySizeDES
);
383 /* the openssh v1 pseudo triple DES. Each DES pass has its own CBC. */
386 CCCryptorStatus cstat
= CCCrypt(op1_3
, kCCAlgorithmDES
,
391 outText
, inTextLen
, &moved
);
393 dprintf("***ssh1DES3Crypt: CCCrypt()(1) returned %u\n", (unsigned)cstat
);
396 cstat
= CCCrypt(op2
, kCCAlgorithmDES
,
397 0, // no padding - SSH does that itself
401 outText
, inTextLen
, &moved
);
403 dprintf("***ssh1DES3Crypt: CCCrypt()(2) returned %u\n", (unsigned)cstat
);
406 cstat
= CCCrypt(op1_3
, kCCAlgorithmDES
,
407 0, // no padding - SSH does that itself
411 outText
, inTextLen
, &moved
);
413 dprintf("***ssh1DES3Crypt: CCCrypt()(3) returned %u\n", (unsigned)cstat
);
421 #pragma mark --- OpenSSH-1 decode ---
423 /* Decode OpenSSH-1 RSA private key */
424 static int decodeSSH1RSAPrivKey(
425 const unsigned char *key
,
428 RSA
*rsa
, // returned
429 char **comment
) // returned
431 const unsigned char *cp
= key
; // running pointer
432 unsigned remLen
= keyLen
;
433 unsigned len
= strlen(authfile_id_string
);
435 /* length: ID string, NULL, Cipher, 4-byte spare */
436 if(remLen
< (len
+ 6)) {
437 dprintf("decodeSSH1RSAPrivKey: short record(1)\n");
441 /* ID string plus a NULL */
442 if(memcmp(authfile_id_string
, cp
, len
)) {
443 dprintf("decodeSSH1RSAPrivKey: bad header\n");
450 unsigned char cipherSpec
= *cp
;
452 case SSH_CIPHER_NONE
:
453 if(password
!= NULL
) {
454 dprintf("decodeSSH1RSAPrivKey: Attempt to decrypt plaintext key\n");
458 case SSH_CIPHER_3DES
:
459 if(password
== NULL
) {
460 dprintf("decodeSSH1RSAPrivKey: Encrypted key with no decryptKey\n");
465 /* I hope we don't see any other values here */
466 dprintf("decodeOpenSSHv1PrivKey: unknown cipherSpec (%u)\n", cipherSpec
);
470 /* skip cipher, spares */
475 * Clear text public key:
480 if(remLen
< sizeof(uint32_t)) {
481 dprintf("decodeSSH1RSAPrivKey: bad len(1)\n");
484 /* skip over keybits */
485 readUint32(cp
, remLen
);
486 rsa
->n
= readBigNum(cp
, remLen
);
488 dprintf("decodeSSH1RSAPrivKey: error decoding n\n");
491 rsa
->e
= readBigNum(cp
, remLen
);
493 dprintf("decodeSSH1RSAPrivKey: error decoding e\n");
497 /* comment string: 4-byte length and the string w/o NULL */
498 if(remLen
< sizeof(uint32_t)) {
499 dprintf("decodeSSH1RSAPrivKey: bad len(2)\n");
502 uint32_t commentLen
= readUint32(cp
, remLen
);
503 if(commentLen
> remLen
) {
504 dprintf("decodeSSH1RSAPrivKey: bad len(3)\n");
507 *comment
= (char *)malloc(commentLen
+ 1);
508 memmove(*comment
, cp
, commentLen
);
509 (*comment
)[commentLen
] = '\0';
511 remLen
-= commentLen
;
513 /* everything that remains is ciphertext */
514 unsigned char ptext
[remLen
];
515 unsigned ptextLen
= 0;
516 if(ssh1DES3Crypt(cipherSpec
, false, cp
, remLen
, password
, ptext
, &ptextLen
)) {
517 dprintf("decodeSSH1RSAPrivKey: decrypt error\n");
520 /* subsequent errors to errOut: */
524 /* plaintext contents:
526 [0-1] -- random bytes
527 [2-3] -- copy of [01] for passphrase validity checking
529 buffer_put_bignum(iqmp)
537 dprintf("decodeSSH1RSAPrivKey: bad len(4)\n");
541 if((cp
[0] != cp
[2]) || (cp
[1] != cp
[3])) {
543 dprintf("decodeSSH1RSAPrivKey: check byte error\n");
550 /* remainder comprises private portion of RSA key */
551 rsa
->d
= readBigNum(cp
, remLen
);
553 dprintf("decodeSSH1RSAPrivKey: error decoding d\n");
556 rsa
->iqmp
= readBigNum(cp
, remLen
);
557 if(rsa
->iqmp
== NULL
) {
558 dprintf("decodeSSH1RSAPrivKey: error decoding iqmp\n");
561 rsa
->q
= readBigNum(cp
, remLen
);
563 dprintf("decodeSSH1RSAPrivKey: error decoding q\n");
566 rsa
->p
= readBigNum(cp
, remLen
);
568 dprintf("decodeSSH1RSAPrivKey: error decoding p\n");
572 /* calculate d mod{p-1,q-1} */
573 ourRtn
= rsa_generate_additional_parameters(rsa
);
576 memset(ptext
, 0, ptextLen
);
580 /* Decode OpenSSH-1 RSA public key */
581 static int decodeSSH1RSAPubKey(
582 const unsigned char *key
,
584 RSA
*rsa
, // returned
585 char **comment
) // returned
587 const unsigned char *cp
= key
; // running pointer
588 unsigned remLen
= keyLen
;
591 skipWhite(cp
, remLen
);
594 * cp points to start of size_in_bits in ASCII decimal' we really don't care about
595 * this field. Find next space.
597 cp
= findNextWhite(cp
, remLen
);
599 dprintf("decodeSSH1RSAPubKey: short key (1)\n");
602 skipWhite(cp
, remLen
);
604 dprintf("decodeSSH1RSAPubKey: short key (2)\n");
609 * cp points to start of e
611 const unsigned char *ep
= findNextWhite(cp
, remLen
);
613 dprintf("decodeSSH1RSAPubKey: short key (3)\n");
616 unsigned len
= ep
- cp
;
617 rsa
->e
= parseDecimalBn(cp
, len
);
624 skipWhite(cp
, remLen
);
626 dprintf("decodeSSH1RSAPubKey: short key (4)\n");
630 /* cp points to start of n */
631 ep
= findNextWhite(cp
, remLen
);
633 rsa
->n
= parseDecimalBn(cp
, len
);
639 skipWhite(cp
, remLen
);
641 /* no comment; we're done */
645 ep
= findNextWhite(cp
, remLen
);
650 *comment
= (char *)malloc(len
+ 1);
651 memmove(*comment
, cp
, len
);
652 if((*comment
)[len
- 1] == '\n') {
653 /* normal case closes with a newline, not part of the comment */
656 (*comment
)[len
] = '\0';
661 #pragma mark --- OpenSSH-1 encode ---
663 /* Encode OpenSSH-1 RSA private key */
664 static int encodeSSH1RSAPrivKey(
666 const char *password
,
668 unsigned char **outKey
, // mallocd and RETURNED
669 unsigned *outKeyLen
) // RETURNED
671 CFMutableDataRef cfOut
= CFDataCreateMutable(NULL
, 0);
673 /* ID string including NULL */
674 CFDataAppendBytes(cfOut
, (const UInt8
*)authfile_id_string
, strlen(authfile_id_string
) + 1);
676 /* one byte cipher */
677 UInt8 cipherSpec
= SSH_CIPHER_3DES
;
678 CFDataAppendBytes(cfOut
, &cipherSpec
, 1);
681 UInt8 spares
[4] = {0};
682 CFDataAppendBytes(cfOut
, spares
, 4);
685 * Clear text public key:
690 uint32_t keybits
= RSA_size(rsa
) * 8;
691 appendUint32(cfOut
, keybits
);
692 appendBigNum(cfOut
, rsa
->n
);
693 appendBigNum(cfOut
, rsa
->e
);
695 /* comment string: 4-byte length and the string w/o NULL */
697 uint32_t len
= strlen(comment
);
698 appendUint32(cfOut
, len
);
699 CFDataAppendBytes(cfOut
, (const UInt8
*)comment
, len
);
703 * Remainder is encrypted, consisting of
705 * [0-1] -- random bytes
706 * [2-3] -- copy of [01] for passphrase validity checking
707 * buffer_put_bignum(d)
708 * buffer_put_bignum(iqmp)
709 * buffer_put_bignum(q)
710 * buffer_put_bignum(p)
713 CFMutableDataRef ptext
= CFDataCreateMutable(NULL
, 0);
715 /* [0..3] check bytes */
717 DevRandomGenerator rng
= DevRandomGenerator();
718 rng
.random(checkBytes
, 2);
719 checkBytes
[2] = checkBytes
[0];
720 checkBytes
[3] = checkBytes
[1];
721 CFDataAppendBytes(ptext
, checkBytes
, 4);
724 appendBigNum(ptext
, rsa
->d
);
725 appendBigNum(ptext
, rsa
->iqmp
);
726 appendBigNum(ptext
, rsa
->q
);
727 appendBigNum(ptext
, rsa
->p
);
730 unsigned ptextLen
= CFDataGetLength(ptext
);
731 unsigned padding
= 0;
732 unsigned rem
= ptextLen
& 0x7;
737 for(unsigned dex
=0; dex
<padding
; dex
++) {
738 CFDataAppendBytes(ptext
, &padByte
, 1);
740 ptextLen
= CFDataGetLength(ptext
);
741 unsigned char ctext
[ptextLen
];
743 int ourRtn
= ssh1DES3Crypt(SSH_CIPHER_3DES
, true,
744 (unsigned char *)CFDataGetBytePtr(ptext
), ptextLen
,
751 /* appended encrypted portion */
752 CFDataAppendBytes(cfOut
, ctext
, ctextLen
);
753 *outKeyLen
= (unsigned)CFDataGetLength(cfOut
);
754 *outKey
= (unsigned char *)malloc(*outKeyLen
);
755 memmove(*outKey
, CFDataGetBytePtr(cfOut
), *outKeyLen
);
758 /* it would be proper to zero out ptext here, but we can't do that to a CFData */
763 /* Encode OpenSSH-1 RSA public key */
764 static int encodeSSH1RSAPubKey(
767 unsigned char **outKey
, // mallocd and RETURNED
768 unsigned *outKeyLen
) // RETURNED
770 CFMutableDataRef cfOut
= CFDataCreateMutable(NULL
, 0);
774 * num_bits in decimal
776 * e, bignum in decimal
778 * n, bignum in decimal
784 unsigned numBits
= BN_num_bits(rsa
->n
);
788 snprintf(bitString
, sizeof(bitString
), "%u ", numBits
);
789 CFDataAppendBytes(cfOut
, (const UInt8
*)bitString
, strlen(bitString
));
790 if(appendBigNumDec(cfOut
, rsa
->e
)) {
794 CFDataAppendBytes(cfOut
, &c
, 1);
795 if(appendBigNumDec(cfOut
, rsa
->n
)) {
799 if(comment
!= NULL
) {
800 CFDataAppendBytes(cfOut
, &c
, 1);
801 CFDataAppendBytes(cfOut
, (UInt8
*)comment
, strlen(comment
));
804 CFDataAppendBytes(cfOut
, &c
, 1);
805 *outKeyLen
= CFDataGetLength(cfOut
);
806 *outKey
= (unsigned char *)malloc(*outKeyLen
);
807 memmove(*outKey
, CFDataGetBytePtr(cfOut
), *outKeyLen
);
813 #pragma mark --- OpenSSH-2 public key decode ---
816 * Decode components from an SSHv2 public key.
817 * Also verifies the leading header, e.g. "ssh-rsa".
818 * The returned decodedBlob is algorithm-specific.
820 static int parseSSH2PubKey(
821 const unsigned char *key
,
823 const char *header
, // SSH2_RSA_HEADER, SSH2_DSA_HEADER
824 unsigned char **decodedBlob
, // mallocd and RETURNED
825 unsigned *decodedBlobLen
, // RETURNED
826 char **comment
) // optionally mallocd and RETURNED, NULL terminated
828 unsigned len
= strlen(header
);
829 const unsigned char *endOfKey
= key
+ keyLen
;
833 /* ID string plus at least one space */
834 if(keyLen
< (len
+ 1)) {
835 dprintf("parseSSH2PubKey: short record(1)\n");
839 if(memcmp(header
, key
, len
)) {
840 dprintf("parseSSH2PubKey: bad header (1)\n");
845 dprintf("parseSSH2PubKey: bad header (2)\n");
850 /* key points to first whitespace after header */
851 skipWhite(key
, keyLen
);
853 dprintf("parseSSH2PubKey: short key\n");
857 /* key is start of base64 blob */
858 const unsigned char *encodedBlob
= key
;
859 const unsigned char *endBlob
= findNextWhite(key
, keyLen
);
860 unsigned encodedBlobLen
= endBlob
- encodedBlob
;
863 *decodedBlob
= cuDec64(encodedBlob
, encodedBlobLen
, decodedBlobLen
);
864 if(*decodedBlob
== NULL
) {
865 dprintf("parseSSH2PubKey: base64 decode error\n");
869 /* skip over the encoded blob and possible whitespace after it */
871 keyLen
= endOfKey
- endBlob
;
872 skipWhite(key
, keyLen
);
874 /* nothing remains, no comment, no error */
878 /* optional comment */
879 *comment
= (char *)malloc(keyLen
+ 1);
880 memmove(*comment
, key
, keyLen
);
881 if((*comment
)[keyLen
- 1] == '\n') {
882 /* normal case closes with a newline, not part of the comment */
885 (*comment
)[keyLen
] = '\0';
889 static int decodeSSH2RSAPubKey(
890 const unsigned char *key
,
892 RSA
*rsa
, // returned
893 char **comment
) // returned
897 * get base64-decoded blob plus optional comment
899 unsigned char *decodedBlob
= NULL
;
900 unsigned decodedBlobLen
= 0;
901 if(parseSSH2PubKey(key
, keyLen
, SSH2_RSA_HEADER
, &decodedBlob
, &decodedBlobLen
, comment
)) {
904 /* subsequent errors to errOut: */
907 * The inner base64-decoded blob, consisting of
917 keyLen
= decodedBlobLen
;
919 /* three length fields at least */
920 dprintf("decodeSSH2RSAPubKey: short record(2)\n");
924 decLen
= readUint32(key
, keyLen
);
925 len
= strlen(SSH2_RSA_HEADER
);
927 dprintf("decodeSSH2RSAPubKey: bad header (2)\n");
931 if(memcmp(SSH2_RSA_HEADER
, key
, len
)) {
932 dprintf("decodeSSH2RSAPubKey: bad header (1)\n");
938 rsa
->e
= readBigNum2(key
, keyLen
);
943 rsa
->n
= readBigNum2(key
, keyLen
);
954 static int decodeSSH2DSAPubKey(
955 const unsigned char *key
,
957 DSA
*dsa
, // returned
958 char **comment
) // returned
962 * get base64-decoded blob plus optional comment
964 unsigned char *decodedBlob
= NULL
;
965 unsigned decodedBlobLen
= 0;
966 if(parseSSH2PubKey(key
, keyLen
, SSH2_DSA_HEADER
, &decodedBlob
, &decodedBlobLen
, comment
)) {
969 /* subsequent errors to errOut: */
972 * The inner base64-decoded blob, consisting of
984 keyLen
= decodedBlobLen
;
986 /* five length fields at least */
987 dprintf("decodeSSH2DSAPubKey: short record(2)\n");
991 decLen
= readUint32(key
, keyLen
);
992 len
= strlen(SSH2_DSA_HEADER
);
994 dprintf("decodeSSH2DSAPubKey: bad header (2)\n");
998 if(memcmp(SSH2_DSA_HEADER
, key
, len
)) {
999 dprintf("decodeSSH2DSAPubKey: bad header (1)\n");
1005 dsa
->p
= readBigNum2(key
, keyLen
);
1006 if(dsa
->p
== NULL
) {
1010 dsa
->q
= readBigNum2(key
, keyLen
);
1011 if(dsa
->q
== NULL
) {
1015 dsa
->g
= readBigNum2(key
, keyLen
);
1016 if(dsa
->g
== NULL
) {
1020 dsa
->pub_key
= readBigNum2(key
, keyLen
);
1021 if(dsa
->pub_key
== NULL
) {
1031 #pragma mark --- OpenSSH-2 public key encode ---
1033 static int encodeSSH2RSAPubKey(
1035 const char *comment
,
1036 unsigned char **outKey
, // mallocd and RETURNED
1037 unsigned *outKeyLen
) // RETURNED
1039 unsigned char *b64
= NULL
;
1044 * First, the inner base64-encoded blob, consisting of
1049 CFMutableDataRef cfOut
= CFDataCreateMutable(NULL
, 0);
1051 appendString(cfOut
, SSH2_RSA_HEADER
, strlen(SSH2_RSA_HEADER
));
1052 ourRtn
= appendBigNum2(cfOut
, rsa
->e
);
1056 ourRtn
= appendBigNum2(cfOut
, rsa
->n
);
1061 /* base64 encode that */
1062 b64
= cuEnc64((unsigned char *)CFDataGetBytePtr(cfOut
), CFDataGetLength(cfOut
), &b64Len
);
1064 /* cuEnc64 added newline and NULL, which we really don't want */
1067 /* Now start over, dropping that base64 into a public blob. */
1068 CFDataSetLength(cfOut
, 0);
1069 CFDataAppendBytes(cfOut
, (UInt8
*)SSH2_RSA_HEADER
, strlen(SSH2_RSA_HEADER
));
1071 CFDataAppendBytes(cfOut
, &c
, 1);
1072 CFDataAppendBytes(cfOut
, b64
, b64Len
);
1074 /* optional comment */
1076 CFDataAppendBytes(cfOut
, &c
, 1);
1077 CFDataAppendBytes(cfOut
, (UInt8
*)comment
, strlen(comment
));
1080 /* finish it with a newline */
1082 CFDataAppendBytes(cfOut
, &c
, 1);
1084 *outKeyLen
= (unsigned)CFDataGetLength(cfOut
);
1085 *outKey
= (unsigned char *)malloc(*outKeyLen
);
1086 memmove(*outKey
, CFDataGetBytePtr(cfOut
), *outKeyLen
);
1096 static int encodeSSH2DSAPubKey(
1098 const char *comment
,
1099 unsigned char **outKey
, // mallocd and RETURNED
1100 unsigned *outKeyLen
) // RETURNED
1102 unsigned char *b64
= NULL
;
1107 * First, the inner base64-encoded blob, consisting of
1114 CFMutableDataRef cfOut
= CFDataCreateMutable(NULL
, 0);
1116 appendString(cfOut
, SSH2_DSA_HEADER
, strlen(SSH2_DSA_HEADER
));
1117 ourRtn
= appendBigNum2(cfOut
, dsa
->p
);
1121 ourRtn
= appendBigNum2(cfOut
, dsa
->q
);
1125 ourRtn
= appendBigNum2(cfOut
, dsa
->g
);
1129 ourRtn
= appendBigNum2(cfOut
, dsa
->pub_key
);
1134 /* base64 encode that */
1135 b64
= cuEnc64((unsigned char *)CFDataGetBytePtr(cfOut
), CFDataGetLength(cfOut
), &b64Len
);
1137 /* cuEnc64 added newline and NULL, which we really don't want */
1140 /* Now start over, dropping that base64 into a public blob. */
1141 CFDataSetLength(cfOut
, 0);
1142 CFDataAppendBytes(cfOut
, (UInt8
*)SSH2_DSA_HEADER
, strlen(SSH2_DSA_HEADER
));
1144 CFDataAppendBytes(cfOut
, &c
, 1);
1145 CFDataAppendBytes(cfOut
, b64
, b64Len
);
1147 /* optional comment */
1149 CFDataAppendBytes(cfOut
, &c
, 1);
1150 CFDataAppendBytes(cfOut
, (UInt8
*)comment
, strlen(comment
));
1153 /* finish it with a newline */
1155 CFDataAppendBytes(cfOut
, &c
, 1);
1157 *outKeyLen
= (unsigned)CFDataGetLength(cfOut
);
1158 *outKey
= (unsigned char *)malloc(*outKeyLen
);
1159 memmove(*outKey
, CFDataGetBytePtr(cfOut
), *outKeyLen
);
1170 #pragma mark --- print RSA/DSA keys ---
1172 static void printBNLong(
1175 /* for now assume it's 32 bits */
1176 unsigned i
= bnl
>> 24;
1178 i
= (bnl
>> 16) & 0xff;
1180 i
= (bnl
>> 8) & 0xff;
1186 static void printBN(
1190 printf("%s: %d bits: bn->top %d: ", label
, BN_num_bits(bn
), bn
->top
);
1191 for(int dex
=bn
->top
-1; dex
>=0; dex
--) {
1192 printBNLong(bn
->d
[dex
]);
1196 static void printRSA(
1200 printBN(" n", rsa
->n
);
1203 printBN(" e", rsa
->e
);
1206 printBN(" d", rsa
->d
);
1209 printBN(" p", rsa
->p
);
1212 printBN(" q", rsa
->q
);
1215 printBN("dmp1", rsa
->dmp1
);
1218 printBN("dmq1", rsa
->dmq1
);
1221 printBN("iqmp", rsa
->iqmp
);
1225 /* only public keys here */
1226 static void printDSA(
1230 printBN(" p", dsa
->p
);
1233 printBN(" q", dsa
->q
);
1236 printBN(" g", dsa
->g
);
1239 printBN(" pub", dsa
->pub_key
);
1243 /* parse format string, returns nonzero on error */
1244 static int parseFormat(
1245 const char *formatStr
,
1248 if(!strcmp(formatStr
, "ssh1")) {
1252 else if(!strcmp(formatStr
, "ssh2")) {
1261 #pragma mark --- main ---
1263 /* parse format string */
1264 int main(int argc
, char **argv
)
1266 char *inFile
= NULL
;
1267 char *outFile
= NULL
;
1268 bool privKeyIn
= false;
1269 bool privKeyOut
= false;
1270 char *password
= NULL
;
1271 char *comment
= NULL
;
1272 bool doPrint
= false;
1274 bool inputSSH1
= false;
1275 bool outputSSH1
= false;
1276 bool clearPrivKeys
= false;
1280 extern char *optarg
;
1282 while ((arg
= getopt(argc
, argv
, "i:o:vVdrf:F:p:Pc:h")) != -1) {
1303 if(parseFormat(optarg
, &inputSSH1
)) {
1308 if(parseFormat(optarg
, &outputSSH1
)) {
1316 clearPrivKeys
= true;
1327 if(inFile
== NULL
) {
1328 printf("***You must specify an input file.\n");
1331 if((privKeyIn
&& !inputSSH1
) || (privKeyOut
&& !outputSSH1
)) {
1332 printf("***Private keys in SSH2 format are handled elsewhere - Wrapped OpenSSL.\n");
1335 if((privKeyIn
|| privKeyOut
) && (password
== NULL
) & !clearPrivKeys
) {
1336 printf("***Private key handling requires a password or the -P option.\n");
1339 unsigned char *inKey
= NULL
;
1340 unsigned inKeyLen
= 0;
1341 if(readFile(inFile
, &inKey
, &inKeyLen
)) {
1342 printf("Error reading %s. Aborting.\n", inFile
);
1349 /* parse incoming key */
1352 printf("***SSHv1 did not support DSA keys.\n");
1355 /* already verified that this is not SSH2 & priv (Wrapped OpenSSL) */
1357 if(decodeSSH2DSAPubKey(inKey
, inKeyLen
, dsa
, &comment
)) {
1358 printf("***Error decoding SSH2 DSA public key.\n");
1365 /* already verified that this is SSH1 (SSH2 is Wrapped OpenSSL) */
1366 if(decodeSSH1RSAPrivKey(inKey
, inKeyLen
, password
, rsa
, &comment
)) {
1367 printf("***Error decoding SSH1 RSA Private key.\n");
1373 if(decodeSSH1RSAPubKey(inKey
, inKeyLen
, rsa
, &comment
)) {
1374 printf("***Error decoding SSH1 RSA Public key.\n");
1379 if(decodeSSH2RSAPubKey(inKey
, inKeyLen
, rsa
, &comment
)) {
1380 printf("***Error decoding SSH2 RSA Public key.\n");
1387 /* optionally display the key */
1390 printf("DSA key:\n");
1392 printf("Comment: %s\n", comment
);
1395 printf("RSA key:\n");
1397 printf("Comment: %s\n", comment
);
1401 /* optionally convert to (optionally different) output format */
1404 unsigned char *outKey
= NULL
;
1405 unsigned outKeyLen
= 0;
1408 if(outputSSH1
|| privKeyOut
) {
1409 printf("***DSA: Only public SSHv2 keys allowed.\n");
1412 if(encodeSSH2DSAPubKey(dsa
, comment
, &outKey
, &outKeyLen
)) {
1413 printf("***Error encoding DSA public key.\n");
1419 /* already verified that this is SSH1 (SSH2 is Wrapped OpenSSL) */
1420 if(encodeSSH1RSAPrivKey(rsa
, password
, comment
, &outKey
, &outKeyLen
)) {
1421 printf("***Error encoding RSA private key.\n");
1427 if(encodeSSH1RSAPubKey(rsa
, comment
, &outKey
, &outKeyLen
)) {
1428 printf("***Error encoding RSA public key.\n");
1433 if(encodeSSH2RSAPubKey(rsa
, comment
, &outKey
, &outKeyLen
)) {
1434 printf("***Error encoding RSA public key.\n");
1441 if(writeFile(outFile
, outKey
, outKeyLen
)) {
1442 printf("***Error writing to %s.\n", outFile
);
1446 printf("...wrote %u bytes to %s.\n", outKeyLen
, outFile
);
1451 printf("...parsed a key but you didn't ask me to do anything with it.\n");