summaryrefslogtreecommitdiffstats
path: root/tcllib/modules/pki
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-10-27 19:39:39 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-10-27 19:39:39 (GMT)
commitea28451286d3ea4a772fa174483f9a7a66bb1ab3 (patch)
tree6ee9d8a7848333a7ceeee3b13d492e40225f8b86 /tcllib/modules/pki
parentb5ca09bae0d6a1edce939eea03594dd56383f2c8 (diff)
parent7c621da28f07e449ad90c387344f07a453927569 (diff)
downloadblt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.zip
blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.gz
blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.bz2
Merge commit '7c621da28f07e449ad90c387344f07a453927569' as 'tcllib'
Diffstat (limited to 'tcllib/modules/pki')
-rw-r--r--tcllib/modules/pki/CA.crt24
-rw-r--r--tcllib/modules/pki/CA.key30
-rw-r--r--tcllib/modules/pki/ChangeLog30
-rw-r--r--tcllib/modules/pki/pkgIndex.tcl1
-rw-r--r--tcllib/modules/pki/pki.man302
-rw-r--r--tcllib/modules/pki/pki.tcl1884
-rw-r--r--tcllib/modules/pki/pki.test403
-rw-r--r--tcllib/modules/pki/test-v1.crt20
-rw-r--r--tcllib/modules/pki/test-v3.crt20
-rw-r--r--tcllib/modules/pki/test.csr17
-rw-r--r--tcllib/modules/pki/test.key.aes30
-rw-r--r--tcllib/modules/pki/test.key.des30
12 files changed, 2791 insertions, 0 deletions
diff --git a/tcllib/modules/pki/CA.crt b/tcllib/modules/pki/CA.crt
new file mode 100644
index 0000000..5308bad
--- /dev/null
+++ b/tcllib/modules/pki/CA.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEBjCCAu6gAwIBAgIJAJsptsQf3TXPMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV
+BAYTAlVTMRAwDgYDVQQIEwdGbG9yaWRhMQ4wDAYDVQQHEwVUYW1wYTEPMA0GA1UE
+ChMGVGNsbGliMQwwCgYDVQQLEwNSU0ExDzANBgNVBAMTBlRlc3RDQTAeFw0xMDA4
+MDkwNzM3MzhaFw0zNTA4MTAwNzM3MzhaMF8xCzAJBgNVBAYTAlVTMRAwDgYDVQQI
+EwdGbG9yaWRhMQ4wDAYDVQQHEwVUYW1wYTEPMA0GA1UEChMGVGNsbGliMQwwCgYD
+VQQLEwNSU0ExDzANBgNVBAMTBlRlc3RDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAL9llE7rpNUW+YI8rdQgoG40E/FMMu2h9/6pVzCtILqOLFlSMSIQ
+4CxKhIzPuiP+BSufNXmeMXSueifuT2mlb5ap6T2tmd5Vi09um5xw3Spu2Juz7Zfj
+LDFnsMxuF3U4Qwl/9qEydmhtDnYATC8iRicXsPGvNVeTZgX3iZzzA3uFkD6NVEki
+X1X3MgmE8eCGTw4U/k3MIK7wPQtZMSO4Aey9I/ub5ieZrAK0yZLjiubP27gb8ZIJ
+wK2EXI2lZZH6g8JML7RwW5Yr0VoczEf79wKAUi15xQKfh+gxVzTnuKgdJ/ZMyzcW
++Pdap1YlHfewQfxeHBt60Rkr2x2UFU4hLIECAwEAAaOBxDCBwTAdBgNVHQ4EFgQU
+hSwdCbdrD6EONdeqS8tIst6yVNUwgZEGA1UdIwSBiTCBhoAUhSwdCbdrD6EONdeq
+S8tIst6yVNWhY6RhMF8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdGbG9yaWRhMQ4w
+DAYDVQQHEwVUYW1wYTEPMA0GA1UEChMGVGNsbGliMQwwCgYDVQQLEwNSU0ExDzAN
+BgNVBAMTBlRlc3RDQYIJAJsptsQf3TXPMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
+AQEFBQADggEBAL7Uhodh2ATX4IvsGqryZdLVnG9TkMn9o8ge9EUupYtt9/9/K+My
+dqXCHVuKwMYr5V5S5HAQxwTTQ4ByPaLYz+V8dumyBOdSp5l5x5NWJ+2oCn/DpL7U
+9ezqL9gzEf5AVLxigSo/V5c4uUpE4K3tbGHDVpF1z7OT6LOpkvWui1FpLQoDc9HK
+BmT1J+fQSHfsScV65T6qsQAGPUyRXqh79LIrY0ZWvDun5xy13KmCk37Gc2+fCx2m
+QEJFyaCMojsCfJz2XRtBtwn8bpBnFUP187f4snkU5Ga/5TjQGq6YBP2BWR8W3tM0
+zGk5KboIltjQSbW9KYVratpF+gp3e6ni9B0=
+-----END CERTIFICATE-----
diff --git a/tcllib/modules/pki/CA.key b/tcllib/modules/pki/CA.key
new file mode 100644
index 0000000..ca1df73
--- /dev/null
+++ b/tcllib/modules/pki/CA.key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,8C1A2E69ACFC3E90
+
+GVnzdBMa+GX2u4jOpSYU6P1JBQYbB9FRrfB/0QGdhKHU2OPEZzYZo2LwgpEBVMWT
+dVsbBECil4h0UnUsC/+DmJPv/SF++VJK8JvtO2opNCmeYrH5VAwqN2tqIXX2sWng
+9Fk7HVjsLfTUvw6nc5TI07qK2okGcT9yMTjEGXKPLkMs5K16gKoMHf5yQPQmOpfR
+HrE8odoPib1JKDV9j4FYjfBEja7Y4GLFbNobvaQIvA/pbKE4mGkGczn6vjdkGTbv
+wAI0fIWqfOnfE+VlSz9lT4mOVHt6x4WQbfUgJKf29pL9DH+I22K2cgzGwMLzLot3
+IhPiO6coFfLOKKNbRv4gtxTjXvnlwm217GDoEPgDKG1qwWzjSwI8DBsLjMhKHDfz
+0ScyItR3wkTVaTE0sQYFS5VmKzgH2rQ8ichk4AUzM5+myxg1ALKi9GRtIKKbY+0H
+EseYxzbRhTSVo0GPGiu8j6BVK40uynF4ydrjlETQsJhZZTeJPjPGNh6m3eP5zW0Q
+Im8UgW6ZZumzINJPrEKrwRF7qEGSLRv3lkMOj08sYFsSqFnBex90cov1pJOUCCAL
+mBgLFFb6P9kEAqZOnNcwrohcf8gmW1pATTgT1+y0OTC1rEJ+asvCn6yN2QkhyNqq
+vabFqxyWxVi9MsodP4+eY9Hjhl5WwkuVCnBG69HugOFvVKHIpGxfQjSnQNaYfI6a
+UKLixw0ZdfWN8sNG4gcisj/O/VHKbEWwp+N452lTzzFE7+QvCtg6rn+LPLEyTZRb
+b7QQc6CcqELL4qksm/FNP8pIyGfsUKYVE79FDK+mxDxZpWEMJ6J6dwj1jwzGbMnP
+5sgmAeaSinkqUQf/4AoiU1eIj1fiXHcsub7mFzzBP9NKQZ4YrkBeu7zfsP8Ex5Ol
+W3WmKIkw2MBG9Q0dP3HQBDw5k35X5ydJvrtaRJLDZZDwA24WcyKffYUHGV4KhsxC
+Vjzkth+MkBaPAtB35tRlr1Fr0wC6MNTeZOuPNgGM+t1ejg1NPN722J+ihne05KW/
+wzBgz3veeJeUlxRPZl0Sklct029fV+pZGwxlx72hMiyBwDh9pBhmIjWJFzuQ35CG
+DNZm1spGcDU9sd1y1txS+QVd3CDP7a5CrUuI7S8FYJEREGzWGn/dF9hxrbeLLXRf
+qvcUwCAobFfV14Z1cwoxZ9MWTXBYynxUYO7TYajb/6vIvYOAQ3DGSPelvNRsrilT
+GV0CJ1Mx6JEaTzGQQeB2JxO09T0qxKMT6h4BbLhZRledmrI/Pvp/roUmnvdE3kBn
+MkRUxzGBD9EBPA02F2paDRxXPZRHvpGomvP32JL7AJ8tln8z4TsHclpls0E7dmk9
+KKQSLk1DOUjarJhPHmGyDOn9tVdgzbfyQjdmZ1wxBvYbBuqS82FcoWohF2GnyYAm
+X7tdS9AVNDErVXOmqh2hcYFY3NFBLy0altpMaTVqTx1SwkpHh6YGt9ioltzP7kxz
+wxHz/w4DPrxaUOFYZX9dffGceFf5/h2DfhqobnwZuCgDo7SDUKJI0f88bR+1d1q6
+qr2T9ZuzIPIllMzGVfNwq5WsPy+klq/lK0QRl6RZdLEM0Q5h6WOYeoxpOGjW6E1h
+-----END RSA PRIVATE KEY-----
diff --git a/tcllib/modules/pki/ChangeLog b/tcllib/modules/pki/ChangeLog
new file mode 100644
index 0000000..55d47a6
--- /dev/null
+++ b/tcllib/modules/pki/ChangeLog
@@ -0,0 +1,30 @@
+2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.15 ========================
+ *
+
+2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.14 ========================
+ *
+
+2011-11-07 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * MD5SUMS: Updated to version 0.2, by Roy Keene.
+ * pkgIndex.tcl:
+ * pki.man:
+ * pki.tcl:
+ * pki.test:
+
+2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.13 ========================
+ *
+
+2010-10-08 Andreas Kupries <andreask@activestate.com>
+
+ * New module: PKI, Public Key Infrastructure. By Roy Keene.
+
diff --git a/tcllib/modules/pki/pkgIndex.tcl b/tcllib/modules/pki/pkgIndex.tcl
new file mode 100644
index 0000000..3892c58
--- /dev/null
+++ b/tcllib/modules/pki/pkgIndex.tcl
@@ -0,0 +1 @@
+package ifneeded pki 0.6 [list source [file join $dir pki.tcl]]
diff --git a/tcllib/modules/pki/pki.man b/tcllib/modules/pki/pki.man
new file mode 100644
index 0000000..c74ce7d
--- /dev/null
+++ b/tcllib/modules/pki/pki.man
@@ -0,0 +1,302 @@
+[comment {-*- tcl -*- doctools manpage}]
+[manpage_begin pki n 0.6]
+[see_also aes(n)]
+[see_also blowfish(n)]
+[see_also des(n)]
+[see_also md5(n)]
+[see_also sha1(n)]
+[keywords cipher]
+[keywords {data integrity}]
+[keywords encryption]
+[keywords {public key cipher}]
+[keywords rsa]
+[keywords security]
+[copyright {2010, 2011, 2012, 2013, Roy Keene, Andreas Kupries}]
+[moddesc {public key encryption}]
+[titledesc {Implementation of the public key cipher}]
+[category {Hashes, checksums, and encryption}]
+[require Tcl 8.5]
+[require pki [opt 0.6]]
+[description]
+[para]
+
+[section {COMMANDS}]
+
+[list_begin definitions]
+
+[call [cmd "::pki::encrypt"] \
+ [opt [arg "-binary"]] \
+ [opt [arg "-hex"]] \
+ [opt [arg "-pad"]] \
+ [opt [arg "-nopad"]] \
+ [opt [arg "-priv"]] \
+ [opt [arg "-pub"]] \
+ [opt [arg "--"]] \
+ [arg input] [arg key]]
+
+Encrypt a message using PKI (probably RSA).
+
+Requires the caller to specify either [option -priv] to encrypt with
+the private key or [option -pub] to encrypt with the public key. The
+default option is to pad and return in hex. One of [option -pub] or
+[option -priv] must be specified.
+
+The [option -hex] option causes the data to be returned in encoded as
+a hexidecimal string, while the [option -binary] option causes the data
+to be returned as a binary string. If they are specified multiple
+times, the last one specified is used.
+
+The [option -pad] option causes the data to be padded per PKCS#1 prior
+to being encrypted. The [option -nopad] inhibits this behaviour. If
+they are specified multiple times, the last one specified is used.
+
+[comment {
+ What happens when both are specified ?
+ -- Last one specified takes precedence
+ What happens when none are specified ?
+ -- Error is generated for "-priv/-pub", defaults to -hex -pad
+
+ What are -hex, -binary ?
+ -- Results stored in hex or binary, like sha1::sha1's -hex/-bin
+ What are -pad, -nopad ?
+ -- Whether or not to pad the input per PKCS#1
+
+ Could it be sensible to use "-encoding binary|hex" instead ?
+ -- Yes, but I was trying to be similar to existing modules
+ Could it be sensible to use "-pad <bool>" ?
+ -- Yes
+ With suitable defaults ?
+}]
+
+The input to encrypt is specified as [arg input].
+
+The [arg key] parameter, holding the key to use, is a return value
+from either
+[cmd ::pki::pkcs::parse_key],
+[cmd ::pki::x509::parse_cert], or
+[cmd ::pki::rsa::generate].
+
+[para] Mapping to OpenSSL's [syscmd openssl] application:
+[list_begin enumerated]
+[enum] "openssl rsautl -encrypt" == "::pki::encrypt -binary -pub"
+[enum] "openssl rsautl -sign" == "::pki::encrypt -binary -priv"
+[list_end]
+
+[call [cmd "::pki::decrypt"] \
+ [opt [arg "-binary"]] \
+ [opt [arg "-hex"]] \
+ [opt [arg "-unpad"]] \
+ [opt [arg "-nounpad"]] \
+ [opt [arg "-priv"]] \
+ [opt [arg "-pub"]] \
+ [opt [arg "--"]] \
+ [arg input] [arg key]]
+
+Decrypt a message using PKI (probably RSA). See [cmd ::pki::encrypt] for option handling.
+
+[para] Mapping to OpenSSL's [syscmd openssl] application:
+[list_begin enumerated]
+[enum] "openssl rsautl -decrypt" == "::pki::decrypt -binary -priv"
+[enum] "openssl rsautl -verify" == "::pki::decrypt -binary -pub"
+[list_end]
+
+[call [cmd ::pki::sign] [arg input] [arg key] [opt [arg algo]]]
+
+Digitally sign message [arg input] using the private [arg key]. If [arg algo]
+is ommited "sha1" is assumed. Possible values for [arg algo] include
+"md5", "sha1", "sha256", and "raw". Specifyin "raw" for [arg algo] will
+inhibit the building of an ASN.1 structure to encode which hashing
+algorithm was chosen.
+
+The [arg input] should be the plain text, hashing will be performed on it.
+
+The [arg key] should include the private key.
+
+[comment {
+ What is the default for algo?
+ -- sha1
+ What choices for algo has the user ?
+ -- md5, sha1, sha256, and "raw" currently
+}]
+
+[call [cmd ::pki::verify] [arg signedmessage] [arg plaintext] [arg key] [opt [arg algo]]]
+
+Verify a digital signature using a public [arg key]. Returns true or false.
+
+[comment {
+ What is the default for algo?
+ -- The default is to look at the data for the OID of the algorithm, but if it was signed "raw" it will need to be specified. It's actually ignored right now.
+ What choices for algo has the user ?
+ -- md5, sha1, sha256
+
+ NOTE: Why is the result OK and ?
+
+ I would have expected a simple boolean value.
+ -- It's probably reasonable to change it. It's more likely to generate an error than return failed.
+}]
+
+[call [cmd ::pki::key] [arg key] [opt [arg password]] [opt [arg encodePem]]]
+
+Convert a key structure into a serialized PEM (default) or DER encoded private key suitable for other applications. For RSA keys this means PKCS#1.
+
+[call [cmd ::pki::pkcs::parse_key] [arg key] [opt [arg password]]]
+
+Convert a PKCS#1 private [arg key] into a usable key, i.e. one which
+can be used as argument for
+[cmd ::pki::encrypt],
+[cmd ::pki::decrypt],
+[cmd ::pki::sign], and
+[cmd ::pki::verify].
+
+[comment {
+ What is the default for password?
+ What choices for password has the user ?
+}]
+
+[call [cmd ::pki::x509::parse_cert] [arg cert]]
+
+Convert an X.509 certificate to a usable (public) key, i.e. one which
+can be used as argument for
+[cmd ::pki:encrypt],
+[cmd ::pki::decrypt], and
+[cmd ::pki::verify].
+
+The [arg cert] argument can be either PEM or DER encoded.
+
+[call [cmd ::pki::rsa::generate] [arg bitlength] [opt [arg exponent]]]
+
+Generate a new RSA key pair, the parts of which can be used as
+argument for
+[cmd ::pki::encrypt],
+[cmd ::pki::decrypt],
+[cmd ::pki::sign], and
+[cmd ::pki::verify].
+
+The [arg bitlength] argument is the length of the public key modulus.
+
+The [arg exponent] argument should generally not be specified unless
+you really know what you are doing.
+
+[comment {
+ What is the default for exponent?
+ -- 65537 (0x10001)
+ What choices for exponent has the user ?
+ -- Any value, but it should be chosen wisely. This is the "RSA exponent" and small values may represent a security risk.
+}]
+
+[call [cmd ::pki::x509::verify_cert] [arg cert] [arg trustedcerts] [opt [arg intermediatecerts]]]
+
+Verify that a trust can be found between the certificate specified in the
+[arg cert] argument and one of the certificates specified in the list
+of certificates in the [arg trustedcerts] argument. (Eventually the
+chain can be through untrusted certificates listed in the [arg intermediatecerts]
+argument, but this is currently unimplemented).
+
+The certificates specified in the [arg cert] and [arg trustedcerts] option
+should be parsed (from [cmd ::pki::x509::parse_cert]).
+
+[call [cmd ::pki::x509::validate_cert] \
+ [arg cert] \
+ [opt "[option -sign_message] [arg dn_of_signer]"] \
+ [opt "[option -encrypt_message] [arg dn_of_signer]"] \
+ [opt "[option -sign_cert] [arg dn_to_be_signed] [arg ca_depth]"] \
+ [opt "[option -ssl] [arg dn]"] \
+]
+
+Validate that a certificate is valid to be used in some capacity. If
+multiple options are specified they must all be met for this procedure
+to return "true".
+
+Currently, only the [option "-sign_cert"] option is functional.
+
+Arguments for the [option "-sign_cert"] option are [arg dn_to_be_signed]
+and [arg ca_depth]. The [arg dn_to_be_signed] is the distinguished from
+the subject of a certificate to verify that the certificate specified in
+the [arg cert] argument can sign. The [arg ca_depth] argument is used to
+indicate at which depth the verification should be done at. Some
+certificates are limited to how far down the chain they can be used to
+verify a given certificate.
+
+[call [cmd ::pki::pkcs::create_csr] [arg keylist] [arg namelist] [opt [arg encodePem]] [opt [arg algo]]]
+
+Generate a certificate signing request from a key pair specified in
+the [arg keylist] argument.
+
+The [arg namelist] argument is a list of "name" followed by "value"
+pairs to encoding as the requested distinguished name in the CSR.
+
+The [arg encodePem] option specifies whether or not the result should
+be PEM encoded or DER encoded. A "true" value results in the result
+being PEM encoded, while any other value 9results in the the result
+being DER encoded. DER encoding is the default.
+
+The [arg algo] argument specifies the hashing algorithm we should use
+to sign this certificate signing request with. The default is "sha1".
+Other possible values include "md5" and "sha256".
+
+[call [cmd ::pki::pkcs::parse_csr] [arg csr]]
+
+Parse a Certificate Signing Request. The [arg csr] argument can be
+either PEM or DER encoded.
+
+[call [cmd ::pki::x509::create_cert] [arg signreqlist] [arg cakeylist] [arg serial_number] [arg notBefore] [arg notAfter] [arg isCA] [arg extensions] [opt [arg encodePem]] [opt [arg algo]]]
+
+Sign a signing request (usually from [cmd ::pki::pkcs::create_csr] or
+[cmd ::pki::pkcs::parse_csr]) with a Certificate Authority (CA) certificate.
+
+The [arg signreqlist] argument should be the parsed signing request.
+
+The [arg cakeylist] argument should be the parsed CA certificate.
+
+The [arg serial_number] argument should be a serial number unique to
+this certificate from this certificate authority.
+
+The [arg notBefore] and [arg notAfter] arguments should contain the
+time before and after which (respectively) the certificate should
+be considered invalid. The time should be encoded as something
+[cmd "clock format"] will accept (i.e., the results of [cmd "clock seconds"]
+and [cmd "clock add"]).
+
+The [arg isCA] argument is a boolean argumen describing whether or not
+the signed certificate should be a a CA certificate. If specified as
+true the "id-ce-basicConstraints" extension is added with the arguments
+of "critical" being true, "allowCA" being true, and caDepth being
+-1 (infinite).
+
+The [arg extensions] argument is a list of extensions and their parameters
+that should be encoded into the created certificate. Currently only one
+extension is understood ("id-ce-basicConstraints"). It accepts three
+arguments [arg critical] [arg allowCA] [arg caDepth]. The [arg critical]
+argument to this extension (and any extension) whether or not the
+validator should reject the certificate as invalid if it does not
+understand the extension (if set to "true") or should ignore the extension
+(if set to "false"). The [arg allowCA] argument is used to specify
+as a boolean value whether or not we can be used a certificate
+authority (CA). The [arg caDepth] argument indicates how many children
+CAs can be children of this CA in a depth-wise fashion. A value of "0"
+for the [arg caDepth] argument means that this CA cannot sign a CA
+certificate and have the result be valid. A value of "-1" indicates
+infinite depth.
+
+[list_end]
+
+[section "EXAMPLES"]
+
+[example {
+}]
+
+[example {
+}]
+
+[section "REFERENCES"]
+
+[list_begin enumerated]
+[enum]
+[list_end]
+
+[section AUTHORS]
+Roy Keene
+
+[vset CATEGORY rsa]
+[include ../doctools2base/include/feedback.inc]
+[manpage_end]
diff --git a/tcllib/modules/pki/pki.tcl b/tcllib/modules/pki/pki.tcl
new file mode 100644
index 0000000..275ca57
--- /dev/null
+++ b/tcllib/modules/pki/pki.tcl
@@ -0,0 +1,1884 @@
+#! /usr/bin/env tclsh
+# -*- tcl -*-
+# RSA
+#
+# (c) 2010, 2011, 2012, 2013 Roy Keene.
+# BSD Licensed.
+
+# # ## ### ##### ######## #############
+## Requisites
+
+package require Tcl 8.5
+
+## Versions of asn lower than 0.8.4 are known to have defects
+package require asn 0.8.4
+
+## Further dependencies
+package require aes
+package require des
+package require math::bignum
+package require md5 2
+package require sha1
+package require sha256
+
+# # ## ### ##### ######## #############
+## Requisites
+
+namespace eval ::pki {
+ variable oids
+ array set oids {
+ 1.2.840.113549.1.1.1 rsaEncryption
+ 1.2.840.113549.1.1.5 sha1WithRSAEncryption
+ 1.2.840.113549.2.5 md5
+ 1.3.14.3.2.26 sha1
+ 2.16.840.1.101.3.4.2.1 sha256
+ 0.9.2342.19200300.100.1.1 uid
+ 0.9.2342.19200300.100.1.10 manager
+ 0.9.2342.19200300.100.1.11 documentIdentifier
+ 0.9.2342.19200300.100.1.12 documentTitle
+ 0.9.2342.19200300.100.1.13 documentVersion
+ 0.9.2342.19200300.100.1.14 documentAuthor
+ 0.9.2342.19200300.100.1.15 documentLocation
+ 0.9.2342.19200300.100.1.2 textEncodedORAddress
+ 0.9.2342.19200300.100.1.20 homePhone
+ 0.9.2342.19200300.100.1.21 secretary
+ 0.9.2342.19200300.100.1.22 otherMailbox
+ 0.9.2342.19200300.100.1.25 dc
+ 0.9.2342.19200300.100.1.26 aRecord
+ 0.9.2342.19200300.100.1.27 mDRecord
+ 0.9.2342.19200300.100.1.28 mXRecord
+ 0.9.2342.19200300.100.1.29 nSRecord
+ 0.9.2342.19200300.100.1.3 mail
+ 0.9.2342.19200300.100.1.30 sOARecord
+ 0.9.2342.19200300.100.1.31 cNAMERecord
+ 0.9.2342.19200300.100.1.37 associatedDomain
+ 0.9.2342.19200300.100.1.38 associatedName
+ 0.9.2342.19200300.100.1.39 homePostalAddress
+ 0.9.2342.19200300.100.1.4 info
+ 0.9.2342.19200300.100.1.40 personalTitle
+ 0.9.2342.19200300.100.1.41 mobile
+ 0.9.2342.19200300.100.1.42 pager
+ 0.9.2342.19200300.100.1.43 co
+ 0.9.2342.19200300.100.1.43 friendlyCountryName
+ 0.9.2342.19200300.100.1.44 uniqueIdentifier
+ 0.9.2342.19200300.100.1.45 organizationalStatus
+ 0.9.2342.19200300.100.1.46 janetMailbox
+ 0.9.2342.19200300.100.1.47 mailPreferenceOption
+ 0.9.2342.19200300.100.1.48 buildingName
+ 0.9.2342.19200300.100.1.49 dSAQuality
+ 0.9.2342.19200300.100.1.5 drink
+ 0.9.2342.19200300.100.1.50 singleLevelQuality
+ 0.9.2342.19200300.100.1.51 subtreeMinimumQuality
+ 0.9.2342.19200300.100.1.52 subtreeMaximumQuality
+ 0.9.2342.19200300.100.1.53 personalSignature
+ 0.9.2342.19200300.100.1.54 dITRedirect
+ 0.9.2342.19200300.100.1.55 audio
+ 0.9.2342.19200300.100.1.56 documentPublisher
+ 0.9.2342.19200300.100.1.6 roomNumber
+ 0.9.2342.19200300.100.1.60 jpegPhoto
+ 0.9.2342.19200300.100.1.7 photo
+ 0.9.2342.19200300.100.1.8 userClass
+ 0.9.2342.19200300.100.1.9 host
+ 1.2.840.113549.1.9.1 email
+ 1.3.6.1.4.1.2428.90.1.1 norEduOrgUniqueNumber
+ 1.3.6.1.4.1.2428.90.1.11 norEduOrgSchemaVersion
+ 1.3.6.1.4.1.2428.90.1.12 norEduOrgNIN
+ 1.3.6.1.4.1.2428.90.1.2 norEduOrgUnitUniqueNumber
+ 1.3.6.1.4.1.2428.90.1.3 norEduPersonBirthDate
+ 1.3.6.1.4.1.2428.90.1.4 norEduPersonLIN
+ 1.3.6.1.4.1.2428.90.1.5 norEduPersonNIN
+ 1.3.6.1.4.1.2428.90.1.6 norEduOrgAcronym
+ 1.3.6.1.4.1.2428.90.1.7 norEduOrgUniqueIdentifier
+ 1.3.6.1.4.1.2428.90.1.8 norEduOrgUnitUniqueIdentifier
+ 1.3.6.1.4.1.2428.90.1.9 federationFeideSchemaVersion
+ 1.3.6.1.4.1.250.1.57 labeledURI
+ 1.3.6.1.4.1.5923.1.1.1.1 eduPersonAffiliation
+ 1.3.6.1.4.1.5923.1.1.1.10 eduPersonTargetedID
+ 1.3.6.1.4.1.5923.1.1.1.2 eduPersonNickname
+ 1.3.6.1.4.1.5923.1.1.1.3 eduPersonOrgDN
+ 1.3.6.1.4.1.5923.1.1.1.4 eduPersonOrgUnitDN
+ 1.3.6.1.4.1.5923.1.1.1.5 eduPersonPrimaryAffiliation
+ 1.3.6.1.4.1.5923.1.1.1.6 eduPersonPrincipalName
+ 1.3.6.1.4.1.5923.1.1.1.7 eduPersonEntitlement
+ 1.3.6.1.4.1.5923.1.1.1.8 eduPersonPrimaryOrgUnitDN
+ 1.3.6.1.4.1.5923.1.1.1.9 eduPersonScopedAffiliation
+ 1.3.6.1.4.1.5923.1.2.1.2 eduOrgHomePageURI
+ 1.3.6.1.4.1.5923.1.2.1.3 eduOrgIdentityAuthNPolicyURI
+ 1.3.6.1.4.1.5923.1.2.1.4 eduOrgLegalName
+ 1.3.6.1.4.1.5923.1.2.1.5 eduOrgSuperiorURI
+ 1.3.6.1.4.1.5923.1.2.1.6 eduOrgWhitePagesURI
+ 1.3.6.1.4.1.5923.1.5.1.1 isMemberOf
+ 2.16.840.1.113730.3.1.1 carLicense
+ 2.16.840.1.113730.3.1.2 departmentNumber
+ 2.16.840.1.113730.3.1.216 userPKCS12
+ 2.16.840.1.113730.3.1.241 displayName
+ 2.16.840.1.113730.3.1.3 employeeNumber
+ 2.16.840.1.113730.3.1.39 preferredLanguage
+ 2.16.840.1.113730.3.1.4 employeeType
+ 2.16.840.1.113730.3.1.40 userSMIMECertificate
+ 2.5.4.0 objectClass
+ 2.5.4.1 aliasedEntryName
+ 2.5.4.10 o
+ 2.5.4.11 ou
+ 2.5.4.12 title
+ 2.5.4.13 description
+ 2.5.4.14 searchGuide
+ 2.5.4.15 businessCategory
+ 2.5.4.16 postalAddress
+ 2.5.4.17 postalCode
+ 2.5.4.18 postOfficeBox
+ 2.5.4.19 physicalDeliveryOfficeName
+ 2.5.4.2 knowledgeInformation
+ 2.5.4.20 telephoneNumber
+ 2.5.4.21 telexNumber
+ 2.5.4.22 teletexTerminalIdentifier
+ 2.5.4.23 facsimileTelephoneNumber
+ 2.5.4.23 fax
+ 2.5.4.24 x121Address
+ 2.5.4.25 internationaliSDNNumber
+ 2.5.4.26 registeredAddress
+ 2.5.4.27 destinationIndicator
+ 2.5.4.28 preferredDeliveryMethod
+ 2.5.4.29 presentationAddress
+ 2.5.4.3 cn
+ 2.5.4.30 supportedApplicationContext
+ 2.5.4.31 member
+ 2.5.4.32 owner
+ 2.5.4.33 roleOccupant
+ 2.5.4.34 seeAlso
+ 2.5.4.35 userPassword
+ 2.5.4.36 userCertificate
+ 2.5.4.37 cACertificate
+ 2.5.4.38 authorityRevocationList
+ 2.5.4.39 certificateRevocationList
+ 2.5.4.4 sn
+ 2.5.4.40 crossCertificatePair
+ 2.5.4.41 name
+ 2.5.4.42 gn
+ 2.5.4.43 initials
+ 2.5.4.44 generationQualifier
+ 2.5.4.45 x500UniqueIdentifier
+ 2.5.4.46 dnQualifier
+ 2.5.4.47 enhancedSearchGuide
+ 2.5.4.48 protocolInformation
+ 2.5.4.49 distinguishedName
+ 2.5.4.5 serialNumber
+ 2.5.4.50 uniqueMember
+ 2.5.4.51 houseIdentifier
+ 2.5.4.52 supportedAlgorithms
+ 2.5.4.53 deltaRevocationList
+ 2.5.4.54 dmdName
+ 2.5.4.6 c
+ 2.5.4.65 pseudonym
+ 2.5.4.7 l
+ 2.5.4.8 st
+ 2.5.4.9 street
+ 2.5.29.14 id-ce-subjectKeyIdentifier
+ 2.5.29.15 id-ce-keyUsage
+ 2.5.29.16 id-ce-privateKeyUsagePeriod
+ 2.5.29.17 id-ce-subjectAltName
+ 2.5.29.18 id-ce-issuerAltName
+ 2.5.29.19 id-ce-basicConstraints
+ 2.5.29.20 id-ce-cRLNumber
+ 2.5.29.32 id-ce-certificatePolicies
+ 2.5.29.33 id-ce-cRLDistributionPoints
+ 2.5.29.35 id-ce-authorityKeyIdentifier
+ }
+
+ variable handlers
+ array set handlers {
+ rsa {::pki::rsa::encrypt ::pki::rsa::decrypt ::pki::rsa::generate ::pki::rsa::serialize_key}
+ }
+
+ variable INT_MAX [expr {[format "%u" -1] / 2}]
+}
+
+namespace eval ::pki::rsa {}
+namespace eval ::pki::x509 {}
+namespace eval ::pki::pkcs {}
+
+# # ## ### ##### ######## #############
+## Implementation
+
+proc ::pki::_dec_to_hex {num} {
+ set retval [format %llx $num]
+ return $retval
+}
+
+proc ::pki::_dec_to_ascii {num {bitlen -1}} {
+ set retval ""
+
+ while {$num} {
+ set currchar [expr {$num & 0xff}]
+ set retval "[format %c $currchar]$retval"
+ set num [expr {$num >> 8}]
+ }
+
+ if {$bitlen != -1} {
+ set bytelen [expr {$bitlen / 8}]
+ while {[string length $retval] < $bytelen} {
+ set retval "\x00$retval"
+ }
+ }
+
+ return $retval
+}
+
+proc ::pki::_powm {x y m} {
+ if {$y == 0} {
+ return 1
+ }
+
+ set retval 1
+
+ while {$y > 0} {
+ if {($y & 1) == 1} {
+ set retval [expr {($retval * $x) % $m}]
+ }
+
+ set y [expr {$y >> 1}]
+ set x [expr {($x * $x) % $m}]
+ }
+
+ return $retval
+}
+
+## **NOTE** Requires that "m" be prime
+### a^-1 === a^(m-2) (all mod m)
+proc ::pki::_modi {a m} {
+ return [_powm $a [expr {$m - 2}] $m]
+}
+
+proc ::pki::_oid_number_to_name {oid} {
+ set oid [join $oid .]
+
+ if {[info exists ::pki::oids($oid)]} {
+ return $::pki::oids($oid)
+ }
+
+ return $oid
+}
+
+proc ::pki::_oid_name_to_number {name} {
+ foreach {chkoid chkname} [array get ::pki::oids] {
+ if {[string equal -nocase $chkname $name]} {
+ return [split $chkoid .]
+ }
+ }
+
+ return -code error
+}
+
+proc ::pki::rsa::_encrypt_num {input exponent mod} {
+ set ret [::pki::_powm $input $exponent $mod]
+
+ return $ret
+}
+
+proc ::pki::rsa::_decrypt_num {input exponent mod} {
+ set ret [::pki::_powm $input $exponent $mod]
+
+ return $ret
+}
+
+proc ::pki::_pad_pkcs {data bitlength {blocktype 2}} {
+ set ret ""
+
+ set bytes_to_pad [expr {($bitlength / 8) - 3 - [string length $data]}]
+ if {$bytes_to_pad < 0} {
+ return $data
+ }
+
+ switch -- $blocktype {
+ 0 {
+ }
+ 1 {
+ append ret "\x00\x01"
+ append ret [string repeat "\xff" $bytes_to_pad]
+ append ret "\x00"
+ }
+ 2 {
+ append ret "\x00\x02"
+ for {set idx 0} {$idx < $bytes_to_pad} {incr idx} {
+ append ret [format %c [expr {int(rand() * 255 + 1)}]]
+ }
+ append ret "\x00"
+ }
+ }
+
+ append ret $data
+
+ return $ret
+}
+
+proc ::pki::_unpad_pkcs {data} {
+ set check [string index $data 0]
+ binary scan [string index $data 1] H* blocktype
+ set datalen [string length $data]
+
+ if {$check != "\x00"} {
+ return $data
+ }
+
+ switch -- $blocktype {
+ "00" {
+ # Padding Scheme 1, the first non-zero byte is the start of data
+ for {set idx 2} {$idx < $datalen} {incr idx} {
+ set char [string index $data $idx]
+ if {$char != "\x00"} {
+ set ret [string range $data $idx end]
+ }
+ }
+ }
+ "01" {
+ # Padding Scheme 2, pad bytes are 0xFF followed by 0x00
+ for {set idx 2} {$idx < $datalen} {incr idx} {
+ set char [string index $data $idx]
+ if {$char != "\xff"} {
+ if {$char == "\x00"} {
+ set ret [string range $data [expr {$idx + 1}] end]
+
+ break
+ } else {
+ return -code error "Invalid padding, seperator byte is not 0x00"
+ }
+ }
+ }
+ }
+ "02" {
+ # Padding Scheme 3, pad bytes are random, followed by 0x00
+ for {set idx 2} {$idx < $datalen} {incr idx} {
+ set char [string index $data $idx]
+ if {$char == "\x00"} {
+ set ret [string range $data [expr {$idx + 1}] end]
+
+ break
+ }
+ }
+ }
+ default {
+ return $data
+ }
+ }
+
+ if {![info exists ret]} {
+ return -code error "Invalid padding, no seperator byte found"
+ }
+
+ return $ret
+}
+
+proc ::pki::rsa::encrypt {mode input keylist} {
+ switch -- $mode {
+ "pub" {
+ set exponent_ent e
+ }
+ "priv" {
+ set exponent_ent d
+ }
+ }
+
+ array set key $keylist
+
+ set exponent $key($exponent_ent)
+ set mod $key(n)
+
+ ## RSA requires that the input be no larger than the key
+ set input_len_bits [expr {[string length $input] * 8}]
+ if {$key(l) < $input_len_bits} {
+ return -code error "Message length exceeds key length"
+ }
+
+ binary scan $input H* input_num
+
+ set input_num "0x${input_num}"
+
+ set retval_num [_encrypt_num $input_num $exponent $mod]
+
+ set retval [::pki::_dec_to_ascii $retval_num $key(l)]
+
+ return $retval
+}
+
+proc ::pki::rsa::decrypt {mode input keylist} {
+ switch -- $mode {
+ "pub" {
+ set exponent_ent e
+ }
+ "priv" {
+ set exponent_ent d
+ }
+ }
+
+ array set key $keylist
+
+ set exponent $key($exponent_ent)
+ set mod $key(n)
+
+ binary scan $input H* input_num
+
+ set input_num "0x${input_num}"
+
+ set retval_num [_decrypt_num $input_num $exponent $mod]
+
+ set retval [::pki::_dec_to_ascii $retval_num $key(l)]
+
+ return $retval
+}
+
+proc ::pki::rsa::serialize_key {keylist} {
+ array set key $keylist
+
+ foreach entry [list n e d p q] {
+ if {![info exists key($entry)]} {
+ return -code error "Key does not contain an element $entry"
+ }
+ }
+
+ # Exponent 1
+ ## d (mod p-1)
+ set e1 [expr {$key(d) % ($key(p) - 1)}]
+
+ # Exponent 2
+ #set e2 [expr d mod (q-1)]
+ set e2 [expr {$key(d) % ($key(q) - 1)}]
+
+ # Coefficient
+ ## Modular multiplicative inverse of q mod p
+ set c [::pki::_modi $key(q) $key(p)]
+
+ set ret [::asn::asnSequence \
+ [::asn::asnBigInteger [::math::bignum::fromstr 0]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $key(n)]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $key(e)]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $key(d)]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $key(p)]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $key(q)]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $e1]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $e2]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $c]] \
+ ]
+
+ return [list data $ret begin "-----BEGIN RSA PRIVATE KEY-----" end "-----END RSA PRIVATE KEY-----"]
+}
+
+proc ::pki::_lookup_command {action keylist} {
+ array set key $keylist
+
+ set type $key(type)
+
+ switch -- $action {
+ "encrypt" {
+ set idx 0
+ }
+ "decrypt" {
+ set idx 1
+ }
+ "generate" {
+ set idx 2
+ }
+ "serialize_key" {
+ set idx 3
+ }
+ }
+
+ set cmdlist $::pki::handlers($type)
+
+ set ret [lindex $cmdlist $idx]
+
+ return $ret
+}
+
+proc ::pki::encrypt args {
+ set outmode "hex"
+ set enablepad 1
+
+ set argsmode 0
+ set newargs [list]
+ foreach arg $args {
+ if {![string match "-*" $arg]} {
+ set argsmode 1
+ }
+
+ if {$argsmode} {
+ lappend newargs $arg
+ continue
+ }
+
+ switch -- $arg {
+ "-pub" {
+ set mode pub
+ set padmode 2
+ }
+ "-priv" {
+ set mode priv
+ set padmode 1
+ }
+ "-hex" {
+ set outmode "hex"
+ }
+ "-binary" {
+ set outmode "bin"
+ }
+ "-pad" {
+ set enablepad 1
+ }
+ "-nopad" {
+ set enablepad 0
+ }
+ "--" {
+ set argsmode 1
+ }
+ default {
+ return -code error "usage: encrypt ?-binary? ?-hex? ?-pad? ?-nopad? -priv|-pub ?--? input key"
+ }
+ }
+ }
+ set args $newargs
+
+ if {[llength $args] != 2 || ![info exists mode]} {
+ return -code error "usage: encrypt ?-binary? ?-hex? ?-pad? ?-nopad? -priv|-pub ?--? input key"
+ }
+
+ set input [lindex $args 0]
+ set keylist [lindex $args 1]
+ array set key $keylist
+
+ if {$enablepad} {
+ set input [::pki::_pad_pkcs $input $key(l) $padmode]
+ }
+
+ set encrypt [::pki::_lookup_command encrypt $keylist]
+
+ set retval [$encrypt $mode $input $keylist]
+
+ switch -- $outmode {
+ "hex" {
+ binary scan $retval H* retval
+ }
+ }
+
+ return $retval
+}
+
+proc ::pki::decrypt args {
+ set inmode "hex"
+ set enableunpad 1
+
+ set argsmode 0
+ set newargs [list]
+ foreach arg $args {
+ if {![string match "-*" $arg]} {
+ set argsmode 1
+ }
+
+ if {$argsmode} {
+ lappend newargs $arg
+ continue
+ }
+
+ switch -- $arg {
+ "-pub" {
+ set mode pub
+ }
+ "-priv" {
+ set mode priv
+ }
+ "-hex" {
+ set inmode "hex"
+ }
+ "-binary" {
+ set inmode "bin"
+ }
+ "-unpad" {
+ set enableunpad 1
+ }
+ "-nounpad" {
+ set enableunpad 0
+ }
+ "--" {
+ set argsmode 1
+ }
+ default {
+ return -code error "usage: decrypt ?-binary? ?-hex? ?-unpad? ?-nounpad? -priv|-pub ?--? input key"
+ }
+ }
+ }
+ set args $newargs
+
+ if {[llength $args] != 2 || ![info exists mode]} {
+ return -code error "usage: decrypt ?-binary? ?-hex? ?-unpad? ?-nounpad? -priv|-pub ?--? input key"
+ }
+
+ set input [lindex $args 0]
+ set keylist [lindex $args 1]
+ array set key $keylist
+
+ switch -- $inmode {
+ "hex" {
+ set input [binary format H* $input]
+ }
+ }
+
+ set decrypt [::pki::_lookup_command decrypt $keylist]
+
+ set retval [$decrypt $mode $input $keylist]
+
+ if {$enableunpad} {
+ set retval [::pki::_unpad_pkcs $retval]
+ }
+
+ return $retval
+}
+
+# Hash and encrypt with private key
+proc ::pki::sign {input keylist {algo "sha1"}} {
+ switch -- $algo {
+ "md5" {
+ package require md5
+
+ set header "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10"
+ set hash [md5::md5 $input]
+ }
+ "sha1" {
+ package require sha1
+
+ set header "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"
+ set hash [sha1::sha1 -bin $input]
+ }
+ "sha256" {
+ package require sha256
+
+ set header "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"
+ set hash [sha2::sha256 -bin $input]
+ }
+ "raw" {
+ set header ""
+ set hash $input
+ }
+ default {
+ return -code error "Invalid algorithm selected, must be one of: md5, sha1, sha256, raw"
+ }
+ }
+
+ set plaintext "${header}${hash}"
+
+ array set key $keylist
+
+ set padded [::pki::_pad_pkcs $plaintext $key(l) 1]
+
+ return [::pki::encrypt -binary -nopad -priv -- $padded $keylist]
+}
+
+# Verify known-plaintext with signature
+proc ::pki::verify {signedmessage checkmessage keylist {algo default}} {
+ package require asn
+
+ if {[catch {
+ set plaintext [::pki::decrypt -binary -unpad -pub -- $signedmessage $keylist]
+ }]} {
+ return false
+ }
+
+ if {$algo == "default"} {
+ set algoId "unknown"
+ set digest ""
+
+ catch {
+ ::asn::asnGetSequence plaintext message
+ ::asn::asnGetSequence message digestInfo
+ ::asn::asnGetObjectIdentifier digestInfo algoId
+ ::asn::asnGetOctetString message digest
+ }
+
+ set algoId [::pki::_oid_number_to_name $algoId]
+ } else {
+ set algoId $algo
+ set digest $plaintext
+ }
+
+ switch -- $algoId {
+ "md5" - "md5WithRSAEncryption" {
+ set checkdigest [md5::md5 $checkmessage]
+ }
+ "sha1" - "sha1WithRSAEncryption" {
+ set checkdigest [sha1::sha1 -bin $checkmessage]
+ }
+ "sha256" - "sha256WithRSAEncryption" {
+ set checkdigest [sha2::sha256 -bin $checkmessage]
+ }
+ default {
+ return -code error "Unknown hashing algorithm: $algoId"
+ }
+ }
+
+ if {$checkdigest != $digest} {
+ return false
+ }
+
+ return true
+}
+
+proc ::pki::key {keylist {password ""} {encodePem 1}} {
+ set serialize_key [::pki::_lookup_command serialize_key $keylist]
+
+ if {$serialize_key eq ""} {
+ array set key $keylist
+
+ return -code error "Do not know how to serialize an $key(type) key"
+ }
+
+ array set retval_parts [$serialize_key $keylist]
+
+ if {$encodePem} {
+ set retval [::pki::_encode_pem $retval_parts(data) $retval_parts(begin) $retval_parts(end) $password]
+ } else {
+ if {$password != ""} {
+ return -code error "DER encoded keys may not be password protected"
+ }
+
+ set retval $retval_parts(data)
+ }
+
+ return $retval
+}
+
+proc ::pki::_parse_init {} {
+ if {[info exists ::pki::_parse_init_done]} {
+ return
+ }
+
+ package require asn
+
+ set test "FAIL"
+ catch {
+ set test [binary decode base64 "UEFTUw=="]
+ }
+
+ switch -- $test {
+ "PASS" {
+ set ::pki::rsa::base64_binary 1
+ }
+ "FAIL" {
+ set ::pki::rsa::base64_binary 0
+
+ package require base64
+ }
+ }
+
+ set ::pki::_parse_init_done 1
+ return
+}
+
+proc ::pki::_getopensslkey {password salt bytes} {
+ package require md5
+
+ set salt [string range $salt 0 7]
+
+ set saltedkey "${password}${salt}"
+ for {set ret ""} {[string length $ret] < $bytes} {} {
+ if {![info exists hash]} {
+ set hash $saltedkey
+ } else {
+ set hash "${hash}${saltedkey}"
+ }
+
+ set hash [md5::md5 $hash]
+
+ append ret $hash
+ }
+
+ if {[string length $ret] < $bytes} {
+ set bytes_to_add [expr $bytes - [string length $ret]]
+ set ret "[string repeat "\x00" $bytes_to_add]${ret}"
+ }
+
+ set ret [string range $ret 0 [expr {$bytes - 1}]]
+
+ return $ret
+}
+
+proc ::pki::_encode_pem {data begin end {password ""} {algo "aes-256-cbc"}} {
+ set ret ""
+
+ append ret "${begin}\n"
+ if {$password != ""} {
+ switch -glob -- $algo {
+ "aes-*" {
+ set algostr [string toupper $algo]
+ set work [split $algo "-"]
+ set algo "aes"
+ set keysize [lindex $work 1]
+ set mode [lindex $work 2]
+ set blocksize 16
+ set ivsize [expr {$blocksize * 8}]
+ }
+ default {
+ return -code error "Only AES is currently supported"
+ }
+ }
+
+ set keybytesize [expr {$keysize / 8}]
+ set ivbytesize [expr {$ivsize / 8}]
+
+ set iv ""
+ while {[string length $iv] < $ivbytesize} {
+ append iv [::pki::_random -binary]
+ }
+ set iv [string range $iv 0 [expr {$ivbytesize - 1}]]
+
+ set password_key [::pki::_getopensslkey $password $iv $keybytesize]
+
+ set pad [expr {$blocksize - ([string length $data] % $blocksize)}]
+ append data [string repeat "\x09" $pad]
+
+ switch -- $algo {
+ "aes" {
+ set data [aes::aes -dir encrypt -mode $mode -iv $iv -key $password_key -- $data]
+ }
+ }
+
+ binary scan $iv H* iv
+ set iv [string toupper $iv]
+
+ append ret "Proc-Type: 4,ENCRYPTED\n"
+ append ret "DEK-Info: $algostr,$iv\n"
+ append ret "\n"
+ }
+
+ if {$::pki::rsa::base64_binary} {
+ append ret [binary encode base64 -maxlen 64 $data]
+ } else {
+ append ret [::base64::encode -maxlen 64 $data]
+ }
+ append ret "\n"
+ append ret "${end}\n"
+
+ return $ret
+}
+
+proc ::pki::_parse_pem {pem begin end {password ""}} {
+ # Unencode a PEM-encoded object
+ set testpem [split $pem \n]
+ set pem_startidx [lsearch -exact $testpem $begin]
+ set pem_endidx [lsearch -exact -start $pem_startidx $testpem $end]
+
+ if {$pem_startidx == -1 || $pem_endidx == -1} {
+ return [list data $pem]
+ }
+
+ set pem $testpem
+
+ incr pem_startidx
+ incr pem_endidx -1
+
+ array set ret [list]
+
+ set newpem ""
+ foreach line [lrange $pem $pem_startidx $pem_endidx] {
+ if {[string match "*:*" $line]} {
+ set work [split $line :]
+
+ set var [string toupper [lindex $work 0]]
+ set val [string trim [join [lrange $work 1 end] :]]
+
+ set ret($var) $val
+
+ continue
+ }
+
+ set line [string trim $line]
+
+ append newpem $line
+ }
+
+ if {$newpem != ""} {
+ if {$::pki::rsa::base64_binary} {
+ set pem [binary decode base64 $newpem]
+ } else {
+ set pem [::base64::decode $newpem]
+ }
+ }
+
+ if {[info exists ret(PROC-TYPE)] && [info exists ret(DEK-INFO)]} {
+ if {$ret(PROC-TYPE) == "4,ENCRYPTED"} {
+ if {$password == ""} {
+ return [list error "ENCRYPTED"]
+ }
+
+ switch -glob -- $ret(DEK-INFO) {
+ "DES-EDE3-*" {
+ package require des
+
+ # DES-EDE3-CBC,03B1F1883BFA4412
+ set keyinfo $ret(DEK-INFO)
+
+ set work [split $keyinfo ,]
+ set cipher [lindex $work 0]
+ set iv [lindex $work 1]
+
+ set work [split $cipher -]
+ set algo [lindex $work 0]
+ set mode [string tolower [lindex $work 2]]
+
+ set iv [binary format H* $iv]
+ set password_key [::pki::_getopensslkey $password $iv 24]
+
+ set pem [DES::des -dir decrypt -mode $mode -iv $iv -key $password_key -- $pem]
+ }
+ "AES-*" {
+ package require aes
+
+ # AES-256-CBC,AF517BA39E94FF39D1395C63F6DE9657
+ set keyinfo $ret(DEK-INFO)
+
+ set work [split $keyinfo ,]
+ set cipher [lindex $work 0]
+ set iv [lindex $work 1]
+
+ set work [split $cipher -]
+ set algo [lindex $work 0]
+ set keysize [lindex $work 1]
+ set mode [string tolower [lindex $work 2]]
+
+ set iv [binary format H* $iv]
+ set password_key [::pki::_getopensslkey $password $iv [expr $keysize / 8]]
+
+ set pem [aes::aes -dir decrypt -mode $mode -iv $iv -key $password_key -- $pem]
+ }
+ }
+ }
+ }
+
+ set ret(data) $pem
+
+ return [array get ret]
+}
+
+proc ::pki::pkcs::parse_key {key {password ""}} {
+ array set parsed_key [::pki::_parse_pem $key "-----BEGIN RSA PRIVATE KEY-----" "-----END RSA PRIVATE KEY-----" $password]
+
+ set key_seq $parsed_key(data)
+
+ ::asn::asnGetSequence key_seq key
+ ::asn::asnGetBigInteger key version
+ ::asn::asnGetBigInteger key ret(n)
+ ::asn::asnGetBigInteger key ret(e)
+ ::asn::asnGetBigInteger key ret(d)
+ ::asn::asnGetBigInteger key ret(p)
+ ::asn::asnGetBigInteger key ret(q)
+
+ set ret(n) [::math::bignum::tostr $ret(n)]
+ set ret(e) [::math::bignum::tostr $ret(e)]
+ set ret(d) [::math::bignum::tostr $ret(d)]
+ set ret(p) [::math::bignum::tostr $ret(p)]
+ set ret(q) [::math::bignum::tostr $ret(q)]
+ set ret(l) [expr {int([::pki::_bits $ret(n)] / 8.0000 + 0.5) * 8}]
+ set ret(type) rsa
+
+ return [array get ret]
+}
+
+proc ::pki::x509::_dn_to_list {dn} {
+ set ret ""
+
+ while {$dn != ""} {
+ ::asn::asnGetSet dn dn_parts
+ ::asn::asnGetSequence dn_parts curr_part
+ ::asn::asnGetObjectIdentifier curr_part label
+ ::asn::asnGetString curr_part value
+
+ set label [::pki::_oid_number_to_name $label]
+ lappend ret $label $value
+ }
+
+ return $ret
+}
+
+proc ::pki::x509::_list_to_dn {name} {
+ set ret ""
+ foreach {oid_name value} $name {
+ if {![regexp {[^ A-Za-z0-9'()+,.:/?=-]} $value]} {
+ set asnValue [::asn::asnPrintableString $value]
+ } else {
+ set asnValue [::asn::asnUTF8String $value]
+ }
+
+ append ret [::asn::asnSet \
+ [::asn::asnSequence \
+ [::asn::asnObjectIdentifier [::pki::_oid_name_to_number $oid_name]] \
+ $asnValue \
+ ] \
+ ] \
+ }
+
+ return $ret
+}
+
+proc ::pki::x509::_dn_to_string {dn} {
+ set ret [list]
+
+ foreach {label value} [_dn_to_list $dn] {
+ set label [string toupper $label]
+
+ lappend ret "$label=$value"
+ }
+
+ set ret [join $ret {, }]
+
+ return $ret
+}
+
+proc ::pki::x509::_string_to_dn {string} {
+ foreach {label value} [split $string ",="] {
+ set label [string trim $label]
+ set value [string trim $value]
+
+ lappend namelist $label $value
+ }
+
+ return [_list_to_dn $namelist]
+}
+
+proc ::pki::x509::_dn_to_cn {dn} {
+ foreach {label value} [split $dn ",="] {
+ set label [string toupper [string trim $label]]
+ set value [string trim $value]
+
+ if {$label == "CN"} {
+ return $value
+ }
+ }
+
+ return ""
+}
+
+proc ::pki::x509::_utctime_to_native {utctime} {
+ return [clock scan $utctime -format {%y%m%d%H%M%SZ} -gmt true]
+}
+
+proc ::pki::x509::_native_to_utctime {time} {
+ return [clock format $time -format {%y%m%d%H%M%SZ} -gmt true]
+}
+
+proc ::pki::x509::parse_cert {cert} {
+ array set parsed_cert [::pki::_parse_pem $cert "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"]
+ set cert_seq $parsed_cert(data)
+
+ array set ret [list]
+
+ # Decode X.509 certificate, which is an ASN.1 sequence
+ ::asn::asnGetSequence cert_seq wholething
+ ::asn::asnGetSequence wholething cert
+
+ set ret(cert) $cert
+ set ret(cert) [::asn::asnSequence $ret(cert)]
+ binary scan $ret(cert) H* ret(cert)
+
+ ::asn::asnPeekByte cert peek_tag
+ if {$peek_tag != 0x02} {
+ # Version number is optional, if missing assumed to be value of 0
+ ::asn::asnGetContext cert - asn_version
+ ::asn::asnGetInteger asn_version ret(version)
+ incr ret(version)
+ } else {
+ set ret(version) 1
+ }
+
+ ::asn::asnGetBigInteger cert ret(serial_number)
+ ::asn::asnGetSequence cert data_signature_algo_seq
+ ::asn::asnGetObjectIdentifier data_signature_algo_seq ret(data_signature_algo)
+ ::asn::asnGetSequence cert issuer
+ ::asn::asnGetSequence cert validity
+ ::asn::asnGetUTCTime validity ret(notBefore)
+ ::asn::asnGetUTCTime validity ret(notAfter)
+ ::asn::asnGetSequence cert subject
+ ::asn::asnGetSequence cert pubkeyinfo
+ ::asn::asnGetSequence pubkeyinfo pubkey_algoid
+ ::asn::asnGetObjectIdentifier pubkey_algoid ret(pubkey_algo)
+ ::asn::asnGetBitString pubkeyinfo pubkey
+
+ set extensions_list [list]
+ while {$cert != ""} {
+ ::asn::asnPeekByte cert peek_tag
+
+ switch -- [format {0x%02x} $peek_tag] {
+ "0xa1" {
+ ::asn::asnGetContext cert - issuerUniqID
+ }
+ "0xa2" {
+ ::asn::asnGetContext cert - subjectUniqID
+ }
+ "0xa3" {
+ ::asn::asnGetContext cert - extensions_ctx
+ ::asn::asnGetSequence extensions_ctx extensions
+ while {$extensions != ""} {
+ ::asn::asnGetSequence extensions extension
+ ::asn::asnGetObjectIdentifier extension ext_oid
+
+ ::asn::asnPeekByte extension peek_tag
+ if {$peek_tag == 0x1} {
+ ::asn::asnGetBoolean extension ext_critical
+ } else {
+ set ext_critical false
+ }
+
+ ::asn::asnGetOctetString extension ext_value_seq
+
+ set ext_oid [::pki::_oid_number_to_name $ext_oid]
+
+ set ext_value [list $ext_critical]
+
+ switch -- $ext_oid {
+ id-ce-basicConstraints {
+ ::asn::asnGetSequence ext_value_seq ext_value_bin
+
+ if {$ext_value_bin != ""} {
+ ::asn::asnGetBoolean ext_value_bin allowCA
+ } else {
+ set allowCA "false"
+ }
+
+ if {$ext_value_bin != ""} {
+ ::asn::asnGetInteger ext_value_bin caDepth
+ } else {
+ set caDepth -1
+ }
+
+ lappend ext_value $allowCA $caDepth
+ }
+ default {
+ binary scan $ext_value_seq H* ext_value_seq_hex
+ lappend ext_value $ext_value_seq_hex
+ }
+ }
+
+ lappend extensions_list $ext_oid $ext_value
+ }
+ }
+ }
+ }
+ set ret(extensions) $extensions_list
+
+ ::asn::asnGetSequence wholething signature_algo_seq
+ ::asn::asnGetObjectIdentifier signature_algo_seq ret(signature_algo)
+ ::asn::asnGetBitString wholething ret(signature)
+
+ # Convert values from ASN.1 decoder to usable values if needed
+ set ret(notBefore) [::pki::x509::_utctime_to_native $ret(notBefore)]
+ set ret(notAfter) [::pki::x509::_utctime_to_native $ret(notAfter)]
+ set ret(serial_number) [::math::bignum::tostr $ret(serial_number)]
+ set ret(data_signature_algo) [::pki::_oid_number_to_name $ret(data_signature_algo)]
+ set ret(signature_algo) [::pki::_oid_number_to_name $ret(signature_algo)]
+ set ret(pubkey_algo) [::pki::_oid_number_to_name $ret(pubkey_algo)]
+ set ret(issuer) [_dn_to_string $issuer]
+ set ret(subject) [_dn_to_string $subject]
+ set ret(signature) [binary format B* $ret(signature)]
+ binary scan $ret(signature) H* ret(signature)
+
+ # Handle RSA public keys by extracting N and E
+ switch -- $ret(pubkey_algo) {
+ "rsaEncryption" {
+ set pubkey [binary format B* $pubkey]
+ binary scan $pubkey H* ret(pubkey)
+
+ ::asn::asnGetSequence pubkey pubkey_parts
+ ::asn::asnGetBigInteger pubkey_parts ret(n)
+ ::asn::asnGetBigInteger pubkey_parts ret(e)
+
+ set ret(n) [::math::bignum::tostr $ret(n)]
+ set ret(e) [::math::bignum::tostr $ret(e)]
+ set ret(l) [expr {int([::pki::_bits $ret(n)] / 8.0000 + 0.5) * 8}]
+ set ret(type) rsa
+ }
+ }
+
+ return [array get ret]
+}
+
+# Verify whether a cert is valid, regardless of trust
+proc ::pki::x509::validate_cert {cert args} {
+ # Verify arguments and load options
+ for {set idx 0} {$idx < [llength $args]} {incr idx} {
+ set arg [lindex $args $idx]
+
+ switch -- $arg {
+ "-sign_message" {
+ incr idx
+ set dn [lindex $args $idx]
+ set cn [_dn_to_cn $dn]
+
+ set opts(sign_message) $cn
+ }
+ "-encrypt_message" {
+ incr idx
+ set dn [lindex $args $idx]
+ set cn [_dn_to_cn $dn]
+
+ set opts(encrypt_message) $cn
+ }
+ "-sign_cert" {
+ incr idx
+ set dn [lindex $args $idx]
+ if {$dn == "ALL" || $dn == "ANY"} {
+ set cn $dn
+ } else {
+ set cn [_dn_to_cn $dn]
+ }
+
+ incr idx
+ set currdepth [lindex $args $idx]
+
+ set opts(sign_cert) [list $cn $currdepth]
+ }
+ "-ssl" {
+ incr idx
+ set dn [lindex $args $idx]
+ set cn [_dn_to_cn $dn]
+
+ set opts(ssl) $cn
+ }
+ default {
+ return -code error {wrong # args: should be "validate_cert cert ?-sign_message dn_of_signer? ?-encrypt_message dn_of_signer? ?-sign_cert [dn_to_be_signed | ANY | ALL] ca_depth? ?-ssl dn?"}
+ }
+ }
+ }
+
+ # Load cert
+ array set cert_arr $cert
+
+ # Validate certificate
+ ## Validate times
+ if {![info exists cert_arr(notBefore)] || ![info exists cert_arr(notAfter)]} {
+ return false
+ }
+
+ set currtime [clock seconds]
+ if {$currtime < $cert_arr(notBefore) || $currtime > $cert_arr(notAfter)} {
+ return false
+ }
+
+ # Check for extensions and process them
+ ## Critical extensions must be understood, non-critical extensions may be ignored if not understood
+ set CA 0
+ set CAdepth -1
+ foreach {ext_id ext_val} $cert_arr(extensions) {
+ set critical [lindex $ext_val 0]
+
+ switch -- $ext_id {
+ id-ce-basicConstraints {
+ set CA [lindex $ext_val 1]
+ set CAdepth [lindex $ext_val 2]
+ }
+ default {
+ ### If this extensions is critical and not understood, we must reject it
+ if {$critical} {
+ return false
+ }
+ }
+ }
+ }
+
+ if {[info exists opts(sign_cert)]} {
+ if {!$CA} {
+ return false
+ }
+
+ if {$CAdepth >= 0} {
+ set sign_depth [lindex $opts(sign_cert) 1]
+ if {$sign_depth > $CAdepth} {
+ return false
+ }
+ }
+ }
+
+ return true
+}
+
+proc ::pki::x509::verify_cert {cert trustedcerts {intermediatecerts ""}} {
+ # Validate cert
+ if {![validate_cert $cert]} {
+ return false;
+ }
+
+ # Load trusted certs
+ foreach trustedcert_list $trustedcerts {
+ if {![validate_cert $trustedcert_list -sign_cert ANY -1]} {
+ continue
+ }
+
+ unset -nocomplain trustedcert
+ array set trustedcert $trustedcert_list
+
+ set subject $trustedcert(subject)
+
+ set trustedcertinfo($subject) $trustedcert_list
+ }
+
+ # Load intermediate certs
+ foreach intermediatecert_list $intermediatecerts {
+ if {![validate_cert $intermediatecert_list -sign_cert ANY -1]} {
+ continue
+ }
+
+ unset -nocomplain intermediatecert
+ array set intermediatecert $intermediatecert_list
+
+ set subject $intermediatecert(subject)
+
+ set intermediatecertinfo($subject) $intermediatecert_list
+ }
+
+ # Load cert
+ array set cert_arr $cert
+
+ # Verify certificate
+ ## Encode certificate to hash later
+ set message [binary format H* $cert_arr(cert)]
+
+ ## Find CA to verify against
+ if {![info exists trustedcertinfo($cert_arr(issuer))]} {
+ ## XXX: Try to find an intermediate path
+
+ ## XXX: Verify each cert in the intermediate path, returning in
+ ## failure if a link in the chain breaks
+
+ ## Otherwise, return in failure
+ return false
+ }
+
+ set cacert $trustedcertinfo($cert_arr(issuer))
+ array set cacert_arr $cacert
+
+ ## Set signature to binary form
+ set signature [::pki::_dec_to_ascii 0x$cert_arr(signature) $cacert_arr(l)]
+
+ ## Verify
+ set ret [::pki::verify $signature $message $cacert]
+
+ return $ret
+}
+
+# Generate a PKCS#10 Certificate Signing Request
+proc ::pki::pkcs::create_csr {keylist namelist {encodePem 0} {algo "sha1"}} {
+ array set key $keylist
+
+ set name [::pki::x509::_list_to_dn $namelist]
+
+ set type $key(type)
+
+ switch -- $type {
+ "rsa" {
+ set pubkey [::asn::asnSequence \
+ [::asn::asnBigInteger [::math::bignum::fromstr $key(n)]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $key(e)]] \
+ ]
+ set pubkey_algo_params [::asn::asnNull]
+ }
+ }
+ binary scan $pubkey B* pubkey_bitstring
+
+ set cert_req_info [::asn::asnSequence \
+ [::asn::asnInteger 0] \
+ [::asn::asnSequence $name] \
+ [::asn::asnSequence \
+ [::asn::asnSequence \
+ [::asn::asnObjectIdentifier [::pki::_oid_name_to_number ${type}Encryption]] \
+ $pubkey_algo_params \
+ ] \
+ [::asn::asnBitString $pubkey_bitstring] \
+ ] \
+ [::asn::asnContextConstr 0 ""] \
+ ]
+
+ set signature [::pki::sign $cert_req_info $keylist $algo]
+ binary scan $signature B* signature_bitstring
+
+ set cert_req [::asn::asnSequence \
+ $cert_req_info \
+ [::asn::asnSequence [::asn::asnObjectIdentifier [::pki::_oid_name_to_number "${algo}With${type}Encryption"]] [::asn::asnNull]] \
+ [::asn::asnBitString $signature_bitstring] \
+ ]
+
+ if {$encodePem} {
+ set cert_req [::pki::_encode_pem $cert_req "-----BEGIN CERTIFICATE REQUEST-----" "-----END CERTIFICATE REQUEST-----"]
+ }
+
+ return $cert_req
+}
+
+# Parse a PKCS#10 CSR
+proc ::pki::pkcs::parse_csr {csr} {
+ array set ret [list]
+
+ array set parsed_csr [::pki::_parse_pem $csr "-----BEGIN CERTIFICATE REQUEST-----" "-----END CERTIFICATE REQUEST-----"]
+ set csr $parsed_csr(data)
+
+ ::asn::asnGetSequence csr cert_req_seq
+ ::asn::asnGetSequence cert_req_seq cert_req_info
+
+ set cert_req_info_saved [::asn::asnSequence $cert_req_info]
+
+ ::asn::asnGetInteger cert_req_info version
+ ::asn::asnGetSequence cert_req_info name
+ ::asn::asnGetSequence cert_req_info pubkeyinfo
+ ::asn::asnGetSequence pubkeyinfo pubkey_algoid
+ ::asn::asnGetObjectIdentifier pubkey_algoid pubkey_type
+ ::asn::asnGetBitString pubkeyinfo pubkey
+ ::asn::asnGetSequence cert_req_seq signature_algo_seq
+ ::asn::asnGetObjectIdentifier signature_algo_seq signature_algo
+ ::asn::asnGetBitString cert_req_seq signature_bitstring
+
+ # Convert parsed fields to native types
+ set signature [binary format B* $signature_bitstring]
+ set ret(subject) [::pki::x509::_dn_to_string $name]
+
+ ## Convert Pubkey type to string
+ set pubkey_type [::pki::_oid_number_to_name $pubkey_type]
+
+ # Parse public key, based on type
+ switch -- $pubkey_type {
+ "rsaEncryption" {
+ set pubkey [binary format B* $pubkey]
+
+ ::asn::asnGetSequence pubkey pubkey_parts
+ ::asn::asnGetBigInteger pubkey_parts key(n)
+ ::asn::asnGetBigInteger pubkey_parts key(e)
+
+ set key(n) [::math::bignum::tostr $key(n)]
+ set key(e) [::math::bignum::tostr $key(e)]
+ set key(l) [expr {2**int(ceil(log([::pki::_bits $key(n)])/log(2)))}]
+ set key(type) rsa
+ }
+ default {
+ return -code error "Unsupported key type: $pubkey_type"
+ }
+ }
+
+ # Convert key to RSA parts
+ set keylist [array get key]
+
+ # Validate CSR requestor has access to the private key
+ set csrValid [::pki::verify $signature $cert_req_info_saved $keylist]
+ if {!$csrValid} {
+ return -code error "CSR Signature check failed"
+ }
+
+ array set ret $keylist
+
+ return [array get ret]
+}
+
+proc ::pki::x509::create_cert {signreqlist cakeylist serial_number notBefore notAfter isCA extensions {encodePem 0} {algo "sha1"}} {
+ # Parse parameters
+ array set cakey $cakeylist
+ array set signreq $signreqlist
+
+ set type $signreq(type)
+
+ # Process extensions
+ set extensions_list $extensions
+ unset extensions
+ array set extensions $extensions_list
+
+ # If we are generating a CA cert, add a CA extension
+ if {$isCA} {
+ set extensions(id-ce-basicConstraints) [list true true -1]
+ }
+
+ # Determine what version we need to use (default to 1)
+ if {[array get extensions] == ""} {
+ set version 1
+ } else {
+ set version 3
+ }
+
+ set certlist [list]
+
+ # Create certificate to be signed
+ ## Insert version number (if not version 1)
+ if {$version != 1} {
+ lappend certlist [::asn::asnContextConstr 0 [::asn::asnInteger [expr {$version - 1}]]]
+ }
+
+ ## Insert serial number
+ lappend certlist [::asn::asnBigInteger [math::bignum::fromstr $serial_number]]
+
+ ## Insert data algorithm
+ lappend certlist [::asn::asnSequence \
+ [::asn::asnObjectIdentifier [::pki::_oid_name_to_number "${algo}With${type}Encryption"]] \
+ [::asn::asnNull] \
+ ]
+
+ ## Insert issuer
+ lappend certlist [::asn::asnSequence [::pki::x509::_string_to_dn $cakey(subject)]]
+
+ ## Insert validity requirements
+ lappend certlist [::asn::asnSequence \
+ [::asn::asnUTCTime [::pki::x509::_native_to_utctime $notBefore]] \
+ [::asn::asnUTCTime [::pki::x509::_native_to_utctime $notAfter]] \
+ ]
+
+ ## Insert subject
+ lappend certlist [::asn::asnSequence [::pki::x509::_string_to_dn $signreq(subject)]]
+
+ ## Insert public key information
+ switch -- $type {
+ "rsa" {
+ set pubkey [::asn::asnSequence \
+ [::asn::asnBigInteger [::math::bignum::fromstr $signreq(n)]] \
+ [::asn::asnBigInteger [::math::bignum::fromstr $signreq(e)]] \
+ ]
+
+ set pubkey_algo_params [::asn::asnNull]
+ }
+ }
+ binary scan $pubkey B* pubkey_bitstring
+
+ lappend certlist [::asn::asnSequence \
+ [::asn::asnSequence \
+ [::asn::asnObjectIdentifier [::pki::_oid_name_to_number "${type}Encryption"]] \
+ $pubkey_algo_params \
+ ] \
+ [::asn::asnBitString $pubkey_bitstring] \
+ ]
+
+ ## Insert extensions
+ if {[array get extensions] != ""} {
+ set extensionslist [list]
+
+ foreach {extension extvalue} [array get extensions] {
+ set critical 0
+
+ switch -- $extension {
+ "id-ce-basicConstraints" {
+ set critical [lindex $extvalue 0]
+ set allowCA [lindex $extvalue 1]
+ set caDepth [lindex $extvalue 2]
+
+ if {$caDepth < 0} {
+ set extvalue [::asn::asnSequence [::asn::asnBoolean $allowCA]]
+ } else {
+ set extvalue [::asn::asnSequence [::asn::asnBoolean $allowCA] [::asn::asnInteger $caDepth]]
+ }
+ }
+ default {
+ return -code error "Unknown extension: $extension"
+ }
+ }
+
+ lappend extensionslist [::asn::asnSequence \
+ [::asn::asnObjectIdentifier [::pki::_oid_name_to_number $extension]] \
+ [::asn::asnBoolean $critical] \
+ [::asn::asnOctetString $extvalue] \
+ ]
+ }
+
+ lappend certlist [::asn::asnContextConstr 3 [::asn::asnSequenceFromList $extensionslist]]
+ }
+
+ ## Enclose certificate data in an ASN.1 sequence
+ set cert [::asn::asnSequenceFromList $certlist]
+
+ # Sign certificate request using CA
+ set signature [::pki::sign $cert $cakeylist $algo]
+ binary scan $signature B* signature_bitstring
+
+ set cert [::asn::asnSequence \
+ $cert \
+ [::asn::asnSequence \
+ [::asn::asnObjectIdentifier [::pki::_oid_name_to_number "${algo}With${type}Encryption"]] \
+ [::asn::asnNull] \
+ ] \
+ [::asn::asnBitString $signature_bitstring] \
+ ]
+
+ if {$encodePem} {
+ set cert [::pki::_encode_pem $cert "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"]
+ }
+
+ return $cert
+}
+
+proc ::pki::_bits {num} {
+ if {$num == 0} {
+ return 0
+ }
+
+ set num [format %llx $num]
+
+ set numlen [string length $num]
+
+ set numprecise 2
+
+ if {$numlen > $numprecise} {
+ set basebits [expr {($numlen - $numprecise) * 4}]
+ } else {
+ set basebits 0
+ }
+
+ set highbits 0x[string range $num 0 [expr {$numprecise - 1}]]
+
+ set ret [expr {$basebits + log($highbits) / 0.69314718055994530941723}]
+
+ set ret [expr {floor($ret) + 1}]
+
+ set ret [lindex [split $ret .] 0]
+
+ return $ret
+}
+
+proc ::pki::_random args {
+ if {[lindex $args 0] == "-binary"} {
+ set outputmode binary
+ } else {
+ set outputmode numeric
+ }
+
+ if {![info exists ::pki::_random_dev]} {
+ foreach trydev [list /dev/urandom /dev/random __RAND__] {
+ if {$trydev != "__RAND__"} {
+ if {[catch {
+ set fd [open $trydev [list RDONLY BINARY]]
+ close $fd
+ unset fd
+ }]} {
+ continue
+ }
+ }
+
+ set ::pki::_random_dev $trydev
+
+ break
+ }
+ }
+
+ set dev ${::pki::_random_dev}
+
+ switch -- $dev {
+ "__RAND__" {
+ set ret [expr {int(rand() * 2147483647)}]
+ }
+ default {
+ set fd [open $dev [list RDONLY BINARY]]
+ set data [read $fd 8]
+ close $fd
+
+ binary scan $data H* ret
+ set ret [expr 0x$ret]
+ }
+ }
+
+ switch -- $outputmode {
+ "numeric" {
+ # Do nothing, results are already numeric
+ }
+ "binary" {
+ set ret [binary format H* [format %02llx $ret]]
+ }
+ }
+
+ return $ret
+}
+
+proc ::pki::_isprime {n} {
+ set k 10
+
+ if {$n <= 3} {
+ return true
+ }
+
+ if {$n % 2 == 0} {
+ return false
+ }
+
+ # write n - 1 as 2^s·d with d odd by factoring powers of 2 from n \u2212 1
+ set d [expr {$n - 1}]
+ set s 0
+ while {$d % 2 == 0} {
+ set d [expr {$d / 2}]
+ incr s
+ }
+
+ while {$k > 0} {
+ incr k -1
+ set rand_1 [expr {int(rand() * $::pki::INT_MAX)}]
+ set rand_2 [expr {int(rand() * $::pki::INT_MAX)}]
+ if {$rand_1 < $rand_2} {
+ set rand_num $rand_1
+ set rand_den $rand_2
+ } else {
+ set rand_num $rand_2
+ set rand_den $rand_1
+ }
+
+ set a [expr {2 + (($n - 4) * $rand_num / $rand_den)}]
+
+ set x [_powm $a $d $n]
+ if {$x == 1 || $x == $n - 1} {
+ continue
+ }
+
+ for {set r 1} {$r < $s} {incr r} {
+ set x [_powm $x 2 $n]
+ if {$x == 1} {
+ return false
+ }
+ if {$x == $n - 1} {
+ break
+ }
+ }
+
+ if {$x != $n - 1} {
+ return false
+ }
+ }
+
+ return true
+}
+
+proc ::pki::rsa::_generate_private {p q e bitlength} {
+ set totient [expr {($p - 1) * ($q - 1)}]
+
+ for {set di 1} {$di < $e} {incr di} {
+ set dchk [expr {($totient * $di + 1) / $e}]
+ set chkval [expr {$dchk * $e - 1}]
+
+ set rem [expr {$chkval % $totient}]
+ if {$rem == 0} {
+ break
+ }
+ }
+
+ # puts "bd=[_bits $dchk], di = $di"
+ for {} {1} {incr di $e} {
+ set dchk [expr {($totient * $di + 1) / $e}]
+ set chkval [expr {$dchk * $e - 1}]
+
+ set rem [expr {$chkval % $totient}]
+ if {$rem == 0} {
+ if {[::pki::_bits $dchk] > $bitlength} {
+ if {![info exists d]} {
+ set d $dchk
+ }
+
+ break
+ }
+
+ set d $dchk
+ }
+
+ }
+
+ return $d
+}
+
+proc ::pki::rsa::generate {bitlength {exponent 0x10001}} {
+ set e $exponent
+
+ # Step 1. Pick 2 numbers that when multiplied together will give a number with the appropriate length
+ set componentbitlen [expr {$bitlength / 2}]
+ set bitmask [expr {(1 << $componentbitlen) - 1}]
+
+ set p 0
+ set q 0
+ while 1 {
+ set plen [::pki::_bits $p]
+ set qlen [::pki::_bits $q]
+
+ if {$plen >= $componentbitlen} {
+ set p [expr {$p & $bitmask}]
+
+ set plen [::pki::_bits $p]
+ }
+
+ if {$qlen >= $componentbitlen} {
+ set q [expr {$q & $bitmask}]
+
+ set qlen [::pki::_bits $q]
+ }
+
+ if {$plen >= $componentbitlen && $qlen >= $componentbitlen} {
+ break
+ }
+
+ set x [::pki::_random]
+ set y [::pki::_random]
+
+ set xlen [expr {[::pki::_bits $x] / 2}]
+ set ylen [expr {[::pki::_bits $y] / 2}]
+
+ set xmask [expr {(1 << $xlen) - 1}]
+ set ymask [expr {(1 << $ylen) - 1}]
+
+ set p [expr {($p << $xlen) + ($x & $xmask)}]
+ set q [expr {($q << $ylen) + ($y & $ymask)}]
+ }
+
+
+ # Step 2. Verify that "p" and "q" are useful
+ ## Step 2.a. Verify that they are not too close
+ ### Where "too close" is defined as 2*n^(1/4)
+ set quadroot_of_n [expr {isqrt(isqrt($p * $q))}]
+ set min_distance [expr {2 * $quadroot_of_n}]
+ set distance [expr {abs($p - $q)}]
+
+ if {$distance < $min_distance} {
+ #### Try again.
+
+ return [::pki::rsa::generate $bitlength $exponent]
+ }
+
+ # Step 3. Convert the numbers into prime numbers
+ if {$p % 2 == 0} {
+ incr p -1
+ }
+ while {![::pki::_isprime $p]} {
+ incr p -2
+ }
+
+ if {$q % 2 == 0} {
+ incr q -1
+ }
+ while {![::pki::_isprime $q]} {
+ incr q -2
+ }
+
+ # Step 4. Compute N by multiplying P and Q
+ set n [expr {$p * $q}]
+ set retkey(n) $n
+
+ # Step 5. Compute D ...
+ ## Step 5.a. Generate D
+ set d [::pki::rsa::_generate_private $p $q $e $bitlength]
+ set retkey(d) $d
+
+ ## Step 5.b. Verify D is large enough
+ ### Verify that D is greater than (1/3)*n^(1/4)
+ set quadroot_of_n [expr {isqrt(isqrt($n))}]
+ set min_d [expr {$quadroot_of_n / 3}]
+ if {$d < $min_d} {
+ #### Try again.
+
+ return [::pki::rsa::generate $bitlength $exponent]
+ }
+
+ # Step 6. Encode key information
+ set retkey(type) rsa
+ set retkey(e) $e
+ set retkey(l) $bitlength
+
+ # Step 7. Record additional information that will be needed to write out a PKCS#1 compliant key
+ set retkey(p) $p
+ set retkey(q) $q
+
+ return [array get retkey]
+}
+
+## Initialize parsing routines, which may load additional packages (base64)
+::pki::_parse_init
+
+# # ## ### ##### ######## #############
+## Ready
+
+package provide pki 0.6
diff --git a/tcllib/modules/pki/pki.test b/tcllib/modules/pki/pki.test
new file mode 100644
index 0000000..9a66ff7
--- /dev/null
+++ b/tcllib/modules/pki/pki.test
@@ -0,0 +1,403 @@
+# rsa.test - Copyright (c) 2010 Roy Keene, Andreas Kupries
+#
+# the test-values are taken from:
+# ??
+#
+# $Id$
+
+# -------------------------------------------------------------------------
+
+source [file join \
+ [file dirname [file dirname [file join [pwd] [info script]]]] \
+ devtools testutilities.tcl]
+
+testsNeedTcl 8.5
+testsNeedTcltest 2
+
+support {
+ use asn/asn.tcl asn
+ use base64/base64.tcl base64
+ use des/des.tcl des
+ use math/bignum.tcl math::bignum
+ use md5/md5x.tcl md5 ;# md5 2.x!
+ use sha1/sha1.tcl sha1
+ use sha1/sha256.tcl sha256
+ use aes/aes.tcl aes
+}
+testing {
+ useLocal pki.tcl pki
+}
+
+# -------------------------------------------------------------------------
+
+test rsa-parse-aeskey-1.0 {parse_key} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test.key.aes] r]
+ set privkey [read $fd]
+ close $fd ; unset fd
+
+ set password "ARoseIsARoseIsARose"
+} -body {
+ dictsort [::pki::pkcs::parse_key $privkey $password]
+} -cleanup {
+ unset privkey password
+} -result {d 4821708078078814272825737770757352316921506482526873519719051463644222257651923197825994018656421190278177720014932984365118366602027256382375921341192781177112398174373076887129954817822368157304055327895506098881228521168940083073283595671416028753301552269895847121119899618677892849270785450525228277087545852904246091363590456069440574806479467479724488815982259021323617233373449030738298588184972148901173121148634892058648703349014218791530847767762515588159636682267580355756974401553230588009724381821442787415368995149629493809933380894522643851913309510865914033228986295772184835354265231482340110365953 e 65537 l 2048 n 20606474229739240365059039861892702888430699076971875439310562489263214483191006887246310401088091004060054335612563612411917991000786455919906798626524375611634511845705141177165689526939976649836053082770641226108840795034158866930145876068965913035873741839723777813944236146677082729876717709231945588228947001753667862858297850461440476551869914895980840772354759428144421149613554402682425181850295101375623815063198594704730226304723548382258248785251384891817576645955371924194287071569929361029204431671633821160423187871463713776212876252047207985933876036458343140423787003660018607222053134170461225878347 p 151903506703070744388477144011062627922344490485619606525415439019240332914406492743669447033002173681213181316912563402042948038453846038272948559137687751740338183189809253891948725876860938554446847405802064303332011747457634488959045893106143553864597546566768167692662722957924358908573096347694362541259 q 135655026516400219835182288872064651548166413404981613626584975073279765274581289299963783472100901444904090497633607380440126562041149560121122191822729803838692049608052805597752016733559257401832457990520641184379220785155858496800358974243933025297673311305372951490297748845447521569920828454777048335233 type rsa}
+
+test rsa-parse-deskey-1.0 {parse_key} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test.key.des] r]
+ set privkey [read $fd]
+ close $fd ; unset fd
+
+ set password "ARoseIsARoseIsARose"
+} -body {
+ dictsort [::pki::pkcs::parse_key $privkey $password]
+} -cleanup {
+ unset privkey password
+} -result {d 4821708078078814272825737770757352316921506482526873519719051463644222257651923197825994018656421190278177720014932984365118366602027256382375921341192781177112398174373076887129954817822368157304055327895506098881228521168940083073283595671416028753301552269895847121119899618677892849270785450525228277087545852904246091363590456069440574806479467479724488815982259021323617233373449030738298588184972148901173121148634892058648703349014218791530847767762515588159636682267580355756974401553230588009724381821442787415368995149629493809933380894522643851913309510865914033228986295772184835354265231482340110365953 e 65537 l 2048 n 20606474229739240365059039861892702888430699076971875439310562489263214483191006887246310401088091004060054335612563612411917991000786455919906798626524375611634511845705141177165689526939976649836053082770641226108840795034158866930145876068965913035873741839723777813944236146677082729876717709231945588228947001753667862858297850461440476551869914895980840772354759428144421149613554402682425181850295101375623815063198594704730226304723548382258248785251384891817576645955371924194287071569929361029204431671633821160423187871463713776212876252047207985933876036458343140423787003660018607222053134170461225878347 p 151903506703070744388477144011062627922344490485619606525415439019240332914406492743669447033002173681213181316912563402042948038453846038272948559137687751740338183189809253891948725876860938554446847405802064303332011747457634488959045893106143553864597546566768167692662722957924358908573096347694362541259 q 135655026516400219835182288872064651548166413404981613626584975073279765274581289299963783472100901444904090497633607380440126562041149560121122191822729803838692049608052805597752016733559257401832457990520641184379220785155858496800358974243933025297673311305372951490297748845447521569920828454777048335233 type rsa}
+
+test rsa-generate-csr-1.0 {csr} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test.key.des] r]
+ set privkey [read $fd]
+ close $fd ; unset fd
+
+ set password "ARoseIsARoseIsARose"
+
+ set privkey [::pki::pkcs::parse_key $privkey $password]
+} -body {
+ ::pki::pkcs::create_csr $privkey [list C US ST Florida L Tampa O Tcllib OU RSA CN TestCert] 1
+} -cleanup {
+ unset privkey password
+} -result {-----BEGIN CERTIFICATE REQUEST-----
+MIICpjCCAY4CAQAwYTELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0Zsb3JpZGExDjAM
+BgNVBAcTBVRhbXBhMQ8wDQYDVQQKEwZUY2xsaWIxDDAKBgNVBAsTA1JTQTERMA8G
+A1UEAxMIVGVzdENlcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj
+PBaKPeHx1oQb0nfMPeglBgyIpsv8CuIH7CAT8SGxoK1zIBQUhnNOZvel1/Kore68
+mY0ftiqmVbLO7aZy9X0Vy97hcEnhIekuPCauVg3kWjOspPbxmd3Bf7vfw7RGsvUt
+GGc3FZnU/X3GhzlKCq1osQCSZdLwirDm8ny4tbkTEDnGIB+4IJbSvifGQDqQNwtx
+cDigwRwAgDDdaKYBiW4bp+ZowJxorqc60w6xcl8DKhpPPaqRFN2kBcAxU/vztan1
+5NjNmmOPIkDcvIDEEkxKDoefTWaaF/MEGNtZZgIfIbVMos9+1soBzBb2ZH5NVtuK
+3RGA+4DxUk5kiI+eLANLAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEAff0WNTPX
+M6rdFGOXBxTsC7NUGcoquuM6QsceadMbBVRtRUUbaUsusXdxDWhdylqHM3xPNx0L
+4Ex0FeL9icD/8xvJmCXzBeNt3uvThTvIwHYqnOCSlhx9InUUx2l6U0rAwZ+CIuMi
+7lG2+Z5qVD035bAZ7LT/4s4fjKSL4cTZQOdCcoFtoptj9+L8EItwwYDzffJWdG8s
+OUtMBn+Zh45k2UtLKu38jBNtVpNFAEJLlr/Arj6Jj3yTmEFrocxkwK6IPbOHQTu3
+tyJ5CkDpUqkxYZ0D4wfr8tFEru0jQNl5bSDt9QQvg1Kj6+OC9aaRvxwnmc6REfky
+8sCv80Pn5dNCGg==
+-----END CERTIFICATE REQUEST-----
+}
+
+test rsa-parse-certv1-1.0 {parse_cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test-v1.crt] r]
+ set pubkey [read $fd]
+ close $fd ; unset fd
+} -body {
+ dictsort [::pki::x509::parse_cert $pubkey]
+} -cleanup {
+ unset pubkey
+} -result {cert 30820221020612481595aa03300d06092a864886f70d0101050500305f310b30090603550406130255533110300e06035504081307466c6f72696461310e300c0603550407130554616d7061310f300d060355040a130654636c6c6962310c300a060355040b1303525341310f300d06035504031306546573744341301e170d3130303830393037353230355a170d3230303830393037353230355a3061310b30090603550406130255533110300e06035504081307466c6f72696461310e300c0603550407130554616d7061310f300d060355040a130654636c6c6962310c300a060355040b13035253413111300f06035504031308546573744365727430820122300d06092a864886f70d01010105000382010f003082010a0282010100a33c168a3de1f1d6841bd277cc3de825060c88a6cbfc0ae207ec2013f121b1a0ad7320141486734e66f7a5d7f2a8adeebc998d1fb62aa655b2ceeda672f57d15cbdee17049e121e92e3c26ae560de45a33aca4f6f199ddc17fbbdfc3b446b2f52d1867371599d4fd7dc687394a0aad68b1009265d2f08ab0e6f27cb8b5b9131039c6201fb82096d2be27c6403a90370b717038a0c11c008030dd68a601896e1ba7e668c09c68aea73ad30eb1725f032a1a4f3daa9114dda405c03153fbf3b5a9f5e4d8cd9a638f2240dcbc80c4124c4a0e879f4d669a17f30418db5966021f21b54ca2cf7ed6ca01cc16f6647e4d56db8add1180fb80f1524e64888f9e2c034b0203010001 data_signature_algo sha1WithRSAEncryption e 65537 extensions {} issuer {C=US, ST=Florida, L=Tampa, O=Tcllib, OU=RSA, CN=TestCA} l 2048 n 20606474229739240365059039861892702888430699076971875439310562489263214483191006887246310401088091004060054335612563612411917991000786455919906798626524375611634511845705141177165689526939976649836053082770641226108840795034158866930145876068965913035873741839723777813944236146677082729876717709231945588228947001753667862858297850461440476551869914895980840772354759428144421149613554402682425181850295101375623815063198594704730226304723548382258248785251384891817576645955371924194287071569929361029204431671633821160423187871463713776212876252047207985933876036458343140423787003660018607222053134170461225878347 notAfter 1596959525 notBefore 1281340325 pubkey 3082010a0282010100a33c168a3de1f1d6841bd277cc3de825060c88a6cbfc0ae207ec2013f121b1a0ad7320141486734e66f7a5d7f2a8adeebc998d1fb62aa655b2ceeda672f57d15cbdee17049e121e92e3c26ae560de45a33aca4f6f199ddc17fbbdfc3b446b2f52d1867371599d4fd7dc687394a0aad68b1009265d2f08ab0e6f27cb8b5b9131039c6201fb82096d2be27c6403a90370b717038a0c11c008030dd68a601896e1ba7e668c09c68aea73ad30eb1725f032a1a4f3daa9114dda405c03153fbf3b5a9f5e4d8cd9a638f2240dcbc80c4124c4a0e879f4d669a17f30418db5966021f21b54ca2cf7ed6ca01cc16f6647e4d56db8add1180fb80f1524e64888f9e2c034b0203010001 pubkey_algo rsaEncryption serial_number 20100809075203 signature 7acbd0a587f1663e8a21b65f9161f08431f454d8b988f52f68d6d5bd9ad9675475819024f11c9c40df209fb05a297ffa72292c81e29bbc6c32e49bd968a49e77ae7be1055357e98602a031396f8b994455f936a85922800bddc379b76c2f63653c3b2bbaec4c81bedf9f9da78b6a80b253e8f98cde7289085837b22af454083ec6b772c374b40f1bf8d3c7a5e779b6fbbac885a5fa905a115475c7cf8a4d18deb7c91b96d5e47ba1291f4bc702371f1aede0b952648a6894c5e4c2496a7a8234788a7290d256e5cda99b772f43f019ddb1d758d06a1fd03865fdce762cf22850f996aa8cdc7d844115e3d1c533428d96a9fe774ba5ba07817607664b6bf3bf12 signature_algo sha1WithRSAEncryption subject {C=US, ST=Florida, L=Tampa, O=Tcllib, OU=RSA, CN=TestCert} type rsa version 1}
+
+test rsa-parse-certv3-1.0 {parse_cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test-v3.crt] r]
+ set pubkey [read $fd]
+ close $fd ; unset fd
+} -body {
+ dictsort [::pki::x509::parse_cert $pubkey]
+} -cleanup {
+ unset pubkey
+} -result {cert 30820226a003020102020612481595a9c0300d06092a864886f70d0101050500305f310b30090603550406130255533110300e06035504081307466c6f72696461310e300c0603550407130554616d7061310f300d060355040a130654636c6c6962310c300a060355040b1303525341310f300d06035504031306546573744341301e170d3130303830393037353133365a170d3230303830393037353133365a3061310b30090603550406130255533110300e06035504081307466c6f72696461310e300c0603550407130554616d7061310f300d060355040a130654636c6c6962310c300a060355040b13035253413111300f06035504031308546573744365727430820122300d06092a864886f70d01010105000382010f003082010a0282010100a33c168a3de1f1d6841bd277cc3de825060c88a6cbfc0ae207ec2013f121b1a0ad7320141486734e66f7a5d7f2a8adeebc998d1fb62aa655b2ceeda672f57d15cbdee17049e121e92e3c26ae560de45a33aca4f6f199ddc17fbbdfc3b446b2f52d1867371599d4fd7dc687394a0aad68b1009265d2f08ab0e6f27cb8b5b9131039c6201fb82096d2be27c6403a90370b717038a0c11c008030dd68a601896e1ba7e668c09c68aea73ad30eb1725f032a1a4f3daa9114dda405c03153fbf3b5a9f5e4d8cd9a638f2240dcbc80c4124c4a0e879f4d669a17f30418db5966021f21b54ca2cf7ed6ca01cc16f6647e4d56db8add1180fb80f1524e64888f9e2c034b0203010001 data_signature_algo sha1WithRSAEncryption e 65537 extensions {} issuer {C=US, ST=Florida, L=Tampa, O=Tcllib, OU=RSA, CN=TestCA} l 2048 n 20606474229739240365059039861892702888430699076971875439310562489263214483191006887246310401088091004060054335612563612411917991000786455919906798626524375611634511845705141177165689526939976649836053082770641226108840795034158866930145876068965913035873741839723777813944236146677082729876717709231945588228947001753667862858297850461440476551869914895980840772354759428144421149613554402682425181850295101375623815063198594704730226304723548382258248785251384891817576645955371924194287071569929361029204431671633821160423187871463713776212876252047207985933876036458343140423787003660018607222053134170461225878347 notAfter 1596959496 notBefore 1281340296 pubkey 3082010a0282010100a33c168a3de1f1d6841bd277cc3de825060c88a6cbfc0ae207ec2013f121b1a0ad7320141486734e66f7a5d7f2a8adeebc998d1fb62aa655b2ceeda672f57d15cbdee17049e121e92e3c26ae560de45a33aca4f6f199ddc17fbbdfc3b446b2f52d1867371599d4fd7dc687394a0aad68b1009265d2f08ab0e6f27cb8b5b9131039c6201fb82096d2be27c6403a90370b717038a0c11c008030dd68a601896e1ba7e668c09c68aea73ad30eb1725f032a1a4f3daa9114dda405c03153fbf3b5a9f5e4d8cd9a638f2240dcbc80c4124c4a0e879f4d669a17f30418db5966021f21b54ca2cf7ed6ca01cc16f6647e4d56db8add1180fb80f1524e64888f9e2c034b0203010001 pubkey_algo rsaEncryption serial_number 20100809075136 signature 30105b040c0d26a7899098e61b73844741510637bdb70ad81c10717bba7bb8dc478ee6cff562ad05f92756475e0ba85b3a9515e73e3904a48165996e48d03a7c435c98d7ca913edf07c7aca6c6dcbc4169bd953c0c073ad8521f7826a57e63740725b56b80ba68291a6c54ea6c74ce6a43879fbd0c0ed2c3b023901a8ef932535742f13b31033c893107caf9642c034952a6a3341edae5d714610d9c040bc044dfeec8bfd3125bf46f2ba15a68e164ca3bdf59f38b8117d1b4c43cfa1755f1169212aaa44ac8251b39b349bb931e8a419257b529e82ad8777fd65a62f45a171eb1b18f0196570e4a3b058e09ad0ec85e0a5968a330e59b32b22a2eae69e1a3a4 signature_algo sha1WithRSAEncryption subject {C=US, ST=Florida, L=Tampa, O=Tcllib, OU=RSA, CN=TestCert} type rsa version 3}
+
+test rsa-parse-cacert-1.0 {parse_cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory CA.crt] r]
+ set pubkey [read $fd]
+ close $fd ; unset fd
+} -body {
+ dictsort [::pki::x509::parse_cert $pubkey]
+} -cleanup {
+ unset pubkey
+} -result {cert 308202eea0030201020209009b29b6c41fdd35cf300d06092a864886f70d0101050500305f310b30090603550406130255533110300e06035504081307466c6f72696461310e300c0603550407130554616d7061310f300d060355040a130654636c6c6962310c300a060355040b1303525341310f300d06035504031306546573744341301e170d3130303830393037333733385a170d3335303831303037333733385a305f310b30090603550406130255533110300e06035504081307466c6f72696461310e300c0603550407130554616d7061310f300d060355040a130654636c6c6962310c300a060355040b1303525341310f300d0603550403130654657374434130820122300d06092a864886f70d01010105000382010f003082010a0282010100bf65944eeba4d516f9823cadd420a06e3413f14c32eda1f7fea95730ad20ba8e2c5952312210e02c4a848ccfba23fe052b9f35799e3174ae7a27ee4f69a56f96a9e93dad99de558b4f6e9b9c70dd2a6ed89bb3ed97e32c3167b0cc6e17753843097ff6a13276686d0e76004c2f22462717b0f1af3557936605f7899cf3037b85903e8d5449225f55f7320984f1e0864f0e14fe4dcc20aef03d0b593123b801ecbd23fb9be62799ac02b4c992e38ae6cfdbb81bf19209c0ad845c8da56591fa83c24c2fb4705b962bd15a1ccc47fbf70280522d79c5029f87e8315734e7b8a81d27f64ccb3716f8f75aa756251df7b041fc5e1c1b7ad1192bdb1d94154e212c810203010001a381c43081c1301d0603551d0e04160414852c1d09b76b0fa10e35d7aa4bcb48b2deb254d53081910603551d230481893081868014852c1d09b76b0fa10e35d7aa4bcb48b2deb254d5a163a461305f310b30090603550406130255533110300e06035504081307466c6f72696461310e300c0603550407130554616d7061310f300d060355040a130654636c6c6962310c300a060355040b1303525341310f300d060355040313065465737443418209009b29b6c41fdd35cf300c0603551d13040530030101ff data_signature_algo sha1WithRSAEncryption e 65537 extensions {id-ce-subjectKeyIdentifier {false 0414852c1d09b76b0fa10e35d7aa4bcb48b2deb254d5} id-ce-authorityKeyIdentifier {false 3081868014852c1d09b76b0fa10e35d7aa4bcb48b2deb254d5a163a461305f310b30090603550406130255533110300e06035504081307466c6f72696461310e300c0603550407130554616d7061310f300d060355040a130654636c6c6962310c300a060355040b1303525341310f300d060355040313065465737443418209009b29b6c41fdd35cf} id-ce-basicConstraints {false 1 -1}} issuer {C=US, ST=Florida, L=Tampa, O=Tcllib, OU=RSA, CN=TestCA} l 2048 n 24161606882664512184359265185750837725108535101957340182512690512950889280510175836653980019439405807509520118166835198926328713901946216313990307823213762851130122553610411084164096778558222486569750697608231998818342043083553499928765840766586577148563804434733745484416582976688503343857730022194918419820043366167270205314454789050632414594372175021330643138454461788318306110532766986666994575469007919602783172146356611527403971910037954057021421611608595373199910520988014831498419657809400692831589285260267441816651113464389720766612297754777522129247659432535168409289483999817599902067306541004223550663809 notAfter 2070344258 notBefore 1281339458 pubkey 3082010a0282010100bf65944eeba4d516f9823cadd420a06e3413f14c32eda1f7fea95730ad20ba8e2c5952312210e02c4a848ccfba23fe052b9f35799e3174ae7a27ee4f69a56f96a9e93dad99de558b4f6e9b9c70dd2a6ed89bb3ed97e32c3167b0cc6e17753843097ff6a13276686d0e76004c2f22462717b0f1af3557936605f7899cf3037b85903e8d5449225f55f7320984f1e0864f0e14fe4dcc20aef03d0b593123b801ecbd23fb9be62799ac02b4c992e38ae6cfdbb81bf19209c0ad845c8da56591fa83c24c2fb4705b962bd15a1ccc47fbf70280522d79c5029f87e8315734e7b8a81d27f64ccb3716f8f75aa756251df7b041fc5e1c1b7ad1192bdb1d94154e212c810203010001 pubkey_algo rsaEncryption serial_number 11180668503388403151 signature bed4868761d804d7e08bec1aaaf265d2d59c6f5390c9fda3c81ef4452ea58b6df7ff7f2be33276a5c21d5b8ac0c62be55e52e47010c704d34380723da2d8cfe57c76e9b204e752a79979c7935627eda80a7fc3a4bed4f5ecea2fd83311fe4054bc62812a3f579738b94a44e0aded6c61c3569175cfb393e8b3a992f5ae8b51692d0a0373d1ca0664f527e7d04877ec49c57ae53eaab100063d4c915ea87bf4b22b634656bc3ba7e71cb5dca982937ec6736f9f0b1da6404245c9a08ca23b027c9cf65d1b41b709fc6e90671543f5f3b7f8b27914e466bfe538d01aae9804fd81591f16ded334cc693929ba0896d8d049b5bd29856b6ada45fa0a777ba9e2f41d signature_algo sha1WithRSAEncryption subject {C=US, ST=Florida, L=Tampa, O=Tcllib, OU=RSA, CN=TestCA} type rsa version 3}
+
+test rsa-generate-cert-1.0 {cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test.key.des] r]
+ set privkey [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory CA.crt] r]
+ set cacert [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory CA.key] r]
+ set cakey [read $fd]
+ close $fd ; unset fd
+
+ set password "ARoseIsARoseIsARose"
+
+ set privkey [::pki::pkcs::parse_key $privkey $password]
+
+ array set ca_arr [::pki::pkcs::parse_key $cakey $password]
+ array set ca_arr [::pki::x509::parse_cert $cacert]
+ set ca [array get ca_arr]
+
+ set csr [::pki::pkcs::parse_csr [::pki::pkcs::create_csr $privkey [list C US ST Florida L Tampa O Tcllib OU RSA CN TestCert]]]
+} -body {
+ ::pki::x509::create_cert $csr $ca 20100809075203 1281340325 1596959525 0 "" 1
+} -cleanup {
+ unset privkey cakey cacert ca_arr ca password csr
+} -result {-----BEGIN CERTIFICATE-----
+MIIDOTCCAiECBhJIFZWqAzANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJVUzEQ
+MA4GA1UECBMHRmxvcmlkYTEOMAwGA1UEBxMFVGFtcGExDzANBgNVBAoTBlRjbGxp
+YjEMMAoGA1UECxMDUlNBMQ8wDQYDVQQDEwZUZXN0Q0EwHhcNMTAwODA5MDc1MjA1
+WhcNMjAwODA5MDc1MjA1WjBhMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHRmxvcmlk
+YTEOMAwGA1UEBxMFVGFtcGExDzANBgNVBAoTBlRjbGxpYjEMMAoGA1UECxMDUlNB
+MREwDwYDVQQDEwhUZXN0Q2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKM8Foo94fHWhBvSd8w96CUGDIimy/wK4gfsIBPxIbGgrXMgFBSGc05m96XX
+8qit7ryZjR+2KqZVss7tpnL1fRXL3uFwSeEh6S48Jq5WDeRaM6yk9vGZ3cF/u9/D
+tEay9S0YZzcVmdT9fcaHOUoKrWixAJJl0vCKsObyfLi1uRMQOcYgH7ggltK+J8ZA
+OpA3C3FwOKDBHACAMN1opgGJbhun5mjAnGiupzrTDrFyXwMqGk89qpEU3aQFwDFT
++/O1qfXk2M2aY48iQNy8gMQSTEoOh59NZpoX8wQY21lmAh8htUyiz37WygHMFvZk
+fk1W24rdEYD7gPFSTmSIj54sA0sCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAesvQ
+pYfxZj6KIbZfkWHwhDH0VNi5iPUvaNbVvZrZZ1R1gZAk8RycQN8gn7BaKX/6ciks
+geKbvGwy5JvZaKSed6574QVTV+mGAqAxOW+LmURV+TaoWSKAC93DebdsL2NlPDsr
+uuxMgb7fn52ni2qAslPo+YzecokIWDeyKvRUCD7Gt3LDdLQPG/jTx6Xnebb7usiF
+pfqQWhFUdcfPik0Y3rfJG5bV5HuhKR9LxwI3Hxrt4LlSZIpolMXkwklqeoI0eIpy
+kNJW5c2pm3cvQ/AZ3bHXWNBqH9A4Zf3OdizyKFD5lqqM3H2EQRXj0cUzQo2Wqf53
+S6W6B4F2B2ZLa/O/Eg==
+-----END CERTIFICATE-----
+}
+
+test rsa-generate-cacert-1.0 {cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test.key.des] r]
+ set privkey [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory CA.crt] r]
+ set cacert [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory CA.key] r]
+ set cakey [read $fd]
+ close $fd ; unset fd
+
+ set password "ARoseIsARoseIsARose"
+
+ set privkey [::pki::pkcs::parse_key $privkey $password]
+
+ array set ca_arr [::pki::pkcs::parse_key $cakey $password]
+ array set ca_arr [::pki::x509::parse_cert $cacert]
+ set ca [array get ca_arr]
+
+ set csr [::pki::pkcs::parse_csr [::pki::pkcs::create_csr $privkey [list C US ST Florida L Tampa O Tcllib OU RSA CN TestCert]]]
+} -body {
+ ::pki::x509::create_cert $csr $ca 20100809075203 1281340325 1596959525 1 "" 1
+} -cleanup {
+ unset privkey cakey cacert ca_arr ca password csr
+} -result {-----BEGIN CERTIFICATE-----
+MIIDUzCCAjugAwIBAgIGEkgVlaoDMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNVBAYT
+AlVTMRAwDgYDVQQIEwdGbG9yaWRhMQ4wDAYDVQQHEwVUYW1wYTEPMA0GA1UEChMG
+VGNsbGliMQwwCgYDVQQLEwNSU0ExDzANBgNVBAMTBlRlc3RDQTAeFw0xMDA4MDkw
+NzUyMDVaFw0yMDA4MDkwNzUyMDVaMGExCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdG
+bG9yaWRhMQ4wDAYDVQQHEwVUYW1wYTEPMA0GA1UEChMGVGNsbGliMQwwCgYDVQQL
+EwNSU0ExETAPBgNVBAMTCFRlc3RDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAozwWij3h8daEG9J3zD3oJQYMiKbL/AriB+wgE/EhsaCtcyAUFIZz
+Tmb3pdfyqK3uvJmNH7YqplWyzu2mcvV9Fcve4XBJ4SHpLjwmrlYN5FozrKT28Znd
+wX+738O0RrL1LRhnNxWZ1P19xoc5SgqtaLEAkmXS8Iqw5vJ8uLW5ExA5xiAfuCCW
+0r4nxkA6kDcLcXA4oMEcAIAw3WimAYluG6fmaMCcaK6nOtMOsXJfAyoaTz2qkRTd
+pAXAMVP787Wp9eTYzZpjjyJA3LyAxBJMSg6Hn01mmhfzBBjbWWYCHyG1TKLPftbK
+AcwW9mR+TVbbit0RgPuA8VJOZIiPniwDSwIDAQABoxMwETAPBgNVHRMBAf8EBTAD
+AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA5nJ5k5HpGnBmDTB/cWd6LP7ygQ/jg9SEc
+dVhSb6xy7h7O2txsndfH5fTyJilKRAfl/NGs5ZyV9q97OIP1aAhIRQiKUwSHu2+l
+kHHVNn8DFGHRKhA5YSreZKR++tjAmowk0XQbEU33MZVPGPFlrL37V84Xf04MmFdD
+kFtZO/soAzO8cPVizz3DNk7SNDCsWjmaTVH1yKmzBLJhrU86o4BEqbYbjbwdtelZ
+cgeGPntr9c/ngnUlPU90HNp2e65zHUyf/3hWps72tSx5dNKcaE9NX8xxK5WZde8i
+mn/PueMPCKdX1v9Nou51yReEa8D9h7D7klWwacrRoYh9Y4++g1by
+-----END CERTIFICATE-----
+}
+
+test rsa-verify-certv1-1.0 {verify_cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test-v1.crt] r]
+ set pubcert [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory CA.crt] r]
+ set cacert [read $fd]
+ close $fd ; unset fd
+
+ set pubcert [::pki::x509::parse_cert $pubcert]
+ set cacert [::pki::x509::parse_cert $cacert]
+} -body {
+ ::pki::x509::verify_cert $pubcert [list $cacert]
+} -cleanup {
+ unset pubcert cacert
+} -result {true}
+
+test rsa-verify-certv3-1.0 {verify_cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test-v3.crt] r]
+ set pubcert [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory CA.crt] r]
+ set cacert [read $fd]
+ close $fd ; unset fd
+
+ set pubcert [::pki::x509::parse_cert $pubcert]
+ set cacert [::pki::x509::parse_cert $cacert]
+} -body {
+ ::pki::x509::verify_cert $pubcert [list $cacert]
+} -cleanup {
+ unset pubcert cacert
+} -result {true}
+
+test rsa-verify-badcertv1-1.0 {verify_cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test-v1.crt] r]
+ set pubcert [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory test-v1.crt] r]
+ set cacert [read $fd]
+ close $fd ; unset fd
+
+ set pubcert [::pki::x509::parse_cert $pubcert]
+ set cacert [::pki::x509::parse_cert $cacert]
+} -body {
+ ::pki::x509::verify_cert $pubcert [list $cacert]
+} -cleanup {
+ unset pubcert cacert
+} -result {false}
+
+test rsa-verify-badcertv3-1.0 {verify_cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test-v3.crt] r]
+ set pubcert [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory test-v3.crt] r]
+ set cacert [read $fd]
+ close $fd ; unset fd
+
+ set pubcert [::pki::x509::parse_cert $pubcert]
+ set cacert [::pki::x509::parse_cert $cacert]
+} -body {
+ ::pki::x509::verify_cert $pubcert [list $cacert]
+} -cleanup {
+ unset pubcert cacert
+} -result {false}
+
+test rsa-verify-badcertv3-2.0 {verify_cert} -setup {
+ set fd [open [file join $::tcltest::testsDirectory test-v3.crt] r]
+ set pubcert [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory CA.crt] r]
+ set cacert [read $fd]
+ close $fd ; unset fd
+
+ set pubcert [::pki::x509::parse_cert $pubcert]
+ set cacert [::pki::x509::parse_cert $cacert]
+
+ # Remove all extensions from CA cert
+ array set cacert_arr $cacert
+ set cacert_arr(extensions) ""
+ set cacert [array get cacert_arr]
+} -body {
+ ::pki::x509::verify_cert $pubcert [list $cacert]
+} -cleanup {
+ unset pubcert cacert
+} -result {false}
+
+test rsa-crypt-roundtrip-1.0 {encrypt, decrypt} -setup {
+ set data "This is a test"
+ set password "ARoseIsARoseIsARose"
+
+ set fd [open [file join $::tcltest::testsDirectory test.key.des] r]
+ set privkey [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory test-v1.crt] r]
+ set pubkey [read $fd]
+ close $fd ; unset fd
+
+ array set key [::pki::pkcs::parse_key $privkey $password]
+ array set key [::pki::x509::parse_cert $pubkey]
+ set keylist [array get key]
+ unset password privkey pubkey key
+} -body {
+ set ciphertext [::pki::encrypt -binary -pub -- $data $keylist]
+ set plaintext [::pki::decrypt -binary -priv -- $ciphertext $keylist]
+} -cleanup {
+ unset -nocomplain data ciphertext plaintext
+} -result {This is a test}
+
+test rsa-sign-verify-1.0 {sign, verify} -setup {
+ set data "This is a test"
+ set password "ARoseIsARoseIsARose"
+
+ set fd [open [file join $::tcltest::testsDirectory test.key.des] r]
+ set privkey [read $fd]
+ close $fd ; unset fd
+
+ set fd [open [file join $::tcltest::testsDirectory test-v1.crt] r]
+ set pubkey [read $fd]
+ close $fd ; unset fd
+
+ array set key [::pki::pkcs::parse_key $privkey $password]
+ array set key [::pki::x509::parse_cert $pubkey]
+ set keylist [array get key]
+ unset password privkey pubkey key
+} -body {
+ set ciphertext [::pki::encrypt -binary -priv -- $data $keylist]
+ set plaintext [::pki::decrypt -binary -pub -- $ciphertext $keylist]
+} -cleanup {
+ unset -nocomplain data ciphertext plaintext
+} -result {This is a test}
+
+
+foreach keylen {256 512 1024 2048} {
+
+ # Just one key for the whole round and its tests. Its possible to
+ # generate one for each test, but then we will really spend way to
+ # much effort on the setup of each test.
+ set key [::pki::rsa::generate $keylen]
+
+ test rsa-crypt-roundtrip-2.0.$keylen "encrypt, decrypt pub/priv for keylen $keylen" -body {
+ set plain "Pub/priv test"
+ set cipher [::pki::encrypt -binary -pub -- $plain $key]
+ set uncipher [::pki::decrypt -binary -priv -- $cipher $key]
+
+ string equal $plain $uncipher
+ } -cleanup {
+ unset -nocomplain plain cipher uncipher
+ } -result 1
+
+ test rsa-crypt-roundtrip-2.1.$keylen "encrypt, decrypt priv/pub for keylen $keylen" -body {
+ set plain "Priv/pub test"
+ set cipher [::pki::encrypt -binary -priv -- $plain $key]
+ set uncipher [::pki::decrypt -binary -pub -- $cipher $key]
+
+ string equal $plain $uncipher
+ } -cleanup {
+ unset -nocomplain plain cipher uncipher
+ } -result 1
+
+ if {$keylen >= 512} {
+ foreach {i hash} {
+ 0 md5
+ 1 sha1
+ 2 sha256
+ } {
+ test rsa-sign-verify-2.$i.$keylen "sign, verify $hash for keylen $keylen" -body {
+ set plain "This message is so long, it will never fit into a key"
+ set signed [::pki::sign $plain $key $hash]
+ set verified [::pki::verify $signed $plain $key]
+ } -cleanup {
+ unset -nocomplain plain signed verified
+ } -result true
+ }
+ }
+
+ unset key
+}
+
+# -------------------------------------------------------------------------
+testsuiteCleanup
+
+# Local variables:
+# mode: tcl
+# indent-tabs-mode: nil
+# End:
diff --git a/tcllib/modules/pki/test-v1.crt b/tcllib/modules/pki/test-v1.crt
new file mode 100644
index 0000000..e39d88b
--- /dev/null
+++ b/tcllib/modules/pki/test-v1.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDOTCCAiECBhJIFZWqAzANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJVUzEQ
+MA4GA1UECBMHRmxvcmlkYTEOMAwGA1UEBxMFVGFtcGExDzANBgNVBAoTBlRjbGxp
+YjEMMAoGA1UECxMDUlNBMQ8wDQYDVQQDEwZUZXN0Q0EwHhcNMTAwODA5MDc1MjA1
+WhcNMjAwODA5MDc1MjA1WjBhMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHRmxvcmlk
+YTEOMAwGA1UEBxMFVGFtcGExDzANBgNVBAoTBlRjbGxpYjEMMAoGA1UECxMDUlNB
+MREwDwYDVQQDEwhUZXN0Q2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKM8Foo94fHWhBvSd8w96CUGDIimy/wK4gfsIBPxIbGgrXMgFBSGc05m96XX
+8qit7ryZjR+2KqZVss7tpnL1fRXL3uFwSeEh6S48Jq5WDeRaM6yk9vGZ3cF/u9/D
+tEay9S0YZzcVmdT9fcaHOUoKrWixAJJl0vCKsObyfLi1uRMQOcYgH7ggltK+J8ZA
+OpA3C3FwOKDBHACAMN1opgGJbhun5mjAnGiupzrTDrFyXwMqGk89qpEU3aQFwDFT
++/O1qfXk2M2aY48iQNy8gMQSTEoOh59NZpoX8wQY21lmAh8htUyiz37WygHMFvZk
+fk1W24rdEYD7gPFSTmSIj54sA0sCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAesvQ
+pYfxZj6KIbZfkWHwhDH0VNi5iPUvaNbVvZrZZ1R1gZAk8RycQN8gn7BaKX/6ciks
+geKbvGwy5JvZaKSed6574QVTV+mGAqAxOW+LmURV+TaoWSKAC93DebdsL2NlPDsr
+uuxMgb7fn52ni2qAslPo+YzecokIWDeyKvRUCD7Gt3LDdLQPG/jTx6Xnebb7usiF
+pfqQWhFUdcfPik0Y3rfJG5bV5HuhKR9LxwI3Hxrt4LlSZIpolMXkwklqeoI0eIpy
+kNJW5c2pm3cvQ/AZ3bHXWNBqH9A4Zf3OdizyKFD5lqqM3H2EQRXj0cUzQo2Wqf53
+S6W6B4F2B2ZLa/O/Eg==
+-----END CERTIFICATE-----
diff --git a/tcllib/modules/pki/test-v3.crt b/tcllib/modules/pki/test-v3.crt
new file mode 100644
index 0000000..f3ba676
--- /dev/null
+++ b/tcllib/modules/pki/test-v3.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPjCCAiagAwIBAgIGEkgVlanAMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNVBAYT
+AlVTMRAwDgYDVQQIEwdGbG9yaWRhMQ4wDAYDVQQHEwVUYW1wYTEPMA0GA1UEChMG
+VGNsbGliMQwwCgYDVQQLEwNSU0ExDzANBgNVBAMTBlRlc3RDQTAeFw0xMDA4MDkw
+NzUxMzZaFw0yMDA4MDkwNzUxMzZaMGExCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdG
+bG9yaWRhMQ4wDAYDVQQHEwVUYW1wYTEPMA0GA1UEChMGVGNsbGliMQwwCgYDVQQL
+EwNSU0ExETAPBgNVBAMTCFRlc3RDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAozwWij3h8daEG9J3zD3oJQYMiKbL/AriB+wgE/EhsaCtcyAUFIZz
+Tmb3pdfyqK3uvJmNH7YqplWyzu2mcvV9Fcve4XBJ4SHpLjwmrlYN5FozrKT28Znd
+wX+738O0RrL1LRhnNxWZ1P19xoc5SgqtaLEAkmXS8Iqw5vJ8uLW5ExA5xiAfuCCW
+0r4nxkA6kDcLcXA4oMEcAIAw3WimAYluG6fmaMCcaK6nOtMOsXJfAyoaTz2qkRTd
+pAXAMVP787Wp9eTYzZpjjyJA3LyAxBJMSg6Hn01mmhfzBBjbWWYCHyG1TKLPftbK
+AcwW9mR+TVbbit0RgPuA8VJOZIiPniwDSwIDAQABMA0GCSqGSIb3DQEBBQUAA4IB
+AQAwEFsEDA0mp4mQmOYbc4RHQVEGN723CtgcEHF7unu43EeO5s/1Yq0F+SdWR14L
+qFs6lRXnPjkEpIFlmW5I0Dp8Q1yY18qRPt8Hx6ymxty8QWm9lTwMBzrYUh94JqV+
+Y3QHJbVrgLpoKRpsVOpsdM5qQ4efvQwO0sOwI5AajvkyU1dC8TsxAzyJMQfK+WQs
+A0lSpqM0Htrl1xRhDZwEC8BE3+7Iv9MSW/RvK6FaaOFkyjvfWfOLgRfRtMQ8+hdV
+8RaSEqqkSsglGzmzSbuTHopBkle1Kegq2Hd/1lpi9FoXHrGxjwGWVw5KOwWOCa0O
+yF4KWWijMOWbMrIqLq5p4aOk
+-----END CERTIFICATE-----
diff --git a/tcllib/modules/pki/test.csr b/tcllib/modules/pki/test.csr
new file mode 100644
index 0000000..6e22f14
--- /dev/null
+++ b/tcllib/modules/pki/test.csr
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICpjCCAY4CAQAwYTELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0Zsb3JpZGExDjAM
+BgNVBAcTBVRhbXBhMQ8wDQYDVQQKEwZUY2xsaWIxDDAKBgNVBAsTA1JTQTERMA8G
+A1UEAxMIVGVzdENlcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj
+PBaKPeHx1oQb0nfMPeglBgyIpsv8CuIH7CAT8SGxoK1zIBQUhnNOZvel1/Kore68
+mY0ftiqmVbLO7aZy9X0Vy97hcEnhIekuPCauVg3kWjOspPbxmd3Bf7vfw7RGsvUt
+GGc3FZnU/X3GhzlKCq1osQCSZdLwirDm8ny4tbkTEDnGIB+4IJbSvifGQDqQNwtx
+cDigwRwAgDDdaKYBiW4bp+ZowJxorqc60w6xcl8DKhpPPaqRFN2kBcAxU/vztan1
+5NjNmmOPIkDcvIDEEkxKDoefTWaaF/MEGNtZZgIfIbVMos9+1soBzBb2ZH5NVtuK
+3RGA+4DxUk5kiI+eLANLAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEAff0WNTPX
+M6rdFGOXBxTsC7NUGcoquuM6QsceadMbBVRtRUUbaUsusXdxDWhdylqHM3xPNx0L
+4Ex0FeL9icD/8xvJmCXzBeNt3uvThTvIwHYqnOCSlhx9InUUx2l6U0rAwZ+CIuMi
+7lG2+Z5qVD035bAZ7LT/4s4fjKSL4cTZQOdCcoFtoptj9+L8EItwwYDzffJWdG8s
+OUtMBn+Zh45k2UtLKu38jBNtVpNFAEJLlr/Arj6Jj3yTmEFrocxkwK6IPbOHQTu3
+tyJ5CkDpUqkxYZ0D4wfr8tFEru0jQNl5bSDt9QQvg1Kj6+OC9aaRvxwnmc6REfky
+8sCv80Pn5dNCGg==
+-----END CERTIFICATE REQUEST-----
diff --git a/tcllib/modules/pki/test.key.aes b/tcllib/modules/pki/test.key.aes
new file mode 100644
index 0000000..1050718
--- /dev/null
+++ b/tcllib/modules/pki/test.key.aes
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,A63D2250AEC17BBA948C0711A7477A88
+
+DcIqtKV3RzcfzOk0K4WwRU4dK4373bsoek/trIvHYbs6sQlU0Ydk8ddDYyWlt0vR
+MdEhEi4kTbtWhczSa9kgjQo59fZ8br8iHaSkUDPqKZUCy5qAlLsfRnCiA4JnZaDy
+BD8ROFNTdb1MTnGPqrY2+9nv/BAPNlqr+95zcAim7vBJxO0idmUKCzVFAEHn+akD
+UPX/Rkku7tIprsA5ePsQ9Z7B8qJ3LqhxKauZE+qV3hcX8/E7fndpYxQ5I3/6W3Wj
+hkfH4cTwNC9H0jcaoDDlh0O1xMfq2YQxyq20Cfse8NO11Is8UMbXWBZ90uhk6lnk
+xsVzOB2A7WbiiNEfbLBYgsUqy9ffGKhJWXoUUzl6Ze9lolD0mJjinKwAMEszwWNg
+qSwJQi+omgv0+0yP2gnMkqwsBzPFrEt/Iw0/nMKNNn8RXK2C3itD61tX/T/4p5Gj
+VvISFp+KjlgASBMaSNCE/FsfAsz7OxrkvrOJcEAZ1sWjfBUXSq20ESFGk74S5AUj
+xjrSeurl7pyLjfEgx2YR6ybF3SSnVst3wQ9BPLnG8439VKONixSukDvk/XJuY4UO
+dLuSm7aTcLy43jLosxLKHJ6jfwJEOhKMFGXkyb1h7UZnU3rydvgJF7Hw+HGsnuH6
+0cd4gKyMoTclxRn8oULlQafuOYwTO0xOV5cOjtKdnaTfasd4xrYuWGuW93INvyTh
+6lYhVgcjo+F7TDm1bqgAH354Cb3FqERx4VmeJm20YfpPilDuM1ygkh5tObM2ffqr
+vJ9T66RsjhqpMv8trjj4F8oZK614D1MVKv7/2A02GEx8eoKiv1IsIoLe4Ihe5Ido
+oswOXxdtkJj4kd/feKPOFnCajOdWGR88IMUldMIsTetxAlaNrZkTz0OAKZ0pTRgj
+BsiXVwT6OX3GF7cL41Q6FXbLZGrLnGGnOjRFpBK+0JeQ8hQFortqiLBXUaNK//gH
+pzMwApPRBfX5VxU4bVxai5WoBz63MAdOJaHKTBfNF8ebMsUC6Z+ue3H/rQmTjscz
+zxelmeKHGbFCUyW/bmGh3MDkriXUNVgqFQTqDQIbsOVzwHAQ3m6N7zREcep+/uCk
+2VlJY2HRuAsf8U+sATGveYkCcq+1nr1ZPcbeLHom9NL8cDXvWRwj84Y3Fv0BPuod
+PRBLZWMe8bOICV7cxsyRPfNf9ov+QyLc7LeMdmy9gYZABnRUZRKAOcNGHDIVULeT
+L9+QiyzeW6AK5BgkNrTpHC6fWo6jqfDjZJrhJvQbzvykJwiMJvahuRYlejLVx1HA
+uFANAhgM9APAjnzpyvbS5EcB+Q+9TrF8d+MufR3ijvMT2evIsUthwprS7CkJLCXl
+WeGw/YI58xWwk1l3LZ8bT04Jy1cS+8CVnmZXtCq0yRVsIVbsMlN4WW1b6AxMTGlC
+VB8ne9lw3zSqraDu/8he0TQi0JzYs86CwjDkMTIaIrXn+6W7tlcSUpnrQeAfgx+d
+w8qklOraCLAcXWhFw2kbc7pzvKvVzRr2R0GtZaGYInEFUP9e7fJT4R9K1cBi9DGm
+gmtJUWHnxI9QMHDslST9NGkrulBisoJ0kuVNUmIX0x2zii7w+Kk5a87lUB06pAmV
+-----END RSA PRIVATE KEY-----
diff --git a/tcllib/modules/pki/test.key.des b/tcllib/modules/pki/test.key.des
new file mode 100644
index 0000000..904521c
--- /dev/null
+++ b/tcllib/modules/pki/test.key.des
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,2AD65AF586C2E7FF
+
+0pAOYQCqE9NPUJSrK4gu5P9H1yydKcLvuhIIsh5S1RtlEqzz1eythmGRTD/S6QnS
+c2zmmkaHndGKB3S55BTw0UnGP3XROdl1VtCqlf2mvp7MY3E9xF+ZKgw6L2yUeFW5
+ZlKEyglWIB5J4yKgX8yAe6nqdhNEXG6JolCQH45x5uRiMTtxuGgivmvFfTV3TjaO
+GXUMcbNeslJ4PZwxmUFR5YD4QSxwHe4Ur3dz2CMW6ea+TGbWNyqoEEEz81BKeAFF
+cAxAtnyWpo9NmRc2TmGRstXrfzBW/lgNrkLgqoNHkfXT5p+kkG4mIInIKWpeKsJB
+wByDFIB5+Sr3VkZJYOMJQcYkVWc2Y+o1l8/cqk9cuLYMO81OQw3QEwy0WSkXpVfP
+CIbIQOyp3Hi02drlcIjv+d6OYWDYwMB1DiPQSYGO16jFORtC0Mrmt5XiUE4kWTap
+3K4MVphvkJaSjUCzbLVzChhXWICwwGPG/x8F1Fkre/xevwdFq6qZB2HfZVqjtWrn
+E6nVWjWcaavIxJEJPuCfhU+gKsV/nyxMFqBpck8YRxUE1yYdG9sSHaiL/z8vom1r
+09VorVpa1v0AI+05XgJPZHPmjbadNn7oA3knqbICv6Jh0URXlW+TtEWGde+YDz9x
+HC6TexiZU2tQcZMtcR999yI9UYXekDS6jXLjfpEZuM971IbUefjEGnBhBpoxW4Zw
+MDzmWDJxyT3bz4ZURANm1nE3i/9R6sDwv7egu62wY6wo8KmEA126eX4CE1TAeRyF
+7DZCsmMWHQJej8N5kmTpREtBKZhCrmRYOE6vTELZWfsLN00yTpUFzzrM5gfgYDt5
+Ry8t9OQrr7T3TZDu20DMrNRL/3/t3Ap133zgM6c/jD955PyfFA3ph/m4wBiVP1cf
+LZDh/Qa+z4xvgSML8NHGhLXJgmvdPGcbKGYRj+LNVanSr3yrVJynIcr/3ckiMgEl
+lS5hr3VJVNbX/DgNsqkVoQPudl5p/aiMx/qPY3brxsxyLCk6oF0Z7L4ksdSGIaMt
+cMH0eNTuqthWK2D2JWrhPWeY0EcSOx4NtNEqUsCQFZhg+mO1xUKJdnc9x1eaHoZe
+XR9lRCjBVXD6iBNy2bscJONoz5MhuHjBqYjSF5jQL/Xfe9qgmLrqdiqqIw4R04c3
+g09JlDWRS78a68FXhc5Yo6AmqJD4UNMKH3npFUk2Vk+SD6HwUmpM+hFDM+L2tFi+
+7QNyKmWwTfFgyLQOG45Kqhl0i6Ia857UedZX7ezrJ2AoiCUn8oT2NmfJif8/jtL4
+wVLdkav7L2H64Xd9ZcWtDIWuLT9L1Fuy9LFFByscRSy1jvSj0ILrNB2Og1YWfWEE
+t/OzTlm9jpzkiKQxFGCizzt1ZnnQfrB3BN28vPThw9Dh0DRCexYXWpwY2KhHTQFo
+ghSBFh0mAZfCpHdiHM27XGvPUAo6WNP8LaA8tvvWArcU3wqaEjpbsaPw2kY0qMY6
+Kds/t/Hi3fC75IN7XWfwxr1c6hxeiBgrR48i+nVPXqlWFoT5svsqquwhxukxifoF
+fBuX6STP+RwCa5sDqfICPeLTj1jg0zBjUVB7f0w6FiOLfxQIxnNJ+g==
+-----END RSA PRIVATE KEY-----