diff options
author | Endi Sukma Dewata <edewata@redhat.com> | 2012-03-24 02:27:47 -0500 |
---|---|---|
committer | Endi Sukma Dewata <edewata@redhat.com> | 2012-03-26 11:43:54 -0500 |
commit | 621d9e5c413e561293d7484b93882d985b3fe15f (patch) | |
tree | 638f3d75761c121d9a8fb50b52a12a6686c5ac5c /base/java-tools | |
parent | 40d3643b8d91886bf210aa27f711731c81a11e49 (diff) | |
download | pki-621d9e5c413e561293d7484b93882d985b3fe15f.tar.gz pki-621d9e5c413e561293d7484b93882d985b3fe15f.tar.xz pki-621d9e5c413e561293d7484b93882d985b3fe15f.zip |
Removed unnecessary pki folder.
Previously the source code was located inside a pki folder.
This folder was created during svn migration and is no longer
needed. This folder has now been removed and the contents have
been moved up one level.
Ticket #131
Diffstat (limited to 'base/java-tools')
31 files changed, 13092 insertions, 0 deletions
diff --git a/base/java-tools/CMakeLists.txt b/base/java-tools/CMakeLists.txt new file mode 100644 index 000000000..427ded555 --- /dev/null +++ b/base/java-tools/CMakeLists.txt @@ -0,0 +1,4 @@ +project(java-tools Java) + +add_subdirectory(src) +add_subdirectory(templates) diff --git a/base/java-tools/LICENSE b/base/java-tools/LICENSE new file mode 100644 index 000000000..e281f4362 --- /dev/null +++ b/base/java-tools/LICENSE @@ -0,0 +1,291 @@ +This Program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This Program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with this Program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/base/java-tools/doc/README b/base/java-tools/doc/README new file mode 100644 index 000000000..fa0af7d4b --- /dev/null +++ b/base/java-tools/doc/README @@ -0,0 +1,161 @@ + Certificate System + Java Command Line Utilities + + +Command Line Utility Purpose +============================================================================== +AtoB <input file> <output file> A command line utility utilized + to convert an ASCII BASE 64 + blob into a BINARY BASE 64 blob. + +AuditVerify A command line utility utilized + to verify signatures in signed + audit log files. + +BtoA <input file> <output file> A command line utility utilized + to convert a BINARY BASE 64 + blob into an ASCII BASE 64 blob. + +CMCEnroll A command line utility used to + sign a certificate enrollment + request with an agent's + certificate. + +CMCRequest A command line utility used to + construct a Certificate + Management Messages over + CMS (CMC) request. + +CMCResponse A command line utility used to + parse a CMC response. + +CMCRevoke A command line utility used to + sign a revocation request with + an agent's certificate. + +CRMFPopClient A command line utility used to + generate CRMF requests with + proof of possession (POP). + +DRMTool -drmtool_config_file A command line utility used to + <path + drmtool config file> change the storage key used + -source_ldif_file to wrap the symmetric key + <path + source ldif file> which is used to encrypt the + -target_ldif_file user's private key. + <path + target ldif file> Optionally, this utility + -log_file may also be used to re-index IDs + <path + log file > associated with the various + [-source_pki_security_database_path records which may be useful + <path to PKI source databases> for DRM consolidation. + -source_storage_token_name + '<source token>' + -source_storage_certificate_nickname + '<source nickname>' + -target_storage_certificate_file + <path to target certificate file> + [-source_pki_security_database_pwdfile + <path + pwdfile>]] + [-append_id_offset + <numeric offset> || + -remove_id_offset + <numeric offset>] + [-source_drm_naming_context + <source DRM naming context>] + [-target_drm_naming_context + <target DRM naming context>] + [-process_requests_and_key_records_only] + +ExtJoiner <ext_file0> . . . <ext_file9> A command line utility utilized + to join a sequence of extensions + together so that the final + output can be used in the + configuration wizard for + specifying extra extensions + in default certificates + (i. e. - CA certificate, + SSL certificate). + +GenExtKeyUsage [true|false] A command line utility utilized + <OID_1> . . . <OID_9> to generate a DER-encoded + Extended Key Usage extension. + The first parameter is the + criticality of the extension, + true or false. The OIDs to be + included in the extension are + passed as command-line + arguments. The OIDs are + described in RFC 2459. For + example, the OID for code + signing is 1.3.6.1.5.5.7.3.3. + +GenIssuerAltNameExt <general_type0> A command line utility utilized + <general_name0> to generate an issuer + . . . alternative name extension in + <general_type3> base-64 encoding. The encoding + <general_name3> output can be used with the + configuration wizard, where: + <general_type#> can be one + of the following strings: + DNSName + EDIPartyName + IPAddressName + URIName + RFC822Name + OIDName + X500Name + <general_name#> is a string + +GenSubjectAltNameExt <general_type0> A command line utility utilized + <general_name0> to generate a subject + . . . alternative name extension in + <general_type3> base-64 encoding. The encoding + <general_name3> output can be used with the + configuration wizard, where: + <general_type#> can be one + of the following strings: + DNSName + EDIPartyName + IPAddressName + URIName + RFC822Name + OIDName + X500Name + <general_name#> is a string + +HttpClient A command line utility used + to communicate with any + http/https server. + +OCSPClient A command line utility that + verifies certificate status by + submitting Online Certificate + Status Protocol (OCSP) requests + to an instance of an OCSP + subsystem. + +PKCS10Client A command line utility that + generates a Public Key + Cryptography Standards + (PKCS) #10 enrollment + request. + +PKCS12Export A command line utility utilized + to create PKCS12 file. + +PrettyPrintCert <input file> [output file] A command line utility utilized + to print the contents of a + certificate stored as an ASCII + BASE 64 encoded blob in a + user-friendly manner. + +PrettyPrintCrl <input file> [output file] A command line utility utilized + to print the contents of a + Certificate Revocation List + (CRL) stored as an ASCII + BASE 64 encoded blob in a + user-friendly manner. + +TokenInfo A command line utility utilized + to display all external HSMs + visible to JSS. + diff --git a/base/java-tools/src/CMakeLists.txt b/base/java-tools/src/CMakeLists.txt new file mode 100644 index 000000000..0411a54c7 --- /dev/null +++ b/base/java-tools/src/CMakeLists.txt @@ -0,0 +1,87 @@ +project(pki-tools_java Java) + +find_file(JSS_JAR + NAMES + jss4.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +find_file(COMMONS_CODEC_JAR + NAMES + commons-codec.jar + PATHS + /usr/share/java +) + +find_file(XALAN_JAR + NAMES + xalan-j2.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +find_file(XERCES_JAR + NAMES + xerces-j2.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +set(pki-tools_java_SRCS + com/netscape/cmstools/PrettyPrintCrl.java + com/netscape/cmstools/BtoA.java + com/netscape/cmstools/PasswordCache.java + com/netscape/cmstools/OCSPClient.java + com/netscape/cmstools/PKCS12Export.java + com/netscape/cmstools/TestCRLSigning.java + com/netscape/cmstools/CRMFPopClient.java + com/netscape/cmstools/AuditVerify.java + com/netscape/cmstools/PrettyPrintCert.java + com/netscape/cmstools/HttpClient.java + com/netscape/cmstools/GenExtKeyUsage.java + com/netscape/cmstools/CMCRevoke.java + com/netscape/cmstools/TokenInfo.java + com/netscape/cmstools/CMCEnroll.java + com/netscape/cmstools/ExtJoiner.java + com/netscape/cmstools/CMCRequest.java + com/netscape/cmstools/AtoB.java + com/netscape/cmstools/GenIssuerAltNameExt.java + com/netscape/cmstools/GenSubjectAltNameExt.java + com/netscape/cmstools/CMCResponse.java + com/netscape/cmstools/PKCS10Client.java + com/netscape/cmstools/DRMTool.java +) + +set(CMAKE_JAVA_INCLUDE_PATH + ${PKI_NSUTIL_JAR} ${PKI_CMSUTIL_JAR} + ${XALAN_JAR} ${XERCES_JAR} + ${JSS_JAR} ${COMMONS_CODEC_JAR}) + +set(CMAKE_JAVA_TARGET_VERSION ${APPLICATION_VERSION}) + +# build pki-tools +add_jar(pki-tools ${pki-tools_java_SRCS}) +add_dependencies(pki-tools pki-nsutil pki-cmsutil) +install( + FILES + com/netscape/cmstools/DRMTool.cfg + DESTINATION + ${SHARE_INSTALL_PREFIX}/pki/java-tools/ +) +install_jar(pki-tools ${JAVA_JAR_INSTALL_DIR}/pki) +set(PKI_TOOLS_JAR ${pki-tools_JAR_FILE} CACHE INTERNAL "pki-tools jar file") + +create_javadoc(pki-java-tools-${APPLICATION_VERSION} + FILES ${pki-tools_java_SRCS} + CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH} + WINDOWTITLE "pki-java-tools" + DOCTITLE "<h1>pki-java-tools</h1>" + AUTHOR TRUE + USE TRUE + VERSION TRUE +) +add_dependencies(pki-java-tools-${APPLICATION_VERSION}_javadoc pki-tools) diff --git a/base/java-tools/src/com/netscape/cmstools/AtoB.java b/base/java-tools/src/com/netscape/cmstools/AtoB.java new file mode 100644 index 000000000..48301e492 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/AtoB.java @@ -0,0 +1,146 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +import com.netscape.cmsutil.util.Utils; + +/** + * The AtoB class is a utility program designed to "translate" an ASCII + * BASE 64 encoded blob into a BINARY BASE 64 encoded blob. It assumes + * that the name of a data file is passed to the program via the command line, + * and that the contents contain a blob encoded in an ASCII BASE 64 + * format. Note that the data file may contain an optional "-----BEGIN" header + * and/or an optional "-----END" trailer. + * + * <P> + * The program may be invoked as follows: + * + * <PRE> + * + * AtoB <input filename> <output filename> + * + * NOTE: <input filename> must contain an ASCII + * BASE 64 encoded blob + * + * <output filename> contains a BINARY + * BASE 64 encoded blob + * </PRE> + * + * @version $Revision$, $Date$ + */ +public class AtoB { + // Define constants + public static final int ARGC = 2; + public static final String HEADER = "-----BEGIN"; + public static final String TRAILER = "-----END"; + + public static void main(String argv[]) { + + BufferedReader inputBlob = null; + String asciiBASE64BlobChunk = new String(); + String asciiBASE64Blob = new String(); + byte binaryBASE64Blob[] = null; + FileOutputStream outputBlob = null; + + // (1) Check that two arguments were submitted to the program + if (argv.length != ARGC) { + System.out.println("Usage: AtoB " + + "<input filename> " + + "<output filename>"); + return; + } + + // (2) Create a DataInputStream() object to the BASE 64 + // encoded blob contained within the file + // specified on the command line + try { + inputBlob = new BufferedReader(new InputStreamReader( + new BufferedInputStream( + new FileInputStream( + argv[0])))); + } catch (FileNotFoundException e) { + System.out.println("AtoB(): can''t find file " + + argv[0] + ":\n" + e); + return; + } + + // (3) Read the entire contents of the specified BASE 64 encoded + // blob into a String() object throwing away any + // headers beginning with HEADER and any trailers beginning + // with TRAILER + try { + while ((asciiBASE64BlobChunk = inputBlob.readLine()) != null) { + if (!(asciiBASE64BlobChunk.startsWith(HEADER)) && + !(asciiBASE64BlobChunk.startsWith(TRAILER))) { + asciiBASE64Blob += asciiBASE64BlobChunk.trim(); + } + } + } catch (IOException e) { + System.out.println("AtoB(): Unexpected BASE64 " + + "encoded error encountered in readLine():\n" + + e); + } + + // (4) Close the DataInputStream() object + try { + inputBlob.close(); + } catch (IOException e) { + System.out.println("AtoB(): Unexpected BASE64 " + + "encoded error encountered in close():\n" + e); + } + + // (5) Decode the ASCII BASE 64 blob enclosed in the + // String() object into a BINARY BASE 64 byte[] object + + binaryBASE64Blob = Utils.base64decode(asciiBASE64Blob); + + // (6) Finally, print the actual AtoB blob to the + // specified output file + try { + outputBlob = new FileOutputStream(argv[1]); + } catch (IOException e) { + System.out.println("AtoB(): unable to open file " + + argv[1] + " for writing:\n" + e); + return; + } + + try { + outputBlob.write(binaryBASE64Blob); + } catch (IOException e) { + System.out.println("AtoB(): I/O error " + + "encountered during write():\n" + + e); + } + + try { + outputBlob.close(); + } catch (IOException e) { + System.out.println("AtoB(): Unexpected error " + + "encountered while attempting to close() " + + argv[1] + ":\n" + e); + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/AuditVerify.java b/base/java-tools/src/com/netscape/cmstools/AuditVerify.java new file mode 100644 index 000000000..fb23e89fd --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/AuditVerify.java @@ -0,0 +1,334 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FilenameFilter; +import java.io.IOException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.security.x509.X509CertImpl; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.X509Certificate; + +import com.netscape.cmsutil.util.Utils; + +/** + * Tool for verifying signed audit logs + * + * @version $Revision$, $Date$ + */ +public class AuditVerify { + + private static void usage() { + System.out + .println("Usage: AuditVerify -d <dbdir> -n <signing certificate nickname> -a <log list file> [-P <cert/key db prefix>] [-v]"); + System.exit(1); + } + + public static final String CRYPTO_PROVIDER = "Mozilla-JSS"; + + public static byte[] base64decode(String input) throws Exception { + return Utils.base64decode(input); + } + + // We always sign 0x0a as the line separator, regardless of what + // line separator characters are used in the log file. This helps + // signature verification be platform-independent. + private static final byte LINE_SEP_BYTE = 0x0a; + + private static void output(int linenum, String mesg) throws IOException { + System.out.println("Line " + linenum + ": " + mesg); + } + + private static void writeFile(String curfileName) { + System.out.println("======\nFile: " + curfileName + "\n======"); + } + + private static void writeSigStatus(int linenum, String sigStartFile, + int sigStartLine, String sigStopFile, int sigStopLine, String mesg) + throws IOException { + output(linenum, mesg + ": signature of " + sigStartFile + ":" + + sigStartLine + " to " + sigStopFile + ":" + sigStopLine); + } + + private static class PrefixFilter implements FilenameFilter { + private String prefix; + + public PrefixFilter(String prefix) { + this.prefix = prefix; + } + + public boolean accept(File dir, String name) { + // look for <prefix>cert* in this directory + return (name.indexOf(prefix + "cert") != -1); + } + } + + public static boolean validPrefix(String configDir, String prefix) + throws IOException { + File dir = new File(configDir); + if (!dir.isDirectory()) { + System.out.println("ERROR: \"" + dir + "\" is not a directory"); + usage(); + } + + String matchingFiles[] = dir.list(new PrefixFilter(prefix)); + + // prefix may be valid if at least one file matched the pattern + return (matchingFiles.length > 0); + } + + public static boolean isSigningCert(X509CertImpl cert) { + boolean[] keyUsage = null; + + try { + keyUsage = cert.getKeyUsage(); + } catch (Exception e) { + e.printStackTrace(); + } + return (keyUsage == null) ? false : keyUsage[0]; + } + + public static void main(String args[]) { + try { + + String dbdir = null; + String logListFile = null; + String signerNick = null; + String prefix = null; + boolean verbose = false; + + for (int i = 0; i < args.length; ++i) { + if (args[i].equals("-d")) { + if (++i >= args.length) + usage(); + dbdir = args[i]; + } else if (args[i].equals("-a")) { + if (++i >= args.length) + usage(); + logListFile = args[i]; + } else if (args[i].equals("-n")) { + if (++i >= args.length) + usage(); + signerNick = args[i]; + } else if (args[i].equals("-P")) { + if (++i >= args.length) + usage(); + prefix = args[i]; + } else if (args[i].equals("-v")) { + verbose = true; + } else { + System.out.println("Unrecognized argument(" + i + "): " + + args[i]); + usage(); + } + } + if (dbdir == null || logListFile == null || signerNick == null) { + System.out.println("Argument omitted"); + usage(); + } + + // get list of log files + Vector<String> logFiles = new Vector<String>(); + BufferedReader r = new BufferedReader(new FileReader(logListFile)); + String listLine; + while ((listLine = r.readLine()) != null) { + StringTokenizer tok = new StringTokenizer(listLine, ","); + while (tok.hasMoreElements()) { + logFiles.addElement(((String) tok.nextElement()).trim()); + } + } + if (logFiles.size() == 0) { + System.out.println("Error: no log files listed in " + logListFile); + System.exit(1); + } + + // initialize crypto stuff + if (prefix == null) { + if (!validPrefix(dbdir, "")) { + System.out.println("ERROR: \"" + dbdir + + "\" does not contain any security databases"); + usage(); + } + CryptoManager.initialize(dbdir); + } else { + if (!validPrefix(dbdir, prefix)) { + System.out.println("ERROR: \"" + prefix + + "\" is not a valid prefix"); + usage(); + } + CryptoManager.initialize( + new CryptoManager.InitializationValues(dbdir, prefix, prefix, + "secmod.db") + ); + } + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate signerCert = cm.findCertByNickname(signerNick); + + X509CertImpl cert_i = null; + if (signerCert != null) { + byte[] signerCert_b = signerCert.getEncoded(); + cert_i = new X509CertImpl(signerCert_b); + } else { + System.out.println("ERROR: signing certificate not found"); + System.exit(1); + } + + // verify signer's certificate + // not checking validity because we want to allow verifying old logs + // + if (!isSigningCert(cert_i)) { + System.out.println("info: signing certificate is not a signing certificate"); + System.exit(1); + } + + PublicKey pubk = signerCert.getPublicKey(); + String sigAlgorithm = null; + if (pubk instanceof RSAPublicKey) { + sigAlgorithm = "SHA-256/RSA"; + } else if (pubk instanceof DSAPublicKey) { + sigAlgorithm = "SHA-256/DSA"; + } else { + System.out.println("Error: unknown key type: " + + pubk.getAlgorithm()); + System.exit(1); + } + Signature sig = Signature.getInstance(sigAlgorithm, CRYPTO_PROVIDER); + sig.initVerify(pubk); + + int goodSigCount = 0; + int badSigCount = 0; + + int lastFileWritten = -1; + + int sigStartLine = 1; + int sigStopLine = 1; + String sigStartFile = (String) logFiles.elementAt(0); + String sigStopFile = null; + int signedLines = 1; + + for (int curfile = 0; curfile < logFiles.size(); ++curfile) { + String curfileName = (String) logFiles.elementAt(curfile); + BufferedReader br = new BufferedReader(new FileReader(curfileName)); + + if (verbose) { + writeFile(curfileName); + lastFileWritten = curfile; + } + + String curLine; + int linenum = 0; + while ((curLine = br.readLine()) != null) { + ++linenum; + if (curLine.indexOf("AUDIT_LOG_SIGNING") != -1) { + if (curfile == 0 && linenum == 1) { + // Ignore the first signature of the first file, + // since it signs data we don't have access to. + if (verbose) { + output(linenum, + "Ignoring first signature of log series"); + } + } else { + int sigStart = curLine.indexOf("sig: ") + 5; + if (sigStart < 5) { + output(linenum, "INVALID SIGNATURE"); + ++badSigCount; + } else { + byte[] logSig = + base64decode(curLine.substring(sigStart)); + + // verify the signature + if (sig.verify(logSig)) { + // signature verifies correctly + if (verbose) { + writeSigStatus(linenum, sigStartFile, + sigStartLine, sigStopFile, sigStopLine, + "verification succeeded"); + } + ++goodSigCount; + } else { + if (lastFileWritten < curfile) { + writeFile(curfileName); + lastFileWritten = curfile; + } + writeSigStatus(linenum, sigStartFile, + sigStartLine, sigStopFile, sigStopLine, + "VERIFICATION FAILED"); + ++badSigCount; + } + } + sig.initVerify(pubk); + signedLines = 0; + sigStartLine = linenum; + sigStartFile = curfileName; + } + } + + byte[] lineBytes = curLine.getBytes("UTF-8"); + sig.update(lineBytes); + sig.update(LINE_SEP_BYTE); + ++signedLines; + sigStopLine = linenum; + sigStopFile = curfileName; + } + + } + + // Make sure there were no unsigned log entries at the end. + // The first signed line is the previous signature, but anything + // more than that is data. + if (signedLines > 1) { + System.out.println( + "ERROR: log entries after " + sigStartFile + + ":" + sigStartLine + " are UNSIGNED"); + badSigCount++; + } + + System.out.println("\nVerification process complete."); + System.out.println("Valid signatures: " + goodSigCount); + System.out.println("Invalid signatures: " + badSigCount); + + if (badSigCount > 0) { + System.exit(2); + } else { + System.exit(0); + } + + } catch (FileNotFoundException fnfe) { + System.out.println(fnfe); + } catch (ObjectNotFoundException onfe) { + System.out.println("ERROR: certificate not found"); + } catch (Exception e) { + e.printStackTrace(); + } + + System.out.println("Verification process FAILED."); + System.exit(1); + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/BtoA.java b/base/java-tools/src/com/netscape/cmstools/BtoA.java new file mode 100644 index 000000000..4c2e5c22a --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/BtoA.java @@ -0,0 +1,119 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import com.netscape.cmsutil.util.Utils; + +/** + * The BtoA class is a utility program designed to "translate" a BINARY + * BASE 64 encoded blob into an ASCII BASE 64 encoded blob. It assumes + * that the name of a data file is passed to the program via the command line, + * and that the contents contain a blob encoded in a BINARY BASE 64 + * format. + * + * <P> + * The program may be invoked as follows: + * + * <PRE> + * + * BtoA <input filename> <output filename> + * + * NOTE: <input filename> must contain a BINARY + * BASE 64 encoded blob + * + * <output filename> contains an ASCII + * BASE 64 encoded blob + * </PRE> + * + * @version $Revision$, $Date$ + */ +public class BtoA { + // Define constants + public static final int ARGC = 2; + + public static void main(String argv[]) { + + FileInputStream inputBlob = null; + FileOutputStream outputBlob = null; + + // (1) Check that two arguments were submitted to the program + if (argv.length != ARGC) { + System.out.println("Usage: BtoA " + + "<input filename> " + + "<output filename>"); + return; + } + + // (2) Create a DataInputStream() object to the BASE 64 + // encoded blob contained within the file + // specified on the command line + try { + inputBlob = new FileInputStream(argv[0]); + } catch (FileNotFoundException e) { + System.out.println("BtoA(): can''t find file " + + argv[0] + ":\n" + e); + return; + } + + // (3) Create a FileOutputStream() object to the BASE 64 + // specified output file + try { + outputBlob = new FileOutputStream(argv[1]); + } catch (IOException e) { + System.out.println("BtoA(): unable to open file " + + argv[1] + " for writing:\n" + e); + return; + } + + // (4) Convert the BINARY BASE 64 blob into an ASCII BASE 64 blob + + try { + byte data[] = new byte[inputBlob.available()]; + inputBlob.read(data); + String out = Utils.base64encode(data); + outputBlob.write(out.getBytes()); + } catch (IOException e) { + System.out.println("BtoA(): Unexpected BASE64 " + + "encoded error encountered:\n" + + e); + } + + // (5) Close the DataInputStream() object + try { + inputBlob.close(); + } catch (IOException e) { + System.out.println("BtoA(): Unexpected input error " + + "encountered while attempting to close() " + + argv[0] + ":\n" + e); + } + + // (6) Close the FileOutputStream() object + try { + outputBlob.close(); + } catch (IOException e) { + System.out.println("BtoA(): Unexpected output error " + + "encountered while attempting to close() " + + argv[1] + ":\n" + e); + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/CMCEnroll.java b/base/java-tools/src/com/netscape/cmstools/CMCEnroll.java new file mode 100644 index 000000000..e2e51a29d --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/CMCEnroll.java @@ -0,0 +1,467 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.util.Date; + +import netscape.security.pkcs.PKCS10; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.ANY; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.DigestAlgorithm; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.SignatureAlgorithm; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkcs10.CertificationRequest; +import org.mozilla.jss.pkix.cmc.PKIData; +import org.mozilla.jss.pkix.cmc.TaggedAttribute; +import org.mozilla.jss.pkix.cmc.TaggedCertificationRequest; +import org.mozilla.jss.pkix.cmc.TaggedRequest; +import org.mozilla.jss.pkix.cms.ContentInfo; +import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo; +import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber; +import org.mozilla.jss.pkix.cms.SignedData; +import org.mozilla.jss.pkix.cms.SignerIdentifier; +import org.mozilla.jss.pkix.cms.SignerInfo; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; +import org.mozilla.jss.pkix.primitive.Name; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.Utils; + +/** + * Tool for signing PKCS #10 , return CMC enrollment request + * + * <P> + * + * @version $Revision$, $Date$ + */ +public class CMCEnroll { + + public static final String PR_REQUEST_CMC = "CMC"; + public static final String PR_REQUEST_PKCS10 = "PKCS10"; + + public static final int ARGC = 4; + private static final String CERTDB = "cert8.db"; + private static final String KEYDB = "key3.db"; + public static final String HEADER = "-----BEGIN NEW CERTIFICATE REQUEST-----"; + public static final String TRAILER = "-----END NEW CERTIFICATE REQUEST-----"; + + void cleanArgs(String[] s) { + + } + + public static X509Certificate getCertificate(String tokenname, + String nickname) throws Exception { + CryptoManager manager = CryptoManager.getInstance(); + CryptoToken token = null; + + if (tokenname.equals("internal")) { + token = manager.getInternalKeyStorageToken(); + } else { + token = manager.getTokenByName(tokenname); + } + StringBuffer certname = new StringBuffer(); + + if (!token.equals(manager.getInternalKeyStorageToken())) { + certname.append(tokenname); + certname.append(":"); + } + certname.append(nickname); + try { + return manager.findCertByNickname(certname.toString()); + } catch (ObjectNotFoundException e) { + throw new IOException("Signing Certificate not found"); + } + } + + public static java.security.PrivateKey getPrivateKey(String tokenname, String nickname) + throws Exception { + + X509Certificate cert = getCertificate(tokenname, nickname); + + return CryptoManager.getInstance().findPrivKeyByCert(cert); + } + + /** + * getCMCBlob create and return the enrollent request. + * <P> + * + * @param signerCert the certificate of the authorized signer of the CMC revocation request. + * @param manager the crypto manger. + * @param nValue the nickname of the certificate inside the token. + * @param rValue request PKCS#10 file name. + * @return the CMC revocation request encoded in base64 + */ + static String getCMCBlob(X509Certificate signerCert, CryptoManager manager, String nValue, String rValue) { + + String asciiBASE64Blob = rValue; // input pkcs10 blob + String tokenname = "internal"; + + try { + + java.security.PrivateKey privKey = null; + PKCS10 pkcs = null; + SignerIdentifier si = null; + ContentInfo fullEnrollmentReq = null; + + try { + byte[] decodedBytes = Utils.base64decode(asciiBASE64Blob); + + pkcs = new PKCS10(decodedBytes); + } catch (IOException e) { + throw new IOException("Internal Error - " + e.toString()); + } catch (SignatureException e) { + throw new IOException("Internal Error - " + e.toString()); + } catch (NoSuchAlgorithmException e) { + throw new IOException("Internal Error - " + e.toString()); + } + + BigInteger serialno = signerCert.getSerialNumber(); + byte[] certB = signerCert.getEncoded(); + X509CertImpl impl = new X509CertImpl(certB); + X500Name issuerName = (X500Name) impl.getIssuerDN(); + byte[] issuerByte = issuerName.getEncoded(); + ByteArrayInputStream istream = new ByteArrayInputStream(issuerByte); + + Name issuer = (Name) Name.getTemplate().decode(istream); + IssuerAndSerialNumber ias = new IssuerAndSerialNumber(issuer, new INTEGER(serialno.toString())); + + si = new SignerIdentifier(SignerIdentifier.ISSUER_AND_SERIALNUMBER, ias, null); + privKey = getPrivateKey(tokenname, nValue); + + // create CMC req + // transfer pkcs10 to jss class + int bpid = 1; + ByteArrayInputStream crInputStream = new ByteArrayInputStream(pkcs.toByteArray()); + CertificationRequest cr = (CertificationRequest) CertificationRequest.getTemplate().decode(crInputStream); + + TaggedCertificationRequest tcr = new + TaggedCertificationRequest(new + INTEGER(bpid++), cr); + TaggedRequest trq = new + TaggedRequest(TaggedRequest.PKCS10, tcr, + null); + + SEQUENCE reqSequence = new SEQUENCE(); + + reqSequence.addElement(trq); + + // Add some control sequence + // Verisign has transactionID,senderNonce + SEQUENCE controlSeq = new SEQUENCE(); + + Date date = new Date(); + String salt = "lala123" + date.toString(); + byte[] dig; + + try { + MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); + + dig = SHA1Digest.digest(salt.getBytes()); + } catch (NoSuchAlgorithmException ex) { + dig = salt.getBytes(); + } + + String sn = Utils.base64encode(dig); + + TaggedAttribute senderNonce = new TaggedAttribute(new + INTEGER(bpid++), + OBJECT_IDENTIFIER.id_cmc_senderNonce, + new OCTET_STRING(sn.getBytes())); + + controlSeq.addElement(senderNonce); + + // Verisign recommend transactionId be MD5 hash of publicKey + byte[] transId; + + try { + MessageDigest MD5Digest = MessageDigest.getInstance("MD5"); + + transId = MD5Digest.digest(pkcs.getSubjectPublicKeyInfo().getKey()); + } catch (Exception ex) { + transId = salt.getBytes(); + } + + TaggedAttribute transactionId = new TaggedAttribute(new + INTEGER(bpid++), + OBJECT_IDENTIFIER.id_cmc_transactionId, + new INTEGER(1, transId)); + + controlSeq.addElement(transactionId); + + PKIData pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), new SEQUENCE()); + + EncapsulatedContentInfo ci = new + EncapsulatedContentInfo(OBJECT_IDENTIFIER.id_cct_PKIData, + pkidata); + // SHA1 is the default digest Alg for now. + DigestAlgorithm digestAlg = null; + SignatureAlgorithm signAlg = SignatureAlgorithm.RSASignatureWithSHA1Digest; + org.mozilla.jss.crypto.PrivateKey.Type signingKeyType = + ((org.mozilla.jss.crypto.PrivateKey) privKey).getType(); + + if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.DSA)) + signAlg = SignatureAlgorithm.DSASignatureWithSHA1Digest; + MessageDigest SHADigest = null; + byte[] digest = null; + + try { + SHADigest = MessageDigest.getInstance("SHA1"); + digestAlg = DigestAlgorithm.SHA1; + + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + + pkidata.encode((OutputStream) ostream); + digest = SHADigest.digest(ostream.toByteArray()); + } catch (NoSuchAlgorithmException e) { + } + SignerInfo signInfo = new + SignerInfo(si, null, null, OBJECT_IDENTIFIER.id_cct_PKIData, digest, signAlg, + (org.mozilla.jss.crypto.PrivateKey) privKey); + SET signInfos = new SET(); + + signInfos.addElement(signInfo); + + SET digestAlgs = new SET(); + + if (digestAlg != null) { + AlgorithmIdentifier ai = new AlgorithmIdentifier(digestAlg.toOID(), null); + + digestAlgs.addElement(ai); + } + + org.mozilla.jss.crypto.X509Certificate[] agentChain = manager.buildCertificateChain(signerCert); + SET certs = new SET(); + + for (int i = 0; i < agentChain.length; i++) { + ANY cert = new ANY(agentChain[i].getEncoded()); + + certs.addElement(cert); + } + SignedData req = new SignedData(digestAlgs, ci, certs, null, signInfos); + + fullEnrollmentReq = new + ContentInfo(req); + + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bs); + + // format is PR_REQUEST_CMC + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + fullEnrollmentReq.encode(os); + ps.print(Utils.base64encode(os.toByteArray())); + //fullEnrollmentReq.print(ps); // no header/trailer + asciiBASE64Blob = bs.toString(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + return asciiBASE64Blob; + } + + /** Creates a new instance of CMCEnroll */ + public static void main(String[] s) { + + String dValue = null, nValue = null, rValue = null, pValue = null; + FileOutputStream outputBlob = null; + + // default path is "." + String mPath = "."; + // default prefix is "" + String mPrefix = ""; + + boolean bWrongParam = false; + + // (1) Check that two arguments were submitted to the program + if (s.length != (ARGC * 2)) { + System.out.println("Wrong number of parameters:" + s.length); + System.out.println("Usage: CMCEnroll " + + "-d <dir to cert8.db, key3.db> " + + "-n <nickname> " + + "-r <request PKCS#10 file name> " + + "-p <password>" + ); + bWrongParam = true; + } else { + int length; + int i; + + length = s.length; + for (i = 0; i < length; i++) { + if (s[i].equals("-d")) { + dValue = s[i + 1]; + } else if (s[i].equals("-n")) { + nValue = s[i + 1]; + } else if (s[i].equals("-r")) { + rValue = s[i + 1]; + } else if (s[i].equals("-p")) { + pValue = s[i + 1]; + } + if (s[i].equals("")) + bWrongParam = true; + + } + + if (dValue == null || nValue == null || rValue == null || pValue == null) + bWrongParam = true; + else if (dValue.length() == 0 || nValue.length() == 0 || rValue.length() == 0 || + pValue.length() == 0) + bWrongParam = true; + if (bWrongParam == true) { + System.out.println("Usage: CMCEnroll " + + "-d <dir to cert8.db, key3.db> " + + "-n <nickname> " + + "-r <request PKCS#10 file name> " + + "-p <password>" + ); + System.exit(0); + } + + try { + // initialize CryptoManager + mPath = dValue; + System.out.println("cert/key prefix = " + mPrefix); + System.out.println("path = " + mPath); + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(mPath, mPrefix, + mPrefix, "secmod.db"); + + CryptoManager.initialize(vals); + + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = cm.getInternalKeyStorageToken(); + Password pass = new Password(pValue.toCharArray()); + + token.login(pass); + X509Certificate signerCert = null; + + signerCert = cm.findCertByNickname(nValue); + + BufferedReader inputBlob = null; + + try { + inputBlob = new BufferedReader(new InputStreamReader( + new BufferedInputStream( + new FileInputStream( + rValue)))); + } catch (FileNotFoundException e) { + System.out.println("CMCEnroll: can''t find file " + + rValue + ":\n" + e); + return; + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + // (3) Read the entire contents of the specified BASE 64 encoded + // blob into a String() object throwing away any + // headers beginning with HEADER and any trailers beginning + // with TRAILER + String asciiBASE64BlobChunk = new String(); + String asciiBASE64Blob = new String(); + + try { + while ((asciiBASE64BlobChunk = inputBlob.readLine()) != null) { + if (!(asciiBASE64BlobChunk.startsWith(HEADER)) && + !(asciiBASE64BlobChunk.startsWith(TRAILER))) { + asciiBASE64Blob += asciiBASE64BlobChunk.trim(); + } + } + } catch (IOException e) { + System.out.println("CMCEnroll: Unexpected BASE64 " + + "encoded error encountered in readLine():\n" + + e); + } + // (4) Close the DataInputStream() object + try { + inputBlob.close(); + } catch (IOException e) { + System.out.println("CMCEnroll(): Unexpected BASE64 " + + "encoded error encountered in close():\n" + e); + } + + asciiBASE64Blob = getCMCBlob(signerCert, cm, nValue, asciiBASE64Blob); + // (5) Decode the ASCII BASE 64 blob enclosed in the + // String() object into a BINARY BASE 64 byte[] object + + @SuppressWarnings("unused") + byte binaryBASE64Blob[] = + Utils.base64decode(asciiBASE64Blob); // check for errors + + // (6) Finally, print the actual CMCEnroll blob to the + // specified output file + try { + outputBlob = new FileOutputStream(rValue + ".out"); + } catch (IOException e) { + System.out.println("CMCEnroll: unable to open file " + + rValue + ".out" + " for writing:\n" + e); + return; + } + + System.out.println(HEADER); + System.out.println(asciiBASE64Blob + TRAILER); + try { + asciiBASE64Blob = HEADER + "\n" + asciiBASE64Blob + TRAILER; + outputBlob.write(asciiBASE64Blob.getBytes()); + } catch (IOException e) { + System.out.println("CMCEnroll: I/O error " + + "encountered during write():\n" + + e); + } + + try { + outputBlob.close(); + } catch (IOException e) { + System.out.println("CMCEnroll: Unexpected error " + + "encountered while attempting to close() " + + "\n" + e); + } + + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + return; + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java new file mode 100644 index 000000000..591361149 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java @@ -0,0 +1,1129 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.util.Date; +import java.util.StringTokenizer; + +import netscape.security.pkcs.PKCS10; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.ANY; +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.BIT_STRING; +import org.mozilla.jss.asn1.ENUMERATED; +import org.mozilla.jss.asn1.GeneralizedTime; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.asn1.UTF8String; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.DigestAlgorithm; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.SignatureAlgorithm; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkcs10.CertificationRequest; +import org.mozilla.jss.pkix.cmc.CMCCertId; +import org.mozilla.jss.pkix.cmc.GetCert; +import org.mozilla.jss.pkix.cmc.LraPopWitness; +import org.mozilla.jss.pkix.cmc.OtherMsg; +import org.mozilla.jss.pkix.cmc.PKIData; +import org.mozilla.jss.pkix.cmc.TaggedAttribute; +import org.mozilla.jss.pkix.cmc.TaggedCertificationRequest; +import org.mozilla.jss.pkix.cmc.TaggedRequest; +import org.mozilla.jss.pkix.cmmf.RevRequest; +import org.mozilla.jss.pkix.cms.ContentInfo; +import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo; +import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber; +import org.mozilla.jss.pkix.cms.SignedData; +import org.mozilla.jss.pkix.cms.SignerIdentifier; +import org.mozilla.jss.pkix.cms.SignerInfo; +import org.mozilla.jss.pkix.crmf.CertReqMsg; +import org.mozilla.jss.pkix.crmf.CertRequest; +import org.mozilla.jss.pkix.crmf.CertTemplate; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; +import org.mozilla.jss.pkix.primitive.Name; +import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.HMACDigest; +import com.netscape.cmsutil.util.Utils; + +/** + * Tool for creating CMC full request + * + * <P> + * + * @version $Revision$, $Date$ + * + */ +public class CMCRequest { + + public static final String PR_REQUEST_CMC = "CMC"; + public static final String PR_REQUEST_CRMF = "CRMF"; + + public static final int ARGC = 1; + private static final String CERTDB = "cert8.db"; + private static final String KEYDB = "key3.db"; + public static final String HEADER = "-----BEGIN NEW CERTIFICATE REQUEST-----"; + public static final String TRAILER = "-----END NEW CERTIFICATE REQUEST-----"; + + void cleanArgs(String[] s) { + + } + + public static X509Certificate getCertificate(String tokenname, + String nickname) throws Exception { + CryptoManager manager = CryptoManager.getInstance(); + CryptoToken token = null; + + if (tokenname.equals("internal")) { + token = manager.getInternalKeyStorageToken(); + } else { + token = manager.getTokenByName(tokenname); + } + StringBuffer certname = new StringBuffer(); + + if (!token.equals(manager.getInternalKeyStorageToken())) { + certname.append(tokenname); + certname.append(":"); + } + certname.append(nickname); + try { + return manager.findCertByNickname(certname.toString()); + } catch (ObjectNotFoundException e) { + throw new IOException("Signing Certificate not found"); + } + } + + public static java.security.PrivateKey getPrivateKey(String tokenname, String nickname) + throws Exception { + + X509Certificate cert = getCertificate(tokenname, nickname); + + return CryptoManager.getInstance().findPrivKeyByCert(cert); + } + + /** + * getCMCBlob create and return the enrollent request. + * <P> + * + * @param signerCert the certificate of the authorized signer of the CMC revocation request. + * @param nickname the nickname of the certificate inside the token. + * @param rValue CRMF/PKCS10 request. + * @param format either crmf or pkcs10 + * @return the CMC enrollment request encoded in base64 + */ + static ContentInfo getCMCBlob(X509Certificate signerCert, String nickname, + String[] rValue, String format, CryptoManager manager, String transactionMgtEnable, + String transactionMgtId, String identityProofEnable, String identityProofSharedSecret, + SEQUENCE controlSeq, SEQUENCE otherMsgSeq, int bpid) { + + String tokenname = "internal"; + + ContentInfo fullEnrollmentReq = null; + try { + java.security.PrivateKey privKey = null; + SignerIdentifier si = null; + + BigInteger serialno = signerCert.getSerialNumber(); + byte[] certB = signerCert.getEncoded(); + X509CertImpl impl = new X509CertImpl(certB); + X500Name issuerName = (X500Name) impl.getIssuerDN(); + byte[] issuerByte = issuerName.getEncoded(); + ByteArrayInputStream istream = new ByteArrayInputStream(issuerByte); + + Name issuer = (Name) Name.getTemplate().decode(istream); + IssuerAndSerialNumber ias = new IssuerAndSerialNumber( + issuer, new INTEGER(serialno.toString())); + + si = new SignerIdentifier( + SignerIdentifier.ISSUER_AND_SERIALNUMBER, ias, null); + privKey = getPrivateKey(tokenname, nickname); + + TaggedRequest trq = null; + PKCS10 pkcs = null; + CertReqMsg certReqMsg = null; + + // create CMC req + SEQUENCE reqSequence = new SEQUENCE(); + try { + for (int k = 0; k < rValue.length; k++) { + String asciiBASE64Blob = rValue[k]; + byte[] decodedBytes = Utils.base64decode(asciiBASE64Blob); + + if (format.equals("crmf")) { + ByteArrayInputStream reqBlob = + new ByteArrayInputStream(decodedBytes); + SEQUENCE crmfMsgs = null; + try { + crmfMsgs = (SEQUENCE) new SEQUENCE.OF_Template(new + CertReqMsg.Template()).decode(reqBlob); + } catch (InvalidBERException ee) { + System.out.println("This is not a crmf request. Or this request has an error."); + System.exit(1); + } + certReqMsg = (CertReqMsg) crmfMsgs.elementAt(0); + trq = new TaggedRequest(TaggedRequest.CRMF, null, + certReqMsg); + } else if (format.equals("pkcs10")) { + try { + pkcs = new PKCS10(decodedBytes); + } catch (IllegalArgumentException e) { + System.out.println("This is not a PKCS10 request."); + System.exit(1); + } + ByteArrayInputStream crInputStream = new ByteArrayInputStream( + pkcs.toByteArray()); + CertificationRequest cr = (CertificationRequest) + CertificationRequest.getTemplate().decode(crInputStream); + TaggedCertificationRequest tcr = new TaggedCertificationRequest( + new INTEGER(bpid++), cr); + trq = new + TaggedRequest(TaggedRequest.PKCS10, tcr, null); + } else { + System.out.println("Unrecognized request format: " + format); + System.exit(1); + } + reqSequence.addElement(trq); + } + } catch (IOException e) { + throw new IOException("Internal Error - " + e.toString()); + } catch (SignatureException e) { + throw new IOException("Internal Error - " + e.toString()); + } catch (NoSuchAlgorithmException e) { + throw new IOException("Internal Error - " + e.toString()); + } + + if (transactionMgtEnable.equals("true")) + bpid = addTransactionAttr(bpid, controlSeq, transactionMgtId, format, + pkcs, certReqMsg); + + if (identityProofEnable.equals("true")) + bpid = addIdentityProofAttr(bpid, controlSeq, reqSequence, + identityProofSharedSecret); + + PKIData pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq); + + EncapsulatedContentInfo ci = new + EncapsulatedContentInfo(OBJECT_IDENTIFIER.id_cct_PKIData, pkidata); + // SHA1 is the default digest Alg for now. + DigestAlgorithm digestAlg = null; + SignatureAlgorithm signAlg = SignatureAlgorithm.RSASignatureWithSHA1Digest; + org.mozilla.jss.crypto.PrivateKey.Type signingKeyType = + ((org.mozilla.jss.crypto.PrivateKey) privKey).getType(); + + if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.DSA)) + signAlg = SignatureAlgorithm.DSASignatureWithSHA1Digest; + MessageDigest SHADigest = null; + + byte[] digest = null; + try { + SHADigest = MessageDigest.getInstance("SHA1"); + digestAlg = DigestAlgorithm.SHA1; + + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + + pkidata.encode((OutputStream) ostream); + digest = SHADigest.digest(ostream.toByteArray()); + } catch (NoSuchAlgorithmException e) { + } + SignerInfo signInfo = new + SignerInfo(si, null, null, OBJECT_IDENTIFIER.id_cct_PKIData, digest, signAlg, + (org.mozilla.jss.crypto.PrivateKey) privKey); + SET signInfos = new SET(); + signInfos.addElement(signInfo); + + SET digestAlgs = new SET(); + + if (digestAlg != null) { + AlgorithmIdentifier ai = new AlgorithmIdentifier(digestAlg.toOID(), null); + digestAlgs.addElement(ai); + } + + org.mozilla.jss.crypto.X509Certificate[] agentChain = manager.buildCertificateChain(signerCert); + SET certs = new SET(); + + for (int i = 0; i < agentChain.length; i++) { + ANY cert = new ANY(agentChain[i].getEncoded()); + certs.addElement(cert); + } + SignedData req = new SignedData(digestAlgs, ci, certs, null, signInfos); + fullEnrollmentReq = new ContentInfo(req); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bs); + + if (fullEnrollmentReq != null) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + fullEnrollmentReq.encode(os); + ps.print(Utils.base64encode(os.toByteArray())); + } + String asciiBASE64Blob = bs.toString(); + + System.out.println(""); + System.out.println("The CMC enrollment request in base-64 encoded format:"); + System.out.println(""); + System.out.println(asciiBASE64Blob); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + return fullEnrollmentReq; + } + + static void printUsage() { + System.out.println(""); + System.out.println("Usage: CMCRequest <configuration file>"); + System.out.println("For example, CMCRequest CMCRequest.cfg"); + System.out.println(""); + System.out.println("The configuration file should look like as follows:"); + System.out.println(""); + System.out.println("#numRequests: Total number of PKCS10 requests or CRMF requests."); + System.out.println("numRequests=1"); + System.out.println(""); + System.out.println("#input: full path for the PKCS10 request or CRMF request,"); + System.out.println("#the content must be in Base-64 encoded format"); + System.out.println("#Multiple files are supported. They must be separated by space."); + System.out.println("input=crmf1"); + System.out.println(""); + System.out.println("#output: full path for the CMC request in binary format"); + System.out.println("output=/u/doc/cmcReq"); + System.out.println(""); + System.out.println("#nickname: nickname for agent certificate which will be used"); + System.out.println("#to sign the CMC full request."); + System.out.println("nickname=CMS Agent Certificate"); + System.out.println(""); + System.out.println("#dbdir: directory for cert8.db, key3.db and secmod.db"); + System.out.println("dbdir=/u/smith/.netscape"); + System.out.println(""); + System.out.println("#password: password for cert8.db which stores the agent"); + System.out.println("#certificate"); + System.out.println("password=pass"); + System.out.println(""); + System.out.println("#format: request format, either pkcs10 or crmf"); + System.out.println("format=crmf"); + System.out.println(""); + System.out.println("#confirmCertAcceptance.enable: if true, then the request will"); + System.out.println("#contain this control. Otherwise, false."); + System.out.println("confirmCertAcceptance.enable=true"); + System.out.println(""); + System.out.println("#confirmCertAcceptance.serial: The serial number for"); + System.out.println("#confirmCertAcceptance control"); + System.out.println("confirmCertAcceptance.serial=3"); + System.out.println(""); + System.out.println("#confirmCertAcceptance.issuer: The issuer name for"); + System.out.println("#confirmCertAcceptance control"); + System.out.println("confirmCertAcceptance.issuer=cn=Certificate Manager,c=us"); + System.out.println(""); + System.out.println("#getCert.enable: if true, then the request will contain this"); + System.out.println("#control. Otherwise, false."); + System.out.println("getCert.enable=true"); + System.out.println(""); + System.out.println("#getCert.serial: The serial number for getCert control"); + System.out.println("getCert.serial=3"); + System.out.println(""); + System.out.println("#getCert.issuer: The issuer name for getCert control"); + System.out.println("getCert.issuer=cn=Certificate Manager,c=us"); + System.out.println(""); + System.out.println("#dataReturn.enable: if true, then the request will contain"); + System.out.println("#this control. Otherwise, false."); + System.out.println("dataReturn.enable=true"); + System.out.println(""); + System.out.println("#dataReturn.data: data contained in the control."); + System.out.println("dataReturn.data=test"); + System.out.println(""); + System.out.println("#transactionMgt.enable: if true, then the request will contain"); + System.out.println("#this control. Otherwise, false."); + System.out.println("transactionMgt.enable=true"); + System.out.println(""); + System.out.println("#transactionMgt.id: transaction identifier. Verisign recommend"); + System.out.println("#transactionId to be MD5 hash of publicKey."); + System.out.println("transactionMgt.id="); + System.out.println(""); + System.out.println("#senderNonce.enable: if true, then the request will contain this"); + System.out.println("#control. Otherwise, false."); + System.out.println("senderNonce.enable=true"); + System.out.println(""); + System.out.println("#senderNonce.id: sender nonce"); + System.out.println("senderNonce.id="); + System.out.println(""); + System.out.println("#revRequest.enable: if true, then the request will contain this"); + System.out.println("#control. Otherwise, false."); + System.out.println("revRequest.enable=true"); + System.out.println(""); + System.out.println("#revRequest.nickname: The nickname for the revoke certificate"); + System.out.println("revRequest.nickname=newuser's 102504a ID"); + System.out.println(""); + System.out.println("#revRequest.issuer: The issuer name for the certificate being"); + System.out.println("#revoked."); + System.out.println("revRequest.issuer=cn=Certificate Manager,c=us"); + System.out.println(""); + System.out.println("#revRequest.serial: The serial number for the certificate being"); + System.out.println("#revoked."); + System.out.println("revRequest.serial=61"); + System.out.println(""); + System.out.println("#revRequest.reason: The reason for revoking this certificate: "); + System.out.println("# unspecified, keyCompromise, caCompromise,"); + System.out.println("# affiliationChanged, superseded, cessationOfOperation,"); + System.out.println("# certificateHold, removeFromCRL"); + System.out.println("revRequest.reason=unspecified"); + System.out.println(""); + System.out.println("#revRequest.sharedSecret: The sharedSecret"); + System.out.println("revRequest.sharedSecret="); + System.out.println(""); + System.out.println("#revRequest.comment: The human readable comment"); + System.out.println("revRequest.comment="); + System.out.println(""); + System.out.println("#revRequest.invalidityDatePresent: if true, the current time will be the"); + System.out.println("# invalidityDate. If false, no invalidityDate"); + System.out.println("# is present."); + System.out.println("revRequest.invalidityDatePresent=false"); + System.out.println(""); + System.out.println("#identityProof.enable: if true, then the request will contain"); + System.out.println("#this control. Otherwise, false."); + System.out.println("identityProof.enable=true"); + System.out.println(""); + System.out.println("#identityProof.sharedSecret: Shared Secret"); + System.out.println("identityProof.sharedSecret=testing"); + System.out.println(""); + System.out.println("#popLinkWitness.enable: if true, then the request will contain"); + System.out.println("#this control. Otherwise, false."); + System.out.println("#If you want to test this control, make sure to use CRMFPopClient "); + System.out.println("# to generate the CRMF request which will include the "); + System.out.println("#idPOPLinkWitness attribute in the controls section of the "); + System.out.println("#CertRequest structure."); + System.out.println("popLinkWitness.enable=false"); + System.out.println(""); + System.out.println("#LraPopWitness.enable: if true, then the request will contain this"); + System.out.println("#control. Otherwise, false."); + System.out.println("LraPopWitness.enable=true"); + System.out.println(""); + System.out.println("#LraPopWitness.bodyPartIDs: List of body part IDs"); + System.out.println("#Each id is separated by space."); + System.out.println("LraPopWitness.bodyPartIDs=1"); + System.exit(1); + } + + private static int addLraPopWitnessAttr(int bpid, SEQUENCE seq, String bodyPartIDs) { + StringTokenizer tokenizer = new StringTokenizer(bodyPartIDs, " "); + SEQUENCE bodyList = new SEQUENCE(); + while (tokenizer.hasMoreTokens()) { + String s = (String) tokenizer.nextToken(); + bodyList.addElement(new INTEGER(s)); + } + LraPopWitness lra = new LraPopWitness(new INTEGER(0), bodyList); + TaggedAttribute cont = new TaggedAttribute(new + INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_lraPOPWitness, lra); + System.out.println("Successfully create LRA POP witness control. bpid = " + (bpid - 1)); + System.out.println(""); + seq.addElement(cont); + return bpid; + } + + private static int addConfirmCertAttr(int bpid, SEQUENCE seq, String confirmCertIssuer, + String confirmCertSerial) { + try { + INTEGER serial = new INTEGER(confirmCertSerial); + X500Name issuername = new X500Name(confirmCertIssuer); + byte[] issuerbyte = issuername.getEncoded(); + ANY issuern = new ANY(issuerbyte); + CMCCertId cmcCertId = new CMCCertId(issuern, serial, null); + TaggedAttribute cmcCertIdControl = new TaggedAttribute(new + INTEGER(bpid++), + OBJECT_IDENTIFIER.id_cmc_idConfirmCertAcceptance, cmcCertId); + System.out.println("Successfully create confirm certificate acceptance control. bpid = " + (bpid - 1)); + System.out.println(""); + seq.addElement(cmcCertIdControl); + } catch (Exception e) { + System.out.println("Error in creating confirm certificate acceptance control. Check the parameters."); + System.exit(1); + } + return bpid; + } + + private static ENUMERATED toCRLReason(String str) { + if (str.equalsIgnoreCase("unspecified")) { + return RevRequest.unspecified; + } else if (str.equalsIgnoreCase("keyCompromise")) { + return RevRequest.keyCompromise; + } else if (str.equalsIgnoreCase("caCompromise")) { + return RevRequest.cACompromise; + } else if (str.equalsIgnoreCase("affiliationChanged")) { + return RevRequest.affiliationChanged; + } else if (str.equalsIgnoreCase("superseded")) { + return RevRequest.superseded; + } else if (str.equalsIgnoreCase("cessationOfOperation")) { + return RevRequest.cessationOfOperation; + } else if (str.equalsIgnoreCase("certificateHold")) { + return RevRequest.certificateHold; + } else if (str.equalsIgnoreCase("removeFromCRL")) { + return RevRequest.removeFromCRL; + } + + System.out.println("Unrecognized CRL reason"); + System.exit(1); + + return RevRequest.unspecified; + } + + private static int addIdentityProofAttr(int bpid, SEQUENCE seq, SEQUENCE reqSequence, + String sharedSecret) { + byte[] b = ASN1Util.encode(reqSequence); + byte[] key = null; + byte[] finalDigest = null; + try { + MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); + key = SHA1Digest.digest(sharedSecret.getBytes()); + } catch (NoSuchAlgorithmException ex) { + System.out.println("CMCRequest::addIdentityProofAttr() - " + + "No such algorithm!"); + return -1; + } + + try { + MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); + HMACDigest hmacDigest = new HMACDigest(SHA1Digest, key); + hmacDigest.update(b); + finalDigest = hmacDigest.digest(); + } catch (NoSuchAlgorithmException ex) { + } + + TaggedAttribute identityProof = new TaggedAttribute(new + INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_identityProof, + new OCTET_STRING(finalDigest)); + seq.addElement(identityProof); + System.out.println("Identity Proof control: "); + System.out.print(" Value: "); + for (int i = 0; i < finalDigest.length; i++) { + System.out.print(finalDigest[i] + " "); + } + System.out.println(""); + System.out.println("Successfully create identityProof control. bpid = " + (bpid - 1)); + System.out.println(""); + return bpid; + } + + private static int addRevRequestAttr(int bpid, SEQUENCE seq, SEQUENCE otherMsgSeq, String nickname, + String revRequestIssuer, String revRequestSerial, String revRequestReason, + String revRequestSharedSecret, String revRequestComment, String invalidityDatePresent, + CryptoManager manager) { + try { + if (nickname.length() <= 0) { + System.out.println("The nickname for the certificate being revoked is null"); + System.exit(1); + } + String nickname1 = nickname; + UTF8String comment = null; + OCTET_STRING sharedSecret = null; + GeneralizedTime d = null; + X500Name subjectname = new X500Name(revRequestIssuer); + INTEGER snumber = new INTEGER(revRequestSerial); + ENUMERATED reason = toCRLReason(revRequestReason); + if (revRequestSharedSecret.length() > 0) + sharedSecret = new OCTET_STRING(revRequestSharedSecret.getBytes()); + if (revRequestComment.length() > 0) + comment = new UTF8String(revRequestComment); + if (invalidityDatePresent.equals("true")) + d = new GeneralizedTime(new Date()); + RevRequest revRequest = + new RevRequest(new ANY(subjectname.getEncoded()), snumber, + reason, d, sharedSecret, comment); + int revokeBpid = bpid; + TaggedAttribute revRequestControl = new TaggedAttribute( + new INTEGER(bpid++), + OBJECT_IDENTIFIER.id_cmc_revokeRequest, revRequest); + seq.addElement(revRequestControl); + + if (sharedSecret != null) { + System.out.println("Successfully create revRequest control. bpid = " + (bpid - 1)); + System.out.println(""); + return bpid; + } + + EncapsulatedContentInfo revokeContent = new EncapsulatedContentInfo( + OBJECT_IDENTIFIER.id_cct_PKIData, revRequestControl); + DigestAlgorithm digestAlg1 = null; + SignatureAlgorithm signAlg1 = SignatureAlgorithm.RSASignatureWithSHA1Digest; + java.security.PrivateKey revokePrivKey = null; + X509Certificate revokeCert = null; + try { + revokeCert = manager.findCertByNickname(nickname1); + } catch (ObjectNotFoundException e) { + System.out.println("Certificate not found: " + nickname1); + System.exit(1); + } + revokePrivKey = manager.findPrivKeyByCert(revokeCert); + org.mozilla.jss.crypto.PrivateKey.Type signingKeyType1 = + ((org.mozilla.jss.crypto.PrivateKey) revokePrivKey).getType(); + if (signingKeyType1.equals(org.mozilla.jss.crypto.PrivateKey.Type.DSA)) + signAlg1 = SignatureAlgorithm.DSASignatureWithSHA1Digest; + + MessageDigest rSHADigest = null; + byte[] rdigest = null; + try { + rSHADigest = MessageDigest.getInstance("SHA1"); + digestAlg1 = DigestAlgorithm.SHA1; + + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + + revRequestControl.encode((OutputStream) ostream); + rdigest = rSHADigest.digest(ostream.toByteArray()); + } catch (NoSuchAlgorithmException e) { + } + + ByteArrayInputStream bistream = + new ByteArrayInputStream(subjectname.getEncoded()); + Name iname = (Name) Name.getTemplate().decode(bistream); + IssuerAndSerialNumber ias1 = new IssuerAndSerialNumber(iname, snumber); + + SignerIdentifier rsi = new SignerIdentifier( + SignerIdentifier.ISSUER_AND_SERIALNUMBER, ias1, null); + + SignerInfo signInfo1 = new SignerInfo(rsi, null, null, + OBJECT_IDENTIFIER.id_cct_PKIData, rdigest, signAlg1, + (org.mozilla.jss.crypto.PrivateKey) revokePrivKey); + + SET signInfos1 = new SET(); + signInfos1.addElement(signInfo1); + SET digestAlgs1 = new SET(); + if (digestAlg1 != null) { + AlgorithmIdentifier ai1 = new AlgorithmIdentifier(digestAlg1.toOID(), null); + digestAlgs1.addElement(ai1); + } + + org.mozilla.jss.crypto.X509Certificate[] revokeCertChain = + manager.buildCertificateChain(revokeCert); + SET certs1 = new SET(); + for (int i = 0; i < revokeCertChain.length; i++) { + ANY cert1 = new ANY(revokeCertChain[i].getEncoded()); + certs1.addElement(cert1); + } + + SignedData sData = new SignedData(digestAlgs1, revokeContent, certs1, null, signInfos1); + OBJECT_IDENTIFIER signedDataOID = new OBJECT_IDENTIFIER("1.2.840.113549.1.7.2"); + ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); + sData.encode(bos1); + OtherMsg otherMsg = new OtherMsg(new INTEGER(revokeBpid), signedDataOID, new ANY(bos1.toByteArray())); + otherMsgSeq.addElement(otherMsg); + System.out.println("Successfully create revRequest control. bpid = " + (bpid - 1)); + System.out.println(""); + } catch (Exception e) { + System.out.println("Error in creating revRequest control. Check the parameters."); + System.exit(1); + } + + return bpid; + } + + private static int addGetCertAttr(int bpid, SEQUENCE seq, String issuer, String serial) { + try { + INTEGER serialno = new INTEGER(serial); + X500Name issuername = new X500Name(issuer); + byte[] issuerbyte = issuername.getEncoded(); + ANY issuern = new ANY(issuerbyte); + GetCert getCert = new GetCert(issuern, serialno); + TaggedAttribute getCertControl = new TaggedAttribute(new + INTEGER(bpid++), + OBJECT_IDENTIFIER.id_cmc_getCert, getCert); + System.out.println("Successfully create get certificate control. bpid = " + (bpid - 1)); + System.out.println(""); + seq.addElement(getCertControl); + } catch (Exception e) { + System.out.println("Error in creating get certificate control. Check the parameters."); + System.exit(1); + } + + return bpid; + } + + private static int addDataReturnAttr(int bpid, SEQUENCE seq, String str) { + try { + byte bvalue[] = str.getBytes(); + System.out.println("Data Return Control: "); + String ss = " Value: "; + for (int m = 0; m < bvalue.length; m++) { + ss = ss + bvalue[m] + " "; + } + System.out.println(ss); + OCTET_STRING s = new OCTET_STRING(bvalue); + TaggedAttribute dataReturnControl = new TaggedAttribute(new + INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_dataReturn, s); + seq.addElement(dataReturnControl); + System.out.println("Successfully create data return control. bpid = " + (bpid - 1)); + System.out.println(""); + } catch (Exception e) { + System.out.println("Error in creating data return control. Check the parameters."); + System.exit(1); + } + + return bpid; + } + + private static int addTransactionAttr(int bpid, SEQUENCE seq, String id, String format, + PKCS10 pkcs, CertReqMsg certReqMsg) { + byte[] transId = null; + Date date = new Date(); + String salt = "lala123" + date.toString(); + if (id == null || id.equals("")) { + try { + MessageDigest MD5Digest = MessageDigest.getInstance("MD5"); + if (format.equals("crmf")) { + CertRequest certreq = certReqMsg.getCertReq(); + CertTemplate certTemplate = certreq.getCertTemplate(); + SubjectPublicKeyInfo pkinfo = certTemplate.getPublicKey(); + BIT_STRING bitString = pkinfo.getSubjectPublicKey(); + byte[] b = bitString.getBits(); + transId = MD5Digest.digest(b); + } else if (format.equals("pkcs10")) { + transId = MD5Digest.digest(pkcs.getSubjectPublicKeyInfo().getKey()); + } + } catch (Exception ex) { + transId = salt.getBytes(); + } + } else { + transId = id.getBytes(); + } + + if (transId == null) { + System.out.println("CMCRequest::addTransactionAttr() - " + + "transId is null!"); + return -1; + } + + INTEGER ii = new INTEGER(1, transId); + TaggedAttribute transactionId = new TaggedAttribute(new + INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_transactionId, ii); + System.out.println("Transaction ID control: "); + System.out.println(" Value: " + ii.toString()); + System.out.println("Successfully create transaction management control. bpid = " + (bpid - 1)); + System.out.println(""); + + seq.addElement(transactionId); + + return bpid; + } + + private static int addSenderNonceAttr(int bpid, SEQUENCE seq, String nonce) { + byte[] dig; + String sn = nonce; + if (nonce == null || nonce.equals("")) { + // Verisign has transactionID,senderNonce + Date date = new Date(); + String salt = "lala123" + date.toString(); + + try { + MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); + + dig = SHA1Digest.digest(salt.getBytes()); + } catch (NoSuchAlgorithmException ex) { + dig = salt.getBytes(); + } + + sn = Utils.base64encode(dig); + } + byte bb[] = sn.getBytes(); + System.out.println("SenderNonce control: "); + String ss = " Value: "; + for (int m = 0; m < bb.length; m++) { + ss = ss + bb[m] + " "; + } + System.out.println(ss); + TaggedAttribute senderNonce = new TaggedAttribute(new + INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_senderNonce, + new OCTET_STRING(sn.getBytes())); + System.out.println("Successfully create sender nonce control. bpid = " + (bpid - 1)); + System.out.println(""); + seq.addElement(senderNonce); + return bpid; + } + + private static int addPopLinkWitnessAttr(int bpid, SEQUENCE controlSeq) { + byte[] seed = + { 0x10, 0x53, 0x42, 0x24, 0x1a, 0x2a, 0x35, 0x3c, + 0x7a, 0x52, 0x54, 0x56, 0x71, 0x65, 0x66, 0x4c, + 0x51, 0x34, 0x35, 0x23, 0x3c, 0x42, 0x43, 0x45, + 0x61, 0x4f, 0x6e, 0x43, 0x1e, 0x2a, 0x2b, 0x31, + 0x32, 0x34, 0x35, 0x36, 0x55, 0x51, 0x48, 0x14, + 0x16, 0x29, 0x41, 0x42, 0x43, 0x7b, 0x63, 0x44, + 0x6a, 0x12, 0x6b, 0x3c, 0x4c, 0x3f, 0x00, 0x14, + 0x51, 0x61, 0x15, 0x22, 0x23, 0x5f, 0x5e, 0x69 }; + + TaggedAttribute idPOPLinkRandom = new TaggedAttribute(new + INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_idPOPLinkRandom, + new OCTET_STRING(seed)); + controlSeq.addElement(idPOPLinkRandom); + System.out.println("Successfully create PopLinkWitness control. bpid = " + (bpid - 1)); + System.out.println(""); + return bpid; + } + + public static void main(String[] s) { + String numRequests = null; + String dbdir = null, nickname = null; + String ifilename = null, ofilename = null, password = null, format = null; + String confirmCertEnable = "false", confirmCertIssuer = null, confirmCertSerial = null; + String getCertEnable = "false", getCertIssuer = null, getCertSerial = null; + String dataReturnEnable = "false", dataReturnData = null; + String transactionMgtEnable = "false", transactionMgtId = null; + String senderNonceEnable = "false", senderNonce = null; + String revCertNickname = ""; + String revRequestEnable = "false", revRequestIssuer = null, revRequestSerial = null; + String revRequestReason = null, revRequestSharedSecret = null, revRequestComment = null; + String revRequestInvalidityDatePresent = "false"; + String identityProofEnable = "false", identityProofSharedSecret = null; + String popLinkWitnessEnable = "false"; + String bodyPartIDs = null, lraPopWitnessEnable = "false"; + + System.out.println(""); + + // Check that the correct # of arguments were submitted to the program + if (s.length != (ARGC)) { + System.out.println("Wrong number of parameters:" + s.length); + printUsage(); + } + + String configFile = s[0]; + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader( + new BufferedInputStream( + new FileInputStream( + configFile)))); + } catch (FileNotFoundException e) { + System.out.println("CMCRequest: can't find configuration file: " + configFile); + printUsage(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + try { + String str = ""; + while ((str = reader.readLine()) != null) { + str = str.trim(); + if (!str.startsWith("#") && str.length() > 0) { + int index = str.indexOf("="); + String name = ""; + String val = ""; + if (index == -1) { + System.out.println("Error in configuration file: " + str); + System.exit(1); + } + name = str.substring(0, index); + if (index != str.length() - 1) + val = str.substring(index + 1); + + if (name.equals("format")) { + format = val; + } else if (name.equals("dbdir")) { + dbdir = val; + } else if (name.equals("nickname")) { + nickname = val; + } else if (name.equals("password")) { + password = val; + } else if (name.equals("output")) { + ofilename = val; + } else if (name.equals("input")) { + ifilename = val; + } else if (name.equals("confirmCertAcceptance.serial")) { + confirmCertSerial = val; + } else if (name.equals("confirmCertAcceptance.issuer")) { + confirmCertIssuer = val; + } else if (name.equals("confirmCertAcceptance.enable")) { + confirmCertEnable = val; + } else if (name.equals("getCert.enable")) { + getCertEnable = val; + } else if (name.equals("getCert.issuer")) { + getCertIssuer = val; + } else if (name.equals("getCert.serial")) { + getCertSerial = val; + } else if (name.equals("dataReturn.enable")) { + dataReturnEnable = val; + } else if (name.equals("dataReturn.data")) { + dataReturnData = val; + } else if (name.equals("transactionMgt.enable")) { + transactionMgtEnable = val; + } else if (name.equals("transactionMgt.id")) { + transactionMgtId = val; + } else if (name.equals("senderNonce.enable")) { + senderNonceEnable = val; + } else if (name.equals("senderNonce")) { + senderNonce = val; + } else if (name.equals("revRequest.enable")) { + revRequestEnable = val; + } else if (name.equals("revRequest.issuer")) { + revRequestIssuer = val; + } else if (name.equals("revRequest.serial")) { + revRequestSerial = val; + } else if (name.equals("revRequest.reason")) { + revRequestReason = val; + } else if (name.equals("revRequest.sharedSecret")) { + revRequestSharedSecret = val; + } else if (name.equals("revRequest.comment")) { + revRequestComment = val; + } else if (name.equals("revRequest.invalidityDatePresent")) { + revRequestInvalidityDatePresent = val; + } else if (name.equals("revRequest.nickname")) { + revCertNickname = val; + } else if (name.equals("identityProof.enable")) { + identityProofEnable = val; + } else if (name.equals("identityProof.sharedSecret")) { + identityProofSharedSecret = val; + } else if (name.equals("popLinkWitness.enable")) { + popLinkWitnessEnable = val; + } else if (name.equals("LraPopWitness.enable")) { + lraPopWitnessEnable = val; + } else if (name.equals("LraPopWitness.bodyPartIDs")) { + bodyPartIDs = val; + } else if (name.equals("numRequests")) { + numRequests = val; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + printUsage(); + } + + if (ifilename == null) { + System.out.println("Missing input filename for PKCS10 or CRMF."); + printUsage(); + } + + int num = 0; + if (numRequests == null) { + System.out.println("Missing numRequests."); + printUsage(); + } else { + try { + num = Integer.parseInt(numRequests); + } catch (Exception ee) { + System.out.println("numRequests must be integer"); + System.exit(1); + } + } + + StringTokenizer tokenizer = new StringTokenizer(ifilename, " "); + String[] ifiles = new String[num]; + for (int i = 0; i < num; i++) { + String ss = (String) tokenizer.nextToken(); + ifiles[i] = ss; + if (ss == null) { + System.out.println("Missing input file for the request."); + System.exit(1); + } + } + + if (ofilename == null) { + System.out.println("Missing output filename for the CMC request."); + printUsage(); + } + + if (format == null) { + System.out.println("Missing format."); + printUsage(); + } + + if (password == null) { + System.out.println("Missing password."); + printUsage(); + } + + if (nickname == null) { + System.out.println("Missing nickname."); + printUsage(); + } + + try { + // initialize CryptoManager + if (dbdir == null) + dbdir = "."; + String mPrefix = ""; + System.out.println("cert/key prefix = " + mPrefix); + System.out.println("path = " + dbdir); + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(dbdir, mPrefix, + mPrefix, "secmod.db"); + + CryptoManager.initialize(vals); + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = cm.getInternalKeyStorageToken(); + Password pass = new Password(password.toCharArray()); + + token.login(pass); + X509Certificate signerCert = null; + + signerCert = cm.findCertByNickname(nickname); + + String[] requests = new String[num]; + for (int i = 0; i < num; i++) { + BufferedReader inputBlob = null; + try { + inputBlob = new BufferedReader(new InputStreamReader( + new BufferedInputStream(new FileInputStream(ifiles[i])))); + } catch (FileNotFoundException e) { + System.out.println("CMCRequest: can't find file " + + ifiles[i] + ":\n" + e); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + // (3) Read the entire contents of the specified BASE 64 encoded + // blob into a String() object throwing away any + // headers beginning with HEADER and any trailers beginning + // with TRAILER + String asciiBASE64BlobChunk = new String(); + String asciiBASE64Blob = new String(); + + try { + while ((asciiBASE64BlobChunk = inputBlob.readLine()) != null) { + if (!(asciiBASE64BlobChunk.startsWith(HEADER)) && + !(asciiBASE64BlobChunk.startsWith(TRAILER))) { + asciiBASE64Blob += asciiBASE64BlobChunk.trim(); + } + } + requests[i] = asciiBASE64Blob; + } catch (IOException e) { + System.out.println("CMCRequest: Unexpected BASE64 " + + "encoded error encountered in readLine():\n" + + e); + } + // (4) Close the DataInputStream() object + try { + inputBlob.close(); + } catch (IOException e) { + System.out.println("CMCRequest(): Unexpected BASE64 " + + "encoded error encountered in close():\n" + e); + } + } + + SEQUENCE controlSeq = new SEQUENCE(); + int bpid = 1; + if (confirmCertEnable.equalsIgnoreCase("true")) { + if (confirmCertIssuer.length() == 0 || confirmCertSerial.length() == 0) { + System.out.println("Illegal parameters for confirm certificate acceptance control"); + printUsage(); + System.exit(1); + } + bpid = addConfirmCertAttr(bpid, controlSeq, confirmCertIssuer, confirmCertSerial); + } + + if (lraPopWitnessEnable.equalsIgnoreCase("true")) { + if (bodyPartIDs.length() == 0) { + System.out.println("Illegal parameters for Lra Pop Witness control"); + printUsage(); + System.exit(1); + } + + bpid = addLraPopWitnessAttr(bpid, controlSeq, bodyPartIDs); + } + + if (getCertEnable.equalsIgnoreCase("true")) { + if (getCertIssuer.length() == 0 || getCertSerial.length() == 0) { + System.out.println("Illegal parameters for get certificate control"); + printUsage(); + System.exit(1); + } + + bpid = addGetCertAttr(bpid, controlSeq, getCertIssuer, getCertSerial); + } + + if (dataReturnEnable.equalsIgnoreCase("true")) { + if (dataReturnData.length() == 0) { + System.out.println("Illegal parameters for data return control"); + printUsage(); + System.exit(1); + } + + bpid = addDataReturnAttr(bpid, controlSeq, dataReturnData); + } + + if (senderNonceEnable.equalsIgnoreCase("true")) + bpid = addSenderNonceAttr(bpid, controlSeq, senderNonce); + + if (popLinkWitnessEnable.equalsIgnoreCase("true")) + bpid = addPopLinkWitnessAttr(bpid, controlSeq); + + SEQUENCE otherMsgSeq = new SEQUENCE(); + if (revRequestEnable.equalsIgnoreCase("true")) { + if (revRequestIssuer.length() == 0 || revRequestSerial.length() == 0 || + revRequestReason.length() == 0) { + System.out.println("Illegal parameters for revRequest control"); + printUsage(); + System.exit(1); + } + + bpid = addRevRequestAttr(bpid, controlSeq, otherMsgSeq, revCertNickname, + revRequestIssuer, revRequestSerial, revRequestReason, revRequestSharedSecret, + revRequestComment, revRequestInvalidityDatePresent, cm); + } + + ContentInfo cmcblob = getCMCBlob(signerCert, nickname, requests, format, + cm, transactionMgtEnable, transactionMgtId, identityProofEnable, + identityProofSharedSecret, controlSeq, otherMsgSeq, bpid); + + // (6) Finally, print the actual CMC blob to the + // specified output file + FileOutputStream os = null; + try { + os = new FileOutputStream(ofilename); + cmcblob.encode(os); + System.out.println(""); + System.out.println(""); + System.out.println("The CMC enrollment request in binary format is stored in " + + ofilename + "."); + } catch (IOException e) { + System.out.println("CMCRequest: unable to open file " + ofilename + + " for writing:\n" + e); + } + + try { + os.close(); + } catch (IOException e) { + System.out.println("CMCRequest: Unexpected error " + + "encountered while attempting to close() " + + "\n" + e); + } + + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/CMCResponse.java b/base/java-tools/src/com/netscape/cmstools/CMCResponse.java new file mode 100644 index 000000000..4d68dd151 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/CMCResponse.java @@ -0,0 +1,234 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import netscape.security.util.CertPrettyPrint; +import netscape.security.x509.X509CertImpl; + +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.pkix.cert.Certificate; +import org.mozilla.jss.pkix.cmc.CMCStatusInfo; +import org.mozilla.jss.pkix.cmc.OtherInfo; +import org.mozilla.jss.pkix.cmc.PendInfo; +import org.mozilla.jss.pkix.cmc.ResponseBody; +import org.mozilla.jss.pkix.cmc.TaggedAttribute; +import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo; + +/** + * Tool for parsing a CMC response + * + * <P> + * + * @version $Revision$, $Date$ + * + */ +public class CMCResponse { + + public CMCResponse() { + } + + public static void printOutput(String path, String filename) { + byte[] bb = new byte[10000]; + FileInputStream fis = null; + try { + fis = new FileInputStream(filename); + while (fis.available() > 0) + fis.read(bb, 0, 10000); + } catch (Exception e) { + System.out.println("Error reading the response. Exception: " + e.toString()); + System.exit(1); + } + + try { + ByteArrayInputStream bis = new ByteArrayInputStream(bb); + org.mozilla.jss.pkix.cms.ContentInfo cii = (org.mozilla.jss.pkix.cms.ContentInfo) + org.mozilla.jss.pkix.cms.ContentInfo.getTemplate().decode(bis); + + org.mozilla.jss.pkix.cms.SignedData cmcFullResp = + (org.mozilla.jss.pkix.cms.SignedData) cii.getInterpretedContent(); + + String content = ""; + if (cmcFullResp.hasCertificates()) { + SET certs = cmcFullResp.getCertificates(); + int numCerts = certs.size(); + + for (int i = 0; i < numCerts; i++) { + Certificate cert = (Certificate) certs.elementAt(i); + X509CertImpl certImpl = new X509CertImpl(ASN1Util.encode(cert)); + CertPrettyPrint print = new CertPrettyPrint(certImpl); + content += print.toString(Locale.getDefault()); + } + } + + System.out.println("Certificates: "); + System.out.println(content); + System.out.println(""); + EncapsulatedContentInfo ci = cmcFullResp.getContentInfo(); + OBJECT_IDENTIFIER id = ci.getContentType(); + OBJECT_IDENTIFIER dataid = new OBJECT_IDENTIFIER("1.2.840.113549.1.7.1"); + if (!id.equals(OBJECT_IDENTIFIER.id_cct_PKIResponse) && !id.equals(dataid)) { + System.out.println("Invalid CMC Response Format"); + } + + if (!ci.hasContent()) + return; + + OCTET_STRING content1 = ci.getContent(); + ByteArrayInputStream bbis = new ByteArrayInputStream(content1.toByteArray()); + ResponseBody responseBody = (ResponseBody) (new ResponseBody.Template()).decode(bbis); + SEQUENCE controlSequence = responseBody.getControlSequence(); + + int numControls = controlSequence.size(); + System.out.println("Number of controls is " + numControls); + + for (int i = 0; i < numControls; i++) { + TaggedAttribute taggedAttr = (TaggedAttribute) controlSequence.elementAt(i); + OBJECT_IDENTIFIER type = taggedAttr.getType(); + + if (type.equals(OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo)) { + System.out.println("Control #" + i + ": CMCStatusInfo"); + System.out.println(" OID: " + type.toString()); + SET sts = taggedAttr.getValues(); + int numSts = sts.size(); + for (int j = 0; j < numSts; j++) { + CMCStatusInfo cst = (CMCStatusInfo) ASN1Util.decode(CMCStatusInfo.getTemplate(), + ASN1Util.encode(sts.elementAt(j))); + SEQUENCE seq = cst.getBodyList(); + + String s = " BodyList: "; + for (int k = 0; k < seq.size(); k++) { + INTEGER n = (INTEGER) seq.elementAt(k); + s = s + n.toString() + " "; + } + System.out.println(s); + int st = cst.getStatus(); + if (st != CMCStatusInfo.SUCCESS && st != CMCStatusInfo.CONFIRM_REQUIRED) { + String stString = cst.getStatusString(); + if (stString != null) + System.out.println(" Status String: " + stString); + OtherInfo oi = cst.getOtherInfo(); + OtherInfo.Type t = oi.getType(); + if (t == OtherInfo.FAIL) + System.out.println(" OtherInfo type: FAIL"); + else if (t == OtherInfo.PEND) { + System.out.println(" OtherInfo type: PEND"); + PendInfo pi = oi.getPendInfo(); + if (pi.getPendTime() != null) { + String datePattern = "dd/MMM/yyyy:HH:mm:ss z"; + SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern); + Date d = pi.getPendTime().toDate(); + System.out.println(" Date: " + dateFormat.format(d)); + } + } + } else if (st == CMCStatusInfo.SUCCESS) { + System.out.println(" Status: SUCCESS"); + } + } + } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_transactionId)) { + System.out.println("Control #" + i + ": CMC Transaction Id"); + System.out.println(" OID: " + type.toString()); + SET transIds = taggedAttr.getValues(); + INTEGER num = (INTEGER) (ASN1Util.decode(INTEGER.getTemplate(), + ASN1Util.encode(transIds.elementAt(0)))); + System.out.println(" INTEGER: " + num); + } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_recipientNonce)) { + System.out.println("Control #" + i + ": CMC Recipient Nonce"); + System.out.println(" OID: " + type.toString()); + SET recipientN = taggedAttr.getValues(); + OCTET_STRING str = + (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(), + ASN1Util.encode(recipientN.elementAt(0)))); + byte b[] = str.toByteArray(); + String s = " Value: "; + for (int m = 0; m < b.length; m++) { + s = s + b[m] + " "; + } + System.out.println(s); + } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_senderNonce)) { + System.out.println("Control #" + i + ": CMC Sender Nonce"); + System.out.println(" OID: " + type.toString()); + SET senderN = taggedAttr.getValues(); + OCTET_STRING str = + (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(), + ASN1Util.encode(senderN.elementAt(0)))); + byte b[] = str.toByteArray(); + String s = " Value: "; + for (int m = 0; m < b.length; m++) { + s = s + b[m] + " "; + } + System.out.println(s); + } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_dataReturn)) { + System.out.println("Control #" + i + ": CMC Data Return"); + System.out.println(" OID: " + type.toString()); + SET dataReturn = taggedAttr.getValues(); + OCTET_STRING str = + (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(), + ASN1Util.encode(dataReturn.elementAt(0)))); + byte b[] = str.toByteArray(); + String s = " Value: "; + for (int m = 0; m < b.length; m++) { + s = s + b[m] + " "; + } + System.out.println(s); + } + } + } catch (Exception e) { + System.out.println("Error found in the response. Exception: " + e.toString()); + System.exit(1); + + } + } + + private static void printUsage() { + System.out.println(""); + System.out.println( + "Usage: CMCResponse -d <pathname for cert8.db> -i <pathname for CMC response in binary format> "); + } + + public static void main(String args[]) { + String filename = null, path = null; + if (args.length != 4) { + printUsage(); + System.exit(1); + } + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-d")) + path = args[i + 1]; + else if (args[i].equals("-i")) + filename = args[i + 1]; + } + + if (filename == null || path == null) { + printUsage(); + System.exit(1); + } + printOutput(path, filename); + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/CMCRevoke.java b/base/java-tools/src/com/netscape/cmstools/CMCRevoke.java new file mode 100644 index 000000000..f29984713 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/CMCRevoke.java @@ -0,0 +1,426 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Date; + +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.NoSuchTokenException; +import org.mozilla.jss.asn1.ANY; +import org.mozilla.jss.asn1.ENUMERATED; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.asn1.UTF8String; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.DigestAlgorithm; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.SignatureAlgorithm; +import org.mozilla.jss.crypto.TokenException; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkix.cmc.PKIData; +import org.mozilla.jss.pkix.cmc.TaggedAttribute; +import org.mozilla.jss.pkix.cms.ContentInfo; +import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo; +import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber; +import org.mozilla.jss.pkix.cms.SignedData; +import org.mozilla.jss.pkix.cms.SignerIdentifier; +import org.mozilla.jss.pkix.cms.SignerInfo; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; +import org.mozilla.jss.pkix.primitive.Name; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.Utils; + +/** + * Tool for signing a CMC revocation request with an agent's certificate. + * + * <P> + * + * @version $Revision$, $Date$ + */ +public class CMCRevoke { + public static final int ARGC = 7; + private static final String CERTDB = "cert8.db"; + private static final String KEYDB = "key3.db"; + public static final String HEADER = "-----BEGIN NEW CERTIFICATE REQUEST-----"; + public static final String TRAILER = "-----END NEW CERTIFICATE REQUEST-----"; + static String dValue = null, nValue = null, iValue = null, sValue = null, mValue = null, hValue = null, + cValue = null; + + public static final String CMS_BASE_CA_SIGNINGCERT_NOT_FOUND = "CA signing certificate not found"; + public static final String PR_INTERNAL_TOKEN_NAME = "internal"; + public static final String PR_REQUEST_CMC = "CMC"; + + static String cleanArgs(String s) { + if (s.startsWith("\"") && s.endsWith("\"")) + return s.substring(1, s.length() - 2); + else if (s.startsWith("\'") && s.endsWith("\'")) + return new String(s.substring(1, s.length() - 2)); + else + return s; + } + + /** + * Creates a new instance of CMCRevoke. + */ + public static void main(String[] s) { + + // default path is "." + String mPath = "."; + // default prefix is "" + String mPrefix = ""; + + boolean bWrongParam = false; + + // (1) Check that two arguments were submitted to the program + if (s.length != (ARGC) && s.length != (ARGC - 1)) { + + bWrongParam = true; + System.out.println("Wrong number of parameters:" + s.length); + System.out.println("Usage: CMCRevoke " + + "-d<dir to cert8.db, key3.db> " + + "-n<nickname> " + + "-i<issuerName> " + + "-s<serialName> " + + "-m<reason to revoke> " + + "-h<password to db> " + + "-c<comment> "); + for (int i = 0; i < s.length; i++) { + System.out.println(i + ":" + s[i]); + } + } else { + int length; + int i; + + length = s.length; + for (i = 0; i < length; i++) { + if (s[i].startsWith("-d")) { + dValue = cleanArgs(s[i].substring(2)); + } else if (s[i].startsWith("-n")) { + nValue = cleanArgs(s[i].substring(2)); + } else if (s[i].startsWith("-i")) { + iValue = cleanArgs(s[i].substring(2)); + } else if (s[i].startsWith("-s")) { + sValue = cleanArgs(s[i].substring(2)); + } else if (s[i].startsWith("-m")) { + mValue = cleanArgs(s[i].substring(2)); + } else if (s[i].startsWith("-h")) { + hValue = cleanArgs(s[i].substring(2)); + } else if (s[i].startsWith("-c")) { + cValue = cleanArgs(s[i].substring(2)); + } + + } + // optional parameter + if (cValue == null) + cValue = new String(); + if (dValue == null + || nValue == null || iValue == null || sValue == null || mValue == null || hValue == null) + bWrongParam = true; + else if (dValue.length() == 0 || nValue.length() == 0 || iValue.length() == 0 || + sValue.length() == 0 || mValue.length() == 0 || hValue.length() == 0) + bWrongParam = true; + + if (bWrongParam == true) { + System.out.println("Usage: CMCRevoke " + + "-d<dir to cert8.db, key3.db> " + + "-n<nickname> " + + "-i<issuerName> " + + "-s<serialName> " + + "-m<reason to revoke> " + + "-h<password to db> " + + "-c<comment> "); + for (i = 0; i < s.length; i++) { + System.out.println(i + ":" + s[i]); + } + System.exit(0); + } + + try { + // initialize CryptoManager + mPath = dValue; + System.out.println("cert/key prefix = " + mPrefix); + System.out.println("path = " + mPath); + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(mPath, mPrefix, mPrefix, "secmod.db"); + + CryptoManager.initialize(vals); + + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = cm.getInternalKeyStorageToken(); + Password pass = new Password(hValue.toCharArray()); + + token.login(pass); + X509Certificate signerCert = null; + + signerCert = cm.findCertByNickname(nValue); + String outBlob = createRevokeReq(signerCert, cm, nValue); + + printCMCRevokeRequest(outBlob); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + return; + } + } + + /** + * printout CMC revoke request in Base64 encoding to a file CMCRevoke.out + * <P> + * + * @param asciiBASE64Blob the ascii string of the request + */ + static void printCMCRevokeRequest(String asciiBASE64Blob) { + + // (6) Finally, print the actual CMCSigning blob to the + // specified output file + FileOutputStream outputBlob = null; + + try { + outputBlob = new FileOutputStream("CMCRevoke.out"); + } catch (IOException e) { + System.out.println("CMCSigning: unable to open file CMCRevoke.out for writing:\n" + e); + return; + } + + System.out.println(HEADER); + System.out.println(asciiBASE64Blob + TRAILER); + try { + asciiBASE64Blob = HEADER + "\n" + asciiBASE64Blob + TRAILER; + outputBlob.write(asciiBASE64Blob.getBytes()); + } catch (IOException e) { + System.out.println("CMCSigning: I/O error " + + "encountered during write():\n" + + e); + } + + try { + outputBlob.close(); + } catch (IOException e) { + System.out.println("CMCSigning: Unexpected error " + + "encountered while attempting to close() " + + "\n" + e); + } + } + + /** + * getCertificate find the certicate inside the token by its nickname. + * <P> + * + * @param manager the CrytoManager + * @param tokenname the name of the token. it's set to "internal". + * @param nickname the nickname of the certificate inside the token. + * @return the X509Certificate. + */ + public static X509Certificate getCertificate(CryptoManager manager, String tokenname, + String nickname) throws NoSuchTokenException, + Exception, TokenException { + CryptoToken token = null; + + if (tokenname.equals(PR_INTERNAL_TOKEN_NAME)) { + token = manager.getInternalKeyStorageToken(); + } else { + token = manager.getTokenByName(tokenname); + } + StringBuffer certname = new StringBuffer(); + + if (!token.equals(manager.getInternalKeyStorageToken())) { + certname.append(tokenname); + certname.append(":"); + } + certname.append(nickname); + try { + return manager.findCertByNickname(certname.toString()); + } catch (ObjectNotFoundException e) { + throw new Exception(CMS_BASE_CA_SIGNINGCERT_NOT_FOUND); + } + } + + /** + * createRevokeReq create and return the revocation request. + * <P> + * + * @param signerCert the certificate of the authorized signer of the CMC revocation request. + * @param manager the crypto manger. + * @param nValue the nickname of the certificate inside the token. + * @return the CMC revocation request encoded in base64 + */ + static String createRevokeReq(X509Certificate signerCert, CryptoManager manager, String nValue) { + + java.security.PrivateKey privKey = null; + SignerIdentifier si = null; + ContentInfo fullEnrollmentReq = null; + String tokenname = "internal"; + String asciiBASE64Blob = new String(); + + try { + + BigInteger serialno = signerCert.getSerialNumber(); + byte[] certB = signerCert.getEncoded(); + X509CertImpl impl = new X509CertImpl(certB); + X500Name issuerName = (X500Name) impl.getIssuerDN(); + byte[] issuerByte = issuerName.getEncoded(); + ByteArrayInputStream istream = new ByteArrayInputStream(issuerByte); + + Name issuer = (Name) Name.getTemplate().decode(istream); + IssuerAndSerialNumber ias = new IssuerAndSerialNumber(issuer, new INTEGER(serialno.toString())); + + si = new SignerIdentifier(SignerIdentifier.ISSUER_AND_SERIALNUMBER, ias, null); + X509Certificate cert = getCertificate(manager, tokenname, nValue); + + privKey = manager.findPrivKeyByCert(cert); + + if (privKey == null) { + System.out.println("CMCRevoke::createRevokeReq() - " + + "privKey is null!"); + return ""; + } + + int bpid = 1; + // Add some control sequence + // Verisign has transactionID,senderNonce + SEQUENCE controlSeq = new SEQUENCE(); + + Date date = new Date(); + String salt = "lala123" + date.toString(); + byte[] dig; + + try { + MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); + + dig = SHA1Digest.digest(salt.getBytes()); + } catch (NoSuchAlgorithmException ex) { + dig = salt.getBytes(); + } + String sn = Utils.base64encode(dig); + + TaggedAttribute senderNonce = + new TaggedAttribute(new INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_senderNonce, + new OCTET_STRING(sn.getBytes())); + + controlSeq.addElement(senderNonce); + + Name subjectName = new Name(); + + subjectName.addCommonName(iValue); + org.mozilla.jss.pkix.cmmf.RevRequest lRevokeRequest = + new org.mozilla.jss.pkix.cmmf.RevRequest(new ANY((new X500Name(iValue)).getEncoded()), + new INTEGER(sValue), + //org.mozilla.jss.pkix.cmmf.RevRequest.unspecified, + new ENUMERATED((new Integer(mValue)).longValue()), + //new GeneralizedTime(new Date(lValue)), + new OCTET_STRING(hValue.getBytes()), + new UTF8String(cValue.toCharArray())); + //byte[] encoded = ASN1Util.encode(lRevokeRequest); + //org.mozilla.jss.asn1.ASN1Template template = new org.mozilla.jss.pkix.cmmf.RevRequest.Template(); + //org.mozilla.jss.pkix.cmmf.RevRequest revRequest = (org.mozilla.jss.pkix.cmmf.RevRequest) + // template.decode(new java.io.ByteArrayInputStream( + // encoded)); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + //lRevokeRequest.encode(os); // khai + TaggedAttribute revokeRequestTag = + new TaggedAttribute(new INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_revokeRequest, + lRevokeRequest); + + controlSeq.addElement(revokeRequestTag); + PKIData pkidata = new PKIData(controlSeq, new SEQUENCE(), new SEQUENCE(), new SEQUENCE()); + + EncapsulatedContentInfo ci = new EncapsulatedContentInfo(OBJECT_IDENTIFIER.id_cct_PKIData, pkidata); + // SHA1 is the default digest Alg for now. + DigestAlgorithm digestAlg = null; + SignatureAlgorithm signAlg = SignatureAlgorithm.RSASignatureWithSHA1Digest; + org.mozilla.jss.crypto.PrivateKey.Type signingKeyType = + ((org.mozilla.jss.crypto.PrivateKey) privKey).getType(); + + if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.DSA)) + signAlg = SignatureAlgorithm.DSASignatureWithSHA1Digest; + MessageDigest SHADigest = null; + byte[] digest = null; + + try { + SHADigest = MessageDigest.getInstance("SHA1"); + digestAlg = DigestAlgorithm.SHA1; + + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + + pkidata.encode((OutputStream) ostream); + digest = SHADigest.digest(ostream.toByteArray()); + } catch (NoSuchAlgorithmException e) { + } + SignerInfo signInfo = new SignerInfo(si, null, null, OBJECT_IDENTIFIER.id_cct_PKIData, digest, signAlg, + (org.mozilla.jss.crypto.PrivateKey) privKey); + SET signInfos = new SET(); + + signInfos.addElement(signInfo); + + SET digestAlgs = new SET(); + + if (digestAlg != null) { + AlgorithmIdentifier ai = new AlgorithmIdentifier(digestAlg.toOID(), null); + + digestAlgs.addElement(ai); + } + + org.mozilla.jss.crypto.X509Certificate[] agentChain = manager.buildCertificateChain(signerCert); + SET certs = new SET(); + + for (int i = 0; i < agentChain.length; i++) { + ANY certificate = new ANY(agentChain[i].getEncoded()); + + certs.addElement(certificate); + } + SignedData req = new SignedData(digestAlgs, ci, certs, null, signInfos); + + fullEnrollmentReq = new ContentInfo(req); + + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bs); + + if (fullEnrollmentReq != null) { + // format is PR_REQUEST_CMC + fullEnrollmentReq.encode(os); + ps.print(Utils.base64encode(os.toByteArray())); + ////fullEnrollmentReq.print(ps); // no header/trailer + } + + asciiBASE64Blob = bs.toString(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + return asciiBASE64Blob; + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java new file mode 100644 index 000000000..c1d463cdb --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java @@ -0,0 +1,620 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.security.KeyPair; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import netscape.security.x509.X500Name; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.BIT_STRING; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.PrintableString; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.IVParameterSpec; +import org.mozilla.jss.crypto.KeyGenAlgorithm; +import org.mozilla.jss.crypto.KeyGenerator; +import org.mozilla.jss.crypto.KeyPairAlgorithm; +import org.mozilla.jss.crypto.KeyPairGenerator; +import org.mozilla.jss.crypto.KeyWrapAlgorithm; +import org.mozilla.jss.crypto.KeyWrapper; +import org.mozilla.jss.crypto.Signature; +import org.mozilla.jss.crypto.SignatureAlgorithm; +import org.mozilla.jss.crypto.SymmetricKey; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkix.crmf.CertReqMsg; +import org.mozilla.jss.pkix.crmf.CertRequest; +import org.mozilla.jss.pkix.crmf.CertTemplate; +import org.mozilla.jss.pkix.crmf.EncryptedKey; +import org.mozilla.jss.pkix.crmf.EncryptedValue; +import org.mozilla.jss.pkix.crmf.PKIArchiveOptions; +import org.mozilla.jss.pkix.crmf.POPOSigningKey; +import org.mozilla.jss.pkix.crmf.ProofOfPossession; +import org.mozilla.jss.pkix.primitive.AVA; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; +import org.mozilla.jss.pkix.primitive.Name; +import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.HMACDigest; +import com.netscape.cmsutil.util.Utils; + +/** + * A command-line utility used to generate a Certificate Request Message + * Format (CRMF) request with proof of possesion (POP). + * + * Usage: + * + * <pre> + * CRMFPopClient TOKEN_PWD + * PROFILE_NAME HOST PORT USER_NAME REQUESTOR_NAME + * POP_OPTION + * SUBJECT_DN [OUTPUT_CERT_REQ] + * + * --- or --- + * + * CRMFPopClient TOKEN_PWD + * POP_OPTION + * OUTPUT_CERT_REQ SUBJECT_DN + * + * + * where POP_OPTION can be [POP_SUCCESS or POP_FAIL or POP_NONE] + * </pre> + * <p> + * Examples: + * + * <pre> + * CRMFPopClient password123 + * caEncUserCert host.example.com 1026 MyUid MyUid + * [POP_SUCCESS or POP_FAIL or POP_NONE] + * CN=MyTest,C=US,UID=MyUid + * + * --- or --- + * + * CRMFPopClient password123 + * caEncUserCert host.example.com 1026 joe joe + * [POP_SUCCESS or POP_FAIL or POP_NONE] + * CN=MyTest,C=US,UID=MyUid OUTPUT_CERT_REQ + * + * --- or --- + * + * CRMFPopClient password123 + * [POP_SUCCESS or POP_FAIL or POP_NONE] + * OUTPUT_CERT_REQ CN=MyTest,C=US,UID=MyUid + * </pre> + * <p> + * + * <pre> + * IMPORTANT: The file "transport.txt" needs to be created to contain the + * transport certificate in its base64 encoded format. This + * file should consist of one line containing a single certificate + * in base64 encoded format with the header and footer removed. + * </pre> + * <p> + * + * @version $Revision$, $Date$ + */ +public class CRMFPopClient { + + private static void usage() { + System.out.println(""); + System.out.println("Description: A command-line utility used to generate a"); + System.out.println(" Certificate Request Message Format (CRMF)"); + System.out.println(" request with proof of possesion (POP).\n\n"); + System.out.println("Usage:"); + System.out.println(""); + System.out.println(" CRMFPopClient TOKEN_PWD"); + System.out.println(" PROFILE_NAME HOST PORT USER_NAME REQUESTOR_NAME"); + System.out.println(" POP_OPTION"); + System.out.println(" SUBJECT_DN [OUTPUT_CERT_REQ] \n"); + System.out.println(" --- or ---\n"); + System.out.println(" CRMFPopClient TOKEN_PWD"); + System.out.println(" POP_OPTION"); + System.out.println(" OUTPUT_CERT_REQ SUBJECT_DN\n\n"); + System.out.println(" where POP_OPTION can be [POP_SUCCESS or POP_FAIL or POP_NONE]\n\n"); + System.out.println("Examples:"); + System.out.println(""); + System.out.println(" CRMFPopClient password123"); + System.out.println(" caEncUserCert host.example.com 1026 MyUid MyUid"); + System.out.println(" [POP_SUCCESS or POP_FAIL or POP_NONE]"); + System.out.println(" CN=MyTest,C=US,UID=MyUid\n"); + System.out.println(" --- or ---\n"); + System.out.println(" CRMFPopClient password123"); + System.out.println(" caEncUserCert host.example.com 1026 MyUid myUid"); + System.out.println(" [POP_SUCCESS or POP_FAIL or POP_NONE]"); + System.out.println(" CN=MyTest,C=US,UID=MyUid OUTPUT_CERT_REQ\n"); + System.out.println(" --- or ---\n"); + System.out.println(" CRMFPopClient password123"); + System.out.println(" [POP_SUCCESS or POP_FAIL or POP_NONE]"); + System.out.println(" OUTPUT_CERT_REQ CN=MyTest,C=US,UID=MyUid"); + System.out.println("\n"); + System.out.println("IMPORTANT: The file \"transport.txt\" needs to be created to contain the"); + System.out.println(" transport certificate in its base64 encoded format. This"); + System.out.println(" file should consist of one line containing a single certificate"); + System.out.println(" in base64 encoded format with the header and footer removed.\n"); + } + + private static int getRealArgsLength(String args[]) { + + int len = args.length; + + String curArg = ""; + int finalLen = len; + + for (int i = 0; i < len; i++) { + + curArg = args[i]; + // System.out.println("arg[" + i + "] " + curArg); + + if (curArg == null || curArg.equalsIgnoreCase("")) { + finalLen--; + } + + } + + //System.out.println("getRealArgsLength: returning " + finalLen); + + if (finalLen < 0) + finalLen = 0; + + return finalLen; + + } + + public static void main(String args[]) { + + int argsLen = getRealArgsLength(args); + + // System.out.println("args length " + argsLen); + + System.out.println("\n\nProof Of Possession Utility...."); + System.out.println(""); + + if (argsLen == 0 || (argsLen != 8 && argsLen != 9 && argsLen != 10 && argsLen != 4)) { + usage(); + return; + } + + String DB_DIR = "./"; + String TOKEN_PWD = args[0]; + int KEY_LEN = 1024; + + int PORT = 0; + String USER_NAME = null; + String REQUESTOR_NAME = null; + String PROFILE_NAME = null; + + String HOST = null; + String SUBJ_DN = null; + + if (argsLen >= 8) { + PROFILE_NAME = args[1]; + HOST = args[2]; + + PORT = Integer.parseInt(args[3]); + + USER_NAME = args[4]; + REQUESTOR_NAME = args[5]; + + SUBJ_DN = args[7]; + + } + + String POP_OPTION = null; + String OUTPUT_CERT_REQ = null; + + if (argsLen == 4) + POP_OPTION = args[1]; + else + POP_OPTION = args[6]; + + int doServerHit = 1; + + if (argsLen >= 9) { + OUTPUT_CERT_REQ = args[8]; + } + + if (argsLen == 4) { + doServerHit = 0; + OUTPUT_CERT_REQ = args[2]; + SUBJ_DN = args[3]; + } + + int dont_do_pop = 0; + + if (POP_OPTION.equals("POP_NONE")) { + dont_do_pop = 1; + } + + URL url = null; + URLConnection conn = null; + InputStream is = null; + BufferedReader reader = null; + KeyPair pair = null; + + boolean foundTransport = false; + String transportCert = null; + try { + BufferedReader br = new BufferedReader(new FileReader("./transport.txt")); + transportCert = br.readLine(); + foundTransport = true; + } catch (Exception e) { + System.out.println("ERROR: cannot find ./transport.txt, so no key archival"); + + return; + } + + try { + CryptoManager.initialize(DB_DIR); + } catch (Exception e) { + // it is ok if it is already initialized + System.out.println("INITIALIZATION ERROR: " + e.toString()); + // return; + } + + try { + CryptoManager manager = CryptoManager.getInstance(); + String token_pwd = TOKEN_PWD; + CryptoToken token = manager.getInternalKeyStorageToken(); + Password password = new Password(token_pwd.toCharArray()); + try { + token.login(password); + } catch (Exception e) { + //System.out.println("login Exception: " + e.toString()); + if (!token.isLoggedIn()) { + token.initPassword(password, password); + } + } + + System.out.println("."); //"done with cryptomanager"); + + KeyPairGenerator kg = token.getKeyPairGenerator( + KeyPairAlgorithm.RSA); + kg.initialize(KEY_LEN); + + String profileName = PROFILE_NAME; + pair = kg.genKeyPair(); + + System.out.println("."); //key pair generated"); + + // wrap private key + byte transport[] = Utils.base64decode(transportCert); + + X509Certificate tcert = manager.importCACertPackage(transport); + + byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; + + KeyGenerator kg1 = token.getKeyGenerator(KeyGenAlgorithm.DES3); + SymmetricKey sk = kg1.generate(); + + System.out.println("."); //before KeyWrapper"); + + // wrap private key using session + KeyWrapper wrapper1 = + token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + + System.out.println("."); //key wrapper created"); + + wrapper1.initWrap(sk, new IVParameterSpec(iv)); + + System.out.println("."); //key wrapper inited"); + byte key_data[] = wrapper1.wrap((org.mozilla.jss.crypto.PrivateKey) pair.getPrivate()); + + System.out.println("."); //key wrapper wrapped"); + + // wrap session using transport + KeyWrapper rsaWrap = token.getKeyWrapper( + KeyWrapAlgorithm.RSA); + + System.out.println("."); //got rsaWrapper"); + + rsaWrap.initWrap(tcert.getPublicKey(), null); + + System.out.println("."); //rsaWrap inited"); + + byte session_data[] = rsaWrap.wrap(sk); + + System.out.println("."); //rsaWrapped"); + + try { + // create CRMF + CertTemplate certTemplate = new CertTemplate(); + certTemplate.setVersion(new INTEGER(2)); + + Name n1 = getJssName(SUBJ_DN); + + Name n = new Name(); + + n.addCommonName("Me"); + n.addCountryName("US"); + n.addElement(new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), new PrintableString("MyUid"))); + + if (n1 != null) + certTemplate.setSubject(n1); + else + certTemplate.setSubject(n); + + certTemplate.setPublicKey(new SubjectPublicKeyInfo(pair.getPublic())); + // set extension + AlgorithmIdentifier algS = + new AlgorithmIdentifier(new OBJECT_IDENTIFIER("1.2.840.113549.3.7"), new OCTET_STRING(iv)); + EncryptedValue encValue = + new EncryptedValue(null, algS, new BIT_STRING(session_data, 0), null, null, new BIT_STRING( + key_data, 0)); + EncryptedKey key = new EncryptedKey(encValue); + PKIArchiveOptions opt = new PKIArchiveOptions(key); + SEQUENCE seq = new SEQUENCE(); + if (foundTransport) { + seq.addElement(new AVA(new OBJECT_IDENTIFIER("1.3.6.1.5.5.7.5.1.4"), opt)); + } + + // Add idPOPLinkWitness control + String secretValue = "testing"; + byte[] key1 = null; + byte[] finalDigest = null; + try { + MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); + key1 = SHA1Digest.digest(secretValue.getBytes()); + } catch (NoSuchAlgorithmException ex) { + } + + /* Example of adding the POP link witness control to CRMF */ + byte[] b = + { 0x10, 0x53, 0x42, 0x24, 0x1a, 0x2a, 0x35, 0x3c, + 0x7a, 0x52, 0x54, 0x56, 0x71, 0x65, 0x66, 0x4c, + 0x51, 0x34, 0x35, 0x23, 0x3c, 0x42, 0x43, 0x45, + 0x61, 0x4f, 0x6e, 0x43, 0x1e, 0x2a, 0x2b, 0x31, + 0x32, 0x34, 0x35, 0x36, 0x55, 0x51, 0x48, 0x14, + 0x16, 0x29, 0x41, 0x42, 0x43, 0x7b, 0x63, 0x44, + 0x6a, 0x12, 0x6b, 0x3c, 0x4c, 0x3f, 0x00, 0x14, + 0x51, 0x61, 0x15, 0x22, 0x23, 0x5f, 0x5e, 0x69 }; + + try { + MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); + HMACDigest hmacDigest = new HMACDigest(SHA1Digest, key1); + hmacDigest.update(b); + finalDigest = hmacDigest.digest(); + } catch (NoSuchAlgorithmException ex) { + } + + OCTET_STRING ostr = new OCTET_STRING(finalDigest); + seq.addElement(new AVA(OBJECT_IDENTIFIER.id_cmc_idPOPLinkWitness, ostr)); + CertRequest certReq = new CertRequest(new INTEGER(1), certTemplate, seq); + + System.out.println("."); //CertRequest created"); + + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + certReq.encode(bo); + byte[] toBeVerified = bo.toByteArray(); + + byte signature[]; + + System.out.println("."); //CertRequest encoded"); + + Signature signer = token.getSignatureContext( + SignatureAlgorithm.RSASignatureWithMD5Digest); + + System.out.println("."); //signer created"); + + signer.initSign((org.mozilla.jss.crypto.PrivateKey) pair.getPrivate()); + + System.out.println("."); //signer inited"); + + System.out.println("."); //FAIL_OR_SUCC " + FAIL_OR_SUCC); + + if (POP_OPTION.equals("POP_SUCCESS")) { + System.out.println("Generating Legal POP Data....."); + signer.update(toBeVerified); + } else if (POP_OPTION.equals("POP_FAIL")) { + System.out.println("Generating Illegal POP Data....."); + signer.update(iv); + } else if (dont_do_pop == 1) { + System.out.println("Generating NO POP Data....."); + } + + System.out.println("."); //signer updated"); + + CertReqMsg crmfMsg = null; + + if (dont_do_pop == 0) { + signature = signer.sign(); + + System.out.println("Signature completed..."); + System.out.println(""); + + AlgorithmIdentifier algID = + new AlgorithmIdentifier(SignatureAlgorithm.RSASignatureWithMD5Digest.toOID(), null); + POPOSigningKey popoKey = new POPOSigningKey(null, algID, new BIT_STRING(signature, 0)); + + ProofOfPossession pop = ProofOfPossession.createSignature(popoKey); + + crmfMsg = new CertReqMsg(certReq, pop, null); + + } else { + crmfMsg = new CertReqMsg(certReq, null, null); + + } + + //crmfMsg.verify(); + + SEQUENCE s1 = new SEQUENCE(); + s1.addElement(crmfMsg); + byte encoded[] = ASN1Util.encode(s1); + + String Req1 = Utils.base64encode(encoded); + + if (OUTPUT_CERT_REQ != null) { + System.out.println("Generated Cert Request: ...... "); + System.out.println(""); + + System.out.println(Req1); + System.out.println(""); + System.out.println("End Request:"); + + if (doServerHit == 0) + return; + } + + String Req = URLEncoder.encode(Req1, "UTF-8"); + + // post PKCS10 + + url = + new URL("http://" + + HOST + ":" + PORT + "/ca/ee/ca/profileSubmit?cert_request_type=crmf&cert_request=" + + Req + "&renewal=false&uid=" + USER_NAME + "&xmlOutput=false&&profileId=" + + profileName + "&sn_uid=" + USER_NAME + "&SubId=profile&requestor_name=" + + REQUESTOR_NAME); + //System.out.println("Posting " + url); + + System.out.println(""); + System.out.println("Server Response....."); + System.out.println("--------------------"); + System.out.println(""); + + conn = url.openConnection(); + is = conn.getInputStream(); + reader = new BufferedReader(new InputStreamReader(is)); + String line = null; + while ((line = reader.readLine()) != null) { + System.out.println(line); + if (line.equals("CMS Enroll Request Success")) { + System.out.println("Enrollment Successful: ......"); + System.out.println(""); + } + } /* while */ + + } catch (Exception e) { + System.out.println("WARNING: " + e.toString()); + e.printStackTrace(); + } + } catch (Exception e) { + System.out.println("ERROR: " + e.toString()); + e.printStackTrace(); + } + } + + static Name getJssName(String dn) { + + X500Name x5Name = null; + + try { + x5Name = new X500Name(dn); + + } catch (IOException e) { + + System.out.println("Illegal Subject Name: " + dn + " Error: " + e.toString()); + System.out.println("Filling in default Subject Name......"); + return null; + } + + Name ret = new Name(); + + netscape.security.x509.RDN[] names = null; + + names = x5Name.getNames(); + + int nameLen = x5Name.getNamesLength(); + + // System.out.println("x5Name len: " + nameLen); + + netscape.security.x509.RDN cur = null; + + for (int i = 0; i < nameLen; i++) { + cur = names[i]; + + String rdnStr = cur.toString(); + + String[] split = rdnStr.split("="); + + if (split.length != 2) + continue; + + try { + + if (split[0].equals("UID")) { + + ret.addElement(new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), new PrintableString( + split[1]))); + // System.out.println("UID found : " + split[1]); + + } + + if (split[0].equals("C")) { + ret.addCountryName(split[1]); + // System.out.println("C found : " + split[1]); + continue; + + } + + if (split[0].equals("CN")) { + ret.addCommonName(split[1]); + // System.out.println("CN found : " + split[1]); + continue; + } + + if (split[0].equals("L")) { + ret.addLocalityName(split[1]); + // System.out.println("L found : " + split[1]); + continue; + } + + if (split[0].equals("O")) { + ret.addOrganizationName(split[1]); + // System.out.println("O found : " + split[1]); + continue; + } + + if (split[0].equals("ST")) { + ret.addStateOrProvinceName(split[1]); + // System.out.println("ST found : " + split[1]); + continue; + } + + if (split[0].equals("OU")) { + ret.addOrganizationalUnitName(split[1]); + // System.out.println("OU found : " + split[1]); + continue; + } + } catch (Exception e) { + System.out.println("Error constructing RDN: " + rdnStr + " Error: " + e.toString()); + + continue; + } + + } + + return ret; + + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/DRMTool.cfg b/base/java-tools/src/com/netscape/cmstools/DRMTool.cfg new file mode 100644 index 000000000..b43441e19 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/DRMTool.cfg @@ -0,0 +1,160 @@ +drmtool.ldif.caEnrollmentRequest._000=######################################## +drmtool.ldif.caEnrollmentRequest._001=## DRM CA Enrollment Request ## +drmtool.ldif.caEnrollmentRequest._002=######################################## +drmtool.ldif.caEnrollmentRequest._003=## ## +drmtool.ldif.caEnrollmentRequest._004=## NEVER allow 'DRMTOOL' the ability ## +drmtool.ldif.caEnrollmentRequest._005=## to change the CA 'naming context' ## +drmtool.ldif.caEnrollmentRequest._006=## data in the following fields: ## +drmtool.ldif.caEnrollmentRequest._007=## ## +drmtool.ldif.caEnrollmentRequest._008=## extdata-auth--005ftoken;uid ## +drmtool.ldif.caEnrollmentRequest._009=## extdata-auth--005ftoken;userid ## +drmtool.ldif.caEnrollmentRequest._010=## extdata-updatedby ## +drmtool.ldif.caEnrollmentRequest._011=## ## +drmtool.ldif.caEnrollmentRequest._012=## NEVER allow 'DRMTOOL' the ability ## +drmtool.ldif.caEnrollmentRequest._013=## to change CA 'numeric' data in ## +drmtool.ldif.caEnrollmentRequest._014=## the following fields: ## +drmtool.ldif.caEnrollmentRequest._015=## ## +drmtool.ldif.caEnrollmentRequest._016=## extdata-requestId ## +drmtool.ldif.caEnrollmentRequest._017=## ## +drmtool.ldif.caEnrollmentRequest._018=######################################## +drmtool.ldif.caEnrollmentRequest.cn=true +drmtool.ldif.caEnrollmentRequest.dateOfModify=true +drmtool.ldif.caEnrollmentRequest.dn=true +drmtool.ldif.caEnrollmentRequest.extdata.keyRecord=true +drmtool.ldif.caEnrollmentRequest.extdata.requestNotes=true +drmtool.ldif.caEnrollmentRequest.requestId=true +drmtool.ldif.caKeyRecord._000=######################################### +drmtool.ldif.caKeyRecord._001=## DRM CA Key Record ## +drmtool.ldif.caKeyRecord._002=######################################### +drmtool.ldif.caKeyRecord._003=## ## +drmtool.ldif.caKeyRecord._004=## NEVER allow 'DRMTOOL' the ability ## +drmtool.ldif.caKeyRecord._005=## to change the CA 'naming context' ## +drmtool.ldif.caKeyRecord._006=## data in the following fields: ## +drmtool.ldif.caKeyRecord._007=## ## +drmtool.ldif.caKeyRecord._008=## archivedBy ## +drmtool.ldif.caKeyRecord._009=## ## +drmtool.ldif.caKeyRecord._010=######################################### +drmtool.ldif.caKeyRecord.cn=true +drmtool.ldif.caKeyRecord.dateOfModify=true +drmtool.ldif.caKeyRecord.dn=true +drmtool.ldif.caKeyRecord.privateKeyData=true +drmtool.ldif.caKeyRecord.serialno=true +drmtool.ldif.namingContext._000=############################################ +drmtool.ldif.namingContext._001=## DRM Naming Context Fields ## +drmtool.ldif.namingContext._002=############################################ +drmtool.ldif.namingContext._003=## ## +drmtool.ldif.namingContext._004=## NEVER allow 'DRMTOOL' the ability to ## +drmtool.ldif.namingContext._005=## change the CA 'naming context' data ## +drmtool.ldif.namingContext._006=## in the following 'non-KeyRecord / ## +drmtool.ldif.namingContext._007=## non-Request' fields (as these records ## +drmtool.ldif.namingContext._008=## should be removed via the option to ## +drmtool.ldif.namingContext._009=## process requests and key records only ## +drmtool.ldif.namingContext._010=## if this is a DRM migration): ## +drmtool.ldif.namingContext._011=## ## +drmtool.ldif.namingContext._012=## cn ## +drmtool.ldif.namingContext._013=## sn ## +drmtool.ldif.namingContext._014=## uid ## +drmtool.ldif.namingContext._015=## uniqueMember ## +drmtool.ldif.namingContext._016=## ## +drmtool.ldif.namingContext._017=## NEVER allow 'DRMTOOL' the ability to ## +drmtool.ldif.namingContext._018=## change the DRM 'naming context' data ## +drmtool.ldif.namingContext._019=## in the following 'non-KeyRecord / ## +drmtool.ldif.namingContext._020=## non-Request' fields (as these records ## +drmtool.ldif.namingContext._021=## should be removed via the option to ## +drmtool.ldif.namingContext._022=## process requests and key records only ## +drmtool.ldif.namingContext._023=## if this is a DRM migration): ## +drmtool.ldif.namingContext._024=## ## +drmtool.ldif.namingContext._025=## dc ## +drmtool.ldif.namingContext._026=## dn ## +drmtool.ldif.namingContext._027=## uniqueMember ## +drmtool.ldif.namingContext._028=## ## +drmtool.ldif.namingContext._029=## NEVER allow 'DRMTOOL' the ability to ## +drmtool.ldif.namingContext._030=## change the TPS 'naming context' data ## +drmtool.ldif.namingContext._031=## in the following 'non-KeyRecord / ## +drmtool.ldif.namingContext._032=## non-Request' fields (as these records ## +drmtool.ldif.namingContext._033=## should be removed via the option to ## +drmtool.ldif.namingContext._034=## process requests and key records only ## +drmtool.ldif.namingContext._035=## if this is a DRM migration): ## +drmtool.ldif.namingContext._036=## ## +drmtool.ldif.namingContext._037=## uid ## +drmtool.ldif.namingContext._038=## uniqueMember ## +drmtool.ldif.namingContext._039=## ## +drmtool.ldif.namingContext._040=## If '-source_naming_context ## +drmtool.ldif.namingContext._041=## <original source DRM naming context>' ## +drmtool.ldif.namingContext._042=## and '-target_naming_context ## +drmtool.ldif.namingContext._043=## <renamed target DRM naming context>' ## +drmtool.ldif.namingContext._044=## options are specified, ALWAYS ## +drmtool.ldif.namingContext._045=## require 'DRMTOOL' to change the ## +drmtool.ldif.namingContext._046=## DRM 'naming context' data in ALL of ## +drmtool.ldif.namingContext._047=## the following fields in EACH of the ## +drmtool.ldif.namingContext._048=## following types of records: ## +drmtool.ldif.namingContext._049=## ## +drmtool.ldif.namingContext._050=## caEnrollmentRequest: ## +drmtool.ldif.namingContext._051=## ## +drmtool.ldif.namingContext._052=## dn ## +drmtool.ldif.namingContext._053=## extdata-auth--005ftoken;user ## +drmtool.ldif.namingContext._054=## extdata-auth--005ftoken;userdn ## +drmtool.ldif.namingContext._055=## ## +drmtool.ldif.namingContext._056=## caKeyRecord: ## +drmtool.ldif.namingContext._057=## ## +drmtool.ldif.namingContext._058=## dn ## +drmtool.ldif.namingContext._059=## ## +drmtool.ldif.namingContext._060=## recoveryRequest: ## +drmtool.ldif.namingContext._061=## ## +drmtool.ldif.namingContext._062=## dn ## +drmtool.ldif.namingContext._063=## ## +drmtool.ldif.namingContext._064=## tpsKeyRecord: ## +drmtool.ldif.namingContext._065=## ## +drmtool.ldif.namingContext._066=## dn ## +drmtool.ldif.namingContext._067=## ## +drmtool.ldif.namingContext._068=## tpsNetkeyKeygenRequest: ## +drmtool.ldif.namingContext._069=## ## +drmtool.ldif.namingContext._070=## dn ## +drmtool.ldif.namingContext._071=## ## +drmtool.ldif.namingContext._072=############################################ +drmtool.ldif.recoveryRequest._000=##################################### +drmtool.ldif.recoveryRequest._001=## DRM CA / TPS Recovery Request ## +drmtool.ldif.recoveryRequest._002=##################################### +drmtool.ldif.recoveryRequest.cn=true +drmtool.ldif.recoveryRequest.dateOfModify=true +drmtool.ldif.recoveryRequest.dn=true +drmtool.ldif.recoveryRequest.extdata.requestId=true +drmtool.ldif.recoveryRequest.extdata.requestNotes=true +drmtool.ldif.recoveryRequest.extdata.serialnumber=true +drmtool.ldif.recoveryRequest.requestId=true +drmtool.ldif.tpsKeyRecord._000=######################################### +drmtool.ldif.tpsKeyRecord._001=## DRM TPS Key Record ## +drmtool.ldif.tpsKeyRecord._002=######################################### +drmtool.ldif.tpsKeyRecord._003=## ## +drmtool.ldif.tpsKeyRecord._004=## NEVER allow 'DRMTOOL' the ability ## +drmtool.ldif.tpsKeyRecord._005=## to change the TPS 'naming context' ## +drmtool.ldif.tpsKeyRecord._006=## data in the following fields: ## +drmtool.ldif.tpsKeyRecord._007=## ## +drmtool.ldif.tpsKeyRecord._008=## archivedBy ## +drmtool.ldif.tpsKeyRecord._009=## ## +drmtool.ldif.tpsKeyRecord._010=######################################### +drmtool.ldif.tpsKeyRecord.cn=true +drmtool.ldif.tpsKeyRecord.dateOfModify=true +drmtool.ldif.tpsKeyRecord.dn=true +drmtool.ldif.tpsKeyRecord.privateKeyData=true +drmtool.ldif.tpsKeyRecord.serialno=true +drmtool.ldif.tpsNetkeyKeygenRequest._000=##################################### +drmtool.ldif.tpsNetkeyKeygenRequest._001=## DRM TPS Netkey Keygen Request ## +drmtool.ldif.tpsNetkeyKeygenRequest._002=##################################### +drmtool.ldif.tpsNetkeyKeygenRequest._003=## ## +drmtool.ldif.tpsNetkeyKeygenRequest._004=## NEVER allow 'DRMTOOL' the ## +drmtool.ldif.tpsNetkeyKeygenRequest._005=## ability to change the ## +drmtool.ldif.tpsNetkeyKeygenRequest._006=## TPS 'naming context' data in ## +drmtool.ldif.tpsNetkeyKeygenRequest._007=## the following fields: ## +drmtool.ldif.tpsNetkeyKeygenRequest._008=## ## +drmtool.ldif.tpsNetkeyKeygenRequest._009=## extdata-updatedby ## +drmtool.ldif.tpsNetkeyKeygenRequest._010=## ## +drmtool.ldif.tpsNetkeyKeygenRequest._011=##################################### +drmtool.ldif.tpsNetkeyKeygenRequest.cn=true +drmtool.ldif.tpsNetkeyKeygenRequest.dateOfModify=true +drmtool.ldif.tpsNetkeyKeygenRequest.dn=true +drmtool.ldif.tpsNetkeyKeygenRequest.extdata.keyRecord=true +drmtool.ldif.tpsNetkeyKeygenRequest.extdata.requestId=true +drmtool.ldif.tpsNetkeyKeygenRequest.extdata.requestNotes=true +drmtool.ldif.tpsNetkeyKeygenRequest.requestId=true + diff --git a/base/java-tools/src/com/netscape/cmstools/DRMTool.java b/base/java-tools/src/com/netscape/cmstools/DRMTool.java new file mode 100644 index 000000000..e2fd2c538 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/DRMTool.java @@ -0,0 +1,5120 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2011 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Vector; +import java.util.regex.PatternSyntaxException; + +import netscape.security.provider.RSAPublicKey; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.x509.X509CertImpl; + +import org.mozilla.jss.CertDatabaseException; +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.KeyDatabaseException; +import org.mozilla.jss.crypto.AlreadyInitializedException; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.InvalidKeyFormatException; +import org.mozilla.jss.crypto.KeyWrapAlgorithm; +import org.mozilla.jss.crypto.KeyWrapper; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.PrivateKey; +import org.mozilla.jss.crypto.SymmetricKey; +import org.mozilla.jss.crypto.TokenCertificate; +import org.mozilla.jss.crypto.TokenException; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkcs11.PK11PubKey; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.Utils; + +/** + * The DRMTool class is a utility program designed to operate on an LDIF file + * to perform one or more of the following tasks: + * + * <PRE> + * (A) Use a new storage key (e. g. - a 2048-bit key to replace a + * 1024-bit key) to rewrap the existing triple DES symmetric key + * that was used to wrap a user's private key. + * + * STARTING INVENTORY: + * + * (1) a DRMTOOL configuration file containing DRM LDIF record + * types and the processing status of their associated fields + * + * (2) an LDIF file containing 'exported' DRM data + * (referred to as the "source" DRM) + * + * NOTE: If this LDIF file contains data that was originally + * from a DRM instance that was prior to RHCS 8, it + * must have previously undergone the appropriate + * migration steps. + * + * (3) the NSS security databases (e. g. - cert8.db, key3.db, + * and secmod.db) associated with the data contained in + * the source LDIF file + * + * NOTE: If the storage key was located on an HSM, then the + * HSM must be available to the machine on which the + * DRMTool is being executed (since the RSA private + * storage key is required for unwrapping the + * symmetric triple DES key). Additionally, a + * password may be required to unlock access to + * this key (e. g. - which may be located in + * the source DRM's 'password.conf' file). + * + * (4) a file containing the ASCII BASE-64 storage certificate + * from the DRM instance for which the output LDIF file is + * intended (referred to as the "target") + * + * ENDING INVENTORY: + * + * (1) all items listed in the STARTING INVENTORY (unchanged) + * + * (2) a log file containing information suitable for audit + * purposes + * + * (3) an LDIF file containing the revised data suitable for + * 'import' into a new DRM (referred to as the "target" DRM) + * + * DRMTool PARAMETERS: + * + * (1) the name of the DRMTOOL configuration file containing + * DRM LDIF record types and the processing status of their + * associated fields + * + * (2) the name of the input LDIF file containing data which was + * 'exported' from the source DRM instance + * + * (3) the name of the output LDIF file intended to contain the + * revised data suitable for 'import' to a target DRM instance + * + * (4) the name of the log file that may be used for auditing + * purposes + * + * (5) the path to the security databases that were used by + * the source DRM instance + * + * (6) the name of the token that was used by + * the source DRM instance + * + * (7) the name of the storage certificate that was used by + * the source DRM instance + * + * (8) the name of the file containing the ASCII BASE-64 storage + * certificate from the target DRM instance for which the + * output LDIF file is intended + * + * (9) OPTIONALLY, the name of a file which ONLY contains the + * password needed to access the source DRM instance's + * security databases + * + * (10) OPTIONALLY, choose to change the specified source DRM naming + * context to the specified target DRM naming context + * + * (11) OPTIONALLY, choose to ONLY process CA enrollment requests, + * CA recovery requests, CA key records, TPS netkeyKeygen + * enrollment requests, TPS recovery requests, and + * TPS key records + * + * DATA FIELDS AFFECTED (using default config file values): + * + * (1) CA DRM enrollment request + * + * (a) dateOfModify + * (b) extdata-requestnotes + * + * (2) CA DRM key record + * + * (a) dateOfModify + * (b) privateKeyData + * + * (3) CA DRM recovery request + * + * (a) dateOfModify + * (b) extdata-requestnotes (NEW) + * + * (4) TPS DRM netkeyKeygen (enrollment) request + * + * (a) dateOfModify + * (b) extdata-requestnotes (NEW) + * + * (5) TPS DRM key record + * + * (a) dateOfModify + * (b) privateKeyData + * + * (6) TPS DRM recovery request + * + * (a) dateOfModify + * (b) extdata-requestnotes (NEW) + * + * (B) Specify an ID offset to append to existing numeric data + * (e. g. - to renumber data for use in DRM consolidation efforts). + * + * STARTING INVENTORY: + * + * (1) a DRMTOOL configuration file containing DRM LDIF record + * types and the processing status of their associated fields + * + * (2) an LDIF file containing 'exported' DRM data + * (referred to as the "source" DRM) + * + * NOTE: If this LDIF file contains data that was originally + * from a DRM instance that was prior to RHCS 8, it + * must have previously undergone the appropriate + * migration steps. + * + * ENDING INVENTORY: + * + * (1) all items listed in the STARTING INVENTORY (unchanged) + * + * (2) a log file containing information suitable for audit + * purposes + * + * (3) an LDIF file containing the revised data suitable for + * 'import' into a new DRM (referred to as the "target" DRM) + * + * DRMTool PARAMETERS: + * + * (1) the name of the DRMTOOL configuration file containing + * DRM LDIF record types and the processing status of their + * associated fields + * + * (2) the name of the input LDIF file containing data which was + * 'exported' from the source DRM instance + * + * (3) the name of the output LDIF file intended to contain the + * revised data suitable for 'import' to a target DRM instance + * + * (4) the name of the log file that may be used for auditing + * purposes + * + * (5) a large numeric ID offset (mask) to be appended to existing + * numeric data in the source DRM instance's LDIF file + * + * (6) OPTIONALLY, choose to change the specified source DRM naming + * context to the specified target DRM naming context + * + * (7) OPTIONALLY, choose to ONLY process CA enrollment requests, + * CA recovery requests, CA key records, TPS netkeyKeygen + * enrollment requests, TPS recovery requests, and + * TPS key records + * + * DATA FIELDS AFFECTED (using default config file values): + * + * (1) CA DRM enrollment request + * + * (a) cn + * (b) dateOfModify + * (c) extdata-keyrecord + * (d) extdata-requestnotes + * (e) requestId + * + * (2) CA DRM key record + * + * (a) cn + * (b) dateOfModify + * (c) serialno + * + * (3) CA DRM recovery request + * + * (a) cn + * (b) dateOfModify + * (c) extdata-requestid + * (d) extdata-requestnotes (NEW) + * (e) extdata-serialnumber + * (f) requestId + * + * (4) TPS DRM netkeyKeygen (enrollment) request + * + * (a) cn + * (b) dateOfModify + * (c) extdata-keyrecord + * (d) extdata-requestid + * (e) extdata-requestnotes (NEW) + * (f) requestId + * + * (5) TPS DRM key record + * + * (a) cn + * (b) dateOfModify + * (c) serialno + * + * (6) TPS DRM recovery request + * + * (a) cn + * (b) dateOfModify + * (c) extdata-requestid + * (d) extdata-requestnotes (NEW) + * (e) extdata-serialnumber + * (f) requestId + * + * (C) Specify an ID offset to be removed from existing numeric data + * (e. g. - to undo renumbering used in DRM consolidation efforts). + * + * STARTING INVENTORY: + * + * (1) a DRMTOOL configuration file containing DRM LDIF record + * types and the processing status of their associated fields + * + * (2) an LDIF file containing 'exported' DRM data + * (referred to as the "source" DRM) + * + * NOTE: If this LDIF file contains data that was originally + * from a DRM instance that was prior to RHCS 8, it + * must have previously undergone the appropriate + * migration steps. + * + * ENDING INVENTORY: + * + * (1) all items listed in the STARTING INVENTORY (unchanged) + * + * (2) a log file containing information suitable for audit + * purposes + * + * (3) an LDIF file containing the revised data suitable for + * 'import' into a new DRM (referred to as the "target" DRM) + * + * DRMTool PARAMETERS: + * + * (1) the name of the DRMTOOL configuration file containing + * DRM LDIF record types and the processing status of their + * associated fields + * + * (2) the name of the input LDIF file containing data which was + * 'exported' from the source DRM instance + * + * (3) the name of the output LDIF file intended to contain the + * revised data suitable for 'import' to a target DRM instance + * + * (4) the name of the log file that may be used for auditing + * purposes + * + * (5) a large numeric ID offset (mask) to be removed from existing + * numeric data in the source DRM instance's LDIF file + * + * (6) OPTIONALLY, choose to change the specified source DRM naming + * context to the specified target DRM naming context + * + * (7) OPTIONALLY, choose to ONLY process CA enrollment requests, + * CA recovery requests, CA key records, TPS netkeyKeygen + * enrollment requests, TPS recovery requests, and + * TPS key records + * + * DATA FIELDS AFFECTED (using default config file values): + * + * (1) CA DRM enrollment request + * + * (a) cn + * (b) dateOfModify + * (c) extdata-keyrecord + * (d) extdata-requestnotes + * (e) requestId + * + * (2) CA DRM key record + * + * (a) cn + * (b) dateOfModify + * (c) serialno + * + * (3) CA DRM recovery request + * + * (a) cn + * (b) dateOfModify + * (c) extdata-requestid + * (d) extdata-requestnotes (NEW) + * (e) extdata-serialnumber + * (f) requestId + * + * (4) TPS DRM netkeyKeygen (enrollment) request + * + * (a) cn + * (b) dateOfModify + * (c) extdata-keyrecord + * (d) extdata-requestid + * (e) extdata-requestnotes (NEW) + * (f) requestId + * + * (5) TPS DRM key record + * + * (a) cn + * (b) dateOfModify + * (c) serialno + * + * (6) TPS DRM recovery request + * + * (a) cn + * (b) dateOfModify + * (c) extdata-requestid + * (d) extdata-requestnotes (NEW) + * (e) extdata-serialnumber + * (f) requestId + * + * </PRE> + * + * <P> + * DRMTool may be invoked as follows: + * + * <PRE> + * + * DRMTool + * -drmtool_config_file <path + drmtool config file> + * -source_ldif_file <path + source ldif file> + * -target_ldif_file <path + target ldif file> + * -log_file <path + log file> + * [-source_pki_security_database_path <path to PKI source database>] + * [-source_storage_token_name '<source token>'] + * [-source_storage_certificate_nickname '<source nickname>'] + * [-target_storage_certificate_file <path to target certificate file>] + * [-source_pki_security_database_pwdfile <path to PKI password file>] + * [-append_id_offset <numeric offset>] + * [-remove_id_offset <numeric offset>] + * [-source_drm_naming_context '<original source DRM naming context>'] + * [-target_drm_naming_context '<renamed target DRM naming context>'] + * [-process_requests_and_key_records_only] + * + * where the following options are 'Mandatory': + * + * -drmtool_config_file <path + drmtool config file> + * -source_ldif_file <path + source ldif file> + * -target_ldif_file <path + target ldif file> + * -log_file <path + log file> + * + * AND at least ONE of the following are a 'Mandatory' set of options: + * + * (a) options for using a new storage key for rewrapping: + * + * [-source_pki_security_database_path + * <path to PKI source database>] + * [-source_storage_token_name '<source token>'] + * [-source_storage_certificate_nickname '<source nickname>'] + * [-target_storage_certificate_file + * <path to target certificate file>] + * + * AND OPTIONALLY, specify the name of a file which ONLY contains + * the password needed to access the source DRM instance's + * security databases: + * + * [-source_pki_security_database_pwdfile + * <path to PKI password file>] + * + * AND OPTIONALLY, rename source DRM naming context --> target + * DRM naming context: + * + * [-source_drm_naming_context '<source DRM naming context>'] + * [-target_drm_naming_context '<target DRM naming context>'] + * + * AND OPTIONALLY, process requests and key records ONLY: + * + * [-process_requests_and_key_records_only] + * + * (b) option for appending the specified numeric ID offset + * to existing numerical data: + * + * [-append_id_offset <numeric offset>] + * + * AND OPTIONALLY, rename source DRM naming context --> target + * DRM naming context: + * + * [-source_drm_naming_context '<source DRM naming context>'] + * [-target_drm_naming_context '<target DRM naming context>'] + * + * AND OPTIONALLY, process requests and key records ONLY: + * + * [-process_requests_and_key_records_only] + * + * (c) option for removing the specified numeric ID offset + * from existing numerical data: + * + * AND OPTIONALLY, rename source DRM naming context --> target + * DRM naming context: + * + * [-source_drm_naming_context '<source DRM naming context>'] + * [-target_drm_naming_context '<target DRM naming context>'] + * + * [-remove_id_offset <numeric offset>] + * + * AND OPTIONALLY, process requests and key records ONLY: + * + * [-process_requests_and_key_records_only] + * + * (d) (a) rewrap AND (b) append ID offset + * [AND OPTIONALLY, rename source DRM naming context --> target + * DRM naming context] + * [AND OPTIONALLY process requests and key records ONLY] + * + * (e) (a) rewrap AND (c) remove ID offset + * [AND OPTIONALLY, rename source DRM naming context --> target + * DRM naming context] + * [AND OPTIONALLY process requests and key records ONLY] + * + * NOTE: Options (b) and (c) are mutually exclusive! + * + * </PRE> + * + * @author mharmsen + * @version $Revision$, $Date$ + */ +public class DRMTool { + /*************/ + /* Constants */ + /*************/ + + // Constants: Miscellaneous + private static final boolean FAILURE = false; + private static final boolean SUCCESS = true; + private static final String COLON = ":"; + private static final String COMMA = ","; + private static final String DOT = "."; + private static final String EQUAL_SIGN = "="; + private static final String HASH = "#"; + private static final String LEFT_BRACE = "["; + private static final String NEWLINE = "\n"; + private static final String PLUS = "+"; + private static final String RIGHT_BRACE = "]"; + private static final String SPACE = " "; + private static final String TIC = "'"; + + // Constants: Calendar + private static final String DATE_OF_MODIFY_PATTERN = "yyyyMMddHHmmss'Z'"; + private static final String LOGGING_DATE_PATTERN = "dd/MMM/yyyy:HH:mm:ss z"; + + // Constants: PKCS #11 Information + private static final String INTERNAL_TOKEN = "Internal Key Storage Token"; + + // Constants: Command-line Options + private static final int ID_OFFSET_NAME_VALUE_PAIRS = 1; + private static final int PWDFILE_NAME_VALUE_PAIRS = 1; + private static final int NAMING_CONTEXT_NAME_VALUE_PAIRS = 2; + private static final int MANDATORY_NAME_VALUE_PAIRS = 4; + private static final int REWRAP_NAME_VALUE_PAIRS = 4; + private static final int ID_OFFSET_ARGS = 10; + private static final int REWRAP_ARGS = 16; + private static final int REWRAP_AND_ID_OFFSET_ARGS = 18; + + // Constants: Command-line Options (Mandatory) + private static final String DRM_TOOL = "DRMTool"; + + private static final String DRMTOOL_CFG_FILE = "-drmtool_config_file"; + + private static final String DRMTOOL_CFG_DESCRIPTION = " <complete path to the drmtool config file" + + NEWLINE + + " " + + " ending with the drmtool config file name>"; + + private static final String DRMTOOL_CFG_FILE_EXAMPLE = DRMTOOL_CFG_FILE + + " " + + "/usr/share/pki/java-tools/DRMTool.cfg"; + + private static final String SOURCE_LDIF_FILE = "-source_ldif_file"; + + private static final String SOURCE_LDIF_DESCRIPTION = " <complete path to the source LDIF input file" + + NEWLINE + + " " + + " ending with the source LDIF file name>"; + + private static final String SOURCE_LDIF_FILE_EXAMPLE = SOURCE_LDIF_FILE + + " " + + "/export/pki/source.ldif"; + + private static final String TARGET_LDIF_FILE = "-target_ldif_file"; + + private static final String TARGET_LDIF_DESCRIPTION = " <complete path to the target LDIF output file" + + NEWLINE + + " " + + " ending with the target LDIF file name>"; + + private static final String TARGET_LDIF_FILE_EXAMPLE = TARGET_LDIF_FILE + + " " + + "/export/pki/target.ldif"; + + private static final String LOG_FILE = "-log_file"; + + private static final String LOG_DESCRIPTION = " <complete path to the log file" + + NEWLINE + + " " + + " ending with the log file name>"; + + private static final String LOG_FILE_EXAMPLE = LOG_FILE + + " " + + "/export/pki/DRMTool.log"; + + // Constants: Command-line Options (Rewrap) + private static final String SOURCE_NSS_DB_PATH = "-source_pki_security_database_path"; + + private static final String SOURCE_NSS_DB_DESCRIPTION = " <complete path to the " + + "source security databases" + + NEWLINE + + " " + + " used by data in the source LDIF file>"; + + private static final String SOURCE_NSS_DB_PATH_EXAMPLE = SOURCE_NSS_DB_PATH + + " " + + "/export/pki"; + + private static final String SOURCE_STORAGE_TOKEN_NAME = "-source_storage_token_name"; + + private static final String SOURCE_STORAGE_TOKEN_DESCRIPTION = " <name of the token containing " + + "the source storage token>"; + + private static final String SOURCE_STORAGE_TOKEN_NAME_EXAMPLE = SOURCE_STORAGE_TOKEN_NAME + + " " + + TIC + + "Internal Key Storage Token" + + TIC; + + private static final String SOURCE_STORAGE_CERT_NICKNAME = "-source_storage_certificate_nickname"; + + private static final String SOURCE_STORAGE_CERT_NICKNAME_DESCRIPTION = " <nickname of the source " + + "storage certificate>"; + + private static final String SOURCE_STORAGE_CERT_NICKNAME_EXAMPLE = SOURCE_STORAGE_CERT_NICKNAME + + " " + + TIC + + "storageCert cert-pki-kra" + + TIC; + + private static final String TARGET_STORAGE_CERTIFICATE_FILE = "-target_storage_certificate_file"; + + private static final String TARGET_STORAGE_CERTIFICATE_DESCRIPTION = " <complete path to the target " + + "storage certificate file" + + NEWLINE + + " " + + " ending with the target " + + "storage certificate file name;" + + NEWLINE + + " " + + " the target storage " + + "certificate is stored in" + + NEWLINE + + " " + + " an ASCII format between a " + + "header and footer>"; + + private static final String TARGET_STORAGE_CERTIFICATE_FILE_EXAMPLE = TARGET_STORAGE_CERTIFICATE_FILE + + " " + + "/export/pki/target_storage.cert"; + + private static final String SOURCE_NSS_DB_PWDFILE = "-source_pki_security_database_pwdfile"; + + private static final String SOURCE_NSS_DB_PWDFILE_DESCRIPTION = " <complete path to the password " + + "file which ONLY contains the" + + NEWLINE + + " " + + " password used to access the " + + "source security databases>"; + + private static final String SOURCE_NSS_DB_PWDFILE_EXAMPLE = SOURCE_NSS_DB_PWDFILE + + " " + + "/export/pki/pwdfile"; + + // Constants: Command-line Options (ID Offset) + private static final String APPEND_ID_OFFSET = "-append_id_offset"; + + private static final String APPEND_ID_OFFSET_DESCRIPTION = " <ID offset that is appended to " + + "each record's source ID>"; + + private static final String APPEND_ID_OFFSET_EXAMPLE = APPEND_ID_OFFSET + + " " + + "100000000000"; + + private static final String REMOVE_ID_OFFSET = "-remove_id_offset"; + + private static final String REMOVE_ID_OFFSET_DESCRIPTION = " <ID offset that is removed from " + + "each record's source ID>"; + + private static final String REMOVE_ID_OFFSET_EXAMPLE = REMOVE_ID_OFFSET + + " " + + "100000000000"; + + // Constants: Command-line Options + private static final String SOURCE_DRM_NAMING_CONTEXT = "-source_drm_naming_context"; + + private static final String SOURCE_DRM_NAMING_CONTEXT_DESCRIPTION = " <source DRM naming context>"; + + private static final String SOURCE_DRM_NAMING_CONTEXT_EXAMPLE = SOURCE_DRM_NAMING_CONTEXT + + " " + + TIC + + "alpha.example.com-pki-kra" + + TIC; + + private static final String TARGET_DRM_NAMING_CONTEXT = "-target_drm_naming_context"; + + private static final String TARGET_DRM_NAMING_CONTEXT_DESCRIPTION = " <target DRM naming context>"; + + private static final String TARGET_DRM_NAMING_CONTEXT_EXAMPLE = TARGET_DRM_NAMING_CONTEXT + + " " + + TIC + + "omega.example.com-pki-kra" + + TIC; + + private static final String PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY = + "-process_requests_and_key_records_only"; + + // Constants: DRMTOOL Config File + private static final String DRMTOOL_CFG_PREFIX = "drmtool.ldif"; + private static final String DRMTOOL_CFG_ENROLLMENT = "caEnrollmentRequest"; + private static final String DRMTOOL_CFG_CA_KEY_RECORD = "caKeyRecord"; + private static final String DRMTOOL_CFG_RECOVERY = "recoveryRequest"; + private static final String DRMTOOL_CFG_TPS_KEY_RECORD = "tpsKeyRecord"; + private static final String DRMTOOL_CFG_KEYGEN = "tpsNetkeyKeygenRequest"; + + // Constants: DRMTOOL Config File (DRM CA Enrollment Request Fields) + private static final String DRMTOOL_CFG_ENROLLMENT_CN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_ENROLLMENT + + DOT + + "cn"; + private static final String DRMTOOL_CFG_ENROLLMENT_DATE_OF_MODIFY = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_ENROLLMENT + + DOT + + "dateOfModify"; + private static final String DRMTOOL_CFG_ENROLLMENT_DN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_ENROLLMENT + + DOT + + "dn"; + private static final String DRMTOOL_CFG_ENROLLMENT_EXTDATA_KEY_RECORD = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_ENROLLMENT + + DOT + + "extdata.keyRecord"; + private static final String DRMTOOL_CFG_ENROLLMENT_EXTDATA_REQUEST_NOTES = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_ENROLLMENT + + DOT + + "extdata.requestNotes"; + private static final String DRMTOOL_CFG_ENROLLMENT_REQUEST_ID = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_ENROLLMENT + + DOT + + "requestId"; + + // Constants: DRMTOOL Config File (DRM CA Key Record Fields) + private static final String DRMTOOL_CFG_CA_KEY_RECORD_CN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_CA_KEY_RECORD + + DOT + + "cn"; + private static final String DRMTOOL_CFG_CA_KEY_RECORD_DATE_OF_MODIFY = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_CA_KEY_RECORD + + DOT + + "dateOfModify"; + private static final String DRMTOOL_CFG_CA_KEY_RECORD_DN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_ENROLLMENT + + DOT + + "dn"; + private static final String DRMTOOL_CFG_CA_KEY_RECORD_PRIVATE_KEY_DATA = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_CA_KEY_RECORD + + DOT + + "privateKeyData"; + private static final String DRMTOOL_CFG_CA_KEY_RECORD_SERIAL_NO = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_CA_KEY_RECORD + + DOT + + "serialno"; + + // Constants: DRMTOOL Config File (DRM CA / TPS Recovery Request Fields) + private static final String DRMTOOL_CFG_RECOVERY_CN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_RECOVERY + + DOT + + "cn"; + private static final String DRMTOOL_CFG_RECOVERY_DATE_OF_MODIFY = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_RECOVERY + + DOT + + "dateOfModify"; + private static final String DRMTOOL_CFG_RECOVERY_DN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_RECOVERY + + DOT + + "dn"; + private static final String DRMTOOL_CFG_RECOVERY_EXTDATA_REQUEST_ID = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_RECOVERY + + DOT + + "extdata.requestId"; + private static final String DRMTOOL_CFG_RECOVERY_EXTDATA_REQUEST_NOTES = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_RECOVERY + + DOT + + "extdata.requestNotes"; + private static final String DRMTOOL_CFG_RECOVERY_EXTDATA_SERIAL_NUMBER = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_RECOVERY + + DOT + + "extdata.serialnumber"; + private static final String DRMTOOL_CFG_RECOVERY_REQUEST_ID = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_RECOVERY + + DOT + + "requestId"; + + // Constants: DRMTOOL Config File (DRM TPS Key Record Fields) + private static final String DRMTOOL_CFG_TPS_KEY_RECORD_CN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_TPS_KEY_RECORD + + DOT + + "cn"; + private static final String DRMTOOL_CFG_TPS_KEY_RECORD_DATE_OF_MODIFY = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_TPS_KEY_RECORD + + DOT + + "dateOfModify"; + private static final String DRMTOOL_CFG_TPS_KEY_RECORD_DN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_TPS_KEY_RECORD + + DOT + + "dn"; + private static final String DRMTOOL_CFG_TPS_KEY_RECORD_PRIVATE_KEY_DATA = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_TPS_KEY_RECORD + + DOT + + "privateKeyData"; + private static final String DRMTOOL_CFG_TPS_KEY_RECORD_SERIAL_NO = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_TPS_KEY_RECORD + + DOT + + "serialno"; + + // Constants: DRMTOOL Config File (DRM TPS Netkey Keygen Request Fields) + private static final String DRMTOOL_CFG_KEYGEN_CN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_KEYGEN + + DOT + + "cn"; + private static final String DRMTOOL_CFG_KEYGEN_DATE_OF_MODIFY = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_KEYGEN + + DOT + + "dateOfModify"; + private static final String DRMTOOL_CFG_KEYGEN_DN = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_KEYGEN + + DOT + + "dn"; + private static final String DRMTOOL_CFG_KEYGEN_EXTDATA_KEY_RECORD = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_KEYGEN + + DOT + + "extdata.keyRecord"; + private static final String DRMTOOL_CFG_KEYGEN_EXTDATA_REQUEST_ID = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_KEYGEN + + DOT + + "extdata.requestId"; + private static final String DRMTOOL_CFG_KEYGEN_EXTDATA_REQUEST_NOTES = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_KEYGEN + + DOT + + "extdata.requestNotes"; + private static final String DRMTOOL_CFG_KEYGEN_REQUEST_ID = DRMTOOL_CFG_PREFIX + + DOT + + DRMTOOL_CFG_KEYGEN + + DOT + + "requestId"; + + // Constants: Target Certificate Information + private static final String HEADER = "-----BEGIN"; + private static final String TRAILER = "-----END"; + private static final String X509_INFO = "x509.INFO"; + + // Constants: DRM LDIF Record Fields + private static final String DRM_LDIF_ARCHIVED_BY = "archivedBy:"; + private static final String DRM_LDIF_CN = "cn:"; + private static final String DRM_LDIF_DATE_OF_MODIFY = "dateOfModify:"; + private static final String DRM_LDIF_DN = "dn:"; + private static final String DRM_LDIF_DN_EMBEDDED_CN_DATA = "dn: cn"; + private static final String DRM_LDIF_EXTDATA_AUTH_TOKEN_USER = "extdata-auth--005ftoken;user:"; + private static final String DRM_LDIF_EXTDATA_AUTH_TOKEN_USER_DN = "extdata-auth--005ftoken;userdn:"; + private static final String DRM_LDIF_EXTDATA_KEY_RECORD = "extdata-keyrecord:"; + private static final String DRM_LDIF_EXTDATA_REQUEST_ID = "extdata-requestid:"; + private static final String DRM_LDIF_EXTDATA_REQUEST_NOTES = "extdata-requestnotes:"; + private static final String DRM_LDIF_EXTDATA_REQUEST_TYPE = "extdata-requesttype:"; + private static final String DRM_LDIF_EXTDATA_SERIAL_NUMBER = "extdata-serialnumber:"; + private static final String DRM_LDIF_PRIVATE_KEY_DATA = "privateKeyData::"; + private static final String DRM_LDIF_REQUEST_ID = "requestId:"; + private static final String DRM_LDIF_REQUEST_TYPE = "requestType:"; + private static final String DRM_LDIF_SERIAL_NO = "serialno:"; + + // Constants: DRM LDIF Record Values + private static final int INITIAL_LDIF_RECORD_CAPACITY = 0; + private static final int EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH = 56; + private static final int PRIVATE_KEY_DATA_FIRST_LINE_DATA_LENGTH = 60; + private static final String DRM_LDIF_RECORD = "Generic"; + private static final String DRM_LDIF_CA_KEY_RECORD = "CA"; + private static final String DRM_LDIF_ENROLLMENT = "enrollment"; + private static final String DRM_LDIF_KEYGEN = "netkeyKeygen"; + private static final String DRM_LDIF_RECOVERY = "recovery"; + private static final String DRM_LDIF_TPS_KEY_RECORD = "TPS"; + + // Constants: DRM LDIF Record Messages + private static final String DRM_LDIF_REWRAP_MESSAGE = "REWRAPPED the '" + + "existing DES3 " + + "symmetric " + + "session key" + + "' with the '"; + private static final String DRM_LDIF_RSA_MESSAGE = "-bit RSA public key' " + + "obtained from the " + + "target storage " + + "certificate"; + private static final String DRM_LDIF_USED_PWDFILE_MESSAGE = + "USED source PKI security database " + + "password file"; + private static final String DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE = + "APPENDED ID offset"; + private static final String DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE = + "REMOVED ID offset"; + private static final String DRM_LDIF_SOURCE_NAME_CONTEXT_MESSAGE = + "RENAMED source DRM naming context '"; + private static final String DRM_LDIF_TARGET_NAME_CONTEXT_MESSAGE = + "' to target DRM naming context '"; + private static final String DRM_LDIF_PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY_MESSAGE = + "PROCESSED requests and key records ONLY!"; + + /*************/ + /* Variables */ + /*************/ + + // Variables: Calendar + private static String mDateOfModify = null; + + // Variables: Command-Line Options + private static boolean mMandatoryFlag = false; + private static boolean mRewrapFlag = false; + private static boolean mPwdfileFlag = false; + private static boolean mAppendIdOffsetFlag = false; + private static boolean mRemoveIdOffsetFlag = false; + private static boolean mDrmNamingContextsFlag = false; + private static boolean mProcessRequestsAndKeyRecordsOnlyFlag = false; + private static int mMandatoryNameValuePairs = 0; + private static int mRewrapNameValuePairs = 0; + private static int mPKISecurityDatabasePwdfileNameValuePairs = 0; + private static int mAppendIdOffsetNameValuePairs = 0; + private static int mRemoveIdOffsetNameValuePairs = 0; + private static int mDrmNamingContextNameValuePairs = 0; + + // Variables: Command-Line Values (Mandatory) + private static String mDrmtoolCfgFilename = null; + private static String mSourceLdifFilename = null; + private static String mTargetLdifFilename = null; + private static String mLogFilename = null; + + // Variables: Command-Line Values (Rewrap) + private static String mSourcePKISecurityDatabasePath = null; + private static String mSourceStorageTokenName = null; + private static String mSourceStorageCertNickname = null; + private static String mTargetStorageCertificateFilename = null; + + // Variables: Command-Line Values (Rewrap Password File) + private static String mSourcePKISecurityDatabasePwdfile = null; + + // Variables: Command-Line Values (ID Offset) + private static BigInteger mAppendIdOffset = null; + private static BigInteger mRemoveIdOffset = null; + + // Variables: Command-Line Values (DRM Naming Contexts) + private static String mSourceDrmNamingContext = null; + private static String mTargetDrmNamingContext = null; + + // Variables: DRMTOOL Config File Parameters of Interest + private static Hashtable<String, Boolean> drmtoolCfg = null; + + // Variables: DRMTOOL LDIF File Parameters of Interest + private static Vector<String> record = null; + private static Iterator<String> ldif_record = null; + + // Variables: Logging + private static boolean mDebug = false; // set 'true' for debug messages + private static PrintWriter logger = null; + private static String current_date_and_time = null; + + // Variables: PKCS #11 Information + private static CryptoToken mSourceToken = null; + private static X509Certificate mUnwrapCert = null; + private static PrivateKey mUnwrapPrivateKey = null; + private static PublicKey mWrapPublicKey = null; + private static int mPublicKeySize = 0; + + // Variables: DRM LDIF Record Messages + private static String mSourcePKISecurityDatabasePwdfileMessage = null; + private static String mDrmNamingContextMessage = null; + private static String mProcessRequestsAndKeyRecordsOnlyMessage = null; + + /********************/ + /* Calendar Methods */ + /********************/ + + /** + * This method is used to get the current date and time. + * <P> + * + * @param pattern string containing desired format of date and time + * @return a formatted string containing the current date and time + */ + private static String now(String pattern) { + Calendar cal = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + return sdf.format(cal.getTime()); + } + + /*****************/ + /* Usage Methods */ + /*****************/ + + /** + * This method prints out the proper command-line usage required to + * execute DRMTool. + */ + private static void printUsage() { + System.out.println("Usage: " + + DRM_TOOL + + NEWLINE + + " " + + DRMTOOL_CFG_FILE + + NEWLINE + + " " + + DRMTOOL_CFG_DESCRIPTION + + NEWLINE + + " " + + SOURCE_LDIF_FILE + + NEWLINE + + " " + + SOURCE_LDIF_DESCRIPTION + + NEWLINE + + " " + + TARGET_LDIF_FILE + + NEWLINE + + " " + + TARGET_LDIF_DESCRIPTION + + NEWLINE + + " " + + LOG_FILE + + NEWLINE + + " " + + LOG_DESCRIPTION + + NEWLINE + + " " + + "[" + + SOURCE_NSS_DB_PATH + + NEWLINE + + " " + + SOURCE_NSS_DB_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + SOURCE_STORAGE_TOKEN_NAME + + NEWLINE + + " " + + SOURCE_STORAGE_TOKEN_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + SOURCE_STORAGE_CERT_NICKNAME + + NEWLINE + + " " + + SOURCE_STORAGE_CERT_NICKNAME_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + TARGET_STORAGE_CERTIFICATE_FILE + + NEWLINE + + " " + + TARGET_STORAGE_CERTIFICATE_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + SOURCE_NSS_DB_PWDFILE + + NEWLINE + + " " + + SOURCE_NSS_DB_PWDFILE_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + APPEND_ID_OFFSET + + NEWLINE + + " " + + APPEND_ID_OFFSET_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + REMOVE_ID_OFFSET + + NEWLINE + + " " + + REMOVE_ID_OFFSET_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + SOURCE_DRM_NAMING_CONTEXT + + NEWLINE + + " " + + SOURCE_DRM_NAMING_CONTEXT_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + TARGET_DRM_NAMING_CONTEXT + + NEWLINE + + " " + + TARGET_DRM_NAMING_CONTEXT_DESCRIPTION + + "]" + + NEWLINE + + " " + + "[" + + PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY + + "]" + + NEWLINE); + + System.out.println("Example of 'Rewrap and Append ID Offset':" + + NEWLINE + + NEWLINE + + " " + + DRM_TOOL + + NEWLINE + + " " + + DRMTOOL_CFG_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + TARGET_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + LOG_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_NSS_DB_PATH_EXAMPLE + + NEWLINE + + " " + + SOURCE_STORAGE_TOKEN_NAME_EXAMPLE + + NEWLINE + + " " + + SOURCE_STORAGE_CERT_NICKNAME_EXAMPLE + + NEWLINE + + " " + + TARGET_STORAGE_CERTIFICATE_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_NSS_DB_PWDFILE_EXAMPLE + + NEWLINE + + " " + + APPEND_ID_OFFSET_EXAMPLE + + NEWLINE + + " " + + SOURCE_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + TARGET_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY + + NEWLINE); + + System.out.println("Example of 'Rewrap and Remove ID Offset':" + + NEWLINE + + NEWLINE + + " " + + DRM_TOOL + + NEWLINE + + " " + + DRMTOOL_CFG_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + TARGET_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + LOG_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_NSS_DB_PATH_EXAMPLE + + NEWLINE + + " " + + SOURCE_STORAGE_TOKEN_NAME_EXAMPLE + + NEWLINE + + " " + + SOURCE_STORAGE_CERT_NICKNAME_EXAMPLE + + NEWLINE + + " " + + TARGET_STORAGE_CERTIFICATE_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_NSS_DB_PWDFILE_EXAMPLE + + NEWLINE + + " " + + REMOVE_ID_OFFSET_EXAMPLE + + NEWLINE + + " " + + SOURCE_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + TARGET_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY + + NEWLINE); + + System.out.println("Example of 'Rewrap':" + + NEWLINE + + NEWLINE + + " " + + DRM_TOOL + + NEWLINE + + " " + + DRMTOOL_CFG_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + TARGET_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + LOG_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_NSS_DB_PATH_EXAMPLE + + NEWLINE + + " " + + SOURCE_STORAGE_TOKEN_NAME_EXAMPLE + + NEWLINE + + " " + + SOURCE_STORAGE_CERT_NICKNAME_EXAMPLE + + NEWLINE + + " " + + TARGET_STORAGE_CERTIFICATE_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_NSS_DB_PWDFILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + TARGET_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY + + NEWLINE); + + System.out.println("Example of 'Append ID Offset':" + + NEWLINE + + NEWLINE + + " " + + DRM_TOOL + + NEWLINE + + " " + + DRMTOOL_CFG_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + TARGET_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + LOG_FILE_EXAMPLE + + NEWLINE + + " " + + APPEND_ID_OFFSET_EXAMPLE + + NEWLINE + + " " + + SOURCE_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + TARGET_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY + + NEWLINE); + + System.out.println("Example of 'Remove ID Offset':" + + NEWLINE + + NEWLINE + + " " + + DRM_TOOL + + NEWLINE + + " " + + DRMTOOL_CFG_FILE_EXAMPLE + + NEWLINE + + " " + + SOURCE_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + TARGET_LDIF_FILE_EXAMPLE + + NEWLINE + + " " + + LOG_FILE_EXAMPLE + + NEWLINE + + " " + + REMOVE_ID_OFFSET_EXAMPLE + + NEWLINE + + " " + + SOURCE_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + TARGET_DRM_NAMING_CONTEXT_EXAMPLE + + NEWLINE + + " " + + PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY + + NEWLINE); + } + + /*******************/ + /* Logging Methods */ + /*******************/ + + /** + * This method opens a new log file for writing. + * <P> + * + * @param logfile string containing the name of the log file to be opened + */ + private static void open_log(String logfile) { + try { + logger = new PrintWriter( + new BufferedWriter( + new FileWriter(logfile))); + } catch (IOException eFile) { + System.err.println("ERROR: Unable to open file '" + + logfile + + "' for writing: '" + + eFile.toString() + + "'" + + NEWLINE); + System.exit(0); + } + } + + /** + * This method closes the specified log file. + * <P> + * + * @param logfile string containing the name of the log file to be closed + */ + private static void close_log(String logfile) { + logger.close(); + } + + /** + * This method writes the specified message to the log file, and also + * to 'stderr' if the boolean flag is set to 'true'. + * <P> + * + * @param msg string containing the message to be written to the log file + * @param stderr boolean which also writes the message to 'stderr' if 'true' + */ + private static void log(String msg, boolean stderr) { + current_date_and_time = now(LOGGING_DATE_PATTERN); + if (stderr) { + System.err.println(msg); + } + logger.write("[" + + current_date_and_time + + "]: " + + msg); + logger.flush(); + } + + /*********************************************/ + /* PKCS #11: Rewrap RSA Storage Key Methods */ + /*********************************************/ + + /** + * Helper method to determine if two arrays contain the same values. + * + * This method is based upon code from 'com.netscape.kra.StorageKeyUnit'. + * <P> + * + * @param bytes first array of bytes + * @param ints second array of bytes + * @return true if the two arrays are identical + */ + private static boolean arraysEqual(byte[] bytes, byte[] ints) { + if (bytes == null || ints == null) { + return false; + } + + if (bytes.length != ints.length) { + return false; + } + + for (int i = 0; i < bytes.length; i++) { + if (bytes[i] != ints[i]) { + return false; + } + } + + return true; + } + + /** + * This method is used to obtain the private RSA storage key from + * the "source" DRM instance's security databases. + * + * This method is based upon code from 'com.netscape.kra.StorageKeyUnit'. + * <P> + * + * @return the private RSA storage key from the "source" DRM + */ + private static PrivateKey getPrivateKey() { + try { + PrivateKey pk[] = mSourceToken.getCryptoStore().getPrivateKeys(); + + for (int i = 0; i < pk.length; i++) { + if (arraysEqual(pk[i].getUniqueID(), + ((TokenCertificate) + mUnwrapCert).getUniqueID())) { + return pk[i]; + } + } + } catch (TokenException exToken) { + log("ERROR: Getting private key - " + + "TokenException: '" + + exToken.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + return null; + } + + /** + * This method gets the public key from the certificate stored + * in the "target" DRM storage certificate file. It also obtains + * the keysize of this RSA key. + * + * This method is based upon code from + * 'com.netscape.cmstools.PrettyPrintCert'. + * <P> + * + * @return the public RSA storage key from the "target" DRM + */ + private static PublicKey getPublicKey() { + BufferedReader inputCert = null; + String encodedBASE64CertChunk = new String(); + String encodedBASE64Cert = new String(); + byte decodedBASE64Cert[] = null; + X509CertImpl cert = null; + PublicKey key = null; + RSAPublicKey rsakey = null; + + // Create a DataInputStream() object to the BASE 64 + // encoded certificate contained within the file + // specified on the command line + try { + inputCert = new BufferedReader( + new InputStreamReader( + new BufferedInputStream( + new FileInputStream( + mTargetStorageCertificateFilename + )))); + } catch (FileNotFoundException exWrapFileNotFound) { + log("ERROR: No target storage " + + "certificate file named '" + + mTargetStorageCertificateFilename + + "' exists! FileNotFoundException: '" + + exWrapFileNotFound.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + // Read the entire contents of the specified BASE 64 encoded + // certificate into a String() object throwing away any + // headers beginning with HEADER and any trailers beginning + // with TRAILER + try { + while ((encodedBASE64CertChunk = inputCert.readLine()) != null) { + if (!(encodedBASE64CertChunk.startsWith(HEADER)) && + !(encodedBASE64CertChunk.startsWith(TRAILER))) { + encodedBASE64Cert += encodedBASE64CertChunk.trim(); + } + } + } catch (IOException exWrapReadLineIO) { + log("ERROR: Unexpected BASE64 " + + "encoded error encountered while reading '" + + mTargetStorageCertificateFilename + + "'! IOException: '" + + exWrapReadLineIO.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + // Close the DataInputStream() object + try { + inputCert.close(); + } catch (IOException exWrapCloseIO) { + log("ERROR: Unexpected BASE64 " + + "encoded error encountered in closing '" + + mTargetStorageCertificateFilename + + "'! IOException: '" + + exWrapCloseIO.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + // Decode the ASCII BASE 64 certificate enclosed in the + // String() object into a BINARY BASE 64 byte[] object + decodedBASE64Cert = Utils.base64decode( + encodedBASE64Cert); + + // Create an X509CertImpl() object from + // the BINARY BASE 64 byte[] object + try { + cert = new X509CertImpl(decodedBASE64Cert); + } catch (CertificateException exWrapCertificate) { + log("ERROR: Error encountered " + + "in parsing certificate in '" + + mTargetStorageCertificateFilename + + "' CertificateException: '" + + exWrapCertificate.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + // Extract the Public Key + key = cert.getPublicKey(); + if (key == null) { + log("ERROR: Unable to extract public key " + + "from certificate that was stored in '" + + mTargetStorageCertificateFilename + + "'." + + NEWLINE, true); + System.exit(0); + } + + // Convert this X.509 public key --> RSA public key + try { + rsakey = new RSAPublicKey(key.getEncoded()); + } catch (InvalidKeyException exInvalidKey) { + log("ERROR: Converting X.509 public key --> RSA public key - " + + "InvalidKeyException: '" + + exInvalidKey.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + // Obtain the Public Key's keysize + mPublicKeySize = rsakey.getKeySize(); + + return key; + } + + /** + * This method is used to obtain the private RSA storage key + * from the "source" DRM instance's security databases and + * the public RSA storage key from the certificate stored in + * the "target" DRM storage certificate file. + * <P> + * + * @return true if successfully able to obtain both keys + */ + private static boolean obtain_RSA_rewrapping_keys() { + CryptoManager cm = null; + + // Initialize the source security databases + try { + log("Initializing source PKI security databases in '" + + mSourcePKISecurityDatabasePath + "'." + + NEWLINE, true); + + CryptoManager.initialize(mSourcePKISecurityDatabasePath); + } catch (KeyDatabaseException exKey) { + log("ERROR: source_pki_security_database_path='" + + mSourcePKISecurityDatabasePath + + "' KeyDatabaseException: '" + + exKey.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (CertDatabaseException exCert) { + log("ERROR: source_pki_security_database_path='" + + mSourcePKISecurityDatabasePath + + "' CertDatabaseException: '" + + exCert.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (AlreadyInitializedException exAlreadyInitialized) { + log("ERROR: source_pki_security_database_path='" + + mSourcePKISecurityDatabasePath + + "' AlreadyInitializedException: '" + + exAlreadyInitialized.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (GeneralSecurityException exSecurity) { + log("ERROR: source_pki_security_database_path='" + + mSourcePKISecurityDatabasePath + + "' GeneralSecurityException: '" + + exSecurity.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + // Retrieve the source storage token by its name + try { + log("Retrieving token from CryptoManager." + + NEWLINE, true); + cm = CryptoManager.getInstance(); + + log("Retrieving source storage token called '" + + mSourceStorageTokenName + + "'." + + NEWLINE, true); + + if (mSourceStorageTokenName.equals(INTERNAL_TOKEN)) { + mSourceToken = cm.getInternalKeyStorageToken(); + } else { + mSourceToken = cm.getTokenByName(mSourceStorageTokenName); + } + + if (mSourceToken == null) { + return FAILURE; + } + + if (mPwdfileFlag) { + BufferedReader in = null; + String pwd = null; + Password mPwd = null; + + try { + in = new BufferedReader( + new FileReader( + mSourcePKISecurityDatabasePwdfile)); + pwd = in.readLine(); + + mPwd = new Password(pwd.toCharArray()); + + mSourceToken.login(mPwd); + } catch (Exception exReadPwd) { + log("ERROR: Failed to read the keydb password from " + + "the file '" + + mSourcePKISecurityDatabasePwdfile + + "'. Exception: '" + + exReadPwd.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + } + } catch (Exception exUninitialized) { + log("ERROR: Uninitialized CryptoManager - '" + + exUninitialized.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + // Retrieve the source storage cert by its nickname + try { + if (mSourceStorageTokenName.equals(INTERNAL_TOKEN)) { + log("Retrieving source storage cert with nickname of '" + + mSourceStorageCertNickname + + "'." + + NEWLINE, true); + + mUnwrapCert = cm.findCertByNickname(mSourceStorageCertNickname + ); + } else { + log("Retrieving source storage cert with nickname of '" + + mSourceStorageTokenName + + ":" + + mSourceStorageCertNickname + + "'. " + + NEWLINE, true); + mUnwrapCert = cm.findCertByNickname(mSourceStorageTokenName + + ":" + + mSourceStorageCertNickname + ); + } + + if (mUnwrapCert == null) { + return FAILURE; + } + } catch (ObjectNotFoundException exUnwrapObjectNotFound) { + if (mSourceStorageTokenName.equals(INTERNAL_TOKEN)) { + log("ERROR: No internal " + + "source storage cert named '" + + mSourceStorageCertNickname + + "' exists! ObjectNotFoundException: '" + + exUnwrapObjectNotFound.toString() + + "'" + + NEWLINE, true); + } else { + log("ERROR: No " + + "source storage cert named '" + + mSourceStorageTokenName + + ":" + + mSourceStorageCertNickname + + "' exists! ObjectNotFoundException: '" + + exUnwrapObjectNotFound + + "'" + + NEWLINE, true); + } + System.exit(0); + } catch (TokenException exUnwrapToken) { + if (mSourceStorageTokenName.equals(INTERNAL_TOKEN)) { + log("ERROR: No internal " + + "source storage cert named '" + + mSourceStorageCertNickname + + "' exists! TokenException: '" + + exUnwrapToken.toString() + + "'" + + NEWLINE, true); + } else { + log("ERROR: No " + + "source storage cert named '" + + mSourceStorageTokenName + + ":" + + mSourceStorageCertNickname + + "' exists! TokenException: '" + + exUnwrapToken + + "'" + + NEWLINE, true); + } + System.exit(0); + } + + // Extract the private key from the source storage token + log("BEGIN: Obtaining the private key from " + + "the source storage token . . ." + + NEWLINE, true); + + mUnwrapPrivateKey = getPrivateKey(); + + if (mUnwrapPrivateKey == null) { + log("ERROR: Failed extracting " + + "private key from the source storage token." + + NEWLINE, true); + System.exit(0); + } + + log("FINISHED: Obtaining the private key from " + + "the source storage token." + + NEWLINE, true); + + // Extract the public key from the target storage certificate + try { + log("BEGIN: Obtaining the public key from " + + "the target storage certificate . . ." + + NEWLINE, true); + + mWrapPublicKey = (PublicKey) + (PK11PubKey.fromSPKI( + getPublicKey().getEncoded())); + + if (mWrapPublicKey == null) { + log("ERROR: Failed extracting " + + "public key from target storage certificate stored in '" + + mTargetStorageCertificateFilename + + "'" + + NEWLINE, true); + System.exit(0); + } + + log("FINISHED: Obtaining the public key from " + + "the target storage certificate." + + NEWLINE, true); + } catch (InvalidKeyFormatException exInvalidPublicKey) { + log("ERROR: Failed extracting " + + "public key from target storage certificate stored in '" + + mTargetStorageCertificateFilename + + "' InvalidKeyFormatException '" + + exInvalidPublicKey.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + return SUCCESS; + } + + /** + * This method basically rewraps the "wrappedKeyData" by implementiing + * "mStorageUnit.decryptInternalPrivate( byte wrappedKeyData[] )" and + * "mStorageUnit.encryptInternalPrivate( byte priKey[] )", where + * "wrappedKeyData" uses the following structure: + * + * SEQUENCE { + * encryptedSession OCTET STRING, + * encryptedPrivate OCTET STRING + * } + * + * This method is based upon code from + * 'com.netscape.kra.EncryptionUnit'. + * <P> + * + * @return a byte[] containing the rewrappedKeyData + */ + private static byte[] rewrap_wrapped_key_data(byte[] wrappedKeyData) + throws Exception { + DerValue val = null; + DerInputStream in = null; + DerValue dSession = null; + byte source_session[] = null; + DerValue dPri = null; + byte pri[] = null; + KeyWrapper source_rsaWrap = null; + SymmetricKey sk = null; + KeyWrapper target_rsaWrap = null; + byte target_session[] = null; + DerOutputStream tmp = null; + DerOutputStream out = null; + byte[] rewrappedKeyData = null; + + // public byte[] + // mStorageUnit.decryptInternalPrivate( byte wrappedKeyData[] ); + // throws EBaseException + try { + val = new DerValue(wrappedKeyData); + in = val.data; + dSession = in.getDerValue(); + source_session = dSession.getOctetString(); + dPri = in.getDerValue(); + pri = dPri.getOctetString(); + source_rsaWrap = mSourceToken.getKeyWrapper( + KeyWrapAlgorithm.RSA); + source_rsaWrap.initUnwrap(mUnwrapPrivateKey, null); + sk = source_rsaWrap.unwrapSymmetric(source_session, + SymmetricKey.DES3, + SymmetricKey.Usage.DECRYPT, + 0); + if (mDebug) { + log("DEBUG: sk = '" + + Utils.base64encode(sk.getEncoded()) + + "' length = '" + + sk.getEncoded().length + + "'" + + NEWLINE, false); + log("DEBUG: pri = '" + + Utils.base64encode(pri) + + "' length = '" + + pri.length + + "'" + + NEWLINE, false); + } + } catch (IOException exUnwrapIO) { + log("ERROR: Unwrapping key data - " + + "IOException: '" + + exUnwrapIO.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (NoSuchAlgorithmException exUnwrapAlgorithm) { + log("ERROR: Unwrapping key data - " + + "NoSuchAlgorithmException: '" + + exUnwrapAlgorithm.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (TokenException exUnwrapToken) { + log("ERROR: Unwrapping key data - " + + "TokenException: '" + + exUnwrapToken.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (InvalidKeyException exUnwrapInvalidKey) { + log("ERROR: Unwrapping key data - " + + "InvalidKeyException: '" + + exUnwrapInvalidKey.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (InvalidAlgorithmParameterException exUnwrapInvalidAlgorithm) { + log("ERROR: Unwrapping key data - " + + "InvalidAlgorithmParameterException: '" + + exUnwrapInvalidAlgorithm.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (IllegalStateException exUnwrapState) { + log("ERROR: Unwrapping key data - " + + "InvalidStateException: '" + + exUnwrapState.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + // public byte[] + // mStorageUnit.encryptInternalPrivate( byte priKey[] ) + // throws EBaseException + try { + // Use "mSourceToken" to get "KeyWrapAlgorithm.RSA" + target_rsaWrap = mSourceToken.getKeyWrapper( + KeyWrapAlgorithm.RSA); + target_rsaWrap.initWrap(mWrapPublicKey, null); + target_session = target_rsaWrap.wrap(sk); + + tmp = new DerOutputStream(); + out = new DerOutputStream(); + + tmp.putOctetString(target_session); + tmp.putOctetString(pri); + out.write(DerValue.tag_Sequence, tmp); + + rewrappedKeyData = out.toByteArray(); + } catch (NoSuchAlgorithmException exWrapAlgorithm) { + log("ERROR: Wrapping key data - " + + "NoSuchAlgorithmException: '" + + exWrapAlgorithm.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (TokenException exWrapToken) { + log("ERROR: Wrapping key data - " + + "TokenException: '" + + exWrapToken.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (InvalidKeyException exWrapInvalidKey) { + log("ERROR: Wrapping key data - " + + "InvalidKeyException: '" + + exWrapInvalidKey.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (InvalidAlgorithmParameterException exWrapInvalidAlgorithm) { + log("ERROR: Wrapping key data - " + + "InvalidAlgorithmParameterException: '" + + exWrapInvalidAlgorithm.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (IllegalStateException exWrapState) { + log("ERROR: Wrapping key data - " + + "InvalidStateException: '" + + exWrapState.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (IOException exWrapIO) { + log("ERROR: Wrapping key data - " + + "IOException: '" + + exWrapIO.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + return rewrappedKeyData; + } + + /** + * Helper method used to remove all EOLs ('\n' and '\r') + * from the passed in string. + * <P> + * + * @param data consisting of a string containing EOLs + * @return a string consisting of a string with no EOLs + */ + private static String stripEOL(String data) { + StringBuffer buffer = new StringBuffer(); + String revised_data = null; + + for (int i = 0; i < data.length(); i++) { + if ((data.charAt(i) != '\n') && + (data.charAt(i) != '\r')) { + buffer.append(data.charAt(i)); + } + } + + revised_data = buffer.toString(); + + return revised_data; + } + + /** + * Helper method used to format a string containing unformatted data + * into a string containing formatted data suitable as an entry for + * an LDIF file. + * <P> + * + * @param length the length of the first line of data + * @param data a string containing unformatted data + * @return formatted data consisting of data formatted for an LDIF record + * suitable for an LDIF file + */ + private static String format_ldif_data(int length, String data) { + String revised_data = ""; + + if (data.length() > length) { + // process first line + for (int i = 0; i < length; i++) { + revised_data += data.charAt(i); + } + + // terminate first line + revised_data += '\n'; + + // process remaining lines + int j = 0; + for (int i = length; i < data.length(); i++) { + if (j == 0) { + revised_data += ' '; + } + + revised_data += data.charAt(i); + + j++; + + if (j == 76) { + revised_data += '\n'; + j = 0; + } + } + } + + return revised_data.replaceAll("\\s+$", ""); + } + + /*********************/ + /* ID Offset Methods */ + /*********************/ + + /** + * Helper method which converts an "indexed" BigInteger into + * its String representation. + * + * <PRE> + * + * NOTE: Indexed data means that the numeric data + * is stored with a prepended length + * (e. g. - record '73' is stored as '0273'). + * + * Indexed data is currently limited to '99' digits + * (an index of '00' is invalid). See + * 'com.netscape.cmscore.dbs.BigIntegerMapper.java' + * for details. + * + * </PRE> + * + * This method is based upon code from + * 'com.netscape.cmscore.dbs.BigIntegerMapper'. + * <P> + * + * @param i an "indexed " BigInteger + * @return the string representation of the "indexed" BigInteger + */ + private static String BigIntegerToDB(BigInteger i) { + int len = i.toString().length(); + String ret = null; + + if (len < 10) { + ret = "0" + Integer.toString(len) + i.toString(); + } else { + ret = Integer.toString(len) + i.toString(); + } + return ret; + } + + /** + * Helper method which converts the string representation of an + * "indexed" integer into a BigInteger. + * + * <PRE> + * NOTE: Indexed data means that the numeric data + * is stored with a prepended length + * (e. g. - record '73' is stored as '0273'). + * + * Indexed data is currently limited to '99' digits + * (an index of '00' is invalid). See + * 'com.netscape.cmscore.dbs.BigIntegerMapper.java' + * for details. + * </PRE> + * + * This method is based upon code from + * 'com.netscape.cmscore.dbs.BigIntegerMapper'. + * <P> + * + * @param i the string representation of the "indexed" integer + * @return an "indexed " BigInteger + */ + private static BigInteger BigIntegerFromDB(String i) { + String s = i.substring(2); + + // possibly check length + return new BigInteger(s); + } + + /** + * This method accepts an "attribute", its "delimiter", a string + * representation of numeric data, and a flag indicating whether + * or not the string representation is "indexed". + * + * An "attribute" consists of one of the following values: + * + * <PRE> + * DRM_LDIF_CN = "cn:"; + * DRM_LDIF_DN_EMBEDDED_CN_DATA = "dn: cn"; + * DRM_LDIF_EXTDATA_KEY_RECORD = "extdata-keyrecord:"; + * DRM_LDIF_EXTDATA_REQUEST_ID = "extdata-requestid:"; + * DRM_LDIF_EXTDATA_SERIAL_NUMBER = "extdata-serialnumber:"; + * DRM_LDIF_REQUEST_ID = "requestId:"; + * DRM_LDIF_SERIAL_NO = "serialno:"; + * + * + * NOTE: Indexed data means that the numeric data + * is stored with a prepended length + * (e. g. - record '73' is stored as '0273'). + * + * Indexed data is currently limited to '99' digits + * (an index of '00' is invalid). See + * 'com.netscape.cmscore.dbs.BigIntegerMapper.java' + * for details. + * </PRE> + * + * <P> + * + * @param attribute the string representation of the "name" + * @param delimiter the separator between the attribute and its contents + * @param source_line the string containing the "name" and "value" + * @param indexed boolean flag indicating if the "value" is "indexed" + * @return a revised line containing the "name" and "value" with the + * specified ID offset applied as a "mask" to the "value" + */ + private static String compose_numeric_line(String attribute, + String delimiter, + String source_line, + boolean indexed) { + String target_line = null; + String data = null; + String revised_data = null; + BigInteger value = null; + + // Since both "-append_id_offset" and "-remove_id_offset" are OPTIONAL + // parameters, first check to see if either has been selected + if (!mAppendIdOffsetFlag && + !mRemoveIdOffsetFlag) { + return source_line; + } + + try { + // extract the data + data = source_line.substring(attribute.length() + 1).trim(); + + // skip values which are non-numeric + if (!data.matches("[0-9]++")) { + // set the target_line to the unchanged source_line + target_line = source_line; + + // log this information + log("Skipped changing non-numeric line '" + + source_line + + "'." + + NEWLINE, false); + } else { + // if indexed, first strip the index from the data + if (indexed) { + // NOTE: Indexed data means that the numeric data + // is stored with a prepended length + // (e. g. - record '73' is stored as '0273'). + // + // Indexed data is currently limited to '99' digits + // (an index of '00' is invalid). See + // 'com.netscape.cmscore.dbs.BigIntegerMapper.java' + // for details. + value = BigIntegerFromDB(data); + } else { + value = new BigInteger(data); + } + + // compare the specified target ID offset + // with the actual value of the attribute + if (mAppendIdOffsetFlag) { + if (mAppendIdOffset.compareTo(value) == 1) { + // add the target ID offset to this value + if (indexed) { + revised_data = BigIntegerToDB( + value.add(mAppendIdOffset) + ).toString(); + } else { + revised_data = value.add( + mAppendIdOffset).toString(); + } + } else { + log("ERROR: attribute='" + + attribute + + "' is greater than the specified " + + "append_id_offset='" + + mAppendIdOffset.toString() + + "'!" + + NEWLINE, true); + System.exit(0); + } + } else if (mRemoveIdOffsetFlag) { + if (mRemoveIdOffset.compareTo(value) <= 0) { + // subtract the target ID offset to this value + if (indexed) { + revised_data = BigIntegerToDB( + value.subtract(mRemoveIdOffset) + ).toString(); + } else { + revised_data = value.subtract(mRemoveIdOffset + ).toString(); + } + } else { + log("ERROR: attribute='" + + attribute + + "' is less than the specified " + + "remove_id_offset='" + + mRemoveIdOffset.toString() + + "'!" + + NEWLINE, true); + System.exit(0); + } + } + + // set the target_line to the revised data + target_line = attribute + delimiter + revised_data; + + // log this information + log("Changed numeric data '" + + data + + "' to '" + + revised_data + + "'." + + NEWLINE, false); + } + } catch (IndexOutOfBoundsException exBounds) { + log("ERROR: source_line='" + + source_line + + "' IndexOutOfBoundsException: '" + + exBounds.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } catch (PatternSyntaxException exPattern) { + log("ERROR: data='" + + data + + "' PatternSyntaxException: '" + + exPattern.toString() + + "'" + + NEWLINE, true); + System.exit(0); + } + + return target_line; + } + + /***********************/ + /* LDIF Parser Methods */ + /***********************/ + + /** + * Helper method which composes the output line for DRM_LDIF_CN. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_cn(String record_type, + String line) { + String output = null; + + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + if (drmtoolCfg.get(DRMTOOL_CFG_ENROLLMENT_CN)) { + output = compose_numeric_line(DRM_LDIF_CN, + SPACE, + line, + false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_CA_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_CA_KEY_RECORD_CN)) { + output = compose_numeric_line(DRM_LDIF_CN, + SPACE, + line, + false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_RECOVERY)) { + if (drmtoolCfg.get(DRMTOOL_CFG_RECOVERY_CN)) { + output = compose_numeric_line(DRM_LDIF_CN, + SPACE, + line, + false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_TPS_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_TPS_KEY_RECORD_CN)) { + output = compose_numeric_line(DRM_LDIF_CN, + SPACE, + line, + false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_KEYGEN)) { + if (drmtoolCfg.get(DRMTOOL_CFG_KEYGEN_CN)) { + output = compose_numeric_line(DRM_LDIF_CN, + SPACE, + line, + false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_RECORD)) { + // Non-Request / Non-Key Record: + // Pass through the original + // 'cn' line UNCHANGED + // so that it is ALWAYS written + output = line; + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_CN + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for DRM_LDIF_DATE_OF_MODIFY. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_date_of_modify(String record_type, + String line) { + String output = null; + + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + if (drmtoolCfg.get(DRMTOOL_CFG_ENROLLMENT_DATE_OF_MODIFY)) { + output = DRM_LDIF_DATE_OF_MODIFY + + SPACE + + mDateOfModify; + + log("Changed '" + + line + + "' to '" + + output + + "'." + + NEWLINE, false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_CA_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_CA_KEY_RECORD_DATE_OF_MODIFY)) { + output = DRM_LDIF_DATE_OF_MODIFY + + SPACE + + mDateOfModify; + + log("Changed '" + + line + + "' to '" + + output + + "'." + + NEWLINE, false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_RECOVERY)) { + if (drmtoolCfg.get(DRMTOOL_CFG_RECOVERY_DATE_OF_MODIFY)) { + output = DRM_LDIF_DATE_OF_MODIFY + + SPACE + + mDateOfModify; + + log("Changed '" + + line + + "' to '" + + output + + "'." + + NEWLINE, false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_TPS_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_TPS_KEY_RECORD_DATE_OF_MODIFY)) { + output = DRM_LDIF_DATE_OF_MODIFY + + SPACE + + mDateOfModify; + + log("Changed '" + + line + + "' to '" + + output + + "'." + + NEWLINE, false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_KEYGEN)) { + if (drmtoolCfg.get(DRMTOOL_CFG_KEYGEN_DATE_OF_MODIFY)) { + output = DRM_LDIF_DATE_OF_MODIFY + + SPACE + + mDateOfModify; + + log("Changed '" + + line + + "' to '" + + output + + "'." + + NEWLINE, false); + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_DATE_OF_MODIFY + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for DRM_LDIF_DN. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_dn(String record_type, + String line) { + String embedded_cn_data[] = null; + String embedded_cn_output = null; + String input = null; + String output = null; + + try { + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + if (drmtoolCfg.get(DRMTOOL_CFG_ENROLLMENT_DN)) { + + // First check for an embedded "cn=<value>" + // name-value pair + if (line.startsWith(DRM_LDIF_DN_EMBEDDED_CN_DATA)) { + // At this point, always extract + // the embedded "cn=<value>" name-value pair + // which will ALWAYS be the first + // portion of the "dn: " attribute + embedded_cn_data = line.split(COMMA, 2); + + embedded_cn_output = compose_numeric_line( + DRM_LDIF_DN_EMBEDDED_CN_DATA, + EQUAL_SIGN, + embedded_cn_data[0], + false); + + input = embedded_cn_output + + COMMA + + embedded_cn_data[1]; + } else { + input = line; + } + + // Since "-source_drm_naming_context", and + // "-target_drm_naming_context" are OPTIONAL + // parameters, ONLY process this portion of the field + // if both of these options have been selected + if (mDrmNamingContextsFlag) { + output = input.replace(mSourceDrmNamingContext, + mTargetDrmNamingContext); + } else { + output = input; + } + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_CA_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_CA_KEY_RECORD_DN)) { + + // First check for an embedded "cn=<value>" + // name-value pair + if (line.startsWith(DRM_LDIF_DN_EMBEDDED_CN_DATA)) { + // At this point, always extract + // the embedded "cn=<value>" name-value pair + // which will ALWAYS be the first + // portion of the "dn: " attribute + embedded_cn_data = line.split(COMMA, 2); + + embedded_cn_output = compose_numeric_line( + DRM_LDIF_DN_EMBEDDED_CN_DATA, + EQUAL_SIGN, + embedded_cn_data[0], + false); + + input = embedded_cn_output + + COMMA + + embedded_cn_data[1]; + } else { + input = line; + } + + // Since "-source_drm_naming_context", and + // "-target_drm_naming_context" are OPTIONAL + // parameters, ONLY process this portion of the field + // if both of these options have been selected + if (mDrmNamingContextsFlag) { + output = input.replace(mSourceDrmNamingContext, + mTargetDrmNamingContext); + } else { + output = input; + } + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_RECOVERY)) { + if (drmtoolCfg.get(DRMTOOL_CFG_RECOVERY_DN)) { + + // First check for an embedded "cn=<value>" + // name-value pair + if (line.startsWith(DRM_LDIF_DN_EMBEDDED_CN_DATA)) { + // At this point, always extract + // the embedded "cn=<value>" name-value pair + // which will ALWAYS be the first + // portion of the "dn: " attribute + embedded_cn_data = line.split(COMMA, 2); + + embedded_cn_output = compose_numeric_line( + DRM_LDIF_DN_EMBEDDED_CN_DATA, + EQUAL_SIGN, + embedded_cn_data[0], + false); + + input = embedded_cn_output + + COMMA + + embedded_cn_data[1]; + } else { + input = line; + } + + // Since "-source_drm_naming_context", and + // "-target_drm_naming_context" are OPTIONAL + // parameters, ONLY process this portion of the field + // if both of these options have been selected + if (mDrmNamingContextsFlag) { + output = input.replace(mSourceDrmNamingContext, + mTargetDrmNamingContext); + } else { + output = input; + } + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_TPS_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_TPS_KEY_RECORD_DN)) { + + // First check for an embedded "cn=<value>" + // name-value pair + if (line.startsWith(DRM_LDIF_DN_EMBEDDED_CN_DATA)) { + // At this point, always extract + // the embedded "cn=<value>" name-value pair + // which will ALWAYS be the first + // portion of the "dn: " attribute + embedded_cn_data = line.split(COMMA, 2); + + embedded_cn_output = compose_numeric_line( + DRM_LDIF_DN_EMBEDDED_CN_DATA, + EQUAL_SIGN, + embedded_cn_data[0], + false); + + input = embedded_cn_output + + COMMA + + embedded_cn_data[1]; + } else { + input = line; + } + + // Since "-source_drm_naming_context", and + // "-target_drm_naming_context" are OPTIONAL + // parameters, ONLY process this portion of the field + // if both of these options have been selected + if (mDrmNamingContextsFlag) { + output = input.replace(mSourceDrmNamingContext, + mTargetDrmNamingContext); + } else { + output = input; + } + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_KEYGEN)) { + if (drmtoolCfg.get(DRMTOOL_CFG_KEYGEN_DN)) { + + // First check for an embedded "cn=<value>" + // name-value pair + if (line.startsWith(DRM_LDIF_DN_EMBEDDED_CN_DATA)) { + // At this point, always extract + // the embedded "cn=<value>" name-value pair + // which will ALWAYS be the first + // portion of the "dn: " attribute + embedded_cn_data = line.split(COMMA, 2); + + embedded_cn_output = compose_numeric_line( + DRM_LDIF_DN_EMBEDDED_CN_DATA, + EQUAL_SIGN, + embedded_cn_data[0], + false); + + input = embedded_cn_output + + COMMA + + embedded_cn_data[1]; + } else { + input = line; + } + + // Since "-source_drm_naming_context", and + // "-target_drm_naming_context" are OPTIONAL + // parameters, ONLY process this portion of the field + // if both of these options have been selected + if (mDrmNamingContextsFlag) { + output = input.replace(mSourceDrmNamingContext, + mTargetDrmNamingContext); + } else { + output = input; + } + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_RECORD)) { + // Non-Request / Non-Key Record: + // Pass through the original + // 'dn' line UNCHANGED + // so that it is ALWAYS written + output = line; + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_DN + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + } catch (PatternSyntaxException exDnEmbeddedCnNameValuePattern) { + log("ERROR: line='" + + line + + "' PatternSyntaxException: '" + + exDnEmbeddedCnNameValuePattern.toString() + + "'" + + NEWLINE, true); + } catch (NullPointerException exNullPointerException) { + log("ERROR: Unable to replace source DRM naming context '" + + mSourceDrmNamingContext + + "' with target DRM naming context '" + + mTargetDrmNamingContext + + "' NullPointerException: '" + + exNullPointerException.toString() + + "'" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for + * DRM_LDIF_EXTDATA_KEY_RECORD. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_extdata_key_record(String record_type, + String line) { + String output = null; + + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + if (drmtoolCfg.get(DRMTOOL_CFG_ENROLLMENT_EXTDATA_KEY_RECORD)) { + output = compose_numeric_line(DRM_LDIF_EXTDATA_KEY_RECORD, + SPACE, + line, + false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_KEYGEN)) { + if (drmtoolCfg.get(DRMTOOL_CFG_KEYGEN_EXTDATA_KEY_RECORD)) { + output = compose_numeric_line(DRM_LDIF_EXTDATA_KEY_RECORD, + SPACE, + line, + false); + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_EXTDATA_KEY_RECORD + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for + * DRM_LDIF_EXTDATA_REQUEST_ID. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_extdata_request_id(String record_type, + String line) { + String output = null; + + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + // ALWAYS pass-through "extdata-requestId" for + // DRM_LDIF_ENROLLMENT records UNCHANGED because the + // value in this field is associated with the issuing CA! + output = line; + } else if (record_type.equals(DRM_LDIF_RECOVERY)) { + if (drmtoolCfg.get(DRMTOOL_CFG_RECOVERY_EXTDATA_REQUEST_ID)) { + output = compose_numeric_line(DRM_LDIF_EXTDATA_REQUEST_ID, + SPACE, + line, + false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_KEYGEN)) { + if (drmtoolCfg.get(DRMTOOL_CFG_KEYGEN_EXTDATA_REQUEST_ID)) { + output = compose_numeric_line(DRM_LDIF_EXTDATA_REQUEST_ID, + SPACE, + line, + false); + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_EXTDATA_REQUEST_ID + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for + * DRM_LDIF_EXTDATA_REQUEST_NOTES. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_extdata_request_notes(String record_type, + String line) { + String input = null; + String data = null; + String unformatted_data = null; + String output = null; + String next_line = null; + + // extract the data + if (line.length() > DRM_LDIF_EXTDATA_REQUEST_NOTES.length()) { + input = line.substring( + DRM_LDIF_EXTDATA_REQUEST_NOTES.length() + 1 + ).trim(); + } else { + input = line.substring( + DRM_LDIF_EXTDATA_REQUEST_NOTES.length() + ).trim(); + } + + while ((line = ldif_record.next()) != null) { + if (line.startsWith(SPACE)) { + // Do NOT use "trim()"; + // remove single leading space and + // trailing carriage returns and newlines ONLY! + input += line.replaceFirst(" ", "").replace('\r', '\0').replace('\n', '\0'); + } else { + next_line = line; + break; + } + } + + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + if (drmtoolCfg.get(DRMTOOL_CFG_ENROLLMENT_EXTDATA_REQUEST_NOTES)) { + // write out a revised 'extdata-requestnotes' line + if (mRewrapFlag && mAppendIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag && mRemoveIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mAppendIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRemoveIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } + + // log this information + log("Changed:" + + NEWLINE + + TIC + + DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + input) + + TIC + + NEWLINE + + "--->" + + NEWLINE + + TIC + + output + + TIC + + NEWLINE, false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_RECOVERY)) { + if (drmtoolCfg.get(DRMTOOL_CFG_RECOVERY_EXTDATA_REQUEST_NOTES)) { + // write out a revised 'extdata-requestnotes' line + if (mRewrapFlag && mAppendIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag && mRemoveIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mAppendIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRemoveIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } + + // log this information + log("Changed:" + + NEWLINE + + TIC + + DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + input) + + TIC + + NEWLINE + + "--->" + + NEWLINE + + TIC + + output + + TIC + + NEWLINE, false); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_KEYGEN)) { + if (drmtoolCfg.get(DRMTOOL_CFG_KEYGEN_EXTDATA_REQUEST_NOTES)) { + // write out a revised 'extdata-requestnotes' line + if (mRewrapFlag && mAppendIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag && mRemoveIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mAppendIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRemoveIdOffsetFlag) { + data = input + + SPACE + + LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } + + // log this information + log("Changed:" + + NEWLINE + + TIC + + DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + input) + + TIC + + NEWLINE + + "--->" + + NEWLINE + + TIC + + output + + TIC + + NEWLINE, false); + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_EXTDATA_REQUEST_NOTES + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + + if (output != null) { + output += NEWLINE + next_line; + } + + return output; + } + + /** + * Helper method which composes the output line for + * DRM_LDIF_EXTDATA_REQUEST_NOTES. + * <P> + * + * @param record_type the string representation of the input record type + * @param previous_line the string representation of the previous input line + * @param writer the PrintWriter used to output this new LDIF line + * @return the composed output line + */ + private static void create_extdata_request_notes(String record_type, + String previous_line, + PrintWriter writer) { + String data = null; + String unformatted_data = null; + String output = null; + + if (record_type.equals(DRM_LDIF_RECOVERY)) { + if (drmtoolCfg.get(DRMTOOL_CFG_RECOVERY_EXTDATA_REQUEST_NOTES)) { + if (!previous_line.startsWith(DRM_LDIF_EXTDATA_REQUEST_NOTES)) { + // write out the missing 'extdata-requestnotes' line + if (mRewrapFlag && mAppendIdOffsetFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag && mRemoveIdOffsetFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mAppendIdOffsetFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRemoveIdOffsetFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } + + // log this information + log("Created:" + + NEWLINE + + TIC + + output + + TIC + + NEWLINE, false); + + // Write out this revised line + // and flush the buffer + writer.write(output + NEWLINE); + writer.flush(); + System.out.print("."); + } + } + } else if (record_type.equals(DRM_LDIF_KEYGEN)) { + if (drmtoolCfg.get(DRMTOOL_CFG_KEYGEN_EXTDATA_REQUEST_NOTES)) { + if (!previous_line.startsWith(DRM_LDIF_EXTDATA_REQUEST_NOTES)) { + // write out the missing 'extdata-requestnotes' line + if (mRewrapFlag && mAppendIdOffsetFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag && mRemoveIdOffsetFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + SPACE + + PLUS + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRewrapFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REWRAP_MESSAGE + + mPublicKeySize + + DRM_LDIF_RSA_MESSAGE + + mSourcePKISecurityDatabasePwdfileMessage + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mAppendIdOffsetFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_APPENDED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mAppendIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } else if (mRemoveIdOffsetFlag) { + data = LEFT_BRACE + + mDateOfModify + + RIGHT_BRACE + + COLON + SPACE + + DRM_LDIF_REMOVED_ID_OFFSET_MESSAGE + + SPACE + + TIC + + mRemoveIdOffset.toString() + + TIC + + mDrmNamingContextMessage + + mProcessRequestsAndKeyRecordsOnlyMessage; + + // Unformat the data + unformatted_data = stripEOL(data); + + // Format the unformatted_data + // to match the desired LDIF format + output = DRM_LDIF_EXTDATA_REQUEST_NOTES + + SPACE + + format_ldif_data( + EXTDATA_REQUEST_NOTES_FIRST_LINE_DATA_LENGTH, + unformatted_data); + } + + // log this information + log("Created:" + + NEWLINE + + TIC + + output + + TIC + + NEWLINE, false); + + // Write out this revised line + // and flush the buffer + writer.write(output + NEWLINE); + writer.flush(); + System.out.print("."); + } + } + } + } + + /** + * Helper method which composes the output line for + * DRM_LDIF_EXTDATA_SERIAL_NUMBER. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_extdata_serial_number(String record_type, + String line) { + String output = null; + + if (record_type.equals(DRM_LDIF_RECOVERY)) { + if (drmtoolCfg.get(DRMTOOL_CFG_RECOVERY_EXTDATA_SERIAL_NUMBER)) { + output = compose_numeric_line(DRM_LDIF_EXTDATA_SERIAL_NUMBER, + SPACE, + line, + false); + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_EXTDATA_SERIAL_NUMBER + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for + * DRM_LDIF_PRIVATE_KEY_DATA. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_private_key_data(String record_type, + String line) { + byte source_wrappedKeyData[] = null; + byte target_wrappedKeyData[] = null; + String data = null; + String revised_data = null; + String unformatted_data = null; + String formatted_data = null; + String output = null; + + try { + if (record_type.equals(DRM_LDIF_CA_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_CA_KEY_RECORD_PRIVATE_KEY_DATA)) { + // Since "-source_pki_security_database_path", + // "-source_storage_token_name", + // "-source_storage_certificate_nickname", and + // "-target_storage_certificate_file" are OPTIONAL + // parameters, ONLY process this field if all of + // these options have been selected + if (mRewrapFlag) { + // extract the data + data = line.substring( + DRM_LDIF_PRIVATE_KEY_DATA.length() + 1 + ).trim(); + + while ((line = ldif_record.next()) != null) { + if (line.startsWith(SPACE)) { + data += line.trim(); + } else { + break; + } + } + + // Decode the ASCII BASE 64 certificate + // enclosed in the String() object + // into a BINARY BASE 64 byte[] object + source_wrappedKeyData = + Utils.base64decode(data); + + // rewrap the source wrapped private key data + target_wrappedKeyData = rewrap_wrapped_key_data( + source_wrappedKeyData); + + // Encode the BINARY BASE 64 byte[] object + // into an ASCII BASE 64 certificate + // enclosed in a String() object + revised_data = Utils.base64encode( + target_wrappedKeyData); + + // Unformat the ASCII BASE 64 certificate + // for the log file + unformatted_data = stripEOL(revised_data); + + // Format the ASCII BASE 64 certificate + // to match the desired LDIF format + formatted_data = format_ldif_data( + PRIVATE_KEY_DATA_FIRST_LINE_DATA_LENGTH, + unformatted_data); + + // construct a revised 'privateKeyData' line + output = DRM_LDIF_PRIVATE_KEY_DATA + + SPACE + + formatted_data + + NEWLINE + + line; + + // log this information + log("Changed 'privateKeyData' from:" + + NEWLINE + + TIC + + data + + TIC + + NEWLINE + + " to:" + + NEWLINE + + TIC + + unformatted_data + + TIC + + NEWLINE, false); + } else { + output = line; + } + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_TPS_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_TPS_KEY_RECORD_PRIVATE_KEY_DATA)) { + // Since "-source_pki_security_database_path", + // "-source_storage_token_name", + // "-source_storage_certificate_nickname", and + // "-target_storage_certificate_file" are OPTIONAL + // parameters, ONLY process this field if all of + // these options have been selected + if (mRewrapFlag) { + // extract the data + data = line.substring( + DRM_LDIF_PRIVATE_KEY_DATA.length() + 1 + ).trim(); + + while ((line = ldif_record.next()) != null) { + if (line.startsWith(SPACE)) { + data += line.trim(); + } else { + break; + } + } + + // Decode the ASCII BASE 64 certificate + // enclosed in the String() object + // into a BINARY BASE 64 byte[] object + source_wrappedKeyData = + Utils.base64decode(data); + + // rewrap the source wrapped private key data + target_wrappedKeyData = rewrap_wrapped_key_data( + source_wrappedKeyData); + + // Encode the BINARY BASE 64 byte[] object + // into an ASCII BASE 64 certificate + // enclosed in a String() object + revised_data = Utils.base64encode( + target_wrappedKeyData); + + // Unformat the ASCII BASE 64 certificate + // for the log file + unformatted_data = stripEOL(revised_data); + + // Format the ASCII BASE 64 certificate + // to match the desired LDIF format + formatted_data = format_ldif_data( + PRIVATE_KEY_DATA_FIRST_LINE_DATA_LENGTH, + unformatted_data); + + // construct a revised 'privateKeyData' line + output = DRM_LDIF_PRIVATE_KEY_DATA + + SPACE + + formatted_data + + NEWLINE + + line; + + // log this information + log("Changed 'privateKeyData' from:" + + NEWLINE + + TIC + + data + + TIC + + NEWLINE + + " to:" + + NEWLINE + + TIC + + unformatted_data + + TIC + + NEWLINE, false); + } else { + output = line; + } + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_PRIVATE_KEY_DATA + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + } catch (Exception exRewrap) { + log("ERROR: Unable to rewrap BINARY BASE 64 data. " + + "Exception: '" + + exRewrap.toString() + + "'" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for DRM_LDIF_REQUEST_ID. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_request_id(String record_type, + String line) { + String output = null; + + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + if (drmtoolCfg.get(DRMTOOL_CFG_ENROLLMENT_REQUEST_ID)) { + output = compose_numeric_line(DRM_LDIF_REQUEST_ID, + SPACE, + line, + true); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_RECOVERY)) { + if (drmtoolCfg.get(DRMTOOL_CFG_RECOVERY_REQUEST_ID)) { + output = compose_numeric_line(DRM_LDIF_REQUEST_ID, + SPACE, + line, + true); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_KEYGEN)) { + if (drmtoolCfg.get(DRMTOOL_CFG_KEYGEN_REQUEST_ID)) { + output = compose_numeric_line(DRM_LDIF_REQUEST_ID, + SPACE, + line, + true); + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_REQUEST_ID + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for DRM_LDIF_SERIAL_NO. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_serial_no(String record_type, + String line) { + String output = null; + + if (record_type.equals(DRM_LDIF_CA_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_CA_KEY_RECORD_SERIAL_NO)) { + output = compose_numeric_line(DRM_LDIF_SERIAL_NO, + SPACE, + line, + true); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_TPS_KEY_RECORD)) { + if (drmtoolCfg.get(DRMTOOL_CFG_TPS_KEY_RECORD_SERIAL_NO)) { + output = compose_numeric_line(DRM_LDIF_SERIAL_NO, + SPACE, + line, + true); + } else { + output = line; + } + } else if (record_type.equals(DRM_LDIF_RECORD)) { + // Non-Request / Non-Key Record: + // Pass through the original + // 'serialno' line UNCHANGED + // so that it is ALWAYS written + output = line; + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_SERIAL_NO + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for + * DRM_LDIF_EXTDATA_AUTH_TOKEN_USER. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_extdata_auth_token_user(String record_type, + String line) { + String output = null; + + try { + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + // Since "-source_drm_naming_context", and + // "-target_drm_naming_context" are OPTIONAL + // parameters, ONLY process this field if both of + // these options have been selected + if (mDrmNamingContextsFlag) { + output = line.replace(mSourceDrmNamingContext, + mTargetDrmNamingContext); + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_EXTDATA_AUTH_TOKEN_USER + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + } catch (NullPointerException exNullPointerException) { + log("ERROR: Unable to replace source DRM naming context '" + + mSourceDrmNamingContext + + "' with target DRM naming context '" + + mTargetDrmNamingContext + + "' NullPointerException: '" + + exNullPointerException.toString() + + "'" + + NEWLINE, true); + } + + return output; + } + + /** + * Helper method which composes the output line for + * DRM_LDIF_EXTDATA_AUTH_TOKEN_USER_DN. + * <P> + * + * @param record_type the string representation of the input record type + * @param line the string representation of the input line + * @return the composed output line + */ + private static String output_extdata_auth_token_user_dn(String record_type, + String line) { + String output = null; + + try { + if (record_type.equals(DRM_LDIF_ENROLLMENT)) { + // Since "-source_drm_naming_context", and + // "-target_drm_naming_context" are OPTIONAL + // parameters, ONLY process this field if both of + // these options have been selected + if (mDrmNamingContextsFlag) { + output = line.replace(mSourceDrmNamingContext, + mTargetDrmNamingContext); + } else { + output = line; + } + } else { + log("ERROR: Mismatched record field='" + + DRM_LDIF_EXTDATA_AUTH_TOKEN_USER_DN + + "' for record type='" + + record_type + + "'!" + + NEWLINE, true); + } + } catch (NullPointerException exNullPointerException) { + log("ERROR: Unable to replace source DRM naming context '" + + mSourceDrmNamingContext + + "' with target DRM naming context '" + + mTargetDrmNamingContext + + "' NullPointerException: '" + + exNullPointerException.toString() + + "'" + + NEWLINE, true); + } + + return output; + } + + /** + * This method performs the actual parsing of the "source" LDIF file + * and produces the "target" LDIF file. + * <P> + * + * @return true if the "target" LDIF file is successfully created + */ + private static boolean convert_source_ldif_to_target_ldif() { + boolean success = false; + BufferedReader reader = null; + PrintWriter writer = null; + String input = null; + String line = null; + String previous_line = null; + String output = null; + String data = null; + String record_type = null; + + if (mRewrapFlag) { + success = obtain_RSA_rewrapping_keys(); + if (!success) { + return FAILURE; + } + } + + // Create a vector for LDIF input + record = new Vector<String>(INITIAL_LDIF_RECORD_CAPACITY); + + // Process each line in the source LDIF file + // and store it in the target LDIF file + try { + // Open source LDIF file for reading + reader = new BufferedReader( + new FileReader(mSourceLdifFilename)); + + // Open target LDIF file for writing + writer = new PrintWriter( + new BufferedWriter( + new FileWriter(mTargetLdifFilename))); + + System.out.print("PROCESSING: "); + while ((input = reader.readLine()) != null) { + // Read in a record from the source LDIF file and + // add this line of input into the record vector + success = record.add(input); + if (!success) { + return FAILURE; + } + + // Check for the end of an LDIF record + if (!input.equals("")) { + // Check to see if input line identifies the record type + if (input.startsWith(DRM_LDIF_REQUEST_TYPE)) { + // set the record type: + // + // * DRM_LDIF_ENROLLMENT + // * DRM_LDIF_KEYGEN + // * DRM_LDIF_RECOVERY + // + record_type = input.substring( + DRM_LDIF_REQUEST_TYPE.length() + 1 + ).trim(); + if (!record_type.equals(DRM_LDIF_ENROLLMENT) && + !record_type.equals(DRM_LDIF_KEYGEN) && + !record_type.equals(DRM_LDIF_RECOVERY)) { + log("ERROR: Unknown LDIF record type='" + + record_type + + "'!" + + NEWLINE, true); + return FAILURE; + } + } else if (input.startsWith(DRM_LDIF_ARCHIVED_BY)) { + // extract the data + data = input.substring( + DRM_LDIF_ARCHIVED_BY.length() + 1 + ).trim(); + + // set the record type: + // + // * DRM_LDIF_CA_KEY_RECORD + // * DRM_LDIF_TPS_KEY_RECORD + // + if (data.startsWith(DRM_LDIF_TPS_KEY_RECORD)) { + record_type = DRM_LDIF_TPS_KEY_RECORD; + } else if (data.startsWith(DRM_LDIF_CA_KEY_RECORD)) { + record_type = DRM_LDIF_CA_KEY_RECORD; + } else { + log("ERROR: Unable to determine LDIF record type " + + "from data='" + + data + + "'!" + + NEWLINE, true); + return FAILURE; + } + } + + // continue adding input lines into this record + continue; + } + + // If record type is unset, then this record is neither + // an LDIF request record nor an LDIF key record; check + // to see if it needs to be written out to the target + // LDIF file or thrown away. + if ((record_type == null) && + mProcessRequestsAndKeyRecordsOnlyFlag) { + // Mark each removed record with an 'x' + System.out.print("x"); + + // log this information + log("INFO: Throwing away an LDIF record which is " + + "neither a Request nor a Key Record!" + + NEWLINE, false); + + // clear this LDIF record from the record vector + record.clear(); + + // NOTE: there is no need to reset the record type + + // begin adding input lines into a new record + continue; + } else if (record_type == null) { + // Set record type to specify a "generic" LDIF record + record_type = DRM_LDIF_RECORD; + } + + ldif_record = record.iterator(); + + // Process each line of the record: + // * If LDIF Record Type for this line is 'valid' + // * If DRMTOOL Configuration File Parameter is 'true' + // * Process this data + // * Else If DRMTOOL Configuration File Parameter is 'false' + // * Pass through this data unchanged + // * Else If LDIF Record Type for this line is 'invalid' + // * Log error and leave method returning 'false' + while (ldif_record.hasNext()) { + + line = ldif_record.next(); + + if (line.startsWith(DRM_LDIF_CN)) { + output = output_cn(record_type, line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_DATE_OF_MODIFY)) { + output = output_date_of_modify(record_type, line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_DN)) { + output = output_dn(record_type, line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_EXTDATA_KEY_RECORD)) { + output = output_extdata_key_record(record_type, + line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_EXTDATA_REQUEST_ID)) { + output = output_extdata_request_id(record_type, + line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_EXTDATA_REQUEST_NOTES)) { + output = output_extdata_request_notes(record_type, + line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_EXTDATA_REQUEST_TYPE)) { + // if one is not already present, + // compose and write out the missing + // 'extdata_requestnotes' line + create_extdata_request_notes(record_type, + previous_line, + writer); + + // ALWAYS pass through the original + // 'extdata-requesttype' line UNCHANGED + // so that it is ALWAYS written + output = line; + } else if (line.startsWith(DRM_LDIF_EXTDATA_SERIAL_NUMBER)) { + output = output_extdata_serial_number(record_type, + line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_PRIVATE_KEY_DATA)) { + output = output_private_key_data(record_type, + line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_REQUEST_ID)) { + output = output_request_id(record_type, line); + if (output == null) { + return FAILURE; + } + } else if (line.startsWith(DRM_LDIF_SERIAL_NO)) { + output = output_serial_no(record_type, line); + if (output == null) { + return FAILURE; + } + } else if (previous_line != null && + previous_line.startsWith( + DRM_LDIF_EXTDATA_AUTH_TOKEN_USER)) { + output = output_extdata_auth_token_user(record_type, + line); + if (output == null) { + return FAILURE; + } + } else if (previous_line != null && + previous_line.startsWith( + DRM_LDIF_EXTDATA_AUTH_TOKEN_USER_DN)) { + output = output_extdata_auth_token_user_dn(record_type, + line); + if (output == null) { + return FAILURE; + } + } else { + // Pass through line unchanged + output = line; + } + + // Always save a copy of this line + previous_line = output; + + // Always write out the output line and flush the buffer + writer.write(output + NEWLINE); + writer.flush(); + System.out.print("."); + } + // Mark the end of the LDIF record + System.out.print("!"); + + // clear this LDIF record from the record vector + record.clear(); + } + System.out.println(" FINISHED." + NEWLINE); + } catch (IOException exIO) { + log("ERROR: line='" + + line + + "' OR output='" + + output + + "' IOException: '" + + exIO.toString() + + "'" + + NEWLINE, true); + return FAILURE; + } + + return SUCCESS; + } + + /**************************************/ + /* DRMTOOL Config File Parser Methods */ + /**************************************/ + + /** + * This method performs the actual parsing of the DRMTOOL config file + * and initializes how the DRM Record Fields should be processed. + * <P> + * + * @return true if the DRMTOOL config file is successfully processed + */ + private static boolean process_drmtool_config_file() { + BufferedReader reader = null; + String line = null; + String name_value_pair[] = null; + String name = null; + Boolean value = null; + + // Process each line containing a name/value pair + // in the DRMTOOL config file + try { + // Open DRMTOOL config file for reading + reader = new BufferedReader( + new FileReader(mDrmtoolCfgFilename)); + + // Create a hashtable for relevant name/value pairs + drmtoolCfg = new Hashtable<String, Boolean>(); + + System.out.print("PROCESSING DRMTOOL CONFIG FILE: "); + while ((line = reader.readLine()) != null) { + if (line.startsWith(DRMTOOL_CFG_PREFIX)) { + // obtain "name=value" pair + name_value_pair = line.split(EQUAL_SIGN); + + // obtain "name" + name = name_value_pair[0]; + + // compute "boolean" value + if (name_value_pair[1].equals("true")) { + value = Boolean.TRUE; + } else { + value = Boolean.FALSE; + } + + // store relevant DRM LDIF fields for processing + if (name.equals(DRMTOOL_CFG_ENROLLMENT_CN) + || name.equals(DRMTOOL_CFG_ENROLLMENT_DATE_OF_MODIFY) + || name.equals(DRMTOOL_CFG_ENROLLMENT_DN) + || name.equals(DRMTOOL_CFG_ENROLLMENT_EXTDATA_KEY_RECORD) + || name.equals(DRMTOOL_CFG_ENROLLMENT_EXTDATA_REQUEST_NOTES) + || name.equals(DRMTOOL_CFG_ENROLLMENT_REQUEST_ID) + || name.equals(DRMTOOL_CFG_CA_KEY_RECORD_CN) + || name.equals(DRMTOOL_CFG_CA_KEY_RECORD_DATE_OF_MODIFY) + || name.equals(DRMTOOL_CFG_CA_KEY_RECORD_DN) + || name.equals(DRMTOOL_CFG_CA_KEY_RECORD_PRIVATE_KEY_DATA) + || name.equals(DRMTOOL_CFG_CA_KEY_RECORD_SERIAL_NO) + || name.equals(DRMTOOL_CFG_RECOVERY_CN) + || name.equals(DRMTOOL_CFG_RECOVERY_DATE_OF_MODIFY) + || name.equals(DRMTOOL_CFG_RECOVERY_DN) + || name.equals(DRMTOOL_CFG_RECOVERY_EXTDATA_REQUEST_ID) + || name.equals(DRMTOOL_CFG_RECOVERY_EXTDATA_REQUEST_NOTES) + || name.equals(DRMTOOL_CFG_RECOVERY_EXTDATA_SERIAL_NUMBER) + || name.equals(DRMTOOL_CFG_RECOVERY_REQUEST_ID) + || name.equals(DRMTOOL_CFG_TPS_KEY_RECORD_CN) + || name.equals(DRMTOOL_CFG_TPS_KEY_RECORD_DATE_OF_MODIFY) + || name.equals(DRMTOOL_CFG_TPS_KEY_RECORD_DN) + || name.equals(DRMTOOL_CFG_TPS_KEY_RECORD_PRIVATE_KEY_DATA) + || name.equals(DRMTOOL_CFG_TPS_KEY_RECORD_SERIAL_NO) + || name.equals(DRMTOOL_CFG_KEYGEN_CN) + || name.equals(DRMTOOL_CFG_KEYGEN_DATE_OF_MODIFY) + || name.equals(DRMTOOL_CFG_KEYGEN_DN) + || name.equals(DRMTOOL_CFG_KEYGEN_EXTDATA_KEY_RECORD) + || name.equals(DRMTOOL_CFG_KEYGEN_EXTDATA_REQUEST_ID) + || name.equals(DRMTOOL_CFG_KEYGEN_EXTDATA_REQUEST_NOTES) + || name.equals(DRMTOOL_CFG_KEYGEN_REQUEST_ID)) { + drmtoolCfg.put(name, value); + System.out.print("."); + } + } + } + System.out.println(" FINISHED." + NEWLINE); + } catch (FileNotFoundException exDrmtoolCfgFileNotFound) { + log("ERROR: No DRMTOOL config file named '" + + mDrmtoolCfgFilename + + "' exists! FileNotFoundException: '" + + exDrmtoolCfgFileNotFound.toString() + + "'" + + NEWLINE, true); + return FAILURE; + } catch (IOException exDrmtoolCfgIO) { + log("ERROR: line='" + + line + + "' IOException: '" + + exDrmtoolCfgIO.toString() + + "'" + + NEWLINE, true); + return FAILURE; + } catch (PatternSyntaxException exDrmtoolCfgNameValuePattern) { + log("ERROR: line='" + + line + + "' PatternSyntaxException: '" + + exDrmtoolCfgNameValuePattern.toString() + + "'" + + NEWLINE, true); + return FAILURE; + } + + return SUCCESS; + } + + /************/ + /* DRM Tool */ + /************/ + + /** + * The main DRMTool method. + * <P> + * + * @param args DRMTool options + */ + public static void main(String[] args) { + // Variables + String append_id_offset = null; + String remove_id_offset = null; + String process_drm_naming_context_fields = null; + String process_requests_and_key_records_only = null; + String use_PKI_security_database_pwdfile = null; + File cfgFile = null; + File sourceFile = null; + File sourceDBPath = null; + File sourceDBPwdfile = null; + File targetStorageCertFile = null; + File targetFile = null; + File logFile = null; + boolean success = false; + + // Get current date and time + mDateOfModify = now(DATE_OF_MODIFY_PATTERN); + + // Check that the correct number of arguments were + // submitted to the program + if ((args.length != ID_OFFSET_ARGS) && + (args.length != (ID_OFFSET_ARGS + 1)) && + (args.length != (ID_OFFSET_ARGS + 4)) && + (args.length != (ID_OFFSET_ARGS + 5)) && + (args.length != REWRAP_ARGS) && + (args.length != (REWRAP_ARGS + 1)) && + (args.length != (REWRAP_ARGS + 2)) && + (args.length != (REWRAP_ARGS + 3)) && + (args.length != (REWRAP_ARGS + 4)) && + (args.length != (REWRAP_ARGS + 5)) && + (args.length != (REWRAP_ARGS + 6)) && + (args.length != (REWRAP_ARGS + 7)) && + (args.length != REWRAP_AND_ID_OFFSET_ARGS) && + (args.length != (REWRAP_AND_ID_OFFSET_ARGS + 1)) && + (args.length != (REWRAP_AND_ID_OFFSET_ARGS + 2)) && + (args.length != (REWRAP_AND_ID_OFFSET_ARGS + 3)) && + (args.length != (REWRAP_AND_ID_OFFSET_ARGS + 4)) && + (args.length != (REWRAP_AND_ID_OFFSET_ARGS + 5)) && + (args.length != (REWRAP_AND_ID_OFFSET_ARGS + 6)) && + (args.length != (REWRAP_AND_ID_OFFSET_ARGS + 7))) { + System.err.println("ERROR: Incorrect number of arguments!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Process command-line arguments + for (int i = 0; i < args.length; i += 2) { + if (args[i].equals(DRMTOOL_CFG_FILE)) { + mDrmtoolCfgFilename = args[i + 1]; + mMandatoryNameValuePairs++; + } else if (args[i].equals(SOURCE_LDIF_FILE)) { + mSourceLdifFilename = args[i + 1]; + mMandatoryNameValuePairs++; + } else if (args[i].equals(TARGET_LDIF_FILE)) { + mTargetLdifFilename = args[i + 1]; + mMandatoryNameValuePairs++; + } else if (args[i].equals(LOG_FILE)) { + mLogFilename = args[i + 1]; + mMandatoryNameValuePairs++; + } else if (args[i].equals(SOURCE_NSS_DB_PATH)) { + mSourcePKISecurityDatabasePath = args[i + 1]; + mRewrapNameValuePairs++; + } else if (args[i].equals(SOURCE_STORAGE_TOKEN_NAME)) { + mSourceStorageTokenName = args[i + 1]; + mRewrapNameValuePairs++; + } else if (args[i].equals(SOURCE_STORAGE_CERT_NICKNAME)) { + mSourceStorageCertNickname = args[i + 1]; + mRewrapNameValuePairs++; + } else if (args[i].equals(TARGET_STORAGE_CERTIFICATE_FILE)) { + mTargetStorageCertificateFilename = args[i + 1]; + mRewrapNameValuePairs++; + } else if (args[i].equals(SOURCE_NSS_DB_PWDFILE)) { + mSourcePKISecurityDatabasePwdfile = args[i + 1]; + mPKISecurityDatabasePwdfileNameValuePairs++; + } else if (args[i].equals(APPEND_ID_OFFSET)) { + append_id_offset = args[i + 1]; + mAppendIdOffsetNameValuePairs++; + } else if (args[i].equals(REMOVE_ID_OFFSET)) { + remove_id_offset = args[i + 1]; + mRemoveIdOffsetNameValuePairs++; + } else if (args[i].equals(SOURCE_DRM_NAMING_CONTEXT)) { + mSourceDrmNamingContext = args[i + 1]; + mDrmNamingContextNameValuePairs++; + } else if (args[i].equals(TARGET_DRM_NAMING_CONTEXT)) { + mTargetDrmNamingContext = args[i + 1]; + mDrmNamingContextNameValuePairs++; + } else if (args[i].equals(PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY)) { + mProcessRequestsAndKeyRecordsOnlyFlag = true; + i -= 1; + } else { + System.err.println("ERROR: Unknown argument '" + + args[i] + + "'!" + + NEWLINE); + printUsage(); + System.exit(0); + } + } + + // Verify that correct number of valid mandatory + // arguments were submitted to the program + if (mMandatoryNameValuePairs != MANDATORY_NAME_VALUE_PAIRS || + mDrmtoolCfgFilename == null || + mDrmtoolCfgFilename.length() == 0 || + mSourceLdifFilename == null || + mSourceLdifFilename.length() == 0 || + mTargetLdifFilename == null || + mTargetLdifFilename.length() == 0 || + mLogFilename == null || + mLogFilename.length() == 0) { + System.err.println("ERROR: Missing mandatory arguments!" + + NEWLINE); + printUsage(); + System.exit(0); + } else { + // Check for a valid DRMTOOL config file + cfgFile = new File(mDrmtoolCfgFilename); + if (!cfgFile.exists() || + !cfgFile.isFile() || + (cfgFile.length() == 0)) { + System.err.println("ERROR: '" + + mDrmtoolCfgFilename + + "' does NOT exist, is NOT a file, " + + "or is empty!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Check for a valid source LDIF file + sourceFile = new File(mSourceLdifFilename); + if (!sourceFile.exists() || + !sourceFile.isFile() || + (sourceFile.length() == 0)) { + System.err.println("ERROR: '" + + mSourceLdifFilename + + "' does NOT exist, is NOT a file, " + + "or is empty!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Check that the target LDIF file does NOT exist + targetFile = new File(mTargetLdifFilename); + if (targetFile.exists()) { + System.err.println("ERROR: '" + + mTargetLdifFilename + + "' ALREADY exists!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Check that the log file does NOT exist + logFile = new File(mLogFilename); + if (logFile.exists()) { + System.err.println("ERROR: '" + + mLogFilename + + "' ALREADY exists!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Mark the 'Mandatory' flag true + mMandatoryFlag = true; + } + + // Check to see that if the 'Rewrap' command-line options were + // specified, that they are all present and accounted for + if (mRewrapNameValuePairs > 0) { + if (mRewrapNameValuePairs != REWRAP_NAME_VALUE_PAIRS || + mSourcePKISecurityDatabasePath == null || + mSourcePKISecurityDatabasePath.length() == 0 || + mSourceStorageTokenName == null || + mSourceStorageTokenName.length() == 0 || + mSourceStorageCertNickname == null || + mSourceStorageCertNickname.length() == 0 || + mTargetStorageCertificateFilename == null || + mTargetStorageCertificateFilename.length() == 0) { + System.err.println("ERROR: Missing 'Rewrap' arguments!" + + NEWLINE); + printUsage(); + System.exit(0); + } else { + // Check for a valid path to the PKI security databases + sourceDBPath = new File(mSourcePKISecurityDatabasePath); + if (!sourceDBPath.exists() || + !sourceDBPath.isDirectory()) { + System.err.println("ERROR: '" + + mSourcePKISecurityDatabasePath + + "' does NOT exist or " + + "'is NOT a directory!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Check for a valid target storage certificate file + targetStorageCertFile = new File( + mTargetStorageCertificateFilename); + if (!targetStorageCertFile.exists() || + !targetStorageCertFile.isFile() || + (targetStorageCertFile.length() == 0)) { + System.err.println("ERROR: '" + + mTargetStorageCertificateFilename + + "' does NOT exist, is NOT a file, " + + "or is empty!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Mark the 'Rewrap' flag true + mRewrapFlag = true; + } + } + + // Check to see that BOTH append 'ID Offset' command-line options + // and remove 'ID Offset' command-line options were NOT specified + // since these two command-line options are mutually exclusive! + if ((mAppendIdOffsetNameValuePairs > 0) && + (mRemoveIdOffsetNameValuePairs > 0)) { + System.err.println("ERROR: The 'append ID Offset' option " + + "and the 'remove ID Offset' option are " + + "mutually exclusive!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Check to see that if the 'append ID Offset' command-line options + // were specified, that they are all present and accounted for + if (mAppendIdOffsetNameValuePairs > 0) { + if (mAppendIdOffsetNameValuePairs == ID_OFFSET_NAME_VALUE_PAIRS && + append_id_offset != null && + append_id_offset.length() != 0) { + try { + if (!append_id_offset.matches("[0-9]++")) { + System.err.println("ERROR: '" + + append_id_offset + + "' contains non-numeric " + + "characters!" + + NEWLINE); + printUsage(); + System.exit(0); + } else { + mAppendIdOffset = new BigInteger( + append_id_offset); + + // Mark the 'append ID Offset' flag true + mAppendIdOffsetFlag = true; + } + } catch (PatternSyntaxException exAppendPattern) { + System.err.println("ERROR: append_id_offset='" + + append_id_offset + + "' PatternSyntaxException: '" + + exAppendPattern.toString() + + "'" + + NEWLINE); + System.exit(0); + } + } else { + System.err.println("ERROR: Missing " + + "'append ID Offset' arguments!" + + NEWLINE); + printUsage(); + System.exit(0); + } + } + + // Check to see that if the 'remove ID Offset' command-line options + // were specified, that they are all present and accounted for + if (mRemoveIdOffsetNameValuePairs > 0) { + if (mRemoveIdOffsetNameValuePairs == ID_OFFSET_NAME_VALUE_PAIRS && + remove_id_offset != null && + remove_id_offset.length() != 0) { + try { + if (!remove_id_offset.matches("[0-9]++")) { + System.err.println("ERROR: '" + + remove_id_offset + + "' contains non-numeric " + + "characters!" + + NEWLINE); + printUsage(); + System.exit(0); + } else { + mRemoveIdOffset = new BigInteger( + remove_id_offset); + + // Mark the 'remove ID Offset' flag true + mRemoveIdOffsetFlag = true; + } + } catch (PatternSyntaxException exRemovePattern) { + System.err.println("ERROR: remove_id_offset='" + + remove_id_offset + + "' PatternSyntaxException: '" + + exRemovePattern.toString() + + "'" + + NEWLINE); + System.exit(0); + } + } else { + System.err.println("ERROR: Missing " + + "'remove ID Offset' arguments!" + + NEWLINE); + printUsage(); + System.exit(0); + } + } + + // Make certain that at least one of the "Rewrap", "Append ID Offset", + // or "Remove ID Offset" options has been specified + if (!mRewrapFlag && + !mAppendIdOffsetFlag && + !mRemoveIdOffsetFlag) { + System.err.println("ERROR: At least one of the 'rewrap', " + + "'append ID Offset', or 'remove ID Offset' " + + "options MUST be specified!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + // Check to see that if the OPTIONAL + // 'PKI Security Database Password File' + // command-line options were specified, + // that they are all present and accounted for + if (mPKISecurityDatabasePwdfileNameValuePairs > 0) { + if (mPKISecurityDatabasePwdfileNameValuePairs != + PWDFILE_NAME_VALUE_PAIRS || + mSourcePKISecurityDatabasePwdfile == null || + mSourcePKISecurityDatabasePwdfile.length() == 0) { + System.err.println("ERROR: Missing 'Password File' " + + "arguments!" + + NEWLINE); + printUsage(); + System.exit(0); + } else { + if (mRewrapFlag) { + // Check for a valid source PKI + // security database password file + sourceDBPwdfile = new + File(mSourcePKISecurityDatabasePwdfile); + if (!sourceDBPwdfile.exists() || + !sourceDBPwdfile.isFile() || + (sourceDBPwdfile.length() == 0)) { + System.err.println("ERROR: '" + + mSourcePKISecurityDatabasePwdfile + + "' does NOT exist, is NOT a file, " + + "or is empty!" + + NEWLINE); + printUsage(); + System.exit(0); + } + + use_PKI_security_database_pwdfile = SPACE + + SOURCE_NSS_DB_PWDFILE + + SPACE + + TIC + + mSourcePKISecurityDatabasePwdfile + + TIC; + + mSourcePKISecurityDatabasePwdfileMessage = SPACE + + PLUS + + SPACE + + DRM_LDIF_USED_PWDFILE_MESSAGE; + + // Mark the 'Password File' flag true + mPwdfileFlag = true; + } else { + System.err.println("ERROR: The " + + TIC + + SOURCE_NSS_DB_PWDFILE + + TIC + + " option is ONLY valid when " + + "performing rewrapping." + + NEWLINE); + printUsage(); + System.exit(0); + } + } + } else { + use_PKI_security_database_pwdfile = ""; + mSourcePKISecurityDatabasePwdfileMessage = ""; + } + + // Check to see that if the OPTIONAL 'DRM Naming Context' command-line + // options were specified, that they are all present and accounted for + if (mDrmNamingContextNameValuePairs > 0) { + if (mDrmNamingContextNameValuePairs != + NAMING_CONTEXT_NAME_VALUE_PAIRS || + mSourceDrmNamingContext == null || + mSourceDrmNamingContext.length() == 0 || + mTargetDrmNamingContext == null || + mTargetDrmNamingContext.length() == 0) { + System.err.println("ERROR: Both 'source DRM naming context' " + + "and 'target DRM naming context' " + + "options MUST be specified!" + + NEWLINE); + printUsage(); + System.exit(0); + } else { + process_drm_naming_context_fields = SPACE + + SOURCE_DRM_NAMING_CONTEXT + + SPACE + + TIC + + mSourceDrmNamingContext + + TIC + + SPACE + + TARGET_DRM_NAMING_CONTEXT + + SPACE + + TIC + + mTargetDrmNamingContext + + TIC; + + mDrmNamingContextMessage = SPACE + + PLUS + + SPACE + + DRM_LDIF_SOURCE_NAME_CONTEXT_MESSAGE + + mSourceDrmNamingContext + + DRM_LDIF_TARGET_NAME_CONTEXT_MESSAGE + + mTargetDrmNamingContext + + TIC; + + // Mark the 'DRM Naming Contexts' flag true + mDrmNamingContextsFlag = true; + } + } else { + process_drm_naming_context_fields = ""; + mDrmNamingContextMessage = ""; + } + + // Check for OPTIONAL "Process Requests and Key Records ONLY" option + if (mProcessRequestsAndKeyRecordsOnlyFlag) { + process_requests_and_key_records_only = SPACE + + PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY; + mProcessRequestsAndKeyRecordsOnlyMessage = SPACE + PLUS + SPACE + + DRM_LDIF_PROCESS_REQUESTS_AND_KEY_RECORDS_ONLY_MESSAGE; + } else { + process_requests_and_key_records_only = ""; + mProcessRequestsAndKeyRecordsOnlyMessage = ""; + } + + // Enable logging process . . . + open_log(mLogFilename); + + // Begin logging progress . . . + if (mRewrapFlag && mAppendIdOffsetFlag) { + log("BEGIN \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + SOURCE_NSS_DB_PATH + SPACE + + mSourcePKISecurityDatabasePath + SPACE + + SOURCE_STORAGE_TOKEN_NAME + SPACE + + TIC + mSourceStorageTokenName + TIC + SPACE + + SOURCE_STORAGE_CERT_NICKNAME + SPACE + + TIC + mSourceStorageCertNickname + TIC + SPACE + + TARGET_STORAGE_CERTIFICATE_FILE + SPACE + + mTargetStorageCertificateFilename + SPACE + + use_PKI_security_database_pwdfile + + APPEND_ID_OFFSET + SPACE + + append_id_offset + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\" . . ." + + NEWLINE, true); + } else if (mRewrapFlag && mRemoveIdOffsetFlag) { + log("BEGIN \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + SOURCE_NSS_DB_PATH + SPACE + + mSourcePKISecurityDatabasePath + SPACE + + SOURCE_STORAGE_TOKEN_NAME + SPACE + + TIC + mSourceStorageTokenName + TIC + SPACE + + SOURCE_STORAGE_CERT_NICKNAME + SPACE + + TIC + mSourceStorageCertNickname + TIC + SPACE + + TARGET_STORAGE_CERTIFICATE_FILE + SPACE + + mTargetStorageCertificateFilename + SPACE + + use_PKI_security_database_pwdfile + + REMOVE_ID_OFFSET + SPACE + + remove_id_offset + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\" . . ." + + NEWLINE, true); + } else if (mRewrapFlag) { + log("BEGIN \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + SOURCE_NSS_DB_PATH + SPACE + + mSourcePKISecurityDatabasePath + SPACE + + SOURCE_STORAGE_TOKEN_NAME + SPACE + + TIC + mSourceStorageTokenName + TIC + SPACE + + SOURCE_STORAGE_CERT_NICKNAME + SPACE + + TIC + mSourceStorageCertNickname + TIC + SPACE + + TARGET_STORAGE_CERTIFICATE_FILE + SPACE + + mTargetStorageCertificateFilename + + use_PKI_security_database_pwdfile + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\" . . ." + + NEWLINE, true); + } else if (mAppendIdOffsetFlag) { + log("BEGIN \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + APPEND_ID_OFFSET + SPACE + + append_id_offset + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\" . . ." + + NEWLINE, true); + } else if (mRemoveIdOffsetFlag) { + log("BEGIN \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + REMOVE_ID_OFFSET + SPACE + + remove_id_offset + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\" . . ." + + NEWLINE, true); + } + + // Process the DRMTOOL config file + success = process_drmtool_config_file(); + if (!success) { + log("FAILED processing drmtool config file!" + + NEWLINE, true); + } else { + log("SUCCESSFULLY processed drmtool config file!" + + NEWLINE, true); + + // Convert the source LDIF file to a target LDIF file + success = convert_source_ldif_to_target_ldif(); + if (!success) { + log("FAILED converting source LDIF file --> target LDIF file!" + + NEWLINE, true); + } else { + log("SUCCESSFULLY converted source LDIF file --> " + + "target LDIF file!" + + NEWLINE, true); + } + } + + // Finish logging progress + if (mRewrapFlag && mAppendIdOffsetFlag) { + log("FINISHED \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + SOURCE_NSS_DB_PATH + SPACE + + mSourcePKISecurityDatabasePath + SPACE + + SOURCE_STORAGE_TOKEN_NAME + SPACE + + TIC + mSourceStorageTokenName + TIC + SPACE + + SOURCE_STORAGE_CERT_NICKNAME + SPACE + + TIC + mSourceStorageCertNickname + TIC + SPACE + + TARGET_STORAGE_CERTIFICATE_FILE + SPACE + + mTargetStorageCertificateFilename + SPACE + + use_PKI_security_database_pwdfile + + APPEND_ID_OFFSET + SPACE + + append_id_offset + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\"." + + NEWLINE, true); + } else if (mRewrapFlag && mRemoveIdOffsetFlag) { + log("FINISHED \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + SOURCE_NSS_DB_PATH + SPACE + + mSourcePKISecurityDatabasePath + SPACE + + SOURCE_STORAGE_TOKEN_NAME + SPACE + + TIC + mSourceStorageTokenName + TIC + SPACE + + SOURCE_STORAGE_CERT_NICKNAME + SPACE + + TIC + mSourceStorageCertNickname + TIC + SPACE + + TARGET_STORAGE_CERTIFICATE_FILE + SPACE + + mTargetStorageCertificateFilename + SPACE + + use_PKI_security_database_pwdfile + + REMOVE_ID_OFFSET + SPACE + + remove_id_offset + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\"." + + NEWLINE, true); + } else if (mRewrapFlag) { + log("FINISHED \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + SOURCE_NSS_DB_PATH + SPACE + + mSourcePKISecurityDatabasePath + SPACE + + SOURCE_STORAGE_TOKEN_NAME + SPACE + + TIC + mSourceStorageTokenName + TIC + SPACE + + SOURCE_STORAGE_CERT_NICKNAME + SPACE + + TIC + mSourceStorageCertNickname + TIC + SPACE + + TARGET_STORAGE_CERTIFICATE_FILE + SPACE + + mTargetStorageCertificateFilename + + use_PKI_security_database_pwdfile + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\"." + + NEWLINE, true); + } else if (mAppendIdOffsetFlag) { + log("FINISHED \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + APPEND_ID_OFFSET + SPACE + + append_id_offset + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\"." + + NEWLINE, true); + } else if (mRemoveIdOffsetFlag) { + log("FINISHED \"" + + DRM_TOOL + SPACE + + DRMTOOL_CFG_FILE + SPACE + + mDrmtoolCfgFilename + SPACE + + SOURCE_LDIF_FILE + SPACE + + mSourceLdifFilename + SPACE + + TARGET_LDIF_FILE + SPACE + + mTargetLdifFilename + SPACE + + LOG_FILE + SPACE + + mLogFilename + SPACE + + REMOVE_ID_OFFSET + SPACE + + remove_id_offset + + process_drm_naming_context_fields + + process_requests_and_key_records_only + + "\"." + + NEWLINE, true); + } + + // Shutdown logging process + close_log(mLogFilename); + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/ExtJoiner.java b/base/java-tools/src/com/netscape/cmstools/ExtJoiner.java new file mode 100644 index 000000000..48f180add --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/ExtJoiner.java @@ -0,0 +1,104 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.FileInputStream; +import java.io.IOException; + +import com.netscape.cmsutil.util.Utils; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This program joins a sequence of extensions together + * so that the final output can be used in configuration + * wizard for specifing extra extensions in default + * certificates (i.e. CA certificate, SSL certificate). + * + * Usage: + * + * <pre> + * ExtJoiner \ + * <ext_file0> <ext_file1> ... <ext_fileN> + * + * where, + * <ext_file> is a file that has the base64 + * encoded DER encoding of an X509 Extension + * + * ExtensionSequence ::= SEQUENCE OF Extension; + * + * 0 30 142: SEQUENCE { + * 3 30 69: SEQUENCE { + * 5 06 3: OBJECT IDENTIFIER issuerAltName (2 5 29 18) + * 10 04 62: OCTET STRING + * : 30 3C 82 01 61 82 01 61 A4 10 30 0E 31 0C 30 0A + * : 06 03 55 04 03 13 03 64 73 61 87 04 01 01 01 01 + * : 86 01 61 81 14 74 68 6F 6D 61 73 6B 40 6E 65 74 + * : 73 63 61 70 65 2E 63 6F 6D 88 03 29 01 01 + * : } + * 74 30 69: SEQUENCE { + * 76 06 3: OBJECT IDENTIFIER subjectAltName (2 5 29 17) + * 81 04 62: OCTET STRING + * : 30 3C 82 01 61 82 01 61 A4 10 30 0E 31 0C 30 0A + * : 06 03 55 04 03 13 03 64 73 61 87 04 01 01 01 01 + * : 86 01 61 81 14 74 68 6F 6D 61 73 6B 40 6E 65 74 + * : 73 63 61 70 65 2E 63 6F 6D 88 03 29 01 01 + * : } + * : } + * </pre> + * + * @version $Revision$, $Date$ + */ +public class ExtJoiner { + + public static void main(String args[]) { + try { + if (args.length == 0) { + System.out.println("Usage: ExtJoiner <ext_file0> <ext_file1> ... <ext_fileN>"); + System.exit(0); + } + DerValue exts[] = new DerValue[args.length]; + + for (int i = 0; i < args.length; i++) { + byte data[] = getFileData(args[i]); + + exts[i] = new DerValue(data); + } + DerOutputStream out = new DerOutputStream(); + + out.putSequence(exts); + System.out.println(Utils.base64encode(out.toByteArray())); + } catch (IOException e) { + System.out.println(e.toString()); + } + } + + public static byte[] getFileData(String fileName) + throws IOException { + FileInputStream fis = new FileInputStream(fileName); + + byte data[] = new byte[fis.available()]; + try { + fis.read(data); + } finally { + fis.close(); + } + return Utils.base64decode(new String(data)); + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/GenExtKeyUsage.java b/base/java-tools/src/com/netscape/cmstools/GenExtKeyUsage.java new file mode 100644 index 000000000..35072aae3 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/GenExtKeyUsage.java @@ -0,0 +1,100 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.util.Vector; + +import com.netscape.cmsutil.util.Utils; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.Extension; + +/** + * Generates a DER-encoded Extended Key Usage extension. + * The first parameter is the criticality of the extension, true or false. + * The OIDs to be included in the extension are passed as command-line + * arguments. The OIDs are described in RFC 2459. For example, + * the OID for code signing is 1.3.6.1.5.5.7.3.3. + * + * @version $Revision$, $Date$ + */ +public class GenExtKeyUsage { + + public static void main(String[] args) { + try { + if (args.length < 2) { + System.out.println("Usage: GenExtKeyUsage [true|false] <OID> ..."); + System.exit(-1); + } + + boolean critical = false; + + if (args[0].equalsIgnoreCase("true")) { + critical = true; + } else if (args[0].equalsIgnoreCase("false")) { + critical = false; + } else { + System.out.println("Usage: GenExtKeyUsage [true|false] <OID> ..."); + System.exit(-1); + } + + // Generate vector of object identifiers from command line + Vector<ObjectIdentifier> oids = new Vector<ObjectIdentifier>(); + + for (int i = 1; i < args.length; i++) { + ObjectIdentifier oid = new ObjectIdentifier(args[i]); + + oids.addElement(oid); + } + + // encode all the object identifiers to the DerOutputStream + DerOutputStream contents = new DerOutputStream(); + + for (int i = 0; i < oids.size(); i++) { + contents.putOID(oids.elementAt(i)); + } + + // stuff the object identifiers into a SEQUENCE + DerOutputStream seq = new DerOutputStream(); + + seq.write(DerValue.tag_Sequence, contents); + + // encode the SEQUENCE in an octet string + DerOutputStream octetString = new DerOutputStream(); + + octetString.putOctetString(seq.toByteArray()); + + // Construct an extension + ObjectIdentifier extKeyUsageOID = new ObjectIdentifier("2.5.29.37"); + Extension extn = new Extension(extKeyUsageOID, critical, + octetString.toByteArray()); + DerOutputStream extdos = new DerOutputStream(); + + extn.encode(extdos); + + // BASE64 encode the whole thing and write it to stdout + + System.out.println(Utils.base64encode(extdos.toByteArray())); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/GenIssuerAltNameExt.java b/base/java-tools/src/com/netscape/cmstools/GenIssuerAltNameExt.java new file mode 100644 index 000000000..5c905278f --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/GenIssuerAltNameExt.java @@ -0,0 +1,141 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.ByteArrayOutputStream; +import java.net.InetAddress; + +import com.netscape.cmsutil.util.Utils; + +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.DNSName; +import netscape.security.x509.GeneralNameInterface; +import netscape.security.x509.GeneralNames; +import netscape.security.x509.IPAddressName; +import netscape.security.x509.IssuerAlternativeNameExtension; +import netscape.security.x509.OIDName; +import netscape.security.x509.RFC822Name; +import netscape.security.x509.URIName; +import netscape.security.x509.X500Name; + +/** + * This program generates an issuer alternative name extension + * in base-64 encoding. The encoding output can be used with + * the configuration wizard. + * + * Usage: + * + * <pre> + * GenIssuerAltNameExt \ + * <general_type0> <general_name0> ... <general_typeN> <general_nameN> + * + * where, + * <general_type> can be one of the following string: + * DNSName + * EDIPartyName + * IPAddressName + * URIName + * RFC822Name + * OIDName + * X500Name + * <general_name> is string + * </pre> + * + * @version $Revision$, $Date$ + */ +public class GenIssuerAltNameExt { + + public static void main(String args[]) { + try { + if ((args.length == 0) || (args.length % 2 != 0)) { + doUsage(); + System.exit(0); + } + GeneralNames gns = new GeneralNames(); + + for (int i = 0; i < args.length; i += 2) { + GeneralNameInterface gni = + buildGeneralNameInterface( + args[i], args[i + 1]); + + gns.addElement(gni); + } + + IssuerAlternativeNameExtension sane = + new IssuerAlternativeNameExtension(gns); + + output(sane); + } catch (Exception e) { + System.out.println(e.toString()); + } + } + + public static void output(IssuerAlternativeNameExtension ext) + throws Exception { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + ext.encode(os); + + System.out.println( + Utils.base64encode(os.toByteArray()) + ); + } + + public static void doUsage() { + System.out.println(); + System.out.println( + "Usage: GenIssuerAltNameExt <general_type0> <general_name0> ... <general_typeN> <general_nameN>"); + System.out.println("where,"); + System.out.println("<general_type> can be one of the following string:"); + System.out.println("\tDNSName"); + System.out.println("\tEDIPartyName"); + System.out.println("\tIPAddressName"); + System.out.println("\tURIName"); + System.out.println("\tRFC822Name"); + System.out.println("\tOIDName"); + System.out.println("\tX500Name"); + System.out.println("<general_name> is a string"); + } + + public static GeneralNameInterface buildGeneralNameInterface( + String type, String value) throws Exception { + if (type.equals("DNSName")) { + return new DNSName(value); + } else if (type.equals("EDIPartyName")) { + return new DNSName(value); + } else if (type.equals("IPAddressName")) { + InetAddress addr = InetAddress.getByName(value); + + return new IPAddressName(addr.getAddress()); + } else if (type.equals("URIName")) { + return new URIName(value); + } else if (type.equals("OIDName")) { + return new OIDName(new ObjectIdentifier(value)); + } else if (type.equals("RFC822Name")) { + return new RFC822Name(value); + } else if (type.equals("X500Name")) { + return new X500Name(value); + } else { + System.out.println("Error: unknown general_type " + + type); + doUsage(); + System.exit(0); + return null; + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/GenSubjectAltNameExt.java b/base/java-tools/src/com/netscape/cmstools/GenSubjectAltNameExt.java new file mode 100644 index 000000000..35e07f772 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/GenSubjectAltNameExt.java @@ -0,0 +1,141 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.ByteArrayOutputStream; +import java.net.InetAddress; + +import com.netscape.cmsutil.util.Utils; + +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.DNSName; +import netscape.security.x509.GeneralNameInterface; +import netscape.security.x509.GeneralNames; +import netscape.security.x509.IPAddressName; +import netscape.security.x509.OIDName; +import netscape.security.x509.RFC822Name; +import netscape.security.x509.SubjectAlternativeNameExtension; +import netscape.security.x509.URIName; +import netscape.security.x509.X500Name; + +/** + * This program generates an subject alternative name extension + * in base-64 encoding. The encoding output can be used with + * the configuration wizard. + * + * Usage: + * + * <pre> + * GenSubjectAltNameExt \ + * <general_type0> <general_name0> ... <general_typeN> <general_nameN> + * + * where, + * <general_type> can be one of the following string: + * DNSName + * EDIPartyName + * IPAddressName + * URIName + * RFC822Name + * OIDName + * X500Name + * <general_name> is string + * </pre> + * + * @version $Revision$, $Date$ + */ +public class GenSubjectAltNameExt { + + public static void main(String args[]) { + try { + if ((args.length == 0) || (args.length % 2 != 0)) { + doUsage(); + System.exit(0); + } + GeneralNames gns = new GeneralNames(); + + for (int i = 0; i < args.length; i += 2) { + GeneralNameInterface gni = + buildGeneralNameInterface( + args[i], args[i + 1]); + + gns.addElement(gni); + } + + SubjectAlternativeNameExtension sane = + new SubjectAlternativeNameExtension(gns); + + output(sane); + } catch (Exception e) { + System.out.println(e.toString()); + } + } + + public static void output(SubjectAlternativeNameExtension ext) + throws Exception { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + ext.encode(os); + + System.out.println( + Utils.base64encode(os.toByteArray()) + ); + } + + public static void doUsage() { + System.out.println(); + System.out + .println("Usage: GenSubjectAltNameExt <general_type0> <general_name0> ... <general_typeN> <general_nameN>"); + System.out.println("where,"); + System.out.println("<general_type> can be one of the following string:"); + System.out.println("\tDNSName"); + System.out.println("\tEDIPartyName"); + System.out.println("\tIPAddressName"); + System.out.println("\tURIName"); + System.out.println("\tRFC822Name"); + System.out.println("\tOIDName"); + System.out.println("\tX500Name"); + System.out.println("<general_name> is a string"); + } + + public static GeneralNameInterface buildGeneralNameInterface( + String type, String value) throws Exception { + if (type.equals("DNSName")) { + return new DNSName(value); + } else if (type.equals("EDIPartyName")) { + return new DNSName(value); + } else if (type.equals("IPAddressName")) { + InetAddress addr = InetAddress.getByName(value); + + return new IPAddressName(addr.getAddress()); + } else if (type.equals("URIName")) { + return new URIName(value); + } else if (type.equals("OIDName")) { + return new OIDName(new ObjectIdentifier(value)); + } else if (type.equals("RFC822Name")) { + return new RFC822Name(value); + } else if (type.equals("X500Name")) { + return new X500Name(value); + } else { + System.out.println("Error: unknown general_type " + + type); + doUsage(); + System.exit(0); + return null; + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/HttpClient.java b/base/java-tools/src/com/netscape/cmstools/HttpClient.java new file mode 100644 index 000000000..c8817b52f --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/HttpClient.java @@ -0,0 +1,403 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.Socket; +import java.net.SocketException; +import java.util.StringTokenizer; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.ssl.SSLHandshakeCompletedEvent; +import org.mozilla.jss.ssl.SSLHandshakeCompletedListener; +import org.mozilla.jss.ssl.SSLSocket; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.Utils; + +/** + * This class implements a CMC Enroll client for testing. + * + * @version $Revision$, $Date$ + */ +public class HttpClient { + private String _host = null; + private int _port = 0; + private boolean _secure = false; + + public static final int ARGC = 1; + static final int cipherSuites[] = { + SSLSocket.SSL3_RSA_WITH_RC4_128_MD5, + SSLSocket.SSL3_RSA_WITH_3DES_EDE_CBC_SHA, + SSLSocket.SSL3_RSA_WITH_DES_CBC_SHA, + SSLSocket.SSL3_RSA_EXPORT_WITH_RC4_40_MD5, + SSLSocket.SSL3_RSA_EXPORT_WITH_RC2_CBC_40_MD5, + SSLSocket.SSL3_RSA_WITH_NULL_MD5, + 0 + }; + + public HttpClient(String host, int port, String secure) + throws Exception { + _host = host; + _port = port; + if (secure.equals("true")) + _secure = true; + } + + public static byte[] getBytesFromFile(String filename) throws IOException { + File file = new File(filename); + FileInputStream is = new FileInputStream(file); + + long length = file.length(); + + if (length > Integer.MAX_VALUE) { + throw new IOException("Input file " + filename + + " is too large. Must be smaller than " + Integer.MAX_VALUE); + } + + byte[] bytes = new byte[(int) length]; + + int offset = 0; + int numRead = 0; + while (offset < bytes.length + && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) { + offset += numRead; + } + + if (offset < bytes.length) { + throw new IOException("Could not completely read file " + filename); + } + + is.close(); + return bytes; + } + + public void send(String ifilename, String ofilename, String dbdir, + String nickname, String password, String servlet, String clientmode) + throws Exception { + byte[] b = getBytesFromFile(ifilename); + + System.out.println("Total number of bytes read = " + b.length); + + DataOutputStream dos = null; + InputStream is = null; + if (_secure) { + try { + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(dbdir, "", "", "secmod.db"); + CryptoManager.initialize(vals); + SSLSocket socket = new SSLSocket(_host, _port); + int i; + + for (i = SSLSocket.SSL2_RC4_128_WITH_MD5; i <= SSLSocket.SSL2_RC2_128_CBC_EXPORT40_WITH_MD5; ++i) { + try { + socket.setCipherPreference(i, true); + } catch (SocketException e) { + } + } + //skip SSL_EN_IDEA_128_EDE3_CBC_WITH_MD5 + for (i = SSLSocket.SSL2_DES_64_CBC_WITH_MD5; i <= SSLSocket.SSL2_DES_192_EDE3_CBC_WITH_MD5; ++i) { + try { + socket.setCipherPreference(i, true); + } catch (SocketException e) { + } + } + for (i = 0; cipherSuites[i] != 0; ++i) { + try { + socket.setCipherPreference(cipherSuites[i], true); + } catch (SocketException e) { + } + } + SSLHandshakeCompletedListener listener = new ClientHandshakeCB(this); + socket.addHandshakeCompletedListener(listener); + + if (clientmode != null && clientmode.equals("true")) { + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = cm.getInternalKeyStorageToken(); + Password pass = new Password(password.toCharArray()); + token.login(pass); + X509Certificate cert = cm.findCertByNickname(nickname); + if (cert == null) + System.out.println("client cert is null"); + else + System.out.println("client cert is not null"); + socket.setUseClientMode(true); + socket.setClientCertNickname(nickname); + } + + socket.forceHandshake(); + dos = new DataOutputStream(socket.getOutputStream()); + is = socket.getInputStream(); + } catch (Exception e) { + System.out.println("Exception: " + e.toString()); + return; + } + } else { + Socket socket = new Socket(_host, _port); + dos = new DataOutputStream(socket.getOutputStream()); + is = socket.getInputStream(); + } + + // send request + if (servlet == null) { + System.out.println("Missing servlet name."); + printUsage(); + } else { + String s = "POST " + servlet + " HTTP/1.0\r\n"; + dos.writeBytes(s); + } + dos.writeBytes("Content-length: " + b.length + "\r\n"); + dos.writeBytes("\r\n"); + dos.write(b); + dos.flush(); + + FileOutputStream fof = new FileOutputStream(ofilename); + boolean startSaving = false; + int sum = 0; + boolean hack = false; + try { + while (true) { + int r = is.read(); + if (r == -1) + break; + if (r == 10) { + sum++; + } + if (sum == 6) { + startSaving = true; + continue; + } + if (startSaving) { + if (hack) { + fof.write(r); + } + if (hack == false) { + hack = true; + } + } + } + } catch (IOException e) { + } + fof.close(); + + byte[] bout = getBytesFromFile(ofilename); + System.out.println("Total number of bytes read = " + bout.length); + + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bs); + ps.print(Utils.base64encode(bout)); + System.out.println(bs.toString()); + + System.out.println(""); + System.out.println("The response in binary format is stored in " + ofilename); + System.out.println(""); + } + + static void printUsage() { + System.out.println(""); + System.out.println("Usage: HttpClient <configuration file>"); + System.out.println("For example, HttpClient HttpClient.cfg"); + System.out.println(""); + System.out.println("The configuration file should look like as follows:"); + System.out.println(""); + System.out.println("#host: host name for the http server"); + System.out.println("host=host1.a.com"); + System.out.println(""); + System.out.println("#port: port number"); + System.out.println("port=1025"); + System.out.println(""); + System.out.println("#secure: true for secure connection, false for nonsecure connection"); + System.out.println("secure=false"); + System.out.println(""); + System.out.println("#input: full path for the enrollment request, the content must be in binary format"); + System.out.println("input=/u/doc/cmcReqCRMFBin"); + System.out.println(""); + System.out.println("#output: full path for the response in binary format"); + System.out.println("output=/u/doc/cmcResp"); + System.out.println(""); + System.out.println("#dbdir: directory for cert8.db, key3.db and secmod.db"); + System.out.println("#This parameter will be ignored if secure=false"); + System.out.println("dbdir=/u/smith/.netscape"); + System.out.println(""); + System.out.println("#clientmode: true for client authentication, false for no client authentication"); + System.out.println("#This parameter will be ignored if secure=false"); + System.out.println("clientmode=false"); + System.out.println(""); + System.out.println("#password: password for cert8.db"); + System.out.println("#This parameter will be ignored if secure=false and clientauth=false"); + System.out.println("password="); + System.out.println(""); + System.out.println("#nickname: nickname for client certificate"); + System.out.println("#This parameter will be ignored if clientmode=false"); + System.out.println("nickname="); + System.out.println(""); + System.out.println("#servlet: servlet name"); + System.out.println("servlet=/ca/profileSubmitCMCFull"); + System.out.println(""); + System.exit(0); + } + + public static void main(String args[]) { + String host = null, portstr = null, secure = null, dbdir = null, nickname = null; + String password = null, ofilename = null, ifilename = null; + String servlet = null; + String clientmode = null; + + System.out.println(""); + + // Check that the correct # of arguments were submitted to the program + if (args.length != (ARGC)) { + System.out.println("Wrong number of parameters:" + args.length); + printUsage(); + } + + String configFile = args[0]; + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader( + new BufferedInputStream( + new FileInputStream(configFile)))); + } catch (FileNotFoundException e) { + System.out.println("HttpClient: can't find configuration file: " + configFile); + printUsage(); + System.exit(1); + } catch (Exception e) { + e.printStackTrace(); + printUsage(); + return; + } + + try { + String str = ""; + while ((str = reader.readLine()) != null) { + str = str.trim(); + if (!str.startsWith("#") && str.length() > 0) { + StringTokenizer tokenizer = new StringTokenizer(str, "="); + if (tokenizer.hasMoreTokens()) { + String name = tokenizer.nextToken(); + String val = null; + if (tokenizer.countTokens() > 0) + val = tokenizer.nextToken(); + if (name.equals("host")) { + host = val; + } else if (name.equals("port")) { + portstr = val; + } else if (name.equals("secure")) { + secure = val; + } else if (name.equals("dbdir")) { + dbdir = val; + } else if (name.equals("nickname")) { + nickname = val; + } else if (name.equals("password")) { + password = val; + } else if (name.equals("output")) { + ofilename = val; + } else if (name.equals("input")) { + ifilename = val; + } else if (name.equals("clientmode")) { + clientmode = val; + } else if (name.equals("servlet")) { + servlet = val; + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + printUsage(); + } + + if (host == null) { + System.out.println("Missing host name."); + printUsage(); + } + + if (portstr == null) { + System.out.println("Missing port number."); + printUsage(); + } + + if (servlet == null) { + System.out.println("Missing servlet name."); + printUsage(); + } + + if (ifilename == null) { + System.out.println("Missing input filename for the enrollment request."); + printUsage(); + } + + if (ofilename == null) { + System.out.println("Missing output filename for the response."); + printUsage(); + } + + int port = Integer.parseInt(portstr); + + if (secure != null && secure.equals("true")) { + if (dbdir == null) { + System.out.println("Missing directory name for the cert7.db."); + printUsage(); + } + + if (clientmode != null && clientmode.equals("true")) { + if (password == null) { + System.out.println("Missing password for the cert7.db."); + printUsage(); + } + if (nickname == null) { + System.out.println("Missing nickname for the client certificate"); + printUsage(); + } + } + } + + try { + HttpClient client = + new HttpClient(host, port, secure); + client.send(ifilename, ofilename, dbdir, nickname, password, servlet, clientmode); + } catch (Exception e) { + System.out.println("Error: " + e.toString()); + } + } + + class ClientHandshakeCB implements SSLHandshakeCompletedListener { + Object sc; + + public ClientHandshakeCB(Object sc) { + this.sc = sc; + } + + public void handshakeCompleted(SSLHandshakeCompletedEvent event) { + System.out.println("handshake happened"); + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/OCSPClient.java b/base/java-tools/src/com/netscape/cmstools/OCSPClient.java new file mode 100644 index 000000000..5b9abe495 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/OCSPClient.java @@ -0,0 +1,276 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; +import java.security.MessageDigest; + +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509Key; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.NULL; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; + +import com.netscape.cmsutil.ocsp.BasicOCSPResponse; +import com.netscape.cmsutil.ocsp.CertID; +import com.netscape.cmsutil.ocsp.CertStatus; +import com.netscape.cmsutil.ocsp.GoodInfo; +import com.netscape.cmsutil.ocsp.OCSPRequest; +import com.netscape.cmsutil.ocsp.OCSPResponse; +import com.netscape.cmsutil.ocsp.Request; +import com.netscape.cmsutil.ocsp.ResponseBytes; +import com.netscape.cmsutil.ocsp.ResponseData; +import com.netscape.cmsutil.ocsp.RevokedInfo; +import com.netscape.cmsutil.ocsp.SingleResponse; +import com.netscape.cmsutil.ocsp.TBSRequest; +import com.netscape.cmsutil.ocsp.UnknownInfo; +import com.netscape.cmsutil.util.Utils; + +/** + * This class implements a OCSP client for testing. + * + * @version $Revision$, $Date$ + */ +public class OCSPClient { + private String _host = null; + private int _port = 0; + + public OCSPClient(String host, int port, String dbdir) + throws Exception { + _host = host; + _port = port; + CryptoManager.initialize(dbdir); + } + + public void send(String uri, String nickname, int serialno, String output) + throws Exception { + CryptoManager manager = CryptoManager.getInstance(); + X509Certificate caCert = manager.findCertByNickname(nickname); + OCSPRequest request = getOCSPRequest(caCert, serialno); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + request.encode(os); + byte request_data[] = os.toByteArray(); + sendOCSPRequest(uri, _host, _port, request_data, output); + } + + public void sendRequestData(String uri, String nickname, byte request_data[], String output) + throws Exception { + sendOCSPRequest(uri, _host, _port, request_data, output); + } + + public OCSPRequest getOCSPRequest(X509Certificate caCert, int serialno) + throws Exception { + MessageDigest md = MessageDigest.getInstance("SHA"); + + // calculate issuer key hash + X509CertImpl x509Cert = new X509CertImpl(caCert.getEncoded()); + X509Key x509key = (X509Key) x509Cert.getPublicKey(); + byte issuerKeyHash[] = md.digest(x509key.getKey()); + + // calculate name hash + X500Name name = (X500Name) x509Cert.getSubjectDN(); + byte issuerNameHash[] = md.digest(name.getEncoded()); + // constructing the OCSP request + CertID certid = new CertID( + new AlgorithmIdentifier( + new OBJECT_IDENTIFIER("1.3.14.3.2.26"), new NULL()), + new OCTET_STRING(issuerNameHash), + new OCTET_STRING(issuerKeyHash), + new INTEGER(serialno)); + Request request = new Request(certid, null); + SEQUENCE requestList = new SEQUENCE(); + requestList.addElement(request); + TBSRequest tbsRequest = new TBSRequest(null, null, requestList, null); + return new OCSPRequest(tbsRequest, null); + } + + public void sendOCSPRequest(String uri, String host, int port, + byte request_data[], String output) throws Exception { + Socket socket = new Socket(host, port); + + // send request + System.out.println("URI: " + uri); + + DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); + dos.writeBytes("POST " + uri + " HTTP/1.0\r\n"); + dos.writeBytes("Content-length: " + request_data.length + "\r\n"); + dos.writeBytes("\r\n"); + dos.write(request_data); + dos.flush(); + + System.out.println("Data Length: " + request_data.length); + System.out.println("Data: " + Utils.base64encode(request_data)); + + InputStream iiss = socket.getInputStream(); + FileOutputStream fof = new FileOutputStream(output); + boolean startSaving = false; + int sum = 0; + boolean hack = false; + try { + while (true) { + int r = iiss.read(); + if (r == -1) + break; + if (r == 10) { + sum++; + } + if (sum == 6) { + startSaving = true; + continue; + } + if (startSaving) { + if (hack) { + fof.write(r); + } + if (hack == false) { + hack = true; + } + } + } // while + } catch (IOException e) { + } + fof.close(); + + // parse OCSPResponse + BufferedInputStream fis = + new BufferedInputStream( + new FileInputStream(output)); + OCSPResponse resp = (OCSPResponse) + OCSPResponse.getTemplate().decode(fis); + ResponseBytes bytes = resp.getResponseBytes(); + BasicOCSPResponse basic = (BasicOCSPResponse) + BasicOCSPResponse.getTemplate().decode( + new ByteArrayInputStream(bytes.getResponse().toByteArray())); + ResponseData rd = basic.getResponseData(); + for (int i = 0; i < rd.getResponseCount(); i++) { + SingleResponse rd1 = rd.getResponseAt(i); + System.out.println("CertID.serialNumber=" + + rd1.getCertID().getSerialNumber()); + CertStatus status1 = rd1.getCertStatus(); + if (status1 instanceof GoodInfo) { + System.out.println("CertStatus=Good"); + } + if (status1 instanceof UnknownInfo) { + System.out.println("CertStatus=Unknown"); + } + if (status1 instanceof RevokedInfo) { + System.out.println("CertStatus=Revoked"); + } + } + } + + public static void printUsage() { + System.out.println("Usage: OCSPClient " + + "<host> <port> <dbdir> <nickname> <serialno_or_filename> <output> <times>"); + System.out.println(" <host> = OCSP server hostname"); + System.out.println(" <port> = OCSP server port number"); + System.out.println(" <dbdir> = Certificate Database Directory"); + System.out.println(" <nickname> = Nickname of CA Certificate"); + System.out.println( + " <serialno_or_filename> = Serial Number Being Checked, Or Name of file that contains the request"); + System.out.println(" <output> = Filename of Response in DER encoding"); + System.out.println(" <times> = Submit Request Multiple Times"); + System.out.println(" [<uri>] = OCSP Service URI (i.e. /ocsp/ee/ocsp)"); + } + + public static void main(String args[]) { + if (args.length != 7 && args.length != 8) { + System.out.println("ERROR: Invalid number of arguments - got " + + args.length + " expected 7!"); + for (int i = 0; i < args.length; i++) { + System.out.println("arg[" + i + "]=" + args[i]); + } + printUsage(); + System.exit(0); + } + + String host = args[0]; + int port = -1; + try { + port = Integer.parseInt(args[1]); + } catch (Exception e) { + System.out.println("Error: Invalid Port Number"); + printUsage(); + System.exit(0); + } + String dbdir = args[2]; + String nickname = args[3]; + int serialno = -1; + byte data[] = null; + try { + serialno = Integer.parseInt(args[4]); + } catch (Exception e) { + try { + System.out.println("Warning: Serial Number not found. It may be a filename."); + /* it could be a file name */ + FileInputStream fis = new FileInputStream(args[4]); + System.out.println("File Size: " + fis.available()); + data = new byte[fis.available()]; + fis.read(data); + } catch (Exception e1) { + System.out.println("Error: Invalid Serial Number or File Name"); + printUsage(); + System.exit(0); + } + } + String output = args[5]; + int times = 1; + try { + times = Integer.parseInt(args[6]); + } catch (Exception e) { + System.out.println("Error: Invalid Times"); + printUsage(); + System.exit(0); + } + String uri = "/ocsp/ee/ocsp"; + if (args.length > 7) { + uri = args[7]; + } + try { + OCSPClient client = + new OCSPClient(host, port, dbdir); + for (int i = 0; i < times; i++) { + if (data != null) { + client.sendRequestData(uri, nickname, data, output); + } else { + client.send(uri, nickname, serialno, output); + } + } + System.out.println("Success: Output " + output); + } catch (Exception e) { + System.out.println("Error: " + e.toString()); + printUsage(); + System.exit(0); + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java b/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java new file mode 100644 index 000000000..7cd50a37a --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java @@ -0,0 +1,249 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.security.KeyPair; +import java.security.MessageDigest; + +import netscape.security.x509.X500Name; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.PrintableString; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.KeyPairAlgorithm; +import org.mozilla.jss.crypto.KeyPairGenerator; +import org.mozilla.jss.crypto.SignatureAlgorithm; +import org.mozilla.jss.pkcs10.CertificationRequest; +import org.mozilla.jss.pkcs10.CertificationRequestInfo; +import org.mozilla.jss.pkix.primitive.AVA; +import org.mozilla.jss.pkix.primitive.Attribute; +import org.mozilla.jss.pkix.primitive.Name; +import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.HMACDigest; +import com.netscape.cmsutil.util.Utils; + +/** + * Generates a 1024-bit RSA key pair in the security database, constructs a + * PKCS#10 certificate request with the public key, and outputs the request + * to a file. + * <p> + * PKCS #10 is a certification request syntax standard defined by RSA. A CA may support multiple types of certificate + * requests. The Certificate System CA supports KEYGEN, PKCS#10, CRMF, and CMC. + * <p> + * To get a certificate from the CA, the certificate request needs to be submitted to and approved by a CA agent. Once + * approved, a certificate is created for the request, and certificate attributes, such as extensions, are populated + * according to certificate profiles. + * <p> + * + * @version $Revision$, $Date$ + */ +public class PKCS10Client { + + private static void printUsage() { + System.out.println( + "Usage: PKCS10Client -p <certdb password> -d <location of certdb> -o <output file which saves the base64 PKCS10> -s <subjectDN>\n"); + } + + public static void main(String args[]) { + String dbdir = null, ofilename = null, password = null, subjectName = null; + + if (args.length != 8) { + printUsage(); + System.exit(1); + } + + for (int i = 0; i < args.length; i++) { + String name = args[i]; + if (name.equals("-p")) { + password = args[i + 1]; + } else if (name.equals("-d")) { + dbdir = args[i + 1]; + } else if (name.equals("-o")) { + ofilename = args[i + 1]; + } else if (name.equals("-s")) { + subjectName = args[i + 1]; + } + } + + if (password == null || ofilename == null || subjectName == null) { + System.out.println("Illegal input parameters."); + printUsage(); + System.exit(1); + } + + if (dbdir == null) + dbdir = "."; + + try { + String mPrefix = ""; + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(dbdir, mPrefix, + mPrefix, "secmod.db"); + + CryptoManager.initialize(vals); + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = cm.getInternalKeyStorageToken(); + Password pass = new Password(password.toCharArray()); + + token.login(pass); + KeyPairGenerator kg = token.getKeyPairGenerator(KeyPairAlgorithm.RSA); + kg.initialize(1024); + KeyPair pair = kg.genKeyPair(); + + // Add idPOPLinkWitness control + String secretValue = "testing"; + byte[] key1 = null; + byte[] finalDigest = null; + MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); + key1 = SHA1Digest.digest(secretValue.getBytes()); + + /* seed */ + byte[] b = + { 0x10, 0x53, 0x42, 0x24, 0x1a, 0x2a, 0x35, 0x3c, + 0x7a, 0x52, 0x54, 0x56, 0x71, 0x65, 0x66, 0x4c, + 0x51, 0x34, 0x35, 0x23, 0x3c, 0x42, 0x43, 0x45, + 0x61, 0x4f, 0x6e, 0x43, 0x1e, 0x2a, 0x2b, 0x31, + 0x32, 0x34, 0x35, 0x36, 0x55, 0x51, 0x48, 0x14, + 0x16, 0x29, 0x41, 0x42, 0x43, 0x7b, 0x63, 0x44, + 0x6a, 0x12, 0x6b, 0x3c, 0x4c, 0x3f, 0x00, 0x14, + 0x51, 0x61, 0x15, 0x22, 0x23, 0x5f, 0x5e, 0x69 }; + + HMACDigest hmacDigest = new HMACDigest(SHA1Digest, key1); + hmacDigest.update(b); + finalDigest = hmacDigest.digest(); + + OCTET_STRING ostr = new OCTET_STRING(finalDigest); + Attribute attr = new Attribute(OBJECT_IDENTIFIER.id_cmc_idPOPLinkWitness, ostr); + + SET attributes = new SET(); + attributes.addElement(attr); + Name n = getJssName(subjectName); + SubjectPublicKeyInfo subjectPub = new SubjectPublicKeyInfo(pair.getPublic()); + CertificationRequestInfo certReqInfo = + new CertificationRequestInfo(new INTEGER(0), n, subjectPub, attributes); + CertificationRequest certRequest = new CertificationRequest(certReqInfo, + pair.getPrivate(), SignatureAlgorithm.RSASignatureWithMD5Digest); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + certRequest.encode(bos); + byte[] bb = bos.toByteArray(); + + String b64E = Utils.base64encode(bb); + + System.out.println(""); + System.out.println(b64E); + System.out.println(""); + + PrintStream ps = null; + ps = new PrintStream(new FileOutputStream(ofilename)); + ps.println(b64E); + ps.flush(); + ps.close(); + } catch (Exception e) { + } + } + + static Name getJssName(String dn) { + + X500Name x5Name = null; + + try { + x5Name = new X500Name(dn); + } catch (IOException e) { + + System.out.println("Illegal Subject Name: " + dn + " Error: " + e.toString()); + System.out.println("Filling in default Subject Name......"); + return null; + } + + Name ret = new Name(); + netscape.security.x509.RDN[] names = null; + names = x5Name.getNames(); + int nameLen = x5Name.getNamesLength(); + + netscape.security.x509.RDN cur = null; + + for (int i = 0; i < nameLen; i++) { + cur = names[i]; + String rdnStr = cur.toString(); + String[] split = rdnStr.split("="); + + if (split.length != 2) + continue; + + try { + if (split[0].equals("UID")) { + ret.addElement(new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), new PrintableString( + split[1]))); + // System.out.println("UID found : " + split[1]); + } + + if (split[0].equals("C")) { + ret.addCountryName(split[1]); + // System.out.println("C found : " + split[1]); + continue; + } + + if (split[0].equals("CN")) { + ret.addCommonName(split[1]); + // System.out.println("CN found : " + split[1]); + continue; + } + + if (split[0].equals("L")) { + ret.addLocalityName(split[1]); + // System.out.println("L found : " + split[1]); + continue; + } + + if (split[0].equals("O")) { + ret.addOrganizationName(split[1]); + // System.out.println("O found : " + split[1]); + continue; + } + + if (split[0].equals("ST")) { + ret.addStateOrProvinceName(split[1]); + // System.out.println("ST found : " + split[1]); + continue; + } + + if (split[0].equals("OU")) { + ret.addOrganizationalUnitName(split[1]); + // System.out.println("OU found : " + split[1]); + continue; + } + } catch (Exception e) { + System.out.println("Error constructing RDN: " + rdnStr + " Error: " + e.toString()); + continue; + } + } + + return ret; + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/PKCS12Export.java b/base/java-tools/src/com/netscape/cmstools/PKCS12Export.java new file mode 100644 index 000000000..8d8e858f2 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/PKCS12Export.java @@ -0,0 +1,301 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.security.MessageDigest; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.BMPString; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.crypto.Cipher; +import org.mozilla.jss.crypto.CryptoStore; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.EncryptionAlgorithm; +import org.mozilla.jss.crypto.IVParameterSpec; +import org.mozilla.jss.crypto.KeyGenAlgorithm; +import org.mozilla.jss.crypto.KeyGenerator; +import org.mozilla.jss.crypto.KeyWrapAlgorithm; +import org.mozilla.jss.crypto.KeyWrapper; +import org.mozilla.jss.crypto.PBEAlgorithm; +import org.mozilla.jss.crypto.SymmetricKey; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkcs12.AuthenticatedSafes; +import org.mozilla.jss.pkcs12.CertBag; +import org.mozilla.jss.pkcs12.PFX; +import org.mozilla.jss.pkcs12.PasswordConverter; +import org.mozilla.jss.pkcs12.SafeBag; +import org.mozilla.jss.pkix.primitive.EncryptedPrivateKeyInfo; +import org.mozilla.jss.pkix.primitive.PrivateKeyInfo; +import org.mozilla.jss.util.Password; + +/** + * Tool for creating PKCS12 file + * + * <P> + * + * @version $Revision$, $Date$ + * + */ +public class PKCS12Export { + + private static boolean debugMode = false; + + private static void debug(String s) { + if (debugMode) + System.out.println("PKCS12Export debug: " + s); + } + + private static void printUsage() { + System.out.println( + "Usage: PKCS12Export -d <cert/key db directory> -p <file containing password for keydb> -w <file containing pkcs12 password> -o <output file for pkcs12>"); + System.out.println(""); + System.out.println("If you want to turn on debug, do the following:"); + System.out.println( + "Usage: PKCS12Export -debug -d <cert/key db directory> -p <file containing password for keydb> -w <file containing pkcs12 password> -o <output file for pkcs12>"); + } + + private static byte[] getEncodedKey(org.mozilla.jss.crypto.PrivateKey pkey) { + try { + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = cm.getInternalKeyStorageToken(); + KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES3); + SymmetricKey sk = kg.generate(); + KeyWrapper wrapper = token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; + IVParameterSpec param = new IVParameterSpec(iv); + wrapper.initWrap(sk, param); + byte[] enckey = wrapper.wrap(pkey); + Cipher c = token.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD); + c.initDecrypt(sk, param); + byte[] recovered = c.doFinal(enckey); + return recovered; + } catch (Exception e) { + debug("PKCS12Export getEncodedKey: Exception=" + e.toString()); + System.exit(1); + } + + return null; + } + + private static void addKeyBag(org.mozilla.jss.crypto.PrivateKey pkey, X509Certificate x509cert, + Password pass, byte[] localKeyId, SEQUENCE safeContents) { + try { + PasswordConverter passConverter = new PasswordConverter(); + byte salt[] = { 0x01, 0x01, 0x01, 0x01 }; + byte[] priData = getEncodedKey(pkey); + + PrivateKeyInfo pki = (PrivateKeyInfo) + ASN1Util.decode(PrivateKeyInfo.getTemplate(), priData); + ASN1Value key = EncryptedPrivateKeyInfo.createPBE( + PBEAlgorithm.PBE_SHA1_DES3_CBC, + pass, salt, 1, passConverter, pki); + SET keyAttrs = createBagAttrs( + x509cert.getSubjectDN().toString(), localKeyId); + SafeBag keyBag = new SafeBag(SafeBag.PKCS8_SHROUDED_KEY_BAG, + key, keyAttrs); + safeContents.addElement(keyBag); + } catch (Exception e) { + debug("PKCS12Export addKeyBag: Exception=" + e.toString()); + System.exit(1); + } + } + + private static byte[] addCertBag(X509Certificate x509cert, String nickname, + SEQUENCE safeContents) throws IOException { + byte[] localKeyId = null; + try { + ASN1Value cert = new OCTET_STRING(x509cert.getEncoded()); + localKeyId = createLocalKeyId(x509cert); + SET certAttrs = null; + if (nickname != null) + certAttrs = createBagAttrs(nickname, localKeyId); + SafeBag certBag = new SafeBag(SafeBag.CERT_BAG, + new CertBag(CertBag.X509_CERT_TYPE, cert), certAttrs); + safeContents.addElement(certBag); + } catch (Exception e) { + debug("PKCS12Export addCertBag: " + e.toString()); + System.exit(1); + } + + return localKeyId; + } + + private static byte[] createLocalKeyId(X509Certificate cert) { + try { + // SHA1 hash of the X509Cert der encoding + byte certDer[] = cert.getEncoded(); + + MessageDigest md = MessageDigest.getInstance("SHA"); + + md.update(certDer); + return md.digest(); + } catch (Exception e) { + debug("PKCS12Export createLocalKeyId: Exception: " + e.toString()); + System.exit(1); + } + + return null; + } + + private static SET createBagAttrs(String nickName, byte localKeyId[]) + throws IOException { + try { + SET attrs = new SET(); + SEQUENCE nickNameAttr = new SEQUENCE(); + + nickNameAttr.addElement(SafeBag.FRIENDLY_NAME); + SET nickNameSet = new SET(); + + nickNameSet.addElement(new BMPString(nickName)); + nickNameAttr.addElement(nickNameSet); + attrs.addElement(nickNameAttr); + SEQUENCE localKeyAttr = new SEQUENCE(); + + localKeyAttr.addElement(SafeBag.LOCAL_KEY_ID); + SET localKeySet = new SET(); + + localKeySet.addElement(new OCTET_STRING(localKeyId)); + localKeyAttr.addElement(localKeySet); + attrs.addElement(localKeyAttr); + return attrs; + } catch (Exception e) { + debug("PKCS12Export createBagAttrs: Exception=" + e.toString()); + System.exit(1); + } + + return null; + } + + public static void main(String args[]) { + if (args.length < 8) { + printUsage(); + System.exit(1); + } + + String pwdfile = null; + String dir = null; + String pk12pwdfile = null; + String pk12output = null; + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-d")) { + dir = args[i + 1]; + } else if (args[i].equals("-p")) { + pwdfile = args[i + 1]; + } else if (args[i].equals("-s")) { + // snickname = args[i + 1]; + } else if (args[i].equals("-w")) { + pk12pwdfile = args[i + 1]; + } else if (args[i].equals("-o")) { + pk12output = args[i + 1]; + } else if (args[i].equals("-debug")) { + debugMode = true; + } + } + + debug("The directory for certdb/keydb is " + dir); + debug("The password file for keydb is " + pwdfile); + + // get password + String pwd = null; + try { + BufferedReader in = new BufferedReader(new FileReader(pwdfile)); + pwd = in.readLine(); + } catch (Exception e) { + debug("Failed to read the keydb password from the file. Exception: " + e.toString()); + System.exit(1); + } + + String pk12pwd = null; + try { + BufferedReader in = new BufferedReader(new FileReader(pk12pwdfile)); + pk12pwd = in.readLine(); + } catch (Exception e) { + debug("Failed to read the keydb password from the file. Exception: " + e.toString()); + System.exit(1); + } + + CryptoManager cm = null; + try { + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(dir, "", "", "secmod.db"); + CryptoManager.initialize(vals); + cm = CryptoManager.getInstance(); + } catch (Exception e) { + debug("Failed to initialize the certdb."); + System.exit(1); + } + + SEQUENCE encSafeContents = new SEQUENCE(); + SEQUENCE safeContents = new SEQUENCE(); + try { + CryptoToken token = cm.getInternalKeyStorageToken(); + Password pass = new Password(pwd.toCharArray()); + token.login(pass); + CryptoStore store = token.getCryptoStore(); + X509Certificate[] certs = store.getCertificates(); + debug("Number of user certificates = " + certs.length); + Password pass12 = new Password(pk12pwd.toCharArray()); + for (int i = 0; i < certs.length; i++) { + String nickname = certs[i].getNickname(); + debug("Certificate nickname = " + nickname); + org.mozilla.jss.crypto.PrivateKey prikey = null; + try { + prikey = cm.findPrivKeyByCert(certs[i]); + } catch (Exception e) { + debug("PKCS12Export Exception: " + e.toString()); + } + + if (prikey == null) { + debug("Private key is null"); + addCertBag(certs[i], null, safeContents); + } else { + debug("Private key is not null"); + byte localKeyId[] = + addCertBag(certs[i], nickname, safeContents); + addKeyBag(prikey, certs[i], pass12, localKeyId, encSafeContents); + } + } + + AuthenticatedSafes authSafes = new AuthenticatedSafes(); + authSafes.addSafeContents(safeContents); + authSafes.addSafeContents(encSafeContents); + PFX pfx = new PFX(authSafes); + pfx.computeMacData(pass12, null, 5); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + pfx.encode(bos); + FileOutputStream fos = new FileOutputStream(pk12output); + fos.write(bos.toByteArray()); + fos.flush(); + fos.close(); + pass.clear(); + pass12.clear(); + } catch (Exception e) { + debug("PKCS12Export Exception: " + e.toString()); + System.exit(1); + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/PasswordCache.java b/base/java-tools/src/com/netscape/cmstools/PasswordCache.java new file mode 100644 index 000000000..ba7fb72a4 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/PasswordCache.java @@ -0,0 +1,870 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.SecretDecoderRing.Decryptor; +import org.mozilla.jss.SecretDecoderRing.Encryptor; +import org.mozilla.jss.SecretDecoderRing.KeyManager; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.TokenException; +import org.mozilla.jss.util.Base64OutputStream; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.Utils; + +/** + * Tool for interacting with the PWcache + * + * @version $Revision$, $Date$ + */ + +public class PasswordCache { + + /* These are the tags that identify various passwords + * They should probably be converted instances of some + * class so that we can expose an API to add additional + * TAG's for use if I want to add a password for use + * with my own authenticaion module + */ + public static final String PROP_PWC_NICKNAME = "sso_key"; + public static final String PW_TAG_INTERNAL_LDAP_DB = "Internal LDAP Database"; + private static final String WRONG_NUM_ARGS = "Error: wrong number of arguments"; + private static final String CERTDB = "cert8.db"; + private static final String KEYDB = "key3.db"; + + private static void usage() { + System.out.println( + "This tool has to be run from the same directory where pwcache.db file resides, normally <cms instance>/config directory, unless the file's full path is specified in the -c option..\nUsage: PasswordCache <SSO_PASSWORD> <-d cert/key db directory> <-h tokenName> <-P cert/key db prefix> <-c pwcache.db_file_full_path> <-k file containing Base64EncodedKeyID> <COMMAND> ..."); + System.out.println(" commands:"); + System.out.println(" 'add <password_name> <password>'"); + System.out.println(" 'change <password_name> <password>'"); + System.out.println(" 'delete <password_name>'"); + System.out.println(" 'rekey'"); + System.out.println(" 'list'"); + System.out.println( + "\nExample:\n\tPasswordCache thePassword1 -d /usr/netscape/servers/cms/alias -P cert-instance1-machine1- -c pwcache.db -k keyidFile list"); + System.exit(1); + } + + private static boolean debugMode = false; + + public PasswordCache() { + } + + private static void debug(String s) { + if (debugMode == true) + System.out.println("PasswordCache debug: " + s); + } + + /** + * clean up an argv by removing the trailing, empty arguments + * + * This is necessary to support the script wrapper which calls the + * tool with arguments in quotes such as: + * "$1" "$2" + * if $2 is not specified, the empty arg "" gets passed, which causes + * an error in the arg-count checking code. + */ + private static String[] cleanArgs(String[] s) { + int length; + int i; + + length = s.length; + debug("before cleanArgs argv length =" + length); + + for (i = length - 1; i >= 0; i--) { + if (s[i].equals("")) { + length--; + } else { + break; + } + } + + String[] new_av = new String[length]; + for (i = 0; i < length; i++) { + new_av[i] = s[i]; + debug("arg " + i + " is " + new_av[i]); + } + debug("after cleanArgs argv length =" + length); + + return new_av; + } + + public static byte[] base64Decode(String s) throws IOException { + byte[] d = Utils.base64decode(s); + return d; + } + + public static String base64Encode(byte[] bytes) throws IOException { + // All this streaming is lame, but Base64OutputStream needs a + // PrintStream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Base64OutputStream b64 = new Base64OutputStream(new + PrintStream(new + FilterOutputStream(output) + ) + ); + + b64.write(bytes); + b64.flush(); + + // This is internationally safe because Base64 chars are + // contained within 8859_1 + return output.toString("8859_1"); + } + + public static void main(String[] av) { + // default path is "." + String mPath = "."; + String mTokenName = null; + // default prefix is "" + String mPrefix = ""; + String mKeyIdString = null; + byte[] mKeyId = null; + String mCacheFile = "pwcache.db"; + + String pwdPath = null; + String instancePath = null; + String instanceName = null; + + String[] argv = cleanArgs(av); + + if (argv.length < 2) { + usage(); + } + + String pw = argv[0]; + + char[] testpw = pw.toCharArray(); + Password pass = new Password(testpw); + + String command = ""; + String aTag = ""; + String aPasswd = ""; + + int i = 0; + for (i = 1; i < argv.length; ++i) { + if (argv[i].equals("-d")) { + if (++i >= argv.length) + usage(); + mPath = argv[i]; + } else if (argv[i].equals("-h")) { + if (++i >= argv.length) + usage(); + mTokenName = argv[i]; + } else if (argv[i].equals("-P")) { + if (++i >= argv.length) + usage(); + mPrefix = argv[i]; + } else if (argv[i].equals("-c")) { + if (++i >= argv.length) + usage(); + mCacheFile = argv[i]; + } else if (argv[i].equals("-k")) { + if (++i >= argv.length) + usage(); + String keyFile = argv[i]; + try { + BufferedReader r = new BufferedReader(new FileReader(keyFile)); + mKeyIdString = r.readLine(); + } catch (Exception e) { + System.out.println("Error: " + e.toString()); + System.exit(1); + } + + if (mKeyIdString != null) { + try { + mKeyId = base64Decode(mKeyIdString); + debug("base64Decode of key id string successful"); + } catch (IOException e) { + System.out.println("base64Decode of key id string failed"); + System.exit(1); + } + } + } else { + command = argv[i++]; + debug("command = " + command); + + if ((command.equals("add")) || + (command.equals("change"))) { + aTag = argv[i++]; + aPasswd = argv[i]; + debug("command is " + command + " " + aTag + ":" + aPasswd); + } else if (command.equals("delete")) { + aTag = argv[i]; + } else if (command.equals("list")) { + } else if (command.equals("rekey")) { + } + break; + } + } + + try { + // initialize CryptoManager + System.out.println("cert/key prefix = " + mPrefix); + System.out.println("cert/key db path = " + mPath); + System.out.println("password cache file = " + mCacheFile); + + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(mPath, mPrefix, + mPrefix, "secmod.db"); + + CryptoManager.initialize(vals); + + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = null; + if (mTokenName == null) { + token = cm.getInternalKeyStorageToken(); + System.out.println("token name = internal"); + } else { + token = cm.getTokenByName(mTokenName); + System.out.println("token name = " + mTokenName); + } + + token.login(pass); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + // generating new key + if (command.equals("rekey")) { + System.out.println("generating new key..."); + PWsdrCache cache = null; + try { + // compose instance name + File passwordCacheDB = new File(mCacheFile); + pwdPath = passwordCacheDB.getAbsolutePath(); + int beginIndex = pwdPath.lastIndexOf("cert-"); + instancePath = pwdPath.substring(beginIndex); + int endIndex = 0; + endIndex = instancePath.lastIndexOf("config"); + instanceName = instancePath.substring(0, (endIndex - 1)); + + cache = new PWsdrCache(mCacheFile, mTokenName, null, true); + cache.deleteUniqueNamedKey(PROP_PWC_NICKNAME + + " " + + instanceName); + byte[] newKeyId = cache.generateSDRKeyWithNickName( + PROP_PWC_NICKNAME + + " " + + instanceName); + if (newKeyId != null) { + String newKeyIDString = base64Encode(newKeyId); + System.out.println("key generated successfully with key id = " + + newKeyIDString); + System.out.println("Save the VALUE portion of this key id in a local file,"); + System.out.println("and under variable \"pwcKeyid\" in CS.cfg !!"); + System.out.println("If you have not already done so,"); + System.out.println("remove the old pwcache.db and use this local file to add passwords."); + // job is done + System.exit(0); + } else { + System.out.println("key expected to be generated but wasn't"); + System.exit(1); + } + } catch (Exception e) { + System.out.println(e.toString()); + System.exit(1); + } + } + + PWsdrCache cache = null; + try { + cache = new PWsdrCache(mCacheFile, mTokenName, mKeyId, true); + } catch (Exception e) { + System.out.println(e.toString()); + System.exit(1); + } + + if ((command.equals("add")) || (command.equals("change"))) { + // current key id must be specified + if (mKeyId == null) { + System.out.println("operation failed: no key id specified"); + System.exit(1); + } + + try { + System.out.println("adding " + aTag + ":" + aPasswd); + cache.addEntry(aTag, aPasswd); + } catch (Exception e) { + System.out.println("--failed--" + e.toString()); + } + } else if (command.equals("list")) { + cache.pprint(); + } else if (command.equals("delete")) { + // current key id must be specified + if (mKeyId == null) { + System.out.println("operation failed: no key id specified"); + System.exit(1); + } + + try { + cache.deleteEntry(aTag); + } catch (Exception e) { + System.out.println("User not found"); + } + } else { + System.out.println("Illegal command: " + command); + System.exit(1); + } + } +} + +/* + * A class for managing passwords in the SDR password cache + * + * @author Christina Fu + * @version $Revision$, $Date$ + */ +class PWsdrCache { + + public static final String PROP_PWC_NICKNAME = "sso_key"; + + private String mPWcachedb = null; + private byte[] mKeyID = null; + private String mTokenName = null; + private CryptoToken mToken = null; + + // mTool tells if this is called from the PasswordCache tool + private boolean mIsTool = false; + + // for PasswordCache tool (isTool == true) + public PWsdrCache(String pwCache, String pwcTokenname, byte[] keyId, + boolean isTool) throws Exception { + mPWcachedb = pwCache; + mIsTool = isTool; + mTokenName = pwcTokenname; + CryptoManager cm = null; + + if (keyId != null) { + mKeyID = keyId; + } + + cm = CryptoManager.getInstance(); + if (mTokenName != null) { + mToken = cm.getTokenByName(mTokenName); + debug("PWsdrCache: mToken = " + mTokenName); + } else { + mToken = cm.getInternalKeyStorageToken(); + debug("PWsdrCache: mToken = internal"); + } + } + + public byte[] getKeyId() { + return mKeyID; + } + + public String getTokenName() { + return mTokenName; + } + + public void deleteUniqueNamedKey(String nickName) + throws Exception { + KeyManager km = new KeyManager(mToken); + km.deleteUniqueNamedKey(nickName); + } + + public byte[] generateSDRKey() throws Exception { + return generateSDRKeyWithNickName(PROP_PWC_NICKNAME); + } + + public byte[] generateSDRKeyWithNickName(String nickName) + throws Exception { + try { + if (mIsTool == true) { + // generate SDR key + KeyManager km = new KeyManager(mToken); + try { + // Bugscape Bug #54838: Due to the CMS cloning feature, + // we must check for the presence of + // a uniquely named symmetric key + // prior to making an attempt to + // generate it! + // + if (!(km.uniqueNamedKeyExists(nickName))) { + mKeyID = km.generateUniqueNamedKey(nickName); + debug("PWsdrCache: SDR key generated"); + } + } catch (TokenException e) { + log(0, "generateSDRKey() failed on " + e.toString()); + throw e; + } + } + } catch (Exception e) { + log(0, e.toString()); + throw e; + } + return mKeyID; + } + + public void addEntry(String tag, String pwd) throws IOException { + addEntry(tag, pwd, (Hashtable<String, String>) null); + } + + /* + * Store passwd in pwcache. + */ + public void addEntry(Hashtable<String, String> ht) throws IOException { + addEntry((String) null, (String) null, ht); + } + + /* + * add passwd in pwcache. + */ + public void addEntry(String tag, String pwd, Hashtable<String, String> tagPwds) throws IOException { + System.out.println("PWsdrCache: in addEntry"); + String stringToAdd = null; + String bufs = null; + + if (tagPwds == null) { + stringToAdd = tag + ":" + pwd + "\n"; + } else { + Enumeration<String> enum1 = tagPwds.keys(); + + while (enum1.hasMoreElements()) { + tag = enum1.nextElement(); + pwd = tagPwds.get(tag); + debug("password tag: " + tag + " stored in " + mPWcachedb); + + if (stringToAdd == null) { + stringToAdd = tag + ":" + pwd + "\n"; + } else { + stringToAdd += tag + ":" + pwd + "\n"; + } + } + } + + String dcrypts = readPWcache(); + System.out.println("PWsdrCache: after readPWcache()"); + if (dcrypts != null) { + // converts to Hashtable, replace if tag exists, add + // if tag doesn't exist + Hashtable<String, String> ht = string2Hashtable(dcrypts); + + if (ht.containsKey(tag) == false) { + debug("adding new tag: " + tag); + ht.put(tag, pwd); + } else { + debug("replacing tag: " + tag); + ht.put(tag, pwd); + } + bufs = hashtable2String(ht); + } else { + debug("adding new tag: " + tag); + bufs = stringToAdd; + } + + // write update to cache + writePWcache(bufs); + } + + /* + * delete passwd in pwcache. + */ + public void deleteEntry(String tag) throws IOException { + String bufs = null; + + String dcrypts = readPWcache(); + + if (dcrypts != null) { + // converts to Hashtable, replace if tag exists, add + // if tag doesn't exist + Hashtable<String, String> ht = string2Hashtable(dcrypts); + + if (ht.containsKey(tag) == false) { + debug("tag: " + tag + " does not exist"); + return; + } else { + debug("deleting tag: " + tag); + ht.remove(tag); + } + bufs = hashtable2String(ht); + } else { + debug("password cache contains no tags"); + return; + } + + // write update to cache + writePWcache(bufs); + } + + /* + * reads and decrypts the pwcache.db content + */ + public String readPWcache() throws IOException { + debug("about to read password cache"); + String dcrypts = null; + if (mToken == null) { + debug("mToken is null"); + throw new IOException("token must be specified"); + } + + Decryptor sdr = new Decryptor(mToken); + + // not used, but could used for debugging + int totalRead = 0; + FileInputStream inputs = null; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + try { + // for SDR -> read, decrypt, append, and write + inputs = new FileInputStream(mPWcachedb); + byte[] readbuf = new byte[2048]; // for now + int numRead = 0; + + while ((numRead = inputs.read(readbuf)) != -1) { + bos.write(readbuf, 0, numRead); + totalRead += numRead; + } + inputs.close(); + } catch (FileNotFoundException e) { + System.out.println("Failed for file " + mPWcachedb + " " + e.toString()); + throw new IOException(e.toString() + ": " + mPWcachedb); + } catch (IOException e) { + System.out.println("Failed for file " + mPWcachedb + " " + e.toString()); + throw new IOException(e.toString() + ": " + mPWcachedb); + } + + if (totalRead > 0) { + try { + // decrypt it first to append + byte[] dcryptb = sdr.decrypt(bos.toByteArray()); + + dcrypts = new String(dcryptb, "UTF-8"); + } catch (TokenException e) { + System.out.println("password cache decrypto failed " + e.toString()); + e.printStackTrace(); + throw new IOException("password cache decrypt failed"); + } catch (UnsupportedEncodingException e) { + System.out.println("password cache decrypto failed " + e.toString()); + e.printStackTrace(); + throw new IOException("password cache decrypt failed"); + } catch (Exception e) { + System.out.println("password cache decrypto failed " + e.toString()); + e.printStackTrace(); + throw new IOException("password cache decrypt failed"); + } + } + + return dcrypts; + } + + /* + * encrypts and writes the whole String buf into pwcache.db + */ + public void writePWcache(String bufs) throws IOException { + + try { + Encryptor sdr = new Encryptor(mToken, mKeyID, + Encryptor.DEFAULT_ENCRYPTION_ALG); + + byte[] writebuf = null; + + try { + // now encrypt it again + writebuf = sdr.encrypt(bufs.getBytes("UTF-8")); + } catch (Exception e) { + System.out.println("password cache encrypt failed " + e.toString()); + e.printStackTrace(); + throw new IOException("password cache encrypt failed"); + } + + File tmpPWcache = new File(mPWcachedb + ".tmp"); + + if (tmpPWcache.exists()) { + // it wasn't removed? + tmpPWcache.delete(); + } + FileOutputStream outstream = new FileOutputStream(mPWcachedb + ".tmp"); + + outstream.write(writebuf); + outstream.close(); + + // Make certain that this temporary file has + // the correct permissions. + if (!isNT()) { + exec("chmod 00660 " + tmpPWcache.getAbsolutePath()); + } + + File origFile = new File(mPWcachedb); + + try { + // Always remove any pre-existing target file + if (origFile.exists()) { + origFile.delete(); + } + + if (isNT()) { + // NT is very picky on the path + exec("copy " + + tmpPWcache.getAbsolutePath().replace('/', '\\') + " " + + origFile.getAbsolutePath().replace('/', '\\')); + } else { + // Create a copy of the temporary file which + // preserves the temporary file's permissions. + exec("cp -p " + tmpPWcache.getAbsolutePath() + " " + + origFile.getAbsolutePath()); + } + + // Remove the temporary file if and only if + // the "rename" was successful. + if (origFile.exists()) { + tmpPWcache.delete(); + + // Make certain that the final file has + // the correct permissions. + if (!isNT()) { + exec("chmod 00660 " + origFile.getAbsolutePath()); + } + + // report success + debug("Renaming operation completed for " + mPWcachedb); + } else { + // report failure and exit + debug("Renaming operation failed for " + mPWcachedb); + System.exit(1); + } + } catch (IOException exx) { + System.out.println("sdrPWcache: Error " + exx.toString()); + throw new IOException(exx.toString() + ": " + mPWcachedb); + } + } catch (FileNotFoundException e) { + System.out.println("sdrPWcache: Error " + e.toString()); + throw new IOException(e.toString() + ": " + mPWcachedb); + } catch (IOException e) { + System.out.println("Failed for file " + mPWcachedb + " " + e.toString()); + throw new IOException(e.toString() + ": " + mPWcachedb); + } catch (Exception e) { + System.out.println("sdrPWcache: Error " + e.toString()); + throw new IOException(e.toString()); + } + } + + public String hashtable2String(Hashtable<String, String> ht) { + Enumeration<String> enum1 = ht.keys(); + String returnString = null; + + while (enum1.hasMoreElements()) { + String tag = enum1.nextElement(); + String pwd = ht.get(tag); + + if (returnString == null) { + returnString = tag + ":" + pwd + "\n"; + } else { + returnString += tag + ":" + pwd + "\n"; + } + } + return returnString; + } + + public Hashtable<String, String> string2Hashtable(String cache) { + Hashtable<String, String> ht = new Hashtable<String, String>(); + + // first, break into lines + StringTokenizer st = new StringTokenizer(cache, "\n"); + + while (st.hasMoreTokens()) { + String line = (String) st.nextToken(); + // break into tag:password format for each line + int colonIdx = line.indexOf(":"); + + if (colonIdx != -1) { + String tag = line.substring(0, colonIdx); + String passwd = line.substring(colonIdx + 1, + line.length()); + + ht.put(tag.trim(), passwd.trim()); + } else { + //invalid format...log or throw...later + } + } + return ht; + } + + /* + * get password from cache. This one supplies cache file name + */ + public Password getEntry(String fileName, String tag) { + mPWcachedb = fileName; + return getEntry(tag); + } + + /* + * if tag found with pwd, return it + * if tag not found, return null, which will cause it to give up + */ + public Password getEntry(String tag) { + Hashtable<String, String> pwTable = null; + String pw = null; + + debug("in getEntry, tag=" + tag); + + if (mPWcachedb == null) { + debug("mPWcachedb file path name is not initialized"); + return null; + } + + String dcrypts = null; + + try { + dcrypts = readPWcache(); + } catch (IOException e) { + System.out.println("dfailed readPWcache() " + e.toString()); + return null; + } + + if (dcrypts != null) { + // parse the cache + String cache = dcrypts; + + // this is created and destroyed at each use + pwTable = string2Hashtable(cache); + debug("in getEntry, pw cache parsed"); + pw = (String) pwTable.get(tag); + } + + if (pw != null) { + debug("getEntry gotten password for " + tag); + return new Password(pw.toCharArray()); + } else { + System.out.println("getEntry did not get password for tag " + tag); + return null; + } + } + + //copied from IOUtil.java + /** + * Checks if this is NT. + */ + public static boolean isNT() { + return ((File.separator).equals("\\")); + } + + public static boolean exec(String cmd) throws IOException { + try { + String cmds[] = null; + + if (isNT()) { + // NT + cmds = new String[3]; + cmds[0] = "cmd"; + cmds[1] = "/c"; + cmds[2] = cmd; + } else { + // UNIX + cmds = new String[3]; + cmds[0] = "/bin/sh"; + cmds[1] = "-c"; + cmds[2] = cmd; + } + Process process = Runtime.getRuntime().exec(cmds); + + process.waitFor(); + + if (process.exitValue() == 0) { + + /** + * pOut = new BufferedReader( + * new InputStreamReader(process.getInputStream())); + * while ((l = pOut.readLine()) != null) { + * System.out.println(l); + * } + **/ + return true; + } else { + + /** + * pOut = new BufferedReader( + * new InputStreamReader(process.getErrorStream())); + * l = null; + * while ((l = pOut.readLine()) != null) { + * System.out.println(l); + * } + **/ + return false; + } + } catch (Exception e) { + return false; + } + } + + public void debug(String msg) { + System.out.println(msg); + } + + public void log(int level, String msg) { + System.out.println(msg); + } + + /* + * list passwds in pwcache. + */ + public boolean pprint() { + String dcrypts = null; + + try { + dcrypts = readPWcache(); + } catch (IOException e) { + System.out.println("failed readPWcache() " + e.toString()); + return false; + } + + debug("----- Password Cache Content -----"); + + if (dcrypts != null) { + // first, break into lines + StringTokenizer st = new StringTokenizer(dcrypts, "\n"); + + while (st.hasMoreTokens()) { + String line = (String) st.nextToken(); + // break into tag:password format for each line + int colonIdx = line.indexOf(":"); + + if (colonIdx != -1) { + String tag = line.substring(0, colonIdx); + String passwd = line.substring(colonIdx + 1, + line.length()); + + debug(tag.trim() + + " : " + passwd.trim()); + } else { + //invalid format...log or throw...later + debug("invalid format"); + } + } + } // else print nothing + return true; + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/PrettyPrintCert.java b/base/java-tools/src/com/netscape/cmstools/PrettyPrintCert.java new file mode 100644 index 000000000..382c4e312 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/PrettyPrintCert.java @@ -0,0 +1,248 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.cert.CertificateException; +import java.util.Locale; + +import com.netscape.cmsutil.util.Utils; + +import netscape.security.util.CertPrettyPrint; +import netscape.security.x509.CertificateSubjectName; +import netscape.security.x509.RDN; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509CertInfo; + +/** + * The PrettyPrintCert class is a utility program designed to "pretty print" + * a certificate. It assumes that the name of a data file is passed to the + * program via the command line, and that the contents contain a certificate + * encoded in an ASCII BASE 64 format. Note that the data file may contain + * an optional "-----BEGIN" header and/or an optional "-----END" trailer. + * + * <P> + * The program may be invoked as follows: + * + * <PRE> + * + * PrettyPrintCert <input filename> [output filename] + * + * NOTE: <input filename> must contain an ASCII + * BASE 64 encoded certificate + * + * <output filename> contains a certificate displayed + * in a "pretty print" ASCII format + * </PRE> + * + * @version $Revision$, $Date$ + */ + +public class PrettyPrintCert { + // Define constants + public static final int ARGC = 2; + public static final String HEADER = "-----BEGIN"; + public static final String TRAILER = "-----END"; + + public static void usageAndExit() { + System.out.println("Usage: PrettyPrintCert " + + "[options] " + + "<input filename> " + + "[output filename]"); + System.out.println("\n options: "); + System.out.println(" -simpleinfo : prints limited cert info in easy to parse format"); + System.exit(0); + } + + public static void main(String argv[]) { + + BufferedReader inputCert = null; + String encodedBASE64CertChunk = new String(); + String encodedBASE64Cert = new String(); + byte decodedBASE64Cert[] = null; + X509CertImpl cert = null; + Locale aLocale = null; + CertPrettyPrint certDetails = null; + String pp = new String(); + FileOutputStream outputCert = null; + boolean mSimpleInfo = false; + String inputfile = null; + String outputfile = null; + + // parse arguments + + for (int i = 0; i < argv.length; i++) { + + // deal with empty arguments passed in by script + if (argv[i].equals("")) { + continue; + } + + // parse options + if (argv[i].charAt(0) == '-') { + if (argv[i].equals("-simpleinfo")) { + mSimpleInfo = true; + continue; + } else { + System.out.println("Illegal option: " + argv[i]); + usageAndExit(); + } + } + + // deal with filename + + if (inputfile == null) { + inputfile = argv[i]; + continue; + } + + if (outputfile == null) { + outputfile = argv[i]; + continue; + } + + System.out.println("Error - Too many arguments"); + System.exit(0); + } + + if (inputfile == null) { + usageAndExit(); + } + + // (2) Create a DataInputStream() object to the BASE 64 + // encoded certificate contained within the file + // specified on the command line + try { + inputCert = new BufferedReader(new InputStreamReader( + new BufferedInputStream( + new FileInputStream( + inputfile)))); + } catch (FileNotFoundException e) { + System.out.println("PrettyPrintCert: can't find file " + + inputfile + ":\n" + e); + return; + } + + // (3) Read the entire contents of the specified BASE 64 encoded + // certificate into a String() object throwing away any + // headers beginning with HEADER and any trailers beginning + // with TRAILER + try { + while ((encodedBASE64CertChunk = inputCert.readLine()) != null) { + if (!(encodedBASE64CertChunk.startsWith(HEADER)) && + !(encodedBASE64CertChunk.startsWith(TRAILER))) { + encodedBASE64Cert += encodedBASE64CertChunk.trim(); + } + } + } catch (IOException e) { + System.out.println("PrettyPrintCert: Unexpected BASE64 " + + "encoded error encountered in readLine():\n" + + e); + } + + // (4) Close the DataInputStream() object + try { + inputCert.close(); + } catch (IOException e) { + System.out.println("PrettyPrintCert: Unexpected BASE64 " + + "encoded error encountered in close():\n" + e); + } + + // (5) Decode the ASCII BASE 64 certificate enclosed in the + // String() object into a BINARY BASE 64 byte[] object + + decodedBASE64Cert = Utils.base64decode(encodedBASE64Cert); + + // (6) Create an X509CertImpl() object from the BINARY BASE 64 + // byte[] object + try { + cert = new X509CertImpl(decodedBASE64Cert); + } catch (CertificateException e) { + System.out.println("PrettyPrintCert: Error encountered " + + "on parsing certificate :\n" + e); + } + + if (mSimpleInfo) { + try { + X509CertInfo certinfo = (X509CertInfo) cert.get("x509.INFO"); + + CertificateSubjectName csn = (CertificateSubjectName) + certinfo.get(X509CertInfo.SUBJECT); + + X500Name dname = (X500Name) csn.get(CertificateSubjectName.DN_NAME); + + pp = ""; + RDN[] rdns = dname.getNames(); + + for (int i = rdns.length - 1; i >= 0; i--) { + pp = pp + rdns[i] + "\n"; + } + + } catch (Exception e) { + System.out.println("ERROR"); + e.printStackTrace(); + } + } else { + // (7) For this utility, always specify the default Locale + aLocale = Locale.getDefault(); + + // (8) Create a CertPrettyPrint() object + certDetails = new CertPrettyPrint(cert); + + // (9) Convert the CertPrettyPrint() object into a String() object + pp = certDetails.toString(aLocale); + } + + // (10) Finally, "pretty print" the actual certificate to the console + // unless an output file has been specified + if (outputfile == null) { + System.out.println(pp); + } else { + try { + outputCert = new FileOutputStream(outputfile); + } catch (Exception e) { + System.out.println("PrettyPrintCert: unable to open file " + + argv[1] + " for writing:\n" + e); + return; + } + + try { + outputCert.write(pp.getBytes()); + } catch (IOException e) { + System.out.println("PrettyPrintCert: Unexpected error " + + "encountered while attempting to write() " + + outputfile + ":\n" + e); + } + + try { + outputCert.close(); + } catch (IOException e) { + System.out.println("PrettyPrintCert: Unexpected error " + + "encountered while attempting to close() " + + outputfile + ":\n" + e); + } + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/PrettyPrintCrl.java b/base/java-tools/src/com/netscape/cmstools/PrettyPrintCrl.java new file mode 100644 index 000000000..8801b2423 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/PrettyPrintCrl.java @@ -0,0 +1,212 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.util.Locale; + +import com.netscape.cmsutil.util.Utils; + +import netscape.security.util.CrlPrettyPrint; +import netscape.security.x509.DeltaCRLIndicatorExtension; +import netscape.security.x509.HoldInstructionExtension; +import netscape.security.x509.InvalidityDateExtension; +import netscape.security.x509.IssuingDistributionPointExtension; +import netscape.security.x509.OIDMap; +import netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509ExtensionException; + +/** + * The PrettyPrintCrl class is a utility program designed to "pretty print" + * a CRL. It assumes that the name of a data file is passed to the + * program via the command line, and that the contents contain a CRL + * encoded in an ASCII BASE 64 format. Note that the data file may contain + * an optional "-----BEGIN" header and/or an optional "-----END" trailer. + * + * <P> + * The program may be invoked as follows: + * + * <PRE> + * + * PrettyPrintCrl <input filename> [output filename] + * + * NOTE: <input filename> must contain an ASCII + * BASE 64 encoded CRL + * + * <output filename> contains a CRL displayed + * in a "pretty print" ASCII format + * </PRE> + * + * @version $Revision$, $Date$ + */ + +public class PrettyPrintCrl { + // Define constants + public static final int ARGC = 2; + public static final String HEADER = "-----BEGIN"; + public static final String TRAILER = "-----END"; + + public static void main(String argv[]) { + + BufferedReader inputCrl = null; + String encodedBASE64CrlChunk = new String(); + String encodedBASE64Crl = new String(); + byte decodedBASE64Crl[] = null; + X509CRLImpl crl = null; + Locale aLocale = null; + CrlPrettyPrint CrlDetails = null; + String pp = new String(); + FileOutputStream outputCrl = null; + + // (1) Check that at least one argument was submitted to the program + if ((argv.length < 1) || (argv.length > ARGC)) { + System.out.println("Usage: PrettyPrintCrl " + + "<input filename> " + + "[output filename]"); + return; + } + + try { + OIDMap.addAttribute(DeltaCRLIndicatorExtension.class.getName(), + DeltaCRLIndicatorExtension.OID, + DeltaCRLIndicatorExtension.NAME); + } catch (CertificateException e) { + } + try { + OIDMap.addAttribute(HoldInstructionExtension.class.getName(), + HoldInstructionExtension.OID, + HoldInstructionExtension.NAME); + } catch (CertificateException e) { + } + try { + OIDMap.addAttribute(InvalidityDateExtension.class.getName(), + InvalidityDateExtension.OID, + InvalidityDateExtension.NAME); + } catch (CertificateException e) { + } + try { + OIDMap.addAttribute(IssuingDistributionPointExtension.class.getName(), + IssuingDistributionPointExtension.OID, + IssuingDistributionPointExtension.NAME); + } catch (CertificateException e) { + } + + // (2) Create a DataInputStream() object to the BASE 64 + // encoded CRL contained within the file + // specified on the command line + try { + inputCrl = new BufferedReader(new InputStreamReader( + new BufferedInputStream( + new FileInputStream( + argv[0])))); + } catch (FileNotFoundException e) { + System.out.println("PrettyPrintCrl(): can''t find file " + + argv[0] + ":\n" + e); + return; + } + + // (3) Read the entire contents of the specified BASE 64 encoded + // CRL into a String() object throwing away any + // headers beginning with HEADER and any trailers beginning + // with TRAILER + try { + while ((encodedBASE64CrlChunk = inputCrl.readLine()) != null) { + if (!(encodedBASE64CrlChunk.startsWith(HEADER)) && + !(encodedBASE64CrlChunk.startsWith(TRAILER))) { + encodedBASE64Crl += encodedBASE64CrlChunk.trim(); + } + } + } catch (IOException e) { + System.out.println("PrettyPrintCrl(): Unexpected BASE64 " + + "encoded error encountered in readLine():\n" + + e); + } + + // (4) Close the DataInputStream() object + try { + inputCrl.close(); + } catch (IOException e) { + System.out.println("PrettyPrintCrl(): Unexpected BASE64 " + + "encoded error encountered in close():\n" + e); + } + + // (5) Decode the ASCII BASE 64 CRL enclosed in the + // String() object into a BINARY BASE 64 byte[] object + + decodedBASE64Crl = Utils.base64decode(encodedBASE64Crl); + + // (6) Create an X509CRLImpl() object from the BINARY BASE 64 + // byte[] object + try { + crl = new X509CRLImpl(decodedBASE64Crl); + } catch (CRLException e) { + System.out.println("PrettyPrintCrl(): Error encountered " + + "on parsing and initialization errors:\n" + e); + } catch (X509ExtensionException e) { + System.out.println("PrettyPrintCrl(): Error encountered " + + "on parsing and initialization errors:\n" + e); + } + + // (7) For this utility, always specify the default Locale + aLocale = Locale.getDefault(); + + // (8) Create a CrlPrettyPrint() object + CrlDetails = new CrlPrettyPrint(crl); + + // (9) Convert the CrlPrettyPrint() object into a String() object + pp = CrlDetails.toString(aLocale); + + // (10) Finally, "pretty print" the actual CRL to the console + // unless an output file has been specified + if (argv.length != ARGC) { + System.out.println(pp); + } else { + try { + outputCrl = new FileOutputStream(argv[1]); + } catch (IOException e) { + System.out.println("PrettyPrintCrl(): unable to open file " + + argv[1] + " for writing:\n" + e); + return; + } + + try { + outputCrl.write(pp.getBytes()); + } catch (IOException e) { + System.out.println("PrettyPrintCrl(): I/O error " + + "encountered during write():\n" + + e); + } + + try { + outputCrl.close(); + } catch (IOException e) { + System.out.println("PrettyPrintCrl(): Unexpected error " + + "encountered while attempting to close() " + + argv[1] + ":\n" + e); + } + } + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/TestCRLSigning.java b/base/java-tools/src/com/netscape/cmstools/TestCRLSigning.java new file mode 100644 index 000000000..369010abf --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/TestCRLSigning.java @@ -0,0 +1,115 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.util.Date; +import java.util.Hashtable; + +import netscape.security.x509.RevokedCertImpl; +import netscape.security.x509.RevokedCertificate; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.KeyPairAlgorithm; +import org.mozilla.jss.crypto.KeyPairGenerator; +import org.mozilla.jss.util.Password; + +/** + * Tool used to test out signing a CRL + * + * <p> + * + * @version $Revision$ Date: $ + */ +public class TestCRLSigning { + public static void printUsage() { + System.out.println("Command <dbdir> <numreovked> <keysize> <tokenname> <tokenpwd>"); + } + + public static void main(String args[]) throws Exception { + String dir = args[0]; + String num = args[1]; + String keysize = args[2]; + String tokenname = args[3]; + String tokenpwd = args[4]; + + // initialize JSS + CryptoManager cm = null; + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(dir, "", "", "secmod.db"); + CryptoManager.initialize(vals); + cm = CryptoManager.getInstance(); + + // Login to token + CryptoToken token = null; + if (tokenname.equals("internal")) { + token = cm.getInternalKeyStorageToken(); + } else { + token = cm.getTokenByName(tokenname); + } + Password pass = new Password(tokenpwd.toCharArray()); + token.login(pass); + + // generate key pair + KeyPairGenerator g = token.getKeyPairGenerator(KeyPairAlgorithm.RSA); + g.initialize(Integer.parseInt(keysize)); + KeyPair pair = g.genKeyPair(); + + // generate revoked certificates + long startPutting = System.currentTimeMillis(); + Date curDate = new Date(); + Hashtable<BigInteger, RevokedCertificate> badCerts = new Hashtable<BigInteger, RevokedCertificate>(); + int n = Integer.parseInt(num); + for (int i = 0; i < n; i++) { + badCerts.put(new BigInteger(Integer.toString(i)), + new RevokedCertImpl(new BigInteger(Integer.toString(i)), curDate)); + } + long endPutting = System.currentTimeMillis(); + + long startConstructing = System.currentTimeMillis(); + X509CRLImpl crl = new X509CRLImpl( + new X500Name("CN=Signer"), + null, + curDate, + curDate, + badCerts, + null); + long endConstructing = System.currentTimeMillis(); + + System.out.println("Start signing"); + long startSigning = System.currentTimeMillis(); + crl.sign(pair.getPrivate(), "SHA1withRSA"); + long endSigning = System.currentTimeMillis(); + System.out.println("Done signing"); + + long startData = System.currentTimeMillis(); + byte data[] = crl.getTBSCertList(); + long endData = System.currentTimeMillis(); + + System.out.println("Summary:"); + System.out.println("Insertion time (ms): " + Long.toString(endPutting - startPutting)); + System.out.println("Construction time (ms): " + Long.toString(endConstructing - startConstructing)); + System.out.println("Signing time (ms): " + Long.toString(endSigning - startSigning)); + System.out.println("Data time (ms): " + Long.toString(endData - startData)); + System.out.println("Data size (bytes): " + Long.toString(data.length)); + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/TokenInfo.java b/base/java-tools/src/com/netscape/cmstools/TokenInfo.java new file mode 100644 index 000000000..fc3d13b42 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/TokenInfo.java @@ -0,0 +1,75 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmstools; + +import java.util.Enumeration; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.pkcs11.PK11Module; + +/** + * Tool used to determine which external hardware tokens are visible to the + * Certificate System subsystem. This can be used to diagnose whether problems + * using tokens are related to the Certificate System being unable to detect it. + * + * <p> + * + * @version $Revision$ Date: $ + */ +public class TokenInfo { + + /** + * Creates a new instance of CMCRevoke. + */ + public static void main(String[] args) { + try { + if (args.length != 1) { + System.out.println("Usage: TokenInfo <alias directory>"); + System.exit(0); + } + System.out.println("Database Path: " + args[0]); + + CryptoManager.InitializationValues vals = + new CryptoManager.InitializationValues(args[0], + "", "", "secmod.db"); + + CryptoManager.initialize(vals); + + CryptoManager cm = CryptoManager.getInstance(); + @SuppressWarnings("unchecked") + Enumeration<PK11Module> modules = cm.getModules(); + while (modules.hasMoreElements()) { + PK11Module m = modules.nextElement(); + System.out.println("Found external module '" + m.getName() + "'"); + } + @SuppressWarnings("unchecked") + Enumeration<CryptoToken> tokens = cm.getExternalTokens(); + + while (tokens.hasMoreElements()) { + CryptoToken t = tokens.nextElement(); + System.out.println("Found external token '" + t.getName() + "'"); + } + + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + } +} diff --git a/base/java-tools/templates/CMakeLists.txt b/base/java-tools/templates/CMakeLists.txt new file mode 100644 index 000000000..b7c6e891c --- /dev/null +++ b/base/java-tools/templates/CMakeLists.txt @@ -0,0 +1,67 @@ +project(java-tools-wrapper) + +set(PKI_PRODUCT pki) +set(PKI_COMMANDS + AtoB + AuditVerify + BtoA + CMCEnroll + CMCRequest + CMCResponse + CMCRevoke + CRMFPopClient + DRMTool + ExtJoiner + GenExtKeyUsage + GenIssuerAltNameExt + GenSubjectAltNameExt + HttpClient + OCSPClient + PKCS10Client + PKCS12Export + TokenInfo +) + +foreach(PKI_COMMAND ${PKI_COMMANDS}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pki_java_command_wrapper.in ${CMAKE_CURRENT_BINARY_DIR}/${PKI_COMMAND} @ONLY) + + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PKI_COMMAND} + DESTINATION + ${BIN_INSTALL_DIR} + PERMISSIONS + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ + ) +endforeach(PKI_COMMAND) + + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pretty_print_cert_command_wrapper.in ${CMAKE_CURRENT_BINARY_DIR}/PrettyPrintCert @ONLY) + +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/PrettyPrintCert + DESTINATION + ${BIN_INSTALL_DIR} + PERMISSIONS + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ +) + + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pretty_print_crl_command_wrapper.in ${CMAKE_CURRENT_BINARY_DIR}/PrettyPrintCrl @ONLY) + +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/PrettyPrintCrl + DESTINATION + ${BIN_INSTALL_DIR} + PERMISSIONS + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ +) + diff --git a/base/java-tools/templates/pki_java_command_wrapper.in b/base/java-tools/templates/pki_java_command_wrapper.in new file mode 100644 index 000000000..b0d406161 --- /dev/null +++ b/base/java-tools/templates/pki_java_command_wrapper.in @@ -0,0 +1,150 @@ +#!/bin/sh +# +# --- BEGIN COPYRIGHT BLOCK --- +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2007 Red Hat, Inc. +# All rights reserved. +# --- END COPYRIGHT BLOCK --- +# + +# Check to insure that this script's original invocation directory +# has not been deleted! +CWD=`/bin/pwd > /dev/null 2>&1` +if [ $? -ne 0 ] ; then + echo "Cannot invoke '$0' from non-existent directory!" + exit 255 +fi + + +############################################################################### +## (1) Specify variables used by this script. ## +############################################################################### + +PRODUCT=@PKI_PRODUCT@ +COMMAND=@PKI_COMMAND@ + + +############################################################################### +## (2) Check for valid usage of this command wrapper. ## +############################################################################### + + + +############################################################################### +## (3) Define helper functions. ## +############################################################################### + +invalid_operating_system() { + echo + echo "ERROR: '$0' does not execute on the '$1' operating system!" + echo +} + +invalid_architecture() { + echo + echo "ERROR: '$0' does not execute on the '$1' architecture!" + echo +} + + +############################################################################### +## (4) Set the LD_LIBRARY_PATH environment variable to determine the ## +## search order this command wrapper uses to find shared libraries. ## +############################################################################### + +OS=`uname -s` + +if [ "${OS}" = "Linux" ] ; then + ARCHITECTURE=`uname -i` + JAVA="java" + JAVA_OPTIONS="" + + if [ "${ARCHITECTURE}" = "i386" ] ; then + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/jss:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + elif [ "${ARCHITECTURE}" = "x86_64" ] ; then + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/jss:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib64:/lib64:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib64/jss:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + else + invalid_architecture "${ARCHITECTURE}" + exit 255 + fi +elif [ "${OS}" = "SunOS" ] ; then + ARCHITECTURE=`uname -p` + if [ "${ARCHITECTURE}" = "sparc" ] && + [ -d "/usr/lib/sparcv9/" ] ; then + ARCHITECTURE="sparcv9" + fi + if [ "${ARCHITECTURE}" = "sparc" ] ; then + JAVA="/usr/jdk/instances/jdk1.5.0/jre/bin/java" + JAVA_OPTIONS="" + + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/${PRODUCT}:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + elif [ "${ARCHITECTURE}" = "sparcv9" ] ; then + JAVA="/usr/jdk/instances/jdk1.5.0/jre/bin/java" + JAVA_OPTIONS="-d64" + + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/${PRODUCT}:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9:/lib/sparcv9:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9/${PRODUCT}:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + else + invalid_architecture "${ARCHITECTURE}" + exit 255 + fi +else + invalid_operating_system "${OS}" + exit 255 +fi + + +############################################################################### +## (5) Set the CP environment variable to determine the search ## +## order this command wrapper uses to find jar files. ## +############################################################################### + +CP=/usr/lib/java/jss4.jar +CP=/usr/lib/java/dirsec/jss4.jar:${CP} +if [ "${OS}" = "Linux" ] && + [ "${ARCHITECTURE}" = "x86_64" ] ; then + # Fedora 16+ + CP=/usr/lib64/java/jss4.jar +fi +CP=/usr/share/java/commons-codec.jar:${CP} +CP=/usr/share/java/ldapjdk.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-nsutil.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-cmsutil.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-tools.jar:${CP} +export CP + + +############################################################################### +## (6) Execute the java command specified by this java command wrapper ## +## based upon the preset LD_LIBRARY_PATH and CP environment variables. ## +############################################################################### + +${JAVA} ${JAVA_OPTIONS} -cp ${CP} com.netscape.cmstools.${COMMAND} "$@" +exit $? + diff --git a/base/java-tools/templates/pretty_print_cert_command_wrapper.in b/base/java-tools/templates/pretty_print_cert_command_wrapper.in new file mode 100644 index 000000000..7f3331357 --- /dev/null +++ b/base/java-tools/templates/pretty_print_cert_command_wrapper.in @@ -0,0 +1,178 @@ +#!/bin/sh +# +# --- BEGIN COPYRIGHT BLOCK --- +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2007 Red Hat, Inc. +# All rights reserved. +# --- END COPYRIGHT BLOCK --- +# + +# Check to insure that this script's original invocation directory +# has not been deleted! +CWD=`/bin/pwd > /dev/null 2>&1` +if [ $? -ne 0 ] ; then + echo "Cannot invoke '$0' from non-existent directory!" + exit 255 +fi + + +############################################################################### +## (1) Specify variables used by this script. ## +############################################################################### + +PRODUCT=@PKI_PRODUCT@ +COMMAND=@PKI_COMMAND@ + + +############################################################################### +## (2) Check for valid usage of this command wrapper. ## +############################################################################### + + + +############################################################################### +## (3) Define helper functions. ## +############################################################################### + +invalid_operating_system() { + echo + echo "ERROR: '$0' does not execute on the '$1' operating system!" + echo +} + +invalid_architecture() { + echo + echo "ERROR: '$0' does not execute on the '$1' architecture!" + echo +} + + +############################################################################### +## (4) Set the LD_LIBRARY_PATH environment variable to determine the ## +## search order this command wrapper uses to find shared libraries. ## +############################################################################### + +OS=`uname -s` + +if [ "${OS}" = "Linux" ] ; then + ARCHITECTURE=`uname -i` + JAVA="java" + JAVA_OPTIONS="" + + if [ "${ARCHITECTURE}" = "i386" ] ; then + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/jss:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + elif [ "${ARCHITECTURE}" = "x86_64" ] ; then + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/jss:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib64:/lib64:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib64/jss:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + else + invalid_architecture "${ARCHITECTURE}" + exit 255 + fi +elif [ "${OS}" = "SunOS" ] ; then + ARCHITECTURE=`uname -p` + if [ "${ARCHITECTURE}" = "sparc" ] && + [ -d "/usr/lib/sparcv9/" ] ; then + ARCHITECTURE="sparcv9" + fi + if [ "${ARCHITECTURE}" = "sparc" ] ; then + JAVA="/usr/jdk/instances/jdk1.5.0/jre/bin/java" + JAVA_OPTIONS="" + + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/${PRODUCT}:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + elif [ "${ARCHITECTURE}" = "sparcv9" ] ; then + JAVA="/usr/jdk/instances/jdk1.5.0/jre/bin/java" + JAVA_OPTIONS="-d64" + + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/${PRODUCT}:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9:/lib/sparcv9:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9/${PRODUCT}:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + else + invalid_architecture "${ARCHITECTURE}" + exit 255 + fi +else + invalid_operating_system "${OS}" + exit 255 +fi + + +############################################################################### +## (5) Set the CP environment variable to determine the search ## +## order this command wrapper uses to find jar files. ## +############################################################################### + +CP=/usr/lib/java/jss4.jar +CP=/usr/lib/java/dirsec/jss4.jar:${CP} +if [ "${OS}" = "Linux" ] && + [ "${ARCHITECTURE}" = "x86_64" ] ; then + # Fedora 16+ + CP=/usr/lib64/java/jss4.jar +fi +CP=/usr/share/java/commons-codec.jar:${CP} +CP=/usr/share/java/ldapjdk.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-nsutil.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-cmsutil.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-tools.jar:${CP} +export CP + + +############################################################################### +## (6) Execute the java command specified by this java command wrapper ## +## based upon the preset LD_LIBRARY_PATH and CP environment variables. ## +############################################################################### + +if [ $# -eq 1 ] || + [ $# -eq 2 ] || + [ $# -eq 3 ] +then + if [ "$1" = "-simpleinfo" ] + then + file $2 | grep 'ASCII text' > /dev/null + if [ $? -ne 0 ] ; then + ${JAVA} ${JAVA_OPTIONS} -cp ${CP} com.netscape.cmstools.${COMMAND} + printf "\n" + printf " ERROR: '$2' is not an ASCII file!\n\n" + printf " First, use 'BtoA $2 $2.b64'\n" + printf " to convert a binary file into an ASCII file.\n\n" + exit 255 + fi + else + file $1 | grep 'ASCII text' > /dev/null + if [ $? -ne 0 ] ; then + ${JAVA} ${JAVA_OPTIONS} -cp ${CP} com.netscape.cmstools.${COMMAND} + printf "\n" + printf " ERROR: '$1' is not an ASCII file!\n\n" + printf " First, use 'BtoA $1 $1.b64'\n" + printf " to convert a binary file into an ASCII file.\n\n" + exit 255 + fi + fi +fi + +${JAVA} ${JAVA_OPTIONS} -cp ${CP} com.netscape.cmstools.${COMMAND} "$@" +exit $? + diff --git a/base/java-tools/templates/pretty_print_crl_command_wrapper.in b/base/java-tools/templates/pretty_print_crl_command_wrapper.in new file mode 100644 index 000000000..95a559503 --- /dev/null +++ b/base/java-tools/templates/pretty_print_crl_command_wrapper.in @@ -0,0 +1,164 @@ +#!/bin/sh +# +# --- BEGIN COPYRIGHT BLOCK --- +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2007 Red Hat, Inc. +# All rights reserved. +# --- END COPYRIGHT BLOCK --- +# + +# Check to insure that this script's original invocation directory +# has not been deleted! +CWD=`/bin/pwd > /dev/null 2>&1` +if [ $? -ne 0 ] ; then + echo "Cannot invoke '$0' from non-existent directory!" + exit 255 +fi + + +############################################################################### +## (1) Specify variables used by this script. ## +############################################################################### + +PRODUCT=@PKI_PRODUCT@ +COMMAND=@PKI_COMMAND@ + + +############################################################################### +## (2) Check for valid usage of this command wrapper. ## +############################################################################### + + + +############################################################################### +## (3) Define helper functions. ## +############################################################################### + +invalid_operating_system() { + echo + echo "ERROR: '$0' does not execute on the '$1' operating system!" + echo +} + +invalid_architecture() { + echo + echo "ERROR: '$0' does not execute on the '$1' architecture!" + echo +} + + +############################################################################### +## (4) Set the LD_LIBRARY_PATH environment variable to determine the ## +## search order this command wrapper uses to find shared libraries. ## +############################################################################### + +OS=`uname -s` + +if [ "${OS}" = "Linux" ] ; then + ARCHITECTURE=`uname -i` + JAVA="java" + JAVA_OPTIONS="" + + if [ "${ARCHITECTURE}" = "i386" ] ; then + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/jss:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + elif [ "${ARCHITECTURE}" = "x86_64" ] ; then + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/jss:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib64:/lib64:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib64/jss:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + else + invalid_architecture "${ARCHITECTURE}" + exit 255 + fi +elif [ "${OS}" = "SunOS" ] ; then + ARCHITECTURE=`uname -p` + if [ "${ARCHITECTURE}" = "sparc" ] && + [ -d "/usr/lib/sparcv9/" ] ; then + ARCHITECTURE="sparcv9" + fi + if [ "${ARCHITECTURE}" = "sparc" ] ; then + JAVA="/usr/jdk/instances/jdk1.5.0/jre/bin/java" + JAVA_OPTIONS="" + + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/${PRODUCT}:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + elif [ "${ARCHITECTURE}" = "sparcv9" ] ; then + JAVA="/usr/jdk/instances/jdk1.5.0/jre/bin/java" + JAVA_OPTIONS="-d64" + + LD_LIBRARY_PATH=/usr/lib:/lib + LD_LIBRARY_PATH=/usr/lib/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/${PRODUCT}:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9:/lib/sparcv9:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9/dirsec:${LD_LIBRARY_PATH} + LD_LIBRARY_PATH=/usr/lib/sparcv9/${PRODUCT}:${LD_LIBRARY_PATH} + export LD_LIBRARY_PATH + else + invalid_architecture "${ARCHITECTURE}" + exit 255 + fi +else + invalid_operating_system "${OS}" + exit 255 +fi + + +############################################################################### +## (5) Set the CP environment variable to determine the search ## +## order this command wrapper uses to find jar files. ## +############################################################################### + +CP=/usr/lib/java/jss4.jar +CP=/usr/lib/java/dirsec/jss4.jar:${CP} +if [ "${OS}" = "Linux" ] && + [ "${ARCHITECTURE}" = "x86_64" ] ; then + # Fedora 16+ + CP=/usr/lib64/java/jss4.jar +fi +CP=/usr/share/java/commons-codec.jar:${CP} +CP=/usr/share/java/ldapjdk.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-nsutil.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-cmsutil.jar:${CP} +CP=/usr/share/java/${PRODUCT}/pki-tools.jar:${CP} +export CP + + +############################################################################### +## (6) Execute the java command specified by this java command wrapper ## +## based upon the preset LD_LIBRARY_PATH and CP environment variables. ## +############################################################################### + +if [ $# -eq 1 ] || + [ $# -eq 2 ] +then + file $1 | grep 'ASCII text' > /dev/null + if [ $? -ne 0 ] ; then + ${JAVA} ${JAVA_OPTIONS} -cp ${CP} com.netscape.cmstools.${COMMAND} + printf "\n" + printf "ERROR: '$1' is not an ASCII file!\n\n" + printf " First, use 'BtoA $1 $1.b64'\n" + printf " to convert a binary file into an ASCII file.\n\n" + exit 255 + fi +fi + +${JAVA} ${JAVA_OPTIONS} -cp ${CP} com.netscape.cmstools.${COMMAND} "$@" +exit $? + |