diff --git a/.gitignore b/.gitignore index dead9a9..d7181f9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ jss-4.2.6.tar.gz /jss-4.4.0.tar.gz /jss-4.4.1.tar.gz /jss-4.4.2.tar.gz +/jss-4.4.4.tar.gz diff --git a/MPL-1.1.txt b/MPL-1.1.txt deleted file mode 100644 index 7714141..0000000 --- a/MPL-1.1.txt +++ /dev/null @@ -1,470 +0,0 @@ - MOZILLA PUBLIC LICENSE - Version 1.1 - - --------------- - -1. Definitions. - - 1.0.1. "Commercial Use" means distribution or otherwise making the - Covered Code available to a third party. - - 1.1. "Contributor" means each entity that creates or contributes to - the creation of Modifications. - - 1.2. "Contributor Version" means the combination of the Original - Code, prior Modifications used by a Contributor, and the Modifications - made by that particular Contributor. - - 1.3. "Covered Code" means the Original Code or Modifications or the - combination of the Original Code and Modifications, in each case - including portions thereof. - - 1.4. "Electronic Distribution Mechanism" means a mechanism generally - accepted in the software development community for the electronic - transfer of data. - - 1.5. "Executable" means Covered Code in any form other than Source - Code. - - 1.6. "Initial Developer" means the individual or entity identified - as the Initial Developer in the Source Code notice required by Exhibit - A. - - 1.7. "Larger Work" means a work which combines Covered Code or - portions thereof with code not governed by the terms of this License. - - 1.8. "License" means this document. - - 1.8.1. "Licensable" means having the right to grant, to the maximum - extent possible, whether at the time of the initial grant or - subsequently acquired, any and all of the rights conveyed herein. - - 1.9. "Modifications" means any addition to or deletion from the - substance or structure of either the Original Code or any previous - Modifications. When Covered Code is released as a series of files, a - Modification is: - A. Any addition to or deletion from the contents of a file - containing Original Code or previous Modifications. - - B. Any new file that contains any part of the Original Code or - previous Modifications. - - 1.10. "Original Code" means Source Code of computer software code - which is described in the Source Code notice required by Exhibit A as - Original Code, and which, at the time of its release under this - License is not already Covered Code governed by this License. - - 1.10.1. "Patent Claims" means any patent claim(s), now owned or - hereafter acquired, including without limitation, method, process, - and apparatus claims, in any patent Licensable by grantor. - - 1.11. "Source Code" means the preferred form of the Covered Code for - making modifications to it, including all modules it contains, plus - any associated interface definition files, scripts used to control - compilation and installation of an Executable, or source code - differential comparisons against either the Original Code or another - well known, available Covered Code of the Contributor's choice. The - Source Code can be in a compressed or archival form, provided the - appropriate decompression or de-archiving software is widely available - for no charge. - - 1.12. "You" (or "Your") means an individual or a legal entity - exercising rights under, and complying with all of the terms of, this - License or a future version of this License issued under Section 6.1. - For legal entities, "You" includes any entity which controls, is - controlled by, or is under common control with You. For purposes of - this definition, "control" means (a) the power, direct or indirect, - to cause the direction or management of such entity, whether by - contract or otherwise, or (b) ownership of more than fifty percent - (50%) of the outstanding shares or beneficial ownership of such - entity. - -2. Source Code License. - - 2.1. The Initial Developer Grant. - The Initial Developer hereby grants You a world-wide, royalty-free, - non-exclusive license, subject to third party intellectual property - claims: - (a) under intellectual property rights (other than patent or - trademark) Licensable by Initial Developer to use, reproduce, - modify, display, perform, sublicense and distribute the Original - Code (or portions thereof) with or without Modifications, and/or - as part of a Larger Work; and - - (b) under Patents Claims infringed by the making, using or - selling of Original Code, to make, have made, use, practice, - sell, and offer for sale, and/or otherwise dispose of the - Original Code (or portions thereof). - - (c) the licenses granted in this Section 2.1(a) and (b) are - effective on the date Initial Developer first distributes - Original Code under the terms of this License. - - (d) Notwithstanding Section 2.1(b) above, no patent license is - granted: 1) for code that You delete from the Original Code; 2) - separate from the Original Code; or 3) for infringements caused - by: i) the modification of the Original Code or ii) the - combination of the Original Code with other software or devices. - - 2.2. Contributor Grant. - Subject to third party intellectual property claims, each Contributor - hereby grants You a world-wide, royalty-free, non-exclusive license - - (a) under intellectual property rights (other than patent or - trademark) Licensable by Contributor, to use, reproduce, modify, - display, perform, sublicense and distribute the Modifications - created by such Contributor (or portions thereof) either on an - unmodified basis, with other Modifications, as Covered Code - and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using, or - selling of Modifications made by that Contributor either alone - and/or in combination with its Contributor Version (or portions - of such combination), to make, use, sell, offer for sale, have - made, and/or otherwise dispose of: 1) Modifications made by that - Contributor (or portions thereof); and 2) the combination of - Modifications made by that Contributor with its Contributor - Version (or portions of such combination). - - (c) the licenses granted in Sections 2.2(a) and 2.2(b) are - effective on the date Contributor first makes Commercial Use of - the Covered Code. - - (d) Notwithstanding Section 2.2(b) above, no patent license is - granted: 1) for any code that Contributor has deleted from the - Contributor Version; 2) separate from the Contributor Version; - 3) for infringements caused by: i) third party modifications of - Contributor Version or ii) the combination of Modifications made - by that Contributor with other software (except as part of the - Contributor Version) or other devices; or 4) under Patent Claims - infringed by Covered Code in the absence of Modifications made by - that Contributor. - -3. Distribution Obligations. - - 3.1. Application of License. - The Modifications which You create or to which You contribute are - governed by the terms of this License, including without limitation - Section 2.2. The Source Code version of Covered Code may be - distributed only under the terms of this License or a future version - of this License released under Section 6.1, and You must include a - copy of this License with every copy of the Source Code You - distribute. You may not offer or impose any terms on any Source Code - version that alters or restricts the applicable version of this - License or the recipients' rights hereunder. However, You may include - an additional document offering the additional rights described in - Section 3.5. - - 3.2. Availability of Source Code. - Any Modification which You create or to which You contribute must be - made available in Source Code form under the terms of this License - either on the same media as an Executable version or via an accepted - Electronic Distribution Mechanism to anyone to whom you made an - Executable version available; and if made available via Electronic - Distribution Mechanism, must remain available for at least twelve (12) - months after the date it initially became available, or at least six - (6) months after a subsequent version of that particular Modification - has been made available to such recipients. You are responsible for - ensuring that the Source Code version remains available even if the - Electronic Distribution Mechanism is maintained by a third party. - - 3.3. Description of Modifications. - You must cause all Covered Code to which You contribute to contain a - file documenting the changes You made to create that Covered Code and - the date of any change. You must include a prominent statement that - the Modification is derived, directly or indirectly, from Original - Code provided by the Initial Developer and including the name of the - Initial Developer in (a) the Source Code, and (b) in any notice in an - Executable version or related documentation in which You describe the - origin or ownership of the Covered Code. - - 3.4. Intellectual Property Matters - (a) Third Party Claims. - If Contributor has knowledge that a license under a third party's - intellectual property rights is required to exercise the rights - granted by such Contributor under Sections 2.1 or 2.2, - Contributor must include a text file with the Source Code - distribution titled "LEGAL" which describes the claim and the - party making the claim in sufficient detail that a recipient will - know whom to contact. If Contributor obtains such knowledge after - the Modification is made available as described in Section 3.2, - Contributor shall promptly modify the LEGAL file in all copies - Contributor makes available thereafter and shall take other steps - (such as notifying appropriate mailing lists or newsgroups) - reasonably calculated to inform those who received the Covered - Code that new knowledge has been obtained. - - (b) Contributor APIs. - If Contributor's Modifications include an application programming - interface and Contributor has knowledge of patent licenses which - are reasonably necessary to implement that API, Contributor must - also include this information in the LEGAL file. - - (c) Representations. - Contributor represents that, except as disclosed pursuant to - Section 3.4(a) above, Contributor believes that Contributor's - Modifications are Contributor's original creation(s) and/or - Contributor has sufficient rights to grant the rights conveyed by - this License. - - 3.5. Required Notices. - You must duplicate the notice in Exhibit A in each file of the Source - Code. If it is not possible to put such notice in a particular Source - Code file due to its structure, then You must include such notice in a - location (such as a relevant directory) where a user would be likely - to look for such a notice. If You created one or more Modification(s) - You may add your name as a Contributor to the notice described in - Exhibit A. You must also duplicate this License in any documentation - for the Source Code where You describe recipients' rights or ownership - rights relating to Covered Code. You may choose to offer, and to - charge a fee for, warranty, support, indemnity or liability - obligations to one or more recipients of Covered Code. However, You - may do so only on Your own behalf, and not on behalf of the Initial - Developer or any Contributor. You must make it absolutely clear than - any such warranty, support, indemnity or liability obligation is - offered by You alone, and You hereby agree to indemnify the Initial - Developer and every Contributor for any liability incurred by the - Initial Developer or such Contributor as a result of warranty, - support, indemnity or liability terms You offer. - - 3.6. Distribution of Executable Versions. - You may distribute Covered Code in Executable form only if the - requirements of Section 3.1-3.5 have been met for that Covered Code, - and if You include a notice stating that the Source Code version of - the Covered Code is available under the terms of this License, - including a description of how and where You have fulfilled the - obligations of Section 3.2. The notice must be conspicuously included - in any notice in an Executable version, related documentation or - collateral in which You describe recipients' rights relating to the - Covered Code. You may distribute the Executable version of Covered - Code or ownership rights under a license of Your choice, which may - contain terms different from this License, provided that You are in - compliance with the terms of this License and that the license for the - Executable version does not attempt to limit or alter the recipient's - rights in the Source Code version from the rights set forth in this - License. If You distribute the Executable version under a different - license You must make it absolutely clear that any terms which differ - from this License are offered by You alone, not by the Initial - Developer or any Contributor. You hereby agree to indemnify the - Initial Developer and every Contributor for any liability incurred by - the Initial Developer or such Contributor as a result of any such - terms You offer. - - 3.7. Larger Works. - You may create a Larger Work by combining Covered Code with other code - not governed by the terms of this License and distribute the Larger - Work as a single product. In such a case, You must make sure the - requirements of this License are fulfilled for the Covered Code. - -4. Inability to Comply Due to Statute or Regulation. - - If it is impossible for You to comply with any of the terms of this - License with respect to some or all of the Covered Code due to - statute, judicial order, or regulation then You must: (a) comply with - the terms of this License to the maximum extent possible; and (b) - describe the limitations and the code they affect. Such description - must be included in the LEGAL file described in Section 3.4 and must - be included with all distributions of the Source Code. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Application of this License. - - This License applies to code to which the Initial Developer has - attached the notice in Exhibit A and to related Covered Code. - -6. Versions of the License. - - 6.1. New Versions. - Netscape Communications Corporation ("Netscape") may publish revised - and/or new versions of the License from time to time. Each version - will be given a distinguishing version number. - - 6.2. Effect of New Versions. - Once Covered Code has been published under a particular version of the - License, You may always continue to use it under the terms of that - version. You may also choose to use such Covered Code under the terms - of any subsequent version of the License published by Netscape. No one - other than Netscape has the right to modify the terms applicable to - Covered Code created under this License. - - 6.3. Derivative Works. - If You create or use a modified version of this License (which you may - only do in order to apply it to code which is not already Covered Code - governed by this License), You must (a) rename Your license so that - the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", - "MPL", "NPL" or any confusingly similar phrase do not appear in your - license (except to note that your license differs from this License) - and (b) otherwise make it clear that Your version of the license - contains terms which differ from the Mozilla Public License and - Netscape Public License. (Filling in the name of the Initial - Developer, Original Code or Contributor in the notice described in - Exhibit A shall not of themselves be deemed to be modifications of - this License.) - -7. DISCLAIMER OF WARRANTY. - - COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, - WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF - DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. - THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE - IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, - YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE - COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER - OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF - ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. - -8. TERMINATION. - - 8.1. This License and the rights granted hereunder will terminate - automatically if You fail to comply with terms herein and fail to cure - such breach within 30 days of becoming aware of the breach. All - sublicenses to the Covered Code which are properly granted shall - survive any termination of this License. Provisions which, by their - nature, must remain in effect beyond the termination of this License - shall survive. - - 8.2. If You initiate litigation by asserting a patent infringement - claim (excluding declatory judgment actions) against Initial Developer - or a Contributor (the Initial Developer or Contributor against whom - You file such action is referred to as "Participant") alleging that: - - (a) such Participant's Contributor Version directly or indirectly - infringes any patent, then any and all rights granted by such - Participant to You under Sections 2.1 and/or 2.2 of this License - shall, upon 60 days notice from Participant terminate prospectively, - unless if within 60 days after receipt of notice You either: (i) - agree in writing to pay Participant a mutually agreeable reasonable - royalty for Your past and future use of Modifications made by such - Participant, or (ii) withdraw Your litigation claim with respect to - the Contributor Version against such Participant. If within 60 days - of notice, a reasonable royalty and payment arrangement are not - mutually agreed upon in writing by the parties or the litigation claim - is not withdrawn, the rights granted by Participant to You under - Sections 2.1 and/or 2.2 automatically terminate at the expiration of - the 60 day notice period specified above. - - (b) any software, hardware, or device, other than such Participant's - Contributor Version, directly or indirectly infringes any patent, then - any rights granted to You by such Participant under Sections 2.1(b) - and 2.2(b) are revoked effective as of the date You first made, used, - sold, distributed, or had made, Modifications made by that - Participant. - - 8.3. If You assert a patent infringement claim against Participant - alleging that such Participant's Contributor Version directly or - indirectly infringes any patent where such claim is resolved (such as - by license or settlement) prior to the initiation of patent - infringement litigation, then the reasonable value of the licenses - granted by such Participant under Sections 2.1 or 2.2 shall be taken - into account in determining the amount or value of any payment or - license. - - 8.4. In the event of termination under Sections 8.1 or 8.2 above, - all end user license agreements (excluding distributors and resellers) - which have been validly granted by You or any distributor hereunder - prior to termination shall survive termination. - -9. LIMITATION OF LIABILITY. - - UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT - (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL - DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, - OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR - ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY - CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, - WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER - COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN - INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF - LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY - RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW - PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE - EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO - THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. - -10. U.S. GOVERNMENT END USERS. - - The Covered Code is a "commercial item," as that term is defined in - 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer - software" and "commercial computer software documentation," as such - terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 - C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), - all U.S. Government End Users acquire Covered Code with only those - rights set forth herein. - -11. MISCELLANEOUS. - - This License represents the complete agreement concerning subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. This License shall be governed by - California law provisions (except to the extent applicable law, if - any, provides otherwise), excluding its conflict-of-law provisions. - With respect to disputes in which at least one party is a citizen of, - or an entity chartered or registered to do business in the United - States of America, any litigation relating to this License shall be - subject to the jurisdiction of the Federal Courts of the Northern - District of California, with venue lying in Santa Clara County, - California, with the losing party responsible for costs, including - without limitation, court costs and reasonable attorneys' fees and - expenses. The application of the United Nations Convention on - Contracts for the International Sale of Goods is expressly excluded. - Any law or regulation which provides that the language of a contract - shall be construed against the drafter shall not apply to this - License. - -12. RESPONSIBILITY FOR CLAIMS. - - As between Initial Developer and the Contributors, each party is - responsible for claims and damages arising, directly or indirectly, - out of its utilization of rights under this License and You agree to - work with Initial Developer and Contributors to distribute such - responsibility on an equitable basis. Nothing herein is intended or - shall be deemed to constitute any admission of liability. - -13. MULTIPLE-LICENSED CODE. - - Initial Developer may designate portions of the Covered Code as - "Multiple-Licensed". "Multiple-Licensed" means that the Initial - Developer permits you to utilize portions of the Covered Code under - Your choice of the NPL or the alternative licenses, if any, specified - by the Initial Developer in the file described in Exhibit A. - -EXHIBIT A -Mozilla Public License. - - ``The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in - compliance with the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - License for the specific language governing rights and limitations - under the License. - - The Original Code is ______________________________________. - - The Initial Developer of the Original Code is ________________________. - Portions created by ______________________ are Copyright (C) ______ - _______________________. All Rights Reserved. - - Contributor(s): ______________________________________. - - Alternatively, the contents of this file may be used under the terms - of the _____ license (the "[___] License"), in which case the - provisions of [______] License are applicable instead of those - above. If you wish to allow use of your version of this file only - under the terms of the [____] License and not to allow others to use - your version of this file under the MPL, indicate your decision by - deleting the provisions above and replace them with the notice and - other provisions required by the [___] License. If you do not delete - the provisions above, a recipient may use your version of this file - under either the MPL or the [___] License." - - [NOTE: The text of this Exhibit A may differ slightly from the text of - the notices in the Source Code files of the Original Code. You should - use the text of this Exhibit A rather than the text found in the - Original Code Source Code for Your Modifications.] - diff --git a/gpl.txt b/gpl.txt deleted file mode 100644 index d511905..0000000 --- a/gpl.txt +++ /dev/null @@ -1,339 +0,0 @@ - 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. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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; either version 2 of the License, or - (at your option) any later version. - - 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. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/jss-add-TLS-SHA384-ciphers.patch b/jss-add-TLS-SHA384-ciphers.patch new file mode 100644 index 0000000..f67601f --- /dev/null +++ b/jss-add-TLS-SHA384-ciphers.patch @@ -0,0 +1,167 @@ +From 82f4b9a032f942fdc005e12a408c8e87c9ea0f36 Mon Sep 17 00:00:00 2001 +From: Christina Fu +Date: Thu, 28 Jun 2018 17:42:36 -0700 +Subject: [PATCH] Ticket #4 Add support for TLS_*_SHA384 ciphers + +This patch adds support for TLS_*_SHA384 ciphers. + +Fixes https://pagure.io/jss/issue/4 +--- + org/mozilla/jss/ssl/SSLCipher.java | 7 +++++ + org/mozilla/jss/ssl/SSLSocket.java | 7 +++++ + org/mozilla/jss/tests/Constants.java | 11 ++++++-- + org/mozilla/jss/tests/SSLClientAuth.java | 45 ++++++++++++++++++++++++++++++++ + 4 files changed, 68 insertions(+), 2 deletions(-) + +diff --git a/org/mozilla/jss/ssl/SSLCipher.java b/org/mozilla/jss/ssl/SSLCipher.java +index 30acdd7..278126b 100644 +--- a/jss/org/mozilla/jss/ssl/SSLCipher.java ++++ b/jss/org/mozilla/jss/ssl/SSLCipher.java +@@ -258,8 +258,11 @@ public enum SSLCipher { + TLS_RSA_WITH_SEED_CBC_SHA (0x0096), + + TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009C), ++ TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009D), + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009E), ++ TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009F), + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 (0x00A2), ++ TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 (0x00A3), + + TLS_ECDH_ECDSA_WITH_NULL_SHA (0xc001, true), + TLS_ECDH_ECDSA_WITH_RC4_128_SHA (0xc002, true), +@@ -292,11 +295,15 @@ public enum SSLCipher { + TLS_ECDH_anon_WITH_AES_256_CBC_SHA (0xc019, true), + + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023, true), ++ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024, true), + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027, true), ++ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028, true), + + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02B, true), ++ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02C, true), + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02D, true), + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02F, true), ++ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030, true), + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 (0xc031, true); + + private int id; +diff --git a/org/mozilla/jss/ssl/SSLSocket.java b/org/mozilla/jss/ssl/SSLSocket.java +index 0dd39fd..e104d3c 100644 +--- a/jss/org/mozilla/jss/ssl/SSLSocket.java ++++ b/jss/org/mozilla/jss/ssl/SSLSocket.java +@@ -268,8 +268,11 @@ public class SSLSocket extends java.net.Socket { + public final static int TLS_RSA_WITH_SEED_CBC_SHA = 0x0096; + + public final static int TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C; ++ public final static int TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D; + public final static int TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E; ++ public final static int TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F; + public final static int TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2; ++ public final static int TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3; + + public final static int TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xc001; + public final static int TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xc002; +@@ -302,11 +305,15 @@ public class SSLSocket extends java.net.Socket { + public final static int TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xc019; + + public final static int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xc023; ++ public final static int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xc024; + public final static int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xc027; ++ public final static int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xc028; + + public final static int TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xc02B; ++ public final static int TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xc02C; + public final static int TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xc02D; + public final static int TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xc02F; ++ public final static int TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xc030; + public final static int TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xc031; + + /* +diff --git a/org/mozilla/jss/tests/Constants.java b/org/mozilla/jss/tests/Constants.java +index e613034..d79ad72 100755 +--- a/jss/org/mozilla/jss/tests/Constants.java ++++ b/jss/org/mozilla/jss/tests/Constants.java +@@ -142,8 +142,15 @@ public interface Constants { + /*77*/ new cipher(SSLSocket.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"), + /*78*/ new cipher(SSLSocket.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), + /*79*/ new cipher(SSLSocket.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"), +-/*78*/ new cipher(SSLSocket.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"), +-/*80*/ new cipher(SSLSocket.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256") ++/*80*/ new cipher(SSLSocket.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"), ++/*81*/ new cipher(SSLSocket.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"), ++/*82*/ new cipher(SSLSocket.TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384"), ++/*83*/ new cipher(SSLSocket.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"), ++/*84*/ new cipher(SSLSocket.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"), ++/*85*/ new cipher(SSLSocket.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"), ++/*86*/ new cipher(SSLSocket.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"), ++/*87*/ new cipher(SSLSocket.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"), ++/*88*/ new cipher(SSLSocket.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384") + }; + + /** Cipher supported by JSSE (JDK 1.5.x) */ +diff --git a/org/mozilla/jss/tests/SSLClientAuth.java b/org/mozilla/jss/tests/SSLClientAuth.java +index e1c6163..b656b82 100644 +--- a/jss/org/mozilla/jss/tests/SSLClientAuth.java ++++ b/jss/org/mozilla/jss/tests/SSLClientAuth.java +@@ -148,6 +148,8 @@ public class SSLClientAuth implements Runnable { + + } + configureDefaultSSLoptions(); ++ ++ testSpecificCiphers(); + + useNickname = false; + testConnection(); +@@ -265,6 +267,49 @@ public class SSLClientAuth implements Runnable { + System.exit(1); + } + } ++ ++ // test one or more specific ciphers ++ // -- normally for newly added ciphers ++ private void testSpecificCiphers() { ++ try { ++ //Disable SSL2 and SSL3 ciphers ++ SSLSocket.enableSSL2Default(false); ++ SSLSocket.enableSSL3Default(false); ++ /* TLS is enabled by default */ ++ ++ /* Enable Session tickets by default */ ++ SSLSocket.enableSessionTicketsDefault(true); ++ ++ /* ++ * when testing specific ciphers: ++ * 1. flip this to true ++ * 2. change the ciphers comparison (the code below was from ++ * the latest test ++ */ ++ if (false) { ++ System.out.println("testing new TLS_*SHA384 ciphers"); ++ System.out.println("Enable ony two new ciphers."); ++ int ciphers[] = ++ org.mozilla.jss.ssl.SSLSocket.getImplementedCipherSuites(); ++ for (int i = 0; i < ciphers.length; ++i) { ++ if (ciphers[i] == 157 || ciphers[i] == 159) { ++ System.out.println("enabling cipher: " + ciphers[i]); ++ /* enable a couple SHA384 ciphers */ ++ SSLSocket.setCipherPreferenceDefault(ciphers[i], true); ++ } else { ++ System.out.println("disabling cipher: " + ciphers[i]); ++ /* disable the non SHA384 ciphers */ ++ SSLSocket.setCipherPreferenceDefault(ciphers[i], false); ++ } ++ } ++ } ++ ++ } catch (SocketException ex) { ++ System.out.println("Error configuring ciphers."); ++ ex.printStackTrace(); ++ System.exit(1); ++ } ++ } + + private void testConnection() throws Exception { + serverReady = false; +-- +1.8.3.1 + diff --git a/jss-add-build-sh.patch b/jss-add-build-sh.patch new file mode 100644 index 0000000..91feff3 --- /dev/null +++ b/jss-add-build-sh.patch @@ -0,0 +1,386 @@ +From f2357a2b5721cddb0a8924be76d0cdc9412d59dc Mon Sep 17 00:00:00 2001 +From: "Endi S. Dewata" +Date: Thu, 21 Jun 2018 21:41:21 -0600 +Subject: [PATCH] Added build.sh + +A new build script has been added to simplify the build process. +--- + build.sh | 366 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 366 insertions(+) + create mode 100755 build.sh + +diff --git a/build.sh b/build.sh +new file mode 100755 +index 00000000..ebd46e97 +--- /dev/null ++++ b/build.sh +@@ -0,0 +1,366 @@ ++#!/bin/bash -e ++ ++# BEGIN COPYRIGHT BLOCK ++# (C) 2018 Red Hat, Inc. ++# All rights reserved. ++# END COPYRIGHT BLOCK ++ ++NAME=jss ++ ++SCRIPT_PATH=`readlink -f "$0"` ++SCRIPT_NAME=`basename "$SCRIPT_PATH"` ++ ++SRC_DIR=`dirname "$SCRIPT_PATH"` ++WORK_DIR="$HOME/build/$NAME" ++ ++SOURCE_TAG= ++ ++WITH_TIMESTAMP= ++WITH_COMMIT_ID= ++DIST= ++ ++VERBOSE= ++DEBUG= ++ ++usage() { ++ echo "Usage: $SCRIPT_NAME [OPTIONS] " ++ echo ++ echo "Options:" ++ echo " --work-dir= Working directory (default: $WORK_DIR)." ++ echo " --source-tag= Generate RPM sources from a source tag." ++ echo " --with-timestamp Append timestamp to release number." ++ echo " --with-commit-id Append commit ID to release number." ++ echo " --dist= Distribution name (e.g. fc28)." ++ echo " -v,--verbose Run in verbose mode." ++ echo " --debug Run in debug mode." ++ echo " --help Show help message." ++ echo ++ echo "Target:" ++ echo " src Generate RPM sources." ++ echo " spec Generate RPM spec." ++ echo " srpm Build SRPM package." ++ echo " rpm Build RPM packages." ++} ++ ++generate_rpm_sources() { ++ ++ TARBALL="$NAME-$VERSION.tar.gz" ++ ++ if [ "$SOURCE_TAG" != "" ] ; then ++ ++ if [ "$VERBOSE" = true ] ; then ++ echo "Generating $TARBALL from $SOURCE_TAG tag" ++ fi ++ ++ git -C "$SRC_DIR" \ ++ archive \ ++ --format=tar.gz \ ++ --prefix $NAME-$VERSION/$NAME/ \ ++ -o "$WORK_DIR/SOURCES/$TARBALL" \ ++ $SOURCE_TAG ++ ++ if [ "$SOURCE_TAG" != "HEAD" ] ; then ++ ++ TAG_ID=`git -C "$SRC_DIR" rev-parse $SOURCE_TAG` ++ HEAD_ID=`git -C "$SRC_DIR" rev-parse HEAD` ++ ++ if [ "$TAG_ID" != "$HEAD_ID" ] ; then ++ generate_patch ++ fi ++ fi ++ ++ return ++ fi ++ ++ if [ "$VERBOSE" = true ] ; then ++ echo "Generating $TARBALL" ++ fi ++ ++ tar czf "$WORK_DIR/SOURCES/$TARBALL" \ ++ --transform "s,^./,$NAME-$VERSION/$NAME/," \ ++ --exclude .git \ ++ --exclude bin \ ++ -C "$SRC_DIR" \ ++ . ++} ++ ++generate_patch() { ++ ++ PATCH="$NAME-$VERSION-$RELEASE.patch" ++ ++ if [ "$VERBOSE" = true ] ; then ++ echo "Generating $PATCH for all changes since $SOURCE_TAG tag" ++ fi ++ ++ git -C "$SRC_DIR" \ ++ format-patch \ ++ --stdout \ ++ $SOURCE_TAG \ ++ > "$WORK_DIR/SOURCES/$PATCH" ++} ++ ++generate_rpm_spec() { ++ ++ RPM_SPEC="$NAME.spec" ++ ++ if [ "$VERBOSE" = true ] ; then ++ echo "Generating $RPM_SPEC" ++ fi ++ ++ # hard-code timestamp ++ commands="s/%{?_timestamp}/${_TIMESTAMP}/g" ++ ++ # hard-code commit ID ++ commands="${commands}; s/%{?_commit_id}/${_COMMIT_ID}/g" ++ ++ # hard-code patch ++ if [ "$PATCH" != "" ] ; then ++ commands="${commands}; s/# Patch: jss-VERSION-RELEASE.patch/Patch: $PATCH/g" ++ fi ++ ++ sed "$commands" "$SPEC_TEMPLATE" > "$WORK_DIR/SPECS/$RPM_SPEC" ++ ++ # rpmlint "$WORK_DIR/SPECS/$RPM_SPEC" ++} ++ ++while getopts v-: arg ; do ++ case $arg in ++ v) ++ VERBOSE=true ++ ;; ++ -) ++ LONG_OPTARG="${OPTARG#*=}" ++ ++ case $OPTARG in ++ work-dir=?*) ++ WORK_DIR=`readlink -f "$LONG_OPTARG"` ++ ;; ++ source-tag=?*) ++ SOURCE_TAG="$LONG_OPTARG" ++ ;; ++ with-timestamp) ++ WITH_TIMESTAMP=true ++ ;; ++ with-commit-id) ++ WITH_COMMIT_ID=true ++ ;; ++ dist=?*) ++ DIST="$LONG_OPTARG" ++ ;; ++ verbose) ++ VERBOSE=true ++ ;; ++ debug) ++ VERBOSE=true ++ DEBUG=true ++ ;; ++ help) ++ usage ++ exit ++ ;; ++ '') ++ break # "--" terminates argument processing ++ ;; ++ work-dir* | source-tag* | dist*) ++ echo "ERROR: Missing argument for --$OPTARG option" >&2 ++ exit 1 ++ ;; ++ *) ++ echo "ERROR: Illegal option --$OPTARG" >&2 ++ exit 1 ++ ;; ++ esac ++ ;; ++ \?) ++ exit 1 # getopts already reported the illegal option ++ ;; ++ esac ++done ++ ++# remove parsed options and args from $@ list ++shift $((OPTIND-1)) ++ ++if [ "$#" -lt 1 ] ; then ++ echo "ERROR: Missing build target" >&2 ++ usage ++ exit 1 ++fi ++ ++BUILD_TARGET=$1 ++ ++if [ "$DEBUG" = true ] ; then ++ echo "WORK_DIR: $WORK_DIR" ++ echo "BUILD_TARGET: $BUILD_TARGET" ++fi ++ ++if [ "$BUILD_TARGET" != "src" ] && ++ [ "$BUILD_TARGET" != "spec" ] && ++ [ "$BUILD_TARGET" != "srpm" ] && ++ [ "$BUILD_TARGET" != "rpm" ] ; then ++ echo "ERROR: Invalid build target: $BUILD_TARGET" >&2 ++ exit 1 ++fi ++ ++SPEC_TEMPLATE="$SRC_DIR/$NAME.spec.in" ++VERSION="`rpmspec -P "$SPEC_TEMPLATE" | grep "^Version:" | awk '{print $2;}'`" ++ ++if [ "$DEBUG" = true ] ; then ++ echo "VERSION: $VERSION" ++fi ++ ++RELEASE="`rpmspec -P "$SPEC_TEMPLATE" --undefine dist | grep "^Release:" | awk '{print $2;}'`" ++ ++if [ "$DEBUG" = true ] ; then ++ echo "RELEASE: $RELEASE" ++fi ++ ++if [ "$WITH_TIMESTAMP" = true ] ; then ++ TIMESTAMP="`date +"%Y%m%d%H%M%S"`" ++ _TIMESTAMP=".$TIMESTAMP" ++fi ++ ++if [ "$DEBUG" = true ] ; then ++ echo "TIMESTAMP: $TIMESTAMP" ++fi ++ ++if [ "$WITH_COMMIT_ID" = true ]; then ++ COMMIT_ID="`git -C "$SRC_DIR" rev-parse --short=8 HEAD`" ++ _COMMIT_ID=".$COMMIT_ID" ++fi ++ ++if [ "$DEBUG" = true ] ; then ++ echo "COMMIT_ID: $COMMIT_ID" ++fi ++ ++echo "Building $NAME-$VERSION-$RELEASE${_TIMESTAMP}${_COMMIT_ID}" ++ ++################################################################################ ++# Initialize working directory ++################################################################################ ++ ++if [ "$VERBOSE" = true ] ; then ++ echo "Initializing $WORK_DIR" ++fi ++ ++mkdir -p $WORK_DIR ++cd $WORK_DIR ++ ++rm -rf BUILD ++rm -rf RPMS ++rm -rf SOURCES ++rm -rf SPECS ++rm -rf SRPMS ++ ++mkdir BUILD ++mkdir RPMS ++mkdir SOURCES ++mkdir SPECS ++mkdir SRPMS ++ ++################################################################################ ++# Generate RPM sources ++################################################################################ ++ ++generate_rpm_sources ++ ++echo "RPM sources:" ++find "$WORK_DIR/SOURCES" -type f -printf " %p\n" ++ ++if [ "$BUILD_TARGET" = "src" ] ; then ++ exit ++fi ++ ++################################################################################ ++# Generate RPM spec ++################################################################################ ++ ++generate_rpm_spec ++ ++echo "RPM spec:" ++find "$WORK_DIR/SPECS" -type f -printf " %p\n" ++ ++if [ "$BUILD_TARGET" = "spec" ] ; then ++ exit ++fi ++ ++################################################################################ ++# Build source package ++################################################################################ ++ ++OPTIONS=() ++ ++OPTIONS+=(--quiet) ++OPTIONS+=(--define "_topdir ${WORK_DIR}") ++ ++if [ "$WITH_TIMESTAMP" = true ] ; then ++ OPTIONS+=(--define "_timestamp ${_TIMESTAMP}") ++fi ++ ++if [ "$WITH_COMMIT_ID" = true ] ; then ++ OPTIONS+=(--define "_commit_id ${_COMMIT_ID}") ++fi ++ ++if [ "$DIST" != "" ] ; then ++ OPTIONS+=(--define "dist .$DIST") ++fi ++ ++if [ "$DEBUG" = true ] ; then ++ echo "rpmbuild -bs ${OPTIONS[@]} $WORK_DIR/SPECS/$RPM_SPEC" ++fi ++ ++# build SRPM with user-provided options ++rpmbuild -bs "${OPTIONS[@]}" "$WORK_DIR/SPECS/$RPM_SPEC" ++ ++rc=$? ++ ++if [ $rc != 0 ]; then ++ echo "ERROR: Unable to build SRPM package" ++ exit 1 ++fi ++ ++SRPM=`find "$WORK_DIR/SRPMS" -type f` ++ ++echo "SRPM package:" ++echo " $SRPM" ++ ++if [ "$BUILD_TARGET" = "srpm" ] ; then ++ exit ++fi ++ ++################################################################################ ++# Build binary packages ++################################################################################ ++ ++OPTIONS=() ++ ++if [ "$VERBOSE" = true ] ; then ++ OPTIONS+=(--define "_verbose 1") ++fi ++ ++OPTIONS+=(--define "_topdir ${WORK_DIR}") ++ ++if [ "$DEBUG" = true ] ; then ++ echo "rpmbuild --rebuild ${OPTIONS[@]} $SRPM" ++fi ++ ++# rebuild RPM with hard-coded options in SRPM ++rpmbuild --rebuild "${OPTIONS[@]}" "$SRPM" ++ ++rc=$? ++ ++if [ $rc != 0 ]; then ++ echo "ERROR: Unable to build RPM packages" ++ exit 1 ++fi ++ ++# install SRPM to restore sources and spec file removed during rebuild ++rpm -i --define "_topdir $WORK_DIR" "$SRPM" ++ ++# flatten folder ++find "$WORK_DIR/RPMS" -mindepth 2 -type f -exec mv -i '{}' "$WORK_DIR/RPMS" ';' ++ ++# remove empty subfolders ++find "$WORK_DIR/RPMS" -mindepth 1 -type d -delete ++ ++echo "RPM packages:" ++find "$WORK_DIR/RPMS" -type f -printf " %p\n" +-- +2.14.4 + diff --git a/jss-add-travis-yml.patch b/jss-add-travis-yml.patch new file mode 100644 index 0000000..c4e3673 --- /dev/null +++ b/jss-add-travis-yml.patch @@ -0,0 +1,37 @@ +commit f6df4da0a66c9fdad6c1b794190431c265931a16 +Author: Endi S. Dewata +Date: Thu May 31 04:34:48 2018 +0200 + + Added .travis.yml + + A Travis CI configuration has been added to build JSS and run a + basic installation test. Additional tests will be added later. + +diff --git a/.travis.yml b/.travis.yml +new file mode 100644 +index 00000000..ae674cc5 +--- /dev/null ++++ b/.travis.yml +@@ -0,0 +1,22 @@ ++# BEGIN COPYRIGHT BLOCK ++# (C) 2018 Red Hat, Inc. ++# All rights reserved. ++# END COPYRIGHT BLOCK ++ ++services: ++ - docker ++ ++install: ++ - docker pull registry.fedoraproject.org/fedora:28 ++ - docker run ++ --name=container ++ --detach ++ -i ++ -v $(pwd):/root/jss ++ registry.fedoraproject.org/fedora:28 ++ - docker exec container dnf install -y dnf-plugins-core gcc make rpm-build ++ - docker exec container dnf builddep -y --spec /root/jss/jss.spec.in ++ - docker exec container /root/jss/build.sh --with-timestamp --with-commit-id rpm ++ ++script: ++ - docker exec container rpm -Uvh /root/build/jss/RPMS/* diff --git a/jss-add-x509-cert-and-crl-migration.patch b/jss-add-x509-cert-and-crl-migration.patch new file mode 100644 index 0000000..7399d2e --- /dev/null +++ b/jss-add-x509-cert-and-crl-migration.patch @@ -0,0 +1,47537 @@ +From 06eacad918e745d632067deea398f14ce9da29ac Mon Sep 17 00:00:00 2001 +From: Jack Magne +Date: Fri, 15 Jun 2018 14:53:53 -0700 +Subject: [PATCH] Address Bugzilla: Bug 1560682 - (RFE) Migrate RHCS x509 cert + and crl functionality to JSS. + + This consists of a migration of low level X509 cert and crl related classes from dogtag into JSS. + This initial migration will allow users of jss to utilize these classes to create certs and crls. + + The initial goal is to simply provide the classes from dogtag to be used in JSS. + A later goal will be to refactor dogtag to use the classes moved to JSS, but that will be for + a future ticket. + + This migration will also address this issue: + + Bug 1577991 - org.mozilla.jss.netscape.security.util.ObjectIdentifier cannot parse OID arcs larger than Integer.MAX_VALUE. + + The file ObjectIdentifier.java has been modified to use BigInt instead of the int type, allowing for a greater range of values. + Fixed minor classpath issue. +--- + org/mozilla/jss/netscape/Makefile | 45 + + org/mozilla/jss/netscape/manifest.mn | 14 + + org/mozilla/jss/netscape/security/Makefile | 45 + + .../jss/netscape/security/acl/AclEntryImpl.java | 182 +++ + org/mozilla/jss/netscape/security/acl/AclImpl.java | 393 +++++ + .../netscape/security/acl/AllPermissionsImpl.java | 43 + + .../jss/netscape/security/acl/GroupImpl.java | 173 +++ + org/mozilla/jss/netscape/security/acl/Makefile | 45 + + .../jss/netscape/security/acl/OwnerImpl.java | 105 ++ + .../jss/netscape/security/acl/PermissionImpl.java | 73 + + .../jss/netscape/security/acl/PrincipalImpl.java | 77 + + .../jss/netscape/security/acl/WorldGroupImpl.java | 42 + + org/mozilla/jss/netscape/security/acl/manifest.mn | 8 + + .../security/extensions/AccessDescription.java | 77 + + .../extensions/AuthInfoAccessExtension.java | 275 ++++ + .../jss/netscape/security/extensions/CertInfo.java | 119 ++ + .../CertificateRenewalWindowExtension.java | 191 +++ + .../security/extensions/CertificateScopeEntry.java | 103 ++ + .../extensions/CertificateScopeOfUseExtension.java | 198 +++ + .../extensions/ExtendedKeyUsageExtension.java | 228 +++ + .../security/extensions/GenericASN1Extension.java | 461 ++++++ + .../extensions/InhibitAnyPolicyExtension.java | 183 +++ + .../netscape/security/extensions/KerberosName.java | 136 ++ + .../jss/netscape/security/extensions/Makefile | 45 + + .../security/extensions/NSCertTypeExtension.java | 382 +++++ + .../security/extensions/OCSPNoCheckExtension.java | 155 ++ + .../extensions/PresenceServerExtension.java | 341 +++++ + .../extensions/SubjectInfoAccessExtension.java | 255 ++++ + .../jss/netscape/security/extensions/manifest.mn | 8 + + org/mozilla/jss/netscape/security/manifest.mn | 12 + + .../jss/netscape/security/pkcs/ContentInfo.java | 155 ++ + .../netscape/security/pkcs/EncodingException.java | 33 + + org/mozilla/jss/netscape/security/pkcs/Makefile | 45 + + org/mozilla/jss/netscape/security/pkcs/PKCS10.java | 368 +++++ + .../netscape/security/pkcs/PKCS10Attribute.java | 235 +++ + .../netscape/security/pkcs/PKCS10Attributes.java | 177 +++ + org/mozilla/jss/netscape/security/pkcs/PKCS12.java | 209 +++ + .../jss/netscape/security/pkcs/PKCS12CertInfo.java | 65 + + .../jss/netscape/security/pkcs/PKCS12KeyInfo.java | 84 ++ + .../jss/netscape/security/pkcs/PKCS12Util.java | 667 ++++++++ + org/mozilla/jss/netscape/security/pkcs/PKCS7.java | 518 +++++++ + .../jss/netscape/security/pkcs/PKCS8Key.java | 430 ++++++ + .../jss/netscape/security/pkcs/PKCS9Attribute.java | 1119 ++++++++++++++ + .../netscape/security/pkcs/PKCS9Attributes.java | 313 ++++ + .../netscape/security/pkcs/ParsingException.java | 35 + + .../jss/netscape/security/pkcs/SignerInfo.java | 348 +++++ + org/mozilla/jss/netscape/security/pkcs/manifest.mn | 8 + + .../jss/netscape/security/provider/CMS.java | 52 + + .../jss/netscape/security/provider/DSA.java | 661 ++++++++ + .../netscape/security/provider/DSAKeyFactory.java | 232 +++ + .../security/provider/DSAKeyPairGenerator.java | 389 +++++ + .../security/provider/DSAParameterGenerator.java | 298 ++++ + .../netscape/security/provider/DSAParameters.java | 131 ++ + .../netscape/security/provider/DSAPrivateKey.java | 170 +++ + .../netscape/security/provider/DSAPublicKey.java | 159 ++ + .../jss/netscape/security/provider/MD5.java | 378 +++++ + .../jss/netscape/security/provider/Makefile | 45 + + .../netscape/security/provider/RSAPublicKey.java | 193 +++ + .../jss/netscape/security/provider/SHA.java | 349 +++++ + .../jss/netscape/security/provider/Sun.java | 135 ++ + .../security/provider/X509CertificateFactory.java | 61 + + .../jss/netscape/security/provider/manifest.mn | 8 + + .../netscape/security/util/ASN1CharStrConvMap.java | 168 +++ + .../security/util/ASN1CharsetProvider.java | 30 + + org/mozilla/jss/netscape/security/util/BigInt.java | 221 +++ + .../jss/netscape/security/util/BitArray.java | 257 ++++ + .../netscape/security/util/ByteArrayLexOrder.java | 60 + + .../netscape/security/util/ByteArrayTagOrder.java | 46 + + org/mozilla/jss/netscape/security/util/Cert.java | 201 +++ + .../netscape/security/util/CertPrettyPrint.java | 346 +++++ + .../jss/netscape/security/util/CrlPrettyPrint.java | 271 ++++ + .../jss/netscape/security/util/DerEncoder.java | 40 + + .../jss/netscape/security/util/DerInputBuffer.java | 186 +++ + .../jss/netscape/security/util/DerInputStream.java | 667 ++++++++ + .../netscape/security/util/DerOutputStream.java | 729 +++++++++ + .../jss/netscape/security/util/DerValue.java | 748 +++++++++ + .../jss/netscape/security/util/ExtPrettyPrint.java | 1586 ++++++++++++++++++++ + .../jss/netscape/security/util/IA5Charset.java | 24 + + .../netscape/security/util/IA5CharsetDecoder.java | 62 + + .../netscape/security/util/IA5CharsetEncoder.java | 69 + + org/mozilla/jss/netscape/security/util/Makefile | 45 + + .../netscape/security/util/ObjectIdentifier.java | 533 +++++++ + .../netscape/security/util/PrettyPrintFormat.java | 164 ++ + .../security/util/PrettyPrintResources.java | 301 ++++ + .../netscape/security/util/PrintableCharset.java | 46 + + .../security/util/PrintableCharsetDecoder.java | 69 + + .../security/util/PrintableCharsetEncoder.java | 71 + + .../netscape/security/util/PubKeyPrettyPrint.java | 123 ++ + .../netscape/security/util/UniversalCharset.java | 24 + + .../security/util/UniversalCharsetDecoder.java | 98 ++ + .../security/util/UniversalCharsetEncoder.java | 68 + + org/mozilla/jss/netscape/security/util/Utils.java | 356 +++++ + org/mozilla/jss/netscape/security/util/manifest.mn | 9 + + .../jss/netscape/security/x509/ACertAttrSet.java | 141 ++ + org/mozilla/jss/netscape/security/x509/AVA.java | 311 ++++ + .../netscape/security/x509/AVAValueConverter.java | 86 ++ + .../jss/netscape/security/x509/AlgIdDSA.java | 215 +++ + .../jss/netscape/security/x509/AlgorithmId.java | 805 ++++++++++ + .../jss/netscape/security/x509/Attribute.java | 327 ++++ + .../x509/AuthorityKeyIdentifierExtension.java | 342 +++++ + .../security/x509/BasicConstraintsExtension.java | 296 ++++ + org/mozilla/jss/netscape/security/x509/CPSuri.java | 66 + + .../security/x509/CRLDistributionPoint.java | 478 ++++++ + .../x509/CRLDistributionPointsExtension.java | 390 +++++ + .../jss/netscape/security/x509/CRLExtensions.java | 254 ++++ + .../netscape/security/x509/CRLNumberExtension.java | 227 +++ + .../netscape/security/x509/CRLReasonExtension.java | 243 +++ + .../jss/netscape/security/x509/CertAndKeyGen.java | 290 ++++ + .../jss/netscape/security/x509/CertAttrSet.java | 120 ++ + .../jss/netscape/security/x509/CertException.java | 165 ++ + .../jss/netscape/security/x509/CertParseError.java | 40 + + .../security/x509/CertificateAlgorithmId.java | 187 +++ + .../netscape/security/x509/CertificateChain.java | 139 ++ + .../security/x509/CertificateExtensions.java | 303 ++++ + .../security/x509/CertificateIssuerExtension.java | 241 +++ + .../security/x509/CertificateIssuerName.java | 172 +++ + .../x509/CertificateIssuerUniqueIdentity.java | 185 +++ + .../x509/CertificatePoliciesExtension.java | 339 +++++ + .../security/x509/CertificatePolicyId.java | 85 ++ + .../security/x509/CertificatePolicyInfo.java | 110 ++ + .../security/x509/CertificatePolicyMap.java | 100 ++ + .../security/x509/CertificatePolicySet.java | 86 ++ + .../security/x509/CertificateSerialNumber.java | 191 +++ + .../security/x509/CertificateSubjectName.java | 211 +++ + .../x509/CertificateSubjectUniqueIdentity.java | 185 +++ + .../security/x509/CertificateValidity.java | 301 ++++ + .../netscape/security/x509/CertificateVersion.java | 248 +++ + .../netscape/security/x509/CertificateX509Key.java | 188 +++ + .../jss/netscape/security/x509/DNSName.java | 90 ++ + .../security/x509/DeltaCRLIndicatorExtension.java | 240 +++ + .../netscape/security/x509/DirStrConverter.java | 172 +++ + .../jss/netscape/security/x509/DisplayText.java | 85 ++ + .../jss/netscape/security/x509/EDIPartyName.java | 157 ++ + .../jss/netscape/security/x509/Extension.java | 195 +++ + .../jss/netscape/security/x509/Extensions.java | 252 ++++ + .../security/x509/FreshestCRLExtension.java | 403 +++++ + .../jss/netscape/security/x509/GeneralName.java | 214 +++ + .../security/x509/GeneralNameInterface.java | 60 + + .../jss/netscape/security/x509/GeneralNames.java | 150 ++ + .../security/x509/GeneralNamesException.java | 50 + + .../jss/netscape/security/x509/GeneralSubtree.java | 162 ++ + .../netscape/security/x509/GeneralSubtrees.java | 109 ++ + .../security/x509/GenericValueConverter.java | 143 ++ + .../security/x509/HoldInstructionExtension.java | 356 +++++ + .../netscape/security/x509/IA5StringConverter.java | 123 ++ + .../jss/netscape/security/x509/IPAddressName.java | 277 ++++ + .../security/x509/InvalidIPAddressException.java | 33 + + .../security/x509/InvalidityDateExtension.java | 242 +++ + .../x509/IssuerAlternativeNameExtension.java | 239 +++ + .../security/x509/IssuingDistributionPoint.java | 323 ++++ + .../x509/IssuingDistributionPointExtension.java | 425 ++++++ + .../jss/netscape/security/x509/KeyIdentifier.java | 87 ++ + .../netscape/security/x509/KeyUsageExtension.java | 420 ++++++ + .../netscape/security/x509/LdapDNStrConverter.java | 144 ++ + .../security/x509/LdapV3DNStrConverter.java | 824 ++++++++++ + org/mozilla/jss/netscape/security/x509/Makefile | 45 + + .../security/x509/NSCCommentExtension.java | 229 +++ + .../security/x509/NameConstraintsExtension.java | 315 ++++ + .../netscape/security/x509/NoticeReference.java | 96 ++ + org/mozilla/jss/netscape/security/x509/OIDMap.java | 337 +++++ + .../jss/netscape/security/x509/OIDName.java | 90 ++ + .../jss/netscape/security/x509/OtherName.java | 214 +++ + .../jss/netscape/security/x509/PKIXExtensions.java | 185 +++ + .../netscape/security/x509/PolicyConstraint.java | 136 ++ + .../security/x509/PolicyConstraintsExtension.java | 307 ++++ + .../security/x509/PolicyMappingsExtension.java | 259 ++++ + .../security/x509/PolicyQualifierInfo.java | 118 ++ + .../netscape/security/x509/PolicyQualifiers.java | 107 ++ + .../netscape/security/x509/PrintableConverter.java | 114 ++ + .../security/x509/PrivateKeyUsageExtension.java | 340 +++++ + .../jss/netscape/security/x509/Qualifier.java | 63 + + org/mozilla/jss/netscape/security/x509/RDN.java | 304 ++++ + .../security/x509/RFC1779StrConverter.java | 102 ++ + .../jss/netscape/security/x509/RFC822Name.java | 85 ++ + .../jss/netscape/security/x509/ReasonFlags.java | 283 ++++ + .../netscape/security/x509/RevocationReason.java | 129 ++ + .../security/x509/RevocationReasonAdapter.java | 38 + + .../netscape/security/x509/RevokedCertImpl.java | 447 ++++++ + .../netscape/security/x509/RevokedCertificate.java | 95 ++ + .../jss/netscape/security/x509/SerialNumber.java | 127 ++ + .../x509/SubjectAlternativeNameExtension.java | 257 ++++ + .../x509/SubjectDirAttributesExtension.java | 289 ++++ + .../x509/SubjectKeyIdentifierExtension.java | 222 +++ + .../jss/netscape/security/x509/URIName.java | 85 ++ + .../jss/netscape/security/x509/UniqueIdentity.java | 112 ++ + .../jss/netscape/security/x509/UserNotice.java | 96 ++ + .../jss/netscape/security/x509/X500Name.java | 723 +++++++++ + .../netscape/security/x509/X500NameAttrMap.java | 376 +++++ + .../jss/netscape/security/x509/X500Signer.java | 110 ++ + .../netscape/security/x509/X509AttributeName.java | 64 + + .../jss/netscape/security/x509/X509CRLImpl.java | 1078 +++++++++++++ + .../jss/netscape/security/x509/X509Cert.java | 846 +++++++++++ + .../jss/netscape/security/x509/X509CertImpl.java | 1239 +++++++++++++++ + .../jss/netscape/security/x509/X509CertInfo.java | 976 ++++++++++++ + .../security/x509/X509ExtensionException.java | 54 + + .../jss/netscape/security/x509/X509Key.java | 501 +++++++ + org/mozilla/jss/netscape/security/x509/manifest.mn | 10 + + org/mozilla/jss/netscape/security/x509/rules.mk | 18 + + org/mozilla/jss/tests/X509CertTest.java | 155 ++ + 199 files changed, 45920 insertions(+), 0 deletion(-) + create mode 100644 org/mozilla/jss/netscape/Makefile + create mode 100644 org/mozilla/jss/netscape/manifest.mn + create mode 100644 org/mozilla/jss/netscape/security/Makefile + create mode 100644 org/mozilla/jss/netscape/security/acl/AclEntryImpl.java + create mode 100644 org/mozilla/jss/netscape/security/acl/AclImpl.java + create mode 100644 org/mozilla/jss/netscape/security/acl/AllPermissionsImpl.java + create mode 100644 org/mozilla/jss/netscape/security/acl/GroupImpl.java + create mode 100644 org/mozilla/jss/netscape/security/acl/Makefile + create mode 100644 org/mozilla/jss/netscape/security/acl/OwnerImpl.java + create mode 100644 org/mozilla/jss/netscape/security/acl/PermissionImpl.java + create mode 100644 org/mozilla/jss/netscape/security/acl/PrincipalImpl.java + create mode 100644 org/mozilla/jss/netscape/security/acl/WorldGroupImpl.java + create mode 100644 org/mozilla/jss/netscape/security/acl/manifest.mn + create mode 100644 org/mozilla/jss/netscape/security/extensions/AccessDescription.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/AuthInfoAccessExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/CertInfo.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/CertificateRenewalWindowExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/CertificateScopeEntry.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/CertificateScopeOfUseExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/ExtendedKeyUsageExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/GenericASN1Extension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/InhibitAnyPolicyExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/KerberosName.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/Makefile + create mode 100644 org/mozilla/jss/netscape/security/extensions/NSCertTypeExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/OCSPNoCheckExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/PresenceServerExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/SubjectInfoAccessExtension.java + create mode 100644 org/mozilla/jss/netscape/security/extensions/manifest.mn + create mode 100644 org/mozilla/jss/netscape/security/manifest.mn + create mode 100644 org/mozilla/jss/netscape/security/pkcs/ContentInfo.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/EncodingException.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/Makefile + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS10.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS10Attribute.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS10Attributes.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS12.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS12CertInfo.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS12KeyInfo.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS12Util.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS7.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS8Key.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS9Attribute.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/PKCS9Attributes.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/ParsingException.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/SignerInfo.java + create mode 100644 org/mozilla/jss/netscape/security/pkcs/manifest.mn + create mode 100644 org/mozilla/jss/netscape/security/provider/CMS.java + create mode 100644 org/mozilla/jss/netscape/security/provider/DSA.java + create mode 100755 org/mozilla/jss/netscape/security/provider/DSAKeyFactory.java + create mode 100644 org/mozilla/jss/netscape/security/provider/DSAKeyPairGenerator.java + create mode 100755 org/mozilla/jss/netscape/security/provider/DSAParameterGenerator.java + create mode 100755 org/mozilla/jss/netscape/security/provider/DSAParameters.java + create mode 100644 org/mozilla/jss/netscape/security/provider/DSAPrivateKey.java + create mode 100644 org/mozilla/jss/netscape/security/provider/DSAPublicKey.java + create mode 100644 org/mozilla/jss/netscape/security/provider/MD5.java + create mode 100644 org/mozilla/jss/netscape/security/provider/Makefile + create mode 100644 org/mozilla/jss/netscape/security/provider/RSAPublicKey.java + create mode 100644 org/mozilla/jss/netscape/security/provider/SHA.java + create mode 100644 org/mozilla/jss/netscape/security/provider/Sun.java + create mode 100644 org/mozilla/jss/netscape/security/provider/X509CertificateFactory.java + create mode 100644 org/mozilla/jss/netscape/security/provider/manifest.mn + create mode 100644 org/mozilla/jss/netscape/security/util/ASN1CharStrConvMap.java + create mode 100644 org/mozilla/jss/netscape/security/util/ASN1CharsetProvider.java + create mode 100644 org/mozilla/jss/netscape/security/util/BigInt.java + create mode 100644 org/mozilla/jss/netscape/security/util/BitArray.java + create mode 100644 org/mozilla/jss/netscape/security/util/ByteArrayLexOrder.java + create mode 100644 org/mozilla/jss/netscape/security/util/ByteArrayTagOrder.java + create mode 100644 org/mozilla/jss/netscape/security/util/Cert.java + create mode 100644 org/mozilla/jss/netscape/security/util/CertPrettyPrint.java + create mode 100644 org/mozilla/jss/netscape/security/util/CrlPrettyPrint.java + create mode 100644 org/mozilla/jss/netscape/security/util/DerEncoder.java + create mode 100644 org/mozilla/jss/netscape/security/util/DerInputBuffer.java + create mode 100644 org/mozilla/jss/netscape/security/util/DerInputStream.java + create mode 100644 org/mozilla/jss/netscape/security/util/DerOutputStream.java + create mode 100644 org/mozilla/jss/netscape/security/util/DerValue.java + create mode 100644 org/mozilla/jss/netscape/security/util/ExtPrettyPrint.java + create mode 100644 org/mozilla/jss/netscape/security/util/IA5Charset.java + create mode 100644 org/mozilla/jss/netscape/security/util/IA5CharsetDecoder.java + create mode 100644 org/mozilla/jss/netscape/security/util/IA5CharsetEncoder.java + create mode 100644 org/mozilla/jss/netscape/security/util/Makefile + create mode 100644 org/mozilla/jss/netscape/security/util/ObjectIdentifier.java + create mode 100644 org/mozilla/jss/netscape/security/util/PrettyPrintFormat.java + create mode 100644 org/mozilla/jss/netscape/security/util/PrettyPrintResources.java + create mode 100644 org/mozilla/jss/netscape/security/util/PrintableCharset.java + create mode 100644 org/mozilla/jss/netscape/security/util/PrintableCharsetDecoder.java + create mode 100644 org/mozilla/jss/netscape/security/util/PrintableCharsetEncoder.java + create mode 100644 org/mozilla/jss/netscape/security/util/PubKeyPrettyPrint.java + create mode 100644 org/mozilla/jss/netscape/security/util/UniversalCharset.java + create mode 100644 org/mozilla/jss/netscape/security/util/UniversalCharsetDecoder.java + create mode 100644 org/mozilla/jss/netscape/security/util/UniversalCharsetEncoder.java + create mode 100644 org/mozilla/jss/netscape/security/util/Utils.java + create mode 100644 org/mozilla/jss/netscape/security/util/manifest.mn + create mode 100755 org/mozilla/jss/netscape/security/x509/ACertAttrSet.java + create mode 100644 org/mozilla/jss/netscape/security/x509/AVA.java + create mode 100644 org/mozilla/jss/netscape/security/x509/AVAValueConverter.java + create mode 100644 org/mozilla/jss/netscape/security/x509/AlgIdDSA.java + create mode 100644 org/mozilla/jss/netscape/security/x509/AlgorithmId.java + create mode 100644 org/mozilla/jss/netscape/security/x509/Attribute.java + create mode 100644 org/mozilla/jss/netscape/security/x509/AuthorityKeyIdentifierExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/BasicConstraintsExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CPSuri.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CRLDistributionPoint.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CRLDistributionPointsExtension.java + create mode 100755 org/mozilla/jss/netscape/security/x509/CRLExtensions.java + create mode 100755 org/mozilla/jss/netscape/security/x509/CRLNumberExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CRLReasonExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertAndKeyGen.java + create mode 100755 org/mozilla/jss/netscape/security/x509/CertAttrSet.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertException.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertParseError.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateAlgorithmId.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateChain.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateExtensions.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateIssuerExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateIssuerName.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateIssuerUniqueIdentity.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificatePoliciesExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificatePolicyId.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificatePolicyInfo.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificatePolicyMap.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificatePolicySet.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateSerialNumber.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateSubjectName.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateSubjectUniqueIdentity.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateValidity.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateVersion.java + create mode 100644 org/mozilla/jss/netscape/security/x509/CertificateX509Key.java + create mode 100644 org/mozilla/jss/netscape/security/x509/DNSName.java + create mode 100755 org/mozilla/jss/netscape/security/x509/DeltaCRLIndicatorExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/DirStrConverter.java + create mode 100644 org/mozilla/jss/netscape/security/x509/DisplayText.java + create mode 100644 org/mozilla/jss/netscape/security/x509/EDIPartyName.java + create mode 100644 org/mozilla/jss/netscape/security/x509/Extension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/Extensions.java + create mode 100644 org/mozilla/jss/netscape/security/x509/FreshestCRLExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/GeneralName.java + create mode 100644 org/mozilla/jss/netscape/security/x509/GeneralNameInterface.java + create mode 100644 org/mozilla/jss/netscape/security/x509/GeneralNames.java + create mode 100644 org/mozilla/jss/netscape/security/x509/GeneralNamesException.java + create mode 100644 org/mozilla/jss/netscape/security/x509/GeneralSubtree.java + create mode 100644 org/mozilla/jss/netscape/security/x509/GeneralSubtrees.java + create mode 100644 org/mozilla/jss/netscape/security/x509/GenericValueConverter.java + create mode 100644 org/mozilla/jss/netscape/security/x509/HoldInstructionExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/IA5StringConverter.java + create mode 100644 org/mozilla/jss/netscape/security/x509/IPAddressName.java + create mode 100644 org/mozilla/jss/netscape/security/x509/InvalidIPAddressException.java + create mode 100755 org/mozilla/jss/netscape/security/x509/InvalidityDateExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/IssuerAlternativeNameExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/IssuingDistributionPoint.java + create mode 100644 org/mozilla/jss/netscape/security/x509/IssuingDistributionPointExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/KeyIdentifier.java + create mode 100644 org/mozilla/jss/netscape/security/x509/KeyUsageExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/LdapDNStrConverter.java + create mode 100644 org/mozilla/jss/netscape/security/x509/LdapV3DNStrConverter.java + create mode 100644 org/mozilla/jss/netscape/security/x509/Makefile + create mode 100644 org/mozilla/jss/netscape/security/x509/NSCCommentExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/NameConstraintsExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/NoticeReference.java + create mode 100644 org/mozilla/jss/netscape/security/x509/OIDMap.java + create mode 100644 org/mozilla/jss/netscape/security/x509/OIDName.java + create mode 100644 org/mozilla/jss/netscape/security/x509/OtherName.java + create mode 100644 org/mozilla/jss/netscape/security/x509/PKIXExtensions.java + create mode 100644 org/mozilla/jss/netscape/security/x509/PolicyConstraint.java + create mode 100644 org/mozilla/jss/netscape/security/x509/PolicyConstraintsExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/PolicyMappingsExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/PolicyQualifierInfo.java + create mode 100644 org/mozilla/jss/netscape/security/x509/PolicyQualifiers.java + create mode 100644 org/mozilla/jss/netscape/security/x509/PrintableConverter.java + create mode 100644 org/mozilla/jss/netscape/security/x509/PrivateKeyUsageExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/Qualifier.java + create mode 100644 org/mozilla/jss/netscape/security/x509/RDN.java + create mode 100644 org/mozilla/jss/netscape/security/x509/RFC1779StrConverter.java + create mode 100644 org/mozilla/jss/netscape/security/x509/RFC822Name.java + create mode 100755 org/mozilla/jss/netscape/security/x509/ReasonFlags.java + create mode 100644 org/mozilla/jss/netscape/security/x509/RevocationReason.java + create mode 100644 org/mozilla/jss/netscape/security/x509/RevocationReasonAdapter.java + create mode 100755 org/mozilla/jss/netscape/security/x509/RevokedCertImpl.java + create mode 100644 org/mozilla/jss/netscape/security/x509/RevokedCertificate.java + create mode 100644 org/mozilla/jss/netscape/security/x509/SerialNumber.java + create mode 100644 org/mozilla/jss/netscape/security/x509/SubjectAlternativeNameExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/SubjectDirAttributesExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/SubjectKeyIdentifierExtension.java + create mode 100644 org/mozilla/jss/netscape/security/x509/URIName.java + create mode 100644 org/mozilla/jss/netscape/security/x509/UniqueIdentity.java + create mode 100644 org/mozilla/jss/netscape/security/x509/UserNotice.java + create mode 100644 org/mozilla/jss/netscape/security/x509/X500Name.java + create mode 100644 org/mozilla/jss/netscape/security/x509/X500NameAttrMap.java + create mode 100644 org/mozilla/jss/netscape/security/x509/X500Signer.java + create mode 100644 org/mozilla/jss/netscape/security/x509/X509AttributeName.java + create mode 100755 org/mozilla/jss/netscape/security/x509/X509CRLImpl.java + create mode 100644 org/mozilla/jss/netscape/security/x509/X509Cert.java + create mode 100755 org/mozilla/jss/netscape/security/x509/X509CertImpl.java + create mode 100644 org/mozilla/jss/netscape/security/x509/X509CertInfo.java + create mode 100644 org/mozilla/jss/netscape/security/x509/X509ExtensionException.java + create mode 100644 org/mozilla/jss/netscape/security/x509/X509Key.java + create mode 100644 org/mozilla/jss/netscape/security/x509/manifest.mn + create mode 100644 org/mozilla/jss/netscape/security/x509/rules.mk + create mode 100644 org/mozilla/jss/tests/X509CertTest.java + +diff --git a/org/mozilla/jss/netscape/Makefile b/org/mozilla/jss/netscape/Makefile +new file mode 100644 +index 00000000..8fd7054b +--- /dev/null ++++ b/org/mozilla/jss/netscape/Makefile +@@ -0,0 +1,45 @@ ++#! gmake ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### +diff --git a/org/mozilla/jss/netscape/manifest.mn b/org/mozilla/jss/netscape/manifest.mn +new file mode 100644 +index 00000000..a69b9346 +--- /dev/null ++++ b/org/mozilla/jss/netscape/manifest.mn +@@ -0,0 +1,14 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++CORE_DEPTH = ../../../.. ++ ++NS_USE_JDK = 1 ++ ++MODULE = jss ++ ++ ++DIRS = security \ ++ $(NULL) +diff --git a/org/mozilla/jss/netscape/security/Makefile b/org/mozilla/jss/netscape/security/Makefile +new file mode 100644 +index 00000000..8fd7054b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/Makefile +@@ -0,0 +1,45 @@ ++#! gmake ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### +diff --git a/org/mozilla/jss/netscape/security/acl/AclEntryImpl.java b/org/mozilla/jss/netscape/security/acl/AclEntryImpl.java +new file mode 100644 +index 00000000..790da68f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/AclEntryImpl.java +@@ -0,0 +1,182 @@ ++// --- 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 netscape.security.acl; ++ ++import java.security.Principal; ++import java.security.acl.AclEntry; ++import java.security.acl.Group; ++import java.security.acl.Permission; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++/** ++ * This is a class that describes one entry that associates users ++ * or groups with permissions in the ACL. ++ * The entry may be used as a way of granting or denying permissions. ++ * ++ * @author Satish Dharmaraj ++ */ ++public class AclEntryImpl implements AclEntry { ++ private Principal user = null; ++ private Vector permissionSet = new Vector(10, 10); ++ private boolean negative = false; ++ ++ /** ++ * Construct an ACL entry that associates a user with permissions ++ * in the ACL. ++ * ++ * @param user The user that is associated with this entry. ++ */ ++ public AclEntryImpl(Principal user) { ++ this.user = user; ++ } ++ ++ /** ++ * Construct a null ACL entry ++ */ ++ public AclEntryImpl() { ++ } ++ ++ /** ++ * Sets the principal in the entity. If a group or a ++ * principal had already been set, a false value is ++ * returned, otherwise a true value is returned. ++ * ++ * @param user The user that is associated with this entry. ++ * @return true if the principal is set, false if there is ++ * one already. ++ */ ++ public boolean setPrincipal(Principal user) { ++ if (this.user != null) ++ return false; ++ this.user = user; ++ return true; ++ } ++ ++ /** ++ * This method sets the ACL to have negative permissions. ++ * That is the user or group is denied the permission set ++ * specified in the entry. ++ */ ++ public void setNegativePermissions() { ++ negative = true; ++ } ++ ++ /** ++ * Returns true if this is a negative ACL. ++ */ ++ public boolean isNegative() { ++ return negative; ++ } ++ ++ /** ++ * A principal or a group can be associated with multiple ++ * permissions. This method adds a permission to the ACL entry. ++ * ++ * @param permission The permission to be associated with ++ * the principal or the group in the entry. ++ * @return true if the permission was added, false if the ++ * permission was already part of the permission set. ++ */ ++ public boolean addPermission(Permission permission) { ++ ++ if (permissionSet.contains(permission)) ++ return false; ++ ++ permissionSet.addElement(permission); ++ ++ return true; ++ } ++ ++ /** ++ * The method disassociates the permission from the Principal ++ * or the Group in this ACL entry. ++ * ++ * @param permission The permission to be disassociated with ++ * the principal or the group in the entry. ++ * @return true if the permission is removed, false if the ++ * permission is not part of the permission set. ++ */ ++ public boolean removePermission(Permission permission) { ++ return permissionSet.removeElement(permission); ++ } ++ ++ /** ++ * Checks if the passed permission is part of the allowed ++ * permission set in this entry. ++ * ++ * @param permission The permission that has to be part of ++ * the permission set in the entry. ++ * @return true if the permission passed is part of the ++ * permission set in the entry, false otherwise. ++ */ ++ public boolean checkPermission(Permission permission) { ++ return permissionSet.contains(permission); ++ } ++ ++ /** ++ * return an enumeration of the permissions in this ACL entry. ++ */ ++ public Enumeration permissions() { ++ return permissionSet.elements(); ++ } ++ ++ /** ++ * Return a string representation of the contents of the ACL entry. ++ */ ++ public String toString() { ++ StringBuffer s = new StringBuffer(); ++ if (negative) ++ s.append("-"); ++ else ++ s.append("+"); ++ if (user instanceof Group) ++ s.append("Group."); ++ else ++ s.append("User."); ++ s.append(user + "="); ++ Enumeration e = permissions(); ++ while (e.hasMoreElements()) { ++ Permission p = e.nextElement(); ++ s.append(p); ++ if (e.hasMoreElements()) ++ s.append(","); ++ } ++ return new String(s); ++ } ++ ++ /** ++ * Clones an AclEntry. ++ */ ++ public synchronized Object clone() { ++ AclEntryImpl cloned; ++ cloned = new AclEntryImpl(user); ++ cloned.permissionSet = new Vector(permissionSet); ++ cloned.negative = negative; ++ return cloned; ++ } ++ ++ /** ++ * Return the Principal associated in this ACL entry. ++ * The method returns null if the entry uses a group ++ * instead of a principal. ++ */ ++ public Principal getPrincipal() { ++ return user; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/acl/AclImpl.java b/org/mozilla/jss/netscape/security/acl/AclImpl.java +new file mode 100644 +index 00000000..9ccb0fc1 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/AclImpl.java +@@ -0,0 +1,393 @@ ++// --- 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 netscape.security.acl; ++ ++import java.security.Principal; ++import java.security.acl.Acl; ++import java.security.acl.AclEntry; ++import java.security.acl.Group; ++import java.security.acl.NotOwnerException; ++import java.security.acl.Permission; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.NoSuchElementException; ++import java.util.Vector; ++ ++/** ++ * An Access Control List (ACL) is encapsulated by this class. ++ * ++ * @author Satish Dharmaraj ++ */ ++public class AclImpl extends OwnerImpl implements Acl { ++ // ++ // Maintain four tables. one each for positive and negative ++ // ACLs. One each depending on whether the entity is a group ++ // or principal. ++ // ++ private Hashtable allowedUsersTable = new Hashtable(23); ++ private Hashtable allowedGroupsTable = new Hashtable(23); ++ private Hashtable deniedUsersTable = new Hashtable(23); ++ private Hashtable deniedGroupsTable = new Hashtable(23); ++ private String aclName = null; ++ private Vector zeroSet = new Vector(1, 1); ++ ++ /** ++ * Constructor for creating an empty ACL. ++ */ ++ public AclImpl(Principal owner, String name) { ++ super(owner); ++ try { ++ setName(owner, name); ++ } catch (Exception e) { ++ } ++ } ++ ++ /** ++ * Sets the name of the ACL. ++ * ++ * @param caller the principal who is invoking this method. ++ * @param name the name of the ACL. ++ * @exception NotOwnerException if the caller principal is ++ * not on the owners list of the Acl. ++ */ ++ public void setName(Principal caller, String name) ++ throws NotOwnerException { ++ if (!isOwner(caller)) ++ throw new NotOwnerException(); ++ ++ aclName = name; ++ } ++ ++ /** ++ * Returns the name of the ACL. ++ * ++ * @return the name of the ACL. ++ */ ++ public String getName() { ++ return aclName; ++ } ++ ++ /** ++ * Adds an ACL entry to this ACL. An entry associates a ++ * group or a principal with a set of permissions. Each ++ * user or group can have one positive ACL entry and one ++ * negative ACL entry. If there is one of the type (negative ++ * or positive) already in the table, a false value is returned. ++ * The caller principal must be a part of the owners list of ++ * the ACL in order to invoke this method. ++ * ++ * @param caller the principal who is invoking this method. ++ * @param entry the ACL entry that must be added to the ACL. ++ * @return true on success, false if the entry is already present. ++ * @exception NotOwnerException if the caller principal ++ * is not on the owners list of the Acl. ++ */ ++ public synchronized boolean addEntry(Principal caller, AclEntry entry) ++ throws NotOwnerException { ++ if (!isOwner(caller)) ++ throw new NotOwnerException(); ++ ++ Hashtable aclTable = findTable(entry); ++ Principal key = entry.getPrincipal(); ++ ++ if (aclTable.get(key) != null) ++ return false; ++ ++ aclTable.put(key, entry); ++ return true; ++ } ++ ++ /** ++ * Removes an ACL entry from this ACL. ++ * The caller principal must be a part of the owners list of the ACL ++ * in order to invoke this method. ++ * ++ * @param caller the principal who is invoking this method. ++ * @param entry the ACL entry that must be removed from the ACL. ++ * @return true on success, false if the entry is not part of the ACL. ++ * @exception NotOwnerException if the caller principal is not ++ * the owners list of the Acl. ++ */ ++ public synchronized boolean removeEntry(Principal caller, AclEntry entry) ++ throws NotOwnerException { ++ if (!isOwner(caller)) ++ throw new NotOwnerException(); ++ ++ Hashtable aclTable = findTable(entry); ++ Object key = entry.getPrincipal(); ++ ++ Object o = aclTable.remove(key); ++ return (o != null); ++ } ++ ++ /** ++ * This method returns the set of allowed permissions for the ++ * specified principal. This set of allowed permissions is calculated ++ * as follows: ++ * ++ * If there is no entry for a group or a principal an empty permission ++ * set is assumed. ++ * ++ * The group positive permission set is the union of all ++ * the positive permissions of each group that the individual belongs to. ++ * The group negative permission set is the union of all ++ * the negative permissions of each group that the individual belongs to. ++ * If there is a specific permission that occurs in both ++ * the postive permission set and the negative permission set, ++ * it is removed from both. The group positive and negatoive permission ++ * sets are calculated. ++ * ++ * The individial positive permission set and the individual negative ++ * permission set is then calculated. Again abscence of an entry means ++ * the empty set. ++ * ++ * The set of permissions granted to the principal is then calculated using ++ * the simple rule: Individual permissions always override the Group permissions. ++ * Specifically, individual negative permission set (specific ++ * denial of permissions) overrides the group positive permission set. ++ * And the individual positive permission set override the group negative ++ * permission set. ++ * ++ * @param user the principal for which the ACL entry is returned. ++ * @return The resulting permission set that the principal is allowed. ++ */ ++ public synchronized Enumeration getPermissions(Principal user) { ++ ++ Enumeration individualPositive; ++ Enumeration individualNegative; ++ Enumeration groupPositive; ++ Enumeration groupNegative; ++ ++ // ++ // canonicalize the sets. That is remove common permissions from ++ // positive and negative sets. ++ // ++ groupPositive = subtract(getGroupPositive(user), getGroupNegative(user)); ++ groupNegative = subtract(getGroupNegative(user), getGroupPositive(user)); ++ individualPositive = subtract(getIndividualPositive(user), getIndividualNegative(user)); ++ individualNegative = subtract(getIndividualNegative(user), getIndividualPositive(user)); ++ ++ // ++ // net positive permissions is individual positive permissions ++ // plus (group positive - individual negative). ++ // ++ Enumeration temp1 = subtract(groupPositive, individualNegative); ++ Enumeration netPositive = union(individualPositive, temp1); ++ ++ // recalculate the enumeration since we lost it in performing the ++ // subtraction ++ // ++ individualPositive = subtract(getIndividualPositive(user), getIndividualNegative(user)); ++ individualNegative = subtract(getIndividualNegative(user), getIndividualPositive(user)); ++ ++ // ++ // net negative permissions is individual negative permissions ++ // plus (group negative - individual positive). ++ // ++ temp1 = subtract(groupNegative, individualPositive); ++ Enumeration netNegative = union(individualNegative, temp1); ++ ++ return subtract(netPositive, netNegative); ++ } ++ ++ /** ++ * This method checks whether or not the specified principal ++ * has the required permission. If permission is denied ++ * permission false is returned, a true value is returned otherwise. ++ * This method does not authenticate the principal. It presumes that ++ * the principal is a valid authenticated principal. ++ * ++ * @param principal the name of the authenticated principal ++ * @param permission the permission that the principal must have. ++ * @return true of the principal has the permission desired, false ++ * otherwise. ++ */ ++ public boolean checkPermission(Principal principal, Permission permission) { ++ Enumeration permSet = getPermissions(principal); ++ while (permSet.hasMoreElements()) { ++ Permission p = permSet.nextElement(); ++ if (p.equals(permission)) ++ return true; ++ } ++ return false; ++ } ++ ++ /** ++ * returns an enumeration of the entries in this ACL. ++ */ ++ public synchronized Enumeration entries() { ++ return new AclEnumerator(this, ++ allowedUsersTable, allowedGroupsTable, ++ deniedUsersTable, deniedGroupsTable); ++ } ++ ++ /** ++ * return a stringified version of the ++ * ACL. ++ */ ++ public String toString() { ++ StringBuffer sb = new StringBuffer(); ++ Enumeration entries = entries(); ++ while (entries.hasMoreElements()) { ++ AclEntry entry = entries.nextElement(); ++ sb.append(entry.toString().trim()); ++ sb.append("\n"); ++ } ++ ++ return sb.toString(); ++ } ++ ++ // ++ // Find the table that this entry belongs to. There are 4 ++ // tables that are maintained. One each for postive and ++ // negative ACLs and one each for groups and users. ++ // This method figures out which ++ // table is the one that this AclEntry belongs to. ++ // ++ private Hashtable findTable(AclEntry entry) { ++ Hashtable aclTable = null; ++ ++ Principal p = entry.getPrincipal(); ++ if (p instanceof Group) { ++ if (entry.isNegative()) ++ aclTable = deniedGroupsTable; ++ else ++ aclTable = allowedGroupsTable; ++ } else { ++ if (entry.isNegative()) ++ aclTable = deniedUsersTable; ++ else ++ aclTable = allowedUsersTable; ++ } ++ return aclTable; ++ } ++ ++ // ++ // returns the set e1 U e2. ++ // ++ private Enumeration union(Enumeration e1, Enumeration e2) { ++ Vector v = new Vector(20, 20); ++ ++ while (e1.hasMoreElements()) ++ v.addElement(e1.nextElement()); ++ ++ while (e2.hasMoreElements()) { ++ T o = e2.nextElement(); ++ if (!v.contains(o)) ++ v.addElement(o); ++ } ++ ++ return v.elements(); ++ } ++ ++ // ++ // returns the set e1 - e2. ++ // ++ private Enumeration subtract(Enumeration e1, Enumeration e2) { ++ Vector v = new Vector(20, 20); ++ ++ while (e1.hasMoreElements()) ++ v.addElement(e1.nextElement()); ++ ++ while (e2.hasMoreElements()) { ++ T o = e2.nextElement(); ++ if (v.contains(o)) ++ v.removeElement(o); ++ } ++ ++ return v.elements(); ++ } ++ ++ private Enumeration getGroupPositive(Principal user) { ++ Enumeration groupPositive = zeroSet.elements(); ++ Enumeration e = allowedGroupsTable.keys(); ++ while (e.hasMoreElements()) { ++ Group g = (Group) e.nextElement(); ++ if (g.isMember(user)) { ++ AclEntry ae = allowedGroupsTable.get(g); ++ groupPositive = union(ae.permissions(), groupPositive); ++ } ++ } ++ return groupPositive; ++ } ++ ++ private Enumeration getGroupNegative(Principal user) { ++ Enumeration groupNegative = zeroSet.elements(); ++ Enumeration e = deniedGroupsTable.keys(); ++ while (e.hasMoreElements()) { ++ Group g = (Group) e.nextElement(); ++ if (g.isMember(user)) { ++ AclEntry ae = deniedGroupsTable.get(g); ++ groupNegative = union(ae.permissions(), groupNegative); ++ } ++ } ++ return groupNegative; ++ } ++ ++ private Enumeration getIndividualPositive(Principal user) { ++ Enumeration individualPositive = zeroSet.elements(); ++ AclEntry ae = allowedUsersTable.get(user); ++ if (ae != null) ++ individualPositive = ae.permissions(); ++ return individualPositive; ++ } ++ ++ private Enumeration getIndividualNegative(Principal user) { ++ Enumeration individualNegative = zeroSet.elements(); ++ AclEntry ae = deniedUsersTable.get(user); ++ if (ae != null) ++ individualNegative = ae.permissions(); ++ return individualNegative; ++ } ++} ++ ++final class AclEnumerator implements Enumeration { ++ Acl acl; ++ Enumeration u1, u2, g1, g2; ++ ++ AclEnumerator(Acl acl, Hashtable u1, Hashtable g1, ++ Hashtable u2, Hashtable g2) { ++ this.acl = acl; ++ this.u1 = u1.elements(); ++ this.u2 = u2.elements(); ++ this.g1 = g1.elements(); ++ this.g2 = g2.elements(); ++ } ++ ++ public boolean hasMoreElements() { ++ synchronized (acl) { ++ return (u1.hasMoreElements() || ++ u2.hasMoreElements() || ++ g1.hasMoreElements() || g2.hasMoreElements()); ++ } ++ } ++ ++ public AclEntry nextElement() { ++ synchronized (acl) { ++ if (u1.hasMoreElements()) ++ return u1.nextElement(); ++ if (u2.hasMoreElements()) ++ return u2.nextElement(); ++ if (g1.hasMoreElements()) ++ return g1.nextElement(); ++ if (g2.hasMoreElements()) ++ return g2.nextElement(); ++ } ++ throw new NoSuchElementException("Acl Enumerator"); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/acl/AllPermissionsImpl.java b/org/mozilla/jss/netscape/security/acl/AllPermissionsImpl.java +new file mode 100644 +index 00000000..df3ff645 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/AllPermissionsImpl.java +@@ -0,0 +1,43 @@ ++// --- 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 netscape.security.acl; ++ ++import java.security.acl.Permission; ++ ++/** ++ * This class implements the principal interface for the set of all permissions. ++ * ++ * @author Satish Dharmaraj ++ */ ++public class AllPermissionsImpl extends PermissionImpl { ++ ++ public AllPermissionsImpl(String s) { ++ super(s); ++ } ++ ++ /** ++ * This function returns true if the permission passed matches the permission represented in ++ * this interface. ++ * ++ * @param another The Permission object to compare with. ++ * @return true always ++ */ ++ public boolean equals(Permission another) { ++ return true; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/acl/GroupImpl.java b/org/mozilla/jss/netscape/security/acl/GroupImpl.java +new file mode 100644 +index 00000000..d28e83f0 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/GroupImpl.java +@@ -0,0 +1,173 @@ ++// --- 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 netscape.security.acl; ++ ++import java.security.Principal; ++import java.security.acl.Group; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++/** ++ * This class implements a group of principals. ++ * ++ * @author Satish Dharmaraj ++ */ ++public class GroupImpl implements Group { ++ private Vector groupMembers = new Vector(50, 100); ++ private String group; ++ ++ /** ++ * Constructs a Group object with no members. ++ * ++ * @param groupName the name of the group ++ */ ++ public GroupImpl(String groupName) { ++ this.group = groupName; ++ } ++ ++ /** ++ * adds the specified member to the group. ++ * ++ * @param user The principal to add to the group. ++ * @return true if the member was added - false if the ++ * member could not be added. ++ */ ++ public boolean addMember(Principal user) { ++ if (groupMembers.contains(user)) ++ return false; ++ ++ // do not allow groups to be added to itself. ++ if (group.equals(user.toString())) ++ throw new IllegalArgumentException(); ++ ++ groupMembers.addElement(user); ++ return true; ++ } ++ ++ /** ++ * removes the specified member from the group. ++ * ++ * @param user The principal to remove from the group. ++ * @param true if the principal was removed false if ++ * the principal was not a member ++ */ ++ public boolean removeMember(Principal user) { ++ return groupMembers.removeElement(user); ++ } ++ ++ /** ++ * returns the enumeration of the members in the group. ++ */ ++ public Enumeration members() { ++ return groupMembers.elements(); ++ } ++ ++ /** ++ * This function returns true if the group passed matches ++ * the group represented in this interface. ++ * ++ * @param another The group to compare this group to. ++ */ ++ public boolean equals(Group another) { ++ return group.equals(another.toString()); ++ } ++ ++ /** ++ * Prints a stringified version of the group. ++ */ ++ public String toString() { ++ return group; ++ } ++ ++ /** ++ * return a hashcode for the principal. ++ */ ++ public int hashCode() { ++ return group.hashCode(); ++ } ++ ++ /** ++ * returns true if the passed principal is a member of the group. ++ * ++ * @param member The principal whose membership must be checked for. ++ * @return true if the principal is a member of this group, ++ * false otherwise ++ */ ++ public boolean isMember(Principal member) { ++ ++ // ++ // if the member is part of the group (common case), return true. ++ // if not, recursively search depth first in the group looking for the ++ // principal. ++ // ++ if (groupMembers.contains(member)) { ++ return true; ++ } else { ++ Vector alreadySeen = new Vector(10); ++ return isMemberRecurse(member, alreadySeen); ++ } ++ } ++ ++ /** ++ * return the name of the principal. ++ */ ++ public String getName() { ++ return group; ++ } ++ ++ // ++ // This function is the recursive search of groups for this ++ // implementation of the Group. The search proceeds building up ++ // a vector of already seen groups. Only new groups are considered, ++ // thereby avoiding loops. ++ // ++ boolean isMemberRecurse(Principal member, Vector alreadySeen) { ++ Enumeration e = members(); ++ while (e.hasMoreElements()) { ++ boolean mem = false; ++ Principal p = e.nextElement(); ++ ++ // if the member is in this collection, return true ++ if (p.equals(member)) { ++ return true; ++ } else if (p instanceof GroupImpl) { ++ // ++ // if not recurse if the group has not been checked already. ++ // Can call method in this package only if the object is an ++ // instance of this class. Otherwise call the method defined ++ // in the interface. (This can lead to a loop if a mixture of ++ // implementations form a loop, but we live with this improbable ++ // case rather than clutter the interface by forcing the ++ // implementation of this method.) ++ // ++ GroupImpl g = (GroupImpl) p; ++ alreadySeen.addElement(this); ++ if (!alreadySeen.contains(g)) ++ mem = g.isMemberRecurse(member, alreadySeen); ++ } else if (p instanceof Group) { ++ Group g = (Group) p; ++ if (!alreadySeen.contains(g)) ++ mem = g.isMember(member); ++ } ++ ++ if (mem) ++ return mem; ++ } ++ return false; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/acl/Makefile b/org/mozilla/jss/netscape/security/acl/Makefile +new file mode 100644 +index 00000000..8fd7054b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/Makefile +@@ -0,0 +1,45 @@ ++#! gmake ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### +diff --git a/org/mozilla/jss/netscape/security/acl/OwnerImpl.java b/org/mozilla/jss/netscape/security/acl/OwnerImpl.java +new file mode 100644 +index 00000000..0e6e13e5 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/OwnerImpl.java +@@ -0,0 +1,105 @@ ++// --- 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 netscape.security.acl; ++ ++import java.security.Principal; ++import java.security.acl.Group; ++import java.security.acl.LastOwnerException; ++import java.security.acl.NotOwnerException; ++import java.security.acl.Owner; ++import java.util.Enumeration; ++ ++/** ++ * Class implementing the Owner interface. The ++ * initial owner principal is configured as ++ * part of the constructor. ++ * ++ * @author Satish Dharmaraj ++ */ ++public class OwnerImpl implements Owner { ++ private Group ownerGroup; ++ ++ public OwnerImpl(Principal owner) { ++ ownerGroup = new GroupImpl("AclOwners"); ++ ownerGroup.addMember(owner); ++ } ++ ++ /** ++ * Adds an owner. Owners can modify ACL contents and can disassociate ++ * ACLs from the objects they protect in the AclConfig interface. ++ * The caller principal must be a part of the owners list of the ACL in ++ * order to invoke this method. The initial owner is configured ++ * at ACL construction time. ++ * ++ * @param caller the principal who is invoking this method. ++ * @param owner The owner that should be added to the owners list. ++ * @return true if success, false if already an owner. ++ * @exception NotOwnerException if the caller principal is not on ++ * the owners list of the Acl. ++ */ ++ public synchronized boolean addOwner(Principal caller, Principal owner) ++ throws NotOwnerException { ++ if (!isOwner(caller)) ++ throw new NotOwnerException(); ++ ++ ownerGroup.addMember(owner); ++ return false; ++ } ++ ++ /** ++ * Delete owner. If this is the last owner in the ACL, an exception is ++ * raised. ++ * The caller principal must be a part of the owners list of the ACL in ++ * order to invoke this method. ++ * ++ * @param caller the principal who is invoking this method. ++ * @param owner The owner to be removed from the owners list. ++ * @return true if the owner is removed, false if the owner is not part ++ * of the owners list. ++ * @exception NotOwnerException if the caller principal is not on ++ * the owners list of the Acl. ++ * @exception LastOwnerException if there is only one owner left in the group, then ++ * deleteOwner would leave the ACL owner-less. This exception is raised in such a case. ++ */ ++ public synchronized boolean deleteOwner(Principal caller, Principal owner) ++ throws NotOwnerException, LastOwnerException { ++ if (!isOwner(caller)) ++ throw new NotOwnerException(); ++ ++ Enumeration e = ownerGroup.members(); ++ // ++ // check if there is atleast 2 members left. ++ // ++ e.nextElement(); // consume next element ++ if (e.hasMoreElements()) ++ return ownerGroup.removeMember(owner); ++ else ++ throw new LastOwnerException(); ++ ++ } ++ ++ /** ++ * returns if the given principal belongs to the owner list. ++ * ++ * @param owner The owner to check if part of the owners list ++ * @return true if the passed principal is in the owner list, false if not. ++ */ ++ public synchronized boolean isOwner(Principal owner) { ++ return ownerGroup.isMember(owner); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/acl/PermissionImpl.java b/org/mozilla/jss/netscape/security/acl/PermissionImpl.java +new file mode 100644 +index 00000000..80b36e1d +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/PermissionImpl.java +@@ -0,0 +1,73 @@ ++// --- 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 netscape.security.acl; ++ ++import java.security.acl.Permission; ++ ++/** ++ * The PermissionImpl class implements the permission ++ * interface for permissions that are strings. ++ * ++ * @author Satish Dharmaraj ++ */ ++public class PermissionImpl implements Permission { ++ ++ private String permission; ++ ++ /** ++ * Construct a permission object using a string. ++ * ++ * @param permission the stringified version of the permission. ++ */ ++ public PermissionImpl(String permission) { ++ this.permission = permission; ++ } ++ ++ /** ++ * This function returns true if the object passed matches the permission ++ * represented in this interface. ++ * ++ * @param another The Permission object to compare with. ++ * @return true if the Permission objects are equal, false otherwise ++ */ ++ public boolean equals(Object another) { ++ if (another instanceof Permission) { ++ Permission p = (Permission) another; ++ return permission.equals(p.toString()); ++ } else { ++ return false; ++ } ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + ((permission == null) ? 0 : permission.hashCode()); ++ return result; ++ } ++ ++ /** ++ * Prints a stringified version of the permission. ++ * ++ * @return the string representation of the Permission. ++ */ ++ public String toString() { ++ return permission; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/acl/PrincipalImpl.java b/org/mozilla/jss/netscape/security/acl/PrincipalImpl.java +new file mode 100644 +index 00000000..54de845d +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/PrincipalImpl.java +@@ -0,0 +1,77 @@ ++// --- 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 netscape.security.acl; ++ ++import java.security.Principal; ++ ++/** ++ * This class implements the principal interface. ++ * ++ * @author Satish Dharmaraj ++ */ ++public class PrincipalImpl implements Principal { ++ ++ private String user; ++ ++ /** ++ * Construct a principal from a string user name. ++ * ++ * @param user The string form of the principal name. ++ */ ++ public PrincipalImpl(String user) { ++ this.user = user; ++ } ++ ++ /** ++ * This function returns true if the object passed matches ++ * the principal represented in this implementation ++ * ++ * @param another the Principal to compare with. ++ * @return true if the Principal passed is the same as that ++ * encapsulated in this object, false otherwise ++ */ ++ public boolean equals(Object another) { ++ if (another instanceof PrincipalImpl) { ++ PrincipalImpl p = (PrincipalImpl) another; ++ return user.equals(p.toString()); ++ } else ++ return false; ++ } ++ ++ /** ++ * Prints a stringified version of the principal. ++ */ ++ public String toString() { ++ return user; ++ } ++ ++ /** ++ * return a hashcode for the principal. ++ */ ++ public int hashCode() { ++ return user.hashCode(); ++ } ++ ++ /** ++ * return the name of the principal. ++ */ ++ public String getName() { ++ return user; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/acl/WorldGroupImpl.java b/org/mozilla/jss/netscape/security/acl/WorldGroupImpl.java +new file mode 100644 +index 00000000..a8eea8a7 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/WorldGroupImpl.java +@@ -0,0 +1,42 @@ ++// --- 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 netscape.security.acl; ++ ++import java.security.Principal; ++ ++/** ++ * This class implements a group of principals. ++ * ++ * @author Satish Dharmaraj ++ */ ++public class WorldGroupImpl extends GroupImpl { ++ ++ public WorldGroupImpl(String s) { ++ super(s); ++ } ++ ++ /** ++ * returns true for all passed principals ++ * ++ * @param member The principal whose membership must be checked in this Group. ++ * @return true always since this is the "world" group. ++ */ ++ public boolean isMember(Principal member) { ++ return true; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/acl/manifest.mn b/org/mozilla/jss/netscape/security/acl/manifest.mn +new file mode 100644 +index 00000000..fc465961 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/acl/manifest.mn +@@ -0,0 +1,8 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++CORE_DEPTH = ../../../../../.. ++PACKAGE = org/mozilla/jss/netscape/security/acl ++MODULE = jss +diff --git a/org/mozilla/jss/netscape/security/extensions/AccessDescription.java b/org/mozilla/jss/netscape/security/extensions/AccessDescription.java +new file mode 100644 +index 00000000..48ca81e8 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/AccessDescription.java +@@ -0,0 +1,77 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++import java.io.Serializable; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.GeneralName; ++ ++public class AccessDescription implements Serializable { ++ ObjectIdentifier mOID = null; ++ GeneralName mLocation = null; ++ ++ AccessDescription(ObjectIdentifier oid, GeneralName location) { ++ mOID = oid; ++ mLocation = location; ++ } ++ ++ public ObjectIdentifier getMethod() { ++ return mOID; ++ } ++ ++ public GeneralName getLocation() { ++ return mLocation; ++ } ++ ++ /** ++ * For serialization: ++ * Note that GeneralName is not serializable. That is ++ * why we need to define our own serialization method. ++ */ ++ private void writeObject(java.io.ObjectOutputStream out) ++ throws IOException { ++ try (DerOutputStream seq = new DerOutputStream(); ++ DerOutputStream tmp = new DerOutputStream()) { ++ ++ tmp.putOID(mOID); ++ mLocation.encode(tmp); ++ seq.write(DerValue.tag_Sequence, tmp); ++ out.write(seq.toByteArray()); ++ } ++ } ++ ++ /** ++ * For serialization ++ * Note that GeneralName is not serializable. That is ++ * why we need to define our own serialization method. ++ */ ++ private void readObject(java.io.ObjectInputStream in) ++ throws IOException { ++ DerValue val = new DerValue(in); ++ DerValue seq = val.data.getDerValue(); ++ ++ mOID = seq.getOID(); ++ DerValue derLoc = val.data.getDerValue(); ++ ++ mLocation = new GeneralName(derLoc); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/AuthInfoAccessExtension.java b/org/mozilla/jss/netscape/security/extensions/AuthInfoAccessExtension.java +new file mode 100644 +index 00000000..ae84a5bf +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/AuthInfoAccessExtension.java +@@ -0,0 +1,275 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.ByteArrayInputStream; ++import java.io.ByteArrayOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.GeneralName; ++import org.mozilla.jss.netscape.security.x509.URIName; ++ ++import org.mozilla.jss.netscape.security.util.Utils; ++ ++/** ++ * This represents the authority information access extension ++ * as defined in RFC2459. ++ * ++ * id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) dod(6) ++ * internet(1) security(5) mechanisms(5) ++ * pkix(7) } } ++ * id-pe OBJECT IDENTIFIER ::= { id-pkix 1 } ++ * id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } ++ * AuthorityInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription ++ * AccessDescription ::= SEQUENCE { ++ * accessMethod OBJECT IDENTIFIER, ++ * accessLocation GeneralName ++ * } ++ * id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } ++ * id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } ++ * id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } ++ * ++ * Need to make sure the following is added to CMS.cfg: ++ * oidmap.auth_info_access.class=com.netscape.certsrv.cert.AuthInfoAccessExtension ++ * oidmap.auth_info_access.oid=1.3.6.1.5.5.7.1.1 ++ * ++ * @author thomask ++ * @version $Revision$, $Date$ ++ */ ++public class AuthInfoAccessExtension extends Extension implements CertAttrSet { ++ private static final long serialVersionUID = 7373316523212538446L; ++ public static final String NAME = "AuthInfoAccessExtension"; ++ public static final String NAME2 = "AuthorityInformationAccess"; ++ ++ public static final int OID_OCSP[] = { 1, 3, 6, 1, 5, 5, 7, 48, 1 }; ++ public static final ObjectIdentifier METHOD_OCSP = new ++ ObjectIdentifier(OID_OCSP); ++ ++ public static final int OID_CA_ISSUERS[] = { 1, 3, 6, 1, 5, 5, 7, 48, 2 }; ++ public static final ObjectIdentifier METHOD_CA_ISSUERS = new ++ ObjectIdentifier(OID_CA_ISSUERS); ++ ++ public static final int OID[] = { 1, 3, 6, 1, 5, 5, 7, 1, 1 }; ++ public static final ObjectIdentifier ID = new ObjectIdentifier(OID); ++ ++ private Vector mDesc = new Vector(); ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public AuthInfoAccessExtension(boolean critical) { ++ this.extensionId = ID; ++ this.critical = critical; ++ this.extensionValue = null; // build this when encodeThis() is called ++ } ++ ++ public AuthInfoAccessExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = ID; ++ this.critical = critical.booleanValue(); ++ this.extensionValue = ((byte[]) value).clone(); ++ decodeThis(); ++ } ++ ++ /** ++ * Sets extension attribute. ++ */ ++ public void set(String name, Object obj) throws CertificateException { ++ // NOT USED ++ } ++ ++ /** ++ * Retrieves extension attribute. ++ */ ++ public Object get(String name) throws CertificateException { ++ // NOT USED ++ return null; ++ } ++ ++ /** ++ * Deletes attribute. ++ */ ++ public void delete(String name) throws CertificateException { ++ // NOT USED ++ } ++ ++ /** ++ * Decodes this extension. ++ */ ++ public void decode(InputStream in) throws IOException { ++ // NOT USED ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ // NOT USED ++ return null; ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return NAME; ++ } ++ ++ /** ++ * Adds Access Description. ++ */ ++ public void addAccessDescription( ++ ObjectIdentifier method, ++ GeneralName gn) { ++ clearValue(); ++ mDesc.addElement(new AccessDescription(method, gn)); ++ } ++ ++ public AccessDescription getAccessDescription(int pos) { ++ return mDesc.elementAt(pos); ++ } ++ ++ /** ++ * Returns the number of access description. ++ */ ++ public int numberOfAccessDescription() { ++ return mDesc.size(); ++ } ++ ++ private void decodeThis() throws IOException { ++ DerValue val = new DerValue(this.extensionValue); ++ ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding of AuthInfoAccess extension"); ++ } ++ while (val.data.available() != 0) { ++ DerValue seq = val.data.getDerValue(); ++ ObjectIdentifier method = seq.data.getDerValue().getOID(); ++ GeneralName gn = new GeneralName(seq.data.getDerValue()); ++ ++ addAccessDescription(method, gn); ++ } ++ } ++ ++ private void encodeThis() throws IOException { ++ try (DerOutputStream seq = new DerOutputStream(); ++ DerOutputStream tmp = new DerOutputStream()) { ++ ++ for (int i = 0; i < mDesc.size(); i++) { ++ DerOutputStream tmp0 = new DerOutputStream(); ++ AccessDescription ad = mDesc.elementAt(i); ++ ++ tmp0.putOID(ad.getMethod()); ++ ad.getLocation().encode(tmp0); ++ tmp.write(DerValue.tag_Sequence, tmp0); ++ } ++ seq.write(DerValue.tag_Sequence, tmp); ++ this.extensionValue = seq.toByteArray(); ++ } ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Returns a printable representation of the AuthInfoAccess. ++ */ ++ public String toString() { ++ StringBuffer s = new StringBuffer(); ++ String b = super.toString() + "AuthInfoAccess [\n"; ++ s.append(b); ++ ++ for (int i = 0; i < mDesc.size(); i++) { ++ AccessDescription ad = mDesc.elementAt(i); ++ ++ s.append("(" + i + ")"); ++ s.append(" "); ++ s.append(ad.getMethod().toString() + " " + ad.getLocation().toString()); ++ } ++ return (s.toString() + "]\n"); ++ } ++ ++ public static void main(String[] argv) { ++ AuthInfoAccessExtension aia = new AuthInfoAccessExtension(false); ++ GeneralName ocspName = new GeneralName(new ++ URIName("http://ocsp.netscape.com")); ++ ++ aia.addAccessDescription(METHOD_OCSP, ocspName); ++ GeneralName caIssuersName = new GeneralName(new ++ URIName("http://ocsp.netscape.com")); ++ ++ aia.addAccessDescription(METHOD_CA_ISSUERS, caIssuersName); ++ ByteArrayOutputStream os = new ByteArrayOutputStream(); ++ ++ try { ++ aia.encode(os); ++ ++ System.out.println(Utils.base64encode(os.toByteArray(), true)); ++ } catch (IOException e) { ++ System.out.println(e.toString()); ++ } ++ ++ try { ++ // test serialization ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ ObjectOutputStream oos = new ObjectOutputStream(bos); ++ ++ oos.writeObject(aia); ++ ++ ByteArrayInputStream bis = new ByteArrayInputStream( ++ bos.toByteArray()); ++ ObjectInputStream ois = new ObjectInputStream(bis); ++ AuthInfoAccessExtension clone = (AuthInfoAccessExtension) ++ ois.readObject(); ++ ++ System.out.println(clone); ++ } catch (Exception e) { ++ System.out.println(e.toString()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/CertInfo.java b/org/mozilla/jss/netscape/security/extensions/CertInfo.java +new file mode 100644 +index 00000000..10ebd40e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/CertInfo.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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++import java.math.BigInteger; ++import java.security.NoSuchAlgorithmException; ++import java.security.cert.CertificateException; ++import java.util.Date; ++ ++import org.mozilla.jss.netscape.security.x509.AlgorithmId; ++import org.mozilla.jss.netscape.security.x509.CertificateAlgorithmId; ++import org.mozilla.jss.netscape.security.x509.CertificateIssuerName; ++import org.mozilla.jss.netscape.security.x509.CertificateSerialNumber; ++import org.mozilla.jss.netscape.security.x509.CertificateSubjectName; ++import org.mozilla.jss.netscape.security.x509.CertificateValidity; ++import org.mozilla.jss.netscape.security.x509.CertificateVersion; ++import org.mozilla.jss.netscape.security.x509.X500Name; ++import org.mozilla.jss.netscape.security.x509.X509CertInfo; ++ ++/** ++ * Extends X509CertInfo class so that minimal fields are initialized at ++ * creation time so an object of this type is always serializable. ++ */ ++public class CertInfo extends X509CertInfo { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -2883888348288591989L; ++ public static final CertificateSubjectName SERIALIZE_SUBJECT; ++ public static final CertificateIssuerName SERIALIZE_ISSUER; ++ public static final CertificateValidity SERIALIZE_VALIDITY; ++ public static final CertificateSerialNumber SERIALIZE_SERIALNO; ++ public static final CertificateAlgorithmId SERIALIZE_ALGOR; ++ public static final CertificateVersion FORCE_VERSION_3; ++ ++ static { ++ try { ++ // force version 3 ++ FORCE_VERSION_3 = ++ new CertificateVersion(CertificateVersion.V3); ++ SERIALIZE_SUBJECT = ++ new CertificateSubjectName( ++ new X500Name("cn=uninitialized")); ++ SERIALIZE_ISSUER = ++ new CertificateIssuerName( ++ new X500Name("cn=uninitialized")); ++ SERIALIZE_VALIDITY = ++ new CertificateValidity(new Date(0), new Date(0)); ++ SERIALIZE_SERIALNO = ++ new CertificateSerialNumber(new BigInteger("0")); ++ SERIALIZE_ALGOR = ++ new CertificateAlgorithmId(AlgorithmId.get("MD5withRSA")); ++ } catch (IOException e) { ++ // should never happen. If does, system is hosed. ++ System.out.println("**** Impossible Error encountered ****"); ++ throw new RuntimeException(e.toString()); ++ } catch (NoSuchAlgorithmException e) { ++ // should never happen. If does, system is hosed. ++ System.out.println("**** Impossible Error encountered ****"); ++ throw new RuntimeException(e.toString()); ++ } ++ } ++ ++ /** ++ * Initializes most fields required by der encoding so object will ++ * serialize properly. ++ */ ++ // XXX should write a class to use something else for serialization ++ // but this is faster and done now for the time crunch. ++ public CertInfo() { ++ super(); ++ makeSerializable(this); ++ } ++ ++ public static void makeSerializable(X509CertInfo certinfo) { ++ try { ++ // force version 3. ++ certinfo.set(X509CertInfo.VERSION, FORCE_VERSION_3); ++ ++ if (certinfo.get(X509CertInfo.SERIAL_NUMBER) == null) { ++ certinfo.set(X509CertInfo.SERIAL_NUMBER, SERIALIZE_SERIALNO); ++ } ++ if (certinfo.get(X509CertInfo.ALGORITHM_ID) == null) { ++ certinfo.set(X509CertInfo.ALGORITHM_ID, SERIALIZE_ALGOR); ++ } ++ if (certinfo.get(X509CertInfo.ISSUER) == null) { ++ certinfo.set(X509CertInfo.ISSUER, SERIALIZE_ISSUER); ++ } ++ if (certinfo.get(X509CertInfo.VALIDITY) == null) { ++ certinfo.set(X509CertInfo.VALIDITY, SERIALIZE_VALIDITY); ++ } ++ // set subject name anyway - it'll get overwritten. ++ if (certinfo.get(X509CertInfo.SUBJECT) == null) { ++ certinfo.set(X509CertInfo.SUBJECT, SERIALIZE_SUBJECT); ++ } ++ // key is set later in the request. ++ } // these exceptions shouldn't happen here unless the ++ // whole process is hosed. ++ catch (CertificateException e) { ++ } catch (IOException e) { ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/CertificateRenewalWindowExtension.java b/org/mozilla/jss/netscape/security/extensions/CertificateRenewalWindowExtension.java +new file mode 100644 +index 00000000..470a6737 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/CertificateRenewalWindowExtension.java +@@ -0,0 +1,191 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Date; ++import java.util.Enumeration; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++ ++/** ++ * This represents the CertificateRenewalWindow extension ++ * as defined in draft-thayes-cert-renewal-00 ++ * ++ * CertificateRenewalWindow ::= SEQUENCE { ++ * beginTime GeneralizedTime, ++ * endTime GeneralizedTime OPTIONAL } ++ * ++ * @author thomask ++ * @version $Revision$, $Date$ ++ */ ++public class CertificateRenewalWindowExtension extends Extension ++ implements CertAttrSet { ++ private static final long serialVersionUID = 4470220533545299271L; ++ public static final String NAME = "CertificateRenewalWindow"; ++ public static final int OID[] = { 2, 16, 840, 1, 113730, 1, 15 }; ++ public static final ObjectIdentifier ID = new ObjectIdentifier(OID); ++ ++ private Date mBeginTime = null; ++ private Date mEndTime = null; // optional ++ ++ public CertificateRenewalWindowExtension(boolean critical, Date beginTime, ++ Date endTime) throws IOException { ++ this.extensionId = ID; ++ this.critical = critical; ++ mBeginTime = beginTime; ++ mEndTime = endTime; ++ encodeThis(); ++ } ++ ++ public CertificateRenewalWindowExtension(boolean critical) { ++ this.extensionId = ID; ++ this.critical = critical; ++ this.extensionValue = null; // build this when encodeThis() is called ++ } ++ ++ public CertificateRenewalWindowExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = ID; ++ this.critical = critical.booleanValue(); ++ this.extensionValue = ((byte[]) value).clone(); ++ decodeThis(); ++ } ++ ++ public String getName() { ++ return NAME; ++ } ++ ++ /** ++ * Sets extension attribute. ++ */ ++ public void set(String name, Object obj) throws CertificateException { ++ // NOT USED ++ } ++ ++ /** ++ * Retrieves extension attribute. ++ */ ++ public Object get(String name) throws CertificateException { ++ // NOT USED ++ return null; ++ } ++ ++ /** ++ * Deletes attribute. ++ */ ++ public void delete(String name) throws CertificateException { ++ // NOT USED ++ } ++ ++ /** ++ * Decodes this extension. ++ */ ++ public void decode(InputStream in) throws IOException { ++ // NOT USED ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ // NOT USED ++ return null; ++ } ++ ++ public Date getBeginTime() { ++ return mBeginTime; ++ } ++ ++ public Date getEndTime() { ++ return mEndTime; ++ } ++ ++ public void setBeginTime(Date d) { ++ mBeginTime = d; ++ } ++ ++ public void setEndTime(Date d) { ++ mEndTime = d; ++ } ++ ++ private void decodeThis() throws IOException { ++ DerValue val = new DerValue(this.extensionValue); ++ ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding of CertificateWindow extension"); ++ } ++ while (val.data.available() != 0) { ++ if (mBeginTime == null) { ++ mBeginTime = val.data.getGeneralizedTime(); ++ } else { ++ mEndTime = val.data.getGeneralizedTime(); ++ } ++ } ++ } ++ ++ private void encodeThis() throws IOException { ++ try (DerOutputStream seq = new DerOutputStream(); ++ DerOutputStream tmp = new DerOutputStream()) { ++ ++ tmp.putGeneralizedTime(mBeginTime); ++ if (mEndTime != null) { ++ tmp.putGeneralizedTime(mEndTime); ++ } ++ seq.write(DerValue.tag_Sequence, tmp); ++ this.extensionValue = seq.toByteArray(); ++ } ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Returns a printable representation of the CertificateRenewalWindow. ++ */ ++ public String toString() { ++ String s = super.toString() + "CertificateRenewalWindow [\n"; ++ ++ s += "BeginTime: " + mBeginTime + "\n"; ++ if (mEndTime != null) { ++ s += "EndTime: " + mEndTime; ++ } ++ return (s + "]\n"); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/CertificateScopeEntry.java b/org/mozilla/jss/netscape/security/extensions/CertificateScopeEntry.java +new file mode 100644 +index 00000000..747557b8 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/CertificateScopeEntry.java +@@ -0,0 +1,103 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.x509.GeneralName; ++ ++/** ++ * This represents the CertificateScopeOfUse extension ++ * as defined in draft-thayes-cert-scope-00 ++ * ++ * CertificateScopeEntry ::= SEQUENCE { ++ * name GeneralName, -- pattern, as for NameConstraints ++ * portNumber INTEGER OPTIONAL ++ * } ++ * CertificateScopeOfUse ::= SEQUENCE OF CertificateScopeEntry ++ * ++ * @author thomask ++ * @version $Revision$, $Date$ ++ */ ++public class CertificateScopeEntry { ++ private GeneralName mGn = null; ++ private BigInt mPort = null; ++ ++ /** ++ * Constructs scope with der value. ++ */ ++ public CertificateScopeEntry(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for PolicyQualifierInfo."); ++ } ++ DerValue gn = val.data.getDerValue(); ++ ++ mGn = new GeneralName(gn); ++ if (val.data.available() != 0) { ++ mPort = val.data.getInteger(); ++ } ++ } ++ ++ /** ++ * Constructs scope wit ++ */ ++ public CertificateScopeEntry(GeneralName gn, BigInt port) { ++ mGn = gn; ++ mPort = port; // optional ++ } ++ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ mGn.encode(tmp); ++ if (mPort != null) { ++ tmp.putInteger(mPort); ++ } ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++ ++ /** ++ * Returns a GeneralName ++ */ ++ public GeneralName getGeneralName() { ++ return mGn; ++ } ++ ++ /** ++ * Returns a port ++ */ ++ public BigInt getPort() { ++ return mPort; ++ } ++ ++ /** ++ * Returns a printable representation of the CertificateRenewalWindow. ++ */ ++ public String toString() { ++ String s = super.toString() + "CertificateScopeEntry [\n"; ++ ++ s += "GeneralName: " + mGn; ++ if (mPort != null) { ++ s += "PortNumber: " + mPort; ++ } ++ return (s + "]\n"); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/CertificateScopeOfUseExtension.java b/org/mozilla/jss/netscape/security/extensions/CertificateScopeOfUseExtension.java +new file mode 100644 +index 00000000..649e6ef4 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/CertificateScopeOfUseExtension.java +@@ -0,0 +1,198 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.OIDMap; ++ ++/** ++ * This represents the CertificateScopeOfUse extension ++ * as defined in draft-thayes-cert-scope-00 ++ * ++ * CertificateScopeEntry ::= SEQUENCE { ++ * name GeneralName, -- pattern, as for NameConstraints ++ * portNumber INTEGER OPTIONAL ++ * } ++ * CertificateScopeOfUse ::= SEQUENCE OF CertificateScopeEntry ++ * ++ * @author thomask ++ * @version $Revision$, $Date$ ++ */ ++public class CertificateScopeOfUseExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 2143292831971567770L; ++ public static final String NAME = "CertificateScopeOfUse"; ++ public static final int OID[] = { 2, 16, 840, 1, 113730, 1, 17 }; ++ public static final ObjectIdentifier ID = new ObjectIdentifier(OID); ++ ++ private Vector mEntries = null; ++ ++ static { ++ try { ++ OIDMap.addAttribute(CertificateScopeOfUseExtension.class.getName(), ++ ID.toString(), NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ public CertificateScopeOfUseExtension(boolean critical, Vector scopeEntries) ++ throws IOException { ++ this.extensionId = ID; ++ this.critical = critical; ++ this.extensionValue = null; // build this when encodeThis() is called ++ mEntries = scopeEntries; ++ encodeThis(); ++ } ++ ++ public CertificateScopeOfUseExtension(boolean critical) { ++ this.extensionId = ID; ++ this.critical = critical; ++ this.extensionValue = null; // build this when encodeThis() is called ++ } ++ ++ public CertificateScopeOfUseExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = ID; ++ this.critical = critical.booleanValue(); ++ this.extensionValue = ((byte[]) value).clone(); ++ decodeThis(); ++ } ++ ++ public String getName() { ++ return NAME; ++ } ++ ++ public Vector getCertificateScopeEntries() { ++ return mEntries; ++ } ++ ++ /** ++ * Sets extension attribute. ++ */ ++ public void set(String name, Object obj) throws CertificateException { ++ // NOT USED ++ } ++ ++ /** ++ * Retrieves extension attribute. ++ */ ++ public Object get(String name) throws CertificateException { ++ // NOT USED ++ return null; ++ } ++ ++ /** ++ * Deletes attribute. ++ */ ++ public void delete(String name) throws CertificateException { ++ // NOT USED ++ } ++ ++ /** ++ * Decodes this extension. ++ */ ++ public void decode(InputStream in) throws IOException { ++ // NOT USED ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ // NOT USED ++ return null; ++ } ++ ++ private void decodeThis() throws IOException { ++ DerValue val = new DerValue(this.extensionValue); ++ ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding of CertificateWindow extension"); ++ } ++ mEntries = new Vector(); ++ while (val.data.available() != 0) { ++ mEntries.addElement(new CertificateScopeEntry( ++ val.data.getDerValue())); ++ } ++ } ++ ++ private void encodeThis() throws IOException { ++ try (DerOutputStream seq = new DerOutputStream(); ++ DerOutputStream tmp = new DerOutputStream()) { ++ ++ if (mEntries == null) ++ throw new IOException("Invalid Scope Entries"); ++ ++ for (int i = 0; i < mEntries.size(); i++) { ++ CertificateScopeEntry se = mEntries.elementAt(i); ++ ++ se.encode(tmp); ++ } ++ ++ seq.write(DerValue.tag_Sequence, tmp); ++ this.extensionValue = seq.toByteArray(); ++ } ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Returns a printable representation of the CertificateRenewalWindow. ++ */ ++ public String toString() { ++ StringBuffer s = new StringBuffer(super.toString() + "CertificateUseOfScope [\n"); ++ ++ if (mEntries != null) { ++ for (int i = 0; i < mEntries.size(); i++) { ++ CertificateScopeEntry se = mEntries.elementAt(i); ++ ++ s.append(se.toString()); ++ } ++ } ++ return (s.toString() + "]\n"); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/ExtendedKeyUsageExtension.java b/org/mozilla/jss/netscape/security/extensions/ExtendedKeyUsageExtension.java +new file mode 100644 +index 00000000..d52233b0 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/ExtendedKeyUsageExtension.java +@@ -0,0 +1,228 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.OIDMap; ++ ++/** ++ * This represents the extended key usage extension. ++ */ ++public class ExtendedKeyUsageExtension extends Extension implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 765403075764697489L; ++ public static final String OID = "2.5.29.37"; ++ public static final String NAME = OIDMap.EXT_KEY_USAGE_NAME; ++ public static final String OID_OCSPSigning = "1.3.6.1.5.5.7.3.9"; ++ public static final String OID_CODESigning = "1.3.6.1.5.5.7.3.3"; ++ ++ public static final int OID_OCSP_SIGNING_STR[] = ++ { 1, 3, 6, 1, 5, 5, 7, 3, 9 }; ++ public static final ObjectIdentifier OID_OCSP_SIGNING = new ++ ObjectIdentifier(OID_OCSP_SIGNING_STR); ++ ++ public static final int OID_CODE_SIGNING_STR[] = ++ { 1, 3, 6, 1, 5, 5, 7, 3, 3 }; ++ public static final ObjectIdentifier OID_CODE_SIGNING = new ++ ObjectIdentifier(OID_OCSP_SIGNING_STR); ++ ++ private Vector oidSet = null; ++ private byte mCached[] = null; ++ ++ static { ++ try { ++ OIDMap.addAttribute(ExtendedKeyUsageExtension.class.getName(), ++ OID, ExtendedKeyUsageExtension.NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ public ExtendedKeyUsageExtension() throws IOException { ++ this(false, null); ++ } ++ ++ public ExtendedKeyUsageExtension(boolean crit, Vector oids) throws IOException { ++ try { ++ extensionId = ObjectIdentifier.getObjectIdentifier(OID); ++ } catch (IOException e) { ++ // never here ++ } ++ critical = crit; ++ if (oids != null) { ++ oidSet = new Vector(oids); ++ } else { ++ oidSet = new Vector(); ++ } ++ encodeExtValue(); ++ } ++ ++ public ExtendedKeyUsageExtension(Boolean crit, Object byteVal) ++ throws IOException { ++ extensionId = ObjectIdentifier.getObjectIdentifier(OID); ++ critical = crit.booleanValue(); ++ extensionValue = ((byte[]) byteVal).clone(); ++ decodeThis(); ++ } ++ ++ public void setCritical(boolean newValue) { ++ if (critical != newValue) { ++ critical = newValue; ++ mCached = null; ++ } ++ } ++ ++ public Enumeration getOIDs() { ++ if (oidSet == null) ++ return null; ++ return oidSet.elements(); ++ } ++ ++ public void deleteAllOIDs() { ++ if (oidSet == null) ++ return; ++ oidSet.clear(); ++ } ++ ++ public void addOID(ObjectIdentifier oid) { ++ if (oidSet == null) { ++ oidSet = new Vector(); ++ } ++ ++ if (oidSet.contains(oid)) ++ return; ++ oidSet.addElement(oid); ++ mCached = null; ++ } ++ ++ public void encode(DerOutputStream out) throws IOException { ++ if (mCached == null) { ++ encodeExtValue(); ++ super.encode(out); ++ mCached = out.toByteArray(); ++ } ++ } ++ ++ @Override ++ public String toString() { ++ String presentation = "oid=" + ExtendedKeyUsageExtension.OID + " "; ++ ++ if (critical) { ++ presentation += "critical=true"; ++ } ++ if (extensionValue != null) { ++ StringBuffer extByteValue = new StringBuffer(" val="); ++ for (int i = 0; i < extensionValue.length; i++) { ++ extByteValue.append(extensionValue[i] + " "); ++ } ++ presentation += extByteValue.toString(); ++ } ++ return presentation; ++ } ++ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ } ++ ++ public void encode(OutputStream out) ++ throws CertificateException, IOException { ++ if (mCached == null) { ++ DerOutputStream temp = new DerOutputStream(); ++ ++ encode(temp); ++ } ++ out.write(mCached); ++ } ++ ++ public void set(String name, Object obj) ++ throws CertificateException, IOException { ++ // NOT USED ++ } ++ ++ public Object get(String name) throws CertificateException, IOException { ++ // NOT USED ++ return null; ++ } ++ ++ public Enumeration getAttributeNames() { ++ return null; ++ } ++ ++ public String getName() { ++ return NAME; ++ } ++ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ // NOT USED ++ } ++ ++ private void decodeThis() throws IOException { ++ DerValue val = new DerValue(this.extensionValue); ++ ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding of AuthInfoAccess extension"); ++ } ++ if (oidSet == null) ++ oidSet = new Vector(); ++ while (val.data.available() != 0) { ++ DerValue oidVal = val.data.getDerValue(); ++ ++ oidSet.addElement(oidVal.getOID()); ++ } ++ } ++ ++ private void encodeExtValue() throws IOException { ++ DerOutputStream out = new DerOutputStream(); ++ DerOutputStream temp = new DerOutputStream(); ++ ++ if (!oidSet.isEmpty()) { ++ Enumeration oidList = oidSet.elements(); ++ ++ try { ++ while (oidList.hasMoreElements()) { ++ temp.putOID(oidList.nextElement()); ++ } ++ } catch (IOException ex) { ++ ex.printStackTrace(); ++ } ++ } ++ ++ try { ++ out.write(DerValue.tag_Sequence, temp); ++ } catch (IOException ex) { ++ } finally { ++ out.close(); ++ } ++ ++ extensionValue = out.toByteArray(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/GenericASN1Extension.java b/org/mozilla/jss/netscape/security/extensions/GenericASN1Extension.java +new file mode 100644 +index 00000000..3fc58bac +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/GenericASN1Extension.java +@@ -0,0 +1,461 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.FileInputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.security.cert.CertificateException; ++import java.text.DateFormat; ++import java.text.ParseException; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.StringTokenizer; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.OIDMap; ++ ++/** ++ * Represent the AsnInteger Extension. ++ */ ++public class GenericASN1Extension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 8047548816784949009L; ++ ++ protected static final int MAX_ATTR = 10; ++ ++ protected static final String PROP_CRITICAL = ++ "critical"; ++ protected static final String PROP_NAME = ++ "name"; ++ protected static final String PROP_OID = ++ "oid"; ++ protected static final String PROP_PATTERN = ++ "pattern"; ++ protected static final String PROP_ATTRIBUTE = ++ "attribute"; ++ protected static final String PROP_TYPE = ++ "type"; ++ protected static final String PROP_SOURCE = ++ "source"; ++ protected static final String PROP_VALUE = ++ "value"; ++ protected static final String PROP_PREDICATE = ++ "predicate"; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ private String name; ++ public String OID = null; ++ public static Hashtable mConfig = null; ++ public String pattern = null; ++ private int index = 0; ++ ++ // Encode this value ++ private void encodeThis() ++ throws IOException, ParseException { ++ this.extensionValue = encodePattern(); ++ } ++ ++ // Encode pattern ++ private byte[] encodePattern() ++ throws IOException, ParseException { ++ DerOutputStream tmp = new DerOutputStream(); ++ String type = null; ++ String value = null; ++ String source = null; ++ while (index < pattern.length()) { ++ char ch = pattern.charAt(index); ++ switch (ch) { ++ case '{': ++ index++; ++ byte[] buff = encodePattern(); ++ tmp.putDerValue(new DerValue(buff)); ++ break; ++ case '}': ++ try (DerOutputStream os = new DerOutputStream()) { ++ os.write(DerValue.tag_Sequence, tmp); ++ return os.toByteArray(); ++ } ++ default: ++ type = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_TYPE); ++ if (type.equalsIgnoreCase("integer")) { ++ int num = Integer.parseInt(mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE)); ++ PutInteger(tmp, num); ++ } else if (type.equalsIgnoreCase("ia5string")) { ++ source = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); ++ value = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); ++ if (source.equalsIgnoreCase("file")) ++ PutIA5String(tmp, getFromFile(value)); ++ else ++ PutIA5String(tmp, value); ++ } else if (type.equalsIgnoreCase("octetstring")) { ++ source = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); ++ value = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); ++ // It should be colon seperated ASCII Hexdecimal String ++ if (source.equalsIgnoreCase("file")) ++ PutOctetString(tmp, getFromFile(value)); ++ else ++ PutOctetString(tmp, value); ++ } else if (type.equalsIgnoreCase("bmpstring")) { ++ source = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); ++ value = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); ++ if (source.equalsIgnoreCase("file")) ++ PutBMPString(tmp, getFromFile(value)); ++ else ++ PutBMPString(tmp, value); ++ } else if (type.equalsIgnoreCase("printablestring")) { ++ source = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); ++ value = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); ++ if (source.equalsIgnoreCase("file")) ++ PutPrintableString(tmp, getFromFile(value)); ++ else ++ PutPrintableString(tmp, value); ++ } else if (type.equalsIgnoreCase("visiblestring")) { ++ source = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); ++ value = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); ++ if (source.equalsIgnoreCase("file")) ++ PutVisibleString(tmp, getFromFile(value)); ++ else ++ PutVisibleString(tmp, value); ++ } else if (type.equalsIgnoreCase("utctime")) { ++ value = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); ++ PutUTCtime(tmp, value); ++ } else if (type.equalsIgnoreCase("oid")) { ++ value = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); ++ PutOID(tmp, value); ++ } else if (type.equalsIgnoreCase("boolean")) { ++ boolean bool = false; ++ String b = mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); ++ if (b.equalsIgnoreCase("true")) ++ bool = true; ++ else ++ bool = false; ++ PutBoolean(tmp, bool); ++ } else if (type.equalsIgnoreCase("null")) { ++ tmp.putNull(); ++ } else { ++ throw new ParseException("Unknown Attribute Type", 0); ++ } ++ } ++ index++; ++ } ++ ++ return tmp.toByteArray(); ++ } ++ ++ /** ++ * Create a GenericASN1Extension with the value and oid. ++ * The criticality is set to false. ++ * ++ * @param the values to be set for the extension. ++ */ ++ public GenericASN1Extension(String name, String oid, String pattern, boolean critical, ++ Hashtable config) ++ throws IOException, ParseException { ++ ObjectIdentifier tmpid = new ObjectIdentifier(oid); ++ this.name = name; ++ OID = oid; ++ mConfig = config; ++ this.pattern = pattern; ++ ++ try { ++ if (OIDMap.getName(tmpid) == null) ++ OIDMap.addAttribute("org.mozilla.jss.netscape.security.x509.GenericASN1Extension", oid, name); ++ } catch (CertificateException e) { ++ } ++ ++ this.extensionId = tmpid; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a GenericASN1Extension with the value and oid. ++ * The criticality is set to false. ++ * ++ * @param the values to be set for the extension. ++ */ ++ public GenericASN1Extension(Hashtable config) ++ throws IOException, ParseException { ++ mConfig = config; ++ ObjectIdentifier tmpid = new ObjectIdentifier(mConfig.get(PROP_OID)); ++ name = mConfig.get(PROP_NAME); ++ OID = mConfig.get(PROP_OID); ++ pattern = mConfig.get(PROP_PATTERN); ++ ++ try { ++ if (OIDMap.getName(tmpid) == null) ++ OIDMap.addAttribute("GenericASN1Extension", OID, name); ++ } catch (CertificateException e) { ++ } ++ ++ this.extensionId = tmpid; ++ this.critical = false; ++ String b = mConfig.get(PROP_CRITICAL); ++ if (b.equalsIgnoreCase("true")) ++ this.critical = true; ++ else ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public GenericASN1Extension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = new ObjectIdentifier(OID); ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ return null; ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Returns a printable representation of the GenericASN1Extension. ++ */ ++ @Override ++ public String toString() { ++ return (null); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) ++ throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ try { ++ if (this.extensionValue == null) { ++ this.extensionId = new ObjectIdentifier(OID); ++ this.critical = true; ++ encodeThis(); ++ } ++ } catch (ParseException e) { ++ } ++ ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return name; ++ } ++ ++ /** ++ * Set the name of this attribute. ++ */ ++ public void setName(String name) { ++ this.name = name; ++ } ++ ++ /** ++ * Return the OID of this attribute. ++ */ ++ public String getOID() { ++ return OID; ++ } ++ ++ /** ++ * Set the OID of this attribute. ++ */ ++ public void setOID(String oid) { ++ OID = oid; ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement("octet"); ++ ++ return (elements.elements()); ++ } ++ ++ private void PutInteger(DerOutputStream os, int number) ++ throws IOException, ParseException { ++ os.putInteger(new BigInt(number)); ++ return; ++ } ++ ++ private void PutIA5String(DerOutputStream os, String value) ++ throws IOException, ParseException { ++ os.putIA5String(value); ++ return; ++ } ++ ++ private void PutOctetString(DerOutputStream os, String value) ++ throws IOException, ParseException { ++ StringTokenizer token = new StringTokenizer(value, ":"); ++ byte[] octets = new byte[token.countTokens()]; ++ for (int i = 0; token.hasMoreElements(); i++) { ++ String num = (String) token.nextElement(); ++ octets[i] = (byte) Integer.parseInt(num, 16); ++ } ++ ++ os.putOctetString(octets); ++ return; ++ } ++ ++ private void PutBMPString(DerOutputStream os, String value) ++ throws IOException, ParseException { ++ os.putBMPString(value); ++ return; ++ } ++ ++ private void PutPrintableString(DerOutputStream os, String value) ++ throws IOException, ParseException { ++ os.putPrintableString(value); ++ return; ++ } ++ ++ private void PutVisibleString(DerOutputStream os, String value) ++ throws IOException, ParseException { ++ os.putVisibleString(value); ++ return; ++ } ++ ++ private void PutUTCtime(DerOutputStream os, String value) ++ throws IOException, ParseException { ++ DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); ++ os.putUTCTime(df.parse(value)); ++ return; ++ } ++ ++ private void PutOID(DerOutputStream os, String value) ++ throws IOException, ParseException { ++ os.putOID(new ObjectIdentifier(value)); ++ return; ++ } ++ ++ private void PutBoolean(DerOutputStream os, boolean value) ++ throws IOException, ParseException { ++ os.putBoolean(value); ++ return; ++ } ++ ++ private String getFromFile(String fname) throws IOException { ++ String s = null; ++ byte[] buff = null; ++ int i = 0; ++ int j = 0; ++ if ((fname == null) || (fname.equals(""))) { ++ throw new IOException("File name is not provided."); ++ } ++ ++ FileInputStream fis = null; ++ try { ++ fis = new FileInputStream(fname); ++ int n = 0; ++ while ((n = fis.available()) > 0) { ++ buff = new byte[n]; ++ int result = fis.read(buff); ++ if (result == -1) ++ break; ++ s = new String(buff); ++ } ++ } finally { ++ if (fis != null) { ++ fis.close(); ++ } ++ } ++ ++ if (s == null) { ++ return ""; ++ } ++ ++ for (i = 0, j = 0; j < s.length(); j++) { ++ int ch = s.charAt(j); ++ if (ch == 10 || ch == 13 || ch == 9) ++ continue; ++ i++; ++ } ++ buff = new byte[i]; ++ for (i = 0, j = 0; j < s.length(); j++) { ++ int ch = s.charAt(j); ++ if (ch == 10 || ch == 13 || ch == 9) ++ continue; ++ buff[i++] = (byte) ch; ++ } ++ s = new String(buff); ++ ++ return s; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/InhibitAnyPolicyExtension.java b/org/mozilla/jss/netscape/security/extensions/InhibitAnyPolicyExtension.java +new file mode 100644 +index 00000000..a658f724 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/InhibitAnyPolicyExtension.java +@@ -0,0 +1,183 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.OIDMap; ++ ++/** ++ * RFC3280: ++ * ++ * id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 } ++ * ++ * InhibitAnyPolicy ::= SkipCerts ++ * ++ * SkipCerts ::= INTEGER (0..MAX) ++ */ ++public class InhibitAnyPolicyExtension ++ extends Extension implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -8963439897419343166L; ++ public static final String OID = "2.5.29.54"; ++ public static final String NAME = OIDMap.EXT_INHIBIT_ANY_POLICY_NAME; ++ ++ private BigInt mSkipCerts = new BigInt(-1); ++ ++ static { ++ try { ++ OIDMap.addAttribute(InhibitAnyPolicyExtension.class.getName(), ++ OID, NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ public InhibitAnyPolicyExtension() throws IOException { ++ this(false, null); ++ } ++ ++ public InhibitAnyPolicyExtension(boolean crit, BigInt skipCerts) throws IOException { ++ try { ++ extensionId = ObjectIdentifier.getObjectIdentifier(OID); ++ } catch (IOException e) { ++ // never here ++ } ++ critical = crit; ++ mSkipCerts = skipCerts; ++ encodeExtValue(); ++ } ++ ++ public InhibitAnyPolicyExtension(Boolean crit, Object value) ++ throws IOException { ++ extensionId = ObjectIdentifier.getObjectIdentifier(OID); ++ critical = crit.booleanValue(); ++ //extensionValue = (byte[]) ((byte[]) byteVal).clone(); ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ ++ extensionValue = extValue; ++ decodeThis(); ++ } ++ ++ public void setCritical(boolean newValue) { ++ if (critical != newValue) { ++ critical = newValue; ++ } ++ } ++ ++ public BigInt getSkipCerts() { ++ return mSkipCerts; ++ } ++ ++ @Override ++ public String toString() { ++ String presentation = "ObjectId: " + OID + " "; ++ ++ if (critical) { ++ presentation += "Criticality=true"; ++ } else { ++ presentation += "Criticality=false"; ++ } ++ if (extensionValue != null) { ++ String extByteValue = " skipCerts=" + mSkipCerts; ++ ++ presentation += extByteValue; ++ } ++ return presentation; ++ } ++ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ } ++ ++ public void set(String name, Object obj) ++ throws CertificateException, IOException { ++ // NOT USED ++ } ++ ++ public Object get(String name) throws CertificateException, IOException { ++ // NOT USED ++ return null; ++ } ++ ++ public Enumeration getAttributeNames() { ++ return null; ++ } ++ ++ public String getName() { ++ return NAME; ++ } ++ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ // NOT USED ++ } ++ ++ private void decodeThis() throws IOException { ++ DerValue val = new DerValue(this.extensionValue); ++ ++ mSkipCerts = val.getInteger(); ++ } ++ ++ public void encode(OutputStream out) throws IOException { ++ try (DerOutputStream os = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ try { ++ extensionId = ObjectIdentifier.getObjectIdentifier(OID); ++ } catch (IOException e) { ++ // never here ++ } ++ os.putInteger(mSkipCerts); ++ this.extensionValue = os.toByteArray(); ++ } ++ ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ } ++ ++ private void encodeExtValue() throws IOException { ++ DerOutputStream out = new DerOutputStream(); ++ try { ++ out.putInteger(mSkipCerts); ++ } catch (IOException e) { ++ } finally { ++ out.close(); ++ } ++ extensionValue = out.toByteArray(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/KerberosName.java b/org/mozilla/jss/netscape/security/extensions/KerberosName.java +new file mode 100644 +index 00000000..cc6e1192 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/KerberosName.java +@@ -0,0 +1,136 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.ByteArrayOutputStream; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.OutputStream; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * This represents a KerberosName as defined in ++ * RFC 1510. ++ * ++ * KerberosName ::= SEQUENCE { ++ * realm [0] Realm, ++ * principalName [1] CertPrincipalName -- defined above ++ * } ++ * ++ * CertPrincipalName ::= SEQUENCE { ++ * name-type[0] INTEGER, ++ * name-string[1] SEQUENCE OF UTF8String ++ * } ++ * ++ * @author thomask ++ * @version $Revision$, $Date$ ++ */ ++public class KerberosName { ++ ++ public static final int OID[] = { 1, 3, 6, 1, 5, 2, 2 }; ++ public static final ObjectIdentifier KRB5_PRINCIPAL_NAME = new ++ ObjectIdentifier(OID); ++ ++ private String m_realm = null; ++ private int m_name_type = 0; ++ private Vector m_name_strings = null; ++ ++ public KerberosName(String realm, int name_type, Vector name_strings) { ++ m_realm = realm; ++ m_name_type = name_type; ++ m_name_strings = name_strings; ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ ++ try (DerOutputStream seq = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ DerOutputStream realm = new DerOutputStream(); ++ realm.putGeneralString(m_realm); ++ tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, (byte) 0), realm); ++ ++ DerOutputStream seq1 = new DerOutputStream(); ++ DerOutputStream tmp1 = new DerOutputStream(); ++ DerOutputStream name_type = new DerOutputStream(); ++ name_type.putInteger(new BigInt(m_name_type)); ++ tmp1.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, (byte) 0), name_type); ++ ++ DerOutputStream name_strings = new DerOutputStream(); ++ DerOutputStream name_string = new DerOutputStream(); ++ for (int i = 0; i < m_name_strings.size(); i++) { ++ name_string.putGeneralString(m_name_strings.elementAt(i)); ++ } ++ name_strings.write(DerValue.tag_SequenceOf, name_string); ++ tmp1.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, (byte) 1), name_strings); ++ seq1.write(DerValue.tag_Sequence, tmp1); ++ tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, (byte) 1), seq1); ++ ++ seq.write(DerValue.tag_Sequence, tmp); ++ out.write(seq.toByteArray()); ++ } ++ } ++ ++ public byte[] toByteArray() throws IOException { ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ encode(bos); ++ return bos.toByteArray(); ++ } ++ ++ public String toString() { ++ String strings = null; ++ for (int i = 0; i < m_name_strings.size(); i++) { ++ if (strings == null) { ++ strings = m_name_strings.elementAt(i); ++ } else { ++ strings += ","; ++ strings += m_name_strings.elementAt(i); ++ } ++ } ++ return "Realm: " + m_realm + " Name Type: " + m_name_type + " Name String(s):" + strings; ++ } ++ ++ public static void main(String[] argv) { ++ Vector strings = new Vector(); ++ strings.addElement("name"); ++ KerberosName k = new KerberosName("realm", 0, strings); ++ ++ System.out.println(k.toString()); ++ try { ++ FileOutputStream os = new FileOutputStream("/tmp/out.der"); ++ k.encode(os); ++ os.close(); ++ } catch (Exception e) { ++ System.out.println(e.toString()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/Makefile b/org/mozilla/jss/netscape/security/extensions/Makefile +new file mode 100644 +index 00000000..8fd7054b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/Makefile +@@ -0,0 +1,45 @@ ++#! gmake ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### +diff --git a/org/mozilla/jss/netscape/security/extensions/NSCertTypeExtension.java b/org/mozilla/jss/netscape/security/extensions/NSCertTypeExtension.java +new file mode 100644 +index 00000000..bd8b3fd5 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/NSCertTypeExtension.java +@@ -0,0 +1,382 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++ ++/** ++ * NSCertTypeExtension ++ * Represents Netscape Certificate Type Extension ++ * ++ *

++ * This deprecated extension, if present, defines both the purpose (e.g., encipherment, signature, certificate signing) ++ * and the application (e.g., SSL, S/Mime or Object Signing of the key contained in the certificate. ++ * ++ * @author galperin ++ * @version $Revision$, $Date$ ++ */ ++public class NSCertTypeExtension extends Extension implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 1856407688086284397L; ++ ++ // The object identifiers ++ private static final int CertType_data[] = { 2, 16, 840, 1, 113730, 1, 1 }; ++ ++ /** ++ * Identifies the particular public key used to sign the certificate. ++ */ ++ public static final ObjectIdentifier CertType_Id = new ++ ObjectIdentifier(CertType_data); ++ ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "NSCertType"; ++ public static final String SSL_CLIENT = "ssl_client"; ++ public static final String SSL_SERVER = "ssl_server"; ++ public static final String EMAIL = "email"; ++ public static final String OBJECT_SIGNING = "object_signing"; ++ public static final String SSL_CA = "ssl_ca"; ++ public static final String EMAIL_CA = "email_ca"; ++ public static final String OBJECT_SIGNING_CA = "object_signing_ca"; ++ ++ /** ++ * Attribute names. ++ */ ++ public static final int SSL_CLIENT_BIT = 0; ++ public static final int SSL_SERVER_BIT = 1; ++ public static final int EMAIL_BIT = 2; ++ public static final int OBJECT_SIGNING_BIT = 3; ++ // 4 is reserved. ++ public static final int SSL_CA_BIT = 5; ++ public static final int EMAIL_CA_BIT = 6; ++ public static final int OBJECT_SIGNING_CA_BIT = 7; ++ ++ public static final int NBITS = 8; ++ ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.NSCertType"; ++ ++ // Private data members ++ private byte[] mBitString; ++ ++ private static class MapEntry { ++ String mName; ++ int mPosition; ++ ++ MapEntry(String name, int position) { ++ mName = name; ++ mPosition = position; ++ } ++ } ++ ++ private static MapEntry[] mMapData = ++ { ++ new MapEntry(SSL_CLIENT, 0), ++ new MapEntry(SSL_SERVER, 1), ++ new MapEntry(EMAIL, 2), ++ new MapEntry(OBJECT_SIGNING, 3), ++ // note that bit 4 is reserved ++ new MapEntry(SSL_CA, 5), ++ new MapEntry(EMAIL_CA, 6), ++ new MapEntry(OBJECT_SIGNING_CA, 7), ++ }; ++ ++ private static Vector mAttributeNames = new Vector(); ++ ++ static { ++ for (int i = 0; i < mMapData.length; ++i) { ++ mAttributeNames.addElement(mMapData[i].mName); ++ } ++ } ++ ++ private static int getPosition(String name) throws CertificateException { ++ for (int i = 0; i < mMapData.length; ++i) { ++ if (name.equalsIgnoreCase(mMapData[i].mName)) ++ return mMapData[i].mPosition; ++ } ++ throw new CertificateException("Attribute name [" + name ++ + "] not recognized by" ++ + " CertAttrSet:NSCertType."); ++ } ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ try (DerOutputStream os = new DerOutputStream()) { ++ ++ os.putUnalignedBitString(mBitString); ++ this.extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Check if bit is set. ++ * ++ * @param position the position in the bit string to check. ++ */ ++ public boolean isSet(int position) { ++ int index = position / 8; ++ byte pos = (byte) (1 << (7 - (position % 8))); ++ ++ if (mBitString.length <= index) ++ return false; ++ return ((mBitString[index] & pos) != 0); ++ } ++ ++ /** ++ * Set the bit at the specified position. ++ */ ++ public void set(int position, boolean val) { ++ int index = position / 8; ++ byte pos = (byte) (1 << (7 - (position % 8))); ++ ++ if (index >= mBitString.length) { ++ byte[] tmp = new byte[index + 1]; ++ ++ System.arraycopy(mBitString, 0, tmp, 0, mBitString.length); ++ mBitString = tmp; ++ } ++ if (val) { ++ mBitString[index] |= pos; ++ } else { ++ mBitString[index] &= ~pos; ++ } ++ } ++ ++ /** ++ * Create NSCertTypeExtension from boolean array. ++ * The criticality is set to false. ++ */ ++ public NSCertTypeExtension(boolean critical, boolean[] bits) { ++ this.extensionId = CertType_Id; ++ this.critical = critical; ++ this.mBitString = new byte[0]; ++ ++ for (int i = 0; i < bits.length && i < 8; i++) { ++ set(i, bits[i]); ++ } ++ } ++ ++ public NSCertTypeExtension(boolean[] bits) { ++ this.extensionId = CertType_Id; ++ this.critical = false; ++ this.mBitString = new byte[0]; ++ ++ for (int i = 0; i < bits.length && i < 8; i++) { ++ set(i, bits[i]); ++ } ++ } ++ ++ /** ++ * Create a NSCertTypeExtension with the passed bit settings. ++ * The criticality is set to false. ++ * ++ * @param bitString the bits to be set for the extension. ++ */ ++ public NSCertTypeExtension(boolean critical, byte[] bitString) throws IOException { ++ this.mBitString = bitString; ++ this.extensionId = CertType_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ public NSCertTypeExtension(byte[] bitString) throws IOException { ++ this.mBitString = bitString; ++ this.extensionId = CertType_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public NSCertTypeExtension(Boolean critical, Object value) ++ throws IOException { ++ ++ /** ++ * Debug.trace("NSCertTypeExtension"); ++ * this.mBitString = new byte[1]; ++ * this.mBitString[0] = (byte)0x00; ++ * return; ++ **/ ++ ++ this.extensionId = CertType_Id; ++ this.critical = critical.booleanValue(); ++ byte[] extValue = ((byte[]) value).clone(); ++ ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ BitArray bitArray = val.getUnalignedBitString(); ++ if (bitArray == null) { ++ throw new IOException("Invalid Encoded DER Value"); ++ } ++ this.mBitString = bitArray.toByteArray(); ++ } ++ ++ /** ++ * Create a default key usage. ++ */ ++ public NSCertTypeExtension() { ++ this.extensionId = CertType_Id; ++ this.critical = false; ++ this.mBitString = new byte[0]; ++ try { ++ encodeThis(); ++ } catch (Exception e) { ++ } ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws CertificateException { ++ if (!(obj instanceof Boolean)) { ++ throw new CertificateException("Attribute must be of type Boolean."); ++ } ++ boolean val = ((Boolean) obj).booleanValue(); ++ ++ set(getPosition(name), val); ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws CertificateException { ++ return Boolean.valueOf(isSet(getPosition(name))); ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws CertificateException { ++ set(getPosition(name), false); ++ } ++ ++ /** ++ * Returns a printable representation of the NSCertType. ++ */ ++ public String toString() { ++ String s = super.toString() + "NSCertType [\n"; ++ ++ try { ++ ++ if (isSet(getPosition(SSL_CLIENT))) { ++ s += " SSL client"; ++ } ++ if (isSet(getPosition(SSL_SERVER))) { ++ s += " SSL server"; ++ } ++ ++ if (isSet(getPosition(EMAIL))) { ++ s += " Email"; ++ } ++ ++ if (isSet(getPosition(OBJECT_SIGNING))) { ++ s += " Object Signing"; ++ } ++ ++ if (isSet(getPosition(SSL_CA))) { ++ s += " SSL CA"; ++ } ++ ++ if (isSet(getPosition(EMAIL_CA))) { ++ s += " Email CA"; ++ } ++ ++ if (isSet(getPosition(OBJECT_SIGNING_CA))) { ++ s += " Object Signing CA"; ++ } ++ ++ } catch (Exception e) { ++ // this is reached only if there is a bug ++ throw new IllegalArgumentException(e.getMessage()); ++ } ++ ++ s += "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ encodeThis(); ++ if (this.extensionValue == null) { ++ this.extensionId = CertType_Id; ++ this.critical = true; ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ return mAttributeNames.elements(); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ public static void main(String[] argv) { ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/OCSPNoCheckExtension.java b/org/mozilla/jss/netscape/security/extensions/OCSPNoCheckExtension.java +new file mode 100644 +index 00000000..1829f5d1 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/OCSPNoCheckExtension.java +@@ -0,0 +1,155 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.OIDMap; ++ ++/** ++ * This represents the OCSPNoCheck extension. ++ */ ++public class OCSPNoCheckExtension extends Extension implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -4692759557964594790L; ++ public static final String OID = "1.3.6.1.5.5.7.48.1.5"; ++ public static final String NAME = "OCSPNoCheckExtension"; ++ ++ private byte mCached[] = null; ++ ++ static { ++ try { ++ OIDMap.addAttribute(OCSPNoCheckExtension.class.getName(), ++ OID, NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ public OCSPNoCheckExtension() throws IOException { ++ this(Boolean.FALSE); ++ } ++ ++ public OCSPNoCheckExtension(Boolean crit) throws IOException { ++ try { ++ extensionId = ObjectIdentifier.getObjectIdentifier(OCSPNoCheckExtension.OID); ++ } catch (IOException e) { ++ // never here ++ } ++ critical = crit.booleanValue(); ++ DerOutputStream tmpD = new DerOutputStream(); ++ ++ try { ++ tmpD.putNull(); ++ } catch (IOException ex) { ++ } finally { ++ tmpD.close(); ++ } ++ extensionValue = tmpD.toByteArray(); ++ } ++ ++ public OCSPNoCheckExtension(Boolean crit, Object byteVal) { ++ try { ++ extensionId = ObjectIdentifier.getObjectIdentifier(OCSPNoCheckExtension.OID); ++ } catch (IOException e) { ++ // never here ++ } ++ critical = crit.booleanValue(); ++ extensionValue = ((byte[]) byteVal).clone(); ++ } ++ ++ public void setCritical(boolean newValue) { ++ if (critical != newValue) { ++ critical = newValue; ++ mCached = null; ++ } ++ } ++ ++ public void encode(DerOutputStream out) throws IOException { ++ if (mCached == null) { ++ super.encode(out); ++ mCached = out.toByteArray(); ++ } ++ } ++ ++ @Override ++ public String toString() { ++ String presentation = "oid=" + OID + " "; ++ ++ if (critical) { ++ presentation += "critical=true"; ++ } ++ if (extensionValue != null) { ++ StringBuffer extByteValue = new StringBuffer(" val="); ++ for (int i = 0; i < extensionValue.length; i++) { ++ extByteValue.append(extensionValue[i] + " "); ++ } ++ presentation += extByteValue.toString(); ++ } ++ return presentation; ++ } ++ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ // NOT USED ++ } ++ ++ public void encode(OutputStream out) ++ throws CertificateException, IOException { ++ if (mCached == null) { ++ DerOutputStream temp = new DerOutputStream(); ++ ++ encode(temp); ++ } ++ out.write(mCached); ++ } ++ ++ public void set(String name, Object obj) ++ throws CertificateException, IOException { ++ // NOT USED ++ } ++ ++ public Object get(String name) throws CertificateException, IOException { ++ // NOT USED ++ return null; ++ } ++ ++ public Enumeration getAttributeNames() { ++ // NOT USED ++ return null; ++ } ++ ++ public String getName() { ++ return NAME; ++ } ++ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ // NOT USED ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/PresenceServerExtension.java b/org/mozilla/jss/netscape/security/extensions/PresenceServerExtension.java +new file mode 100644 +index 00000000..b4e9f698 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/PresenceServerExtension.java +@@ -0,0 +1,341 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.ByteArrayOutputStream; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++ ++public class PresenceServerExtension extends Extension implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -6333109673043357921L; ++ private boolean mCritical; ++ private int mVersion = 0; ++ private String mStreetAddress = null; ++ private String mTelephoneNumber = null; ++ private String mRFC822Name = null; ++ private String mID = null; ++ private String mHostName = null; ++ private int mPortNumber = 0; ++ private int mMaxUsers = 0; ++ private int mServiceLevel = 0; ++ ++ public static final String OID = "2.16.840.1.113730.1.18"; ++ ++ /* ++ public PresenceServerExtension() ++ { ++ } ++ */ ++ ++ public PresenceServerExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = new ObjectIdentifier(OID); ++ this.critical = critical.booleanValue(); ++ this.extensionValue = ((byte[]) value).clone(); ++ decodeThis(); ++ } ++ ++ public PresenceServerExtension( ++ boolean critical, ++ int version, ++ String streetAddress, ++ String telephoneNumber, ++ String rfc822Name, ++ String ID, ++ String hostName, ++ int portNumber, ++ int maxUsers, ++ int serviceLevel) ++ throws IOException { ++ mCritical = critical; ++ mVersion = version; ++ mStreetAddress = streetAddress; ++ mTelephoneNumber = telephoneNumber; ++ mRFC822Name = rfc822Name; ++ mID = ID; ++ mHostName = hostName; ++ mPortNumber = portNumber; ++ mMaxUsers = maxUsers; ++ mServiceLevel = serviceLevel; ++ ++ this.extensionId = new ObjectIdentifier(OID); ++ this.critical = mCritical; ++ encodeThis(); ++ } ++ ++ public int getVersion() { ++ return mVersion; ++ } ++ ++ public String getStreetAddress() { ++ return mStreetAddress; ++ } ++ ++ public String getTelephoneNumber() { ++ return mTelephoneNumber; ++ } ++ ++ public String getRFC822() { ++ return mRFC822Name; ++ } ++ ++ public String getID() { ++ return mID; ++ } ++ ++ public String getHostName() { ++ return mHostName; ++ } ++ ++ public int getPortNumber() { ++ return mPortNumber; ++ } ++ ++ public int getMaxUsers() { ++ return mMaxUsers; ++ } ++ ++ public int getServiceLevel() { ++ return mServiceLevel; ++ } ++ ++ public void encodeThis() throws IOException { ++ try (DerOutputStream out = new DerOutputStream()) { ++ DerOutputStream temp = new DerOutputStream(); ++ temp.putInteger(new BigInt(mVersion)); ++ temp.putOctetString(mStreetAddress.getBytes()); ++ temp.putOctetString(mTelephoneNumber.getBytes()); ++ temp.putOctetString(mRFC822Name.getBytes()); ++ temp.putOctetString(mID.getBytes()); ++ temp.putOctetString(mHostName.getBytes()); ++ temp.putInteger(new BigInt(mPortNumber)); ++ temp.putInteger(new BigInt(mMaxUsers)); ++ temp.putInteger(new BigInt(mServiceLevel)); ++ out.write(DerValue.tag_Sequence, temp); ++ this.extensionValue = out.toByteArray(); ++ } ++ } ++ ++ public void decodeThis() throws IOException { ++ DerInputStream val = new DerInputStream(this.extensionValue); ++ byte data[] = null; ++ DerValue seq[] = val.getSequence(0); ++ ++ mVersion = seq[0].getInteger().toInt(); ++ data = null; ++ if (seq[1].length() > 0) { ++ data = seq[1].getOctetString(); ++ } ++ if (data == null) { ++ mStreetAddress = ""; ++ } else { ++ mStreetAddress = new String(data); ++ } ++ data = null; ++ if (seq[2].length() > 0) ++ data = seq[2].getOctetString(); ++ if (data == null) { ++ mTelephoneNumber = ""; ++ } else { ++ mTelephoneNumber = new String(data); ++ } ++ data = null; ++ if (seq[3].length() > 0) ++ data = seq[3].getOctetString(); ++ if (data == null) { ++ mRFC822Name = ""; ++ } else { ++ mRFC822Name = new String(data); ++ } ++ data = null; ++ if (seq[4].length() > 0) ++ data = seq[4].getOctetString(); ++ if (data == null) { ++ mID = ""; ++ } else { ++ mID = new String(data); ++ } ++ data = null; ++ if (seq[5].length() > 0) ++ data = seq[5].getOctetString(); ++ if (data == null) { ++ mHostName = ""; ++ } else { ++ mHostName = new String(data); ++ } ++ mPortNumber = seq[6].getInteger().toInt(); ++ mMaxUsers = seq[7].getInteger().toInt(); ++ mServiceLevel = seq[8].getInteger().toInt(); ++ } ++ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ } ++ ++ public void encode(OutputStream out) ++ throws CertificateException, IOException { ++ DerOutputStream dos = new DerOutputStream(); ++ super.encode(dos); ++ out.write(dos.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ return null; ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ public Enumeration getAttributeNames() { ++ return null; ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return "PresenceServerExtension"; ++ } ++ ++ /** ++ * Set the name of this attribute. ++ */ ++ public void setName(String name) { ++ } ++ ++ /** ++ * Return the OID of this attribute. ++ */ ++ public String getOID() { ++ return OID; ++ } ++ ++ /** ++ * Set the OID of this attribute. ++ */ ++ public void setOID(String oid) { ++ } ++ ++ public static void main(String args[]) { ++ /* ++ 0 30 115: SEQUENCE { ++ 2 06 9: OBJECT IDENTIFIER '2 16 840 1 113730 1 100' ++ 13 04 102: OCTET STRING, encapsulates { ++ 15 30 100: SEQUENCE { ++ 17 02 1: INTEGER 0 ++ 20 04 31: OCTET STRING ++ : 34 30 31 45 20 4D 69 64 64 6C 65 66 69 65 6C 64 ++ : 20 52 64 2E 2C 4D 56 2C 43 41 39 34 30 34 31 ++ 53 04 12: OCTET STRING ++ : 36 35 30 2D 31 31 31 2D 31 31 31 31 ++ 67 04 18: OCTET STRING ++ : 61 64 6D 69 6E 40 6E 65 74 73 63 61 70 65 2E 63 ++ : 6F 6D ++ 87 04 10: OCTET STRING ++ : 70 73 2D 63 61 70 69 74 6F 6C ++ 99 04 7: OCTET STRING ++ : 63 61 70 69 74 6F 6C ++ 108 02 1: INTEGER 80 ++ 111 02 1: INTEGER 10 ++ 114 02 1: INTEGER 1 ++ : } ++ : } ++ : } ++ */ ++ ByteArrayOutputStream dos = null; ++ FileOutputStream fos = null; ++ try { ++ boolean critical = false; ++ int version = 1; ++ String streetAddress = "401E Middlefield Rd.,MV,CA94041"; ++ String telephoneNumber = "650-111-1111"; ++ String rfc822Name = "admin@netscape.com"; ++ String ID = "ps-capitol"; ++ String hostName = "capitol"; ++ int portNumber = 80; ++ int maxUsers = 10; ++ int serviceLevel = 1; ++ ++ PresenceServerExtension ext = new PresenceServerExtension( ++ critical, ++ version, streetAddress, telephoneNumber, ++ rfc822Name, ID, hostName, portNumber, ++ maxUsers, serviceLevel); ++ ++ // encode ++ ++ dos = new ByteArrayOutputStream(); ++ ext.encode(dos); ++ fos = new FileOutputStream("pse.der"); ++ fos.write(dos.toByteArray()); ++ Extension ext1 = new Extension(new DerValue(dos.toByteArray())); ++ ++ @SuppressWarnings("unused") ++ PresenceServerExtension ext2 = new PresenceServerExtension( ++ Boolean.valueOf(false), ext1.getExtensionValue()); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } catch (CertificateException e) { ++ e.printStackTrace(); ++ } finally { ++ if (dos != null) { ++ try { ++ dos.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ if (fos != null) { ++ try { ++ fos.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/SubjectInfoAccessExtension.java b/org/mozilla/jss/netscape/security/extensions/SubjectInfoAccessExtension.java +new file mode 100644 +index 00000000..e6d9dee1 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/SubjectInfoAccessExtension.java +@@ -0,0 +1,255 @@ ++// --- 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 org.mozilla.jss.netscape.security.extensions; ++ ++import java.io.ByteArrayInputStream; ++import java.io.ByteArrayOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.GeneralName; ++import org.mozilla.jss.netscape.security.x509.URIName; ++ ++import org.mozilla.jss.netscape.security.util.Cert; ++import org.mozilla.jss.netscape.security.util.Utils; ++ ++/** ++ * This represents the subject information access extension ++ * as defined in RFC3280. ++ * ++ * @author thomask ++ * @version $Revision$, $Date$ ++ */ ++public class SubjectInfoAccessExtension extends Extension implements CertAttrSet { ++ private static final long serialVersionUID = 7237321566602583325L; ++ ++ public static final String NAME = "SubjectInfoAccessExtension"; ++ ++ public static final int OID_OCSP[] = { 1, 3, 6, 1, 5, 5, 7, 48, 1 }; ++ public static final ObjectIdentifier METHOD_OCSP = new ++ ObjectIdentifier(OID_OCSP); ++ ++ public static final int OID_CA_ISSUERS[] = { 1, 3, 6, 1, 5, 5, 7, 48, 2 }; ++ public static final ObjectIdentifier METHOD_CA_ISSUERS = new ++ ObjectIdentifier(OID_CA_ISSUERS); ++ ++ public static final int OID[] = { 1, 3, 6, 1, 5, 5, 7, 1, 11 }; ++ public static final ObjectIdentifier ID = new ObjectIdentifier(OID); ++ ++ private Vector mDesc = new Vector(); ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public SubjectInfoAccessExtension(boolean critical) { ++ this.extensionId = ID; ++ this.critical = critical; ++ this.extensionValue = null; // build this when encodeThis() is called ++ } ++ ++ public SubjectInfoAccessExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = ID; ++ this.critical = critical.booleanValue(); ++ this.extensionValue = ((byte[]) value).clone(); ++ decodeThis(); ++ } ++ ++ /** ++ * Sets extension attribute. ++ */ ++ public void set(String name, Object obj) throws CertificateException { ++ // NOT USED ++ } ++ ++ /** ++ * Retrieves extension attribute. ++ */ ++ public Object get(String name) throws CertificateException { ++ // NOT USED ++ return null; ++ } ++ ++ /** ++ * Deletes attribute. ++ */ ++ public void delete(String name) throws CertificateException { ++ // NOT USED ++ } ++ ++ /** ++ * Decodes this extension. ++ */ ++ public void decode(InputStream in) throws IOException { ++ // NOT USED ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ // NOT USED ++ return null; ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return NAME; ++ } ++ ++ /** ++ * Adds Access Description. ++ */ ++ public void addAccessDescription( ++ ObjectIdentifier method, ++ GeneralName gn) { ++ clearValue(); ++ mDesc.addElement(new AccessDescription(method, gn)); ++ } ++ ++ public AccessDescription getAccessDescription(int pos) { ++ return mDesc.elementAt(pos); ++ } ++ ++ /** ++ * Returns the number of access description. ++ */ ++ public int numberOfAccessDescription() { ++ return mDesc.size(); ++ } ++ ++ private void decodeThis() throws IOException { ++ DerValue val = new DerValue(this.extensionValue); ++ ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding of AuthInfoAccess extension"); ++ } ++ while (val.data.available() != 0) { ++ DerValue seq = val.data.getDerValue(); ++ ObjectIdentifier method = seq.data.getDerValue().getOID(); ++ GeneralName gn = new GeneralName(seq.data.getDerValue()); ++ ++ addAccessDescription(method, gn); ++ } ++ } ++ ++ private void encodeThis() throws IOException { ++ try (DerOutputStream seq = new DerOutputStream(); ++ DerOutputStream tmp = new DerOutputStream()) { ++ ++ for (int i = 0; i < mDesc.size(); i++) { ++ DerOutputStream tmp0 = new DerOutputStream(); ++ AccessDescription ad = mDesc.elementAt(i); ++ ++ tmp0.putOID(ad.getMethod()); ++ ad.getLocation().encode(tmp0); ++ tmp.write(DerValue.tag_Sequence, tmp0); ++ } ++ seq.write(DerValue.tag_Sequence, tmp); ++ this.extensionValue = seq.toByteArray(); ++ } ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Returns a printable representation of the AuthInfoAccess. ++ */ ++ public String toString() { ++ StringBuffer s=new StringBuffer(super.toString() + "AuthInfoAccess [\n"); ++ ++ for (int i = 0; i < mDesc.size(); i++) { ++ AccessDescription ad = mDesc.elementAt(i); ++ s.append( "(" + i + ")"); ++ s.append(" "); ++ s.append(ad.getMethod().toString() + " " + ad.getLocation().toString()); ++ } ++ return (s.toString() + "]\n"); ++ } ++ ++ public static void main(String[] argv) { ++ AuthInfoAccessExtension aia = new AuthInfoAccessExtension(false); ++ GeneralName ocspName = new GeneralName(new ++ URIName("http://ocsp.netscape.com")); ++ ++ aia.addAccessDescription(METHOD_OCSP, ocspName); ++ GeneralName caIssuersName = new GeneralName(new ++ URIName("http://ocsp.netscape.com")); ++ ++ aia.addAccessDescription(METHOD_CA_ISSUERS, caIssuersName); ++ ByteArrayOutputStream os = new ByteArrayOutputStream(); ++ ++ try { ++ aia.encode(os); ++ ++ System.out.println(Utils.base64encode(os.toByteArray(), true)); ++ } catch (IOException e) { ++ System.out.println(e.toString()); ++ } ++ ++ try { ++ // test serialization ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ ObjectOutputStream oos = new ObjectOutputStream(bos); ++ ++ oos.writeObject(aia); ++ ++ ByteArrayInputStream bis = new ByteArrayInputStream( ++ bos.toByteArray()); ++ ObjectInputStream ois = new ObjectInputStream(bis); ++ AuthInfoAccessExtension clone = (AuthInfoAccessExtension) ++ ois.readObject(); ++ ++ System.out.println(clone); ++ } catch (Exception e) { ++ System.out.println(e.toString()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/extensions/manifest.mn b/org/mozilla/jss/netscape/security/extensions/manifest.mn +new file mode 100644 +index 00000000..c9ba7382 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/extensions/manifest.mn +@@ -0,0 +1,8 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++CORE_DEPTH = ../../../../../.. ++PACKAGE = org/mozilla/jss/netscape/security/extensions ++MODULE = jss +diff --git a/org/mozilla/jss/netscape/security/manifest.mn b/org/mozilla/jss/netscape/security/manifest.mn +new file mode 100644 +index 00000000..a6f16ad4 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/manifest.mn +@@ -0,0 +1,12 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++CORE_DEPTH = ../../../../.. ++ ++MODULE = jss ++ ++DIRS = extensions \ ++ x509 \ ++ $(NULL) +diff --git a/org/mozilla/jss/netscape/security/pkcs/ContentInfo.java b/org/mozilla/jss/netscape/security/pkcs/ContentInfo.java +new file mode 100644 +index 00000000..a5effa66 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/ContentInfo.java +@@ -0,0 +1,155 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * A ContentInfo type, as defined in PKCS#7. ++ * ++ * @version 1.12 ++ * @author Benjamin Renaud ++ */ ++ ++public class ContentInfo { ++ ++ // pkcs7 pre-defined content types ++ private static int[] pkcs7 = { 1, 2, 840, 113549, 1, 7 }; ++ private static int[] data = { 1, 2, 840, 113549, 1, 7, 1 }; ++ private static int[] sdata = { 1, 2, 840, 113549, 1, 7, 2 }; ++ private static int[] edata = { 1, 2, 840, 113549, 1, 7, 3 }; ++ private static int[] sedata = { 1, 2, 840, 113549, 1, 7, 4 }; ++ private static int[] ddata = { 1, 2, 840, 113549, 1, 7, 5 }; ++ private static int[] crdata = { 1, 2, 840, 113549, 1, 7, 6 }; ++ ++ public static final ObjectIdentifier PKCS7_OID = ++ new ObjectIdentifier(pkcs7); ++ ++ public static final ObjectIdentifier DATA_OID = ++ new ObjectIdentifier(data); ++ ++ public static final ObjectIdentifier SIGNED_DATA_OID = ++ new ObjectIdentifier(sdata); ++ ++ public static final ObjectIdentifier ENVELOPED_DATA_OID = ++ new ObjectIdentifier(edata); ++ ++ public static final ObjectIdentifier SIGNED_AND_ENVELOPED_DATA_OID = ++ new ObjectIdentifier(sedata); ++ ++ public static final ObjectIdentifier DIGESTED_DATA_OID = ++ new ObjectIdentifier(ddata); ++ ++ public static final ObjectIdentifier ENCRYPTED_DATA_OID = ++ new ObjectIdentifier(crdata); ++ ++ ObjectIdentifier contentType; ++ DerValue content; // OPTIONAL ++ ++ public ContentInfo(ObjectIdentifier contentType, DerValue content) { ++ this.contentType = contentType; ++ this.content = content; ++ } ++ ++ /** ++ * Make a contentInfo of type data. ++ */ ++ public ContentInfo(byte[] bytes) { ++ DerValue octetString = new DerValue(DerValue.tag_OctetString, bytes); ++ this.contentType = DATA_OID; ++ this.content = octetString; ++ } ++ ++ public ContentInfo(DerInputStream derin) ++ throws IOException, ParsingException { ++ DerInputStream disType; ++ DerInputStream disTaggedContent; ++ DerValue type; ++ DerValue taggedContent; ++ DerValue[] typeAndContent; ++ DerValue[] contents; ++ ++ typeAndContent = derin.getSequence(2); ++ ++ // Parse the content type ++ type = typeAndContent[0]; ++ disType = new DerInputStream(type.toByteArray()); ++ contentType = disType.getOID(); ++ ++ // Parse the content (OPTIONAL field). ++ // Skip the [0] EXPLICIT tag by pretending that the content is the one ++ // and only element in an implicitly tagged set ++ if (typeAndContent.length > 1) { // content is OPTIONAL ++ taggedContent = typeAndContent[1]; ++ disTaggedContent = new DerInputStream(taggedContent.toByteArray()); ++ contents = disTaggedContent.getSet(1, true); ++ content = contents[0]; ++ } ++ } ++ ++ public DerValue getContent() { ++ return content; ++ } ++ ++ public byte[] getData() throws IOException { ++ if (contentType.equals(DATA_OID)) { ++ return content.getOctetString(); ++ } ++ throw new IOException("content type is not DATA: " + contentType); ++ } ++ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream contentDerCode; ++ DerOutputStream seq; ++ DerValue taggedContent; ++ ++ contentDerCode = new DerOutputStream(); ++ content.encode(contentDerCode); ++ // Add the [0] EXPLICIT tag in front of the content encoding ++ taggedContent = new DerValue((byte) 0xA0, ++ contentDerCode.toByteArray()); ++ ++ seq = new DerOutputStream(); ++ seq.putOID(contentType); ++ seq.putDerValue(taggedContent); ++ ++ out.write(DerValue.tag_Sequence, seq); ++ } ++ ++ /** ++ * Returns a byte array representation of the data held in ++ * the content field. ++ */ ++ public byte[] getContentBytes() throws IOException { ++ DerInputStream dis = new DerInputStream(content.toByteArray()); ++ return dis.getOctetString(); ++ } ++ ++ public String toString() { ++ String out = ""; ++ ++ out += "Content Info Sequence\n\tContent type: " + contentType + "\n"; ++ out += "\tContent: " + content; ++ return out; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/EncodingException.java b/org/mozilla/jss/netscape/security/pkcs/EncodingException.java +new file mode 100644 +index 00000000..27874a44 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/EncodingException.java +@@ -0,0 +1,33 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++public class EncodingException extends Exception { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -6126764125859196917L; ++ ++ public EncodingException() { ++ super(); ++ } ++ ++ public EncodingException(String s) { ++ super(s); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/Makefile b/org/mozilla/jss/netscape/security/pkcs/Makefile +new file mode 100644 +index 00000000..8fd7054b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/Makefile +@@ -0,0 +1,45 @@ ++#! gmake ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS10.java b/org/mozilla/jss/netscape/security/pkcs/PKCS10.java +new file mode 100644 +index 00000000..1993037d +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS10.java +@@ -0,0 +1,368 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.IOException; ++import java.io.PrintStream; ++import java.security.InvalidKeyException; ++import java.security.NoSuchAlgorithmException; ++import java.security.PublicKey; ++import java.security.Signature; ++import java.security.SignatureException; ++import java.security.cert.CertificateException; ++ ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.x509.AlgorithmId; ++import org.mozilla.jss.netscape.security.x509.X500Name; ++import org.mozilla.jss.netscape.security.x509.X500Signer; ++import org.mozilla.jss.netscape.security.x509.X509Key; ++import org.mozilla.jss.netscape.security.util.Utils; ++/** ++ * PKCS #10 certificate requests are created and sent to Certificate ++ * Authorities, which then create X.509 certificates and return them to ++ * the entity which created the certificate request. These cert requests ++ * basically consist of the subject's X.500 name and public key, signed ++ * using the corresponding private key. ++ * ++ * The ASN.1 syntax for a Certification Request is: ++ * ++ *

++ * CertificationRequest ::= SEQUENCE {
++ *    certificationRequestInfo CertificationRequestInfo,
++ *    signatureAlgorithm       SignatureAlgorithmIdentifier,
++ *    signature                Signature
++ *  }
++ *
++ * SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
++ * Signature ::= BIT STRING
++ *
++ * CertificationRequestInfo ::= SEQUENCE {
++ *    version                 Version,
++ *    subject                 Name,
++ *    subjectPublicKeyInfo    SubjectPublicKeyInfo,
++ *    attributes [0] IMPLICIT Attributes
++ * }
++ * Attributes ::= SET OF Attribute
++ * 
++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.28 ++ */ ++public class PKCS10 { ++ /** ++ * Constructs an unsigned PKCS #10 certificate request. Before this ++ * request may be used, it must be encoded and signed. Then it ++ * must be retrieved in some conventional format (e.g. string). ++ * ++ * @param publicKey the public key that should be placed ++ * into the certificate generated by the CA. ++ */ ++ public PKCS10(X509Key publicKey) { ++ subjectPublicKeyInfo = publicKey; ++ attributeSet = new PKCS10Attributes(); ++ } ++ ++ /** ++ * Constructs an unsigned PKCS #10 certificate request. Before this ++ * request may be used, it must be encoded and signed. Then it ++ * must be retrieved in some conventional format (e.g. string). ++ * ++ * @param publicKey the public key that should be placed ++ * into the certificate generated by the CA. ++ * @param attributes additonal set of PKCS10 attributes requested ++ * for in the certificate. ++ */ ++ public PKCS10(X509Key publicKey, PKCS10Attributes attributes) { ++ subjectPublicKeyInfo = publicKey; ++ if (attributes != null) ++ attributeSet = attributes; ++ else ++ attributeSet = new PKCS10Attributes(); ++ } ++ ++ /** ++ * Parses an encoded, signed PKCS #10 certificate request, verifying ++ * the request's signature as it does so. This constructor would ++ * typically be used by a Certificate Authority, from which a new ++ * certificate would then be constructed. ++ * ++ * @param data the DER-encoded PKCS #10 request. ++ * @param sigver boolean specifies signature verification enabled or not ++ * @exception IOException for low level errors reading the data ++ * @exception SignatureException when the signature is invalid ++ * @exception NoSuchAlgorithmException when the signature ++ * algorithm is not supported in this environment ++ */ ++ public PKCS10(byte data[], boolean sigver) ++ throws IOException, SignatureException, NoSuchAlgorithmException, java.security.NoSuchProviderException { ++ DerInputStream in; ++ DerValue seq[]; ++ AlgorithmId id; ++ byte sigData[]; ++ Signature sig; ++ ++ String method = "PKCS10: PKCS10: "; ++ String msg = ""; ++ ++ System.out.println(method + "begins"); ++ if (data == null) { ++ throw new IllegalArgumentException(method + "param data cann't be null"); ++ } ++ certificateRequest = data; ++ ++ // ++ // Outer sequence: request, signature algorithm, signature. ++ // Parse, and prepare to verify later. ++ // ++ in = new DerInputStream(data); ++ seq = in.getSequence(3); ++ if (seq == null) { ++ throw new IllegalArgumentException(method + "in.getSequence null"); ++ } ++ ++ if (seq.length != 3) ++ throw new IllegalArgumentException(method + "not a PKCS #10 request"); ++ ++ data = seq[0].toByteArray(); // reusing this variable ++ certRequestInfo = seq[0].toByteArray(); // make a copy ++ id = AlgorithmId.parse(seq[1]); ++ sigData = seq[2].getBitString(); ++ ++ // ++ // Inner sequence: version, name, key, attributes ++ // ++ @SuppressWarnings("unused") ++ BigInt serial = seq[0].data.getInteger(); // consume serial ++ ++ /* ++ if (serial.toInt () != 0) ++ throw new IllegalArgumentException ("not PKCS #10 v1"); ++ */ ++ ++ subject = new X500Name(seq[0].data); ++ msg = "Request Subject: " + subject + ": "; ++ ++ byte val1[] = seq[0].data.getDerValue().toByteArray(); ++ subjectPublicKeyInfo = X509Key.parse(new DerValue(val1)); ++ PublicKey publicKey = X509Key.parsePublicKey(new DerValue(val1)); ++ if (publicKey == null) { ++ System.out.println(method + msg + "publicKey null"); ++ throw new SignatureException (method + msg + "publicKey null"); ++ } ++ ++ // Cope with a somewhat common illegal PKCS #10 format ++ if (seq[0].data.available() != 0) { ++ attributeSet = new PKCS10Attributes(seq[0].data); ++ } else { ++ attributeSet = new PKCS10Attributes(); ++ } ++ ++ // ++ // OK, we parsed it all ... validate the signature using the ++ // key and signature algorithm we found. ++ // temporary commented out ++ try { ++ String idName = id.getName(); ++ if (idName.equals("MD5withRSA")) ++ idName = "MD5/RSA"; ++ else if (idName.equals("MD2withRSA")) ++ idName = "MD2/RSA"; ++ else if (idName.equals("SHA1withRSA")) ++ idName = "SHA1/RSA"; ++ else if (idName.equals("SHA1withDSA")) ++ idName = "SHA1/DSA"; ++ else if (idName.equals("SHA256withRSA")) ++ idName = "SHA256/RSA"; ++ else if (idName.equals("SHA384withRSA")) ++ idName = "SHA384/RSA"; ++ else if (idName.equals("SHA512withRSA")) ++ idName = "SHA512/RSA"; ++ else if (idName.equals("SHA1withEC")) ++ idName = "SHA1/EC"; ++ else if (idName.equals("SHA256withEC")) ++ idName = "SHA256/EC"; ++ else if (idName.equals("SHA384withEC")) ++ idName = "SHA384/EC"; ++ else if (idName.equals("SHA512withEC")) ++ idName = "SHA512/EC"; ++ ++ if (sigver) { ++ sig = Signature.getInstance(idName, "Mozilla-JSS"); ++ ++ sig.initVerify(publicKey); ++ sig.update(data); ++ if (!sig.verify(sigData)) { ++ System.out.println(method + msg + "sig.verify() failed"); ++ throw new SignatureException(method + msg + "Invalid PKCS #10 signature"); ++ } ++ } ++ } catch (InvalidKeyException e) { ++ System.out.println(method + msg + e.toString()); ++ throw new SignatureException(method + msg + "invalid key"); ++ } ++ System.out.println(method + "ends"); ++ } ++ ++ public PKCS10(byte data[]) ++ throws IOException, SignatureException, NoSuchAlgorithmException, java.security.NoSuchProviderException { ++ this(data, true); ++ } ++ ++ /** ++ * Create the signed certificate request. This will later be ++ * retrieved in either string or binary format. ++ * ++ * @param requester identifies the signer (by X.500 name) ++ * and provides the private key used to sign. ++ * @exception IOException on errors. ++ * @exception CertificateException on certificate handling errors. ++ * @exception SignatureException on signature handling errors. ++ */ ++ public void encodeAndSign(X500Signer requester) ++ throws CertificateException, IOException, SignatureException { ++ DerOutputStream out, scratch; ++ byte certificateRequestInfo[]; ++ byte sig[]; ++ ++ if (certificateRequest != null) ++ throw new SignatureException("request is already signed"); ++ ++ subject = requester.getSigner(); ++ ++ /* ++ * Encode cert request info, wrap in a sequence for signing ++ */ ++ scratch = new DerOutputStream(); ++ scratch.putInteger(new BigInt(0)); // version zero ++ subject.encode(scratch); // X.500 name ++ subjectPublicKeyInfo.encode(scratch); // public key ++ attributeSet.encode(scratch); ++ ++ out = new DerOutputStream(); ++ out.write(DerValue.tag_Sequence, scratch); // wrap it! ++ certificateRequestInfo = out.toByteArray(); ++ scratch = out; ++ ++ /* ++ * Sign it ... ++ */ ++ requester.update(certificateRequestInfo, 0, ++ certificateRequestInfo.length); ++ sig = requester.sign(); ++ ++ /* ++ * Build guts of SIGNED macro ++ */ ++ requester.getAlgorithmId().encode(scratch); // sig algorithm ++ scratch.putBitString(sig); // sig ++ ++ /* ++ * Wrap those guts in a sequence ++ */ ++ out = new DerOutputStream(); ++ out.write(DerValue.tag_Sequence, scratch); ++ certificateRequest = out.toByteArray(); ++ } ++ ++ /** ++ * Returns the subject's name. ++ */ ++ public X500Name getSubjectName() { ++ return subject; ++ } ++ ++ /** ++ * Returns the subject's public key. ++ */ ++ public X509Key getSubjectPublicKeyInfo() { ++ return subjectPublicKeyInfo; ++ } ++ ++ /** ++ * Returns the additional attributes requested. ++ */ ++ public PKCS10Attributes getAttributes() { ++ return attributeSet; ++ } ++ ++ /** ++ * Returns the encoded and signed certificate request as a ++ * DER-encoded byte array. ++ * ++ * @return the certificate request, or null if encodeAndSign() ++ * has not yet been called. ++ */ ++ public byte[] toByteArray() { ++ return certificateRequest; ++ } ++ ++ /** ++ * Prints an E-Mailable version of the certificate request on the print ++ * stream passed. The format is a common base64 encoded one, supported ++ * by most Certificate Authorities because Netscape web servers have ++ * used this for some time. Some certificate authorities expect some ++ * more information, in particular contact information for the web ++ * server administrator. ++ * ++ * @param out the print stream where the certificate request ++ * will be printed. ++ * @exception IOException when an output operation failed ++ * @exception SignatureException when the certificate request was ++ * not yet signed. ++ */ ++ public void print(PrintStream out) ++ throws IOException, SignatureException { ++ if (certificateRequest == null) ++ throw new SignatureException("Cert request was not signed"); ++ ++ out.println("-----BEGIN NEW CERTIFICATE REQUEST-----"); ++ out.println(Utils.base64encode(certificateRequest, true)); ++ out.println("-----END NEW CERTIFICATE REQUEST-----"); ++ } ++ ++ /** ++ * Provides a short description of this request. ++ */ ++ public String toString() { ++ return "[PKCS #10 certificate request:\n" ++ + subjectPublicKeyInfo.toString() ++ + " subject: <" + subject + ">" + "\n" ++ + " attributes: " + attributeSet.toString() ++ + "\n]"; ++ } ++ ++ /** ++ * Retrieve the PKCS10 CertificateRequestInfo as a byte array ++ */ ++ public byte[] getCertRequestInfo() { ++ return certRequestInfo; ++ } ++ ++ private X500Name subject; ++ private X509Key subjectPublicKeyInfo; ++ private PKCS10Attributes attributeSet; ++ ++ private byte certificateRequest[]; // signed ++ private byte certRequestInfo[]; // inner content signed ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS10Attribute.java b/org/mozilla/jss/netscape/security/pkcs/PKCS10Attribute.java +new file mode 100644 +index 00000000..1447926e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS10Attribute.java +@@ -0,0 +1,235 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.IOException; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.lang.reflect.Array; ++import java.lang.reflect.Constructor; ++import java.lang.reflect.InvocationTargetException; ++import java.security.cert.CertificateException; ++ ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.ACertAttrSet; ++import org.mozilla.jss.netscape.security.x509.CertAttrSet; ++import org.mozilla.jss.netscape.security.x509.Extensions; ++import org.mozilla.jss.netscape.security.x509.OIDMap; ++ ++/** ++ * Represent a PKCS Attribute. ++ * ++ *

++ * Attributes are addiitonal attributes which can be inserted in a PKCS certificate request. For example a ++ * "Driving License Certificate" could have the driving license number as a attribute. ++ * ++ *

++ * Attributes are represented as a sequence of the attribute identifier (Object Identifier) and a set of DER encoded ++ * attribute values. The current implementation only supports one value per attribute. ++ * ++ * ASN.1 definition of Attribute: ++ * ++ *

++ * Attribute :: SEQUENCE {
++ *    type    AttributeValue,
++ *    values  SET OF AttributeValue
++ * }
++ * AttributeValue ::= ANY
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.13 ++ */ ++public class PKCS10Attribute implements DerEncoder, Serializable { ++ private static final long serialVersionUID = 2002480042340316170L; ++ protected ObjectIdentifier attributeId = null; ++ protected CertAttrSet attributeValue = null; ++ ++ /** ++ * Default constructor. Used only by sub-classes. ++ */ ++ public PKCS10Attribute() { ++ } ++ ++ /** ++ * Constructs an attribute from a DER encoded array of bytes. ++ */ ++ public PKCS10Attribute(DerValue derVal) throws IOException { ++ if (derVal.tag != DerValue.tag_Sequence) { ++ throw new IOException("Sequence tag missing for PKCS10Attribute."); ++ } ++ ++ DerInputStream in = derVal.toDerInputStream(); ++ // Object identifier ++ attributeId = in.getOID(); ++ // System.out.println("attribute ID in pkcs10 "+attributeId.toString()); ++ ++ // Rest of the stuff is attribute value(s), wrapped in a SET. ++ // For now, assume there is only one attribute value present. ++ DerValue[] inAttrValues = in.getSet(1); ++ int attrValueNum = inAttrValues.length; ++ if (attrValueNum > 1) { ++ throw new IOException("More than one value per attribute not supported"); ++ } ++ ++ // Read the first attribute value ++ DerValue inAttrValue = inAttrValues[0]; ++ ++ if (attributeId.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) { ++ //pkcs9 extensionAttr ++ try { ++ // remove the tag ++ //DerValue dv = inAttrValue.data.getDerValue(); ++ // hack. toDerInputStream only gives one extension. ++ DerInputStream fi = new DerInputStream(inAttrValue.toByteArray()); ++ attributeValue = new Extensions(fi); ++ //CertificateExtensions(fi); ++ return; ++ } catch (Exception e) { ++ throw new IOException(e.toString()); ++ } ++ } ++ byte[] val = inAttrValue.toByteArray(); ++ Class[] params = { Object.class }; ++ try { ++ @SuppressWarnings("unchecked") ++ Class extClass = (Class) OIDMap.getClass(attributeId); ++ if (extClass != null) { ++ Constructor cons = extClass.getConstructor(params); ++ Object value = Array.newInstance(byte.class, val.length); ++ for (int i = 0; i < val.length; i++) { ++ Array.setByte(value, i, val[i]); ++ } ++ Object[] passed = new Object[] { value }; ++ attributeValue = cons.newInstance(passed); ++ } else { ++ // attribute classes are usable for PKCS10 attributes. ++ // this is used where the attributes are not actual ++ // implemented extensions. ++ attributeValue = new ACertAttrSet(inAttrValue); ++ } ++ } catch (InvocationTargetException invk) { ++ throw new IOException(invk.getTargetException().getMessage(), invk); ++ } catch (Exception e) { ++ throw new IOException(e.toString()); ++ } ++ } ++ ++ /** ++ * Constructs an attribute from individual components of ObjectIdentifier ++ * and the DER encoded value. ++ * ++ * @param attributeId the ObjectIdentifier of the attribute. ++ * @param attributeValue the CertAttrSet. ++ */ ++ public PKCS10Attribute(ObjectIdentifier attributeId, ++ CertAttrSet attributeValue) { ++ this.attributeId = attributeId; ++ this.attributeValue = attributeValue; ++ } ++ ++ /** ++ * Constructs an attribute from another attribute. To be used for ++ * creating decoded subclasses. ++ * ++ * @param attr the attribute to create from. ++ */ ++ public PKCS10Attribute(PKCS10Attribute attr) { ++ this.attributeId = attr.attributeId; ++ this.attributeValue = attr.attributeValue; ++ } ++ ++ /** ++ * Write the output to the DerOutputStream. ++ * ++ * @param out the OutputStream to write the attribute to. ++ * @exception CertificateException on certificate encoding errors. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) ++ throws CertificateException, IOException { ++ try (DerOutputStream tmp = new DerOutputStream()) { ++ // Encode the attribute value ++ DerOutputStream outAttrValue = new DerOutputStream(); ++ attributeValue.encode(outAttrValue); ++ ++ // Wrap the encoded attribute value into a SET ++ DerValue outAttrValueSet = new DerValue(DerValue.tag_Set, ++ outAttrValue.toByteArray()); ++ ++ // Create the attribute ++ DerOutputStream outAttr = new DerOutputStream(); ++ outAttr.putOID(attributeId); ++ outAttr.putDerValue(outAttrValueSet); ++ ++ // Wrap the OID and the set of attribute values into a SEQUENCE ++ tmp.write(DerValue.tag_Sequence, outAttr); ++ ++ // write the results to out ++ out.write(tmp.toByteArray()); ++ } ++ } ++ ++ /** ++ * DER encode this object onto an output stream. ++ * Implements the DerEncoder interface. ++ * ++ * @param out ++ * the OutputStream on which to write the DER encoding. ++ * ++ * @exception IOException on encoding errors. ++ */ ++ public void derEncode(OutputStream out) throws IOException { ++ try { ++ encode(out); ++ } catch (CertificateException ce) { ++ IOException ioe = new IOException(ce.toString()); ++ ioe.fillInStackTrace(); ++ throw ioe; ++ } ++ } ++ ++ /** ++ * Returns the ObjectIdentifier of the attribute. ++ */ ++ public ObjectIdentifier getAttributeId() { ++ return (attributeId); ++ } ++ ++ /** ++ * Returns the attribute value as an byte array for further processing. ++ */ ++ public CertAttrSet getAttributeValue() { ++ return (attributeValue); ++ } ++ ++ /** ++ * Returns the attribute in user readable form. ++ */ ++ public String toString() { ++ String s = "AttributeId: " + attributeId.toString() + "\n"; ++ s += "AttributeValue: " + attributeValue.toString(); ++ ++ return (s); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS10Attributes.java b/org/mozilla/jss/netscape/security/pkcs/PKCS10Attributes.java +new file mode 100644 +index 00000000..5d9209ea +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS10Attributes.java +@@ -0,0 +1,177 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.IOException; ++import java.io.OutputStream; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the PKCS10 attributes for the request. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.10 ++ */ ++public class PKCS10Attributes extends Vector implements DerEncoder { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 1362260612357629542L; ++ private Hashtable map; ++ ++ /** ++ * Default constructor for the certificate attribute. ++ */ ++ public PKCS10Attributes() { ++ map = new Hashtable(); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the attributes from. ++ * @exception IOException on decoding errors. ++ */ ++ public PKCS10Attributes(DerInputStream in) ++ throws IOException { ++ ++ map = new Hashtable(); ++ DerValue[] attrs = in.getSet(5, true); ++ ++ if (attrs != null) { ++ for (int i = 0; i < attrs.length; i++) { ++ PKCS10Attribute attr = new PKCS10Attribute(attrs[i]); ++ addElement(attr); ++ System.out.println("PKCS10Attributes: adding attribute: " + ++ attr.getAttributeValue().getName()); ++ map.put(attr.getAttributeValue().getName(), attr); ++ } ++ } ++ } ++ ++ /** ++ * Encode the attributes in DER form to the stream. ++ * ++ * @param out the OutputStream to marshal the contents to. ++ * ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) ++ throws IOException { ++ derEncode(out); ++ } ++ ++ /** ++ * Encode the attributes in DER form to the stream. ++ * Implements the DerEncoder interface. ++ * ++ * @param out the OutputStream to marshal the contents to. ++ * @exception IOException on encoding errors. ++ */ ++ public void derEncode(OutputStream out) ++ throws IOException { ++ try (DerOutputStream attrOut = new DerOutputStream()) { ++ // first copy the elements into an array ++ PKCS10Attribute[] attribs = new PKCS10Attribute[size()]; ++ copyInto(attribs); ++ ++ attrOut.putOrderedSetOf(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), ++ attribs); ++ ++ out.write(attrOut.toByteArray()); ++ } catch (IOException e) { ++ throw e; ++ } ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void setAttribute(String name, PKCS10Attribute attr) throws IOException { ++ map.put(name, attr); ++ addElement(attr); ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public PKCS10Attribute getAttribute(String name) throws IOException { ++ PKCS10Attribute attr = map.get(name); ++ /* ++ if (attr == null) { ++ throw new IOException("No attribute found with name " + name); ++ } ++ */ ++ return (attr); ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void deleteAttribute(String name) throws IOException { ++ PKCS10Attribute attr = map.get(name); ++ if (attr == null) { ++ throw new IOException("No attribute found with name " + name); ++ } ++ map.remove(name); ++ removeElement(attr); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getElements() { ++ return map.elements(); ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = super.hashCode(); ++ result = prime * result + ((map == null) ? 0 : map.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (!super.equals(obj)) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ PKCS10Attributes other = (PKCS10Attributes) obj; ++ if (map == null) { ++ if (other.map != null) ++ return false; ++ } else if (!map.equals(other.map)) ++ return false; ++ return true; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS12.java b/org/mozilla/jss/netscape/security/pkcs/PKCS12.java +new file mode 100644 +index 00000000..64f7bfc2 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS12.java +@@ -0,0 +1,209 @@ ++// --- 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) 2016 Red Hat, Inc. ++// All rights reserved. ++// --- END COPYRIGHT BLOCK --- ++package org.mozilla.jss.netscape.security.pkcs; ++ ++import java.math.BigInteger; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.LinkedHashMap; ++import java.util.Map; ++ ++import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; ++ ++public class PKCS12 { ++ ++ // PKI OID: 2.16.840.1.113730.5 ++ public final static OBJECT_IDENTIFIER PKI_OID = new OBJECT_IDENTIFIER("2.16.840.1.113730.5"); ++ ++ // PKCS #12 OID: 2.16.840.1.113730.5.1 ++ public final static OBJECT_IDENTIFIER PKCS12_OID = PKI_OID.subBranch(1); ++ ++ // PKCS #12 attributes OID: 2.16.840.1.113730.5.1.1 ++ public final static OBJECT_IDENTIFIER PKCS12_ATTRIBUTES_OID = PKCS12_OID.subBranch(1); ++ ++ // Certificate trust flags OID: 2.16.840.1.113730.5.1.1.1 ++ public final static OBJECT_IDENTIFIER CERT_TRUST_FLAGS_OID = PKCS12_ATTRIBUTES_OID.subBranch(1); ++ ++ // based on certdb.h in NSS ++ public final static int TERMINAL_RECORD = 1 << 0; ++ public final static int TRUSTED = 1 << 1; ++ public final static int SEND_WARN = 1 << 2; ++ public final static int VALID_CA = 1 << 3; ++ public final static int TRUSTED_CA = 1 << 4; ++ public final static int NS_TRUSTED_CA = 1 << 5; ++ public final static int USER = 1 << 6; ++ public final static int TRUSTED_CLIENT_CA = 1 << 7; ++ public final static int INVISIBLE_CA = 1 << 8; ++ public final static int GOVT_APPROVED_CA = 1 << 9; ++ ++ public static boolean isFlagEnabled(int flag, int flags) { ++ return (flag & flags) > 0; ++ } ++ ++ // based on printflags() in secutil.c in NSS ++ public static String encodeFlags(int flags) { ++ ++ StringBuffer sb = new StringBuffer(); ++ ++ if (isFlagEnabled(VALID_CA, flags) && !isFlagEnabled(TRUSTED_CA, flags) && !isFlagEnabled(TRUSTED_CLIENT_CA, flags)) ++ sb.append("c"); ++ ++ if (isFlagEnabled(TERMINAL_RECORD, flags) && !isFlagEnabled(TRUSTED, flags)) ++ sb.append("p"); ++ ++ if (isFlagEnabled(TRUSTED_CA, flags)) ++ sb.append("C"); ++ ++ if (isFlagEnabled(TRUSTED_CLIENT_CA, flags)) ++ sb.append("T"); ++ ++ if (isFlagEnabled(TRUSTED, flags)) ++ sb.append("P"); ++ ++ if (isFlagEnabled(USER, flags)) ++ sb.append("u"); ++ ++ if (isFlagEnabled(SEND_WARN, flags)) ++ sb.append("w"); ++ ++ if (isFlagEnabled(INVISIBLE_CA, flags)) ++ sb.append("I"); ++ ++ if (isFlagEnabled(GOVT_APPROVED_CA, flags)) ++ sb.append("G"); ++ ++ return sb.toString(); ++ } ++ ++ // based on CERT_DecodeTrustString() in certdb.c in NSS ++ public static int decodeFlags(String flags) throws Exception { ++ ++ int value = 0; ++ ++ for (char c : flags.toCharArray()) { ++ switch (c) { ++ case 'p': ++ value = value | TERMINAL_RECORD; ++ break; ++ ++ case 'P': ++ value = value | TRUSTED | TERMINAL_RECORD; ++ break; ++ ++ case 'w': ++ value = value | SEND_WARN; ++ break; ++ ++ case 'c': ++ value = value | VALID_CA; ++ break; ++ ++ case 'T': ++ value = value | TRUSTED_CLIENT_CA | VALID_CA; ++ break; ++ ++ case 'C' : ++ value = value | TRUSTED_CA | VALID_CA; ++ break; ++ ++ case 'u': ++ value = value | USER; ++ break; ++ ++ case 'i': ++ value = value | INVISIBLE_CA; ++ break; ++ case 'g': ++ value = value | GOVT_APPROVED_CA; ++ break; ++ ++ default: ++ throw new Exception("Invalid trust flag: " + c); ++ } ++ } ++ ++ return value; ++ } ++ ++ Map keyInfosByID = new LinkedHashMap(); ++ ++ Map certInfosByID = new LinkedHashMap(); ++ ++ public PKCS12() { ++ } ++ ++ public Collection getKeyInfos() { ++ return keyInfosByID.values(); ++ } ++ ++ public void addKeyInfo(PKCS12KeyInfo keyInfo) { ++ keyInfosByID.put(keyInfo.id, keyInfo); ++ } ++ ++ public PKCS12KeyInfo getKeyInfoByID(BigInteger id) { ++ return keyInfosByID.get(id); ++ } ++ ++ public PKCS12KeyInfo removeKeyInfoByID(BigInteger id) { ++ return keyInfosByID.remove(id); ++ } ++ ++ public Collection getCertInfos() { ++ return certInfosByID.values(); ++ } ++ ++ public void addCertInfo(PKCS12CertInfo certInfo, boolean replace) { ++ BigInteger id = certInfo.getID(); ++ ++ if (!replace && certInfosByID.containsKey(id)) ++ return; ++ ++ certInfosByID.put(id, certInfo); ++ } ++ ++ public PKCS12CertInfo getCertInfoByID(BigInteger id) { ++ return certInfosByID.get(id); ++ } ++ ++ public Collection getCertInfosByNickname(String nickname) { ++ ++ Collection result = new ArrayList(); ++ ++ for (PKCS12CertInfo certInfo : certInfosByID.values()) { ++ if (!nickname.equals(certInfo.getNickname())) continue; ++ result.add(certInfo); ++ } ++ ++ return result; ++ } ++ ++ public void removeCertInfoByNickname(String nickname) throws Exception { ++ ++ Collection result = getCertInfosByNickname(nickname); ++ ++ if (result.isEmpty()) { ++ throw new Exception("Certificate not found: " + nickname); ++ } ++ ++ for (PKCS12CertInfo certInfo : result) { ++ // remove cert and key ++ certInfosByID.remove(certInfo.getID()); ++ keyInfosByID.remove(certInfo.getID()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS12CertInfo.java b/org/mozilla/jss/netscape/security/pkcs/PKCS12CertInfo.java +new file mode 100644 +index 00000000..ac52ece6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS12CertInfo.java +@@ -0,0 +1,65 @@ ++// --- 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) 2016 Red Hat, Inc. ++// All rights reserved. ++// --- END COPYRIGHT BLOCK --- ++package org.mozilla.jss.netscape.security.pkcs; ++ ++import java.math.BigInteger; ++ ++import org.mozilla.jss.netscape.security.x509.X509CertImpl; ++ ++public class PKCS12CertInfo { ++ ++ BigInteger id; ++ X509CertImpl cert; ++ String nickname; ++ String trustFlags; ++ ++ public PKCS12CertInfo() { ++ } ++ ++ public BigInteger getID() { ++ return id; ++ } ++ ++ public void setID(BigInteger id) { ++ this.id = id; ++ } ++ ++ public X509CertImpl getCert() { ++ return cert; ++ } ++ ++ public void setCert(X509CertImpl cert) { ++ this.cert = cert; ++ } ++ ++ public String getNickname() { ++ return nickname; ++ } ++ ++ public void setNickname(String nickname) { ++ this.nickname = nickname; ++ } ++ ++ public String getTrustFlags() { ++ return trustFlags; ++ } ++ ++ public void setTrustFlags(String trustFlags) { ++ this.trustFlags = trustFlags; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS12KeyInfo.java b/org/mozilla/jss/netscape/security/pkcs/PKCS12KeyInfo.java +new file mode 100644 +index 00000000..8bb9968c +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS12KeyInfo.java +@@ -0,0 +1,84 @@ ++// --- 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) 2016 Red Hat, Inc. ++// All rights reserved. ++// --- END COPYRIGHT BLOCK --- ++package org.mozilla.jss.netscape.security.pkcs; ++ ++import java.math.BigInteger; ++ ++import org.mozilla.jss.crypto.PrivateKey; ++ ++/** ++ * This object is used for carrying key info around. ++ * ++ * It does not handle raw key material (but it used to). ++ * ++ * FIXME: A clear refactoring opportunity exists. The 'privateKey' ++ * field (and associated constructor) is only used during export, ++ * and the 'epkiBytes' field (and associated constructor) is only ++ * used during import. Therefore this should be two different ++ * types. ++ */ ++public class PKCS12KeyInfo { ++ ++ private PrivateKey privateKey; ++ private byte[] epkiBytes; ++ BigInteger id; ++ String subjectDN; ++ ++ public PKCS12KeyInfo() { ++ } ++ ++ /** ++ * Construct with a PrivateKey. This constructor is used ++ * for moving the PrivateKey handle around during export. ++ */ ++ public PKCS12KeyInfo(PrivateKey k) { ++ this.privateKey = k; ++ } ++ ++ /** Construct with a (serialised) EncrypedPrivateKeyInfo. This ++ * constructor is used for moving the EPKI data around during ++ * import. ++ */ ++ public PKCS12KeyInfo(byte[] epkiBytes) { ++ this.epkiBytes = epkiBytes; ++ } ++ ++ public PrivateKey getPrivateKey() { ++ return this.privateKey; ++ } ++ ++ public byte[] getEncryptedPrivateKeyInfoBytes() { ++ return epkiBytes; ++ } ++ ++ public BigInteger getID() { ++ return id; ++ } ++ ++ public void setID(BigInteger id) { ++ this.id = id; ++ } ++ ++ public String getSubjectDN() { ++ return subjectDN; ++ } ++ ++ public void setSubjectDN(String subjectDN) { ++ this.subjectDN = subjectDN; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS12Util.java b/org/mozilla/jss/netscape/security/pkcs/PKCS12Util.java +new file mode 100644 +index 00000000..e0fbe7fe +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS12Util.java +@@ -0,0 +1,667 @@ ++// --- 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) 2016 Red Hat, Inc. ++// All rights reserved. ++// --- END COPYRIGHT BLOCK --- ++package org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.ByteArrayInputStream; ++import java.io.ByteArrayOutputStream; ++import java.io.FileOutputStream; ++import java.math.BigInteger; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.nio.file.Paths; ++import java.security.MessageDigest; ++import java.security.Principal; ++import java.security.PublicKey; ++import java.security.cert.CertificateException; ++import java.util.Collection; ++ ++import org.apache.commons.lang.StringUtils; ++import org.mozilla.jss.CryptoManager; ++import org.mozilla.jss.asn1.ANY; ++import org.mozilla.jss.asn1.ASN1Value; ++import org.mozilla.jss.asn1.BMPString; ++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.CryptoStore; ++import org.mozilla.jss.crypto.CryptoToken; ++import org.mozilla.jss.crypto.EncryptionAlgorithm; ++import org.mozilla.jss.crypto.InternalCertificate; ++import org.mozilla.jss.crypto.NoSuchItemOnTokenException; ++import org.mozilla.jss.crypto.ObjectNotFoundException; ++import org.mozilla.jss.crypto.PrivateKey; ++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.Attribute; ++import org.mozilla.jss.util.Password; ++ ++import netscape.ldap.LDAPDN; ++import netscape.ldap.util.DN; ++import org.mozilla.jss.netscape.security.x509.X509CertImpl; ++ ++public class PKCS12Util { ++ ++ ++ boolean trustFlagsEnabled = true; ++ ++ public boolean isTrustFlagsEnabled() { ++ return trustFlagsEnabled; ++ } ++ ++ public void setTrustFlagsEnabled(boolean trustFlagsEnabled) { ++ this.trustFlagsEnabled = trustFlagsEnabled; ++ } ++ ++ public String getTrustFlags(X509Certificate cert) { ++ ++ InternalCertificate icert = (InternalCertificate) cert; ++ ++ StringBuilder sb = new StringBuilder(); ++ ++ sb.append(PKCS12.encodeFlags(icert.getSSLTrust())); ++ sb.append(","); ++ sb.append(PKCS12.encodeFlags(icert.getEmailTrust())); ++ sb.append(","); ++ sb.append(PKCS12.encodeFlags(icert.getObjectSigningTrust())); ++ ++ return sb.toString(); ++ } ++ ++ public void setTrustFlags(X509Certificate cert, String trustFlags) throws Exception { ++ ++ InternalCertificate icert = (InternalCertificate) cert; ++ ++ String[] flags = trustFlags.split(",", -1); // don't remove empty string ++ if (flags.length < 3) throw new Exception("Invalid trust flags: " + trustFlags); ++ ++ icert.setSSLTrust(PKCS12.decodeFlags(flags[0])); ++ icert.setEmailTrust(PKCS12.decodeFlags(flags[1])); ++ icert.setObjectSigningTrust(PKCS12.decodeFlags(flags[2])); ++ } ++ ++ /** Add a private key to the PKCS #12 object. ++ * ++ * The PKCS12KeyInfo object received comes about in two ++ * different scenarios: ++ * ++ * - The private key could be in encrypted byte[] form (e.g. ++ * when we have merely loaded a PKCS #12 file for inspection ++ * or e.g. to delete a certificate and its associated key). ++ * In this case we simply re-use this encrypted private key ++ * info byte[]. ++ * ++ * - The private key could be a be an NSS PrivateKey handle. In ++ * this case we must export the PrivateKey from the token to ++ * obtain the EncryptedPrivateKeyInfo. ++ * ++ * The common final step is to add the encrypted private key ++ * data to a "Shrouded Key Bag" to the PKCS #12 object. ++ * Unencrypted key material is never seen. ++ */ ++ public void addKeyBag(PKCS12KeyInfo keyInfo, Password password, ++ SEQUENCE encSafeContents) throws Exception { ++ ++ byte[] epkiBytes = keyInfo.getEncryptedPrivateKeyInfoBytes(); ++ if (epkiBytes == null) { ++ PrivateKey k = keyInfo.getPrivateKey(); ++ if (k == null) { ++ return; ++ } ++ ++ epkiBytes = CryptoManager.getInstance() ++ .getInternalKeyStorageToken() ++ .getCryptoStore() ++ .getEncryptedPrivateKeyInfo( ++ /* For compatibility with OpenSSL and NSS >= 3.31, ++ * do not BMPString-encode the passphrase when using ++ * non-PKCS #12 PBE scheme such as PKCS #5 PBES2. ++ * ++ * The resulting PKCS #12 is not compatible with ++ * NSS < 3.31. ++ */ ++ null /* passConverter */, ++ password, ++ /* NSS has a bug that causes any AES CBC encryption ++ * to use AES-256, but AlgorithmID contains chosen ++ * alg. To avoid mismatch, use AES_256_CBC. */ ++ EncryptionAlgorithm.AES_256_CBC, ++ 0 /* iterations (default) */, ++ k); ++ } ++ ++ SET keyAttrs = createKeyBagAttrs(keyInfo); ++ ++ SafeBag safeBag = new SafeBag( ++ SafeBag.PKCS8_SHROUDED_KEY_BAG, new ANY(epkiBytes), keyAttrs); ++ encSafeContents.addElement(safeBag); ++ } ++ ++ public void addCertBag(PKCS12CertInfo certInfo, ++ SEQUENCE safeContents) throws Exception { ++ ++ ++ ASN1Value cert = new OCTET_STRING(certInfo.cert.getEncoded()); ++ CertBag certBag = new CertBag(CertBag.X509_CERT_TYPE, cert); ++ ++ SET certAttrs = createCertBagAttrs(certInfo); ++ ++ SafeBag safeBag = new SafeBag(SafeBag.CERT_BAG, certBag, certAttrs); ++ safeContents.addElement(safeBag); ++ } ++ ++ BigInteger createLocalID(X509Certificate cert) throws Exception { ++ // SHA1 hash of the X509Cert DER encoding ++ return createLocalID(cert.getEncoded()); ++ } ++ ++ BigInteger createLocalID(byte[] bytes) throws Exception { ++ ++ MessageDigest md = MessageDigest.getInstance("SHA"); ++ md.update(bytes); ++ return new BigInteger(1, md.digest()); ++ } ++ ++ SET createKeyBagAttrs(PKCS12KeyInfo keyInfo) throws Exception { ++ ++ SET attrs = new SET(); ++ ++ SEQUENCE subjectAttr = new SEQUENCE(); ++ subjectAttr.addElement(SafeBag.FRIENDLY_NAME); ++ ++ SET subjectSet = new SET(); ++ subjectSet.addElement(new BMPString(keyInfo.subjectDN)); ++ subjectAttr.addElement(subjectSet); ++ ++ attrs.addElement(subjectAttr); ++ ++ SEQUENCE localKeyAttr = new SEQUENCE(); ++ localKeyAttr.addElement(SafeBag.LOCAL_KEY_ID); ++ ++ SET localKeySet = new SET(); ++ localKeySet.addElement(new OCTET_STRING(keyInfo.id.toByteArray())); ++ localKeyAttr.addElement(localKeySet); ++ ++ attrs.addElement(localKeyAttr); ++ ++ return attrs; ++ } ++ ++ SET createCertBagAttrs(PKCS12CertInfo certInfo) throws Exception { ++ ++ SET attrs = new SET(); ++ ++ SEQUENCE nicknameAttr = new SEQUENCE(); ++ nicknameAttr.addElement(SafeBag.FRIENDLY_NAME); ++ ++ SET nicknameSet = new SET(); ++ nicknameSet.addElement(new BMPString(certInfo.nickname)); ++ nicknameAttr.addElement(nicknameSet); ++ ++ attrs.addElement(nicknameAttr); ++ ++ if (certInfo.getID() != null) { ++ SEQUENCE localKeyAttr = new SEQUENCE(); ++ localKeyAttr.addElement(SafeBag.LOCAL_KEY_ID); ++ ++ SET localKeySet = new SET(); ++ localKeySet.addElement(new OCTET_STRING(certInfo.id.toByteArray())); ++ localKeyAttr.addElement(localKeySet); ++ ++ attrs.addElement(localKeyAttr); ++ } ++ ++ if (certInfo.trustFlags != null && trustFlagsEnabled) { ++ SEQUENCE trustFlagsAttr = new SEQUENCE(); ++ trustFlagsAttr.addElement(PKCS12.CERT_TRUST_FLAGS_OID); ++ ++ SET trustFlagsSet = new SET(); ++ trustFlagsSet.addElement(new BMPString(certInfo.trustFlags)); ++ trustFlagsAttr.addElement(trustFlagsSet); ++ ++ attrs.addElement(trustFlagsAttr); ++ } ++ ++ return attrs; ++ } ++ ++ public void loadFromNSS(PKCS12 pkcs12) throws Exception { ++ loadFromNSS(pkcs12, true, true); ++ } ++ ++ public void loadFromNSS(PKCS12 pkcs12, boolean includeKey, boolean includeChain) throws Exception { ++ ++ ++ CryptoManager cm = CryptoManager.getInstance(); ++ CryptoToken token = cm.getInternalKeyStorageToken(); ++ CryptoStore store = token.getCryptoStore(); ++ ++ for (X509Certificate cert : store.getCertificates()) { ++ loadCertFromNSS(pkcs12, cert, includeKey, includeChain); ++ } ++ } ++ ++ public void loadCertFromNSS(PKCS12 pkcs12, String nickname, boolean includeKey, boolean includeChain) throws Exception { ++ ++ CryptoManager cm = CryptoManager.getInstance(); ++ ++ X509Certificate[] certs = cm.findCertsByNickname(nickname); ++ for (X509Certificate cert : certs) { ++ loadCertFromNSS(pkcs12, cert, includeKey, includeChain); ++ } ++ } ++ ++ public void loadCertFromNSS(PKCS12 pkcs12, X509Certificate cert, boolean includeKey, boolean includeChain) throws Exception { ++ ++ CryptoManager cm = CryptoManager.getInstance(); ++ ++ BigInteger id = createLocalID(cert); ++ ++ // load cert info ++ loadCertInfoFromNSS(pkcs12, cert, id, true); ++ ++ if (includeKey) { ++ // load key info if exists ++ loadKeyInfoFromNSS(pkcs12, cert, id); ++ } ++ ++ if (includeChain) { ++ // load cert chain ++ X509Certificate[] certChain = cm.buildCertificateChain(cert); ++ for (int i = 1; i < certChain.length; i++) { ++ X509Certificate c = certChain[i]; ++ BigInteger cid = createLocalID(c); ++ loadCertInfoFromNSS(pkcs12, c, cid, false); ++ } ++ } ++ } ++ ++ public void loadCertInfoFromNSS(PKCS12 pkcs12, X509Certificate cert, BigInteger id, boolean replace) throws Exception { ++ ++ String nickname = cert.getNickname(); ++ ++ PKCS12CertInfo certInfo = new PKCS12CertInfo(); ++ certInfo.id = id; ++ certInfo.nickname = nickname; ++ certInfo.cert = new X509CertImpl(cert.getEncoded()); ++ certInfo.trustFlags = getTrustFlags(cert); ++ ++ pkcs12.addCertInfo(certInfo, replace); ++ } ++ ++ public void loadKeyInfoFromNSS(PKCS12 pkcs12, X509Certificate cert, BigInteger id) throws Exception { ++ ++ String nickname = cert.getNickname(); ++ ++ CryptoManager cm = CryptoManager.getInstance(); ++ ++ try { ++ PrivateKey privateKey = cm.findPrivKeyByCert(cert); ++ ++ PKCS12KeyInfo keyInfo = new PKCS12KeyInfo(privateKey); ++ keyInfo.id = id; ++ keyInfo.subjectDN = cert.getSubjectDN().toString(); ++ ++ pkcs12.addKeyInfo(keyInfo); ++ ++ } catch (ObjectNotFoundException e) { ++ } ++ } ++ ++ public PFX generatePFX(PKCS12 pkcs12, Password password) throws Exception { ++ ++ ++ SEQUENCE safeContents = new SEQUENCE(); ++ ++ for (PKCS12CertInfo certInfo : pkcs12.getCertInfos()) { ++ addCertBag(certInfo, safeContents); ++ } ++ ++ SEQUENCE encSafeContents = new SEQUENCE(); ++ ++ for (PKCS12KeyInfo keyInfo : pkcs12.getKeyInfos()) { ++ addKeyBag(keyInfo, password, encSafeContents); ++ } ++ ++ AuthenticatedSafes authSafes = new AuthenticatedSafes(); ++ authSafes.addSafeContents(safeContents); ++ authSafes.addSafeContents(encSafeContents); ++ ++ PFX pfx = new PFX(authSafes); ++ pfx.computeMacData(password, null, 5); ++ ++ return pfx; ++ } ++ ++ public void storeIntoFile(PKCS12 pkcs12, String filename, Password password) throws Exception { ++ ++ PFX pfx = generatePFX(pkcs12, password); ++ ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ pfx.encode(bos); ++ byte[] data = bos.toByteArray(); ++ ++ try (FileOutputStream fos = new FileOutputStream(filename)) { ++ fos.write(data); ++ } ++ } ++ ++ /** ++ * Loads key bags (for IMPORT and other operations on existing ++ * PKCS #12 files). Does not decrypt EncryptedPrivateKeyInfo ++ * values, but stores them in PKCS12KeyInfo objects for possible ++ * later use. ++ */ ++ public PKCS12KeyInfo getKeyInfo(SafeBag bag, Password password) throws Exception { ++ ++ PKCS12KeyInfo keyInfo = new PKCS12KeyInfo(bag.getBagContent().getEncoded()); ++ ++ // get key attributes ++ SET bagAttrs = bag.getBagAttributes(); ++ ++ for (int i = 0; bagAttrs != null && i < bagAttrs.size(); i++) { ++ ++ Attribute attr = (Attribute) bagAttrs.elementAt(i); ++ OBJECT_IDENTIFIER oid = attr.getType(); ++ ++ if (oid.equals(SafeBag.FRIENDLY_NAME)) { ++ ++ SET values = attr.getValues(); ++ ANY value = (ANY) values.elementAt(0); ++ ++ ByteArrayInputStream bis = new ByteArrayInputStream(value.getEncoded()); ++ BMPString subjectDN = (BMPString) new BMPString.Template().decode(bis); ++ ++ keyInfo.subjectDN = subjectDN.toString(); ++ ++ } else if (oid.equals(SafeBag.LOCAL_KEY_ID)) { ++ ++ SET values = attr.getValues(); ++ ANY value = (ANY) values.elementAt(0); ++ ++ ByteArrayInputStream bis = new ByteArrayInputStream(value.getEncoded()); ++ OCTET_STRING keyID = (OCTET_STRING) new OCTET_STRING.Template().decode(bis); ++ ++ keyInfo.id = new BigInteger(1, keyID.toByteArray()); ++ } ++ } ++ ++ return keyInfo; ++ } ++ ++ public PKCS12CertInfo getCertInfo(SafeBag bag) throws Exception { ++ ++ PKCS12CertInfo certInfo = new PKCS12CertInfo(); ++ ++ CertBag certBag = (CertBag) bag.getInterpretedBagContent(); ++ ++ OCTET_STRING certStr = (OCTET_STRING) certBag.getInterpretedCert(); ++ byte[] x509cert = certStr.toByteArray(); ++ ++ certInfo.cert = new X509CertImpl(x509cert); ++ Principal subjectDN = certInfo.cert.getSubjectDN(); ++ ++ SET bagAttrs = bag.getBagAttributes(); ++ ++ for (int i = 0; bagAttrs != null && i < bagAttrs.size(); i++) { ++ ++ Attribute attr = (Attribute) bagAttrs.elementAt(i); ++ OBJECT_IDENTIFIER oid = attr.getType(); ++ ++ if (oid.equals(SafeBag.FRIENDLY_NAME)) { ++ ++ SET values = attr.getValues(); ++ ANY value = (ANY) values.elementAt(0); ++ ++ ByteArrayInputStream bis = new ByteArrayInputStream(value.getEncoded()); ++ BMPString nickname = (BMPString) (new BMPString.Template()).decode(bis); ++ ++ certInfo.nickname = nickname.toString(); ++ ++ ++ } else if (oid.equals(SafeBag.LOCAL_KEY_ID)) { ++ ++ SET values = attr.getValues(); ++ ANY value = (ANY) values.elementAt(0); ++ ++ ByteArrayInputStream bis = new ByteArrayInputStream(value.getEncoded()); ++ OCTET_STRING keyID = (OCTET_STRING) new OCTET_STRING.Template().decode(bis); ++ ++ certInfo.id = new BigInteger(1, keyID.toByteArray()); ++ ++ } else if (oid.equals(PKCS12.CERT_TRUST_FLAGS_OID) && trustFlagsEnabled) { ++ ++ SET values = attr.getValues(); ++ ANY value = (ANY) values.elementAt(0); ++ ++ ByteArrayInputStream is = new ByteArrayInputStream(value.getEncoded()); ++ BMPString trustFlags = (BMPString) (new BMPString.Template()).decode(is); ++ ++ certInfo.trustFlags = trustFlags.toString(); ++ } ++ } ++ ++ if (certInfo.id == null) { ++ certInfo.id = createLocalID(x509cert); ++ } ++ ++ if (certInfo.nickname == null) { ++ DN dn = new DN(subjectDN.getName()); ++ String[] values = dn.explodeDN(true); ++ certInfo.nickname = StringUtils.join(values, " - "); ++ } ++ ++ return certInfo; ++ } ++ ++ public void getKeyInfos(PKCS12 pkcs12, PFX pfx, Password password) throws Exception { ++ ++ ++ AuthenticatedSafes safes = pfx.getAuthSafes(); ++ ++ for (int i = 0; i < safes.getSize(); i++) { ++ ++ SEQUENCE contents = safes.getSafeContentsAt(password, i); ++ ++ for (int j = 0; j < contents.size(); j++) { ++ ++ SafeBag bag = (SafeBag) contents.elementAt(j); ++ OBJECT_IDENTIFIER oid = bag.getBagType(); ++ ++ if (!oid.equals(SafeBag.PKCS8_SHROUDED_KEY_BAG)) continue; ++ ++ PKCS12KeyInfo keyInfo = getKeyInfo(bag, password); ++ pkcs12.addKeyInfo(keyInfo); ++ } ++ } ++ } ++ ++ public void getCertInfos(PKCS12 pkcs12, PFX pfx, Password password) throws Exception { ++ ++ ++ AuthenticatedSafes safes = pfx.getAuthSafes(); ++ ++ for (int i = 0; i < safes.getSize(); i++) { ++ ++ SEQUENCE contents = safes.getSafeContentsAt(password, i); ++ ++ for (int j = 0; j < contents.size(); j++) { ++ ++ SafeBag bag = (SafeBag) contents.elementAt(j); ++ OBJECT_IDENTIFIER oid = bag.getBagType(); ++ ++ if (!oid.equals(SafeBag.CERT_BAG)) continue; ++ ++ PKCS12CertInfo certInfo = getCertInfo(bag); ++ pkcs12.addCertInfo(certInfo, true); ++ } ++ } ++ } ++ ++ public PKCS12 loadFromFile(String filename, Password password) throws Exception { ++ ++ ++ Path path = Paths.get(filename); ++ byte[] b = Files.readAllBytes(path); ++ return loadFromByteArray(b, password); ++ } ++ ++ public PKCS12 loadFromByteArray(byte[] b, Password password) throws Exception { ++ ByteArrayInputStream bis = new ByteArrayInputStream(b); ++ ++ PFX pfx = (PFX) (new PFX.Template()).decode(bis); ++ ++ PKCS12 pkcs12 = new PKCS12(); ++ ++ StringBuffer reason = new StringBuffer(); ++ boolean valid = pfx.verifyAuthSafes(password, reason); ++ ++ if (!valid) { ++ throw new Exception("Unable to validate PKCS #12 file: " + reason); ++ } ++ ++ getKeyInfos(pkcs12, pfx, password); ++ getCertInfos(pkcs12, pfx, password); ++ ++ return pkcs12; ++ } ++ ++ public PKCS12 loadFromFile(String filename) throws Exception { ++ return loadFromFile(filename, null); ++ } ++ ++ public PrivateKey.Type getPrivateKeyType(PublicKey publicKey) { ++ if (publicKey.getAlgorithm().equals("EC")) { ++ return PrivateKey.Type.EC; ++ } ++ return PrivateKey.Type.RSA; ++ } ++ ++ public PKCS12CertInfo getCertBySubjectDN(PKCS12 pkcs12, String subjectDN) ++ throws CertificateException { ++ ++ for (PKCS12CertInfo certInfo : pkcs12.getCertInfos()) { ++ Principal certSubjectDN = certInfo.cert.getSubjectDN(); ++ if (LDAPDN.equals(certSubjectDN.toString(), subjectDN)) return certInfo; ++ } ++ ++ return null; ++ } ++ ++ public void importKey( ++ PKCS12 pkcs12, ++ Password password, ++ String nickname, ++ PKCS12KeyInfo keyInfo) throws Exception { ++ ++ ++ PKCS12CertInfo certInfo = pkcs12.getCertInfoByID(keyInfo.getID()); ++ if (certInfo == null) { ++ return; ++ } ++ ++ CryptoManager cm = CryptoManager.getInstance(); ++ CryptoToken token = cm.getInternalKeyStorageToken(); ++ CryptoStore store = token.getCryptoStore(); ++ ++ X509Certificate cert = cm.importCACertPackage(certInfo.cert.getEncoded()); ++ ++ // get public key ++ PublicKey publicKey = cert.getPublicKey(); ++ ++ byte[] epkiBytes = keyInfo.getEncryptedPrivateKeyInfoBytes(); ++ try { ++ // first true without BMPString-encoding the passphrase. ++ store.importEncryptedPrivateKeyInfo( ++ null, password, nickname, publicKey, epkiBytes); ++ } catch (Exception e) { ++ // if that failed, try again with BMPString-encoded ++ // passphrase. This is required for PKCS #12 PBE ++ // schemes and for PKCS #12 files using PBES2 generated ++ // by NSS < 3.31 ++ store.importEncryptedPrivateKeyInfo( ++ new PasswordConverter(), password, nickname, publicKey, epkiBytes); ++ } ++ ++ // delete the cert again (it will be imported again later ++ // with the correct nickname) ++ try { ++ store.deleteCert(cert); ++ } catch (NoSuchItemOnTokenException e) { ++ // this is OK ++ } ++ } ++ ++ /** ++ * Store a certificate (and key, if present) in NSSDB. ++ */ ++ public void storeCertIntoNSS( ++ PKCS12 pkcs12, Password password, ++ PKCS12CertInfo certInfo, boolean overwrite) ++ throws Exception ++ { ++ CryptoManager cm = CryptoManager.getInstance(); ++ CryptoToken ct = cm.getInternalKeyStorageToken(); ++ CryptoStore store = ct.getCryptoStore(); ++ ++ BigInteger id = certInfo.getID(); ++ PKCS12KeyInfo keyInfo = pkcs12.getKeyInfoByID(id); ++ ++ for (X509Certificate cert : cm.findCertsByNickname(certInfo.nickname)) { ++ if (!overwrite) { ++ return; ++ } ++ store.deleteCert(cert); ++ } ++ ++ X509Certificate cert; ++ if (keyInfo != null) { // cert has key ++ importKey(pkcs12, password, certInfo.nickname, keyInfo); ++ ++ cert = cm.importUserCACertPackage(certInfo.cert.getEncoded(), certInfo.nickname); ++ ++ } else { // cert has no key ++ // Note: JSS does not preserve CA certificate nickname ++ cert = cm.importCACertPackage(certInfo.cert.getEncoded()); ++ } ++ ++ if (certInfo.trustFlags != null && trustFlagsEnabled) ++ setTrustFlags(cert, certInfo.trustFlags); ++ } ++ ++ public void storeCertIntoNSS(PKCS12 pkcs12, Password password, String nickname, boolean overwrite) throws Exception { ++ Collection certInfos = pkcs12.getCertInfosByNickname(nickname); ++ for (PKCS12CertInfo certInfo : certInfos) { ++ storeCertIntoNSS(pkcs12, password, certInfo, overwrite); ++ } ++ } ++ ++ public void storeIntoNSS( ++ PKCS12 pkcs12, Password password, boolean overwrite) ++ throws Exception ++ { ++ ++ for (PKCS12CertInfo certInfo : pkcs12.getCertInfos()) { ++ storeCertIntoNSS(pkcs12, password, certInfo, overwrite); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS7.java b/org/mozilla/jss/netscape/security/pkcs/PKCS7.java +new file mode 100644 +index 00000000..dbba6265 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS7.java +@@ -0,0 +1,518 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.BufferedReader; ++import java.io.ByteArrayOutputStream; ++import java.io.DataInputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.io.PrintWriter; ++import java.io.StringReader; ++import java.io.StringWriter; ++import java.math.BigInteger; ++import java.security.NoSuchAlgorithmException; ++import java.security.SignatureException; ++import java.security.cert.CertificateException; ++import java.security.cert.X509Certificate; ++import java.util.Vector; ++ ++import org.apache.commons.codec.binary.Base64; ++ ++import org.mozilla.jss.netscape.security.util.Utils; ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.AlgorithmId; ++import org.mozilla.jss.netscape.security.x509.X500Name; ++import org.mozilla.jss.netscape.security.x509.X509CertImpl; ++ ++/** ++ * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile ++ * Supports only SignedData ContentInfo ++ * type, where to the type of data signed is plain Data. ++ * For signedData, crls, attributes and ++ * PKCS#6 Extended Certificates are not supported. ++ * ++ * @version 1.33 97/12/10 ++ * @author Benjamin Renaud ++ */ ++public class PKCS7 { ++ ++ public final static String HEADER = "-----BEGIN PKCS7-----"; ++ public final static String FOOTER = "-----END PKCS7-----"; ++ ++ private ObjectIdentifier contentType; ++ ++ // the ASN.1 members for a signedData (and other) contentTypes ++ private BigInt version; ++ private AlgorithmId[] digestAlgorithmIds; ++ private ContentInfo contentInfo; ++ private X509Certificate[] certificates; ++ private SignerInfo[] signerInfos; ++ ++ /** ++ * Unmarshals a PKCS7 block from its encoded form, parsing the ++ * encoded bytes from the InputStream. ++ * ++ * @param in an input stream holding at least one PKCS7 block. ++ * @exception ParsingException on parsing errors. ++ * @exception IOException on other errors. ++ */ ++ public PKCS7(InputStream in) throws ParsingException, IOException { ++ DataInputStream dis = new DataInputStream(in); ++ ++ int len = 0; ++ byte[] newbuf = new byte[len]; ++ byte[] oldbuf = new byte[len]; ++ byte[] data = new byte[len]; ++ ++ do { ++ newbuf = new byte[dis.available()]; ++ len += dis.available(); ++ dis.readFully(newbuf); ++ data = new byte[len]; ++ ++ System.arraycopy(oldbuf, 0, data, 0, oldbuf.length); ++ System.arraycopy(newbuf, 0, data, oldbuf.length, newbuf.length); ++ oldbuf = new byte[len]; ++ System.arraycopy(data, 0, oldbuf, 0, data.length); ++ ++ } while (dis.available() > 0); ++ ++ parse(new DerInputStream(data)); ++ } ++ ++ /** ++ * Unmarshals a PKCS7 block from its encoded form, parsing the ++ * encoded bytes from the DerInputStream. ++ * ++ * @param derin a DerInputStream holding at least one PKCS7 block. ++ * @exception ParsingException on parsing errors. ++ */ ++ public PKCS7(DerInputStream derin) throws ParsingException { ++ parse(derin); ++ } ++ ++ /** ++ * Unmarshals a PKCS7 block from its encoded form, parsing the ++ * encoded bytes. ++ * ++ * @param bytes the encoded bytes. ++ * @exception ParsingException on parsing errors. ++ */ ++ public PKCS7(byte[] bytes) throws ParsingException { ++ DerInputStream derin = new DerInputStream(bytes); ++ parse(derin); ++ } ++ ++ /** ++ * Unmarshals a PKCS7 block from PEM format. ++ * ++ * @param input the PKCS7 block in PEM format. ++ * @exception ParsingException on parsing errors. ++ */ ++ public PKCS7(String input) throws ParsingException, IOException { ++ ++ StringBuilder sb = new StringBuilder(); ++ ++ try (StringReader sr = new StringReader(input.trim()); ++ BufferedReader in = new BufferedReader(sr)) { ++ ++ String line; ++ ++ // skip everything up to header ++ String header = null; ++ while ((line = in.readLine()) != null) { ++ if (HEADER.equals(line)) { ++ header = line; ++ break; ++ } ++ } ++ ++ if (header == null) { ++ throw new ParsingException("Missing PKCS #7 header"); ++ } ++ ++ // store everything up to footer ++ String footer = null; ++ while ((line = in.readLine()) != null) { ++ if (FOOTER.equals(line)) { ++ footer = line; ++ break; ++ } ++ sb.append(line); ++ } ++ ++ if (footer == null) { ++ throw new ParsingException("Missing PKCS #7 footer"); ++ } ++ } ++ ++ byte[] bytes = Base64.decodeBase64(sb.toString()); ++ parse(new DerInputStream(bytes)); ++ } ++ ++ private void parse(DerInputStream derin) throws ParsingException { ++ try { ++ ContentInfo contentInfo = new ContentInfo(derin); ++ contentType = contentInfo.contentType; ++ if (contentType.equals(ContentInfo.SIGNED_DATA_OID)) { ++ parseSignedData(contentInfo.getContent()); ++ } else { ++ throw new ParsingException("content type " + contentType + ++ " not supported."); ++ } ++ } catch (IOException e) { ++ ParsingException pe = ++ new ParsingException("IOException: " + e.getMessage()); ++ pe.fillInStackTrace(); ++ throw pe; ++ } ++ } ++ ++ /** ++ * Construct an initialized PKCS7 block. ++ * ++ * @param digestAlgorithmIds the message digest algorithm identifiers. ++ * @param contentInfo the content information. ++ * @param certificates an array of X.509 certificates. ++ * @param signerInfos an array of signer information. ++ */ ++ public PKCS7(AlgorithmId[] digestAlgorithmIds, ++ ContentInfo contentInfo, ++ X509Certificate[] certificates, ++ SignerInfo[] signerInfos) { ++ ++ version = new BigInt(1); ++ this.digestAlgorithmIds = digestAlgorithmIds; ++ this.contentInfo = contentInfo; ++ this.certificates = certificates; ++ this.signerInfos = signerInfos; ++ } ++ ++ private void parseSignedData(DerValue val) ++ throws ParsingException, IOException { ++ ++ DerInputStream dis = val.toDerInputStream(); ++ ++ // Version ++ version = dis.getInteger(); ++ ++ // digestAlgorithmIds ++ DerValue[] digestAlgorithmIdVals = dis.getSet(1); ++ int len = digestAlgorithmIdVals.length; ++ digestAlgorithmIds = new AlgorithmId[len]; ++ try { ++ for (int i = 0; i < len; i++) { ++ DerValue oid = digestAlgorithmIdVals[i]; ++ digestAlgorithmIds[i] = AlgorithmId.parse(oid); ++ } ++ ++ } catch (IOException e) { ++ ParsingException pe = ++ new ParsingException("Error parsing digest AlgorithmId IDs: " + ++ e.getMessage()); ++ pe.fillInStackTrace(); ++ throw pe; ++ } ++ // contentInfo ++ contentInfo = new ContentInfo(dis); ++ ++ /* ++ * check if certificates (implicit tag) are provided ++ * (certificates are OPTIONAL) ++ */ ++ if ((byte) (dis.peekByte()) == (byte) 0xA0) { ++ DerValue[] certificateVals = dis.getSet(2, true); ++ ++ len = certificateVals.length; ++ certificates = new X509Certificate[len]; ++ ++ for (int i = 0; i < len; i++) { ++ try { ++ X509Certificate cert = new ++ X509CertImpl(certificateVals[i]); ++ certificates[i] = cert; ++ } catch (CertificateException e) { ++ ParsingException pe = ++ new ParsingException("CertificateException: " + ++ e.getMessage()); ++ pe.fillInStackTrace(); ++ throw pe; ++ } ++ } ++ } ++ ++ // check if crls (implicit tag) are provided (crls are OPTIONAL) ++ if ((byte) (dis.peekByte()) == (byte) 0xA1) { ++ dis.getSet(0, true); ++ } ++ ++ // signerInfos ++ DerValue[] signerInfoVals = dis.getSet(1); ++ ++ len = signerInfoVals.length; ++ signerInfos = new SignerInfo[len]; ++ ++ for (int i = 0; i < len; i++) { ++ DerInputStream in = signerInfoVals[i].toDerInputStream(); ++ signerInfos[i] = new SignerInfo(in); ++ } ++ ++ } ++ ++ /** ++ * Encodes the signed data to an output stream. ++ * ++ * @param out the output stream to write the encoded data to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encodeSignedData(OutputStream out) throws IOException { ++ DerOutputStream derout = new DerOutputStream(); ++ encodeSignedData(derout, true); ++ out.write(derout.toByteArray()); ++ } ++ ++ /** ++ * Like method above but not sorted. ++ */ ++ public void encodeSignedData(OutputStream out, boolean sort) ++ throws IOException { ++ DerOutputStream derout = new DerOutputStream(); ++ encodeSignedData(derout, sort); ++ out.write(derout.toByteArray()); ++ } ++ ++ /** ++ * encode signed data, sort certs by default. ++ */ ++ public void encodeSignedData(DerOutputStream out) ++ throws IOException { ++ encodeSignedData(out, true); ++ } ++ ++ /** ++ * Encodes the signed data to a DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the encoded data to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encodeSignedData(DerOutputStream out, boolean sort) ++ throws IOException { ++ ++ DerOutputStream signedData = new DerOutputStream(); ++ ++ // version ++ signedData.putInteger(version); ++ ++ // digestAlgorithmIds ++ signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds); ++ ++ // contentInfo ++ contentInfo.encode(signedData); ++ ++ // cast to X509CertImpl[] since X509CertImpl implements DerEncoder ++ X509CertImpl implCerts[] = new X509CertImpl[certificates.length]; ++ try { ++ for (int i = 0; i < certificates.length; i++) { ++ implCerts[i] = (X509CertImpl) certificates[i]; ++ } ++ } catch (ClassCastException e) { ++ IOException ioe = ++ new IOException("Certificates in PKCS7 " + ++ "must be of class " + ++ "org.mozilla.jss.netscape.security.X509CertImpl"); ++ ioe.fillInStackTrace(); ++ } ++ ++ // Add the certificate set (tagged with [0] IMPLICIT) ++ // to the signed data ++ if (sort) { ++ signedData.putOrderedSetOf((byte) 0xA0, implCerts); ++ } else { ++ signedData.putSet((byte) 0xA0, implCerts); ++ } ++ ++ // no crls (OPTIONAL field) ++ ++ // signerInfos ++ signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos); ++ ++ // making it a signed data block ++ DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence, ++ signedData.toByteArray()); ++ ++ // making it a content info sequence ++ ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID, ++ signedDataSeq); ++ ++ // writing out the contentInfo sequence ++ block.encode(out); ++ } ++ ++ /** ++ * This verifies a given SignerInfo. ++ * ++ * @param info the signer information. ++ * @param bytes the DER encoded content information. ++ * ++ * @exception NoSuchAlgorithmException on unrecognized algorithms. ++ * @exception SignatureException on signature handling errors. ++ */ ++ public SignerInfo verify(SignerInfo info, byte[] bytes) ++ throws NoSuchAlgorithmException, SignatureException { ++ return info.verify(this, bytes); ++ } ++ ++ /** ++ * Returns all signerInfos which self-verify. ++ * ++ * @param bytes the DER encoded content information. ++ * ++ * @exception NoSuchAlgorithmException on unrecognized algorithms. ++ * @exception SignatureException on signature handling errors. ++ */ ++ public SignerInfo[] verify(byte[] bytes) ++ throws NoSuchAlgorithmException, SignatureException { ++ ++ Vector intResult = new Vector(); ++ for (int i = 0; i < signerInfos.length; i++) { ++ ++ SignerInfo signerInfo = verify(signerInfos[i], bytes); ++ if (signerInfo != null) { ++ intResult.addElement(signerInfo); ++ } ++ } ++ if (intResult.size() != 0) { ++ ++ SignerInfo[] result = new SignerInfo[intResult.size()]; ++ intResult.copyInto(result); ++ return result; ++ } ++ return null; ++ } ++ ++ /** ++ * Returns all signerInfos which self-verify. ++ * ++ * @exception NoSuchAlgorithmException on unrecognized algorithms. ++ * @exception SignatureException on signature handling errors. ++ */ ++ public SignerInfo[] verify() ++ throws NoSuchAlgorithmException, SignatureException { ++ return verify(null); ++ } ++ ++ /** ++ * Returns the version number of this PKCS7 block. ++ */ ++ public BigInt getVersion() { ++ return version; ++ } ++ ++ /** ++ * Returns the message digest algorithms specified in this PKCS7 block. ++ */ ++ public AlgorithmId[] getDigestAlgorithmIds() { ++ return digestAlgorithmIds; ++ } ++ ++ /** ++ * Returns the content information specified in this PKCS7 block. ++ */ ++ public ContentInfo getContentInfo() { ++ return contentInfo; ++ } ++ ++ /** ++ * Returns the X.509 certificates listed in this PKCS7 block. ++ */ ++ public X509Certificate[] getCertificates() { ++ return certificates; ++ } ++ ++ /** ++ * Returns the signer's information specified in this PKCS7 block. ++ */ ++ public SignerInfo[] getSignerInfos() { ++ return signerInfos; ++ } ++ ++ /** ++ * Returns the X.509 certificate listed in this PKCS7 block ++ * which has a matching serial number and Issuer name, or ++ * null if one is not found. ++ * ++ * @param serial the serial number of the certificate to retrieve. ++ * @param name the Distinguished Name of the Issuer. ++ */ ++ public X509Certificate getCertificate(BigInt serial, X500Name name) { ++ ++ for (int i = 0; i < certificates.length; i++) { ++ X509Certificate cert = certificates[i]; ++ X500Name thisName = (X500Name) cert.getIssuerDN(); ++ BigInteger tmpSerial = cert.getSerialNumber(); ++ BigInt thisSerial = new BigInt(tmpSerial); ++ if (serial.equals(thisSerial) && name.equals(thisName)) { ++ return cert; ++ } ++ } ++ return null; ++ } ++ ++ public byte[] getBytes() throws IOException { ++ ByteArrayOutputStream out = new ByteArrayOutputStream(); ++ encodeSignedData(out); ++ return out.toByteArray(); ++ } ++ ++ public String toPEMString() throws IOException { ++ StringWriter sw = new StringWriter(); ++ PrintWriter pw = new PrintWriter(sw, true); ++ pw.println(HEADER); ++ pw.print(Utils.base64encode(getBytes(), true)); ++ pw.println(FOOTER); ++ return sw.toString(); ++ } ++ ++ /** ++ * Returns the PKCS7 block in a printable string form. ++ */ ++ public String toString() { ++ StringBuffer out = new StringBuffer("PKCS7 :: version: " + version + "\n"); ++ ++ out.append("PKCS7 :: digest AlgorithmIds: " + "\n"); ++ for (int i = 0; i < digestAlgorithmIds.length; i++) { ++ out.append("\t" + digestAlgorithmIds[i] + "\n"); ++ } ++ out.append(contentInfo + "\n"); ++ out.append("PKCS7 :: certificates: " + "\n"); ++ for (int i = 0; i < certificates.length; i++) { ++ out.append("\t" + i + ". " + certificates[i] + "\n"); ++ } ++ out.append("PKCS7 :: signer infos: " + "\n"); ++ for (int i = 0; i < signerInfos.length; i++) { ++ out.append("\t" + i + ". " + signerInfos[i] + "\n"); ++ } ++ return out.toString(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS8Key.java b/org/mozilla/jss/netscape/security/pkcs/PKCS8Key.java +new file mode 100644 +index 00000000..ec46df84 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS8Key.java +@@ -0,0 +1,430 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.ByteArrayInputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.math.BigInteger; ++import java.security.InvalidKeyException; ++import java.security.Key; ++import java.security.KeyFactory; ++import java.security.NoSuchAlgorithmException; ++import java.security.PrivateKey; ++import java.security.Provider; ++import java.security.Security; ++import java.security.spec.InvalidKeySpecException; ++import java.security.spec.PKCS8EncodedKeySpec; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.x509.AlgorithmId; ++ ++/** ++ * Holds a PKCS#8 key, for example a private key ++ * ++ * @version 1.30, 97/12/10 ++ * @author Dave Brownell ++ * @author Benjamin Renaud ++ */ ++public class PKCS8Key implements PrivateKey { ++ ++ /** use serialVersionUID from JDK 1.1. for interoperability */ ++ private static final long serialVersionUID = -3836890099307167124L; ++ ++ /* The algorithm information (name, parameters, etc). */ ++ protected AlgorithmId algid; ++ ++ /* The key bytes, without the algorithm information */ ++ protected byte[] key; ++ ++ /* The encoded for the key. */ ++ protected byte[] encodedKey; ++ ++ /* The version for this key */ ++ public static final BigInteger VERSION = BigInteger.valueOf(0); ++ ++ /** ++ * Default constructor. The key constructed must have its key ++ * and algorithm initialized before it may be used, for example ++ * by using decode. ++ */ ++ public PKCS8Key() { ++ } ++ ++ /** ++ * Construct PKCS#8 subject public key from a DER value. If ++ * the runtime environment is configured with a specific class for ++ * this kind of key, a subclass is returned. Otherwise, a generic ++ * PKCS8Key object is returned. ++ * ++ *

++ * This mechanism gurantees that keys (and algorithms) may be freely manipulated and transferred, without risk of ++ * losing information. Also, when a key (or algorithm) needs some special handling, that specific need can be ++ * accomodated. ++ * ++ * @param in the DER-encoded SubjectPublicKeyInfo value ++ * @exception IOException on data format errors ++ */ ++ public static PKCS8Key parse(DerValue in) throws IOException { ++ AlgorithmId algorithm; ++ PKCS8Key subjectKey; ++ ++ if (in.tag != DerValue.tag_Sequence) ++ throw new IOException("corrupt private key"); ++ ++ BigInteger parsedVersion = in.data.getInteger().toBigInteger(); ++ if (!VERSION.equals(parsedVersion)) { ++ throw new IOException("version mismatch: (supported: " + ++ VERSION + ", parsed: " + ++ parsedVersion); ++ } ++ ++ algorithm = AlgorithmId.parse(in.data.getDerValue()); ++ ++ try { ++ subjectKey = buildPKCS8Key(algorithm, in.data.getOctetString()); ++ ++ } catch (InvalidKeyException e) { ++ throw new IOException("corrupt private key"); ++ } ++ ++ if (in.data.available() != 0) ++ throw new IOException("excess private key"); ++ return subjectKey; ++ } ++ ++ /** ++ * Parse the key bits. This may be redefined by subclasses to take ++ * advantage of structure within the key. For example, RSA public ++ * keys encapsulate two unsigned integers (modulus and exponent) as ++ * DER values within the key bits; Diffie-Hellman and ++ * DSS/DSA keys encapsulate a single unsigned integer. ++ * ++ *

++ * This function is called when creating PKCS#8 SubjectPublicKeyInfo values using the PKCS8Key member functions, ++ * such as parse and decode. ++ * ++ * @exception IOException if a parsing error occurs. ++ * @exception InvalidKeyException if the key encoding is invalid. ++ */ ++ protected void parseKeyBits() throws IOException, InvalidKeyException { ++ encode(); ++ } ++ ++ /* ++ * Factory interface, building the kind of key associated with this ++ * specific algorithm ID or else returning this generic base class. ++ * See the description above. ++ */ ++ public static PKCS8Key buildPKCS8Key(AlgorithmId algid, byte[] key) ++ throws IOException, InvalidKeyException { ++ /* ++ * Use the algid and key parameters to produce the ASN.1 encoding ++ * of the key, which will then be used as the input to the ++ * key factory. ++ */ ++ DerOutputStream pkcs8EncodedKeyStream = new DerOutputStream(); ++ encode(pkcs8EncodedKeyStream, algid, key); ++ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(pkcs8EncodedKeyStream.toByteArray()); ++ ++ try { ++ // Instantiate the key factory of the appropriate algorithm ++ KeyFactory keyFac = KeyFactory.getInstance(algid.getName()); ++ ++ // Generate the private key ++ PrivateKey privKey = keyFac.generatePrivate(pkcs8KeySpec); ++ ++ if (privKey instanceof PKCS8Key) { ++ /* ++ * Return specialized PKCS8Key, where the structure within the ++ * key has been parsed ++ */ ++ return (PKCS8Key) privKey; ++ } ++ } catch (NoSuchAlgorithmException e) { ++ // Return generic PKCS8Key with opaque key data (see below) ++ } catch (InvalidKeySpecException e) { ++ // Return generic PKCS8Key with opaque key data (see below) ++ } ++ ++ /* ++ * Try again using JDK1.1-style for backwards compatibility. ++ */ ++ String classname = ""; ++ try { ++ Provider sunProvider; ++ ++ sunProvider = Security.getProvider("SUN"); ++ if (sunProvider == null) ++ throw new InstantiationException(); ++ classname = sunProvider.getProperty("PrivateKey.PKCS#8." + ++ algid.getName()); ++ if (classname == null) { ++ throw new InstantiationException(); ++ } ++ ++ Class keyClass = Class.forName(classname); ++ Object inst; ++ PKCS8Key result; ++ ++ inst = keyClass.newInstance(); ++ if (inst instanceof PKCS8Key) { ++ result = (PKCS8Key) inst; ++ result.algid = algid; ++ result.key = key; ++ result.parseKeyBits(); ++ return result; ++ } ++ } catch (ClassNotFoundException e) { ++ } catch (InstantiationException e) { ++ } catch (IllegalAccessException e) { ++ // this should not happen. ++ throw new IOException(classname + " [internal error]"); ++ } ++ ++ PKCS8Key result = new PKCS8Key(); ++ result.algid = algid; ++ result.key = key; ++ return result; ++ } ++ ++ /** ++ * Returns the algorithm to be used with this key. ++ */ ++ public String getAlgorithm() { ++ return algid.getName(); ++ } ++ ++ /** ++ * Returns the algorithm ID to be used with this key. ++ */ ++ public AlgorithmId getAlgorithmId() { ++ return algid; ++ } ++ ++ /** ++ * PKCS#8 sequence on the DER output stream. ++ */ ++ public final void encode(DerOutputStream out) throws IOException { ++ encode(out, this.algid, this.key); ++ } ++ ++ /** ++ * Returns the DER-encoded form of the key as a byte array. ++ */ ++ public synchronized byte[] getEncoded() { ++ byte[] result = null; ++ try { ++ result = encode(); ++ } catch (InvalidKeyException e) { ++ } ++ return result; ++ } ++ ++ /** ++ * Returns the format for this key: "PKCS#8" ++ */ ++ public String getFormat() { ++ return "PKCS#8"; ++ } ++ ++ /** ++ * Returns the DER-encoded form of the key as a byte array. ++ * ++ * @exception InvalidKeyException if an encoding error occurs. ++ */ ++ public byte[] encode() throws InvalidKeyException { ++ if (encodedKey == null) { ++ try { ++ DerOutputStream out; ++ ++ out = new DerOutputStream(); ++ encode(out); ++ encodedKey = out.toByteArray(); ++ ++ } catch (IOException e) { ++ throw new InvalidKeyException("IOException : " + ++ e.getMessage()); ++ } ++ } ++ return copyEncodedKey(encodedKey); ++ } ++ ++ /* ++ * Returns a printable representation of the key ++ */ ++ public String toString() { ++ org.mozilla.jss.netscape.security.util.PrettyPrintFormat pp = ++ new org.mozilla.jss.netscape.security.util.PrettyPrintFormat(" ", 20); ++ String keybits = pp.toHexString(key); ++ ++ return "algorithm = " + algid.toString() ++ + ", unparsed keybits = \n" + keybits; ++ } ++ ++ /** ++ * Initialize an PKCS8Key object from an input stream. The data ++ * on that input stream must be encoded using DER, obeying the ++ * PKCS#8 format: a sequence consisting of a version, an algorithm ++ * ID and a bit string which holds the key. (That bit string is ++ * often used to encapsulate another DER encoded sequence.) ++ * ++ *

++ * Subclasses should not normally redefine this method; they should instead provide a parseKeyBits ++ * method to parse any fields inside the key member. ++ * ++ * @param in an input stream with a DER-encoded PKCS#8 ++ * SubjectPublicKeyInfo value ++ * ++ * @exception InvalidKeyException if a parsing error occurs. ++ */ ++ public void decode(InputStream in) throws InvalidKeyException { ++ DerValue val; ++ ++ try { ++ val = new DerValue(in); ++ if (val.tag != DerValue.tag_Sequence) ++ throw new InvalidKeyException("invalid key format"); ++ ++ BigInteger version = val.data.getInteger().toBigInteger(); ++ if (!version.equals(PKCS8Key.VERSION)) { ++ throw new IOException("version mismatch: (supported: " + ++ PKCS8Key.VERSION + ", parsed: " + ++ version); ++ } ++ algid = AlgorithmId.parse(val.data.getDerValue()); ++ key = val.data.getOctetString(); ++ parseKeyBits(); ++ if (val.data.available() != 0) ++ throw new InvalidKeyException("excess key data"); ++ ++ } catch (IOException e) { ++ // e.printStackTrace (); ++ throw new InvalidKeyException("IOException : " + ++ e.getMessage()); ++ } ++ } ++ ++ public void decode(byte[] encodedKey) throws InvalidKeyException { ++ decode(new ByteArrayInputStream(encodedKey)); ++ } ++ ++ /** ++ * Serialization write ... PKCS#8 keys serialize as ++ * themselves, and they're parsed when they get read back. ++ */ ++ private void writeObject(java.io.ObjectOutputStream stream) throws IOException { ++ stream.write(getEncoded()); ++ } ++ ++ /** ++ * Serialization read ... PKCS#8 keys serialize as ++ * themselves, and they're parsed when they get read back. ++ */ ++ private void readObject(ObjectInputStream stream) throws IOException { ++ try { ++ decode(stream); ++ } catch (InvalidKeyException e) { ++ e.printStackTrace(); ++ throw new IOException("deserialized key is invalid: " + ++ e.getMessage()); ++ } ++ } ++ ++ /* ++ * Make a copy of the encoded key. ++ */ ++ private byte[] copyEncodedKey(byte[] encodedKey) { ++ int len = encodedKey.length; ++ byte[] copy = new byte[len]; ++ System.arraycopy(encodedKey, 0, copy, 0, len); ++ return copy; ++ } ++ ++ /* ++ * Produce PKCS#8 encoding from algorithm id and key material. ++ */ ++ static void encode(DerOutputStream out, AlgorithmId algid, byte[] key) ++ throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putInteger(new BigInt(VERSION.toByteArray())); ++ algid.encode(tmp); ++ tmp.putOctetString(key); ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++ ++ /** ++ * Compares two private keys. This returns false if the object with which ++ * to compare is not of type Key. ++ * Otherwise, the encoding of this key object is compared with the ++ * encoding of the given key object. ++ * ++ * @param object the object with which to compare ++ * @return true if this key has the same encoding as the ++ * object argument; false otherwise. ++ */ ++ public boolean equals(Object object) { ++ if (this == object) { ++ return true; ++ } ++ ++ if (object instanceof Key) { ++ ++ // this encoding ++ byte[] b1; ++ if (encodedKey != null) { ++ b1 = encodedKey; ++ } else { ++ b1 = getEncoded(); ++ } ++ ++ // that encoding ++ byte[] b2 = ((Key) object).getEncoded(); ++ ++ // do the comparison ++ int i; ++ if (b1.length != b2.length) ++ return false; ++ for (i = 0; i < b1.length; i++) { ++ if (b1[i] != b2[i]) { ++ return false; ++ } ++ } ++ return true; ++ } ++ ++ return false; ++ } ++ ++ /** ++ * Calculates a hash code value for this object. Objects ++ * which are equal will also have the same hashcode. ++ */ ++ public int hashCode() { ++ int retval = 0; ++ byte[] b1 = getEncoded(); ++ ++ for (int i = 1; i < b1.length; i++) { ++ retval += b1[i] * i; ++ } ++ return (retval); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS9Attribute.java b/org/mozilla/jss/netscape/security/pkcs/PKCS9Attribute.java +new file mode 100644 +index 00000000..41e08122 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS9Attribute.java +@@ -0,0 +1,1119 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.IOException; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Date; ++import java.util.Hashtable; ++ ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.CertificateExtensions; ++ ++/** ++ * Class supporting any PKCS9 attribute except ++ * ExtendedCertificateAttribute. Supports DER decoding and access to ++ * attribute values, but not DER encoding or setting of values. ++ * ++ * @version 1.2 97/12/10 ++ * @author Douglas Hoover ++ */ ++public class PKCS9Attribute implements DerEncoder { ++ ++ /* ++ * OIDs of PKCS #9 attribute types. ++ */ ++ private static final String RSADSI_str = "1.2.840.113549"; ++ private static final String PKCS_str = RSADSI_str + ".1"; ++ private static final String PKCS9_str = PKCS_str + ".9"; ++ ++ /** ++ * Array of attribute OIDs defined in PKCS9, by number. ++ */ ++ static final ObjectIdentifier[] PKCS9_OIDS = ++ //new ObjectIdentifier[10]; ++ // There are some Obsolete(?) attribute identifiers. ++ // This is mainly for extensionRequest (14) in pkcs10. ++ // We just add the other 4 as by products. ++ new ObjectIdentifier[15]; ++ ++ static { // static initializer for PKCS9_OIDS ++ for (int i = 1; i < PKCS9_OIDS.length; i++) { ++ PKCS9_OIDS[i] = new ObjectIdentifier(PKCS9_str + "." + i); ++ } ++ } ++ ++ public static final ObjectIdentifier EMAIL_ADDRESS_OID = PKCS9_OIDS[1]; ++ public static final ObjectIdentifier UNSTRUCTURED_NAME_OID = PKCS9_OIDS[2]; ++ public static final ObjectIdentifier CONTENT_TYPE_OID = PKCS9_OIDS[3]; ++ public static final ObjectIdentifier MESSAGE_DIGEST_OID = PKCS9_OIDS[4]; ++ public static final ObjectIdentifier SIGNING_TIME_OID = PKCS9_OIDS[5]; ++ public static final ObjectIdentifier COUNTERSIGNATURE_OID = PKCS9_OIDS[6]; ++ public static final ObjectIdentifier CHALLENGE_PASSWORD_OID = PKCS9_OIDS[7]; ++ public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID = PKCS9_OIDS[8]; ++ public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID = PKCS9_OIDS[9]; ++ ++ public static final ObjectIdentifier ISSUER_AND_SERIALNUMBER_OID = PKCS9_OIDS[10]; ++ public static final ObjectIdentifier PASSWORD_CHECK_OID = PKCS9_OIDS[11]; ++ public static final ObjectIdentifier PUBLIC_KEY_OID = PKCS9_OIDS[12]; ++ public static final ObjectIdentifier SIGNING_DESCRIPTION_OID = PKCS9_OIDS[13]; ++ public static final ObjectIdentifier EXTENSION_REQUEST_OID = PKCS9_OIDS[14]; ++ ++ public static final String EMAIL_ADDRESS_STR = "EmailAddress"; ++ public static final String UNSTRUCTURED_NAME_STR = "UnstructuredName"; ++ public static final String CONTENT_TYPE_STR = "ContentType"; ++ public static final String MESSAGE_DIGEST_STR = "MessageDigest"; ++ public static final String SIGNING_TIME_STR = "SigningTime"; ++ public static final String COUNTERSIGNATURE_STR = "Countersignature"; ++ public static final String CHALLENGE_PASSWORD_STR = "ChallengePassword"; ++ public static final String UNSTRUCTURED_ADDRESS_STR = "UnstructuredAddress"; ++ public static final String EXTENDED_CERTIFICATE_ATTRIBUTES_STR = "ExtendedCertificateAttributes"; ++ ++ public static final String ISSUER_AND_SERIALNUMBER_STR = "IssuerAndSerialNumber"; ++ public static final String PASSWORD_CHECK_STR = "PasswordCheck"; ++ public static final String PUBLIC_KEY_STR = "PublicKey"; ++ public static final String SIGNING_DESCRIPTION_STR = "SigningDescription"; ++ public static final String EXTENSION_REQUEST_STR = "ExtensionRequest"; ++ ++ /** ++ * Hashtable mapping names and variant names of supported ++ * attributes to their OIDs. This table contains all name forms ++ * that occur in PKCS9, in lower case. ++ */ ++ private static final Hashtable NAME_OID_TABLE = new Hashtable( ++ 28); ++ ++ static { // static initializer for PCKS9_NAMES ++ NAME_OID_TABLE.put("emailaddress", PKCS9_OIDS[1]); ++ NAME_OID_TABLE.put("unstructuredname", PKCS9_OIDS[2]); ++ NAME_OID_TABLE.put("contenttype", PKCS9_OIDS[3]); ++ NAME_OID_TABLE.put("messagedigest", PKCS9_OIDS[4]); ++ NAME_OID_TABLE.put("signingtime", PKCS9_OIDS[5]); ++ NAME_OID_TABLE.put("countersignature", PKCS9_OIDS[6]); ++ NAME_OID_TABLE.put("challengepassword", PKCS9_OIDS[7]); ++ NAME_OID_TABLE.put("unstructuredaddress", PKCS9_OIDS[8]); ++ NAME_OID_TABLE.put("extendedcertificateattributes", PKCS9_OIDS[9]); ++ ++ NAME_OID_TABLE.put("issuerandserialNumber", PKCS9_OIDS[10]); ++ NAME_OID_TABLE.put("passwordcheck", PKCS9_OIDS[11]); ++ NAME_OID_TABLE.put("publickey", PKCS9_OIDS[12]); ++ NAME_OID_TABLE.put("signingdescription", PKCS9_OIDS[13]); ++ NAME_OID_TABLE.put("extensionrequest", PKCS9_OIDS[14]); ++ }; ++ ++ /** ++ * Hashtable mapping attribute OIDs defined in PKCS9 to the ++ * corresponding attribute value type. ++ */ ++ private static final Hashtable OID_NAME_TABLE = new Hashtable( ++ 14); ++ static { ++ OID_NAME_TABLE.put(PKCS9_OIDS[1], EMAIL_ADDRESS_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[2], UNSTRUCTURED_NAME_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[3], CONTENT_TYPE_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[4], MESSAGE_DIGEST_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[5], SIGNING_TIME_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[6], COUNTERSIGNATURE_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[7], CHALLENGE_PASSWORD_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[8], UNSTRUCTURED_ADDRESS_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[9], EXTENDED_CERTIFICATE_ATTRIBUTES_STR); ++ ++ OID_NAME_TABLE.put(PKCS9_OIDS[10], ISSUER_AND_SERIALNUMBER_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[11], PASSWORD_CHECK_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[12], PUBLIC_KEY_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[13], SIGNING_DESCRIPTION_STR); ++ OID_NAME_TABLE.put(PKCS9_OIDS[14], EXTENSION_REQUEST_STR); ++ } ++ ++ /** ++ * Acceptable ASN.1 tags for DER encodings of values of PKCS9 ++ * attributes, by index in PKCS9_OIDS. ++ * Sets of acceptable tags are represented as arrays. ++ */ ++ private static final Byte[][] PKCS9_VALUE_TAGS = { ++ null, ++ { Byte.valueOf(DerValue.tag_IA5String) }, // EMailAddress ++ { Byte.valueOf(DerValue.tag_IA5String) }, // UnstructuredName ++ { Byte.valueOf(DerValue.tag_ObjectId) }, // ContentType ++ { Byte.valueOf(DerValue.tag_OctetString) }, // MessageDigest ++ { Byte.valueOf(DerValue.tag_UtcTime) }, // SigningTime ++ { Byte.valueOf(DerValue.tag_Sequence) }, // Countersignature ++ { Byte.valueOf(DerValue.tag_PrintableString), ++ Byte.valueOf(DerValue.tag_T61String) }, // ChallengePassword ++ { Byte.valueOf(DerValue.tag_PrintableString), ++ Byte.valueOf(DerValue.tag_T61String) }, // UnstructuredAddress ++ { Byte.valueOf(DerValue.tag_SetOf) }, // ExtendedCertificateAttributes ++ ++ null, //IssuerAndSerialNumber ++ null, //PasswordCheck ++ null, //PublicKey ++ null, //SigningDescription ++ { Byte.valueOf(DerValue.tag_Sequence) } //ExtensionRequest ++ }; ++ ++ /** ++ * Class types required for values for a given PKCS9 ++ * attribute type. ++ * ++ *

++ * The following table shows the correspondence between attribute types and value component classes. ++ * ++ *

++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ *
OIDAttribute Type NameKindValue Class
1.2.840.113549.1.9.1EmailAddressMultiple-valuedString[]
1.2.840.113549.1.9.2UnstructuredNameMultiple-valuedString
1.2.840.113549.1.9.3ContentTypeSingle-valuedObjectIdentifier
1.2.840.113549.1.9.4MessageDigestSingle-valuedbyte[]
1.2.840.113549.1.9.5SigningTimeSingle-valuedDate
1.2.840.113549.1.9.6CountersignatureMultiple-valuedSignerInfo
1.2.840.113549.1.9.7ChallengePasswordSingle-valuedString
1.2.840.113549.1.9.8UnstructuredAddressSingle-valuedString
1.2.840.113549.1.9.9ExtendedCertificateAttributesMultiple-valued(not supported)
1.2.840.113549.1.9.10IssuerAndSerialNumberSingle-valued(not supported)
1.2.840.113549.1.9.11PasswordCheckSingle-valued(not supported)
1.2.840.113549.1.9.12PublicKeySingle-valued(not supported)
1.2.840.113549.1.9.13SigningDescriptionSingle-valued(not supported)
1.2.840.113549.1.9.14ExtensionRequestSingle-valuedSequence
++ */ ++ private static final Class[] VALUE_CLASSES = new Class[15]; ++ ++ static { ++ VALUE_CLASSES[0] = null; // not used ++ VALUE_CLASSES[1] = String[].class; // EMailAddress ++ VALUE_CLASSES[2] = String[].class; // UnstructuredName ++ VALUE_CLASSES[3] = ObjectIdentifier.class; // ContentType ++ VALUE_CLASSES[4] = byte[].class; // MessageDigest (byte[]) ++ VALUE_CLASSES[5] = Date.class; // SigningTime ++ VALUE_CLASSES[6] = SignerInfo[].class; // Countersignature ++ VALUE_CLASSES[7] = String.class; // ChallengePassword ++ VALUE_CLASSES[8] = String[].class; // UnstructuredAddress ++ VALUE_CLASSES[9] = null; // ExtendedCertificateAttributes ++ ++ VALUE_CLASSES[10] = null; // IssuerAndSerialNumber ++ VALUE_CLASSES[11] = null; // PasswordCheck ++ VALUE_CLASSES[12] = null; // PublicKey ++ VALUE_CLASSES[13] = null; // SigningDescription ++ VALUE_CLASSES[14] = CertificateExtensions.class; // ExtensionRequest ++ } ++ ++ /** ++ * Array indicating which PKCS9 attributes are single-valued, ++ * by index in PKCS9_OIDS. ++ */ ++ private static final boolean[] SINGLE_VALUED = ++ { false, ++ false, // EMailAddress ++ false, // UnstructuredName ++ true, // ContentType ++ true, // MessageDigest ++ true, // SigningTime ++ false, // Countersignature ++ true, // ChallengePassword ++ false, // UnstructuredAddress ++ false, // ExtendedCertificateAttributes ++ ++ true, // IssuerAndSerialNumber ++ true, // PasswordCheck ++ true, // PublicKey ++ true, // SigningDescription ++ true // ExtensionRequest ++ }; ++ ++ /** ++ * The OID of this attribute is PKCS9_OIDS[index]. ++ */ ++ private int index; ++ ++ /** ++ * Value set of this attribute. Its class is given by VALUE_CLASSES[index]. ++ */ ++ private Object value; ++ ++ /** ++ * Construct an attribute object from the attribute's OID and ++ * value. If the attribute is single-valued, provide only one ++ * value. If the attribute is ++ * multiple-valued, provide an array containing all the values. ++ * Arrays of length zero are accepted, though probably useless. ++ * ++ *

++ * The following table gives the class that value must have for a given attribute. ++ * ++ *

++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ *
OIDAttribute Type NameKindValue Class
1.2.840.113549.1.9.1EmailAddressMultiple-valuedString[]
1.2.840.113549.1.9.2UnstructuredNameMultiple-valuedString[]
1.2.840.113549.1.9.3ContentTypeSingle-valuedObjectIdentifier
1.2.840.113549.1.9.4MessageDigestSingle-valuedbyte[]
1.2.840.113549.1.9.5SigningTimeSingle-valuedDate
1.2.840.113549.1.9.6CountersignatureMultiple-valuedSignerInfo[]
1.2.840.113549.1.9.7ChallengePasswordSingle-valuedString
1.2.840.113549.1.9.8UnstructuredAddressSingle-valuedString[]
1.2.840.113549.1.9.9ExtendedCertificateAttributesMultiple-valued(not supported)
1.2.840.113549.1.9.10IssuerAndSerialNumberSingle-valued(not supported)
1.2.840.113549.1.9.11PasswordCheckSingle-valued(not supported)
1.2.840.113549.1.9.12PublicKeySingle-valued(not supported)
1.2.840.113549.1.9.13SigningDescriptionSingle-valued(not supported)
1.2.840.113549.1.9.14ExtensionRequestSingle-valuedSequence
++ */ ++ public PKCS9Attribute(ObjectIdentifier oid, Object value) ++ throws IllegalArgumentException { ++ ++ init(oid, value); ++ } ++ ++ /** ++ * Construct an attribute object from the attribute's name and ++ * value. If the attribute is single-valued, provide only one ++ * value. If the attribute is ++ * multiple-valued, provide an array containing all the values. ++ * Arrays of length zero are accepted, though probably useless. ++ * ++ *

++ * The following table gives the class that value must have for a given attribute. Reasonable variants ++ * of these attributes are accepted; in particular, case does not matter. ++ * ++ *

++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ *
OIDAttribute Type NameKindValue Class
1.2.840.113549.1.9.1EmailAddressMultiple-valuedString[]
1.2.840.113549.1.9.2UnstructuredNameMultiple-valuedString[]
1.2.840.113549.1.9.3ContentTypeSingle-valuedObjectIdentifier
1.2.840.113549.1.9.4MessageDigestSingle-valuedbyte[]
1.2.840.113549.1.9.5SigningTimeSingle-valuedDate
1.2.840.113549.1.9.6CountersignatureMultiple-valuedSignerInfo[]
1.2.840.113549.1.9.7ChallengePasswordSingle-valuedString
1.2.840.113549.1.9.8UnstructuredAddressSingle-valuedString[]
1.2.840.113549.1.9.9ExtendedCertificateAttributesMultiple-valued(not supported)
1.2.840.113549.1.9.10IssuerAndSerialNumberSingle-valued(not supported)
1.2.840.113549.1.9.11PasswordCheckSingle-valued(not supported)
1.2.840.113549.1.9.12PublicKeySingle-valued(not supported)
1.2.840.113549.1.9.13SigningDescriptionSingle-valued(not supported)
1.2.840.113549.1.9.14ExtensionRequestSingle-valuedSequence
++ * ++ * @exception IllegalArgumentException ++ * if the name is not recognized of the value has the wrong type. ++ */ ++ public PKCS9Attribute(String name, Object value) ++ throws IllegalArgumentException { ++ ObjectIdentifier oid = getOID(name); ++ ++ if (oid == null) ++ throw new IllegalArgumentException( ++ "Unrecognized attribute name " + name + ++ " constructing PKCS9Attribute."); ++ ++ init(oid, value); ++ } ++ ++ private void init(ObjectIdentifier oid, Object value) ++ throws IllegalArgumentException { ++ ++ index = indexOf(oid, PKCS9_OIDS, 1); ++ ++ if (index == -1) ++ throw new IllegalArgumentException( ++ "Unsupported OID " + oid + ++ " constructing PKCS9Attribute."); ++ ++ if (!VALUE_CLASSES[index].isInstance(value)) ++ throw new IllegalArgumentException( ++ "Wrong value class " + ++ " for attribute " + oid + ++ " constructing PKCS9Attribute; was " + ++ value.getClass().toString() + ", should be " + ++ VALUE_CLASSES[index].toString()); ++ ++ this.value = value; ++ } ++ ++ /** ++ * Construct a PKCS9Attribute from its encoding on an input ++ * stream. ++ * ++ * @exception IOException on parsing error. ++ */ ++ public PKCS9Attribute(DerValue derVal) throws IOException { ++ ++ decode(derVal); ++ } ++ ++ /** ++ * Decode a PKCS9 attribute. ++ * ++ * @param val ++ * the DerValue representing the DER encoding of the attribute. ++ */ ++ private void decode(DerValue derVal) throws IOException { ++ DerInputStream derIn = new DerInputStream(derVal.toByteArray()); ++ DerValue[] val = derIn.getSequence(2); ++ ++ if (derIn.available() != 0) ++ throw new IOException("Excess data parsing PKCS9Attribute"); ++ ++ if (val.length != 2) ++ throw new IOException("PKCS9Attribute doesn't have two components"); ++ ++ DerValue[] elems; ++ ++ // get the oid ++ ObjectIdentifier oid = val[0].getOID(); ++ ++ index = indexOf(oid, PKCS9_OIDS, 1); ++ Byte tag; ++ ++ if (index == -1) ++ throw new IOException("Invalid OID for PKCS9 attribute: " + ++ oid); ++ ++ elems = new DerInputStream(val[1].toByteArray()).getSet(1); ++ ++ // check single valued have only one value ++ if (SINGLE_VALUED[index] && elems.length > 1) ++ throwSingleValuedException(); ++ ++ // check for illegal element tags ++ for (int i = 0; i < elems.length; i++) { ++ tag = Byte.valueOf(elems[i].tag); ++ ++ if (indexOf(tag, PKCS9_VALUE_TAGS[index], 0) == -1) ++ throwTagException(tag); ++ } ++ ++ switch (index) { ++ case 1: // email address ++ case 2: // unstructured name ++ case 8: // unstructured address ++ { // open scope ++ String[] values = new String[elems.length]; ++ ++ for (int i = 0; i < elems.length; i++) ++ values[i] = elems[i].getAsString(); ++ value = values; ++ } // close scope ++ break; ++ ++ case 3: // content type ++ value = elems[0].getOID(); ++ break; ++ ++ case 4: // message digest ++ value = elems[0].getOctetString(); ++ break; ++ ++ case 5: // signing time ++ value = (new DerInputStream(elems[0].toByteArray())).getUTCTime(); ++ break; ++ ++ case 6: // countersignature ++ { // open scope ++ SignerInfo[] values = new SignerInfo[elems.length]; ++ for (int i = 0; i < elems.length; i++) ++ values[i] = ++ new SignerInfo(elems[i].toDerInputStream()); ++ value = values; ++ } // close scope ++ break; ++ ++ case 7: // challenge password ++ value = elems[0].getAsString(); ++ break; ++ ++ case 9: // extended-certificate attribute -- not ++ // supported ++ throw new IOException("PKCS9 extended-certificate " + ++ "attribute not supported."); ++ ++ case 10: // IssuerAndSerialNumber attribute -- not ++ // supported ++ throw new IOException("PKCS9 IssuerAndSerialNumber " + ++ "attribute not supported."); ++ ++ case 11: // passwordCheck attribute -- not ++ // supported ++ throw new IOException("PKCS9 passwordCheck " + ++ "attribute not supported."); ++ case 12: // PublicKey attribute -- not ++ // supported ++ throw new IOException("PKCS9 PublicKey " + ++ "attribute not supported."); ++ case 13: // SigningDescription attribute -- not ++ // supported ++ throw new IOException("PKCS9 SigningDescription " + ++ "attribute not supported."); ++ case 14: // ExtensionRequest attribute ++ value = ++ new CertificateExtensions(elems[0].toDerInputStream()); ++ ++ // break unnecessary ++ ++ default: // can't happen ++ } ++ ++ } ++ ++ /** ++ * Write the DER encoding of this attribute to an output stream. ++ * ++ *

++ * N.B.: This method always encodes values of ChallengePassword and UnstructuredAddress attributes as ASN.1 ++ * PrintableStrings, without checking whether they should be encoded as T61Strings. ++ */ ++ public void derEncode(OutputStream out) throws IOException { ++ try (DerOutputStream temp = new DerOutputStream(); ++ DerOutputStream temp2 = new DerOutputStream(); ++ DerOutputStream derOut = new DerOutputStream()) { ++ temp.putOID(getOID()); ++ switch (index) { ++ case 1: // email address ++ case 2: // unstructured name ++ { // open scope ++ String[] values = (String[]) value; ++ DerOutputStream[] temps = new ++ DerOutputStream[values.length]; ++ ++ for (int i = 0; i < values.length; i++) { ++ temps[i] = new DerOutputStream(); ++ ++ temps[i].putIA5String(values[i]); ++ } ++ temp.putOrderedSetOf(DerValue.tag_Set, temps); ++ } // close scope ++ break; ++ ++ case 3: // content type ++ { ++ temp2.putOID((ObjectIdentifier) value); ++ temp.write(DerValue.tag_Set, temp2.toByteArray()); ++ } ++ break; ++ ++ case 4: // message digest ++ { ++ temp2.putOctetString((byte[]) value); ++ temp.write(DerValue.tag_Set, temp2.toByteArray()); ++ } ++ break; ++ ++ case 5: // signing time ++ { ++ temp2.putUTCTime((Date) value); ++ temp.write(DerValue.tag_Set, temp2.toByteArray()); ++ } ++ break; ++ ++ case 6: // countersignature ++ temp.putOrderedSetOf(DerValue.tag_Set, (DerEncoder[]) value); ++ break; ++ ++ case 7: // challenge password ++ { ++ temp2.putPrintableString((String) value); ++ temp.write(DerValue.tag_Set, temp2.toByteArray()); ++ } ++ break; ++ ++ case 8: // unstructured address ++ { // open scope ++ String[] values = (String[]) value; ++ DerOutputStream[] temps = new ++ DerOutputStream[values.length]; ++ ++ for (int i = 0; i < values.length; i++) { ++ temps[i] = new DerOutputStream(); ++ ++ temps[i].putPrintableString(values[i]); ++ } ++ temp.putOrderedSetOf(DerValue.tag_Set, temps); ++ } // close scope ++ break; ++ ++ case 9: // extended-certificate attribute -- not ++ // supported ++ throw new IOException("PKCS9 extended-certificate " + ++ "attribute not supported."); ++ ++ case 10: // IssuerAndSerialNumber attribute -- not ++ // supported ++ throw new IOException("PKCS9 IssuerAndSerialNumber " + ++ "attribute not supported."); ++ ++ case 11: // passwordCheck attribute -- not ++ // supported ++ throw new IOException("PKCS9 passwordCheck " + ++ "attribute not supported."); ++ case 12: // PublicKey attribute -- not ++ // supported ++ throw new IOException("PKCS9 PublicKey " + ++ "attribute not supported."); ++ case 13: // SigningDescription attribute -- not ++ // supported ++ throw new IOException("PKCS9 SigningDescription " + ++ "attribute not supported."); ++ case 14: // ExtensionRequest attribute ++ try { ++ //temp2.putSequence((CertificateExtensions) value); ++ ((CertificateExtensions) value).encode(temp2); ++ temp.write(DerValue.tag_Sequence, temp2.toByteArray()); ++ } catch (CertificateException e) { ++ throw new IOException("PKCS9 extension attributes not encoded"); ++ } ++ ++ // break unnecessary ++ default: // can't happen ++ } ++ ++ derOut.write(DerValue.tag_Sequence, temp.toByteArray()); ++ ++ out.write(derOut.toByteArray()); ++ } ++ } ++ ++ /** ++ * Get the value of this attribute. If the attribute is ++ * single-valued, return just the one value. If the attribute is ++ * multiple-valued, return an array containing all the values. ++ * It is possible for this array to be of length 0. ++ * ++ *

++ * The following table gives the class of the value returned, depending on the type of this attribute. ++ * ++ *

++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ *
OIDAttribute Type NameKindValue Class
1.2.840.113549.1.9.1EmailAddressMultiple-valuedString[]
1.2.840.113549.1.9.2UnstructuredNameMultiple-valuedString[]
1.2.840.113549.1.9.3ContentTypeSingle-valuedObjectIdentifier
1.2.840.113549.1.9.4MessageDigestSingle-valuedbyte[]
1.2.840.113549.1.9.5SigningTimeSingle-valuedDate
1.2.840.113549.1.9.6CountersignatureMultiple-valuedSignerInfo[]
1.2.840.113549.1.9.7ChallengePasswordSingle-valuedString
1.2.840.113549.1.9.8UnstructuredAddressSingle-valuedString[]
1.2.840.113549.1.9.9ExtendedCertificateAttributesMultiple-valued(not supported)
1.2.840.113549.1.9.10IssuerAndSerialNumberSingle-valued(not supported)
1.2.840.113549.1.9.11PasswordCheckSingle-valued(not supported)
1.2.840.113549.1.9.12PublicKeySingle-valued(not supported)
1.2.840.113549.1.9.13SigningDescriptionSingle-valued(not supported)
1.2.840.113549.1.9.14ExtensionRequestSingle-valuedSequence
++ * ++ */ ++ public Object getValue() { ++ return value; ++ } ++ ++ /** ++ * Show whether this attribute is single-valued. ++ */ ++ public boolean isSingleValued() { ++ return SINGLE_VALUED[index]; ++ } ++ ++ /** ++ * Return the OID of this attribute. ++ */ ++ public ObjectIdentifier getOID() { ++ return PKCS9_OIDS[index]; ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return OID_NAME_TABLE.get(PKCS9_OIDS[index]); ++ } ++ ++ /** ++ * Return the OID for a given attribute name or null if we don't recognize ++ * the name. ++ */ ++ public static ObjectIdentifier getOID(String name) { ++ return NAME_OID_TABLE.get(name.toLowerCase()); ++ } ++ ++ /** ++ * Return the attribute name for a given OID or null if we don't recognize ++ * the oid. ++ */ ++ public static String getName(ObjectIdentifier oid) { ++ return OID_NAME_TABLE.get(oid); ++ } ++ ++ /** ++ * Returns a string representation of this attribute. ++ */ ++ public String toString() { ++ StringBuffer buf = new StringBuffer(100); ++ ++ buf.append("["); ++ ++ buf.append(OID_NAME_TABLE.get(PKCS9_OIDS[index])); ++ buf.append(": "); ++ ++ if (SINGLE_VALUED[index]) { ++ if (value instanceof byte[]) { // special case for octet string ++ org.mozilla.jss.netscape.security.util.PrettyPrintFormat pp = ++ new org.mozilla.jss.netscape.security.util.PrettyPrintFormat(" ", 20); ++ String valuebits = pp.toHexString(((byte[]) value)); ++ buf.append(valuebits); ++ } else { ++ buf.append(value.toString()); ++ } ++ buf.append("]"); ++ return buf.toString(); ++ } else { // multiple-valued ++ boolean first = true; ++ Object[] values = (Object[]) value; ++ ++ for (int j = 0; j < values.length; j++) { ++ if (first) ++ first = false; ++ else ++ buf.append(", "); ++ ++ buf.append(values[j].toString()); ++ } ++ return buf.toString(); ++ } ++ } ++ ++ /** ++ * Beginning the search at start, find the first ++ * index i such that a[i] = obj. ++ * ++ * @return the index, if found, and -1 otherwise. ++ */ ++ static int indexOf(Object obj, Object[] a, int start) { ++ for (int i = start; i < a.length; i++) { ++ if (obj.equals(a[i])) ++ return i; ++ } ++ return -1; ++ } ++ ++ /** ++ * Throw an exception when there are multiple values for ++ * a single-valued attribute. ++ */ ++ private void throwSingleValuedException() throws IOException { ++ throw new IOException("Single-value attribute " + ++ getOID() + " (" + getName() + ")" + ++ " has multiple values."); ++ } ++ ++ /** ++ * Throw an exception when the tag on a value encoding is ++ * wrong for the attribute whose value it is. ++ */ ++ private void throwTagException(Byte tag) ++ throws IOException { ++ Byte[] expectedTags = PKCS9_VALUE_TAGS[index]; ++ StringBuffer msg = new StringBuffer(100); ++ msg.append("Value of attribute "); ++ msg.append(getOID().toString()); ++ msg.append(" ("); ++ msg.append(getName()); ++ msg.append(") has wrong tag: "); ++ msg.append(tag.toString()); ++ msg.append(". Expected tags: "); ++ ++ msg.append(expectedTags[0].toString()); ++ ++ for (int i = 1; i < expectedTags.length; i++) { ++ msg.append(", "); ++ msg.append(expectedTags[i].toString()); ++ } ++ msg.append("."); ++ throw new IOException(msg.toString()); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/PKCS9Attributes.java b/org/mozilla/jss/netscape/security/pkcs/PKCS9Attributes.java +new file mode 100644 +index 00000000..412fece4 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/PKCS9Attributes.java +@@ -0,0 +1,313 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.IOException; ++import java.io.OutputStream; ++import java.util.Hashtable; ++ ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * A set of attributes of class PKCS9Attribute. ++ * ++ * @version 1.2 97/12/10 ++ * @author Douglas Hoover ++ */ ++public class PKCS9Attributes { ++ ++ /** ++ * Attributes in this set indexed by OID. ++ */ ++ private final Hashtable attributes = new Hashtable(3); ++ ++ /** ++ * The keys of this hashtable are the OIDs of permitted attributes. ++ */ ++ private final Hashtable permittedAttributes; ++ ++ /** ++ * The DER encoding of this attribute set. The tag byte must be ++ * DerValue.tag_SetOf. ++ */ ++ private final byte[] derEncoding; ++ ++ /** ++ * Construct a set of PKCS9 Attributes from its ++ * DER encoding on a DerInputStream, accepting only attributes ++ * with OIDs on the given ++ * list. If the array is null, accept all attributes supported by ++ * class PKCS9Attribute. ++ * ++ * @param permittedAttributes ++ * Array of attribute OIDs that will be accepted. ++ * @param buf ++ * the contents of the DER encoding of the attribute set. ++ * ++ * @exception IOException ++ * on i/o error, encoding syntax error, unacceptable or ++ * unsupported attribute, or duplicate attribute. ++ * ++ * @see PKCS9Attribute ++ */ ++ public PKCS9Attributes(ObjectIdentifier[] permittedAttributes, ++ DerInputStream in) throws IOException { ++ if (permittedAttributes != null) { ++ this.permittedAttributes = ++ new Hashtable(permittedAttributes.length); ++ ++ for (int i = 0; i < permittedAttributes.length; i++) ++ this.permittedAttributes.put(permittedAttributes[i], ++ permittedAttributes[i]); ++ } else { ++ this.permittedAttributes = null; ++ } ++ ++ // derEncoding initialized in decode() ++ derEncoding = decode(in); ++ } ++ ++ /** ++ * Construct a set of PKCS9 Attributes from its contents of its ++ * DER encoding on a DerInputStream. Accept all attributes ++ * supported by class PKCS9Attribute. ++ * ++ * @exception IOException ++ * on i/o error, encoding syntax error, or unsupported or ++ * duplicate attribute. ++ * ++ * @see PKCS9Attribute ++ */ ++ public PKCS9Attributes(DerInputStream in) throws IOException { ++ // anything goes ++ // derEncoding initialized in decode() ++ derEncoding = decode(in); ++ permittedAttributes = null; ++ } ++ ++ /** ++ * Construct a set of PKCS9 Attributes from the given array of ++ * PCK9 attributes. ++ * DER encoding on a DerInputStream. All attributes in attribs must be ++ * supported by class PKCS9Attribute. ++ * ++ * @exception IOException ++ * on i/o error, encoding syntax error, or unsupported or ++ * duplicate attribute. ++ * ++ * @see PKCS9Attribute ++ */ ++ public PKCS9Attributes(PKCS9Attribute[] attribs) ++ throws IllegalArgumentException, IOException { ++ ObjectIdentifier oid; ++ for (int i = 0; i < attribs.length; i++) { ++ oid = attribs[i].getOID(); ++ if (attributes.containsKey(oid)) ++ throw new IllegalArgumentException( ++ "PKCSAttribute " + attribs[i].getOID() + ++ " duplicated while constructing " + ++ "PKCS9Attributes."); ++ ++ attributes.put(oid, attribs[i]); ++ } ++ derEncoding = generateDerEncoding(); ++ permittedAttributes = null; ++ } ++ ++ /** ++ * Decode this set of PKCS9 attribute set from the contents of its ++ * DER encoding. ++ * ++ * @param buf ++ * the contents of the DER encoding of the attribute set. ++ * ++ * @exception IOException ++ * on i/o error, encoding syntax error, unacceptable or ++ * unsupported attribute, or duplicate attribute. ++ */ ++ private byte[] decode(DerInputStream in) throws IOException { ++ ++ DerValue val = in.getDerValue(); ++ ++ // save the DER encoding with its proper tag byte. ++ byte[] derEncoding = val.toByteArray(); ++ derEncoding[0] = DerValue.tag_SetOf; ++ ++ DerInputStream derIn = new DerInputStream(derEncoding); ++ DerValue[] derVals = derIn.getSet(3, true); ++ ++ PKCS9Attribute attrib; ++ ObjectIdentifier oid; ++ ++ for (int i = 0; i < derVals.length; i++) { ++ attrib = new PKCS9Attribute(derVals[i]); ++ oid = attrib.getOID(); ++ ++ if (attributes.get(oid) != null) ++ throw new IOException("Duplicate PKCS9 attribute: " + oid); ++ ++ if (permittedAttributes != null && ++ !permittedAttributes.containsKey(oid)) ++ throw new IOException("Attribute " + oid + ++ " not permitted in this attribute set"); ++ ++ attributes.put(oid, attrib); ++ } ++ return derEncoding; ++ ++ } ++ ++ /** ++ * Put the DER encoding of this PKCS9 attribute set on an ++ * DerOutputStream, tagged with the given implicit tag. ++ * ++ * @param tag the implicit tag to use in the DER encoding. ++ * @param out the output stream on which to put the DER encoding. ++ * ++ * @exception IOException on output error. ++ */ ++ public void encode(byte tag, OutputStream out) throws IOException { ++ out.write(tag); ++ out.write(derEncoding, 1, derEncoding.length - 1); ++ } ++ ++ private byte[] generateDerEncoding() throws IOException { ++ try (DerOutputStream out = new DerOutputStream()) { ++ Object[] attribVals = attributes.values().toArray(); ++ ++ out.putOrderedSetOf(DerValue.tag_SetOf, ++ castToDerEncoder(attribVals)); ++ return out.toByteArray(); ++ } ++ } ++ ++ /** ++ * Return the DER encoding of this attribute set, tagged with ++ * DerValue.tag_SetOf. ++ */ ++ public byte[] getDerEncoding() throws IOException { ++ return derEncoding.clone(); ++ ++ } ++ ++ /** ++ * Get an attribute from this set. ++ */ ++ public PKCS9Attribute getAttribute(ObjectIdentifier oid) { ++ return attributes.get(oid); ++ } ++ ++ /** ++ * Get an attribute from this set. ++ */ ++ public PKCS9Attribute getAttribute(String name) { ++ return attributes.get(PKCS9Attribute.getOID(name)); ++ } ++ ++ /** ++ * Get an array of all attributes in this set, in order of OID. ++ */ ++ public PKCS9Attribute[] getAttributes() { ++ PKCS9Attribute[] attribs = new PKCS9Attribute[attributes.size()]; ++ ++ int j = 0; ++ for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length && ++ j < attribs.length; i++) { ++ attribs[j] = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]); ++ ++ if (attribs[j] != null) ++ j++; ++ } ++ return attribs; ++ } ++ ++ /** ++ * Get an attribute value by OID. ++ */ ++ public Object getAttributeValue(ObjectIdentifier oid) ++ throws IOException { ++ try { ++ Object value = getAttribute(oid).getValue(); ++ return value; ++ } catch (NullPointerException ex) { ++ throw new IOException("No value found for attribute " + oid); ++ } ++ ++ } ++ ++ /** ++ * Get an attribute value by type name. ++ */ ++ public Object getAttributeValue(String name) throws IOException { ++ ObjectIdentifier oid = PKCS9Attribute.getOID(name); ++ ++ if (oid == null) ++ throw new IOException("Attribute name " + name + ++ " not recognized or not supported."); ++ ++ return getAttributeValue(oid); ++ } ++ ++ /** ++ * Returns the PKCS9 block in a printable string form. ++ */ ++ public String toString() { ++ StringBuffer buf = new StringBuffer(200); ++ buf.append("PKCS9 Attributes: [\n\t"); ++ ++ PKCS9Attribute value; ++ ++ boolean first = true; ++ for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length; i++) { ++ value = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]); ++ ++ if (value == null) ++ continue; ++ ++ // we have a value; print it ++ if (first) ++ first = false; ++ else ++ buf.append(";\n\t"); ++ ++ buf.append(value.toString()); ++ } ++ ++ buf.append("\n\t] (end PKCS9 Attributes)"); ++ ++ return buf.toString(); ++ } ++ ++ /** ++ * Cast an object array whose components are DerEncoders to DerEncoder[]. ++ */ ++ static DerEncoder[] castToDerEncoder(Object[] objs) { ++ ++ DerEncoder[] encoders = new DerEncoder[objs.length]; ++ ++ for (int i = 0; i < encoders.length; i++) ++ encoders[i] = (DerEncoder) objs[i]; ++ ++ return encoders; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/ParsingException.java b/org/mozilla/jss/netscape/security/pkcs/ParsingException.java +new file mode 100644 +index 00000000..90676d3c +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/ParsingException.java +@@ -0,0 +1,35 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.IOException; ++ ++public class ParsingException extends IOException { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -8135726194372647410L; ++ ++ public ParsingException() { ++ super(); ++ } ++ ++ public ParsingException(String s) { ++ super(s); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/SignerInfo.java b/org/mozilla/jss/netscape/security/pkcs/SignerInfo.java +new file mode 100644 +index 00000000..436ed35f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/SignerInfo.java +@@ -0,0 +1,348 @@ ++// --- 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 org.mozilla.jss.netscape.security.pkcs; ++ ++import java.io.IOException; ++import java.io.OutputStream; ++import java.security.InvalidKeyException; ++import java.security.MessageDigest; ++import java.security.NoSuchAlgorithmException; ++import java.security.PublicKey; ++import java.security.Signature; ++import java.security.SignatureException; ++import java.security.cert.X509Certificate; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.AlgorithmId; ++import org.mozilla.jss.netscape.security.x509.X500Name; ++ ++/** ++ * A SignerInfo, as defined in PKCS#7's signedData type. ++ * ++ * @author Benjamin Renaud ++ * @version 1.27 97/12/10 ++ */ ++public class SignerInfo implements DerEncoder { ++ ++ BigInt version; ++ X500Name issuerName; ++ BigInt certificateSerialNumber; ++ AlgorithmId digestAlgorithmId; ++ AlgorithmId digestEncryptionAlgorithmId; ++ byte[] encryptedDigest; ++ ++ PKCS9Attributes authenticatedAttributes; ++ PKCS9Attributes unauthenticatedAttributes; ++ ++ public SignerInfo(X500Name issuerName, ++ BigInt serial, ++ AlgorithmId digestAlgorithmId, ++ AlgorithmId digestEncryptionAlgorithmId, ++ byte[] encryptedDigest) { ++ this.version = new BigInt(1); ++ this.issuerName = issuerName; ++ this.certificateSerialNumber = serial; ++ this.digestAlgorithmId = digestAlgorithmId; ++ this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId; ++ this.encryptedDigest = encryptedDigest; ++ } ++ ++ public SignerInfo(X500Name issuerName, ++ BigInt serial, ++ AlgorithmId digestAlgorithmId, ++ PKCS9Attributes authenticatedAttributes, ++ AlgorithmId digestEncryptionAlgorithmId, ++ byte[] encryptedDigest, ++ PKCS9Attributes unauthenticatedAttributes) { ++ this.version = new BigInt(1); ++ this.issuerName = issuerName; ++ this.certificateSerialNumber = serial; ++ this.digestAlgorithmId = digestAlgorithmId; ++ this.authenticatedAttributes = authenticatedAttributes; ++ this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId; ++ this.encryptedDigest = encryptedDigest; ++ this.unauthenticatedAttributes = unauthenticatedAttributes; ++ } ++ ++ public SignerInfo(DerInputStream derin) ++ throws IOException, ParsingException { ++ ++ // version ++ version = derin.getInteger(); ++ ++ // issuerAndSerialNumber ++ DerValue[] issuerAndSerialNumber = derin.getSequence(2); ++ byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray(); ++ issuerName = new X500Name(new DerValue(DerValue.tag_Sequence, ++ issuerBytes)); ++ certificateSerialNumber = issuerAndSerialNumber[1].getInteger(); ++ ++ // digestAlgorithmId ++ DerValue tmp = derin.getDerValue(); ++ ++ digestAlgorithmId = AlgorithmId.parse(tmp); ++ ++ /* ++ * check if set of auth attributes (implicit tag) is provided ++ * (auth attributes are OPTIONAL) ++ */ ++ if ((byte) (derin.peekByte()) == (byte) 0xA0) { ++ authenticatedAttributes = new PKCS9Attributes(derin); ++ } ++ ++ // digestEncryptionAlgorithmId - little RSA naming scheme - ++ // signature == encryption... ++ tmp = derin.getDerValue(); ++ ++ digestEncryptionAlgorithmId = AlgorithmId.parse(tmp); ++ ++ // encryptedDigest ++ encryptedDigest = derin.getOctetString(); ++ ++ /* ++ * check if set of unauth attributes (implicit tag) is provided ++ * (unauth attributes are OPTIONAL) ++ */ ++ if (derin.available() != 0 && (byte) (derin.peekByte()) == (byte) 0xA1) { ++ unauthenticatedAttributes = new PKCS9Attributes(derin); ++ } ++ ++ // all done ++ if (derin.available() != 0) { ++ throw new ParsingException("extra data at the end"); ++ } ++ } ++ ++ public void encode(DerOutputStream out) throws IOException { ++ ++ derEncode(out); ++ } ++ ++ /** ++ * DER encode this object onto an output stream. ++ * Implements the DerEncoder interface. ++ * ++ * @param out ++ * the output stream on which to write the DER encoding. ++ * ++ * @exception IOException on encoding error. ++ */ ++ public void derEncode(OutputStream out) throws IOException { ++ try (DerOutputStream tmp = new DerOutputStream()) { ++ DerOutputStream seq = new DerOutputStream(); ++ seq.putInteger(version); ++ DerOutputStream issuerAndSerialNumber = new DerOutputStream(); ++ issuerName.encode(issuerAndSerialNumber); ++ issuerAndSerialNumber.putInteger(certificateSerialNumber); ++ seq.write(DerValue.tag_Sequence, issuerAndSerialNumber); ++ ++ digestAlgorithmId.encode(seq); ++ ++ // encode authenticated attributes if there are any ++ if (authenticatedAttributes != null) ++ authenticatedAttributes.encode((byte) 0xA0, seq); ++ ++ digestEncryptionAlgorithmId.encode(seq); ++ ++ seq.putOctetString(encryptedDigest); ++ ++ // encode unauthenticated attributes if there are any ++ if (unauthenticatedAttributes != null) ++ unauthenticatedAttributes.encode((byte) 0xA1, seq); ++ ++ tmp.write(DerValue.tag_Sequence, seq); ++ ++ out.write(tmp.toByteArray()); ++ } ++ } ++ ++ public X509Certificate getCertificate(PKCS7 block) ++ throws IOException { ++ return block.getCertificate(certificateSerialNumber, issuerName); ++ } ++ ++ /* Returns null if verify fails, this signerInfo if ++ verify succeeds. */ ++ SignerInfo verify(PKCS7 block, byte[] data) ++ throws NoSuchAlgorithmException, SignatureException { ++ ++ try { ++ ++ ContentInfo content = block.getContentInfo(); ++ if (data == null) { ++ data = content.getContentBytes(); ++ } ++ ++ String digestAlgname = ++ getDigestAlgorithmId().getName(); ++ ++ byte[] dataSigned; ++ ++ // if there are authenticate attributes, get the message ++ // digest and compare it with the digest of data ++ if (authenticatedAttributes == null) { ++ dataSigned = data; ++ } else { ++ ++ // first, check content type ++ ObjectIdentifier contentType = (ObjectIdentifier) ++ authenticatedAttributes.getAttributeValue( ++ PKCS9Attribute.CONTENT_TYPE_OID); ++ if (contentType == null || ++ !contentType.equals(content.contentType)) ++ return null; // contentType does not match, bad SignerInfo ++ ++ // now, check message digest ++ byte[] messageDigest = (byte[]) ++ authenticatedAttributes.getAttributeValue( ++ PKCS9Attribute.MESSAGE_DIGEST_OID); ++ ++ if (messageDigest == null) // fail if there is no message digest ++ return null; ++ ++ MessageDigest md = MessageDigest.getInstance(digestAlgname); ++ byte[] computedMessageDigest = md.digest(data); ++ ++ if (messageDigest.length != computedMessageDigest.length) ++ return null; ++ for (int i = 0; i < messageDigest.length; i++) { ++ if (messageDigest[i] != computedMessageDigest[i]) ++ return null; ++ } ++ ++ // message digest attribute matched ++ // digest of original data ++ ++ // the data actually signed is the DER encoding of ++ // the authenticated attributes (tagged with ++ // the "SET OF" tag, not 0xA0). ++ dataSigned = authenticatedAttributes.getDerEncoding(); ++ } ++ ++ // put together digest algorithm and encryption algorithm ++ // to form signing algorithm ++ String encryptionAlgname = ++ getDigestEncryptionAlgorithmId().getName(); ++ ++ String algname; ++ if (encryptionAlgname.equals("DSA") || ++ encryptionAlgname.equals("SHA1withDSA")) { ++ algname = "DSA"; ++ } else { ++ algname = digestAlgname + "/" + encryptionAlgname; ++ } ++ ++ Signature sig = Signature.getInstance(algname); ++ X509Certificate cert = getCertificate(block); ++ ++ if (cert == null) { ++ return null; ++ } ++ ++ PublicKey key = cert.getPublicKey(); ++ sig.initVerify(key); ++ ++ sig.update(dataSigned); ++ ++ if (sig.verify(encryptedDigest)) { ++ return this; ++ } ++ ++ } catch (IOException e) { ++ throw new SignatureException("IO error verifying signature:\n" + ++ e.getMessage()); ++ ++ } catch (InvalidKeyException e) { ++ throw new SignatureException("InvalidKey: " + e.getMessage()); ++ ++ } ++ return null; ++ } ++ ++ /* Verify the content of the pkcs7 block. */ ++ SignerInfo verify(PKCS7 block) ++ throws NoSuchAlgorithmException, SignatureException { ++ return verify(block, null); ++ } ++ ++ public BigInt getVersion() { ++ return version; ++ } ++ ++ public X500Name getIssuerName() { ++ return issuerName; ++ } ++ ++ public BigInt getCertificateSerialNumber() { ++ return certificateSerialNumber; ++ } ++ ++ public AlgorithmId getDigestAlgorithmId() { ++ return digestAlgorithmId; ++ } ++ ++ public PKCS9Attributes getAuthenticatedAttributes() { ++ return authenticatedAttributes; ++ } ++ ++ public AlgorithmId getDigestEncryptionAlgorithmId() { ++ return digestEncryptionAlgorithmId; ++ } ++ ++ public byte[] getEncryptedDigest() { ++ return encryptedDigest; ++ } ++ ++ public PKCS9Attributes getUnauthenticatedAttributes() { ++ return unauthenticatedAttributes; ++ } ++ ++ public String toString() { ++ org.mozilla.jss.netscape.security.util.PrettyPrintFormat pp = ++ new org.mozilla.jss.netscape.security.util.PrettyPrintFormat(" ", 20); ++ String digestbits = pp.toHexString(encryptedDigest); ++ ++ String out = ""; ++ ++ out += "Signer Info for (issuer): " + issuerName + "\n"; ++ out += "\tversion: " + version + "\n"; ++ out += "\tcertificateSerialNumber: " + certificateSerialNumber + ++ "\n"; ++ out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n"; ++ if (authenticatedAttributes != null) { ++ out += "\tauthenticatedAttributes: " + authenticatedAttributes + ++ "\n"; ++ } ++ out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId + ++ "\n"; ++ ++ out += "\tencryptedDigest: " + "\n" + ++ digestbits + "\n"; ++ if (unauthenticatedAttributes != null) { ++ out += "\tunauthenticatedAttributes: " + ++ unauthenticatedAttributes + "\n"; ++ } ++ return out; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/pkcs/manifest.mn b/org/mozilla/jss/netscape/security/pkcs/manifest.mn +new file mode 100644 +index 00000000..38720420 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/pkcs/manifest.mn +@@ -0,0 +1,8 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++CORE_DEPTH = ../../../../../.. ++PACKAGE = org/mozilla/jss/netscape/security/pkcs ++MODULE = jss +diff --git a/org/mozilla/jss/netscape/security/provider/CMS.java b/org/mozilla/jss/netscape/security/provider/CMS.java +new file mode 100644 +index 00000000..e0a2a8be +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/CMS.java +@@ -0,0 +1,52 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.security.AccessController; ++import java.security.Provider; ++ ++/** ++ * The CMS Security Provider. ++ */ ++ ++public final class CMS extends Provider { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 1065207998900104219L; ++ private static final String INFO = "CMS " + ++ "(DSA key/parameter generation; DSA signing; " + ++ "SHA-1, MD5 digests; SecureRandom; X.509 certificates)"; ++ ++ public CMS() { ++ /* We are the SUN provider */ ++ super("CMS", 1.0, INFO); ++ ++ AccessController.doPrivileged(new java.security.PrivilegedAction() { ++ public Object run() { ++ /* ++ * Certificates ++ */ ++ put("CertificateFactory.X.509", "org.mozilla.jss.netscape.security.provider.X509CertificateFactory"); ++ put("Alg.Alias.CertificateFactory.X.509", "X.509"); ++ return null; ++ } ++ }); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/DSA.java b/org/mozilla/jss/netscape/security/provider/DSA.java +new file mode 100644 +index 00000000..e7f155ea +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/DSA.java +@@ -0,0 +1,661 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.io.IOException; ++import java.io.PrintStream; ++import java.math.BigInteger; ++import java.security.InvalidKeyException; ++import java.security.InvalidParameterException; ++import java.security.MessageDigest; ++import java.security.NoSuchAlgorithmException; ++import java.security.PrivateKey; ++import java.security.PublicKey; ++import java.security.SecureRandom; ++import java.security.Signature; ++import java.security.SignatureException; ++import java.security.interfaces.DSAParams; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * The Digital Signature Standard (using the Digital Signature ++ * Algorithm), as described in fips186 of the National Instute of ++ * Standards and Technology (NIST), using fips180-1 (SHA-1). ++ * ++ * @author Benjamin Renaud ++ * ++ * @version 1.86, 97/09/17 ++ * ++ * @see DSAPublicKey ++ * @see DSAPrivateKey ++ */ ++ ++public final class DSA extends Signature { ++ ++ /* Are we debugging? */ ++ private static boolean debug = false; ++ ++ /* The parameter object */ ++ @SuppressWarnings("unused") ++ private DSAParams params; ++ ++ /* algorithm parameters */ ++ private BigInteger presetP, presetQ, presetG; ++ ++ /* The public key, if any */ ++ private BigInteger presetY; ++ ++ /* The private key, if any */ ++ private BigInteger presetX; ++ ++ /* The SHA hash for the data */ ++ private MessageDigest dataSHA; ++ ++ /* The random seed used to generate k */ ++ private int[] Kseed; ++ ++ /* The random seed used to generate k (specified by application) */ ++ private byte[] KseedAsByteArray; ++ ++ /* ++ * The random seed used to generate k ++ * (prevent the same Kseed from being used twice in a row ++ */ ++ private int[] previousKseed; ++ ++ /* The RNG used to output a seed for generating k */ ++ private SecureRandom signingRandom; ++ ++ /** ++ * Construct a blank DSA object. It can generate keys, but must be ++ * initialized before being usable for signing or verifying. ++ */ ++ public DSA() throws NoSuchAlgorithmException { ++ super("SHA/DSA"); ++ dataSHA = MessageDigest.getInstance("SHA"); ++ } ++ ++ /** ++ * Initialize the DSA object with a DSA private key. ++ * ++ * @param privateKey the DSA private key ++ * ++ * @exception InvalidKeyException if the key is not a valid DSA private ++ * key. ++ */ ++ protected void engineInitSign(PrivateKey privateKey) ++ throws InvalidKeyException { ++ if (!(privateKey instanceof java.security.interfaces.DSAPrivateKey)) { ++ throw new InvalidKeyException("not a DSA private key: " + ++ privateKey); ++ } ++ java.security.interfaces.DSAPrivateKey priv = ++ (java.security.interfaces.DSAPrivateKey) privateKey; ++ ++ this.presetX = priv.getX(); ++ initialize(priv.getParams()); ++ } ++ ++ /** ++ * Initialize the DSA object with a DSA public key. ++ * ++ * @param publicKey the DSA public key. ++ * ++ * @exception InvalidKeyException if the key is not a valid DSA public ++ * key. ++ */ ++ protected void engineInitVerify(PublicKey publicKey) ++ throws InvalidKeyException { ++ if (!(publicKey instanceof java.security.interfaces.DSAPublicKey)) { ++ throw new InvalidKeyException("not a DSA public key: " + ++ publicKey); ++ } ++ java.security.interfaces.DSAPublicKey pub = ++ (java.security.interfaces.DSAPublicKey) publicKey; ++ this.presetY = pub.getY(); ++ initialize(pub.getParams()); ++ } ++ ++ private void initialize(DSAParams params) { ++ dataSHA.reset(); ++ setParams(params); ++ } ++ ++ /** ++ * Sign all the data thus far updated. The signature is formatted ++ * according to the Canonical Encoding Rules, returned as a DER ++ * sequence of Integer, r and s. ++ * ++ * @return a signature block formatted according to the Canonical ++ * Encoding Rules. ++ * ++ * @exception SignatureException if the signature object was not ++ * properly initialized, or if another exception occurs. ++ * ++ * @see org.mozilla.jss.netscape.security.provider.DSA#engineUpdate ++ * @see org.mozilla.jss.netscape.security.provider.DSA#engineVerify ++ */ ++ protected byte[] engineSign() throws SignatureException { ++ BigInteger k = generateK(presetQ); ++ BigInteger r = generateR(presetP, presetQ, presetG, k); ++ BigInteger s = generateS(presetX, presetQ, r, k); ++ ++ // got to convert to BigInt... ++ BigInt rAsBigInt = new BigInt(r.toByteArray()); ++ BigInt sAsBigInt = new BigInt(s.toByteArray()); ++ ++ try (DerOutputStream outseq = new DerOutputStream(100)) { ++ outseq.putInteger(rAsBigInt); ++ outseq.putInteger(sAsBigInt); ++ DerValue result = new DerValue(DerValue.tag_Sequence, ++ outseq.toByteArray()); ++ ++ return result.toByteArray(); ++ ++ } catch (IOException e) { ++ throw new SignatureException("error encoding signature"); ++ } ++ } ++ ++ /** ++ * Verify all the data thus far updated. ++ * ++ * @param signature the alledged signature, encoded using the ++ * Canonical Encoding Rules, as a sequence of integers, r and s. ++ * ++ * @exception SignatureException if the signature object was not ++ * properly initialized, or if another exception occurs. ++ * ++ * @see org.mozilla.jss.netscape.security.provider.DSA#engineUpdate ++ * @see org.mozilla.jss.netscape.security.provider.DSA#engineSign ++ */ ++ protected boolean engineVerify(byte[] signature) ++ throws SignatureException { ++ ++ BigInteger r = null; ++ BigInteger s = null; ++ // first decode the signature. ++ try { ++ DerInputStream in = new DerInputStream(signature); ++ DerValue[] values = in.getSequence(2); ++ ++ r = values[0].getInteger().toBigInteger(); ++ s = values[1].getInteger().toBigInteger(); ++ ++ } catch (IOException e) { ++ throw new SignatureException("invalid encoding for signature"); ++ } ++ BigInteger w = generateW(presetP, presetQ, presetG, s); ++ BigInteger v = generateV(presetY, presetP, presetQ, presetG, w, r); ++ ++ return v.equals(r); ++ } ++ ++ BigInteger generateR(BigInteger p, BigInteger q, BigInteger g, ++ BigInteger k) { ++ BigInteger temp = g.modPow(k, p); ++ return temp.remainder(q); ++ ++ } ++ ++ BigInteger generateS(BigInteger x, BigInteger q, ++ BigInteger r, BigInteger k) { ++ ++ byte[] s2 = dataSHA.digest(); ++ BigInteger temp = new BigInteger(1, s2); ++ BigInteger k1 = k.modInverse(q); ++ ++ BigInteger s = x.multiply(r); ++ s = temp.add(s); ++ s = k1.multiply(s); ++ return s.remainder(q); ++ } ++ ++ BigInteger generateW(BigInteger p, BigInteger q, ++ BigInteger g, BigInteger s) { ++ return s.modInverse(q); ++ } ++ ++ BigInteger generateV(BigInteger y, BigInteger p, ++ BigInteger q, BigInteger g, ++ BigInteger w, BigInteger r) { ++ ++ byte[] s2 = dataSHA.digest(); ++ BigInteger temp = new BigInteger(1, s2); ++ ++ temp = temp.multiply(w); ++ BigInteger u1 = temp.remainder(q); ++ ++ BigInteger u2 = (r.multiply(w)).remainder(q); ++ ++ BigInteger t1 = g.modPow(u1, p); ++ BigInteger t2 = y.modPow(u2, p); ++ BigInteger t3 = t1.multiply(t2); ++ BigInteger t5 = t3.remainder(p); ++ return t5.remainder(q); ++ } ++ ++ /* ++ * Please read bug report 4044247 for an alternative, faster, ++ * NON-FIPS approved method to generate K ++ */ ++ BigInteger generateK(BigInteger q) { ++ ++ BigInteger k = null; ++ ++ // The application specified a Kseed for us to use. ++ // Note that we do not allow usage of the same Kseed twice in a row ++ if (Kseed != null && compareSeeds(Kseed, previousKseed) != 0) { ++ k = generateK(Kseed, q); ++ if (k.signum() > 0 && k.compareTo(q) < 0) { ++ previousKseed = new int[Kseed.length]; ++ System.arraycopy(Kseed, 0, previousKseed, 0, Kseed.length); ++ return k; ++ } ++ } ++ ++ // The application did not specify a Kseed for us to use. ++ // We'll generate a new Kseed by getting random bytes from ++ // a SecureRandom object. ++ SecureRandom random = getSigningRandom(); ++ ++ while (true) { ++ int[] seed = new int[5]; ++ ++ for (int i = 0; i < 5; i++) ++ seed[i] = random.nextInt(); ++ k = generateK(seed, q); ++ if (k.signum() > 0 && k.compareTo(q) < 0) { ++ previousKseed = new int[seed.length]; ++ System.arraycopy(seed, 0, previousKseed, 0, seed.length); ++ return k; ++ } ++ } ++ } ++ ++ // Use the application-specified SecureRandom Object if provided. ++ // Otherwise, use our default SecureRandom Object. ++ private SecureRandom getSigningRandom() { ++ if (signingRandom == null) { ++ if (appRandom != null) ++ signingRandom = appRandom; ++ else ++ signingRandom = new SecureRandom(); ++ } ++ return signingRandom; ++ } ++ ++ /* ++ * return 0 if equal ++ * return 1 if not equal ++ */ ++ private int compareSeeds(int[] seed1, int[] seed2) { ++ ++ if (seed1 == null || seed2 == null) { ++ return 1; ++ } ++ if (seed1.length != seed2.length) { ++ return 1; ++ } ++ ++ for (int i = 0; i < seed1.length; i++) { ++ if (seed1[i] != seed2[i]) ++ return 1; ++ } ++ ++ return 0; ++ ++ } ++ ++ /** ++ * Compute k for a DSA signature. ++ * ++ * @param seed the seed for generating k. This seed should be ++ * secure. This is what is refered to as the KSEED in the DSA ++ * specification. ++ * ++ * @param g the g parameter from the DSA key pair. ++ */ ++ BigInteger generateK(int[] seed, BigInteger q) { ++ ++ // check out t in the spec. ++ int[] t = { 0xEFCDAB89, 0x98BADCFE, 0x10325476, ++ 0xC3D2E1F0, 0x67452301 }; ++ // ++ int[] tmp = DSA.SHA_7(seed, t); ++ byte[] tmpBytes = new byte[tmp.length * 4]; ++ for (int i = 0; i < tmp.length; i++) { ++ int k = tmp[i]; ++ for (int j = 0; j < 4; j++) { ++ tmpBytes[(i * 4) + j] = (byte) (k >>> (24 - (j * 8))); ++ } ++ } ++ BigInteger k = new BigInteger(1, tmpBytes).mod(q); ++ return k; ++ } ++ ++ // Constants for each round ++ private static final int round1_kt = 0x5a827999; ++ private static final int round2_kt = 0x6ed9eba1; ++ private static final int round3_kt = 0x8f1bbcdc; ++ private static final int round4_kt = 0xca62c1d6; ++ ++ /** ++ * Computes set 1 thru 7 of SHA-1 on m1. ++ */ ++ static int[] SHA_7(int[] m1, int[] h) { ++ ++ int[] W = new int[80]; ++ System.arraycopy(m1, 0, W, 0, m1.length); ++ int temp = 0; ++ ++ for (int t = 16; t <= 79; t++) { ++ temp = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]; ++ W[t] = ((temp << 1) | (temp >>> (32 - 1))); ++ } ++ ++ int a = h[0], b = h[1], c = h[2], d = h[3], e = h[4]; ++ for (int i = 0; i < 20; i++) { ++ temp = ((a << 5) | (a >>> (32 - 5))) + ++ ((b & c) | ((~b) & d)) + e + W[i] + round1_kt; ++ e = d; ++ d = c; ++ c = ((b << 30) | (b >>> (32 - 30))); ++ b = a; ++ a = temp; ++ } ++ ++ // Round 2 ++ for (int i = 20; i < 40; i++) { ++ temp = ((a << 5) | (a >>> (32 - 5))) + ++ (b ^ c ^ d) + e + W[i] + round2_kt; ++ e = d; ++ d = c; ++ c = ((b << 30) | (b >>> (32 - 30))); ++ b = a; ++ a = temp; ++ } ++ ++ // Round 3 ++ for (int i = 40; i < 60; i++) { ++ temp = ((a << 5) | (a >>> (32 - 5))) + ++ ((b & c) | (b & d) | (c & d)) + e + W[i] + round3_kt; ++ e = d; ++ d = c; ++ c = ((b << 30) | (b >>> (32 - 30))); ++ b = a; ++ a = temp; ++ } ++ ++ // Round 4 ++ for (int i = 60; i < 80; i++) { ++ temp = ((a << 5) | (a >>> (32 - 5))) + ++ (b ^ c ^ d) + e + W[i] + round4_kt; ++ e = d; ++ d = c; ++ c = ((b << 30) | (b >>> (32 - 30))); ++ b = a; ++ a = temp; ++ } ++ int[] md = new int[5]; ++ md[0] = h[0] + a; ++ md[1] = h[1] + b; ++ md[2] = h[2] + c; ++ md[3] = h[3] + d; ++ md[4] = h[4] + e; ++ return md; ++ } ++ ++ /** ++ * This implementation recognizes the following parameter: ++ *
++ * ++ *
Kseed ++ * ++ *
a byte array. ++ * ++ *
++ * ++ * @deprecated ++ */ ++ protected void engineSetParameter(String key, Object param) { ++ ++ if (key.equals("KSEED")) { ++ ++ if (param instanceof byte[]) { ++ ++ Kseed = byteArray2IntArray((byte[]) param); ++ KseedAsByteArray = (byte[]) param; ++ ++ } else { ++ debug("unrecognized param: " + key); ++ throw new InvalidParameterException("Kseed not a byte array"); ++ } ++ ++ } else { ++ throw new InvalidParameterException("invalid parameter"); ++ } ++ } ++ ++ /** ++ * Return the value of the requested parameter. Recognized ++ * parameters are: ++ * ++ *
++ * ++ *
Kseed ++ * ++ *
a byte array. ++ * ++ *
++ * ++ * @return the value of the requested parameter. ++ * ++ * @deprecated ++ */ ++ protected Object engineGetParameter(String key) { ++ if (key.equals("KSEED")) { ++ return KseedAsByteArray; ++ } else { ++ return null; ++ } ++ } ++ ++ /** ++ * Set the algorithm object. ++ */ ++ private void setParams(DSAParams params) { ++ this.params = params; ++ this.presetP = params.getP(); ++ this.presetQ = params.getQ(); ++ this.presetG = params.getG(); ++ } ++ ++ /** ++ * Update a byte to be signed or verified. ++ * ++ * @param b the byte to updated. ++ */ ++ protected void engineUpdate(byte b) { ++ dataSHA.update(b); ++ } ++ ++ /** ++ * Update an array of bytes to be signed or verified. ++ * ++ * @param data the bytes to be updated. ++ */ ++ protected void engineUpdate(byte[] data, int off, int len) { ++ dataSHA.update(data, off, len); ++ } ++ ++ /** ++ * Return a human readable rendition of the engine. ++ */ ++ public String toString() { ++ String printable = "DSA Signature"; ++ if (presetP != null && presetQ != null && presetG != null) { ++ printable += "\n\tp: " + presetP.toString(16); ++ printable += "\n\tq: " + presetQ.toString(16); ++ printable += "\n\tg: " + presetG.toString(16); ++ } else { ++ printable += "\n\t P, Q or G not initialized."; ++ } ++ if (presetY != null) { ++ printable += "\n\ty: " + presetY.toString(16); ++ } ++ if (presetY == null && presetX == null) { ++ printable += "\n\tUNINIIALIZED"; ++ } ++ return printable; ++ } ++ ++ /* ++ * Utility routine for converting a byte array into an int array ++ */ ++ private int[] byteArray2IntArray(byte[] byteArray) { ++ ++ int j = 0; ++ byte[] newBA; ++ int mod = byteArray.length % 4; ++ ++ // guarantee that the incoming byteArray is a multiple of 4 ++ // (pad with 0's) ++ switch (mod) { ++ case 3: ++ newBA = new byte[byteArray.length + 1]; ++ break; ++ case 2: ++ newBA = new byte[byteArray.length + 2]; ++ break; ++ case 1: ++ newBA = new byte[byteArray.length + 3]; ++ break; ++ default: ++ newBA = new byte[byteArray.length + 0]; ++ break; ++ } ++ System.arraycopy(byteArray, 0, newBA, 0, byteArray.length); ++ ++ // copy each set of 4 bytes in the byte array into an integer ++ int[] newSeed = new int[newBA.length / 4]; ++ for (int i = 0; i < newBA.length; i += 4) { ++ newSeed[j] = newBA[i + 3] & 0xFF; ++ newSeed[j] |= (newBA[i + 2] << 8) & 0xFF00; ++ newSeed[j] |= (newBA[i + 1] << 16) & 0xFF0000; ++ newSeed[j] |= (newBA[i + 0] << 24) & 0xFF000000; ++ j++; ++ } ++ ++ return newSeed; ++ } ++ ++ /* We include the test vectors from the DSA specification, FIPS ++ 186, and the FIPS 186 Change No 1, which updates the test ++ vector using SHA-1 instead of SHA (for both the G function and ++ the message hash. */ ++ ++ static void testDSA() throws Exception { ++ PrintStream p = System.out; ++ ++ DSA dsa = new DSA(); ++ int[] Kseed = { 0x687a66d9, 0x0648f993, 0x867e121f, ++ 0x4ddf9ddb, 0x1205584 }; ++ BigInteger k = dsa.generateK(Kseed, q512); ++ p.println("k: " + k.toString(16)); ++ BigInteger r = dsa.generateR(p512, q512, g512, k); ++ p.println("r: " + r.toString(16)); ++ byte[] abc = { 0x61, 0x62, 0x63 }; ++ dsa.dataSHA.update(abc); ++ BigInteger s = dsa.generateS(x512, q512, r, k); ++ p.println("s: " + s.toString(16)); ++ ++ dsa.dataSHA.update(abc); ++ BigInteger w = dsa.generateW(p512, q512, g512, s); ++ p.println("w: " + w.toString(16)); ++ BigInteger v = dsa.generateV(y512, p512, q512, g512, w, r); ++ p.println("v: " + v.toString(16)); ++ if (v.equals(r)) { ++ p.println("signature verifies."); ++ } else { ++ p.println("signature does not verify."); ++ } ++ } ++ ++ /* Test vector: 512-bit keys generated by our key generator. */ ++ ++ static BigInteger p512 = ++ new BigInteger("fca682ce8e12caba26efccf7110e526db078b05edecb" + ++ "cd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e1" + ++ "2ed0899bcd132acd50d99151bdc43ee737592e17", 16); ++ ++ static BigInteger q512 = ++ new BigInteger("962eddcc369cba8ebb260ee6b6a126d9346e38c5", 16); ++ ++ static BigInteger g512 = ++ new BigInteger("678471b27a9cf44ee91a49c5147db1a9aaf244f05a43" + ++ "4d6486931d2d14271b9e35030b71fd73da179069b32e" + ++ "2935630e1c2062354d0da20a6c416e50be794ca4", 16); ++ ++ static BigInteger x512 = ++ new BigInteger("3406c2d71b04b5fc0db62afcad58a6607d3de688", 16); ++ ++ static BigInteger y512 = ++ new BigInteger("2d335d76b8ec9d610aa8f2cbb4b149fd96fdd" + ++ "3a9a6e62bd6c2e01d406be4d1d72718a2fe08bea6d12f5e452474461f70f4" + ++ "dea60508e9fe2eaec23d2ec5d1a866", 16); ++ ++ /* Official NIST 512-bit test keys */ ++ ++ static String pString = "8df2a494492276aa3d25759bb06869cbeac0d83afb8d0" + ++ "cf7cbb8324f0d7882e5d0762fc5b7210eafc2e9adac32ab7aac49693dfbf83724c2ec" + ++ "0736ee31c80291"; ++ ++ static BigInteger testP = new BigInteger(pString, 16); ++ ++ static String gString = "626d027839ea0a13413163a55b4cb500299d5522956ce" + ++ "fcb3bff10f399ce2c2e71cb9de5fa24babf58e5b79521925c9cc42e9f6f464b088cc5" + ++ "72af53e6d78802"; ++ ++ static BigInteger testG = new BigInteger(gString, 16); ++ ++ static BigInteger testQ = new BigInteger("c773218c737ec8ee993b4f2ded30" + ++ "f48edace915f", 16); ++ ++ static BigInteger testX = new BigInteger("2070b3223dba372fde1c0ffc7b2e" + ++ "3b498b260614", 16); ++ ++ static String yString = "19131871d75b1612a819f29d78d1b0d7346f7aa77" + ++ "bb62a859bfd6c5675da9d212d3a36ef1672ef660b8c7c255cc0ec74858fba33f44c06" + ++ "699630a76b030ee333"; ++ ++ static BigInteger testY = new BigInteger(yString, 16); ++ ++ /* End test vector values */ ++ ++ private static void debug(String s) { ++ if (debug) { ++ System.err.println(s); ++ } ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/provider/DSAKeyFactory.java b/org/mozilla/jss/netscape/security/provider/DSAKeyFactory.java +new file mode 100755 +index 00000000..ec24e8f0 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/DSAKeyFactory.java +@@ -0,0 +1,232 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.security.InvalidKeyException; ++import java.security.Key; ++import java.security.KeyFactorySpi; ++import java.security.PrivateKey; ++import java.security.PublicKey; ++import java.security.interfaces.DSAParams; ++import java.security.spec.DSAPrivateKeySpec; ++import java.security.spec.DSAPublicKeySpec; ++import java.security.spec.InvalidKeySpecException; ++import java.security.spec.KeySpec; ++import java.security.spec.PKCS8EncodedKeySpec; ++import java.security.spec.X509EncodedKeySpec; ++ ++/** ++ * This class implements the DSA key factory of the Sun provider. ++ * ++ * @author Jan Luehe ++ * ++ * @version 1.8, 97/12/10 ++ * ++ * @since JDK1.2 ++ */ ++ ++public class DSAKeyFactory extends KeyFactorySpi { ++ ++ /** ++ * Generates a public key object from the provided key specification ++ * (key material). ++ * ++ * @param keySpec the specification (key material) of the public key ++ * ++ * @return the public key ++ * ++ * @exception InvalidKeySpecException if the given key specification ++ * is inappropriate for this key factory to produce a public key. ++ */ ++ protected PublicKey engineGeneratePublic(KeySpec keySpec) ++ throws InvalidKeySpecException { ++ try { ++ if (keySpec instanceof DSAPublicKeySpec) { ++ DSAPublicKeySpec dsaPubKeySpec = (DSAPublicKeySpec) keySpec; ++ return new DSAPublicKey(dsaPubKeySpec.getY(), ++ dsaPubKeySpec.getP(), ++ dsaPubKeySpec.getQ(), ++ dsaPubKeySpec.getG()); ++ ++ } else if (keySpec instanceof X509EncodedKeySpec) { ++ return new DSAPublicKey(((X509EncodedKeySpec) keySpec).getEncoded()); ++ ++ } else { ++ throw new InvalidKeySpecException("Inappropriate key specification"); ++ } ++ } catch (InvalidKeyException e) { ++ throw new InvalidKeySpecException("Inappropriate key specification: " + e.getMessage()); ++ } ++ } ++ ++ /** ++ * Generates a private key object from the provided key specification ++ * (key material). ++ * ++ * @param keySpec the specification (key material) of the private key ++ * ++ * @return the private key ++ * ++ * @exception InvalidKeySpecException if the given key specification ++ * is inappropriate for this key factory to produce a private key. ++ */ ++ protected PrivateKey engineGeneratePrivate(KeySpec keySpec) ++ throws InvalidKeySpecException { ++ try { ++ if (keySpec instanceof DSAPrivateKeySpec) { ++ DSAPrivateKeySpec dsaPrivKeySpec = (DSAPrivateKeySpec) keySpec; ++ return new DSAPrivateKey(dsaPrivKeySpec.getX(), ++ dsaPrivKeySpec.getP(), ++ dsaPrivKeySpec.getQ(), ++ dsaPrivKeySpec.getG()); ++ ++ } else if (keySpec instanceof PKCS8EncodedKeySpec) { ++ return new DSAPrivateKey(((PKCS8EncodedKeySpec) keySpec).getEncoded()); ++ ++ } else { ++ throw new InvalidKeySpecException("Inappropriate key specification"); ++ } ++ } catch (InvalidKeyException e) { ++ throw new InvalidKeySpecException("Inappropriate key specification: " + e.getMessage()); ++ } ++ } ++ ++ /** ++ * Returns a specification (key material) of the given key object ++ * in the requested format. ++ * ++ * @param key the key ++ * ++ * @param keySpec the requested format in which the key material shall be ++ * returned ++ * ++ * @return the underlying key specification (key material) in the ++ * requested format ++ * ++ * @exception InvalidKeySpecException if the requested key specification is ++ * inappropriate for the given key, or the given key cannot be processed ++ * (e.g., the given key has an unrecognized algorithm or format). ++ */ ++ @SuppressWarnings("unchecked") ++ protected T engineGetKeySpec(Key key, Class keySpec) ++ throws InvalidKeySpecException { ++ ++ DSAParams params; ++ ++ try { ++ ++ if (key instanceof java.security.interfaces.DSAPublicKey) { ++ ++ // Determine valid key specs ++ Class dsaPubKeySpec = Class.forName ++ ("java.security.spec.DSAPublicKeySpec"); ++ Class x509KeySpec = Class.forName ++ ("java.security.spec.X509EncodedKeySpec"); ++ ++ if (dsaPubKeySpec.isAssignableFrom(keySpec)) { ++ java.security.interfaces.DSAPublicKey dsaPubKey = (java.security.interfaces.DSAPublicKey) key; ++ params = dsaPubKey.getParams(); ++ return (T) new DSAPublicKeySpec(dsaPubKey.getY(), ++ params.getP(), ++ params.getQ(), ++ params.getG()); ++ ++ } else if (x509KeySpec.isAssignableFrom(keySpec)) { ++ return (T) new X509EncodedKeySpec(key.getEncoded()); ++ ++ } else { ++ throw new InvalidKeySpecException("Inappropriate key specification"); ++ } ++ ++ } else if (key instanceof java.security.interfaces.DSAPrivateKey) { ++ ++ // Determine valid key specs ++ Class dsaPrivKeySpec = Class.forName ++ ("java.security.spec.DSAPrivateKeySpec"); ++ Class pkcs8KeySpec = Class.forName ++ ("java.security.spec.PKCS8EncodedKeySpec"); ++ ++ if (dsaPrivKeySpec.isAssignableFrom(keySpec)) { ++ java.security.interfaces.DSAPrivateKey dsaPrivKey = (java.security.interfaces.DSAPrivateKey) key; ++ params = dsaPrivKey.getParams(); ++ return (T) new DSAPrivateKeySpec(dsaPrivKey.getX(), ++ params.getP(), ++ params.getQ(), ++ params.getG()); ++ ++ } else if (pkcs8KeySpec.isAssignableFrom(keySpec)) { ++ return (T) new PKCS8EncodedKeySpec(key.getEncoded()); ++ ++ } else { ++ throw new InvalidKeySpecException("Inappropriate key specification"); ++ } ++ ++ } else { ++ throw new InvalidKeySpecException("Inappropriate key type"); ++ } ++ ++ } catch (ClassNotFoundException e) { ++ throw new InvalidKeySpecException("Unsupported key specification: " + e.getMessage()); ++ } ++ } ++ ++ /** ++ * Translates a key object, whose provider may be unknown or potentially ++ * untrusted, into a corresponding key object of this key factory. ++ * ++ * @param key the key whose provider is unknown or untrusted ++ * ++ * @return the translated key ++ * ++ * @exception InvalidKeyException if the given key cannot be processed by ++ * this key factory. ++ */ ++ protected Key engineTranslateKey(Key key) throws InvalidKeyException { ++ ++ try { ++ ++ if (key instanceof java.security.interfaces.DSAPublicKey) { ++ // Check if key originates from this factory ++ if (key instanceof org.mozilla.jss.netscape.security.provider.DSAPublicKey) { ++ return key; ++ } ++ // Convert key to spec ++ DSAPublicKeySpec dsaPubKeySpec = engineGetKeySpec(key, DSAPublicKeySpec.class); ++ // Create key from spec, and return it ++ return engineGeneratePublic(dsaPubKeySpec); ++ ++ } else if (key instanceof java.security.interfaces.DSAPrivateKey) { ++ // Check if key originates from this factory ++ if (key instanceof org.mozilla.jss.netscape.security.provider.DSAPrivateKey) { ++ return key; ++ } ++ // Convert key to spec ++ DSAPrivateKeySpec dsaPrivKeySpec = engineGetKeySpec(key, DSAPrivateKeySpec.class); ++ // Create key from spec, and return it ++ return engineGeneratePrivate(dsaPrivKeySpec); ++ ++ } else { ++ throw new InvalidKeyException("Wrong algorithm type"); ++ } ++ ++ } catch (InvalidKeySpecException e) { ++ throw new InvalidKeyException("Cannot translate key: " ++ + e.getMessage()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/DSAKeyPairGenerator.java b/org/mozilla/jss/netscape/security/provider/DSAKeyPairGenerator.java +new file mode 100644 +index 00000000..c4942dea +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/DSAKeyPairGenerator.java +@@ -0,0 +1,389 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.math.BigInteger; ++import java.security.AlgorithmParameterGenerator; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.InvalidKeyException; ++import java.security.InvalidParameterException; ++import java.security.KeyPair; ++import java.security.KeyPairGenerator; ++import java.security.NoSuchAlgorithmException; ++import java.security.NoSuchProviderException; ++import java.security.ProviderException; ++import java.security.SecureRandom; ++import java.security.interfaces.DSAParams; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.spec.DSAParameterSpec; ++import java.security.spec.InvalidParameterSpecException; ++import java.util.Hashtable; ++ ++import org.mozilla.jss.netscape.security.x509.AlgIdDSA; ++ ++/** ++ * This class generates DSA key parameters and public/private key ++ * pairs according to the DSS standard NIST FIPS 186. It uses the ++ * updated version of SHA, SHA-1 as described in FIPS 180-1. ++ * ++ * @author Benjamin Renaud ++ * ++ * @version 1.23, 97/12/10 ++ */ ++ ++public class DSAKeyPairGenerator extends KeyPairGenerator ++ implements java.security.interfaces.DSAKeyPairGenerator { ++ ++ private static Hashtable precomputedParams; ++ ++ static { ++ ++ /* We support precomputed parameter for 512, 768 and 1024 bit ++ moduli. In this file we provide both the seed and counter ++ value of the generation process for each of these seeds, ++ for validation purposes. We also include the test vectors ++ from the DSA specification, FIPS 186, and the FIPS 186 ++ Change No 1, which updates the test vector using SHA-1 ++ instead of SHA (for both the G function and the message ++ hash. ++ */ ++ ++ precomputedParams = new Hashtable(); ++ ++ /* ++ * L = 512 ++ * SEED = b869c82b35d70e1b1ff91b28e37a62ecdc34409b ++ * counter = 123 ++ */ ++ BigInteger p512 = ++ new BigInteger("fca682ce8e12caba26efccf7110e526db078b05edecb" + ++ "cd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e1" + ++ "2ed0899bcd132acd50d99151bdc43ee737592e17", 16); ++ ++ BigInteger q512 = ++ new BigInteger("962eddcc369cba8ebb260ee6b6a126d9346e38c5", 16); ++ ++ BigInteger g512 = ++ new BigInteger("678471b27a9cf44ee91a49c5147db1a9aaf244f05a43" + ++ "4d6486931d2d14271b9e35030b71fd73da179069b32e" + ++ "2935630e1c2062354d0da20a6c416e50be794ca4", 16); ++ ++ /* ++ * L = 768 ++ * SEED = 77d0f8c4dad15eb8c4f2f8d6726cefd96d5bb399 ++ * counter = 263 ++ */ ++ BigInteger p768 = ++ new BigInteger("e9e642599d355f37c97ffd3567120b8e25c9cd43e" + ++ "927b3a9670fbec5d890141922d2c3b3ad24800937" + ++ "99869d1e846aab49fab0ad26d2ce6a22219d470bc" + ++ "e7d777d4a21fbe9c270b57f607002f3cef8393694" + ++ "cf45ee3688c11a8c56ab127a3daf", 16); ++ ++ BigInteger q768 = ++ new BigInteger("9cdbd84c9f1ac2f38d0f80f42ab952e7338bf511", ++ 16); ++ ++ BigInteger g768 = ++ new BigInteger("30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5fac" + ++ "baecbe95f190aa7a31d23c4dbbcbe06174544401a" + ++ "5b2c020965d8c2bd2171d3668445771f74ba084d2" + ++ "029d83c1c158547f3a9f1a2715be23d51ae4d3e5a" + ++ "1f6a7064f316933a346d3f529252", 16); ++ ++ /* ++ * L = 1024 ++ * SEED = 8d5155894229d5e689ee01e6018a237e2cae64cd ++ * counter = 92 ++ */ ++ BigInteger p1024 = ++ new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523c" + ++ "ef4400c31e3f80b6512669455d402251fb593d8d58" + ++ "fabfc5f5ba30f6cb9b556cd7813b801d346ff26660" + ++ "b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" + ++ "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554" + ++ "135a169132f675f3ae2b61d72aeff22203199dd148" + ++ "01c7", 16); ++ ++ BigInteger q1024 = ++ new BigInteger("9760508f15230bccb292b982a2eb840bf0581cf5", ++ 16); ++ ++ BigInteger g1024 = ++ new BigInteger("f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa" + ++ "3aea82f9574c0b3d0782675159578ebad4594fe671" + ++ "07108180b449167123e84c281613b7cf09328cc8a6" + ++ "e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" + ++ "0bfa213562f1fb627a01243bcca4f1bea8519089a8" + ++ "83dfe15ae59f06928b665e807b552564014c3bfecf" + ++ "492a", 16); ++ ++ try { ++ AlgIdDSA alg512 = new AlgIdDSA(p512, q512, g512); ++ AlgIdDSA alg768 = new AlgIdDSA(p768, q768, g768); ++ AlgIdDSA alg1024 = new AlgIdDSA(p1024, q1024, g1024); ++ ++ precomputedParams.put(Integer.valueOf(512), alg512); ++ precomputedParams.put(Integer.valueOf(768), alg768); ++ precomputedParams.put(Integer.valueOf(1024), alg1024); ++ ++ } catch (Exception e) { ++ throw new InternalError("initializing precomputed " + ++ "algorithm parameters for Sun DSA"); ++ } ++ } ++ ++ /* The modulus length */ ++ private int modlen = 1024; ++ ++ /* Generate new parameters, even if we have precomputed ones. */ ++ boolean generateNewParameters = false; ++ ++ /* preset algorithm parameters. */ ++ private BigInteger presetP, presetQ, presetG; ++ ++ /* The source of random bits to use */ ++ SecureRandom random; ++ ++ public DSAKeyPairGenerator() { ++ super("DSA"); ++ } ++ ++ public void initialize(int strength, SecureRandom random) { ++ if ((strength < 512) || (strength > 1024) || (strength % 64 != 0)) { ++ throw new InvalidParameterException("Modulus size must range from 512 to 1024 " ++ + "and be a multiple of 64"); ++ } ++ ++ /* Set the random */ ++ this.random = random; ++ if (this.random == null) { ++ this.random = new SecureRandom(); ++ } ++ ++ this.modlen = strength; ++ DSAParams params = null; ++ ++ /* Find the precomputed parameters, if any */ ++ if (!generateNewParameters) { ++ Integer mod = Integer.valueOf(this.modlen); ++ params = precomputedParams.get(mod); ++ } ++ if (params != null) { ++ setParams(params); ++ } ++ } ++ ++ /** ++ * Initializes the DSA key pair generator. If genParams is false, a set of pre-computed parameters is ++ * used. In this case, modelen must be 512, 768, or 1024. ++ */ ++ public void initialize(int modlen, boolean genParams, SecureRandom random) ++ throws InvalidParameterException { ++ if (genParams == false && modlen != 512 && modlen != 768 ++ && modlen != 1024) { ++ throw new InvalidParameterException("No precomputed parameters for requested modulus size " ++ + "available"); ++ } ++ this.generateNewParameters = genParams; ++ initialize(modlen, random); ++ } ++ ++ /** ++ * Initializes the DSA object using a DSA parameter object. ++ * ++ * @param params a fully initialized DSA parameter object. ++ */ ++ public void initialize(DSAParams params, SecureRandom random) ++ throws InvalidParameterException { ++ initialize(params.getP().bitLength(), random); ++ setParams(params); ++ } ++ ++ /** ++ * Initializes the DSA object using a parameter object. ++ * ++ * @param params the parameter set to be used to generate ++ * the keys. ++ * @param random the source of randomness for this generator. ++ * ++ * @exception InvalidAlgorithmParameterException if the given parameters ++ * are inappropriate for this key pair generator ++ */ ++ public void initialize(AlgorithmParameterSpec params, SecureRandom random) ++ throws InvalidAlgorithmParameterException { ++ if (!(params instanceof DSAParameterSpec)) { ++ throw new InvalidAlgorithmParameterException("Inappropriate parameter"); ++ } ++ initialize(((DSAParameterSpec) params).getP().bitLength(), ++ random); ++ setParams((DSAParameterSpec) params); ++ } ++ ++ /** ++ * Generates a pair of keys usable by any JavaSecurity compliant ++ * DSA implementation. ++ * ++ * @param rnd the source of random bits from which the random key ++ * generation parameters are drawn. In particular, this includes ++ * the XSEED parameter. ++ * ++ * @exception InvalidParameterException if the modulus is not ++ * between 512 and 1024. ++ */ ++ public KeyPair generateKeyPair() { ++ ++ // set random if initialize() method has been skipped ++ if (this.random == null) { ++ this.random = new SecureRandom(); ++ } ++ ++ if (presetP == null || presetQ == null || presetG == null || ++ generateNewParameters) { ++ ++ AlgorithmParameterGenerator dsaParamGen; ++ ++ try { ++ dsaParamGen = AlgorithmParameterGenerator.getInstance("DSA", ++ "SUN"); ++ } catch (NoSuchAlgorithmException e) { ++ // this should never happen, because we provide it ++ throw new RuntimeException(e.getMessage()); ++ } catch (NoSuchProviderException e) { ++ // this should never happen, because we provide it ++ throw new RuntimeException(e.getMessage()); ++ } ++ ++ dsaParamGen.init(modlen, random); ++ ++ DSAParameterSpec dsaParamSpec; ++ try { ++ dsaParamSpec = dsaParamGen.generateParameters().getParameterSpec ++ (DSAParameterSpec.class); ++ } catch (InvalidParameterSpecException e) { ++ // this should never happen ++ throw new RuntimeException(e.getMessage()); ++ } ++ presetP = dsaParamSpec.getP(); ++ presetQ = dsaParamSpec.getQ(); ++ presetG = dsaParamSpec.getG(); ++ } ++ ++ return generateKeyPair(presetP, presetQ, presetG, random); ++ } ++ ++ public KeyPair generateKeyPair(BigInteger p, BigInteger q, BigInteger g, ++ SecureRandom random) { ++ ++ BigInteger x = generateX(random, q); ++ BigInteger y = generateY(x, p, g); ++ ++ try { ++ DSAPublicKey pub = new DSAPublicKey(y, p, q, g); ++ DSAPrivateKey priv = new DSAPrivateKey(x, p, q, g); ++ ++ KeyPair pair = new KeyPair(pub, priv); ++ return pair; ++ ++ } catch (InvalidKeyException e) { ++ throw new ProviderException(e.getMessage()); ++ } ++ } ++ ++ /** ++ * Generate the private key component of the key pair using the ++ * provided source of random bits. This method uses the random but ++ * source passed to generate a seed and then calls the seed-based ++ * generateX method. ++ */ ++ private BigInteger generateX(SecureRandom random, BigInteger q) { ++ BigInteger x = null; ++ while (true) { ++ int[] seed = new int[5]; ++ for (int i = 0; i < 5; i++) { ++ seed[i] = random.nextInt(); ++ } ++ x = generateX(seed, q); ++ if (x.signum() > 0 && (x.compareTo(q) < 0)) { ++ break; ++ } ++ } ++ return x; ++ } ++ ++ /** ++ * Given a seed, generate the private key component of the key ++ * pair. In the terminology used in the DSA specification ++ * (FIPS-186) seed is the XSEED quantity. ++ * ++ * @param seed the seed to use to generate the private key. ++ */ ++ BigInteger generateX(int[] seed, BigInteger q) { ++ ++ /* Test vector ++ int[] tseed = { 0xbd029bbe, 0x7f51960b, 0xcf9edb2b, ++ 0x61f06f0f, 0xeb5a38b6 }; ++ seed = tseed; ++ */ ++ // check out t in the spec. ++ int[] t = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, ++ 0x10325476, 0xC3D2E1F0 }; ++ // ++ ++ int[] tmp = DSA.SHA_7(seed, t); ++ byte[] tmpBytes = new byte[tmp.length * 4]; ++ for (int i = 0; i < tmp.length; i++) { ++ int k = tmp[i]; ++ for (int j = 0; j < 4; j++) { ++ tmpBytes[(i * 4) + j] = (byte) (k >>> (24 - (j * 8))); ++ } ++ } ++ BigInteger x = new BigInteger(1, tmpBytes).mod(q); ++ return x; ++ } ++ ++ /** ++ * Generate the public key component y of the key pair. ++ * ++ * @param x the private key component. ++ * ++ * @param p the base parameter. ++ */ ++ BigInteger generateY(BigInteger x, BigInteger p, BigInteger g) { ++ BigInteger y = g.modPow(x, p); ++ return y; ++ } ++ ++ /** ++ * Set the parameters. ++ */ ++ private void setParams(DSAParams params) { ++ presetP = params.getP(); ++ presetQ = params.getQ(); ++ presetG = params.getG(); ++ } ++ ++ /** ++ * Set the parameters. ++ */ ++ private void setParams(DSAParameterSpec params) { ++ presetP = params.getP(); ++ presetQ = params.getQ(); ++ presetG = params.getG(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/DSAParameterGenerator.java b/org/mozilla/jss/netscape/security/provider/DSAParameterGenerator.java +new file mode 100755 +index 00000000..5cd86c79 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/DSAParameterGenerator.java +@@ -0,0 +1,298 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.math.BigInteger; ++import java.security.AlgorithmParameterGeneratorSpi; ++import java.security.AlgorithmParameters; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.InvalidParameterException; ++import java.security.NoSuchAlgorithmException; ++import java.security.NoSuchProviderException; ++import java.security.SecureRandom; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.spec.DSAParameterSpec; ++import java.security.spec.InvalidParameterSpecException; ++ ++/* ++ * This class generates parameters for the DSA algorithm. It uses a default ++ * prime modulus size of 1024 bits, which can be overwritten during ++ * initialization. ++ * ++ * @author Jan Luehe ++ * ++ * @version 1.4, 97/12/10 ++ * ++ * @see java.security.AlgorithmParameters ++ * @see java.security.spec.AlgorithmParameterSpec ++ * @see DSAParameters ++ * ++ * @since JDK1.2 ++ */ ++ ++public class DSAParameterGenerator extends AlgorithmParameterGeneratorSpi { ++ ++ // the modulus length ++ private int modLen = 1024; // default ++ ++ // the source of randomness ++ private SecureRandom random; ++ ++ // useful constants ++ private static final BigInteger ONE = BigInteger.valueOf(1); ++ private static final BigInteger TWO = BigInteger.valueOf(2); ++ ++ // Make a SHA-1 hash function ++ private SHA sha; ++ ++ public DSAParameterGenerator() { ++ this.sha = new SHA(); ++ } ++ ++ /** ++ * Initializes this parameter generator for a certain strength ++ * and source of randomness. ++ * ++ * @param strength the strength (size of prime) in bits ++ * @param random the source of randomness ++ */ ++ protected void engineInit(int strength, SecureRandom random) { ++ /* ++ * Bruce Schneier, "Applied Cryptography", 2nd Edition, ++ * Description of DSA: ++ * [...] The algorithm uses the following parameter: ++ * p=a prime number L bits long, when L ranges from 512 to 1024 and is ++ * a multiple of 64. [...] ++ */ ++ if ((strength < 512) || (strength > 1024) || (strength % 64 != 0)) { ++ throw new InvalidParameterException("Prime size must range from 512 to 1024 " ++ + "and be a multiple of 64"); ++ } ++ this.modLen = strength; ++ this.random = random; ++ } ++ ++ /** ++ * Initializes this parameter generator with a set of ++ * algorithm-specific parameter generation values. ++ * ++ * @param params the set of algorithm-specific parameter generation values ++ * @param random the source of randomness ++ * ++ * @exception InvalidAlgorithmParameterException if the given parameter ++ * generation values are inappropriate for this parameter generator ++ */ ++ protected void engineInit(AlgorithmParameterSpec genParamSpec, ++ SecureRandom random) ++ throws InvalidAlgorithmParameterException { ++ throw new InvalidAlgorithmParameterException("Invalid parameter"); ++ } ++ ++ /** ++ * Generates the parameters. ++ * ++ * @return the new AlgorithmParameters object ++ */ ++ protected AlgorithmParameters engineGenerateParameters() { ++ AlgorithmParameters algParams = null; ++ try { ++ if (this.random == null) { ++ this.random = new SecureRandom(); ++ } ++ ++ BigInteger[] pAndQ = generatePandQ(this.random, this.modLen); ++ BigInteger paramP = pAndQ[0]; ++ BigInteger paramQ = pAndQ[1]; ++ BigInteger paramG = generateG(paramP, paramQ); ++ ++ DSAParameterSpec dsaParamSpec = new DSAParameterSpec(paramP, ++ paramQ, ++ paramG); ++ algParams = AlgorithmParameters.getInstance("DSA", "SUN"); ++ algParams.init(dsaParamSpec); ++ } catch (InvalidParameterSpecException e) { ++ // this should never happen ++ throw new RuntimeException(e.getMessage()); ++ } catch (NoSuchAlgorithmException e) { ++ // this should never happen, because we provide it ++ throw new RuntimeException(e.getMessage()); ++ } catch (NoSuchProviderException e) { ++ // this should never happen, because we provide it ++ throw new RuntimeException(e.getMessage()); ++ } ++ ++ return algParams; ++ } ++ ++ /* ++ * Generates the prime and subprime parameters for DSA, ++ * using the provided source of randomness. ++ * This method will generate new seeds until a suitable ++ * seed has been found. ++ * ++ * @param random the source of randomness to generate the ++ * seed ++ * @param L the size of p, in bits. ++ * ++ * @return an array of BigInteger, with p at index 0 and ++ * q at index 1. ++ */ ++ BigInteger[] generatePandQ(SecureRandom random, int L) { ++ BigInteger[] result = null; ++ byte[] seed = new byte[20]; ++ ++ while (result == null) { ++ for (int i = 0; i < 20; i++) { ++ seed[i] = (byte) random.nextInt(); ++ } ++ result = generatePandQ(seed, L); ++ } ++ return result; ++ } ++ ++ /* ++ * Generates the prime and subprime parameters for DSA. ++ * ++ *

The seed parameter corresponds to the SEED parameter ++ * referenced in the FIPS specification of the DSA algorithm, ++ * and L is the size of p, in bits. ++ * ++ * @param seed the seed to generate the parameters ++ * @param L the size of p, in bits. ++ * ++ * @return an array of BigInteger, with p at index 0, ++ * q at index 1, the seed at index 2, and the counter value ++ * at index 3, or null if the seed does not yield suitable numbers. ++ */ ++ BigInteger[] generatePandQ(byte[] seed, int L) { ++ ++ /* Useful variables */ ++ int g = seed.length * 8; ++ int n = (L - 1) / 160; ++ int b = (L - 1) % 160; ++ ++ BigInteger SEED = new BigInteger(1, seed); ++ BigInteger TWOG = TWO.pow(2 * g); ++ ++ /* Step 2 (Step 1 is getting seed). */ ++ byte[] U1 = SHA(seed); ++ byte[] U2 = SHA(toByteArray((SEED.add(ONE)).mod(TWOG))); ++ ++ xor(U1, U2); ++ byte[] U = U1; ++ ++ /* Step 3: For q by setting the msb and lsb to 1 */ ++ U[0] |= 0x80; ++ U[19] |= 1; ++ BigInteger q = new BigInteger(1, U); ++ ++ /* Step 5 */ ++ if (!q.isProbablePrime(40)) { ++ return null; ++ ++ } else { ++ BigInteger V[] = new BigInteger[n + 1]; ++ BigInteger offset = TWO; ++ ++ /* Step 6 */ ++ for (int counter = 0; counter < 4096; counter++) { ++ ++ /* Step 7 */ ++ for (int k = 0; k <= n; k++) { ++ BigInteger K = BigInteger.valueOf(k); ++ BigInteger tmp = (SEED.add(offset).add(K)).mod(TWOG); ++ V[k] = new BigInteger(1, SHA(toByteArray(tmp))); ++ } ++ ++ /* Step 8 */ ++ BigInteger W = V[0]; ++ for (int i = 1; i < n; i++) { ++ W = W.add(V[i].multiply(TWO.pow(i * 160))); ++ } ++ W = W.add((V[n].mod(TWO.pow(b))).multiply(TWO.pow(n * 160))); ++ ++ BigInteger TWOLm1 = TWO.pow(L - 1); ++ BigInteger X = W.add(TWOLm1); ++ ++ /* Step 9 */ ++ BigInteger c = X.mod(q.multiply(TWO)); ++ BigInteger p = X.subtract(c.subtract(ONE)); ++ ++ /* Step 10 - 13 */ ++ if (p.compareTo(TWOLm1) > -1 && p.isProbablePrime(15)) { ++ BigInteger[] result = { p, q, SEED, ++ BigInteger.valueOf(counter) }; ++ return result; ++ } ++ offset = offset.add(BigInteger.valueOf(n)).add(ONE); ++ } ++ return null; ++ } ++ } ++ ++ /* ++ * Generates the g parameter for DSA. ++ * ++ * @param p the prime, p. ++ * @param q the subprime, q. ++ * ++ * @param the g ++ */ ++ BigInteger generateG(BigInteger p, BigInteger q) { ++ BigInteger h = ONE; ++ BigInteger pMinusOneOverQ = (p.subtract(ONE)).divide(q); ++ BigInteger g = ONE; ++ while (g.compareTo(TWO) < 0) { ++ g = h.modPow(pMinusOneOverQ, p); ++ h = h.add(ONE); ++ } ++ return g; ++ } ++ ++ /* ++ * Returns the SHA-1 digest of some data ++ */ ++ private byte[] SHA(byte[] array) { ++ sha.engineReset(); ++ sha.engineUpdate(array, 0, array.length); ++ return sha.engineDigest(); ++ } ++ ++ /* ++ * Converts the result of a BigInteger.toByteArray call to an exact ++ * signed magnitude representation for any positive number. ++ */ ++ private byte[] toByteArray(BigInteger bigInt) { ++ byte[] result = bigInt.toByteArray(); ++ if (result[0] == 0) { ++ byte[] tmp = new byte[result.length - 1]; ++ System.arraycopy(result, 1, tmp, 0, tmp.length); ++ result = tmp; ++ } ++ return result; ++ } ++ ++ /* ++ * XORs U2 into U1 ++ */ ++ private void xor(byte[] U1, byte[] U2) { ++ for (int i = 0; i < U1.length; i++) { ++ U1[i] ^= U2[i]; ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/DSAParameters.java b/org/mozilla/jss/netscape/security/provider/DSAParameters.java +new file mode 100755 +index 00000000..c457698b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/DSAParameters.java +@@ -0,0 +1,131 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.io.IOException; ++import java.math.BigInteger; ++import java.security.AlgorithmParametersSpi; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.spec.DSAParameterSpec; ++import java.security.spec.InvalidParameterSpecException; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class implements the parameter set used by the ++ * Digital Signature Algorithm as specified in the FIPS 186 ++ * standard. ++ * ++ * @author Jan Luehe ++ * ++ * @version 1.8, 97/12/10 ++ * ++ * @since JDK1.2 ++ */ ++ ++public class DSAParameters extends AlgorithmParametersSpi { ++ ++ // the prime (p) ++ protected BigInteger p; ++ ++ // the sub-prime (q) ++ protected BigInteger q; ++ ++ // the base (g) ++ protected BigInteger g; ++ ++ protected void engineInit(AlgorithmParameterSpec paramSpec) ++ throws InvalidParameterSpecException { ++ if (!(paramSpec instanceof DSAParameterSpec)) { ++ throw new InvalidParameterSpecException("Inappropriate parameter specification"); ++ } ++ this.p = ((DSAParameterSpec) paramSpec).getP(); ++ this.q = ((DSAParameterSpec) paramSpec).getQ(); ++ this.g = ((DSAParameterSpec) paramSpec).getG(); ++ } ++ ++ protected void engineInit(byte[] params) throws IOException { ++ DerValue encodedParams = new DerValue(params); ++ ++ if (encodedParams.tag != DerValue.tag_Sequence) { ++ throw new IOException("DSA params parsing error"); ++ } ++ ++ encodedParams.data.reset(); ++ ++ this.p = encodedParams.data.getInteger().toBigInteger(); ++ this.q = encodedParams.data.getInteger().toBigInteger(); ++ this.g = encodedParams.data.getInteger().toBigInteger(); ++ ++ if (encodedParams.data.available() != 0) { ++ throw new IOException("encoded params have " + ++ encodedParams.data.available() + ++ " extra bytes"); ++ } ++ } ++ ++ protected void engineInit(byte[] params, String decodingMethod) ++ throws IOException { ++ engineInit(params); ++ } ++ ++ @SuppressWarnings("unchecked") ++ protected T engineGetParameterSpec(Class paramSpec) ++ throws InvalidParameterSpecException { ++ try { ++ Class dsaParamSpec = Class.forName ++ ("java.security.spec.DSAParameterSpec"); ++ if (dsaParamSpec.isAssignableFrom(paramSpec)) { ++ return (T) new DSAParameterSpec(this.p, this.q, this.g); ++ } else { ++ throw new InvalidParameterSpecException("Inappropriate parameter Specification"); ++ } ++ } catch (ClassNotFoundException e) { ++ throw new InvalidParameterSpecException("Unsupported parameter specification: " + e.getMessage()); ++ } ++ } ++ ++ protected byte[] engineGetEncoded() throws IOException { ++ try (DerOutputStream out = new DerOutputStream()) { ++ DerOutputStream bytes = new DerOutputStream(); ++ ++ bytes.putInteger(new BigInt(p.toByteArray())); ++ bytes.putInteger(new BigInt(q.toByteArray())); ++ bytes.putInteger(new BigInt(g.toByteArray())); ++ out.write(DerValue.tag_Sequence, bytes); ++ return out.toByteArray(); ++ } ++ } ++ ++ protected byte[] engineGetEncoded(String encodingMethod) ++ throws IOException { ++ return engineGetEncoded(); ++ } ++ ++ /* ++ * Returns a formatted string describing the parameters. ++ */ ++ protected String engineToString() { ++ return "\n\tp: " + new BigInt(p).toString() ++ + "\n\tq: " + new BigInt(q).toString() ++ + "\n\tg: " + new BigInt(g).toString() ++ + "\n"; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/DSAPrivateKey.java b/org/mozilla/jss/netscape/security/provider/DSAPrivateKey.java +new file mode 100644 +index 00000000..6db2dbf6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/DSAPrivateKey.java +@@ -0,0 +1,170 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.io.IOException; ++import java.io.Serializable; ++import java.math.BigInteger; ++import java.security.AlgorithmParameters; ++import java.security.InvalidKeyException; ++import java.security.interfaces.DSAParams; ++import java.security.spec.DSAParameterSpec; ++import java.security.spec.InvalidParameterSpecException; ++ ++import org.mozilla.jss.netscape.security.pkcs.PKCS8Key; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.x509.AlgIdDSA; ++ ++/** ++ * A PKCS#8 private key for the Digital Signature Algorithm. ++ * ++ * @author Benjamin Renaud ++ * ++ * @version 1.47, 97/12/10 ++ * ++ * @see DSAPublicKey ++ * @see AlgIdDSA ++ * @see DSA ++ */ ++ ++public final class DSAPrivateKey extends PKCS8Key ++ implements java.security.interfaces.DSAPrivateKey, Serializable { ++ ++ /** use serialVersionUID from JDK 1.1. for interoperability */ ++ private static final long serialVersionUID = -3244453684193605938L; ++ ++ /* the private key */ ++ private BigInteger x; ++ ++ /* ++ * Keep this constructor for backwards compatibility with JDK1.1. ++ */ ++ public DSAPrivateKey() { ++ } ++ ++ /** ++ * Make a DSA private key out of a private key and three parameters. ++ */ ++ public DSAPrivateKey(BigInteger x, BigInteger p, ++ BigInteger q, BigInteger g) ++ throws InvalidKeyException { ++ this.x = x; ++ algid = new AlgIdDSA(p, q, g); ++ ++ try { ++ key = new DerValue(DerValue.tag_Integer, ++ x.toByteArray()).toByteArray(); ++ encode(); ++ } catch (IOException e) { ++ throw new InvalidKeyException("could not DER encode x: " + ++ e.getMessage()); ++ } ++ } ++ ++ /** ++ * Make a DSA private key from its DER encoding (PKCS #8). ++ */ ++ public DSAPrivateKey(byte[] encoded) throws InvalidKeyException { ++ clearOldKey(); ++ decode(encoded); ++ } ++ ++ /** ++ * Returns the DSA parameters associated with this key, or null if the ++ * parameters could not be parsed. ++ */ ++ public DSAParams getParams() { ++ try { ++ if (algid instanceof DSAParams) { ++ return (DSAParams) algid; ++ } else { ++ AlgorithmParameters algParams = algid.getParameters(); ++ if (algParams == null) { ++ return null; ++ } ++ return algParams.getParameterSpec(DSAParameterSpec.class); ++ } ++ } catch (InvalidParameterSpecException e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Get the raw private key, x, without the parameters. ++ * ++ */ ++ public BigInteger getX() { ++ return x; ++ } ++ ++ private void clearOldKey() { ++ int i; ++ if (this.encodedKey != null) { ++ for (i = 0; i < this.encodedKey.length; i++) { ++ this.encodedKey[i] = (byte) 0x00; ++ } ++ } ++ if (this.key != null) { ++ for (i = 0; i < this.key.length; i++) { ++ this.key[i] = (byte) 0x00; ++ } ++ } ++ } ++ ++ public String toString() { ++ return "Sun DSA Private Key \nparameters:" + algid + "\nx: " + ++ x.toString(16) + "\n"; ++ } ++ ++ protected void parseKeyBits() throws InvalidKeyException { ++ DerInputStream in = new DerInputStream(key); ++ ++ try { ++ x = in.getInteger().toBigInteger(); ++ } catch (IOException e) { ++ throw new InvalidKeyException(e.getMessage()); ++ } ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = super.hashCode(); ++ result = prime * result + ((x == null) ? 0 : x.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (!super.equals(obj)) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ DSAPrivateKey other = (DSAPrivateKey) obj; ++ if (x == null) { ++ if (other.x != null) ++ return false; ++ } else if (!x.equals(other.x)) ++ return false; ++ return true; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/provider/DSAPublicKey.java b/org/mozilla/jss/netscape/security/provider/DSAPublicKey.java +new file mode 100644 +index 00000000..b05210e2 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/DSAPublicKey.java +@@ -0,0 +1,159 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.io.IOException; ++import java.io.Serializable; ++import java.math.BigInteger; ++import java.security.AlgorithmParameters; ++import java.security.InvalidKeyException; ++import java.security.interfaces.DSAParams; ++import java.security.spec.DSAParameterSpec; ++import java.security.spec.InvalidParameterSpecException; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.x509.AlgIdDSA; ++import org.mozilla.jss.netscape.security.x509.X509Key; ++ ++/** ++ * An X.509 public key for the Digital Signature Algorithm. ++ * ++ * @author Benjamin Renaud ++ * ++ * @version 1.52, 97/12/10 ++ * ++ * @see DSAPrivateKey ++ * @see AlgIdDSA ++ * @see DSA ++ */ ++ ++public final class DSAPublicKey extends X509Key ++ implements java.security.interfaces.DSAPublicKey, Serializable { ++ ++ /** use serialVersionUID from JDK 1.1. for interoperability */ ++ private static final long serialVersionUID = -2994193307391104133L; ++ ++ /* the public key */ ++ private BigInteger y; ++ ++ /* ++ * Keep this constructor for backwards compatibility with JDK1.1. ++ */ ++ public DSAPublicKey() { ++ } ++ ++ /** ++ * Make a DSA public key out of a public key and three parameters. ++ */ ++ public DSAPublicKey(BigInteger y, BigInteger p, BigInteger q, ++ BigInteger g) ++ throws InvalidKeyException { ++ this.y = y; ++ algid = new AlgIdDSA(p, q, g); ++ ++ try { ++ key = new DerValue(DerValue.tag_Integer, ++ y.toByteArray()).toByteArray(); ++ encode(); ++ } catch (IOException e) { ++ throw new InvalidKeyException("could not DER encode y: " + ++ e.getMessage()); ++ } ++ } ++ ++ /** ++ * Make a DSA public key from its DER encoding (X.509). ++ */ ++ public DSAPublicKey(byte[] encoded) throws InvalidKeyException { ++ decode(encoded); ++ } ++ ++ /** ++ * Returns the DSA parameters associated with this key, or null if the ++ * parameters could not be parsed. ++ */ ++ public DSAParams getParams() { ++ try { ++ if (algid instanceof DSAParams) { ++ return (DSAParams) algid; ++ } else { ++ DSAParameterSpec paramSpec; ++ AlgorithmParameters algParams = algid.getParameters(); ++ if (algParams == null) { ++ return null; ++ } ++ paramSpec = algParams.getParameterSpec ++ (DSAParameterSpec.class); ++ return paramSpec; ++ } ++ } catch (InvalidParameterSpecException e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Get the raw public value, y, without the parameters. ++ * ++ */ ++ public BigInteger getY() { ++ return y; ++ } ++ ++ public String toString() { ++ return "Sun DSA Public Key\n Parameters:" + algid ++ + "\n y:\n" + (new BigInt(y)).toString() + "\n"; ++ } ++ ++ protected void parseKeyBits() throws InvalidKeyException { ++ try { ++ DerInputStream in = new DerInputStream(key); ++ y = in.getInteger().toBigInteger(); ++ } catch (IOException e) { ++ throw new InvalidKeyException("Invalid key: y value\n" + ++ e.getMessage()); ++ } ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = super.hashCode(); ++ result = prime * result + ((y == null) ? 0 : y.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (!super.equals(obj)) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ DSAPublicKey other = (DSAPublicKey) obj; ++ if (y == null) { ++ if (other.y != null) ++ return false; ++ } else if (!y.equals(other.y)) ++ return false; ++ return true; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/provider/MD5.java b/org/mozilla/jss/netscape/security/provider/MD5.java +new file mode 100644 +index 00000000..d46da4fc +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/MD5.java +@@ -0,0 +1,378 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.security.DigestException; ++import java.security.MessageDigestSpi; ++ ++/** ++ * The MD5 class is used to compute an MD5 message digest over a given ++ * buffer of bytes. It is an implementation of the RSA Data Security Inc ++ * MD5 algorithim as described in internet RFC 1321. ++ * ++ * @version 1.24 97/12/10 ++ * @author Chuck McManis ++ * @author Benjamin Renaud ++ */ ++ ++public final class MD5 extends MessageDigestSpi implements Cloneable { ++ ++ /** contains the computed message digest */ ++ private byte[] digestBits; ++ ++ private int state[]; ++ private long count; // bit count AND buffer[] index aid ++ private byte buffer[]; ++ private int transformBuffer[]; ++ ++ private static final int S11 = 7; ++ private static final int S12 = 12; ++ private static final int S13 = 17; ++ private static final int S14 = 22; ++ private static final int S21 = 5; ++ private static final int S22 = 9; ++ private static final int S23 = 14; ++ private static final int S24 = 20; ++ private static final int S31 = 4; ++ private static final int S32 = 11; ++ private static final int S33 = 16; ++ private static final int S34 = 23; ++ private static final int S41 = 6; ++ private static final int S42 = 10; ++ private static final int S43 = 15; ++ private static final int S44 = 21; ++ ++ private static final int MD5_LENGTH = 16; ++ ++ /** ++ * Standard constructor, creates a new MD5 instance, allocates its ++ * buffers from the heap. ++ */ ++ public MD5() { ++ init(); ++ } ++ ++ /* ********************************************************** ++ * The MD5 Functions. These are copied verbatim from ++ * the RFC to insure accuracy. The results of this ++ * implementation were checked against the RSADSI version. ++ * ********************************************************** ++ */ ++ ++ private int F(int x, int y, int z) { ++ return ((x & y) | ((~x) & z)); ++ } ++ ++ private int G(int x, int y, int z) { ++ return ((x & z) | (y & (~z))); ++ } ++ ++ private int H(int x, int y, int z) { ++ return ((x ^ y) ^ z); ++ } ++ ++ private int I(int x, int y, int z) { ++ return (y ^ (x | (~z))); ++ } ++ ++ private int rotateLeft(int a, int n) { ++ return ((a << n) | (a >>> (32 - n))); ++ } ++ ++ private int FF(int a, int b, int c, int d, int x, int s, int ac) { ++ a += F(b, c, d) + x + ac; ++ a = rotateLeft(a, s); ++ a += b; ++ return a; ++ } ++ ++ private int GG(int a, int b, int c, int d, int x, int s, int ac) { ++ a += G(b, c, d) + x + ac; ++ a = rotateLeft(a, s); ++ a += b; ++ return a; ++ } ++ ++ private int HH(int a, int b, int c, int d, int x, int s, int ac) { ++ a += H(b, c, d) + x + ac; ++ a = rotateLeft(a, s); ++ a += b; ++ return a; ++ } ++ ++ private int II(int a, int b, int c, int d, int x, int s, int ac) { ++ a += I(b, c, d) + x + ac; ++ a = rotateLeft(a, s); ++ a += b; ++ return a; ++ } ++ ++ /** ++ * This is where the functions come together as the generic MD5 ++ * transformation operation, it is called by update() which is ++ * synchronized (to protect transformBuffer). It consumes sixteen ++ * bytes from the buffer, beginning at the specified offset. ++ */ ++ void transform(byte buf[], int offset) { ++ int a, b, c, d; ++ int x[] = transformBuffer; ++ ++ a = state[0]; ++ b = state[1]; ++ c = state[2]; ++ d = state[3]; ++ ++ for (int i = 0; i < 16; i++) { ++ x[i] = buf[i * 4 + offset] & 0xff; ++ for (int j = 1; j < 4; j++) { ++ x[i] += (buf[i * 4 + j + offset] & 0xff) << (j * 8); ++ } ++ } ++ ++ /* Round 1 */ ++ a = FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ ++ d = FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ ++ c = FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ ++ b = FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ ++ a = FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ ++ d = FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ ++ c = FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ ++ b = FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ ++ a = FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ ++ d = FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ ++ c = FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ ++ b = FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ ++ a = FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ ++ d = FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ ++ c = FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ ++ b = FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ ++ ++ /* Round 2 */ ++ a = GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ ++ d = GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ ++ c = GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ ++ b = GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ ++ a = GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ ++ d = GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ ++ c = GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ ++ b = GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ ++ a = GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ ++ d = GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ ++ c = GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ ++ b = GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ ++ a = GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ ++ d = GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ ++ c = GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ ++ b = GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ ++ ++ /* Round 3 */ ++ a = HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ ++ d = HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ ++ c = HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ ++ b = HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ ++ a = HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ ++ d = HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ ++ c = HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ ++ b = HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ ++ a = HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ ++ d = HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ ++ c = HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ ++ b = HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ ++ a = HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ ++ d = HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ ++ c = HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ ++ b = HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ ++ ++ /* Round 4 */ ++ a = II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ ++ d = II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ ++ c = II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ ++ b = II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ ++ a = II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ ++ d = II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ ++ c = II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ ++ b = II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ ++ a = II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ ++ d = II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ ++ c = II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ ++ b = II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ ++ a = II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ ++ d = II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ ++ c = II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ ++ b = II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ ++ ++ state[0] += a; ++ state[1] += b; ++ state[2] += c; ++ state[3] += d; ++ } ++ ++ /** ++ * Initialize the MD5 state information and reset the bit count ++ * to 0. Given this implementation you are constrained to counting ++ * 2^64 bits. ++ */ ++ public void init() { ++ state = new int[4]; ++ transformBuffer = new int[16]; ++ synchronized (this) { ++ buffer = new byte[64]; ++ } ++ digestBits = new byte[16]; ++ count = 0; ++ // Load magic initialization constants. ++ state[0] = 0x67452301; ++ state[1] = 0xefcdab89; ++ state[2] = 0x98badcfe; ++ state[3] = 0x10325476; ++ for (int i = 0; i < digestBits.length; i++) ++ digestBits[i] = 0; ++ } ++ ++ protected void engineReset() { ++ init(); ++ } ++ ++ /** ++ * Return the digest length in bytes ++ */ ++ protected int engineGetDigestLength() { ++ return (MD5_LENGTH); ++ } ++ ++ /** ++ * Update adds the passed byte to the digested data. ++ */ ++ protected synchronized void engineUpdate(byte b) { ++ int index; ++ ++ index = (int) ((count >>> 3) & 0x3f); ++ count += 8; ++ buffer[index] = b; ++ if (index >= 63) { ++ transform(buffer, 0); ++ } ++ } ++ ++ /** ++ * Update adds the selected part of an array of bytes to the digest. ++ * This version is more efficient than the byte-at-a-time version; ++ * it avoids data copies and reduces per-byte call overhead. ++ */ ++ protected synchronized void engineUpdate(byte input[], int offset, ++ int len) { ++ int i; ++ ++ for (i = offset; len > 0;) { ++ int index = (int) ((count >>> 3) & 0x3f); ++ ++ if (index == 0 && len > 64) { ++ count += (64 * 8); ++ transform(input, i); ++ len -= 64; ++ i += 64; ++ } else { ++ count += 8; ++ buffer[index] = input[i]; ++ if (index >= 63) ++ transform(buffer, 0); ++ i++; ++ len--; ++ } ++ } ++ } ++ ++ /** ++ * Perform the final computations, any buffered bytes are added ++ * to the digest, the count is added to the digest, and the resulting ++ * digest is stored. After calling final you will need to call ++ * init() again to do another digest. ++ */ ++ private void finish() { ++ byte bits[] = new byte[8]; ++ byte padding[]; ++ int i, index, padLen; ++ ++ for (i = 0; i < 8; i++) { ++ bits[i] = (byte) ((count >>> (i * 8)) & 0xff); ++ } ++ ++ index = (int) (count >> 3) & 0x3f; ++ padLen = (index < 56) ? (56 - index) : (120 - index); ++ padding = new byte[padLen]; ++ padding[0] = (byte) 0x80; ++ engineUpdate(padding, 0, padding.length); ++ engineUpdate(bits, 0, bits.length); ++ ++ for (i = 0; i < 4; i++) { ++ for (int j = 0; j < 4; j++) { ++ digestBits[i * 4 + j] = (byte) ((state[i] >>> (j * 8)) & 0xff); ++ } ++ } ++ } ++ ++ /** ++ */ ++ protected byte[] engineDigest() { ++ finish(); ++ ++ byte[] result = new byte[MD5_LENGTH]; ++ System.arraycopy(digestBits, 0, result, 0, MD5_LENGTH); ++ ++ init(); ++ ++ return result; ++ } ++ ++ /** ++ */ ++ protected int engineDigest(byte[] buf, int offset, int len) ++ throws DigestException { ++ finish(); ++ ++ if (len < MD5_LENGTH) ++ throw new DigestException("partial digests not returned"); ++ if (buf.length - offset < MD5_LENGTH) ++ throw new DigestException("insufficient space in the output " + ++ "buffer to store the digest"); ++ ++ System.arraycopy(digestBits, 0, buf, offset, MD5_LENGTH); ++ ++ init(); ++ ++ return MD5_LENGTH; ++ } ++ ++ /* ++ * Clones this object. ++ */ ++ public Object clone() { ++ MD5 that = null; ++ try { ++ that = (MD5) super.clone(); ++ that.state = this.state.clone(); ++ that.transformBuffer = this.transformBuffer.clone(); ++ that.buffer = this.buffer.clone(); ++ that.digestBits = this.digestBits.clone(); ++ that.count = this.count; ++ return that; ++ } catch (CloneNotSupportedException e) { ++ } ++ return that; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/Makefile b/org/mozilla/jss/netscape/security/provider/Makefile +new file mode 100644 +index 00000000..8fd7054b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/Makefile +@@ -0,0 +1,45 @@ ++#! gmake ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### +diff --git a/org/mozilla/jss/netscape/security/provider/RSAPublicKey.java b/org/mozilla/jss/netscape/security/provider/RSAPublicKey.java +new file mode 100644 +index 00000000..2c5886ac +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/RSAPublicKey.java +@@ -0,0 +1,193 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.io.IOException; ++import java.io.Serializable; ++import java.security.InvalidKeyException; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.x509.AlgorithmId; ++import org.mozilla.jss.netscape.security.x509.X509Key; ++ ++/** ++ * An X.509 public key for the RSA Algorithm. ++ * ++ * @author galperin ++ * ++ * @version $Revision$, $Date$ ++ * ++ */ ++ ++public final class RSAPublicKey extends X509Key implements Serializable { ++ ++ /* XXX This currently understands only PKCS#1 RSA Encryption OID ++ and parameter format ++ Later we may consider adding X509v3 OID for RSA keys. Besides ++ different OID it also has a parameter equal to modulus size ++ in bits (redundant!) ++ */ ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 7764823589128565374L; ++ ++ private static final ObjectIdentifier ALGORITHM_OID = ++ AlgorithmId.RSAEncryption_oid; ++ ++ private BigInt modulus; ++ private BigInt publicExponent; ++ ++ /* ++ * Keep this constructor for backwards compatibility with JDK1.1. ++ */ ++ public RSAPublicKey() { ++ } ++ ++ /** ++ * Make a RSA public key out of a public exponent and modulus ++ */ ++ public RSAPublicKey(BigInt modulus, BigInt publicExponent) ++ throws InvalidKeyException { ++ this.modulus = modulus; ++ this.publicExponent = publicExponent; ++ this.algid = new AlgorithmId(ALGORITHM_OID); ++ ++ try (DerOutputStream out = new DerOutputStream()) { ++ out.putInteger(modulus); ++ out.putInteger(publicExponent); ++ key = (new DerValue(DerValue.tag_Sequence, ++ out.toByteArray())).toByteArray(); ++ encode(); ++ } catch (IOException ex) { ++ throw new InvalidKeyException("could not DER encode : " + ++ ex.getMessage()); ++ } ++ } ++ ++ /** ++ * Make a RSA public key from its DER encoding (X.509). ++ */ ++ public RSAPublicKey(byte[] encoded) throws InvalidKeyException { ++ decode(encoded); ++ } ++ ++ /** ++ * Get key size as number of bits in modulus ++ * (Always rounded up to a multiple of 8) ++ * ++ */ ++ public int getKeySize() { ++ return this.modulus.byteLength() * 8; ++ } ++ ++ /** ++ * Get the raw public exponent ++ * ++ */ ++ public BigInt getPublicExponent() { ++ return this.publicExponent; ++ } ++ ++ /** ++ * Get the raw modulus ++ * ++ */ ++ public BigInt getModulus() { ++ return this.modulus; ++ } ++ ++ public String toString() { ++ return "RSA Public Key\n Algorithm: " + algid ++ + "\n modulus:\n" + this.modulus.toString() + "\n" ++ + "\n publicExponent:\n" + this.publicExponent.toString() ++ + "\n"; ++ } ++ ++ protected void parseKeyBits() throws InvalidKeyException { ++ if (!this.algid.getOID().equals(ALGORITHM_OID) && ++ !this.algid.getOID().equals(AlgorithmId.RSA_oid)) { ++ throw new InvalidKeyException("Key algorithm OID is not RSA"); ++ } ++ ++ try { ++ DerValue val = new DerValue(key); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new InvalidKeyException("Invalid RSA public key format:" + ++ " must be a SEQUENCE"); ++ } ++ ++ DerInputStream in = val.data; ++ ++ this.modulus = in.getInteger(); ++ this.publicExponent = in.getInteger(); ++ } catch (IOException e) { ++ throw new InvalidKeyException("Invalid RSA public key: " + ++ e.getMessage()); ++ } ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = super.hashCode(); ++ result = prime * result + ((modulus == null) ? 0 : modulus.hashCode()); ++ result = prime * result + ((publicExponent == null) ? 0 : publicExponent.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (!super.equals(obj)) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ RSAPublicKey other = (RSAPublicKey) obj; ++ if (modulus == null) { ++ if (other.modulus != null) ++ return false; ++ } else if (!modulus.equals(other.modulus)) ++ return false; ++ if (publicExponent == null) { ++ if (other.publicExponent != null) ++ return false; ++ } else if (!publicExponent.equals(other.publicExponent)) ++ return false; ++ return true; ++ } ++ ++ public boolean bigIntEquals(BigInt x, BigInt y) { ++ if (x == null) { ++ if (y != null) { ++ return false; ++ } ++ } else { ++ if (!x.equals(y)) { ++ return false; ++ } ++ } ++ return true; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/SHA.java b/org/mozilla/jss/netscape/security/provider/SHA.java +new file mode 100644 +index 00000000..a911e386 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/SHA.java +@@ -0,0 +1,349 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.security.DigestException; ++import java.security.MessageDigestSpi; ++ ++/** ++ * This class implements the Secure Hash Algorithm (SHA) developed by ++ * the National Institute of Standards and Technology along with the ++ * National Security Agency. This is the updated version of SHA ++ * fip-180 as superseded by fip-180-1. ++ * ++ *

++ * It implement JavaSecurity MessageDigest, and can be used by in the Java Security framework, as a pluggable ++ * implementation, as a filter for the digest stream classes. ++ * ++ * @version 1.30 97/12/10 ++ * @author Roger Riggs ++ * @author Benjamin Renaud ++ */ ++ ++public class SHA extends MessageDigestSpi implements Cloneable { ++ ++ /* This private hookm controlled by the appropriate constructor, ++ causes this class to implement the first version of SHA, ++ as defined in FIPS 180, as opposed to FIPS 180-1. This was ++ useful for DSA testing. */ ++ private int version = 1; ++ ++ private static final int SHA_LENGTH = 20; ++ ++ // Buffer of int's and count of characters accumulated ++ // 64 bytes are included in each hash block so the low order ++ // bits of count are used to know how to pack the bytes into ints ++ // and to know when to compute the block and start the next one. ++ private int W[] = new int[80]; ++ private long count = 0; ++ private final int countmax = 64; ++ private final int countmask = (countmax - 1); ++ ++ private int AA, BB, CC, DD, EE; ++ ++ SHA(int version) { ++ this(); ++ this.version = version; ++ } ++ ++ /** ++ * Creates a new SHA object. ++ */ ++ public SHA() { ++ init(); ++ } ++ ++ /** ++ * Return the length of the digest in bytes ++ */ ++ protected int engineGetDigestLength() { ++ return (SHA_LENGTH); ++ } ++ ++ public void engineUpdate(byte b) { ++ engineUpdate((int) b); ++ } ++ ++ /** ++ * Update a byte. ++ * ++ * @param b the byte ++ */ ++ private void engineUpdate(int b) { ++ int word; ++ int offset; ++ ++ /* compute word offset and bit offset within word the low bits ++ of count are inverted to make put the bytes in the write ++ order */ ++ word = ((int) count & countmask) >>> 2; ++ offset = (~(int) count & 3) << 3; ++ ++ W[word] = (W[word] & ~(0xff << offset)) | ((b & 0xff) << offset); ++ ++ /* If this is the last byte of a block, compute the partial hash */ ++ if (((int) count & countmask) == countmask) { ++ computeBlock(); ++ } ++ count++; ++ } ++ ++ /** ++ * Update a buffer. ++ * ++ * @param b the data to be updated. ++ * @param off the start offset in the data ++ * @param len the number of bytes to be updated. ++ */ ++ public void engineUpdate(byte b[], int off, int len) { ++ int word; ++ ++ if ((off < 0) || (len < 0) || (off + len > b.length)) ++ throw new ArrayIndexOutOfBoundsException(); ++ ++ // Use single writes until integer aligned ++ while ((len > 0) && ++ ((int) count & 3) != 0) { ++ engineUpdate(b[off]); ++ off++; ++ len--; ++ } ++ ++ /* Assemble groups of 4 bytes to be inserted in integer array */ ++ for (; len >= 4; len -= 4, off += 4) { ++ ++ word = ((int) count & countmask) >> 2; ++ ++ W[word] = ((b[off] & 0xff) << 24) | ++ ((b[off + 1] & 0xff) << 16) | ++ ((b[off + 2] & 0xff) << 8) | ++ ((b[off + 3] & 0xff)); ++ ++ count += 4; ++ if (((int) count & countmask) == 0) { ++ computeBlock(); ++ } ++ } ++ ++ /* Use single writes for last few bytes */ ++ for (; len > 0; len--, off++) { ++ engineUpdate(b[off]); ++ } ++ } ++ ++ /** ++ * Resets the buffers and hash value to start a new hash. ++ */ ++ public void init() { ++ AA = 0x67452301; ++ BB = 0xefcdab89; ++ CC = 0x98badcfe; ++ DD = 0x10325476; ++ EE = 0xc3d2e1f0; ++ ++ for (int i = 0; i < 80; i++) ++ W[i] = 0; ++ count = 0; ++ } ++ ++ /** ++ * Resets the buffers and hash value to start a new hash. ++ */ ++ public void engineReset() { ++ init(); ++ } ++ ++ /** ++ * Computes the final hash and returns the final value as a ++ * byte[20] array. The object is reset to be ready for further ++ * use, as specified in the JavaSecurity MessageDigest ++ * specification. ++ */ ++ public byte[] engineDigest() { ++ byte hashvalue[] = new byte[SHA_LENGTH]; ++ ++ try { ++ engineDigest(hashvalue, 0, hashvalue.length); ++ } catch (DigestException e) { ++ throw new InternalError(""); ++ } ++ return hashvalue; ++ } ++ ++ /** ++ * Computes the final hash and returns the final value as a ++ * byte[20] array. The object is reset to be ready for further ++ * use, as specified in the JavaSecurity MessageDigest ++ * specification. ++ */ ++ public int engineDigest(byte[] hashvalue, int offset, int len) ++ throws DigestException { ++ ++ if (len < SHA_LENGTH) ++ throw new DigestException("partial digests not returned"); ++ if (hashvalue.length - offset < SHA_LENGTH) ++ throw new DigestException("insufficient space in the output " + ++ "buffer to store the digest"); ++ ++ /* The number of bits before padding occurs */ ++ long bits = count << 3; ++ ++ engineUpdate(0x80); ++ ++ /* Pad with zeros until length is a multiple of 448 (the last two ++ 32 ints are used a holder for bits (see above). */ ++ while ((int) (count & countmask) != 56) { ++ engineUpdate(0); ++ } ++ ++ W[14] = (int) (bits >>> 32); ++ W[15] = (int) (bits & 0xffffffff); ++ ++ count += 8; ++ computeBlock(); ++ ++ // Copy out the result ++ hashvalue[offset + 0] = (byte) (AA >>> 24); ++ hashvalue[offset + 1] = (byte) (AA >>> 16); ++ hashvalue[offset + 2] = (byte) (AA >>> 8); ++ hashvalue[offset + 3] = (byte) (AA >>> 0); ++ ++ hashvalue[offset + 4] = (byte) (BB >>> 24); ++ hashvalue[offset + 5] = (byte) (BB >>> 16); ++ hashvalue[offset + 6] = (byte) (BB >>> 8); ++ hashvalue[offset + 7] = (byte) (BB >>> 0); ++ ++ hashvalue[offset + 8] = (byte) (CC >>> 24); ++ hashvalue[offset + 9] = (byte) (CC >>> 16); ++ hashvalue[offset + 10] = (byte) (CC >>> 8); ++ hashvalue[offset + 11] = (byte) (CC >>> 0); ++ ++ hashvalue[offset + 12] = (byte) (DD >>> 24); ++ hashvalue[offset + 13] = (byte) (DD >>> 16); ++ hashvalue[offset + 14] = (byte) (DD >>> 8); ++ hashvalue[offset + 15] = (byte) (DD >>> 0); ++ ++ hashvalue[offset + 16] = (byte) (EE >>> 24); ++ hashvalue[offset + 17] = (byte) (EE >>> 16); ++ hashvalue[offset + 18] = (byte) (EE >>> 8); ++ hashvalue[offset + 19] = (byte) (EE >>> 0); ++ ++ engineReset(); // remove the evidence ++ ++ return SHA_LENGTH; ++ } ++ ++ // Constants for each round ++ private final int round1_kt = 0x5a827999; ++ private final int round2_kt = 0x6ed9eba1; ++ private final int round3_kt = 0x8f1bbcdc; ++ private final int round4_kt = 0xca62c1d6; ++ ++ /** ++ * Compute a the hash for the current block. ++ * ++ * This is in the same vein as Peter Gutmann's algorithm listed in ++ * the back of Applied Cryptography, Compact implementation of ++ * "old" NIST Secure Hash Algorithm. ++ * ++ */ ++ private void computeBlock() { ++ int temp, a, b, c, d, e; ++ ++ // The first 16 ints have the byte stream, compute the rest of ++ // the buffer ++ for (int t = 16; t <= 79; t++) { ++ if (version == 0) { ++ W[t] = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]; ++ } else { ++ temp = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]; ++ W[t] = ((temp << 1) | (temp >>> (32 - 1))); ++ } ++ } ++ ++ a = AA; ++ b = BB; ++ c = CC; ++ d = DD; ++ e = EE; ++ ++ // Round 1 ++ for (int i = 0; i < 20; i++) { ++ temp = ((a << 5) | (a >>> (32 - 5))) + ++ ((b & c) | ((~b) & d)) + e + W[i] + round1_kt; ++ e = d; ++ d = c; ++ c = ((b << 30) | (b >>> (32 - 30))); ++ b = a; ++ a = temp; ++ } ++ ++ // Round 2 ++ for (int i = 20; i < 40; i++) { ++ temp = ((a << 5) | (a >>> (32 - 5))) + ++ (b ^ c ^ d) + e + W[i] + round2_kt; ++ e = d; ++ d = c; ++ c = ((b << 30) | (b >>> (32 - 30))); ++ b = a; ++ a = temp; ++ } ++ ++ // Round 3 ++ for (int i = 40; i < 60; i++) { ++ temp = ((a << 5) | (a >>> (32 - 5))) + ++ ((b & c) | (b & d) | (c & d)) + e + W[i] + round3_kt; ++ e = d; ++ d = c; ++ c = ((b << 30) | (b >>> (32 - 30))); ++ b = a; ++ a = temp; ++ } ++ ++ // Round 4 ++ for (int i = 60; i < 80; i++) { ++ temp = ((a << 5) | (a >>> (32 - 5))) + ++ (b ^ c ^ d) + e + W[i] + round4_kt; ++ e = d; ++ d = c; ++ c = ((b << 30) | (b >>> (32 - 30))); ++ b = a; ++ a = temp; ++ } ++ AA += a; ++ BB += b; ++ CC += c; ++ DD += d; ++ EE += e; ++ } ++ ++ /* ++ * Clones this object. ++ */ ++ public Object clone() { ++ SHA that = null; ++ try { ++ that = (SHA) super.clone(); ++ that.W = new int[80]; ++ System.arraycopy(this.W, 0, that.W, 0, W.length); ++ return that; ++ } catch (CloneNotSupportedException e) { ++ } ++ return that; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/Sun.java b/org/mozilla/jss/netscape/security/provider/Sun.java +new file mode 100644 +index 00000000..94e72ef4 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/Sun.java +@@ -0,0 +1,135 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.security.Provider; ++ ++/** ++ * The SUN Security Provider. ++ * ++ * @author Benjamin Renaud ++ * ++ * @version 1.24, 97/12/10 ++ */ ++ ++/** ++ * Defines the SUN provider. ++ * ++ * Algorithm supported, and their names: ++ * ++ * - SHA-1 is the message digest scheme decribed FIPS 180-1. ++ * Aliases for SHA-1 are SHA. ++ * ++ * - DSA is the signature scheme described in FIPS 186. (SHA used in ++ * DSA is SHA-1: FIPS 186 with Change No 1.) Aliases for DSA are ++ * SHA/DSA, SHA-1/DSA, SHA1/DSA, DSS and the object identifier ++ * strings "OID.1.3.14.3.2.13", "OID.1.3.14.3.2.27" and ++ * "OID.1.2.840.10040.4.3". ++ * ++ * - DSA is the key generation scheme as described in FIPS 186. ++ * Aliases for DSA include the OID strings "OID.1.3.14.3.2.12" ++ * and "OID.1.2.840.10040.4.1". ++ * ++ * - MD5 is the message digest scheme described in RFC 1321. ++ * There are no aliases for MD5. ++ * ++ * Notes: The name of algorithm described in FIPS-180 is SHA-0, and is ++ * not supported by the SUN provider.) ++ */ ++public final class Sun extends Provider { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 9134942296334703727L; ++ private static String info = "SUN Security Provider v1.0, " + ++ "DSA signing and key generation, SHA-1 and MD5 message digests."; ++ ++ public Sun() { ++ /* We are the SUN provider */ ++ super("SUN", 1.0, info); ++ ++ try { ++ ++ // AccessController.beginPrivileged(); ++ ++ /* ++ * Signature engines ++ */ ++ put("Signature.DSA", "org.mozilla.jss.netscape.security.provider.DSA"); ++ ++ put("Alg.Alias.Signature.SHA/DSA", "DSA"); ++ put("Alg.Alias.Signature.SHA1/DSA", "DSA"); ++ put("Alg.Alias.Signature.SHA-1/DSA", "DSA"); ++ put("Alg.Alias.Signature.DSS", "DSA"); ++ put("Alg.Alias.Signature.OID.1.3.14.3.2.13", "DSA"); ++ put("Alg.Alias.Signature.OID.1.3.14.3.2.27", "DSA"); ++ put("Alg.Alias.Signature.OID.1.2.840.10040.4.3", "DSA"); ++ // the following are not according to our formal spec but ++ // are still supported ++ put("Alg.Alias.Signature.1.3.14.3.2.13", "DSA"); ++ put("Alg.Alias.Signature.1.3.14.3.2.27", "DSA"); ++ put("Alg.Alias.Signature.1.2.840.10040.4.3", "DSA"); ++ put("Alg.Alias.Signature.SHAwithDSA", "DSA"); ++ put("Alg.Alias.Signature.SHA1withDSA", "DSA"); ++ ++ /* ++ * Key Pair Generator engines ++ */ ++ put("KeyPairGenerator.DSA", ++ "org.mozilla.jss.netscape.security.provider.DSAKeyPairGenerator"); ++ ++ put("Alg.Alias.KeyPairGenerator.OID.1.3.14.3.2.12", "DSA"); ++ put("Alg.Alias.KeyPairGenerator.OID.1.2.840.10040.4.1", "DSA"); ++ // the following are not according to our formal spec but ++ // are still supported ++ put("Alg.Alias.KeyPairGenerator.1.3.14.3.2.12", "DSA"); ++ put("Alg.Alias.KeyPairGenerator.1.2.840.10040.4.1", "DSA"); ++ ++ /* ++ * Digest engines ++ */ ++ put("MessageDigest.MD5", "org.mozilla.jss.netscape.security.provider.MD5"); ++ put("MessageDigest.SHA-1", "org.mozilla.jss.netscape.security.provider.SHA"); ++ ++ put("Alg.Alias.MessageDigest.SHA", "SHA-1"); ++ put("Alg.Alias.MessageDigest.SHA1", "SHA-1"); ++ ++ /* ++ * Algorithm Parameter Generator engines ++ */ ++ put("AlgorithmParameterGenerator.DSA", ++ "org.mozilla.jss.netscape.security.provider.DSAParameterGenerator"); ++ ++ /* ++ * Algorithm Parameter engines ++ */ ++ put("AlgorithmParameters.DSA", ++ "org.mozilla.jss.netscape.security.provider.DSAParameters"); ++ put("Alg.Alias.AlgorithmParameters.1.3.14.3.2.12", "DSA"); ++ put("Alg.Alias.AlgorithmParameters.1.2.840.10040.4.1", "DSA"); ++ /* ++ * Key factories ++ */ ++ put("KeyFactory.DSA", "org.mozilla.jss.netscape.security.provider.DSAKeyFactory"); ++ ++ } finally { ++ // AccessController.endPrivileged(); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/provider/X509CertificateFactory.java b/org/mozilla/jss/netscape/security/provider/X509CertificateFactory.java +new file mode 100644 +index 00000000..ab579299 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/X509CertificateFactory.java +@@ -0,0 +1,61 @@ ++// --- 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 org.mozilla.jss.netscape.security.provider; ++ ++import java.io.InputStream; ++import java.security.cert.CRL; ++import java.security.cert.CRLException; ++import java.security.cert.Certificate; ++import java.security.cert.CertificateException; ++import java.security.cert.CertificateFactorySpi; ++import java.util.Collection; ++ ++import org.mozilla.jss.netscape.security.x509.X509CRLImpl; ++import org.mozilla.jss.netscape.security.x509.X509CertImpl; ++import org.mozilla.jss.netscape.security.x509.X509ExtensionException; ++ ++public class X509CertificateFactory extends CertificateFactorySpi { ++ ++ public Certificate engineGenerateCertificate(InputStream inStream) ++ throws CertificateException { ++ return new X509CertImpl(inStream); ++ } ++ ++ public Collection engineGenerateCertificates(InputStream inStream) ++ throws CertificateException { ++ return null; ++ } ++ ++ public CRL engineGenerateCRL(InputStream inStream) ++ throws CRLException { ++ X509CRLImpl crl = null; ++ try { ++ crl = new X509CRLImpl(inStream); ++ } catch (X509ExtensionException e) { ++ ; ++ } ++ ++ return crl; ++ } ++ ++ public Collection engineGenerateCRLs(InputStream inStream) ++ throws CRLException { ++ return null; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/provider/manifest.mn b/org/mozilla/jss/netscape/security/provider/manifest.mn +new file mode 100644 +index 00000000..3073171c +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/provider/manifest.mn +@@ -0,0 +1,8 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++CORE_DEPTH = ../../../../../.. ++PACKAGE = org/mozilla/jss/netscape/security/provider ++MODULE = jss +diff --git a/org/mozilla/jss/netscape/security/util/ASN1CharStrConvMap.java b/org/mozilla/jss/netscape/security/util/ASN1CharStrConvMap.java +new file mode 100644 +index 00000000..c4f82679 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/ASN1CharStrConvMap.java +@@ -0,0 +1,168 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.CharsetEncoder; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.Map; ++ ++/** ++ * Maps a ASN.1 character string type to a charset encoder and decoder. ++ * The converter is used to convert a DerValue of a ASN.1 character string type ++ * from bytes to unicode characters and vice versa. ++ * ++ *

++ * A global default ASN1CharStrConvMap is created when the class is initialized. The global default map is extensible. ++ * ++ * @author Lily Hsiao ++ * @author Slava Galperin ++ * ++ */ ++ ++public class ASN1CharStrConvMap { ++ // public constructors ++ ++ /** ++ * Constructs a ASN1CharStrConvMap. ++ */ ++ public ASN1CharStrConvMap() { ++ } ++ ++ /** ++ * Get an encoder for the specified DER tag. ++ * ++ * @param tag A DER tag of a ASN.1 character string type, ++ * for example DerValue.tag_PrintableString. ++ * ++ * @return An encoder for the DER tag. ++ */ ++ public CharsetEncoder getEncoder(byte tag) { ++ Charset charset = charsets.get(tag); ++ if (charset == null) ++ return null; ++ return charset.newEncoder(); ++ } ++ ++ /** ++ * Get a decoder for the given DER tag. ++ * ++ * @param tag A DER tag of a ASN.1 character string type, ++ * for example DerValue.tag_PrintableString. ++ * ++ * @return A decoder for the DER tag. ++ */ ++ public CharsetDecoder getDecoder(byte tag) { ++ Charset charset = charsets.get(tag); ++ if (charset == null) ++ return null; ++ return charset.newDecoder(); ++ } ++ ++ /** ++ * Add a tag-charset entry in the map. ++ * ++ * @param tag A DER tag of a ASN.1 character string type, ++ * ex. DerValue.tag_IA5String ++ * @param charset A charset for the tag. ++ */ ++ public void addEntry(byte tag, Charset charset) { ++ ++ Charset currentCharset = charsets.get(tag); ++ ++ if (currentCharset != null) { ++ if (currentCharset != charset) { ++ throw new IllegalArgumentException( ++ "a DER tag to converter entry already exists."); ++ } else { ++ return; ++ } ++ } ++ ++ charsets.put(tag, charset); ++ } ++ ++ /** ++ * Get an iterator of all tags in the map. ++ * ++ * @return An Iterator of DER tags in the map as Bytes. ++ */ ++ public Iterator getTags() { ++ return charsets.keySet().iterator(); ++ } ++ ++ // static public methods. ++ ++ /** ++ * Get the global ASN1CharStrConvMap. ++ * ++ * @return The global default ASN1CharStrConvMap. ++ */ ++ static public ASN1CharStrConvMap getDefault() { ++ return defaultMap; ++ } ++ ++ /** ++ * Set the global default ASN1CharStrConvMap. ++ * ++ * @param newDefault The new default ASN1CharStrConvMap. ++ */ ++ static public void setDefault(ASN1CharStrConvMap newDefault) { ++ if (newDefault == null) ++ throw new IllegalArgumentException( ++ "Cannot set a null default Der Tag Converter map"); ++ defaultMap = newDefault; ++ } ++ ++ // private methods and variables. ++ ++ private Map charsets = new HashMap(); ++ ++ private static ASN1CharStrConvMap defaultMap; ++ ++ /** ++ * Create the default converter map on initialization ++ */ ++ static { ++ ASN1CharsetProvider provider = new ASN1CharsetProvider(); ++ ++ defaultMap = new ASN1CharStrConvMap(); ++ defaultMap.addEntry(DerValue.tag_PrintableString, ++ provider.charsetForName("ASN.1-Printable")); ++ defaultMap.addEntry(DerValue.tag_VisibleString, ++ provider.charsetForName("ASN.1-Printable")); ++ defaultMap.addEntry(DerValue.tag_IA5String, ++ provider.charsetForName("ASN.1-IA5")); ++ defaultMap.addEntry(DerValue.tag_BMPString, ++ Charset.forName("UnicodeBig")); ++ defaultMap.addEntry(DerValue.tag_UniversalString, ++ provider.charsetForName("ASN.1-Universal")); ++ // XXX this is an oversimplified implementation of T.61 strings, it ++ // doesn't handle all cases ++ defaultMap.addEntry(DerValue.tag_T61String, ++ Charset.forName("ISO-8859-1")); ++ // UTF8String added to ASN.1 in 1998 ++ defaultMap.addEntry(DerValue.tag_UTF8String, ++ Charset.forName("UTF-8")); ++ defaultMap.addEntry(DerValue.tag_GeneralString, ++ Charset.forName("UTF-8")); ++ }; ++ ++}; +diff --git a/org/mozilla/jss/netscape/security/util/ASN1CharsetProvider.java b/org/mozilla/jss/netscape/security/util/ASN1CharsetProvider.java +new file mode 100644 +index 00000000..9ac4698b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/ASN1CharsetProvider.java +@@ -0,0 +1,30 @@ ++package org.mozilla.jss.netscape.security.util; ++ ++import java.nio.charset.Charset; ++import java.nio.charset.spi.CharsetProvider; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.Map; ++ ++public class ASN1CharsetProvider extends CharsetProvider { ++ ++ protected Map charsets = new HashMap(); ++ ++ public ASN1CharsetProvider() { ++ addCharset(new PrintableCharset()); ++ addCharset(new IA5Charset()); ++ addCharset(new UniversalCharset()); ++ } ++ ++ public Iterator charsets() { ++ return charsets.values().iterator(); ++ } ++ ++ public Charset charsetForName(String charsetName) { ++ return charsets.get(charsetName); ++ } ++ ++ public void addCharset(Charset cs) { ++ charsets.put(cs.name(), cs); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/BigInt.java b/org/mozilla/jss/netscape/security/util/BigInt.java +new file mode 100644 +index 00000000..e3ba42b8 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/BigInt.java +@@ -0,0 +1,221 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.Serializable; ++import java.math.BigInteger; ++import java.util.Arrays; ++ ++/** ++ * A low-overhead arbitrary-precision unsigned integer. ++ * This is intended for use with ASN.1 parsing, and printing of ++ * such parsed values. Convert to "BigInteger" if you need to do ++ * arbitrary precision arithmetic, rather than just represent ++ * the number as a wrapped array of bytes. ++ * ++ *

++ * NOTE: This class may eventually disappear, to ++ * be supplanted by big-endian byte arrays which hold both signed ++ * and unsigned arbitrary-precision integers. ++ * ++ * @version 1.23 ++ * @author David Brownell ++ */ ++public final class BigInt implements Serializable { ++ ++ private static final long serialVersionUID = 705094142021888265L; ++ // Big endian -- MSB first. ++ private byte[] places; ++ ++ /** ++ * Constructs a "Big" integer from a set of (big-endian) bytes. ++ * Leading zeroes should be stripped off. ++ * ++ * @param data a sequence of bytes, most significant bytes/digits ++ * first. CONSUMED. ++ */ ++ public BigInt(byte[] data) { ++ places = data.clone(); ++ } ++ ++ /** ++ * Constructs a "Big" integer from a "BigInteger", which must be ++ * positive (or zero) in value. ++ */ ++ public BigInt(BigInteger i) { ++ byte[] temp = i.toByteArray(); ++ ++ if ((temp[0] & 0x80) != 0) ++ throw new IllegalArgumentException("negative BigInteger"); ++ ++ // XXX we assume exactly _one_ sign byte is used... ++ ++ if (temp[0] != 0) ++ places = temp; ++ else { ++ // Note that if i = new BigInteger("0"), ++ // i.toByteArray() contains only 1 zero. ++ if (temp.length == 1) { ++ places = new byte[1]; ++ places[0] = (byte) 0; ++ } else { ++ places = new byte[temp.length - 1]; ++ for (int j = 1; j < temp.length; j++) ++ places[j - 1] = temp[j]; ++ } ++ } ++ } ++ ++ /** ++ * Constructs a "Big" integer from a normal Java integer. ++ * ++ * @param i the java primitive integer ++ */ ++ public BigInt(int i) { ++ if (i < (1 << 8)) { ++ places = new byte[1]; ++ places[0] = (byte) i; ++ } else if (i < (1 << 16)) { ++ places = new byte[2]; ++ places[0] = (byte) (i >> 8); ++ places[1] = (byte) i; ++ } else if (i < (1 << 24)) { ++ places = new byte[3]; ++ places[0] = (byte) (i >> 16); ++ places[1] = (byte) (i >> 8); ++ places[2] = (byte) i; ++ } else { ++ places = new byte[4]; ++ places[0] = (byte) (i >> 24); ++ places[1] = (byte) (i >> 16); ++ places[2] = (byte) (i >> 8); ++ places[3] = (byte) i; ++ } ++ } ++ ++ /** ++ * Converts the "big" integer to a java primitive integer. ++ * ++ * @exception NumberFormatException if 32 bits is insufficient. ++ */ ++ public int toInt() { ++ if (places.length > 4) ++ throw new NumberFormatException("BigInt.toInt, too big"); ++ int retval = 0, i = 0; ++ for (; i < places.length; i++) ++ retval = (retval << 8) + (places[i] & 0xff); ++ return retval; ++ } ++ ++ /** ++ * Returns a hexadecimal printed representation. The value is ++ * formatted to fit on lines of at least 75 characters, with ++ * embedded newlines. Words are separated for readability, ++ * with eight words (32 bytes) per line. ++ */ ++ public String toString() { ++ return hexify(); ++ } ++ ++ /** ++ * Returns a BigInteger value which supports many arithmetic ++ * operations. Assumes negative values will never occur. ++ */ ++ public BigInteger toBigInteger() { ++ return new BigInteger(1, places); ++ } ++ ++ /** ++ * Returns the length of the data as a byte array. ++ */ ++ public int byteLength() { ++ return places.length; ++ } ++ ++ /** ++ * Returns the data as a byte array. The most significant bit ++ * of the array is bit zero (as in java.math.BigInteger). ++ */ ++ public byte[] toByteArray() { ++ if (places.length == 0) { ++ byte zero[] = new byte[1]; ++ zero[0] = (byte) 0; ++ return zero; ++ } else { ++ return places.clone(); ++ } ++ } ++ ++ private static final String digits = "0123456789abcdef"; ++ ++ private String hexify() { ++ if (places.length == 0) ++ return " 0 "; ++ ++ StringBuffer buf = new StringBuffer(places.length * 2); ++ buf.append(" "); // four spaces ++ for (int i = 0; i < places.length; i++) { ++ buf.append(digits.charAt((places[i] >> 4) & 0x0f)); ++ buf.append(digits.charAt(places[i] & 0x0f)); ++ if (((i + 1) % 32) == 0) { ++ if ((i + 1) != places.length) ++ buf.append("\n "); // line after four words ++ } else if (((i + 1) % 4) == 0) ++ buf.append(' '); // space between words ++ } ++ return buf.toString(); ++ } ++ ++ /** ++ * Returns true iff the parameter is a numerically equivalent ++ * BigInt. ++ * ++ * @param other the object being compared with this one. ++ */ ++ public boolean equals(Object other) { ++ if (other instanceof BigInt) ++ return equals((BigInt) other); ++ return false; ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + Arrays.hashCode(places); ++ return result; ++ } ++ ++ /** ++ * Returns true iff the parameter is numerically equivalent. ++ * ++ * @param other the BigInt being compared with this one. ++ */ ++ public boolean equals(BigInt other) { ++ if (this == other) ++ return true; ++ ++ byte[] otherPlaces = other.toByteArray(); ++ if (places.length != otherPlaces.length) ++ return false; ++ for (int i = 0; i < places.length; i++) ++ if (places[i] != otherPlaces[i]) ++ return false; ++ return true; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/BitArray.java b/org/mozilla/jss/netscape/security/util/BitArray.java +new file mode 100644 +index 00000000..5c77ac2c +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/BitArray.java +@@ -0,0 +1,257 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.ByteArrayOutputStream; ++ ++/** ++ * A packed array of booleans. ++ * ++ * @author Joshua Bloch ++ * @author Douglas Hoover ++ * @version 1.2 97/12/10 ++ */ ++ ++public class BitArray { ++ ++ private byte[] repn; ++ private int length; ++ ++ private static final int BITS_PER_UNIT = 8; ++ ++ private static int subscript(int idx) { ++ return idx / BITS_PER_UNIT; ++ } ++ ++ private static int position(int idx) { // bits big-endian in each unit ++ return 1 << (BITS_PER_UNIT - 1 - (idx % BITS_PER_UNIT)); ++ } ++ ++ /** ++ * Creates a BitArray of the specified size, initialized to zeros. ++ */ ++ public BitArray(int length) throws IllegalArgumentException { ++ if (length < 0) { ++ throw new IllegalArgumentException("Negative length for BitArray"); ++ } ++ ++ this.length = length; ++ ++ repn = new byte[(length + BITS_PER_UNIT - 1) / BITS_PER_UNIT]; ++ } ++ ++ /** ++ * Creates a BitArray of the specified size, initialized from the ++ * specified byte array. The most significant bit of a[0] gets ++ * index zero in the BitArray. The array a must be large enough ++ * to specify a value for every bit in the BitArray. In other words, ++ * 8*a.length >= length. ++ */ ++ public BitArray(int length, byte[] a) throws IllegalArgumentException { ++ ++ if (length < 0) { ++ throw new IllegalArgumentException("Negative length for BitArray"); ++ } ++ if (a.length * BITS_PER_UNIT < length) { ++ throw new IllegalArgumentException("Byte array too short to represent " + ++ "bit array of given length"); ++ } ++ ++ this.length = length; ++ ++ int repLength = ((length + BITS_PER_UNIT - 1) / BITS_PER_UNIT); ++ int unusedBits = repLength * BITS_PER_UNIT - length; ++ byte bitMask = (byte) (0xFF << unusedBits); ++ ++ /* ++ normalize the representation: ++ 1. discard extra bytes ++ 2. zero out extra bits in the last byte ++ */ ++ repn = new byte[repLength]; ++ System.arraycopy(a, 0, repn, 0, repLength); ++ if (repn.length > 0) ++ repn[repn.length - 1] = (byte) (repn[repn.length - 1] & bitMask); ++ } ++ ++ /** ++ * Create a BitArray whose bits are those of the given array ++ * of Booleans. ++ */ ++ public BitArray(boolean[] bits) { ++ length = bits.length; ++ repn = new byte[(length + 7) / 8]; ++ ++ for (int i = 0; i < length; i++) { ++ set(i, bits[i]); ++ } ++ } ++ ++ /** ++ * Copy constructor (for cloning). ++ */ ++ private BitArray(BitArray ba) { ++ length = ba.length; ++ repn = ba.repn.clone(); ++ } ++ ++ /** ++ * Returns the indexed bit in this BitArray. ++ */ ++ public boolean get(int index) throws ArrayIndexOutOfBoundsException { ++ if (index < 0 || index >= length) { ++ throw new ArrayIndexOutOfBoundsException(Integer.toString(index)); ++ } ++ ++ return (repn[subscript(index)] & position(index)) != 0; ++ } ++ ++ /** ++ * Sets the indexed bit in this BitArray. ++ */ ++ public void set(int index, boolean value) ++ throws ArrayIndexOutOfBoundsException { ++ if (index < 0 || index >= length) { ++ throw new ArrayIndexOutOfBoundsException(Integer.toString(index)); ++ } ++ int idx = subscript(index); ++ int bit = position(index); ++ ++ if (value) { ++ repn[idx] |= bit; ++ } else { ++ repn[idx] &= ~bit; ++ } ++ } ++ ++ /** ++ * Returns the length of this BitArray. ++ */ ++ public int length() { ++ return length; ++ } ++ ++ /** ++ * Returns a Byte array containing the contents of this BitArray. ++ * The bit stored at index zero in this BitArray will be copied ++ * into the most significant bit of the zeroth element of the ++ * returned byte array. The last byte of the returned byte array ++ * will be contain zeros in any bits that do not have corresponding ++ * bits in the BitArray. (This matters only if the BitArray's size ++ * is not a multiple of 8.) ++ */ ++ public byte[] toByteArray() { ++ return repn.clone(); ++ } ++ ++ public boolean equals(Object obj) { ++ if (obj == this) ++ return true; ++ if (obj == null || !(obj instanceof BitArray)) ++ return false; ++ ++ BitArray ba = (BitArray) obj; ++ ++ if (ba.length != length) ++ return false; ++ ++ for (int i = 0; i < repn.length; i += 1) { ++ if (repn[i] != ba.repn[i]) ++ return false; ++ } ++ return true; ++ } ++ ++ /** ++ * Return a boolean array with the same bit values a this BitArray. ++ */ ++ public boolean[] toBooleanArray() { ++ boolean[] bits = new boolean[length]; ++ ++ for (int i = 0; i < length; i++) { ++ bits[i] = get(i); ++ } ++ return bits; ++ } ++ ++ /** ++ * Returns a hash code value for this bit array. ++ * ++ * @return a hash code value for this bit array. ++ */ ++ public int hashCode() { ++ int hashCode = 0; ++ ++ for (int i = 0; i < repn.length; i++) ++ hashCode = 31 * hashCode + repn[i]; ++ ++ return hashCode ^ length; ++ } ++ ++ public Object clone() { ++ return new BitArray(this); ++ } ++ ++ private static final byte[][] NYBBLE = { ++ { (byte) '0', (byte) '0', (byte) '0', (byte) '0' }, ++ { (byte) '0', (byte) '0', (byte) '0', (byte) '1' }, ++ { (byte) '0', (byte) '0', (byte) '1', (byte) '0' }, ++ { (byte) '0', (byte) '0', (byte) '1', (byte) '1' }, ++ { (byte) '0', (byte) '1', (byte) '0', (byte) '0' }, ++ { (byte) '0', (byte) '1', (byte) '0', (byte) '1' }, ++ { (byte) '0', (byte) '1', (byte) '1', (byte) '0' }, ++ { (byte) '0', (byte) '1', (byte) '1', (byte) '1' }, ++ { (byte) '1', (byte) '0', (byte) '0', (byte) '0' }, ++ { (byte) '1', (byte) '0', (byte) '0', (byte) '1' }, ++ { (byte) '1', (byte) '0', (byte) '1', (byte) '0' }, ++ { (byte) '1', (byte) '0', (byte) '1', (byte) '1' }, ++ { (byte) '1', (byte) '1', (byte) '0', (byte) '0' }, ++ { (byte) '1', (byte) '1', (byte) '0', (byte) '1' }, ++ { (byte) '1', (byte) '1', (byte) '1', (byte) '0' }, ++ { (byte) '1', (byte) '1', (byte) '1', (byte) '1' } ++ }; ++ ++ private static final int BYTES_PER_LINE = 8; ++ ++ /** ++ * Returns a string representation of this BitArray. ++ */ ++ public String toString() { ++ ByteArrayOutputStream out = new ByteArrayOutputStream(); ++ ++ for (int i = 0; i < repn.length - 1; i++) { ++ out.write(NYBBLE[(repn[i] >> 4) & 0x0F], 0, 4); ++ out.write(NYBBLE[repn[i] & 0x0F], 0, 4); ++ ++ if (i % BYTES_PER_LINE == BYTES_PER_LINE - 1) { ++ out.write('\n'); ++ } else { ++ out.write(' '); ++ } ++ } ++ ++ // in last byte of repn, use only the valid bits ++ for (int i = BITS_PER_UNIT * (repn.length - 1); i < length; i++) { ++ out.write(get(i) ? '1' : '0'); ++ } ++ ++ return new String(out.toByteArray()); ++ ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/util/ByteArrayLexOrder.java b/org/mozilla/jss/netscape/security/util/ByteArrayLexOrder.java +new file mode 100644 +index 00000000..2747721a +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/ByteArrayLexOrder.java +@@ -0,0 +1,60 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.util.Comparator; ++ ++/** ++ * Compare two byte arrays in lexicographical order. ++ * ++ * @version 1.4 97/12/10 ++ * @author D. N. Hoover ++ */ ++public class ByteArrayLexOrder implements Comparator, java.io.Serializable { ++ ++ private static final long serialVersionUID = 1897537410212918669L; ++ ++ /** ++ * Perform lexicographical comparison of two byte arrays, ++ * regarding each byte as unsigned. That is, compare array entries ++ * in order until they differ--the array with the smaller entry ++ * is "smaller". If array entries are ++ * equal till one array ends, then the longer array is "bigger". ++ * ++ * @param obj1 first byte array to compare. ++ * @param obj2 second byte array to compare. ++ * @return negative number if obj1 < obj2, 0 if obj1 == obj2, ++ * positive number if obj1 > obj2. ++ * ++ * @exception ClassCastException if either argument is not a byte array. ++ */ ++ public final int compare(byte[] bytes1, byte[] bytes2) { ++ ++ int diff; ++ for (int i = 0; i < bytes1.length && i < bytes2.length; i++) { ++ diff = (bytes1[i] & 0xFF) - (bytes2[i] & 0xFF); ++ if (diff != 0) { ++ return diff; ++ } ++ } ++ // if array entries are equal till the first ends, then the ++ // longer is "bigger" ++ return bytes1.length - bytes2.length; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/util/ByteArrayTagOrder.java b/org/mozilla/jss/netscape/security/util/ByteArrayTagOrder.java +new file mode 100644 +index 00000000..02f9687e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/ByteArrayTagOrder.java +@@ -0,0 +1,46 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.util.Comparator; ++ ++public class ByteArrayTagOrder implements Comparator, java.io.Serializable { ++ ++ private static final long serialVersionUID = -2027007556858126443L; ++ ++ /** ++ * Compare two byte arrays, by the order of their tags, ++ * as defined in ITU-T X.680, sec. 6.4. (First compare ++ * tag classes, then tag numbers, ignoring the constructivity bit.) ++ * ++ * @param obj1 first byte array to compare. ++ * @param obj2 second byte array to compare. ++ * @return negative number if obj1 < obj2, 0 if obj1 == obj2, ++ * positive number if obj1 > obj2. ++ * ++ * @exception ClassCastException if either argument is not a byte array. ++ */ ++ ++ public final int compare(byte[] bytes1, byte[] bytes2) { ++ ++ // tag order is same as byte order ignoring any difference in ++ // the constructivity bit (0x02) ++ return (bytes1[0] | 0x20) - (bytes2[0] | 0x20); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/util/Cert.java b/org/mozilla/jss/netscape/security/util/Cert.java +new file mode 100644 +index 00000000..508b9b86 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/Cert.java +@@ -0,0 +1,201 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.IOException; ++import java.security.cert.CertificateException; ++import java.security.cert.X509CRL; ++import java.security.cert.X509Certificate; ++ ++import org.mozilla.jss.crypto.SignatureAlgorithm; ++ ++import org.mozilla.jss.netscape.security.pkcs.PKCS7; ++import org.mozilla.jss.netscape.security.x509.X509CRLImpl; ++import org.mozilla.jss.netscape.security.x509.X509CertImpl; ++ ++public class Cert { ++ ++ public static final String HEADER = "-----BEGIN CERTIFICATE-----"; ++ public static final String FOOTER = "-----END CERTIFICATE-----"; ++ ++ // From https://www.rfc-editor.org/rfc/rfc7468.txt ++ public static final String REQUEST_HEADER = "-----BEGIN CERTIFICATE REQUEST-----"; ++ public static final String REQUEST_FOOTER = "-----END CERTIFICATE REQUEST-----"; ++ ++ public static SignatureAlgorithm mapAlgorithmToJss(String algname) { ++ if (algname.equals("MD5withRSA")) ++ return SignatureAlgorithm.RSASignatureWithMD5Digest; ++ else if (algname.equals("MD2withRSA")) ++ return SignatureAlgorithm.RSASignatureWithMD2Digest; ++ else if (algname.equals("SHA1withRSA")) ++ return SignatureAlgorithm.RSASignatureWithSHA1Digest; ++ else if (algname.equals("SHA1withDSA")) ++ return SignatureAlgorithm.DSASignatureWithSHA1Digest; ++ else if (algname.equals("SHA256withRSA")) ++ return SignatureAlgorithm.RSASignatureWithSHA256Digest; ++ else if (algname.equals("SHA384withRSA")) ++ return SignatureAlgorithm.RSASignatureWithSHA384Digest; ++ else if (algname.equals("SHA512withRSA")) ++ return SignatureAlgorithm.RSASignatureWithSHA512Digest; ++ else if (algname.equals("SHA1withEC")) ++ return SignatureAlgorithm.ECSignatureWithSHA1Digest; ++ else if (algname.equals("SHA256withEC")) ++ return SignatureAlgorithm.ECSignatureWithSHA256Digest; ++ else if (algname.equals("SHA384withEC")) ++ return SignatureAlgorithm.ECSignatureWithSHA384Digest; ++ else if (algname.equals("SHA512withEC")) ++ return SignatureAlgorithm.ECSignatureWithSHA512Digest; ++ return null; ++ } ++ ++ public static String stripBrackets(String s) { ++ if (s == null) { ++ return s; ++ } ++ ++ if ((s.startsWith(HEADER)) && ++ (s.endsWith(FOOTER))) { ++ return (s.substring(27, (s.length() - 25))); ++ } ++ ++ // To support Thawte's header and footer ++ if ((s.startsWith("-----BEGIN PKCS #7 SIGNED DATA-----")) && ++ (s.endsWith("-----END PKCS #7 SIGNED DATA-----"))) { ++ return (s.substring(35, (s.length() - 33))); ++ } ++ ++ return s; ++ } ++ ++ public static String stripCRLBrackets(String s) { ++ if (s == null) { ++ return s; ++ } ++ if ((s.startsWith("-----BEGIN CERTIFICATE REVOCATION LIST-----")) && ++ (s.endsWith("-----END CERTIFICATE REVOCATION LIST-----"))) { ++ return (s.substring(43, (s.length() - 41))); ++ } ++ return s; ++ } ++ ++ public static String stripCertBrackets(String s) { ++ return stripBrackets(s); ++ } ++ ++ // private static BASE64Decoder mDecoder = new BASE64Decoder(); ++ public static X509CertImpl mapCert(String mime64) ++ throws IOException { ++ mime64 = stripCertBrackets(mime64.trim()); ++ String newval = normalizeCertStr(mime64); ++ // byte rawPub[] = mDecoder.decodeBuffer(newval); ++ byte rawPub[] = Utils.base64decode(newval); ++ X509CertImpl cert = null; ++ ++ try { ++ cert = new X509CertImpl(rawPub); ++ } catch (CertificateException e) { ++ } ++ return cert; ++ } ++ ++ public static X509Certificate[] mapCertFromPKCS7(String mime64) ++ throws IOException { ++ mime64 = stripCertBrackets(mime64.trim()); ++ String newval = normalizeCertStr(mime64); ++ // byte rawPub[] = mDecoder.decodeBuffer(newval); ++ byte rawPub[] = Utils.base64decode(newval); ++ PKCS7 p7 = null; ++ ++ try { ++ p7 = new PKCS7(rawPub); ++ } catch (Exception e) { ++ throw new IOException("p7 is null"); ++ } ++ return p7.getCertificates(); ++ } ++ ++ public static X509CRL mapCRL(String mime64) ++ throws IOException { ++ mime64 = stripCRLBrackets(mime64.trim()); ++ String newval = normalizeCertStr(mime64); ++ // byte rawPub[] = mDecoder.decodeBuffer(newval); ++ byte rawPub[] = Utils.base64decode(newval); ++ X509CRL crl = null; ++ ++ try { ++ crl = new X509CRLImpl(rawPub); ++ } catch (Exception e) { ++ } ++ return crl; ++ } ++ ++ public static X509CRL mapCRL1(String mime64) ++ throws IOException { ++ mime64 = stripCRLBrackets(mime64.trim()); ++ ++ byte rawPub[] = Utils.base64decode(mime64); ++ X509CRL crl = null; ++ ++ try { ++ crl = new X509CRLImpl(rawPub); ++ } catch (Exception e) { ++ throw new IOException(e.toString()); ++ } ++ return crl; ++ } ++ ++ public static String normalizeCertStr(String s) { ++ StringBuffer val = new StringBuffer(); ++ ++ for (int i = 0; i < s.length(); i++) { ++ if (s.charAt(i) == '\n') { ++ continue; ++ } else if (s.charAt(i) == '\r') { ++ continue; ++ } else if (s.charAt(i) == '"') { ++ continue; ++ } else if (s.charAt(i) == ' ') { ++ continue; ++ } ++ val.append(s.charAt(i)); ++ } ++ return val.toString(); ++ } ++ ++ public static String normalizeCertStrAndReq(String s) { ++ StringBuffer val = new StringBuffer(); ++ ++ for (int i = 0; i < s.length(); i++) { ++ if (s.charAt(i) == '\n') { ++ continue; ++ } else if (s.charAt(i) == '\r') { ++ continue; ++ } else if (s.charAt(i) == '"') { ++ continue; ++ } ++ val.append(s.charAt(i)); ++ } ++ return val.toString(); ++ } ++ ++ public static byte[] parseCertificate(String cert) { ++ String encoded = normalizeCertStrAndReq(cert); ++ String b64 = stripBrackets(encoded); ++ return Utils.base64decode(b64); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/CertPrettyPrint.java b/org/mozilla/jss/netscape/security/util/CertPrettyPrint.java +new file mode 100644 +index 00000000..e6a09b06 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/CertPrettyPrint.java +@@ -0,0 +1,346 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.security.MessageDigest; ++import java.security.PublicKey; ++import java.security.cert.Certificate; ++import java.text.DateFormat; ++import java.util.Locale; ++import java.util.ResourceBundle; ++import java.util.TimeZone; ++ ++import org.mozilla.jss.asn1.ASN1Util; ++import org.mozilla.jss.asn1.SET; ++import org.mozilla.jss.pkcs7.ContentInfo; ++import org.mozilla.jss.pkcs7.SignedData; ++ ++ ++import org.mozilla.jss.netscape.security.x509.CertificateExtensions; ++import org.mozilla.jss.netscape.security.x509.CertificateX509Key; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.X509CertImpl; ++import org.mozilla.jss.netscape.security.x509.X509CertInfo; ++import org.mozilla.jss.netscape.security.x509.X509Key; ++ ++/** ++ * This class will display the certificate content in predefined ++ * format. ++ * ++ * @author Jack Pan-Chen ++ * @version $Revision$, $Date$ ++ */ ++public class CertPrettyPrint { ++ ++ /*========================================================== ++ * constants ++ *==========================================================*/ ++ private final static String CUSTOM_LOCALE = "Custom"; ++ ++ /*========================================================== ++ * variables ++ *==========================================================*/ ++ private X509CertImpl mX509Cert = null; ++ private PrettyPrintFormat pp = null; ++ private byte[] mCert_b = null; ++ ++ /*========================================================== ++ * constructors ++ *==========================================================*/ ++ ++ public CertPrettyPrint(Certificate cert) { ++ if (cert instanceof X509CertImpl) ++ mX509Cert = (X509CertImpl) cert; ++ ++ pp = new PrettyPrintFormat(":"); ++ } ++ ++ public CertPrettyPrint(byte[] certb) { ++ mCert_b = certb; ++ pp = new PrettyPrintFormat(":"); ++ } ++ ++ /*========================================================== ++ * public methods ++ *==========================================================*/ ++ ++ /** ++ * This method return string representation of the certificate ++ * in predefined format using specified client local. I18N Support. ++ * ++ * @param clientLocale Locale to be used for localization ++ * @return string representation of the certificate ++ */ ++ public String toString(Locale clientLocale) { ++ ++ if (mX509Cert != null) ++ return X509toString(clientLocale); ++ else if (mCert_b != null) ++ return pkcs7toString(clientLocale); ++ else ++ return null; ++ } ++ ++ public String pkcs7toString(Locale clientLocale) { ++ StringBuffer content=new StringBuffer(); ++ ++ try { ++ mX509Cert = new X509CertImpl(mCert_b); ++ return toString(clientLocale); ++ } catch (Exception e) { ++ } ++ ++ ContentInfo ci = null; ++ try { ++ ci = (ContentInfo) ++ ASN1Util.decode(ContentInfo.getTemplate(), mCert_b); ++ } catch (Exception e) { ++ return ""; ++ } ++ ++ if (ci.getContentType().equals(ContentInfo.SIGNED_DATA)) { ++ SignedData sd = null; ++ try { ++ sd = (SignedData) ci.getInterpretedContent(); ++ } catch (Exception e) { ++ return ""; ++ } ++ ++ if (sd.hasCertificates()) { ++ SET certs = sd.getCertificates(); ++ ++ for (int i = 0; i < certs.size(); i++) { ++ org.mozilla.jss.pkix.cert.Certificate cert = ++ (org.mozilla.jss.pkix.cert.Certificate) certs.elementAt(i); ++ X509CertImpl certImpl = null; ++ try { ++ certImpl = new X509CertImpl( ++ ASN1Util.encode(cert)); ++ } catch (Exception e) { ++ } ++ ++ CertPrettyPrint print = new CertPrettyPrint(certImpl); ++ content.append(print.toString(Locale.getDefault())); ++ content.append("\n"); ++ } ++ ++ return content.toString(); ++ } ++ } ++ ++ return content.toString(); ++ } ++ ++ public String stripCertBrackets(String s) { ++ if (s == null) { ++ return s; ++ } ++ ++ if ((s.startsWith(Cert.HEADER)) && ++ (s.endsWith(Cert.FOOTER))) { ++ return (s.substring(27, (s.length() - 25))); ++ } ++ ++ // To support Thawte's header and footer ++ if ((s.startsWith("-----BEGIN PKCS #7 SIGNED DATA-----")) && ++ (s.endsWith("-----END PKCS #7 SIGNED DATA-----"))) { ++ return (s.substring(35, (s.length() - 33))); ++ } ++ ++ return s; ++ } ++ ++ public String normalizeCertStr(String s) { ++ StringBuffer val = new StringBuffer(); ++ ++ for (int i = 0; i < s.length(); i++) { ++ if (s.charAt(i) == '\n') { ++ continue; ++ } else if (s.charAt(i) == '\r') { ++ continue; ++ } else if (s.charAt(i) == '"') { ++ continue; ++ } else if (s.charAt(i) == ' ') { ++ continue; ++ } ++ val.append(s.charAt(i)); ++ } ++ return val.toString(); ++ } ++ ++ public String X509toString(Locale clientLocale) { ++ ++ //get I18N resources ++ ResourceBundle resource = ResourceBundle.getBundle( ++ PrettyPrintResources.class.getName()); ++ DateFormat dateFormater = DateFormat.getDateTimeInstance( ++ DateFormat.FULL, DateFormat.FULL, clientLocale); ++ //get timezone and timezone ID ++ String tz = " "; ++ String tzid = " "; ++ ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ X509CertInfo info = (X509CertInfo) mX509Cert.get( ++ X509CertImpl.NAME + "." + X509CertImpl.INFO); ++ String serial2 = mX509Cert.getSerialNumber().toString(16).toUpperCase(); ++ ++ //get correct instance of key ++ PublicKey pKey = mX509Cert.getPublicKey(); ++ X509Key key = null; ++ ++ if (pKey instanceof CertificateX509Key) { ++ CertificateX509Key certKey = (CertificateX509Key) pKey; ++ ++ key = (X509Key) certKey.get(CertificateX509Key.KEY); ++ } ++ if (pKey instanceof X509Key) { ++ key = (X509Key) pKey; ++ } ++ ++ //take care of spki ++ sb.append(pp.indent(4) + resource.getString( ++ PrettyPrintResources.TOKEN_CERTIFICATE) + "\n"); ++ sb.append(pp.indent(8) + resource.getString( ++ PrettyPrintResources.TOKEN_DATA) + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_VERSION) + " v"); ++ sb.append((mX509Cert.getVersion() + 1) + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_SERIAL) + "0x" + serial2 + "\n"); ++ //XXX I18N Algorithm Name ? ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_SIGALG) + mX509Cert.getSigAlgName() + ++ " - " + mX509Cert.getSigAlgOID() + "\n"); ++ //XXX I18N IssuerDN ? ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_ISSUER) + ++ mX509Cert.getIssuerDN().toString() + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_VALIDITY) + "\n"); ++ String notBefore = dateFormater.format(mX509Cert.getNotBefore()); ++ String notAfter = dateFormater.format(mX509Cert.getNotAfter()); ++ ++ //get timezone and timezone ID ++ if (TimeZone.getDefault() != null) { ++ tz = TimeZone.getDefault().getDisplayName( ++ TimeZone.getDefault().inDaylightTime( ++ mX509Cert.getNotBefore()), ++ TimeZone.SHORT, ++ clientLocale); ++ tzid = TimeZone.getDefault().getID(); ++ } ++ // Specify notBefore ++ if (tz.equals(tzid) || tzid.equals(CUSTOM_LOCALE)) { ++ // Do NOT append timezone ID ++ sb.append(pp.indent(16) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_NOT_BEFORE) ++ + notBefore ++ + "\n"); ++ } else { ++ // Append timezone ID ++ sb.append(pp.indent(16) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_NOT_BEFORE) ++ + notBefore ++ + " " + tzid + "\n"); ++ } ++ // re-get timezone (just in case it is different . . .) ++ if (TimeZone.getDefault() != null) { ++ tz = TimeZone.getDefault().getDisplayName( ++ TimeZone.getDefault().inDaylightTime( ++ mX509Cert.getNotAfter()), ++ TimeZone.SHORT, ++ clientLocale); ++ } ++ // Specify notAfter ++ if (tz.equals(tzid) || tzid.equals(CUSTOM_LOCALE)) { ++ // Do NOT append timezone ID ++ sb.append(pp.indent(16) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_NOT_AFTER) ++ + notAfter ++ + "\n"); ++ } else { ++ // Append timezone ID ++ sb.append(pp.indent(16) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_NOT_AFTER) ++ + notAfter ++ + " " + tzid + "\n"); ++ } ++ //XXX I18N SubjectDN ? ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_SUBJECT) + ++ mX509Cert.getSubjectDN().toString() + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_SPKI) + "\n"); ++ ++ PubKeyPrettyPrint pkpp = new PubKeyPrettyPrint(key); ++ ++ sb.append(pkpp.toString(clientLocale, 16, 16)); ++ ++ //take care of extensions ++ CertificateExtensions extensions = (CertificateExtensions) ++ info.get(X509CertInfo.EXTENSIONS); ++ ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_EXTENSIONS) + "\n"); ++ if (extensions != null) ++ for (int i = 0; i < extensions.size(); i++) { ++ Extension ext = extensions.elementAt(i); ++ ExtPrettyPrint extpp = new ExtPrettyPrint(ext, 16); ++ ++ sb.append(extpp.toString()); ++ } ++ ++ //take care of signature ++ sb.append(pp.indent(8) + resource.getString( ++ PrettyPrintResources.TOKEN_SIGNATURE) + "\n"); ++ //XXX I18N Algorithm Name ? ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_ALGORITHM) + ++ mX509Cert.getSigAlgName() + " - " + mX509Cert.getSigAlgOID() + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_SIGNATURE) + "\n"); ++ sb.append(pp.toHexString(mX509Cert.getSignature(), 16, 16)); ++ ++ // fingerprints ++ String[] hashes = new String[] { "MD2", "MD5", "SHA-1", "SHA-256", "SHA-512" }; ++ StringBuffer certFingerprints = new StringBuffer(); ++ ++ sb.append(pp.indent(8) + "FingerPrint\n"); ++ for (int i = 0; i < hashes.length; i++) { ++ MessageDigest md = MessageDigest.getInstance(hashes[i]); ++ ++ md.update(mX509Cert.getEncoded()); ++ certFingerprints.append(pp.indent(12) + hashes[i] + ":\n" + ++ pp.toHexString(md.digest(), 16, 16)); ++ } ++ ++ sb.append(certFingerprints.toString()); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } ++ ++ return sb.toString(); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/util/CrlPrettyPrint.java b/org/mozilla/jss/netscape/security/util/CrlPrettyPrint.java +new file mode 100644 +index 00000000..842e95d6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/CrlPrettyPrint.java +@@ -0,0 +1,271 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.text.DateFormat; ++import java.util.Iterator; ++import java.util.Locale; ++import java.util.ResourceBundle; ++import java.util.Set; ++import java.util.TimeZone; ++ ++import org.mozilla.jss.netscape.security.x509.CRLExtensions; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.RevokedCertificate; ++import org.mozilla.jss.netscape.security.x509.X509CRLImpl; ++ ++/** ++ * This class will display the certificate content in predefined ++ * format. ++ * ++ * @author Andrew Wnuk ++ * @version $Revision$, $Date$ ++ */ ++public class CrlPrettyPrint { ++ ++ /*========================================================== ++ * constants ++ *==========================================================*/ ++ private final static String CUSTOM_LOCALE = "Custom"; ++ ++ /*========================================================== ++ * variables ++ *==========================================================*/ ++ private X509CRLImpl mCRL = null; ++ private PrettyPrintFormat pp = null; ++ ++ /*========================================================== ++ * constructors ++ *==========================================================*/ ++ ++ public CrlPrettyPrint(X509CRLImpl crl) { ++ mCRL = crl; ++ pp = new PrettyPrintFormat(":"); ++ } ++ ++ /*========================================================== ++ * public methods ++ *==========================================================*/ ++ ++ /** ++ * This method return string representation of the certificate ++ * revocation list in predefined format using specified client ++ * local. I18N Support. ++ * ++ * @param clientLocale Locale to be used for localization ++ * @return string representation of the certificate ++ */ ++ public String toString(Locale clientLocale) { ++ return toString(clientLocale, 0, 0, 0); ++ } ++ ++ public String toString(Locale clientLocale, long crlSize, long pageStart, long pageSize) { ++ ++ //get I18N resources ++ ResourceBundle resource = ResourceBundle.getBundle( ++ PrettyPrintResources.class.getName()); ++ DateFormat dateFormater = DateFormat.getDateTimeInstance( ++ DateFormat.FULL, DateFormat.FULL, clientLocale); ++ //get timezone and timezone ID ++ String tz = " "; ++ String tzid = " "; ++ ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(4) + resource.getString( ++ PrettyPrintResources.TOKEN_CRL) + "\n"); ++ sb.append(pp.indent(8) + resource.getString( ++ PrettyPrintResources.TOKEN_DATA) + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_VERSION) + " v"); ++ sb.append((mCRL.getVersion() + 1) + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_SIGALG) + mCRL.getSigAlgName() + ++ " - " + mCRL.getSigAlgOID() + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_ISSUER) + ++ mCRL.getIssuerDN().toString() + "\n"); ++ // Format thisUpdate ++ String thisUpdate = dateFormater.format(mCRL.getThisUpdate()); ++ ++ // get timezone and timezone ID ++ if (TimeZone.getDefault() != null) { ++ tz = TimeZone.getDefault().getDisplayName( ++ TimeZone.getDefault().inDaylightTime( ++ mCRL.getThisUpdate()), ++ TimeZone.SHORT, ++ clientLocale); ++ tzid = TimeZone.getDefault().getID(); ++ } ++ // Specify ThisUpdate ++ if (tz.equals(tzid) || tzid.equals(CUSTOM_LOCALE)) { ++ // Do NOT append timezone ID ++ sb.append(pp.indent(12) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_THIS_UPDATE) ++ + thisUpdate ++ + "\n"); ++ } else { ++ // Append timezone ID ++ sb.append(pp.indent(12) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_THIS_UPDATE) ++ + thisUpdate ++ + " " + tzid + "\n"); ++ } ++ // Check for presence of NextUpdate ++ if (mCRL.getNextUpdate() != null) { ++ // Format nextUpdate ++ String nextUpdate = dateFormater.format(mCRL.getNextUpdate()); ++ ++ // re-get timezone (just in case it is different . . .) ++ if (TimeZone.getDefault() != null) { ++ tz = TimeZone.getDefault().getDisplayName( ++ TimeZone.getDefault().inDaylightTime( ++ mCRL.getNextUpdate()), ++ TimeZone.SHORT, ++ clientLocale); ++ } ++ // Specify NextUpdate ++ if (tz.equals(tzid) || tzid.equals(CUSTOM_LOCALE)) { ++ // Do NOT append timezone ID ++ sb.append(pp.indent(12) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_NEXT_UPDATE) ++ + nextUpdate ++ + "\n"); ++ } else { ++ // Append timezone ID ++ sb.append(pp.indent(12) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_NEXT_UPDATE) ++ + nextUpdate ++ + " " + tzid + "\n"); ++ } ++ } ++ ++ if (crlSize > 0 && pageStart == 0 && pageSize == 0) { ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_REVOKED_CERTIFICATES) + crlSize + "\n"); ++ } else if ((crlSize == 0 && pageStart == 0 && pageSize == 0) || ++ (crlSize > 0 && pageStart > 0 && pageSize > 0)) { ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_REVOKED_CERTIFICATES)); ++ if (crlSize > 0 && pageStart > 0 && pageSize > 0) { ++ long upperLimit = (pageStart + pageSize - 1 > crlSize) ? crlSize : pageStart + pageSize - 1; ++ ++ sb.append("" + pageStart + "-" + upperLimit + " of " + crlSize); ++ } ++ sb.append("\n"); ++ ++ Set revokedCerts = mCRL.getRevokedCertificates(); ++ ++ if (revokedCerts != null) { ++ Iterator i = revokedCerts.iterator(); ++ long l = 1; ++ ++ while ((i.hasNext()) && ((crlSize == 0) || (pageStart + pageSize > l))) { ++ RevokedCertificate revokedCert = i.next(); ++ ++ if ((crlSize == 0) || ((pageStart <= l) && (pageStart + pageSize > l))) { ++ sb.append(pp.indent(16) + resource.getString( ++ PrettyPrintResources.TOKEN_SERIAL) + "0x" + ++ revokedCert.getSerialNumber().toString(16).toUpperCase() + "\n"); ++ String revocationDate = ++ dateFormater.format(revokedCert.getRevocationDate()); ++ ++ // re-get timezone ++ // (just in case it is different . . .) ++ if (TimeZone.getDefault() != null) { ++ tz = TimeZone.getDefault().getDisplayName( ++ TimeZone.getDefault().inDaylightTime( ++ revokedCert.getRevocationDate()), ++ TimeZone.SHORT, ++ clientLocale); ++ } ++ // Specify revocationDate ++ if (tz.equals(tzid) || ++ tzid.equals(CUSTOM_LOCALE)) { ++ // Do NOT append timezone ID ++ sb.append(pp.indent(16) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_REVOCATION_DATE) ++ + revocationDate ++ + "\n"); ++ } else { ++ // Append timezone ID ++ sb.append(pp.indent(16) ++ + resource.getString( ++ PrettyPrintResources.TOKEN_REVOCATION_DATE) ++ + revocationDate ++ + " " + tzid + "\n"); ++ } ++ if (revokedCert.hasExtensions()) { ++ sb.append(pp.indent(16) + resource.getString( ++ PrettyPrintResources.TOKEN_EXTENSIONS) + "\n"); ++ CRLExtensions crlExtensions = revokedCert.getExtensions(); ++ ++ if (crlExtensions != null) { ++ for (int k = 0; k < crlExtensions.size(); k++) { ++ Extension ext = crlExtensions.elementAt(k); ++ ExtPrettyPrint extpp = new ExtPrettyPrint(ext, 20); ++ ++ sb.append(extpp.toString()); ++ } ++ } ++ } ++ } ++ l++; ++ } ++ } ++ } ++ ++ CRLExtensions crlExtensions = mCRL.getExtensions(); ++ ++ if (crlExtensions != null) { ++ sb.append(pp.indent(8) + resource.getString( ++ PrettyPrintResources.TOKEN_EXTENSIONS) + "\n"); ++ for (int k = 0; k < crlExtensions.size(); k++) { ++ Extension ext = crlExtensions.elementAt(k); ++ ExtPrettyPrint extpp = new ExtPrettyPrint(ext, 12); ++ ++ sb.append(extpp.toString()); ++ } ++ } ++ ++ //take care of signature ++ sb.append(pp.indent(8) + resource.getString( ++ PrettyPrintResources.TOKEN_SIGNATURE) + "\n"); ++ //XXX I18N Algorithm Name ? ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_ALGORITHM) + ++ mCRL.getSigAlgName() + " - " + mCRL.getSigAlgOID() + "\n"); ++ sb.append(pp.indent(12) + resource.getString( ++ PrettyPrintResources.TOKEN_SIGNATURE) + "\n"); ++ sb.append(pp.toHexString(mCRL.getSignature(), 16, 16)); ++ ++ } catch (Exception e) { ++ sb.append("\n\n" + pp.indent(4) + resource.getString( ++ PrettyPrintResources.TOKEN_DECODING_ERROR) + "\n\n"); ++ e.printStackTrace(); ++ } ++ ++ return sb.toString(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/DerEncoder.java b/org/mozilla/jss/netscape/security/util/DerEncoder.java +new file mode 100644 +index 00000000..9121e24d +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/DerEncoder.java +@@ -0,0 +1,40 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.IOException; ++import java.io.OutputStream; ++ ++/** ++ * Interface to an object that knows how to write its own DER ++ * encoding to an output stream. ++ * ++ * @version 1.2 97/12/10 ++ * @author D. N. Hoover ++ */ ++public interface DerEncoder { ++ ++ /** ++ * DER encode this object and write the results to a stream. ++ * ++ * @param out the stream on which the DER encoding is written. ++ */ ++ public void derEncode(OutputStream out) ++ throws IOException; ++ ++} +diff --git a/org/mozilla/jss/netscape/security/util/DerInputBuffer.java b/org/mozilla/jss/netscape/security/util/DerInputBuffer.java +new file mode 100644 +index 00000000..c1c33ac0 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/DerInputBuffer.java +@@ -0,0 +1,186 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.ByteArrayInputStream; ++import java.io.IOException; ++import java.io.OutputStream; ++ ++/** ++ * DER input buffer ... this is the main abstraction in the DER library ++ * which actively works with the "untyped byte stream" abstraction. It ++ * does so with impunity, since it's not intended to be exposed to the ++ * anyone who could violate the "typed value stream" DER model and hence ++ * corrupt the input stream of DER values. ++ * ++ * @version 1.11 ++ * @author David Brownell ++ */ ++class DerInputBuffer extends ByteArrayInputStream implements Cloneable { ++ ++ DerInputBuffer(byte[] buf) { ++ super(buf); ++ } ++ ++ DerInputBuffer(byte[] buf, int offset, int len) { ++ super(buf, offset, len); ++ } ++ ++ DerInputBuffer dup() { ++ try { ++ DerInputBuffer retval = (DerInputBuffer) clone(); ++ ++ retval.mark(Integer.MAX_VALUE); ++ return retval; ++ } catch (CloneNotSupportedException e) { ++ throw new IllegalArgumentException(e.toString()); ++ } ++ } ++ ++ byte[] toByteArray() throws IOException { ++ int len = available(); ++ if (len <= 0) ++ throw new IOException("No Buffer Space Available."); ++ byte[] retval = new byte[len]; ++ ++ System.arraycopy(buf, pos, retval, 0, len); ++ return retval; ++ } ++ ++ int peek() throws IOException { ++ if (pos >= count) ++ throw new IOException("out of data"); ++ else ++ return buf[pos]; ++ } ++ ++ /** ++ * Compares this DerInputBuffer for equality with the specified ++ * object. ++ */ ++ public boolean equals(Object other) { ++ if (other instanceof DerInputBuffer) ++ return equals((DerInputBuffer) other); ++ else ++ return false; ++ } ++ ++ boolean equals(DerInputBuffer other) { ++ if (this == other) ++ return true; ++ ++ int max = this.available(); ++ if (other.available() != max) ++ return false; ++ for (int i = 0; i < max; i++) { ++ if (this.buf[this.pos + i] != other.buf[other.pos + i]) { ++ return false; ++ } ++ } ++ return true; ++ } ++ ++ void truncate(int len) throws IOException { ++ if (len > available()) ++ throw new IOException("insufficient data"); ++ count = pos + len; ++ } ++ ++ /** ++ * Returns the unsigned integer which takes up the specified number ++ * of bytes in this buffer. ++ */ ++ BigInt getUnsigned(int len) throws IOException { ++ if (len > available()) ++ throw new IOException("short read, getInteger"); ++ ++ /* ++ * A prepended zero is used to ensure that the integer is ++ * interpreted as unsigned even when the high order bit is ++ * zero. We don't support signed BigInts. ++ * ++ * Fix this here ... BigInts aren't expected to have these, ++ * and stuff like signing (sigsize = f(modulus)) misbehaves. ++ */ ++ if (len > 1 && buf[pos] == 0) { ++ len--; ++ skip(1); ++ } ++ ++ /* ++ * Consume the rest of the buffer, returning its value as ++ * an unsigned integer. ++ */ ++ byte[] bytes = new byte[len]; ++ ++ System.arraycopy(buf, pos, bytes, 0, len); ++ skip(len); ++ return new BigInt(bytes); ++ } ++ ++ /** ++ * Returns the bit string which takes up the rest of this buffer. ++ * This bit string must be byte-aligned. ++ */ ++ byte[] getBitString() { ++ if (pos >= count || buf[pos] != 0) ++ return null; ++ /* ++ * Just copy the data into an aligned, padded octet buffer, ++ * and consume the rest of the buffer. ++ */ ++ int len = available(); ++ byte[] retval = new byte[len - 1]; ++ ++ System.arraycopy(buf, pos + 1, retval, 0, len - 1); ++ pos = count; ++ return retval; ++ } ++ ++ /** ++ * Returns the bit string which takes up the rest of this buffer. ++ * The bit string need not be byte-aligned. ++ */ ++ BitArray getUnalignedBitString() { ++ if (pos >= count) ++ return null; ++ /* ++ * Just copy the data into an aligned, padded octet buffer, ++ * and consume the rest of the buffer. ++ */ ++ int len = available(); ++ byte[] bits = new byte[len - 1]; ++ int length = bits.length * 8 - buf[pos]; // number of valid bits ++ ++ System.arraycopy(buf, pos + 1, bits, 0, len - 1); ++ ++ BitArray bitArray = new BitArray(length, bits); ++ pos = count; ++ return bitArray; ++ } ++ ++ /** ++ * Package-access method to optimize output operations ++ */ ++ void dump(OutputStream out, int length) throws IOException { ++ if (count < mark + length) ++ throw new IOException("short DER value (encode)"); ++ out.write(buf, mark, length); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/util/DerInputStream.java b/org/mozilla/jss/netscape/security/util/DerInputStream.java +new file mode 100644 +index 00000000..6ff4bf23 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/DerInputStream.java +@@ -0,0 +1,667 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.util.Calendar; ++import java.util.Date; ++import java.util.GregorianCalendar; ++import java.util.TimeZone; ++import java.util.Vector; ++ ++/** ++ * A DER input stream, used for parsing ASN.1 DER-encoded data such as ++ * that found in X.509 certificates. DER is a subset of BER/1, which has ++ * the advantage that it allows only a single encoding of primitive data. ++ * (High level data such as dates still support many encodings.) That is, ++ * it uses the "Definite" Encoding Rules (DER) not the "Basic" ones (BER). ++ * ++ *

++ * Note that, like BER/1, DER streams are streams of explicitly tagged data values. Accordingly, this programming ++ * interface does not expose any variant of the java.io.InputStream interface, since that kind of input stream holds ++ * untagged data values and using that I/O model could prevent correct parsing of the DER data. ++ * ++ *

++ * At this time, this class supports only a subset of the types of DER data encodings which are defined. That subset is ++ * sufficient for parsing most X.509 certificates. ++ * ++ * @version 1.35 ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class DerInputStream { ++ /* ++ * This version only supports fully buffered DER. This is easy to ++ * work with, though if large objects are manipulated DER becomes ++ * awkward to deal with. That's where BER is useful, since BER ++ * handles streaming data relatively well. ++ */ ++ DerInputBuffer buffer; ++ ++ /** ++ * Create a DER input stream from a data buffer. The buffer is not ++ * copied, it is shared. Accordingly, the buffer should be treated ++ * as read-only. ++ * ++ * @param data the buffer from which to create the string (CONSUMED) ++ */ ++ public DerInputStream(byte[] data) { ++ buffer = new DerInputBuffer(data); ++ buffer.mark(Integer.MAX_VALUE); ++ } ++ ++ /** ++ * Create a DER input stream from part of a data buffer. ++ * The buffer is not copied, it is shared. Accordingly, the ++ * buffer should be treated as read-only. ++ * ++ * @param data the buffer from which to create the string (CONSUMED) ++ * @param offset the first index of data which will ++ * be read as DER input in the new stream ++ * @param len how long a chunk of the buffer to use, ++ * starting at "offset" ++ */ ++ public DerInputStream(byte[] data, int offset, int len) { ++ buffer = new DerInputBuffer(data, offset, len); ++ buffer.mark(Integer.MAX_VALUE); ++ } ++ ++ DerInputStream(DerInputBuffer buf) { ++ buffer = buf; ++ buffer.mark(Integer.MAX_VALUE); ++ } ++ ++ /** ++ * Creates a new DER input stream from part of this input stream. ++ * ++ * @param len how long a chunk of the current input stream to use, ++ * starting at the current position. ++ * @param do_skip true if the existing data in the input stream should ++ * be skipped. If this value is false, the next data read ++ * on this stream and the newly created stream will be the ++ * same. ++ */ ++ public DerInputStream subStream(int len, boolean do_skip) ++ throws IOException { ++ DerInputBuffer newbuf = buffer.dup(); ++ ++ newbuf.truncate(len); ++ if (do_skip) ++ buffer.skip(len); ++ return new DerInputStream(newbuf); ++ } ++ ++ /** ++ * Return what has been written to this DerInputStream ++ * as a byte array. Useful for debugging. ++ * @throws IOException ++ */ ++ public byte[] toByteArray() throws IOException { ++ return buffer.toByteArray(); ++ } ++ ++ /* ++ * PRIMITIVES -- these are "universal" ASN.1 simple types. ++ * ++ * INTEGER, BIT STRING, OCTET STRING, NULL ++ * OBJECT IDENTIFIER, SEQUENCE (OF), SET (OF) ++ * PrintableString, T61String, IA5String, UTCTime ++ */ ++ ++ /** ++ * Get an (unsigned) integer from the input stream. ++ */ ++ public BigInt getInteger() throws IOException { ++ if (buffer.read() != DerValue.tag_Integer) ++ throw new IOException("DER input, Integer tag error"); ++ ++ return buffer.getUnsigned(getLength(buffer)); ++ } ++ ++ /** ++ * Get a bit string from the input stream. Only octet-aligned ++ * bitstrings (multiples of eight bits in length) are handled ++ * by this method. ++ */ ++ public byte[] getBitString() throws IOException { ++ if (buffer.read() != DerValue.tag_BitString) ++ throw new IOException("DER input not an bit string"); ++ int length = getLength(buffer); ++ ++ /* ++ * This byte affects alignment and padding (for the last byte). ++ * Use getUnalignedBitString() for none 8-bit aligned bit strings. ++ */ ++ if (buffer.read() != 0) ++ return null; ++ length--; ++ ++ /* ++ * Just read the data into an aligned, padded octet buffer. ++ */ ++ byte[] retval = new byte[length]; ++ if (buffer.read(retval) != length) ++ throw new IOException("short read of DER bit string"); ++ return retval; ++ } ++ ++ /** ++ * Get a bit string from the input stream. The bit string need ++ * not be byte-aligned. ++ */ ++ public BitArray getUnalignedBitString() throws IOException { ++ if (buffer.read() != DerValue.tag_BitString) ++ throw new IOException("DER input not a bit string"); ++ ++ int length = getLength(buffer) - 1; ++ ++ /* ++ * First byte = number of excess bits in the last octet of the ++ * representation. ++ */ ++ int validBits = length * 8 - buffer.read(); ++ ++ byte[] repn = new byte[length]; ++ ++ if (buffer.read(repn) != length) ++ throw new IOException("short read of DER bit string"); ++ return new BitArray(validBits, repn); ++ } ++ ++ /** ++ * Returns an ASN.1 OCTET STRING from the input stream. ++ */ ++ public byte[] getOctetString() throws IOException { ++ if (buffer.read() != DerValue.tag_OctetString) ++ throw new IOException("DER input not an octet string"); ++ ++ int length = getLength(buffer); ++ byte[] retval = new byte[length]; ++ if (buffer.read(retval) != length) ++ throw new IOException("short read of DER octet string"); ++ ++ return retval; ++ } ++ ++ /** ++ * Returns the asked number of bytes from the input stream. ++ */ ++ public void getBytes(byte[] val) throws IOException { ++ if (val.length != 0) { ++ if (buffer.read(val) != val.length) { ++ throw new IOException("short read of DER octet string"); ++ } ++ } ++ } ++ ++ /** ++ * Reads an encoded null value from the input stream. ++ */ ++ public void getNull() throws IOException { ++ if (buffer.read() != DerValue.tag_Null || buffer.read() != 0) ++ throw new IOException("getNull, bad data"); ++ } ++ ++ /** ++ * Reads an X.200 style Object Identifier from the stream. ++ */ ++ public ObjectIdentifier getOID() throws IOException { ++ return new ObjectIdentifier(this); ++ } ++ ++ /** ++ * Return a sequence of encoded entities. ASN.1 sequences are ++ * ordered, and they are often used, like a "struct" in C or C++, ++ * to group data values. They may have optional or context ++ * specific values. ++ * ++ * @param startLen guess about how long the sequence will be ++ * (used to initialize an auto-growing data structure) ++ * @return array of the values in the sequence ++ */ ++ public DerValue[] getSequence(int startLen) throws IOException { ++ int b = buffer.read(); ++ if (b != DerValue.tag_Sequence) ++ throw new IOException("Sequence tag error " + b); ++ return readVector(startLen); ++ } ++ ++ public void skipSequence(int startLen) throws IOException { ++ int b = buffer.read(); ++ if (b != DerValue.tag_Sequence) ++ throw new IOException("Sequence tag error " + b); ++ int len = getLength(buffer); ++ buffer.skip(len); ++ } ++ ++ /** ++ * Return a set of encoded entities. ASN.1 sets are unordered, ++ * though DER may specify an order for some kinds of sets (such ++ * as the attributes in an X.500 relative distinguished name) ++ * to facilitate binary comparisons of encoded values. ++ * ++ * @param startLen guess about how large the set will be ++ * (used to initialize an auto-growing data structure) ++ * @return array of the values in the sequence ++ */ ++ public DerValue[] getSet(int startLen) throws IOException { ++ if (buffer.read() != DerValue.tag_Set) ++ throw new IOException("Set tag error"); ++ return readVector(startLen); ++ } ++ ++ /** ++ * Return a set of encoded entities. ASN.1 sets are unordered, ++ * though DER may specify an order for some kinds of sets (such ++ * as the attributes in an X.500 relative distinguished name) ++ * to facilitate binary comparisons of encoded values. ++ * ++ * @param startLen guess about how large the set will be ++ * (used to initialize an auto-growing data structure) ++ * @param implicit if true tag is assumed implicit. ++ * @return array of the values in the sequence ++ */ ++ public DerValue[] getSet(int startLen, boolean implicit) throws IOException { ++ int tag = buffer.read(); ++ if (!implicit) { ++ if (tag != DerValue.tag_Set) { ++ throw new IOException("Set tag error"); ++ } ++ } ++ return (readVector(startLen)); ++ } ++ ++ /* ++ * Read a "vector" of values ... set or sequence have the ++ * same encoding, except for the initial tag, so both use ++ * this same helper routine. ++ */ ++ protected DerValue[] readVector(int startLen) throws IOException { ++ int len = getLength(buffer); ++ DerInputStream newstr; ++ ++ if (len == 0) ++ // return empty array instead of null, which should be ++ // used only for missing optionals ++ return new DerValue[0]; ++ ++ /* ++ * Create a temporary stream from which to read the data, ++ * unless it's not really needed. ++ */ ++ if (buffer.available() == len) ++ newstr = this; ++ else ++ newstr = subStream(len, true); ++ ++ /* ++ * Pull values out of the stream. ++ */ ++ Vector vec = new Vector(startLen); ++ DerValue value; ++ ++ do { ++ value = new DerValue(newstr.buffer); ++ vec.addElement(value); ++ } while (newstr.available() > 0); ++ ++ if (newstr.available() != 0) ++ throw new IOException("extra data at end of vector"); ++ ++ /* ++ * Now stick them into the array we're returning. ++ */ ++ int i, max = vec.size(); ++ DerValue[] retval = new DerValue[max]; ++ ++ for (i = 0; i < max; i++) ++ retval[i] = vec.elementAt(i); ++ ++ return retval; ++ } ++ ++ /** ++ * Get a single DER-encoded value from the input stream. ++ * It can often be useful to pull a value from the stream ++ * and defer parsing it. For example, you can pull a nested ++ * sequence out with one call, and only examine its elements ++ * later when you really need to. ++ */ ++ public DerValue getDerValue() throws IOException { ++ return new DerValue(buffer); ++ } ++ ++ public String getPrintableString() throws IOException { ++ return (new DerValue(buffer)).getPrintableString(); ++ } ++ ++ public String getT61String() throws IOException { ++ return (new DerValue(buffer)).getT61String(); ++ } ++ ++ public String getIA5String() throws IOException { ++ return (new DerValue(buffer)).getIA5String(); ++ } ++ ++ public String getBMPString() throws IOException { ++ return (new DerValue(buffer)).getBMPString(); ++ } ++ ++ public String getUniversalString() throws IOException { ++ return (new DerValue(buffer)).getUniversalString(); ++ } ++ ++ public String getDirectoryString() throws IOException { ++ return (new DerValue(buffer)).getDirectoryString(); ++ } ++ ++ /** ++ * Get a UTC encoded time value from the input stream. ++ */ ++ public Date getUTCTime() throws IOException { ++ if (buffer.read() != DerValue.tag_UtcTime) ++ throw new IOException("DER input, UTCtime tag invalid "); ++ if (buffer.available() < 11) ++ throw new IOException("DER input, UTCtime short input"); ++ ++ int len = getLength(buffer); ++ ++ if (len < 11 || len > 17) ++ throw new IOException("DER getUTCTime length error"); ++ ++ /* ++ * UTC time encoded as ASCII chars, YYMMDDhhmmss. ++ * If YY <= 50, we assume 20YY; ++ * if YY > 50, we assume 19YY, as per IETF-PKIX part I. ++ */ ++ int year, month, day, hour, minute, second; ++ ++ year = 10 * Character.digit((char) buffer.read(), 10); ++ year += Character.digit((char) buffer.read(), 10); ++ if (year <= 50) // origin 2000 ++ year += 2000; ++ else ++ year += 1900; // origin 1900 ++ ++ month = 10 * Character.digit((char) buffer.read(), 10); ++ month += Character.digit((char) buffer.read(), 10); ++ month -= 1; // months are 0-11 ++ ++ day = 10 * Character.digit((char) buffer.read(), 10); ++ day += Character.digit((char) buffer.read(), 10); ++ ++ hour = 10 * Character.digit((char) buffer.read(), 10); ++ hour += Character.digit((char) buffer.read(), 10); ++ ++ minute = 10 * Character.digit((char) buffer.read(), 10); ++ minute += Character.digit((char) buffer.read(), 10); ++ ++ len -= 10; ++ ++ /** ++ * We allow for non-encoded seconds, even though the ++ * IETF-PKIX specification says that the seconds should ++ * always be encoded even if it is zero. ++ */ ++ ++ if (len == 3 || len == 7) { ++ second = 10 * Character.digit((char) buffer.read(), 10); ++ second += Character.digit((char) buffer.read(), 10); ++ len -= 2; ++ } else ++ second = 0; ++ ++ if (month < 0 || day <= 0 ++ || month > 11 || day > 31 || hour >= 24 ++ || minute >= 60 || second >= 60) ++ throw new IOException("Parse UTC time, invalid format"); ++ ++ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); ++ cal.set(year, month, day, hour, minute, second); ++ cal.set(Calendar.MILLISECOND, 0); /* To clear millisecond field */ ++ cal.set(Calendar.ERA, GregorianCalendar.AD); ++ Date readDate = cal.getTime(); ++ long utcTime = readDate.getTime(); ++ ++ /* ++ * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm ++ */ ++ if (!(len == 1 || len == 5)) ++ throw new IOException("Parse UTC time, invalid offset"); ++ ++ switch (buffer.read()) { ++ case '+': { ++ int Htmp = 10 * Character.digit((char) buffer.read(), 10); ++ Htmp += Character.digit((char) buffer.read(), 10); ++ int Mtmp = 10 * Character.digit((char) buffer.read(), 10); ++ Mtmp += Character.digit((char) buffer.read(), 10); ++ ++ if (Htmp >= 24 || Mtmp >= 60) ++ throw new IOException("Parse UTCtime, +hhmm"); ++ ++ utcTime += ((Htmp * 60) + Mtmp) * 60 * 1000L; ++ } ++ break; ++ ++ case '-': { ++ int Htmp = 10 * Character.digit((char) buffer.read(), 10); ++ Htmp += Character.digit((char) buffer.read(), 10); ++ int Mtmp = 10 * Character.digit((char) buffer.read(), 10); ++ Mtmp += Character.digit((char) buffer.read(), 10); ++ ++ if (Htmp >= 24 || Mtmp >= 60) ++ throw new IOException("Parse UTCtime, -hhmm"); ++ ++ utcTime -= ((Htmp * 60) + Mtmp) * 60 * 1000L; ++ } ++ break; ++ ++ case 'Z': ++ break; ++ ++ default: ++ throw new IOException("Parse UTCtime, garbage offset"); ++ } ++ readDate.setTime(utcTime); ++ return readDate; ++ } ++ ++ /** ++ * Get a Generalized encoded time value from the input stream. ++ */ ++ public Date getGeneralizedTime() throws IOException { ++ if (buffer.read() != DerValue.tag_GeneralizedTime) ++ throw new IOException("DER input, GeneralizedTime tag invalid "); ++ ++ if (buffer.available() < 13) ++ throw new IOException("DER input, GeneralizedTime short input"); ++ ++ int len = getLength(buffer); ++ ++ /* ++ * Generalized time encoded as ASCII chars, YYYYMMDDhhmm[ss] ++ */ ++ int year, month, day, hour, minute, second; ++ ++ year = 1000 * Character.digit((char) buffer.read(), 10); ++ year += 100 * Character.digit((char) buffer.read(), 10); ++ year += 10 * Character.digit((char) buffer.read(), 10); ++ year += Character.digit((char) buffer.read(), 10); ++ ++ month = 10 * Character.digit((char) buffer.read(), 10); ++ month += Character.digit((char) buffer.read(), 10); ++ month -= 1; // Calendar months are 0-11 ++ ++ day = 10 * Character.digit((char) buffer.read(), 10); ++ day += Character.digit((char) buffer.read(), 10); ++ ++ hour = 10 * Character.digit((char) buffer.read(), 10); ++ hour += Character.digit((char) buffer.read(), 10); ++ ++ minute = 10 * Character.digit((char) buffer.read(), 10); ++ minute += Character.digit((char) buffer.read(), 10); ++ ++ len -= 12; ++ ++ /** ++ * We allow for non-encoded seconds, even though the ++ * IETF-PKIX specification says that the seconds should ++ * always be encoded even if it is zero. ++ */ ++ ++ if (len == 3 || len == 7) { ++ second = 10 * Character.digit((char) buffer.read(), 10); ++ second += Character.digit((char) buffer.read(), 10); ++ len -= 2; ++ } else ++ second = 0; ++ ++ if (month < 0 || day <= 0 ++ || month > 11 || day > 31 || hour >= 24 ++ || minute >= 60 || second >= 60) ++ throw new IOException("Parse Generalized time, invalid format"); ++ ++ /* Shouldn't this construct a Gregorian calendar directly??? ++ * We don't really want locale dependant processing here */ ++ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); ++ cal.set(year, month, day, hour, minute, second); ++ cal.set(Calendar.MILLISECOND, 0); /* To clear millisecond field */ ++ cal.set(Calendar.ERA, GregorianCalendar.AD); ++ Date readDate = cal.getTime(); ++ long utcTime = readDate.getTime(); ++ ++ /* ++ * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm ++ */ ++ if (!(len == 1 || len == 5)) ++ throw new IOException("Parse Generalized time, invalid offset"); ++ ++ switch (buffer.read()) { ++ case '+': { ++ int Htmp = 10 * Character.digit((char) buffer.read(), 10); ++ Htmp += Character.digit((char) buffer.read(), 10); ++ int Mtmp = 10 * Character.digit((char) buffer.read(), 10); ++ Mtmp += Character.digit((char) buffer.read(), 10); ++ ++ if (Htmp >= 24 || Mtmp >= 60) ++ throw new IOException("Parse GeneralizedTime, +hhmm"); ++ ++ utcTime += ((Htmp * 60) + Mtmp) * 60 * 1000L; ++ } ++ break; ++ ++ case '-': { ++ int Htmp = 10 * Character.digit((char) buffer.read(), 10); ++ Htmp += Character.digit((char) buffer.read(), 10); ++ int Mtmp = 10 * Character.digit((char) buffer.read(), 10); ++ Mtmp += Character.digit((char) buffer.read(), 10); ++ ++ if (Htmp >= 24 || Mtmp >= 60) ++ throw new IOException("Parse GeneralizedTime, -hhmm"); ++ ++ utcTime -= ((Htmp * 60) + Mtmp) * 60 * 1000L; ++ } ++ break; ++ ++ case 'Z': ++ break; ++ ++ default: ++ throw new IOException("Parse GeneralizedTime, garbage offset"); ++ } ++ readDate.setTime(utcTime); ++ return readDate; ++ } ++ ++ /* ++ * Get a byte from the input stream. ++ */ ++ // package private ++ int getByte() throws IOException { ++ return (0x00ff & buffer.read()); ++ } ++ ++ public int peekByte() throws IOException { ++ return buffer.peek(); ++ } ++ ++ // package private ++ int getLength() throws IOException { ++ return getLength(buffer); ++ } ++ ++ /* ++ * Get a length from the input stream, allowing for at most 32 bits of ++ * encoding to be used. (Not the same as getting a tagged integer!) ++ */ ++ static int getLength(InputStream in) throws IOException { ++ int value, tmp; ++ ++ tmp = in.read(); ++ if ((tmp & 0x080) == 0x00) { // 1 byte datum? ++ value = tmp; ++ } else { // no, more ... ++ tmp &= 0x07f; ++ ++ /* ++ * NOTE: tmp == 0 indicates BER encoded data. ++ * tmp > 4 indicates more than 4Gb of data. ++ */ ++ if (tmp <= 0 || tmp > 4) ++ throw new IOException("DerInput.getLength(): lengthTag=" ++ + tmp + ", " ++ + ((tmp == 0) ? "Indefinite length encoding not supported" ++ + " or incorrect DER encoding." ++ : "too big.")); ++ ++ for (value = 0; tmp > 0; tmp--) { ++ value <<= 8; ++ value += 0x0ff & in.read(); ++ } ++ } ++ return value; ++ } ++ ++ /** ++ * Mark the current position in the buffer, so that ++ * a later call to reset will return here. ++ */ ++ public void mark(int value) { ++ buffer.mark(value); ++ } ++ ++ /** ++ * Return to the position of the last mark call. A mark is implicitly set at the beginning of ++ * the stream when it is created. ++ */ ++ public void reset() { ++ buffer.reset(); ++ } ++ ++ /** ++ * Returns the number of bytes available for reading. ++ * This is most useful for testing whether the stream is ++ * empty. ++ */ ++ public int available() { ++ return buffer.available(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/DerOutputStream.java b/org/mozilla/jss/netscape/security/util/DerOutputStream.java +new file mode 100644 +index 00000000..ad355926 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/DerOutputStream.java +@@ -0,0 +1,729 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.ByteArrayOutputStream; ++import java.io.IOException; ++import java.io.OutputStream; ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.CharacterCodingException; ++import java.nio.charset.CharsetEncoder; ++import java.text.SimpleDateFormat; ++import java.util.Arrays; ++import java.util.Calendar; ++import java.util.Comparator; ++import java.util.Date; ++import java.util.GregorianCalendar; ++import java.util.TimeZone; ++ ++/** ++ * Output stream marshaling DER-encoded data. This is eventually provided ++ * in the form of a byte array; there is no advance limit on the size of ++ * that byte array. ++ * ++ *

++ * At this time, this class supports only a subset of the types of DER data encodings which are defined. That subset is ++ * sufficient for generating most X.509 certificates. ++ * ++ * @version 1.32 ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class DerOutputStream ++ extends ByteArrayOutputStream implements DerEncoder { ++ /** ++ * Construct an DER output stream. ++ * ++ * @param size how large a buffer to preallocate. ++ */ ++ public DerOutputStream(int size) { ++ super(size); ++ } ++ ++ /** ++ * Construct an DER output stream. ++ */ ++ public DerOutputStream() { ++ } ++ ++ /** ++ * Writes tagged, pre-marshaled data. This calcuates and encodes ++ * the length, so that the output data is the standard triple of ++ * { tag, length, data } used by all DER values. ++ * ++ * @param tag the DER value tag for the data, such as DerValue.tag_Sequence ++ * @param buf buffered data, which must be DER-encoded ++ */ ++ public void write(byte tag, byte[] buf) throws IOException { ++ write(tag); ++ putLength(buf.length); ++ write(buf, 0, buf.length); ++ } ++ ++ /** ++ * Writes tagged data using buffer-to-buffer copy. As above, ++ * this writes a standard DER record. This is often used when ++ * efficiently encapsulating values in sequences. ++ * ++ * @param tag the DER value tag for the data, such as DerValue.tag_Sequence ++ * @param out buffered data ++ */ ++ public void write(byte tag, DerOutputStream out) throws IOException { ++ write(tag); ++ putLength(out.count); ++ write(out.buf, 0, out.count); ++ } ++ ++ /** ++ * Writes implicitly tagged data using buffer-to-buffer copy. As above, ++ * this writes a standard DER record. This is often used when ++ * efficiently encapsulating implicitly tagged values. ++ * ++ * @param tag the DER value of the context-specific tag that replaces ++ * original tag of the value in the output , such as in ++ * ++ *

++     * 	  [N] IMPLICIT 
++     * 
++ * ++ * For example, FooLength [1] IMPLICIT INTEGER, with value=4; ++ * would be encoded as "81 01 04" whereas in explicit ++ * tagging it would be encoded as "A1 03 02 01 04". ++ * Notice that the tag is A1 and not 81, this is because with ++ * explicit tagging the form is always constructed. ++ * @param value original value being implicitly tagged ++ */ ++ public void writeImplicit(byte tag, DerOutputStream value) ++ throws IOException { ++ write(tag); ++ write(value.buf, 1, value.count - 1); ++ } ++ ++ /** ++ * Marshals pre-encoded DER value onto the output stream. ++ */ ++ public void putDerValue(DerValue val) throws IOException { ++ val.encode(this); ++ } ++ ++ /* ++ * PRIMITIVES -- these are "universal" ASN.1 simple types. ++ * ++ * BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL ++ * OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF) ++ * PrintableString, T61String, IA5String, UTCTime ++ */ ++ ++ /** ++ * Marshals a DER boolean on the output stream. ++ */ ++ public void putBoolean(boolean val) throws IOException { ++ write(DerValue.tag_Boolean); ++ putLength(1); ++ if (val) { ++ write(0xff); ++ } else { ++ write(0); ++ } ++ } ++ ++ /** ++ * Marshals a DER unsigned integer on the output stream. ++ */ ++ public void putInteger(BigInt i) throws IOException { ++ putUnsignedInteger(i.toByteArray()); ++ } ++ ++ /** ++ * Marshals a DER unsigned integer on the output stream. ++ */ ++ public void putUnsignedInteger(byte[] integerBytes) throws IOException { ++ ++ write(DerValue.tag_Integer); ++ if ((integerBytes[0] & 0x080) != 0) { ++ /* ++ * prepend zero so it's not read as a negative number ++ */ ++ putLength(integerBytes.length + 1); ++ write(0); ++ } else ++ putLength(integerBytes.length); ++ write(integerBytes, 0, integerBytes.length); ++ } ++ ++ /** ++ * Marshals a DER enumerated value on the output stream. ++ */ ++ public void putEnumerated(int i) throws IOException { ++ write(DerValue.tag_Enumerated); ++ ++ int bytemask = 0xff000000; ++ int signmask = 0x80000000; ++ int length; ++ if ((i & 0x80000000) != 0) { ++ // negative case ++ for (length = 4; length > 1; --length) { ++ if ((i & bytemask) != bytemask) ++ break; ++ bytemask = bytemask >>> 8; ++ signmask = signmask >>> 8; ++ } ++ if ((i & signmask) == 0) { ++ // ensure negative case ++ putLength(length + 1); ++ write(0xff); ++ } else { ++ putLength(length); ++ } ++ // unrolled loop ++ switch (length) { ++ case 4: ++ write((byte) (i >>> 24)); ++ case 3: ++ write((byte) (i >>> 16)); ++ case 2: ++ write((byte) (i >>> 8)); ++ case 1: ++ write((byte) i); ++ } ++ } else { ++ // positive case ++ for (length = 4; length > 0; --length) { ++ if ((i & bytemask) != 0) ++ break; ++ bytemask = bytemask >>> 8; ++ signmask = signmask >>> 8; ++ } ++ if ((i & signmask) != 0) { ++ // ensure posititive case ++ putLength(length + 1); ++ write(0x00); ++ } else { ++ putLength(length); ++ } ++ // unrolled loop ++ switch (length) { ++ case 4: ++ write((byte) (i >>> 24)); ++ case 3: ++ write((byte) (i >>> 16)); ++ case 2: ++ write((byte) (i >>> 8)); ++ case 1: ++ write((byte) i); ++ } ++ } ++ } ++ ++ /** ++ * Marshals a DER bit string on the output stream. The bit ++ * string must be byte-aligned. ++ * ++ * @param bits the bit string, MSB first ++ */ ++ public void putBitString(byte[] bits) throws IOException { ++ write(DerValue.tag_BitString); ++ putLength(bits.length + 1); ++ write(0); // all of last octet is used ++ write(bits); ++ } ++ ++ /** ++ * Converts a boolean array to a BitArray. Trims trailing 0 bits ++ * in accordance with DER encoding standard. We assume the input is not ++ * null. ++ */ ++ private static BitArray toBitArray(boolean[] bitString) { ++ if (bitString.length == 0) { ++ return new BitArray(bitString); ++ } ++ ++ // find index of last 1 bit. -1 if there aren't any ++ int i; ++ for (i = bitString.length - 1; i >= 0; i--) { ++ if (bitString[i]) { ++ break; ++ } ++ } ++ int length = i + 1; ++ ++ // if length changed, copy to new appropriately-sized array ++ if (length != bitString.length) { ++ boolean[] newBitString = new boolean[length]; ++ System.arraycopy(bitString, 0, newBitString, 0, length); ++ bitString = newBitString; ++ } ++ ++ return new BitArray(bitString); ++ } ++ ++ /** ++ * Converts bit string to a BitArray, stripping off trailing 0 bits. ++ * We assume that the bit string is not null. ++ */ ++ private static BitArray toBitArray(byte[] bitString) { ++ // compute length in bits of bit string ++ int length, i; ++ int maxIndex = 0; ++ ++ if (bitString.length == 0) { ++ return new BitArray(0, bitString); ++ } ++ ++ // find the index of the last byte with a 1 bit ++ for (i = 0; i < bitString.length; i++) { ++ if (bitString[i] != 0) { ++ maxIndex = i; ++ } ++ } ++ byte lastByte = bitString[maxIndex]; ++ length = (maxIndex + 1) * 8; // maximum, might reduce in next step ++ ++ // now find the last 1 bit in this last byte ++ for (i = 1; i <= 0x80; i <<= 1) { ++ if ((lastByte & i) == 0) { ++ length--; ++ } else { ++ break; ++ } ++ } ++ return new BitArray(length, bitString); ++ } ++ ++ /** ++ * Marshals a DER bit string on the output stream. ++ * The bit strings need not be byte-aligned. ++ * ++ * @param bits the bit string, MSB first ++ */ ++ public void putUnalignedBitString(BitArray ba) throws IOException { ++ byte[] bits = ba.toByteArray(); ++ ++ write(DerValue.tag_BitString); ++ putLength(bits.length + 1); ++ write(bits.length * 8 - ba.length()); // excess bits in last octet ++ write(bits); ++ } ++ ++ /** ++ * Marshals a DER bit string on the output stream. ++ * All trailing 0 bits will be stripped off in accordance with DER ++ * encoding. ++ * ++ * @param bits the bit string, MSB first ++ */ ++ public void putUnalignedBitString(byte[] bitString) throws IOException { ++ putUnalignedBitString(toBitArray(bitString)); ++ } ++ ++ /** ++ * Marshals a DER bit string on the output stream. ++ * All trailing 0 bits will be stripped off in accordance with DER ++ * encoding. ++ * ++ * @param bits the bit string as an array of booleans. ++ */ ++ public void putUnalignedBitString(boolean[] bitString) throws IOException { ++ putUnalignedBitString(toBitArray(bitString)); ++ } ++ ++ /** ++ * DER-encodes an ASN.1 OCTET STRING value on the output stream. ++ * ++ * @param octets the octet string ++ */ ++ public void putOctetString(byte[] octets) throws IOException { ++ write(DerValue.tag_OctetString, octets); ++ } ++ ++ /** ++ * Marshals a DER "null" value on the output stream. These are ++ * often used to indicate optional values which have been omitted. ++ */ ++ public void putNull() throws IOException { ++ write(DerValue.tag_Null); ++ putLength(0); ++ } ++ ++ /** ++ * Marshals an object identifier (OID) on the output stream. ++ * Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct. ++ */ ++ public void putOID(ObjectIdentifier oid) throws IOException { ++ oid.encode(this); ++ } ++ ++ /** ++ * Marshals a sequence on the output stream. This supports both ++ * the ASN.1 "SEQUENCE" (zero to N values) and "SEQUENCE OF" ++ * (one to N values) constructs. ++ */ ++ public void putSequence(DerValue[] seq) throws IOException { ++ DerOutputStream bytes = new DerOutputStream(); ++ int i; ++ ++ for (i = 0; i < seq.length; i++) ++ seq[i].encode(bytes); ++ ++ write(DerValue.tag_Sequence, bytes); ++ } ++ ++ /** ++ * Marshals the contents of a set on the output stream without ++ * ordering the elements. Ok for BER encoding, but not for DER ++ * encoding. ++ * ++ * For DER encoding, use orderedPutSet() or orderedPutSetOf(). ++ */ ++ public void putSet(DerValue[] set) throws IOException { ++ DerOutputStream bytes = new DerOutputStream(); ++ int i; ++ ++ for (i = 0; i < set.length; i++) ++ set[i].encode(bytes); ++ ++ write(DerValue.tag_Set, bytes); ++ } ++ ++ /** ++ * NSCP : ++ * Like putOrderSetOf, except not sorted. ++ * This may defy DER encoding but is needed for compatibility ++ * with communicator. ++ */ ++ public void putSet(byte tag, DerEncoder[] set) throws IOException { ++ putOrderedSet(tag, set, null); ++ } ++ ++ /** ++ * Marshals the contents of a set on the output stream. Sets ++ * are semantically unordered, but DER requires that encodings of ++ * set elements be sorted into ascending lexicographical order ++ * before being output. Hence sets with the same tags and ++ * elements have the same DER encoding. ++ * ++ * This method supports the ASN.1 "SET OF" construct, but not ++ * "SET", which uses a different order. ++ */ ++ public void putOrderedSetOf(byte tag, DerEncoder[] set) throws IOException { ++ putOrderedSet(tag, set, lexOrder); ++ } ++ ++ /** ++ * Marshals the contents of a set on the output stream. Sets ++ * are semantically unordered, but DER requires that encodings of ++ * set elements be sorted into ascending tag order ++ * before being output. Hence sets with the same tags and ++ * elements have the same DER encoding. ++ * ++ * This method supports the ASN.1 "SET" construct, but not ++ * "SET OF", which uses a different order. ++ */ ++ public void putOrderedSet(byte tag, DerEncoder[] set) throws IOException { ++ putOrderedSet(tag, set, tagOrder); ++ } ++ ++ /** ++ * Lexicographical order comparison on byte arrays, for ordering ++ * elements of a SET OF objects in DER encoding. ++ */ ++ private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder(); ++ ++ /** ++ * Tag order comparison on byte arrays, for ordering elements of ++ * SET objects in DER encoding. ++ */ ++ private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder(); ++ ++ /** ++ * Marshals a the contents of a set on the output stream with the ++ * encodings of its sorted in increasing order. ++ * ++ * @param order the order to use when sorting encodings of components. ++ */ ++ private void putOrderedSet(byte tag, DerEncoder[] set, ++ Comparator order) throws IOException { ++ DerOutputStream[] streams = new DerOutputStream[set.length]; ++ ++ for (int i = 0; i < set.length; i++) { ++ streams[i] = new DerOutputStream(); ++ set[i].derEncode(streams[i]); ++ } ++ ++ // order the element encodings ++ byte[][] bufs = new byte[streams.length][]; ++ for (int i = 0; i < streams.length; i++) { ++ bufs[i] = streams[i].toByteArray(); ++ } ++ if (order != null) { ++ Arrays.sort(bufs, order); ++ } ++ ++ DerOutputStream bytes = new DerOutputStream(); ++ for (int i = 0; i < streams.length; i++) { ++ bytes.write(bufs[i]); ++ } ++ write(tag, bytes); ++ ++ } ++ ++ /** ++ * Converts string to printable and writes to der output stream. ++ */ ++ public void putPrintableString(String s) throws IOException { ++ putStringType(DerValue.tag_PrintableString, s); ++ } ++ ++ public void putVisibleString(String s) throws IOException { ++ putStringType(DerValue.tag_VisibleString, s); ++ } ++ ++ /** ++ * Marshals a string which is consists of BMP (unicode) characters ++ */ ++ public void putBMPString(String s) throws IOException { ++ putStringType(DerValue.tag_BMPString, s); ++ } ++ ++ public void putGeneralString(String s) throws IOException { ++ putStringType(DerValue.tag_GeneralString, s); ++ } ++ ++ // /* ++ // * T61 is an 8 bit extension to ASCII, escapes e.g. to Japanese ++ // */ ++ // void putT61String(String s) throws IOException ++ // { ++ // // XXX IMPLEMENT ME ++ // ++ // throw new IOException("DerOutputStream.putT61String() NYI"); ++ // } ++ ++ // /* ++ // * Universal String. ++ // */ ++ // void putUniversalString(String s) throws IOException ++ // { ++ // // XXX IMPLEMENT ME ++ // ++ // throw new IOException("DerOutputStream.putUniversalString() NYI"); ++ // } ++ ++ /** ++ * Marshals a string which is consists of IA5(ASCII) characters ++ */ ++ public void putIA5String(String s) throws IOException { ++ putStringType(DerValue.tag_IA5String, s); ++ } ++ ++ public void putUTF8String(String s) throws IOException { ++ putStringType(DerValue.tag_UTF8String, s); ++ } ++ ++ public void putStringType(byte tag, String s) throws IOException { ++ try { ++ CharsetEncoder encoder = ASN1CharStrConvMap.getDefault().getEncoder(tag); ++ if (encoder == null) ++ throw new IOException("No encoder for tag"); ++ ++ CharBuffer charBuffer = CharBuffer.wrap(s.toCharArray()); ++ ByteBuffer byteBuffer = encoder.encode(charBuffer); ++ ++ write(tag); ++ putLength(byteBuffer.limit()); ++ write(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.limit()); ++ ++ } catch (CharacterCodingException e) { ++ throw new IOException("Not a valid string type " + tag, e); ++ } ++ } ++ ++ private void put2DateBytes(byte[] buffer, int value, int offset) { ++ int upper = value / 10; ++ int lower = value % 10; ++ buffer[offset] = (byte) ((byte) upper + (byte) '0'); ++ buffer[offset + 1] = (byte) ((byte) lower + (byte) '0'); ++ } ++ ++ private static Calendar GMTGregorianCalendar = null; ++ ++ private Calendar getGMTGregorianCalendar() { ++ if (GMTGregorianCalendar == null) { ++ TimeZone tz = TimeZone.getTimeZone("GMT"); ++ GMTGregorianCalendar = new GregorianCalendar(tz); ++ } ++ return (Calendar) GMTGregorianCalendar.clone(); ++ } ++ ++ public byte[] getDateBytes(Date d, boolean UTC) { ++ ++ byte[] datebytes; ++ ++ if (UTC) { ++ datebytes = new byte[13]; ++ } else { // generalized time has 4 digits for yr ++ datebytes = new byte[15]; ++ } ++ ++ Calendar cal = getGMTGregorianCalendar(); ++ cal.setTime(d); ++ ++ int i = 0; ++ if (!UTC) { ++ put2DateBytes(datebytes, cal.get(Calendar.YEAR) / 100, i); ++ i += 2; ++ } ++ put2DateBytes(datebytes, cal.get(Calendar.YEAR) % 100, i); ++ // Calendar's MONTH is zero-based ++ i += 2; ++ put2DateBytes(datebytes, cal.get(Calendar.MONTH) + 1, i); ++ i += 2; ++ put2DateBytes(datebytes, cal.get(Calendar.DAY_OF_MONTH), i); ++ i += 2; ++ put2DateBytes(datebytes, cal.get(Calendar.HOUR_OF_DAY), i); ++ i += 2; ++ put2DateBytes(datebytes, cal.get(Calendar.MINUTE), i); ++ i += 2; ++ put2DateBytes(datebytes, cal.get(Calendar.SECOND), i); ++ i += 2; ++ // datebytes[i] = 'Z'; ++ datebytes[i] = (byte) 'Z'; ++ ++ return datebytes; ++ } ++ ++ /** ++ * Marshals a DER UTC time/date value. ++ * ++ *

++ * YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time and with seconds (even if seconds=0) as per IETF-PKIX ++ * partI. ++ */ ++ public void putUTCTime(Date d) throws IOException { ++ /* ++ * Format the date. ++ */ ++ ++ // This was the old code. Way too slow to be usable (stevep) ++ ++ // String pattern = "yyMMddHHmmss'Z'"; ++ // SimpleDateFormat sdf = new SimpleDateFormat(pattern); ++ // TimeZone tz = TimeZone.getTimeZone("GMT"); ++ // sdf.setTimeZone(tz); ++ // byte[] utc = (sdf.format(d)).getBytes(); ++ ++ byte[] datebytes = getDateBytes(d, true); // UTC = true ++ ++ /* ++ * Write the formatted date. ++ */ ++ write(DerValue.tag_UtcTime); ++ putLength(datebytes.length); ++ write(datebytes); ++ } ++ ++ /** ++ * Marshals a DER Generalized Time/date value. ++ * ++ *

++ * YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time and with seconds (even if seconds=0) as per ++ * IETF-PKIX partI. ++ */ ++ public void putGeneralizedTime(Date d) throws IOException { ++ /* ++ * Format the date. ++ */ ++ TimeZone tz = TimeZone.getTimeZone("GMT"); ++ ++ // This is way too slow to be usable (stevep) ++ String pattern = "yyyyMMddHHmmss'Z'"; ++ SimpleDateFormat sdf = new SimpleDateFormat(pattern); ++ sdf.setTimeZone(tz); ++ byte[] gt = (sdf.format(d)).getBytes(); ++ ++ /* ++ * Write the formatted date. ++ */ ++ write(DerValue.tag_GeneralizedTime); ++ putLength(gt.length); ++ write(gt); ++ } ++ ++ /** ++ * Put the encoding of the length in the stream. ++ * ++ * @param len the length of the attribute. ++ * @exception IOException on writing errors. ++ */ ++ public void putLength(int len) throws IOException { ++ if (len < 128) { ++ write((byte) len); ++ ++ } else if (len < (1 << 8)) { ++ write((byte) 0x081); ++ write((byte) len); ++ ++ } else if (len < (1 << 16)) { ++ write((byte) 0x082); ++ write((byte) (len >> 8)); ++ write((byte) len); ++ ++ } else if (len < (1 << 24)) { ++ write((byte) 0x083); ++ write((byte) (len >> 16)); ++ write((byte) (len >> 8)); ++ write((byte) len); ++ ++ } else { ++ write((byte) 0x084); ++ write((byte) (len >> 24)); ++ write((byte) (len >> 16)); ++ write((byte) (len >> 8)); ++ write((byte) len); ++ } ++ } ++ ++ /** ++ * Put the tag of the attribute in the stream. ++ * ++ * @param class the tag class type, one of UNIVERSAL, CONTEXT, ++ * APPLICATION or PRIVATE ++ * @param form if true, the value is constructed, otherwise it is ++ * primitive. ++ * @param val the tag value ++ */ ++ public void putTag(byte tagClass, boolean form, byte val) { ++ byte tag = (byte) (tagClass | val); ++ if (form) { ++ tag |= (byte) 0x20; ++ } ++ write(tag); ++ } ++ ++ /** ++ * Write the current contents of this DerOutputStream to an OutputStream. ++ * ++ * @exception IOException on output error. ++ */ ++ public void derEncode(OutputStream out) throws IOException { ++ out.write(toByteArray()); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/DerValue.java b/org/mozilla/jss/netscape/security/util/DerValue.java +new file mode 100644 +index 00000000..8419690c +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/DerValue.java +@@ -0,0 +1,748 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.ByteArrayInputStream; ++import java.io.DataInputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.CharacterCodingException; ++import java.nio.charset.CharsetDecoder; ++import java.util.Arrays; ++ ++import org.mozilla.jss.netscape.security.x509.AVAValueConverter; ++import org.mozilla.jss.netscape.security.x509.GenericValueConverter; ++ ++/** ++ * Represents a single DER-encoded value. DER encoding rules are a subset ++ * of the "Basic" Encoding Rules (BER), but they only support a single way ++ * ("Definite" encoding) to encode any given value. ++ * ++ *

++ * All DER-encoded data are triples {type, length, data}. This class represents such tagged values as they have ++ * been read (or constructed), and provides structured access to the encoded data. ++ * ++ *

++ * At this time, this class supports only a subset of the types of DER data encodings which are defined. That subset is ++ * sufficient for parsing most X.509 certificates, and working with selected additional formats (such as PKCS #10 ++ * certificate requests, and some kinds of PKCS #7 data). ++ * ++ * @version 1.43 ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class DerValue { ++ /** The tag class types */ ++ public static final byte TAG_UNIVERSAL = (byte) 0x000; ++ public static final byte TAG_APPLICATION = (byte) 0x040; ++ public static final byte TAG_CONTEXT = (byte) 0x080; ++ public static final byte TAG_PRIVATE = (byte) 0x0c0; ++ ++ /** The DER tag of the value; one of the tag_ constants. */ ++ public byte tag; ++ ++ protected DerInputBuffer buffer; ++ ++ /** ++ * The DER-encoded data of the value. ++ */ ++ public DerInputStream data; ++ ++ private int length; ++ ++ /* ++ * The type starts at the first byte of the encoding, and ++ * is one of these tag_* values. That may be all the type ++ * data that is needed. ++ */ ++ ++ /* ++ * These tags are the "universal" tags ... they mean the same ++ * in all contexts. (Mask with 0x1f -- five bits.) ++ */ ++ ++ /** Tag value indicating an ASN.1 "BOOLEAN" value. */ ++ public final static byte tag_Boolean = 0x01; ++ ++ /** Tag value indicating an ASN.1 "INTEGER" value. */ ++ public final static byte tag_Integer = 0x02; ++ ++ /** Tag value indicating an ASN.1 "BIT STRING" value. */ ++ public final static byte tag_BitString = 0x03; ++ ++ /** Tag value indicating an ASN.1 "OCTET STRING" value. */ ++ public final static byte tag_OctetString = 0x04; ++ ++ /** Tag value indicating an ASN.1 "NULL" value. */ ++ public final static byte tag_Null = 0x05; ++ ++ /** Tag value indicating an ASN.1 "OBJECT IDENTIFIER" value. */ ++ public final static byte tag_ObjectId = 0x06; ++ ++ /** Tag value including an ASN.1 "ENUMERATED" value */ ++ public final static byte tag_Enumerated = 0x0A; ++ ++ /** Tag value including a "printable" string */ ++ public final static byte tag_PrintableString = 0x13; ++ ++ public final static byte tag_VisibleString = 0x1A; ++ ++ /** Tag value including a "teletype" string */ ++ public final static byte tag_T61String = 0x14; ++ ++ /** Tag value including an ASCII string */ ++ public final static byte tag_IA5String = 0x16; ++ ++ /** Tag value indicating an ASN.1 "UTCTime" value. */ ++ public final static byte tag_UtcTime = 0x17; ++ ++ /** Tag value indicating an ASN.1 "GeneralizedTime" value. */ ++ public final static byte tag_GeneralizedTime = 0x18; ++ ++ /** Tag value indicating an ASN.1 "GeneralString" value. */ ++ public final static byte tag_GeneralString = 0x1B; ++ ++ /** Tag value indicating an ASN.1 "BMPString" value. */ ++ public final static byte tag_BMPString = 0x1E; ++ ++ /** Tag value indicating an ASN.1 "UniversalString" value. */ ++ public final static byte tag_UniversalString = 0x1C; ++ ++ /** Tag value indicating an ASN.1 "UTF8String" value. (since 1998) */ ++ public final static byte tag_UTF8String = 0x0C; ++ ++ public final static byte[] tags_DirectoryString = ++ { tag_T61String ++ , tag_PrintableString ++ , tag_UniversalString ++ , tag_UTF8String ++ , tag_BMPString }; ++ ++ // CONSTRUCTED seq/set ++ ++ /** ++ * Tag value indicating an ASN.1 ++ * "SEQUENCE" (zero to N elements, order is significant). ++ */ ++ public final static byte tag_Sequence = 0x30; ++ ++ /** ++ * Tag value indicating an ASN.1 ++ * "SEQUENCE OF" (one to N elements, order is significant). ++ */ ++ public final static byte tag_SequenceOf = 0x30; ++ ++ /** ++ * Tag value indicating an ASN.1 ++ * "SET" (zero to N members, order does not matter). ++ */ ++ public final static byte tag_Set = 0x31; ++ ++ /** ++ * Tag value indicating an ASN.1 ++ * "SET OF" (one to N members, order does not matter). ++ */ ++ public final static byte tag_SetOf = 0x31; ++ ++ /* ++ * These values are the high order bits for the other kinds of tags. ++ */ ++ boolean isUniversal() { ++ return ((tag & 0x0c0) == 0x000); ++ } ++ ++ boolean isApplication() { ++ return ((tag & 0x0c0) == 0x040); ++ } ++ ++ /** ++ * Returns true iff the CONTEXT SPECIFIC bit is set in the type tag. ++ * This is associated with the ASN.1 "DEFINED BY" syntax. ++ */ ++ public boolean isContextSpecific() { ++ return ((tag & 0x0c0) == 0x080); ++ } ++ ++ /** ++ * Returns true iff the CONTEXT SPECIFIC TAG matches the passed tag. ++ */ ++ public boolean isContextSpecific(byte cntxtTag) { ++ if (!isContextSpecific()) { ++ return false; ++ } ++ return ((tag & 0x01f) == cntxtTag); ++ } ++ ++ boolean isPrivate() { ++ return ((tag & 0x0c0) == 0x0c0); ++ } ++ ++ /** Returns true iff the CONSTRUCTED bit is set in the type tag. */ ++ public boolean isConstructed() { ++ return ((tag & 0x020) == 0x020); ++ } ++ ++ /** ++ * Creates a DER value from a string ++ * using a generic way of determining the proper tag for the string. ++ * Assumes the string is a Generic attribute value and uses ++ * the converter for generic string values to convert to the Der Value. ++ */ ++ public DerValue(String value) ++ throws IOException { ++ AVAValueConverter genericValue = new GenericValueConverter(); ++ DerValue val; ++ ++ val = genericValue.getValue(value); ++ tag = val.tag; ++ buffer = val.buffer; ++ length = val.length; ++ data = val.data; ++ data.mark(Integer.MAX_VALUE); ++ } ++ ++ /** ++ * Creates a DerValue from a tag and some DER-encoded data. ++ * ++ * @param tag the DER type tag ++ * @param data the DER-encoded data ++ */ ++ public DerValue(byte tag, byte[] data) { ++ this.tag = tag; ++ buffer = new DerInputBuffer(data.clone()); ++ length = data.length; ++ this.data = new DerInputStream(buffer); ++ this.data.mark(Integer.MAX_VALUE); ++ } ++ ++ /** ++ * Creates a DerValue from a tag and some DER-encoded data. ++ * ++ * @param tag the DER type tag ++ * @param data the DER-encoded data ++ * @param offset offset of the data ++ * @param length length of the data ++ */ ++ public DerValue(byte tag, byte[] data, int offset, int length) { ++ this(tag, Arrays.copyOfRange(data, offset, offset + length)); ++ } ++ ++ /* ++ * package private ++ */ ++ DerValue(DerInputBuffer in) throws IOException { ++ // NOTE: This must handle the special value used ++ // to terminate BER indefinite encodings (tag and ++ // length are both zero) ++ ++ // XXX must also parse BER-encoded constructed ++ // values such as sequences, sets... ++ ++ tag = (byte) in.read(); ++ length = DerInputStream.getLength(in); ++ ++ buffer = in.dup(); ++ buffer.truncate(length); ++ data = new DerInputStream(buffer); ++ ++ in.skip(length); ++ } ++ ++ /** ++ * Get an ASN.1/DER encoded datum from a buffer. The ++ * entire buffer must hold exactly one datum, including ++ * its tag and length. ++ * ++ * @param buf buffer holding a single DER-encoded datum. ++ */ ++ public DerValue(byte[] buf) throws IOException { ++ init(true, new ByteArrayInputStream(buf)); ++ } ++ ++ /** ++ * Get an ASN.1/DER encoded datum from part of a buffer. ++ * That part of the buffer must hold exactly one datum, including ++ * its tag and length. ++ * ++ * @param buf the buffer ++ * @param offset start point of the single DER-encoded dataum ++ * @param length how many bytes are in the encoded datum ++ */ ++ public DerValue(byte[] buf, int offset, int len) throws IOException { ++ init(true, new ByteArrayInputStream(buf, offset, len)); ++ } ++ ++ /** ++ * Get an ASN1/DER encoded datum from an input stream. The ++ * stream may have additional data following the encoded datum. ++ * ++ * @param in the input stream holding a single DER datum, ++ * which may be followed by additional data ++ */ ++ public DerValue(InputStream in) throws IOException { ++ init(false, in); ++ } ++ ++ /* ++ * helper routine ++ */ ++ private void init(boolean fullyBuffered, InputStream in) ++ throws IOException { ++ byte[] bytes; ++ ++ tag = (byte) in.read(); ++ length = DerInputStream.getLength(in); ++ ++ /* ++ if (length == 0) ++ return; ++ */ ++ ++ if (fullyBuffered && in.available() != length) ++ throw new IOException("extra DER value data (constructor)"); ++ ++ bytes = new byte[length]; ++ ++ // n.b. readFully not needed in normal fullyBuffered case ++ DataInputStream dis = new DataInputStream(in); ++ ++ dis.readFully(bytes); ++ buffer = new DerInputBuffer(bytes); ++ data = new DerInputStream(buffer); ++ } ++ ++ /** ++ * Encode an ASN1/DER encoded datum onto a DER output stream. ++ */ ++ public void encode(DerOutputStream out) ++ throws IOException { ++ out.write(tag); ++ out.putLength(length); ++ buffer.dump(out, length); ++ ++ } ++ ++ /** ++ * Returns an ASN.1 BOOLEAN ++ * ++ * @return the boolean held in this DER value ++ */ ++ public boolean getBoolean() throws IOException { ++ if (tag != tag_Boolean) { ++ throw new IOException("DerValue.getBoolean, not a BOOLEAN " + tag); ++ } ++ if (length != 1) { ++ throw new IOException("DerValue.getBoolean, invalid length " + length); ++ } ++ if (buffer.read() != 0) { ++ return true; ++ } ++ return false; ++ } ++ ++ /** ++ * Returns an ASN.1 OBJECT IDENTIFIER. ++ * ++ * @return the OID held in this DER value ++ */ ++ public ObjectIdentifier getOID() throws IOException { ++ if (tag != tag_ObjectId) ++ throw new IOException("DerValue.getOID, not an OID " + tag); ++ return new ObjectIdentifier(buffer); ++ } ++ ++ /** ++ * Returns an ASN.1 OCTET STRING ++ * ++ * @return the octet string held in this DER value ++ */ ++ public byte[] getOctetString() throws IOException { ++ if (tag != tag_OctetString) ++ throw new IOException( ++ "DerValue.getOctetString, not an Octet String: " + tag); ++ ++ byte[] bytes = new byte[length]; ++ ++ if (buffer.read(bytes) != length) ++ throw new IOException("short read on DerValue buffer"); ++ return bytes; ++ } ++ ++ /** ++ * Returns an ASN.1 unsigned integer value of enumerated value. ++ * ++ * @return the (unsigned) integer held in this DER value ++ */ ++ public int getEnumerated() ++ throws IOException { ++ if (tag != tag_Enumerated) ++ throw new IOException("DerValue.getEnumerated, not an ENUMERATED " + tag); ++ if (length == 0) ++ return 0; ++ if (length > 4 || length < 1) ++ throw new IOException("DerValue.getEnumerated, invalid length " + length + "(must be between 1 and 4)"); ++ ++ int value = 0; ++ int nextbyte = buffer.read(); ++ if (nextbyte == -1) ++ throw new IOException("short read on DerValue buffer"); ++ // perform sign extension ++ value = (byte) nextbyte; ++ ++ for (int i = length - 1; i > 0; --i) { ++ nextbyte = buffer.read(); ++ if (nextbyte == -1) ++ throw new IOException("short read on DerValue buffer"); ++ value = 256 * value + nextbyte; ++ } ++ return value; ++ } ++ ++ /** ++ * Returns an ASN.1 unsigned INTEGER value. ++ * ++ * @return the (unsigned) integer held in this DER value ++ */ ++ public BigInt getInteger() throws IOException { ++ if (tag != tag_Integer) ++ throw new IOException("DerValue.getInteger, not an int " + tag); ++ return buffer.getUnsigned(data.available()); ++ } ++ ++ /** ++ * Returns an ASN.1 unsigned INTEGER value, the parameter determining ++ * if the tag is implicit. ++ * ++ * @param tagImplicit if true, ignores the tag value as it is ++ * assumed implicit. ++ * @return the (unsigned) integer held in this DER value ++ */ ++ public BigInt getInteger(boolean tagImplicit) throws IOException { ++ if (!tagImplicit) { ++ if (tag != tag_Integer) { ++ throw new IOException("DerValue.getInteger, not an int " ++ + tag); ++ } ++ } ++ return buffer.getUnsigned(data.available()); ++ } ++ ++ /** ++ * Returns an ASN.1 BIT STRING value. The bit string must be byte-aligned. ++ * ++ * @return the bit string held in this value ++ */ ++ public byte[] getBitString() throws IOException { ++ if (tag != tag_BitString) ++ throw new IOException( ++ "DerValue.getBitString, not a bit string " + tag); ++ ++ return buffer.getBitString(); ++ } ++ ++ /** ++ * Returns an ASN.1 BIT STRING value that need not be byte-aligned. ++ * ++ * @return a BitArray representing the bit string held in this value ++ */ ++ public BitArray getUnalignedBitString() throws IOException { ++ if (tag != tag_BitString) ++ throw new IOException( ++ "DerValue.getBitString, not a bit string " + tag); ++ ++ return buffer.getUnalignedBitString(); ++ } ++ ++ /** ++ * Returns the name component as a Java string, regardless of its ++ * encoding restrictions (ASCII, T61, Printable, etc). ++ */ ++ public String getAsString() throws IOException { ++ AVAValueConverter genericValue = new GenericValueConverter(); ++ return genericValue.getAsString(this); ++ } ++ ++ /** ++ * Returns an ASN.1 BIT STRING value, with the tag assumed implicit ++ * based on the parameter. The bit string must be byte-aligned. ++ * ++ * @param tagImplicit if true, the tag is assumed implicit. ++ * @return the bit string held in this value ++ */ ++ public byte[] getBitString(boolean tagImplicit) throws IOException { ++ if (!tagImplicit) { ++ if (tag != tag_BitString) ++ throw new IOException("DerValue.getBitString, not a bit string " ++ + tag); ++ } ++ return buffer.getBitString(); ++ } ++ ++ /** ++ * Returns an ASN.1 BIT STRING value, with the tag assumed implicit ++ * based on the parameter. The bit string need not be byte-aligned. ++ * ++ * @param tagImplicit if true, the tag is assumed implicit. ++ * @return the bit string held in this value ++ */ ++ public BitArray getUnalignedBitString(boolean tagImplicit) ++ throws IOException { ++ if (!tagImplicit) { ++ if (tag != tag_BitString) ++ throw new IOException("DerValue.getBitString, not a bit string " ++ + tag); ++ } ++ return buffer.getUnalignedBitString(); ++ } ++ ++ /** ++ * Returns an ASN.1 STRING value ++ * ++ * @return the printable string held in this value ++ */ ++ public String getPrintableString() ++ throws IOException { ++ if (tag != tag_PrintableString) ++ throw new IOException( ++ "DerValue.getPrintableString, not a string " + tag); ++ ++ return getASN1CharString(); ++ } ++ ++ public String getDirectoryString() throws IOException { ++ boolean tagValid = false; ++ for (int i = 0; i < tags_DirectoryString.length; i++) { ++ if (tag == tags_DirectoryString[i]) { ++ tagValid = true; ++ break; ++ } ++ } ++ if (!tagValid) ++ throw new IOException( ++ "DerValue.getDirectoryString: invalid tag: " + tag); ++ ++ return getASN1CharString(); ++ } ++ ++ /* ++ * @eturns a string if the DerValue is a ASN.1 character string type and ++ * if there is a decoder for the type. Returns null otherwise. ++ */ ++ public String getASN1CharString() throws IOException { ++ try { ++ CharsetDecoder decoder = ASN1CharStrConvMap.getDefault().getDecoder(tag); ++ if (decoder == null) ++ return null; ++ ++ ByteBuffer byteBuffer = ByteBuffer.allocate(length); ++ ++ data.reset(); ++ data.getBytes(byteBuffer.array()); ++ ++ CharBuffer charBuffer = decoder.decode(byteBuffer); ++ return charBuffer.toString(); ++ ++ } catch (CharacterCodingException e) { ++ throw new IOException("Misformed DER value", e); ++ } ++ } ++ ++ /** ++ * Returns an ASN.1 T61 (Teletype) STRING value ++ * ++ * @return the teletype string held in this value ++ */ ++ public String getT61String() throws IOException { ++ if (tag != tag_T61String) ++ throw new IOException( ++ "DerValue.getT61String, not T61 " + tag); ++ ++ return getASN1CharString(); ++ } ++ ++ /** ++ * Returns an ASN.1 IA5 (ASCII) STRING value ++ * ++ * @return the ASCII string held in this value ++ */ ++ public String getIA5String() throws IOException { ++ if (tag != tag_IA5String) ++ throw new IOException( ++ "DerValue.getIA5String, not IA5 " + tag); ++ ++ return getASN1CharString(); ++ } ++ ++ public String getBMPString() ++ throws IOException { ++ if (tag != tag_BMPString) ++ throw new IOException( ++ "DerValue.getBMPString, not BMP " + tag); ++ ++ return getASN1CharString(); ++ } ++ ++ public String getUniversalString() ++ throws IOException { ++ if (tag != tag_UniversalString) ++ throw new IOException( ++ "DerValue.getUniversalString, not UniversalString " + tag); ++ ++ return getASN1CharString(); ++ } ++ ++ public String getUTF8String() ++ throws IOException { ++ if (tag != tag_UTF8String) ++ throw new IOException( ++ "DerValue.getUTF8String, not UTF8String " + tag); ++ ++ return getASN1CharString(); ++ } ++ ++ /** ++ * Returns true iff the other object is a DER value which ++ * is bitwise equal to this one. ++ * ++ * @param other the object being compared with this one ++ */ ++ public boolean equals(Object other) { ++ if (other instanceof DerValue) ++ return equals((DerValue) other); ++ else ++ return false; ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + ((buffer == null) ? 0 : buffer.hashCode()); ++ result = prime * result + ((data == null) ? 0 : data.hashCode()); ++ result = prime * result + length; ++ result = prime * result + tag; ++ return result; ++ } ++ ++ /** ++ * Bitwise equality comparison. DER encoded values have a single ++ * encoding, so that bitwise equality of the encoded values is an ++ * efficient way to establish equivalence of the unencoded values. ++ * ++ * @param other the object being compared with this one ++ */ ++ public boolean equals(DerValue other) { ++ data.reset(); ++ other.data.reset(); ++ if (this == other) ++ return true; ++ else if (tag != other.tag) { ++ return false; ++ } else { ++ return buffer.equals(other.buffer); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the value. ++ * ++ * @return printable representation of the value ++ */ ++ public String toString() { ++ try { ++ String s = getAsString(); ++ if (s != null) ++ return s; ++ if (tag == tag_Null) ++ return "[DerValue, null]"; ++ if (tag == tag_ObjectId) ++ return "OID." + getOID(); ++ ++ // integers ++ else ++ return "[DerValue, tag = " + tag ++ + ", length = " + length + "]"; ++ } catch (IOException e) { ++ throw new IllegalArgumentException("misformatted DER value"); ++ } ++ } ++ ++ /** ++ * Returns a DER-encoded value, such that if it's passed to the ++ * DerValue constructor, a value equivalent to "this" is returned. ++ * ++ * @return DER-encoded value, including tag and length. ++ */ ++ public byte[] toByteArray() throws IOException { ++ DerOutputStream out = new DerOutputStream(); ++ ++ encode(out); ++ data.reset(); ++ return out.toByteArray(); ++ } ++ ++ /** ++ * For "set" and "sequence" types, this function may be used ++ * to return a DER stream of the members of the set or sequence. ++ * This operation is not supported for primitive types such as ++ * integers or bit strings. ++ */ ++ public DerInputStream toDerInputStream() throws IOException { ++ if (tag == tag_Sequence || tag == tag_Set) ++ return new DerInputStream(buffer); ++ throw new IOException("toDerInputStream rejects tag type " + tag); ++ } ++ ++ /** ++ * Get the length of the encoded value. ++ */ ++ public int length() { ++ return length; ++ } ++ ++ /** ++ * Create the tag of the attribute. ++ * ++ * @param class the tag class type, one of UNIVERSAL, CONTEXT, ++ * APPLICATION or PRIVATE ++ * @param form if true, the value is constructed, otherwise it ++ * is primitive. ++ * @param val the tag value ++ */ ++ public static byte createTag(byte tagClass, boolean form, byte val) { ++ byte tag = (byte) (tagClass | val); ++ if (form) { ++ tag |= (byte) 0x20; ++ } ++ return (tag); ++ } ++ ++ /** ++ * Set the tag of the attribute. Commonly used to reset the ++ * tag value used for IMPLICIT encodings. ++ * ++ * @param tag the tag value ++ */ ++ public void resetTag(byte tag) { ++ this.tag = tag; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/ExtPrettyPrint.java b/org/mozilla/jss/netscape/security/util/ExtPrettyPrint.java +new file mode 100644 +index 00000000..83fe1996 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/ExtPrettyPrint.java +@@ -0,0 +1,1586 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.IOException; ++import java.math.BigInteger; ++import java.security.cert.CertificateException; ++import java.text.DateFormat; ++import java.util.Enumeration; ++import java.util.ResourceBundle; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.extensions.AccessDescription; ++import org.mozilla.jss.netscape.security.extensions.AuthInfoAccessExtension; ++import org.mozilla.jss.netscape.security.extensions.CertificateScopeEntry; ++import org.mozilla.jss.netscape.security.extensions.CertificateScopeOfUseExtension; ++import org.mozilla.jss.netscape.security.extensions.ExtendedKeyUsageExtension; ++import org.mozilla.jss.netscape.security.extensions.InhibitAnyPolicyExtension; ++import org.mozilla.jss.netscape.security.extensions.NSCertTypeExtension; ++import org.mozilla.jss.netscape.security.extensions.OCSPNoCheckExtension; ++import org.mozilla.jss.netscape.security.extensions.PresenceServerExtension; ++import org.mozilla.jss.netscape.security.extensions.SubjectInfoAccessExtension; ++import org.mozilla.jss.netscape.security.x509.Attribute; ++import org.mozilla.jss.netscape.security.x509.AuthorityKeyIdentifierExtension; ++import org.mozilla.jss.netscape.security.x509.BasicConstraintsExtension; ++import org.mozilla.jss.netscape.security.x509.CPSuri; ++import org.mozilla.jss.netscape.security.x509.CRLDistributionPoint; ++import org.mozilla.jss.netscape.security.x509.CRLDistributionPointsExtension; ++import org.mozilla.jss.netscape.security.x509.CRLDistributionPointsExtension.Reason; ++import org.mozilla.jss.netscape.security.x509.CRLNumberExtension; ++import org.mozilla.jss.netscape.security.x509.CRLReasonExtension; ++import org.mozilla.jss.netscape.security.x509.CertificateIssuerExtension; ++import org.mozilla.jss.netscape.security.x509.CertificatePoliciesExtension; ++import org.mozilla.jss.netscape.security.x509.CertificatePolicyInfo; ++import org.mozilla.jss.netscape.security.x509.CertificatePolicyMap; ++import org.mozilla.jss.netscape.security.x509.DeltaCRLIndicatorExtension; ++import org.mozilla.jss.netscape.security.x509.DisplayText; ++import org.mozilla.jss.netscape.security.x509.Extension; ++import org.mozilla.jss.netscape.security.x509.FreshestCRLExtension; ++import org.mozilla.jss.netscape.security.x509.GeneralName; ++import org.mozilla.jss.netscape.security.x509.GeneralNameInterface; ++import org.mozilla.jss.netscape.security.x509.GeneralNames; ++import org.mozilla.jss.netscape.security.x509.HoldInstructionExtension; ++import org.mozilla.jss.netscape.security.x509.InvalidityDateExtension; ++import org.mozilla.jss.netscape.security.x509.IssuerAlternativeNameExtension; ++import org.mozilla.jss.netscape.security.x509.IssuingDistributionPoint; ++import org.mozilla.jss.netscape.security.x509.IssuingDistributionPointExtension; ++import org.mozilla.jss.netscape.security.x509.KeyIdentifier; ++import org.mozilla.jss.netscape.security.x509.KeyUsageExtension; ++import org.mozilla.jss.netscape.security.x509.NSCCommentExtension; ++import org.mozilla.jss.netscape.security.x509.NameConstraintsExtension; ++import org.mozilla.jss.netscape.security.x509.NoticeReference; ++import org.mozilla.jss.netscape.security.x509.OIDMap; ++import org.mozilla.jss.netscape.security.x509.PolicyConstraintsExtension; ++import org.mozilla.jss.netscape.security.x509.PolicyMappingsExtension; ++import org.mozilla.jss.netscape.security.x509.PolicyQualifierInfo; ++import org.mozilla.jss.netscape.security.x509.PolicyQualifiers; ++import org.mozilla.jss.netscape.security.x509.PrivateKeyUsageExtension; ++import org.mozilla.jss.netscape.security.x509.Qualifier; ++import org.mozilla.jss.netscape.security.x509.RDN; ++import org.mozilla.jss.netscape.security.x509.SerialNumber; ++import org.mozilla.jss.netscape.security.x509.SubjectAlternativeNameExtension; ++import org.mozilla.jss.netscape.security.x509.SubjectDirAttributesExtension; ++import org.mozilla.jss.netscape.security.x509.SubjectKeyIdentifierExtension; ++import org.mozilla.jss.netscape.security.x509.UserNotice; ++ ++/** ++ * This class will display the certificate content in predefined ++ * format. ++ * ++ * @author Andrew Wnuk ++ * @version $Revision$, $Date$ ++ */ ++public class ExtPrettyPrint { ++ ++ /*========================================================== ++ * variables ++ *==========================================================*/ ++ private Extension mExt = null; ++ private ResourceBundle mResource = null; ++ private PrettyPrintFormat pp = null; ++ private int mIndentSize = 0; ++ ++ DateFormat dateFormater = null; ++ ++ /*========================================================== ++ * constructors ++ *==========================================================*/ ++ ++ public ExtPrettyPrint(Extension ext, int indentSize) { ++ mExt = ext; ++ mResource = ResourceBundle.getBundle(PrettyPrintResources.class.getName()); ++ mIndentSize = indentSize; ++ pp = new PrettyPrintFormat(":"); ++ } ++ ++ /*========================================================== ++ * public methods ++ *==========================================================*/ ++ ++ /** ++ * This method return string representation of the certificate ++ * in predefined format using specified client local. I18N Support. ++ * ++ * @param clientLocale Locale to be used for localization ++ * @return string representation of the certificate ++ */ ++ // public String toString(int indentSize) { ++ public String toString() { ++ ++ StringBuffer sb = new StringBuffer(); ++ ++ //check if the extension is known ++ if (mExt instanceof KeyUsageExtension) { ++ return getKeyUsage(); ++ } ++ if (mExt instanceof NSCertTypeExtension) { ++ return getCertType(); ++ } ++ if (mExt instanceof AuthorityKeyIdentifierExtension) { ++ return getAuthorityKeyIdentifier(); ++ } ++ if (mExt instanceof SubjectKeyIdentifierExtension) { ++ return getSubjectKeyIdentifier(); ++ } ++ if (mExt instanceof CRLReasonExtension) { ++ return getCRLReasonExtension(); ++ } ++ if (mExt instanceof BasicConstraintsExtension) { ++ return getBasicConstraintsExtension(); ++ } ++ if (mExt instanceof NSCCommentExtension) { ++ return getNSCCommentExtension(); ++ } ++ if (mExt instanceof NameConstraintsExtension) { ++ return getNameConstraintsExtension(); ++ } ++ if (mExt instanceof CRLNumberExtension) { ++ return getCRLNumberExtension(); ++ } ++ if (mExt instanceof DeltaCRLIndicatorExtension) { ++ return getDeltaCRLIndicatorExtension(); ++ } ++ if (mExt instanceof IssuerAlternativeNameExtension) { ++ return getIssuerAlternativeNameExtension(); ++ } ++ if (mExt instanceof SubjectAlternativeNameExtension) { ++ return getSubjectAlternativeNameExtension(); ++ } ++ if (mExt instanceof FreshestCRLExtension) { ++ return getFreshestCRLExtension(); ++ } ++ if (mExt instanceof CRLDistributionPointsExtension) { ++ return getCRLDistributionPointsExtension(); ++ } ++ if (mExt instanceof IssuingDistributionPointExtension) { ++ return getIssuingDistributionPointExtension(); ++ } ++ if (mExt instanceof ExtendedKeyUsageExtension) { ++ return getExtendedKeyUsageExtension(); ++ } ++ if (mExt instanceof AuthInfoAccessExtension) { ++ return getAuthInfoAccessExtension(); ++ } ++ if (mExt instanceof SubjectInfoAccessExtension) { ++ return getSubjectInfoAccessExtension(); ++ } ++ if (mExt instanceof OCSPNoCheckExtension) { ++ return getOCSPNoCheckExtension(); ++ } ++ if (mExt instanceof PrivateKeyUsageExtension) { ++ return getPrivateKeyUsageExtension(); ++ } ++ if (mExt instanceof InvalidityDateExtension) { ++ return getInvalidityDateExtension(); ++ } ++ if (mExt instanceof CertificateIssuerExtension) { ++ return getCertificateIssuerExtension(); ++ } ++ if (mExt instanceof HoldInstructionExtension) { ++ return getHoldInstructionExtension(); ++ } ++ if (mExt instanceof PolicyConstraintsExtension) { ++ return getPolicyConstraintsExtension(); ++ } ++ if (mExt instanceof PolicyMappingsExtension) { ++ return getPolicyMappingsExtension(); ++ } ++ if (mExt instanceof SubjectDirAttributesExtension) { ++ return getSubjectDirAttributesExtension(); ++ } ++ if (mExt instanceof CertificateScopeOfUseExtension) { ++ return getCertificateScopeOfUseExtension(); ++ } ++ if (mExt instanceof PresenceServerExtension) { ++ return getPresenceServerExtension(); ++ } ++ ++ if (mExt instanceof InhibitAnyPolicyExtension) { ++ return getInhibitAnyPolicyExtension(); ++ } ++ ++ if (mExt instanceof CertificatePoliciesExtension) { ++ return getCertificatePoliciesExtension(); ++ } ++ ++ //unknown cert extension ++ String extName = OIDMap.getName(mExt.getExtensionId()); ++ ++ if (extName == null) ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER) + ++ mExt.getExtensionId().toString() + "\n"); ++ else ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER) + " " + extName + " - " + ++ mExt.getExtensionId().toString() + "\n"); ++ ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_VALUE) + "\n"); ++ sb.append(pp.toHexString(mExt.getExtensionValue(), mIndentSize + 8, 16)); ++ ++ return sb.toString(); ++ ++ } ++ ++ /*========================================================== ++ * Private methods ++ *==========================================================*/ ++ ++ private String getNSCCommentExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NSC_COMMENT) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + ((NSCCommentExtension) mExt).toPrint(mIndentSize) + "\n"); ++ return sb.toString(); ++ } ++ ++ private String getNameConstraintsExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NAME_CONSTRAINTS) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ sb.append(pp.indent(mIndentSize + 4) + ((NameConstraintsExtension) mExt).toPrint(mIndentSize + 4)); ++ ++ return sb.toString(); ++ } ++ ++ private String getOCSPNoCheckExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_OCSP_NOCHECK) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ return sb.toString(); ++ } ++ ++ private String getSubjectInfoAccessExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_SIA) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_ACCESS_DESC) + "\n"); ++ SubjectInfoAccessExtension aia = (SubjectInfoAccessExtension) mExt; ++ ++ for (int i = 0; i < aia.numberOfAccessDescription(); i++) { ++ AccessDescription ad = aia.getAccessDescription(i); ++ ObjectIdentifier method = ad.getMethod(); ++ ++ if (method.equals(SubjectInfoAccessExtension.METHOD_OCSP)) { ++ sb.append(pp.indent(mIndentSize + 8) + "Method #" + i + ": " + ++ "ocsp" + "\n"); ++ } else { ++ sb.append(pp.indent(mIndentSize + 8) + "Method #" + i + ": " + ++ method.toString() + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 8) + "Location #" + i + ": " + ++ ad.getLocation().toString() + "\n"); ++ } ++ return sb.toString(); ++ } ++ ++ private String getAuthInfoAccessExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_AIA) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_ACCESS_DESC) + "\n"); ++ AuthInfoAccessExtension aia = (AuthInfoAccessExtension) mExt; ++ ++ for (int i = 0; i < aia.numberOfAccessDescription(); i++) { ++ AccessDescription ad = aia.getAccessDescription(i); ++ ObjectIdentifier method = ad.getMethod(); ++ ++ if (method.equals(AuthInfoAccessExtension.METHOD_OCSP)) { ++ sb.append(pp.indent(mIndentSize + 8) + "Method #" + i + ": " + ++ "ocsp" + "\n"); ++ } else { ++ sb.append(pp.indent(mIndentSize + 8) + "Method #" + i + ": " + ++ method.toString() + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 8) + "Location #" + i + ": " + ++ ad.getLocation().toString() + "\n"); ++ } ++ return sb.toString(); ++ } ++ ++ private String getPresenceServerExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_PRESENCE_SERVER) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ PresenceServerExtension pse = (PresenceServerExtension) mExt; ++ ++ sb.append(pp.indent(mIndentSize + 4) + "Version : " + pse.getVersion() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + "Street Address : " + pse.getStreetAddress() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + "Telephone Number : " + pse.getTelephoneNumber() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + "RFC822 Name : " + pse.getRFC822() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + "ID : " + pse.getID() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + "Host Name : " + pse.getHostName() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + "Port Number : " + pse.getPortNumber() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + "Max Users : " + pse.getMaxUsers() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + "Service Level : " + pse.getServiceLevel() + "\n"); ++ ++ return sb.toString(); ++ } ++ ++ private String getPrivateKeyUsageExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_PRIVATE_KEY_USAGE) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ PrivateKeyUsageExtension usage = (PrivateKeyUsageExtension) mExt; ++ ++ sb.append(pp.indent(mIndentSize + 4) + "Validity:\n"); ++ ++ if (dateFormater == null) { ++ dateFormater = DateFormat.getDateInstance(DateFormat.FULL); ++ } ++ String notBefore = dateFormater.format(usage.getNotBefore()); ++ String notAfter = dateFormater.format(usage.getNotAfter()); ++ ++ sb.append(pp.indent(mIndentSize + 8) + "Not Before: " + notBefore + "\n"); ++ sb.append(pp.indent(mIndentSize + 8) + "Not After: " + notAfter + "\n"); ++ ++ return sb.toString(); ++ } ++ ++ private String getExtendedKeyUsageExtension() { ++ StringBuffer sb = new StringBuffer(); ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_EXTENDED_KEY_USAGE) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_EXTENDED_KEY_USAGE) + "\n"); ++ ExtendedKeyUsageExtension usage = (ExtendedKeyUsageExtension) mExt; ++ Enumeration e = usage.getOIDs(); ++ ++ if (e != null) { ++ while (e.hasMoreElements()) { ++ ObjectIdentifier oid = e.nextElement(); ++ ++ if (oid.equals(ExtendedKeyUsageExtension.OID_OCSP_SIGNING)) { ++ sb.append(pp.indent(mIndentSize + 8) + "OCSPSigning" + "\n"); ++ } else { ++ sb.append(pp.indent(mIndentSize + 8) + oid.toString() + "\n"); ++ } ++ } ++ } ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of KeyUsageExtension ++ */ ++ private String getKeyUsage() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_KEY_USAGE) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_KEY_USAGE) + "\n"); ++ KeyUsageExtension usage = (KeyUsageExtension) mExt; ++ ++ if (((Boolean) usage.get(KeyUsageExtension.DIGITAL_SIGNATURE)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.DIGITAL_SIGNATURE) + "\n"); ++ } ++ if (((Boolean) usage.get(KeyUsageExtension.NON_REPUDIATION)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.NON_REPUDIATION) + "\n"); ++ } ++ if (((Boolean) usage.get(KeyUsageExtension.KEY_ENCIPHERMENT)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.KEY_ENCIPHERMENT) + "\n"); ++ } ++ if (((Boolean) usage.get(KeyUsageExtension.DATA_ENCIPHERMENT)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.DATA_ENCIPHERMENT) + "\n"); ++ } ++ if (((Boolean) usage.get(KeyUsageExtension.KEY_AGREEMENT)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.KEY_AGREEMENT) + "\n"); ++ } ++ if (((Boolean) usage.get(KeyUsageExtension.KEY_CERTSIGN)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.KEY_CERTSIGN) + "\n"); ++ } ++ if (((Boolean) usage.get(KeyUsageExtension.CRL_SIGN)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.CRL_SIGN) + "\n"); ++ } ++ if (((Boolean) usage.get(KeyUsageExtension.ENCIPHER_ONLY)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.ENCIPHER_ONLY) + "\n"); ++ } ++ if (((Boolean) usage.get(KeyUsageExtension.DECIPHER_ONLY)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.DECIPHER_ONLY) + "\n"); ++ } ++ return sb.toString(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ return sb.toString(); ++ } ++ ++ } ++ ++ /** ++ * String Representation of NSCertTypeExtension ++ */ ++ private String getCertType() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_CERT_TYPE) ++ + "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CERT_USAGE) + "\n"); ++ NSCertTypeExtension type = (NSCertTypeExtension) mExt; ++ ++ if (((Boolean) type.get(NSCertTypeExtension.SSL_CLIENT)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.SSL_CLIENT) + "\n"); ++ } ++ if (((Boolean) type.get(NSCertTypeExtension.SSL_SERVER)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.SSL_SERVER) + "\n"); ++ } ++ if (((Boolean) type.get(NSCertTypeExtension.EMAIL)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.EMAIL) + "\n"); ++ } ++ if (((Boolean) type.get(NSCertTypeExtension.OBJECT_SIGNING)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.OBJECT_SIGNING) + "\n"); ++ } ++ if (((Boolean) type.get(NSCertTypeExtension.SSL_CA)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.SSL_CA) + "\n"); ++ } ++ if (((Boolean) type.get(NSCertTypeExtension.EMAIL_CA)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.EMAIL_CA) + "\n"); ++ } ++ if (((Boolean) type.get(NSCertTypeExtension.OBJECT_SIGNING_CA)).booleanValue()) { ++ sb.append(pp.indent(mIndentSize + 8) ++ + mResource.getString(NSCertTypeExtension.OBJECT_SIGNING_CA) + "\n"); ++ } ++ return sb.toString(); ++ } catch (CertificateException e) { ++ e.printStackTrace(); ++ return ""; ++ } ++ ++ } ++ ++ /** ++ * String Representation of SubjectKeyIdentifierExtension ++ */ ++ private String getSubjectKeyIdentifier() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_SKI) ++ + "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ SubjectKeyIdentifierExtension id = (SubjectKeyIdentifierExtension) mExt; ++ KeyIdentifier keyId = (KeyIdentifier) id.get(SubjectKeyIdentifierExtension.KEY_ID); ++ ++ if (keyId != null) { ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_KEY_ID) + "\n"); ++ sb.append(pp.toHexString(keyId.getIdentifier(), 24, 16)); ++ } ++ return sb.toString(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of AuthorityKeyIdentifierExtension ++ */ ++ private String getAuthorityKeyIdentifier() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_AKI) ++ + "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ AuthorityKeyIdentifierExtension id = (AuthorityKeyIdentifierExtension) mExt; ++ KeyIdentifier keyId = (KeyIdentifier) id.get(AuthorityKeyIdentifierExtension.KEY_ID); ++ ++ if (keyId != null) { ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_KEY_ID) + "\n"); ++ sb.append(pp.toHexString(keyId.getIdentifier(), mIndentSize + 8, 16)); ++ // sb.append(pp.toHexString(keyId.getIdentifier(),24,16)); ++ } ++ GeneralNames authNames = (GeneralNames) id.get(AuthorityKeyIdentifierExtension.AUTH_NAME); ++ ++ if (authNames != null) { ++ for (int i = 0; i < authNames.size(); i++) { ++ GeneralName authName = (GeneralName) authNames.elementAt(i); ++ ++ if (authName != null) { ++ sb.append(pp.indent(mIndentSize + 4) ++ + mResource.getString(PrettyPrintResources.TOKEN_AUTH_NAME) + authName.toString() ++ + "\n"); ++ } ++ } ++ } ++ ++ SerialNumber serial = (SerialNumber) id.get(AuthorityKeyIdentifierExtension.SERIAL_NUMBER); ++ ++ if (serial != null) { ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_SERIAL) + ++ "0x" + serial.getNumber().toBigInteger().toString(16).toUpperCase() + "\n"); ++ } ++ return sb.toString(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of CRLReasonExtension ++ */ ++ private String getCRLReasonExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_REVOCATION_REASON) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ CRLReasonExtension ext = (CRLReasonExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_REASON) + ++ ext.getReason().toString() + "\n"); ++ ++ return sb.toString(); ++ } catch (Exception e) { ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of InhibitAnyPolicyExtension ++ */ ++ private String getInhibitAnyPolicyExtension() { ++ StringBuffer sb = new StringBuffer(); ++ sb.append(pp.indent(mIndentSize) + ++ mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_INHIBIT_ANY_POLICY_EXT) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ InhibitAnyPolicyExtension ext = (InhibitAnyPolicyExtension) mExt; ++ if (mExt.isCritical()) ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ else ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_SKIP_CERTS)); ++ BigInt num = ext.getSkipCerts(); ++ sb.append("" + num.toInt() + "\n"); ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of BasicConstraintsExtension ++ */ ++ private String getBasicConstraintsExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_BASIC_CONSTRAINTS) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ BasicConstraintsExtension ext = (BasicConstraintsExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_IS_CA)); ++ boolean isCA = ((Boolean) ext.get(BasicConstraintsExtension.IS_CA)).booleanValue(); ++ ++ if (isCA) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ Integer pathLength = (Integer) ext.get(BasicConstraintsExtension.PATH_LEN); ++ ++ if (pathLength != null) { ++ if (pathLength.longValue() >= 0) { ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN) + ++ pathLength.toString() + "\n"); ++ } else if (pathLength.longValue() == -1 || pathLength.longValue() == -2) { ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN) + ++ mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN_UNLIMITED) + "\n"); ++ } else { ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN) + ++ mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN_INVALID) + ++ " (" + pathLength.toString() + ")\n"); ++ } ++ } ++ ++ return sb.toString(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of CRLNumberExtension ++ */ ++ private String getCRLNumberExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_CRL_NUMBER) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ CRLNumberExtension ext = (CRLNumberExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ BigInteger crlNumber = (BigInteger) ext.get(CRLNumberExtension.NUMBER); ++ ++ if (crlNumber != null) { ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_NUMBER) + ++ crlNumber.toString() + "\n"); ++ } ++ ++ return sb.toString(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of DeltaCRLIndicatorExtension ++ */ ++ private String getDeltaCRLIndicatorExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_DELTA_CRL_INDICATOR) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ DeltaCRLIndicatorExtension ext = (DeltaCRLIndicatorExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ BigInteger crlNumber = (BigInteger) ext.get(DeltaCRLIndicatorExtension.NUMBER); ++ ++ if (crlNumber != null) { ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_BASE_CRL_NUMBER) + ++ crlNumber.toString() + "\n"); ++ } ++ ++ return sb.toString(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of IssuerAlternativeName Extension ++ */ ++ private String getIssuerAlternativeNameExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_ISSUER_ALT_NAME) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ IssuerAlternativeNameExtension ext = (IssuerAlternativeNameExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ GeneralNames issuerNames = (GeneralNames) ext.get(IssuerAlternativeNameExtension.ISSUER_NAME); ++ ++ if (issuerNames != null) { ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_ISSUER_NAMES) + "\n"); ++ for (int i = 0; i < issuerNames.size(); i++) { ++ GeneralName issuerName = (GeneralName) issuerNames.elementAt(i); ++ ++ if (issuerName != null) { ++ String nameType = ""; ++ ++ if (issuerName.getType() == GeneralNameInterface.NAME_DIRECTORY) ++ nameType = "DirectoryName: "; ++ sb.append(pp.indent(mIndentSize + 8) + nameType + issuerName.toString() + "\n"); ++ } ++ } ++ } ++ ++ return sb.toString(); ++ } catch (IOException e) { ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of SubjectAlternativeName Extension ++ */ ++ private String getSubjectAlternativeNameExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_SUBJECT_ALT_NAME) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ SubjectAlternativeNameExtension ext = (SubjectAlternativeNameExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ GeneralNames subjectNames = (GeneralNames) ext.get(SubjectAlternativeNameExtension.SUBJECT_NAME); ++ ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_VALUE) + "\n"); ++ for (int i = 0; i < subjectNames.size(); i++) { ++ GeneralName subjectName = (GeneralName) subjectNames.elementAt(i); ++ ++ if (subjectName != null) { ++ String nameType = ""; ++ ++ if (subjectName.getType() == GeneralNameInterface.NAME_DIRECTORY) ++ nameType = "DirectoryName: "; ++ sb.append(pp.indent(mIndentSize + 8) + nameType + subjectName.toString() + "\n"); ++ } ++ } ++ ++ return sb.toString(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of CertificateScopeOfUse Extension ++ */ ++ private String getCertificateScopeOfUseExtension() { ++ StringBuffer sb = new StringBuffer(); ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_CERT_SCOPE_OF_USE) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ CertificateScopeOfUseExtension ext = (CertificateScopeOfUseExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ Vector entries = ext.getCertificateScopeEntries(); ++ ++ if (entries != null) { ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_SCOPE_OF_USE) + "\n"); ++ for (int i = 0; i < entries.size(); i++) { ++ CertificateScopeEntry se = entries.elementAt(i); ++ GeneralName gn = se.getGeneralName(); ++ ++ if (gn != null) { ++ String nameType = ""; ++ ++ if (gn.getType() == GeneralNameInterface.NAME_DIRECTORY) ++ nameType = "DirectoryName: "; ++ sb.append(pp.indent(mIndentSize + 8) + nameType + gn.toString() + "\n"); ++ } ++ BigInt port = se.getPort(); ++ ++ if (port != null) { ++ sb.append(pp.indent(mIndentSize + 8) + PrettyPrintResources.TOKEN_PORT + ++ port.toBigInteger().toString() + "\n"); ++ } ++ } ++ } ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of FreshestCRLExtension ++ */ ++ private String getFreshestCRLExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ // ++ // Generic stuff: name, OID, criticality ++ // ++ sb.append(pp.indent(mIndentSize) + ++ mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_FRESHEST_CRL_EXT) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ // ++ // Now the CRLDP-specific stuff ++ // ++ FreshestCRLExtension ext = (FreshestCRLExtension) mExt; ++ ++ int numPoints = ext.getNumPoints(); ++ ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRLDP_NUMPOINTS) ++ + numPoints + "\n"); ++ ++ for (int i = 0; i < numPoints; i++) { ++ ++ // ++ // print one individual CRL distribution point ++ // ++ ++ int idt; ++ ++ idt = mIndentSize + 4; // reset each time through loop ++ boolean isEmpty = true; ++ ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_POINTN) + ++ i + "\n"); ++ ++ CRLDistributionPoint pt = ext.getPointAt(i); ++ ++ idt += 4; // further indent rest of information ++ ++ if (pt.getFullName() != null) { ++ isEmpty = false; ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_DISTPOINT) ++ + pt.getFullName() + "\n"); ++ } ++ ++ if (pt.getRelativeName() != null) { ++ isEmpty = false; ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_DISTPOINT) ++ + pt.getRelativeName() + "\n"); ++ } ++ ++ if (pt.getReasons() != null) { ++ isEmpty = false; ++ byte[] reasonBits = pt.getReasons().toByteArray(); ++ String reasonList = reasonBitsToReasonList(reasonBits); ++ ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_REASONS) ++ + reasonList + "\n"); ++ } ++ ++ if (pt.getCRLIssuer() != null) { ++ isEmpty = false; ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_CRLISSUER) ++ + pt.getCRLIssuer() + "\n"); ++ } ++ ++ if (isEmpty) { ++ sb.append(pp.indent(idt) + "empty\n"); ++ } ++ ++ } ++ ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of CRLDistributionPointsExtension ++ */ ++ private String getCRLDistributionPointsExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ // ++ // Generic stuff: name, OID, criticality ++ // ++ sb.append(pp.indent(mIndentSize) + ++ mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_CRL_DP_EXT) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ // ++ // Now the CRLDP-specific stuff ++ // ++ CRLDistributionPointsExtension ext = ++ (CRLDistributionPointsExtension) mExt; ++ ++ int numPoints = ext.getNumPoints(); ++ ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRLDP_NUMPOINTS) ++ + numPoints + "\n"); ++ ++ for (int i = 0; i < numPoints; i++) { ++ ++ // ++ // print one individual CRL distribution point ++ // ++ ++ int idt; ++ ++ idt = mIndentSize + 4; // reset each time through loop ++ boolean isEmpty = true; ++ ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_POINTN) + ++ i + "\n"); ++ ++ CRLDistributionPoint pt = ext.getPointAt(i); ++ ++ idt += 4; // further indent rest of information ++ ++ if (pt.getFullName() != null) { ++ isEmpty = false; ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_DISTPOINT) ++ + pt.getFullName() + "\n"); ++ } ++ ++ if (pt.getRelativeName() != null) { ++ isEmpty = false; ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_DISTPOINT) ++ + pt.getRelativeName() + "\n"); ++ } ++ ++ if (pt.getReasons() != null) { ++ isEmpty = false; ++ byte[] reasonBits = pt.getReasons().toByteArray(); ++ String reasonList = reasonBitsToReasonList(reasonBits); ++ ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_REASONS) ++ + reasonList + "\n"); ++ } ++ ++ if (pt.getCRLIssuer() != null) { ++ isEmpty = false; ++ sb.append(pp.indent(idt) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRLDP_CRLISSUER) ++ + pt.getCRLIssuer() + "\n"); ++ } ++ ++ if (isEmpty) { ++ sb.append(pp.indent(idt) + "empty\n"); ++ } ++ ++ } ++ ++ return sb.toString(); ++ } ++ ++ private static String reasonBitsToReasonList(byte[] reasonBits) { ++ ++ Reason[] reasons = Reason.bitArrayToReasonArray(reasonBits); ++ ++ if (reasons.length == 0) { ++ return ""; ++ } else { ++ StringBuffer buf = new StringBuffer(); ++ ++ buf.append(reasons[0].getName()); ++ for (int i = 1; i < reasons.length; i++) { ++ buf.append(", "); ++ buf.append(reasons[i].getName()); ++ } ++ return buf.toString(); ++ } ++ } ++ ++ /** ++ * String Representation of IssuerAlternativeName Extension ++ */ ++ private String getIssuingDistributionPointExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_ISSUING_DIST_POINT) + "- " + ++ mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ IssuingDistributionPointExtension ext = (IssuingDistributionPointExtension) mExt; ++ IssuingDistributionPoint issuingDistributionPoint = ext.getIssuingDistributionPoint(); ++ ++ if (issuingDistributionPoint != null) { ++ GeneralNames fullNames = issuingDistributionPoint.getFullName(); ++ RDN relativeName = issuingDistributionPoint.getRelativeName(); ++ ++ if (fullNames != null || relativeName != null) { ++ sb.append(pp.indent(mIndentSize + 4) ++ + mResource.getString(PrettyPrintResources.TOKEN_DIST_POINT_NAME) + "\n"); ++ if (fullNames != null) { ++ sb.append(pp.indent(mIndentSize + 8) ++ + mResource.getString(PrettyPrintResources.TOKEN_FULL_NAME) + "\n"); ++ for (int i = 0; i < fullNames.size(); i++) { ++ GeneralName fullName = (GeneralName) fullNames.elementAt(i); ++ ++ if (fullName != null) { ++ sb.append(pp.indent(mIndentSize + 12) + fullName.toString() + "\n"); ++ } ++ } ++ } ++ if (relativeName != null) { ++ sb.append(pp.indent(mIndentSize + 8) ++ + mResource.getString(PrettyPrintResources.TOKEN_RELATIVE_NAME) + ++ relativeName.toString() + "\n"); ++ } ++ } ++ ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_ONLY_USER_CERTS)); ++ if (issuingDistributionPoint.getOnlyContainsUserCerts()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_ONLY_CA_CERTS)); ++ if (issuingDistributionPoint.getOnlyContainsCACerts()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ BitArray onlySomeReasons = issuingDistributionPoint.getOnlySomeReasons(); ++ ++ if (onlySomeReasons != null) { ++ sb.append(pp.indent(mIndentSize + 4) ++ + mResource.getString(PrettyPrintResources.TOKEN_ONLY_SOME_REASONS)); ++ sb.append("0x" + pp.toHexString(onlySomeReasons.toByteArray())); ++ } ++ ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_INDIRECT_CRL)); ++ if (issuingDistributionPoint.getIndirectCRL()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ } ++ ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of InvalidityDateExtension ++ */ ++ private String getInvalidityDateExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_INVALIDITY_DATE) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ InvalidityDateExtension ext = (InvalidityDateExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_DATE_OF_INVALIDITY) + ++ ext.getInvalidityDate().toString() + "\n"); ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of CertificateIssuerExtension ++ */ ++ private String getCertificateIssuerExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_CERTIFICATE_ISSUER) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ CertificateIssuerExtension ext = (CertificateIssuerExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ GeneralNames issuerNames = (GeneralNames) ext.get( ++ CertificateIssuerExtension.CERTIFICATE_ISSUER); ++ ++ if (issuerNames != null) { ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_ISSUER_NAMES) + "\n"); ++ for (int i = 0; i < issuerNames.size(); i++) { ++ GeneralName issuerName = (GeneralName) issuerNames.elementAt(i); ++ ++ if (issuerName != null) { ++ String nameType = ""; ++ ++ if (issuerName.getType() == GeneralNameInterface.NAME_DIRECTORY) ++ nameType = "DirectoryName: "; ++ sb.append(pp.indent(mIndentSize + 8) + nameType + issuerName.toString() + "\n"); ++ } ++ } ++ } ++ ++ return sb.toString(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ return ""; ++ } ++ } ++ ++ /** ++ * String Representation of HoldInstructionExtension ++ */ ++ private String getHoldInstructionExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_HOLD_INSTRUCTION) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ HoldInstructionExtension ext = (HoldInstructionExtension) mExt; ++ ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_HOLD_INSTRUCTION_CODE) + ++ ext.getHoldInstructionCodeDescription() + "\n"); ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of PolicyConstraintsExtension ++ */ ++ private String getPolicyConstraintsExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append( ++ mResource.getString( ++ PrettyPrintResources.TOKEN_POLICY_CONSTRAINTS) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ PolicyConstraintsExtension ext = (PolicyConstraintsExtension) mExt; ++ int require = ext.getRequireExplicitMapping(); ++ int inhibit = ext.getInhibitPolicyMapping(); ++ ++ sb.append( ++ pp.indent(mIndentSize + 4) + ++ mResource.getString( ++ PrettyPrintResources.TOKEN_REQUIRE_EXPLICIT_POLICY) + ++ ((require == -1) ? ++ mResource.getString(PrettyPrintResources.TOKEN_NOT_SET) : ++ String.valueOf(require)) + "\n"); ++ sb.append( ++ pp.indent(mIndentSize + 4) + ++ mResource.getString( ++ PrettyPrintResources.TOKEN_INHIBIT_POLICY_MAPPING) + ++ ((inhibit == -1) ? ++ mResource.getString(PrettyPrintResources.TOKEN_NOT_SET) : ++ String.valueOf(inhibit)) + "\n"); ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of PolicyMappingsExtension ++ */ ++ private String getPolicyMappingsExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_POLICY_MAPPINGS) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ PolicyMappingsExtension ext = (PolicyMappingsExtension) mExt; ++ Enumeration maps = ext.getMappings(); ++ ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_MAPPINGS)); ++ if (maps == null || !maps.hasMoreElements()) { ++ sb.append( ++ mResource.getString(PrettyPrintResources.TOKEN_NONE) + "\n"); ++ } else { ++ sb.append("\n"); ++ for (int i = 0; maps.hasMoreElements(); i++) { ++ sb.append(pp.indent(mIndentSize + 8) + ++ mResource.getString( ++ PrettyPrintResources.TOKEN_MAP) + i + ":" + "\n"); ++ CertificatePolicyMap m = ++ maps.nextElement(); ++ ++ sb.append(pp.indent(mIndentSize + 12) + ++ mResource.getString( ++ PrettyPrintResources.TOKEN_ISSUER_DOMAIN_POLICY) + ++ m.getIssuerIdentifier().getIdentifier().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 12) + ++ mResource.getString( ++ PrettyPrintResources.TOKEN_SUBJECT_DOMAIN_POLICY) + ++ m.getSubjectIdentifier().getIdentifier().toString() + "\n"); ++ } ++ } ++ return sb.toString(); ++ } ++ ++ /** ++ * String Representation of SubjectDirAttributesExtension ++ */ ++ private String getSubjectDirAttributesExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_SUBJECT_DIR_ATTR) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ ++ SubjectDirAttributesExtension ext = ++ (SubjectDirAttributesExtension) mExt; ++ ++ sb.append(pp.indent(mIndentSize + 4) + ++ mResource.getString(PrettyPrintResources.TOKEN_ATTRIBUTES)); ++ Enumeration attrs = ext.getAttributesList(); ++ ++ if (attrs == null || !attrs.hasMoreElements()) { ++ sb.append( ++ mResource.getString(PrettyPrintResources.TOKEN_NONE) + "\n"); ++ } else { ++ sb.append("\n"); ++ for (int j = 0; attrs.hasMoreElements(); j++) { ++ Attribute attr = attrs.nextElement(); ++ ++ sb.append(pp.indent(mIndentSize + 8) + ++ mResource.getString( ++ PrettyPrintResources.TOKEN_ATTRIBUTE) + j + ":" + "\n"); ++ sb.append(pp.indent(mIndentSize + 12) + ++ mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER) + ++ attr.getOid().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 12) + ++ mResource.getString( ++ PrettyPrintResources.TOKEN_VALUES)); ++ Enumeration values = attr.getValues(); ++ ++ if (values == null || !values.hasMoreElements()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NONE) + "\n"); ++ } else { ++ for (int k = 0; values.hasMoreElements(); k++) { ++ String v = values.nextElement(); ++ ++ if (k != 0) ++ sb.append(","); ++ sb.append(v); ++ } ++ } ++ sb.append("\n"); ++ } ++ } ++ return sb.toString(); ++ } catch (Throwable e) { ++ return ""; ++ } ++ } ++ ++ private String getCertificatePoliciesExtension() { ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ sb.append(pp.indent(mIndentSize) + mResource.getString( ++ PrettyPrintResources.TOKEN_IDENTIFIER)); ++ sb.append(mResource.getString(PrettyPrintResources.TOKEN_CERT_POLICIES) + ++ "- " + mExt.getExtensionId().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CRITICAL)); ++ if (mExt.isCritical()) { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_YES) + "\n"); ++ } else { ++ sb.append(mResource.getString( ++ PrettyPrintResources.TOKEN_NO) + "\n"); ++ } ++ sb.append(pp.indent(mIndentSize + 4) + mResource.getString( ++ PrettyPrintResources.TOKEN_CERT_POLICIES) + "\n"); ++ CertificatePoliciesExtension cp = (CertificatePoliciesExtension) mExt; ++ @SuppressWarnings("unchecked") ++ Vector cpv = (Vector) cp.get("infos"); ++ Enumeration e = cpv.elements(); ++ ++ if (e != null) { ++ while (e.hasMoreElements()) { ++ CertificatePolicyInfo cpi = e.nextElement(); ++ ++ sb.append(pp.indent(mIndentSize + 8) ++ + "Policy Identifier: " + cpi.getPolicyIdentifier().getIdentifier().toString() + "\n"); ++ PolicyQualifiers cpq = cpi.getPolicyQualifiers(); ++ if (cpq != null) { ++ for (int i = 0; i < cpq.size(); i++) { ++ PolicyQualifierInfo pq = cpq.getInfoAt(i); ++ Qualifier q = pq.getQualifier(); ++ if (q instanceof CPSuri) { ++ sb.append(pp.indent(mIndentSize + 12) ++ + "Policy Qualifier Identifier: CPS Pointer Qualifier - " ++ + pq.getId() + "\n"); ++ sb.append(pp.indent(mIndentSize + 12) ++ + "Policy Qualifier Data: " + ((CPSuri) q).getURI() + "\n"); ++ } else if (q instanceof UserNotice) { ++ sb.append(pp.indent(mIndentSize + 12) ++ + "Policy Qualifier Identifier: CPS User Notice Qualifier - " ++ + pq.getId() + "\n"); ++ NoticeReference nref = ((UserNotice) q).getNoticeReference(); ++ DisplayText dt = ((UserNotice) q).getDisplayText(); ++ sb.append(pp.indent(mIndentSize + 12) + "Policy Qualifier Data: \n"); ++ if (nref != null) { ++ sb.append(pp.indent(mIndentSize + 16) ++ + "Organization: " + nref.getOrganization().toString() + "\n"); ++ sb.append(pp.indent(mIndentSize + 16) + "Notice Numbers: "); ++ int[] nums = nref.getNumbers(); ++ for (int k = 0; k < nums.length; k++) { ++ if (k != 0) { ++ sb.append(","); ++ sb.append(nums[k]); ++ } else { ++ sb.append(nums[k]); ++ } ++ } ++ sb.append("\n"); ++ } ++ if (dt != null) { ++ sb.append(pp.indent(mIndentSize + 16) + "Explicit Text: " + dt.toString() + "\n"); ++ } ++ } ++ } ++ } ++ } ++ } ++ return sb.toString(); ++ } catch (IOException e) { ++ return sb.toString(); ++ } ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/util/IA5Charset.java b/org/mozilla/jss/netscape/security/util/IA5Charset.java +new file mode 100644 +index 00000000..03533661 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/IA5Charset.java +@@ -0,0 +1,24 @@ ++package org.mozilla.jss.netscape.security.util; ++ ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.CharsetEncoder; ++ ++public class IA5Charset extends Charset { ++ ++ public IA5Charset() { ++ super("ASN.1-IA5", null); ++ } ++ ++ public boolean contains(Charset cs) { ++ return false; ++ } ++ ++ public CharsetDecoder newDecoder() { ++ return new IA5CharsetDecoder(this); ++ } ++ ++ public CharsetEncoder newEncoder() { ++ return new IA5CharsetEncoder(this); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/IA5CharsetDecoder.java b/org/mozilla/jss/netscape/security/util/IA5CharsetDecoder.java +new file mode 100644 +index 00000000..8b9a05e8 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/IA5CharsetDecoder.java +@@ -0,0 +1,62 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.CoderResult; ++import java.nio.charset.CodingErrorAction; ++ ++/** ++ * Converts bytes in ASN.1 IA5String character set to IA5String characters. ++ * ++ * @author Lily Hsiao ++ * @author Slava Galperin ++ */ ++ ++public class IA5CharsetDecoder extends CharsetDecoder { ++ ++ public IA5CharsetDecoder(Charset cs) { ++ super(cs, 1, 1); ++ } ++ ++ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { ++ ++ while (true) { ++ ++ if (in.remaining() < 1) ++ return CoderResult.UNDERFLOW; ++ ++ in.mark(); ++ byte b = in.get(); ++ ++ if (CodingErrorAction.REPORT == unmappableCharacterAction() && (b & 0x80) != 0) { ++ return CoderResult.unmappableForLength(1); ++ } ++ ++ if (out.remaining() < 1) { ++ in.reset(); ++ return CoderResult.OVERFLOW; ++ } ++ ++ out.put((char) (b & 0x7f)); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/IA5CharsetEncoder.java b/org/mozilla/jss/netscape/security/util/IA5CharsetEncoder.java +new file mode 100644 +index 00000000..3651ca85 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/IA5CharsetEncoder.java +@@ -0,0 +1,69 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetEncoder; ++import java.nio.charset.CoderResult; ++import java.nio.charset.CodingErrorAction; ++ ++/** ++ * Converts characters in ASN.1 IA5String character set to IA5String bytes. ++ * ++ * @author Lily Hsiao ++ * @author Slava Galperin ++ */ ++ ++public class IA5CharsetEncoder extends CharsetEncoder { ++ ++ public IA5CharsetEncoder(Charset cs) { ++ super(cs, 1, 1); ++ } ++ ++ /* ++ * Converts an array of Unicode characters into an array of IA5String ++ * bytes and returns the conversion result. ++ * @param in input character buffer to convert. ++ * @param out byte buffer to store output. ++ * @return encoding result. ++ */ ++ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { ++ ++ while (true) { ++ ++ if (in.remaining() < 1) ++ return CoderResult.UNDERFLOW; ++ ++ in.mark(); ++ char c = in.get(); ++ ++ if (CodingErrorAction.REPORT == unmappableCharacterAction() && (c & 0xFF80) != 0) { ++ return CoderResult.unmappableForLength(1); ++ } ++ ++ if (out.remaining() < 1) { ++ in.reset(); ++ return CoderResult.OVERFLOW; ++ } ++ ++ out.put((byte) (c & 0x7f)); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/Makefile b/org/mozilla/jss/netscape/security/util/Makefile +new file mode 100644 +index 00000000..8fd7054b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/Makefile +@@ -0,0 +1,45 @@ ++#! gmake ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### +diff --git a/org/mozilla/jss/netscape/security/util/ObjectIdentifier.java b/org/mozilla/jss/netscape/security/util/ObjectIdentifier.java +new file mode 100644 +index 00000000..9b34f8b6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/ObjectIdentifier.java +@@ -0,0 +1,533 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.IOException; ++import java.io.Serializable; ++import java.math.BigInteger; ++import java.util.Hashtable; ++import java.util.StringTokenizer; ++ ++/** ++ * Represent an ISO Object Identifier. ++ * ++ *

++ * Object Identifiers are arbitrary length hierarchical identifiers. The individual components are numbers, and they ++ * define paths from the root of an ISO-managed identifier space. You will sometimes see a string name used instead of ++ * (or in addition to) the numerical id. These are synonyms for the numerical IDs, but are not widely used since most ++ * sites do not know all the requisite strings, while all sites can parse the numeric forms. ++ * ++ *

++ * So for example, JavaSoft has the sole authority to assign the meaning to identifiers below the 1.3.6.1.4.42.2.17 node ++ * in the hierarchy, and other organizations can easily acquire the ability to assign such unique identifiers. ++ * ++ * @version 1.23 ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++final public class ObjectIdentifier implements Serializable { ++ /** use serialVersionUID from JDK 1.1. for interoperability */ ++ private static final long serialVersionUID = 8697030238860181294L; ++ ++ /** ++ * Constructs an object identifier from a string. This string ++ * should be of the form 1.23.34.45.56 etc. ++ */ ++ public ObjectIdentifier(String oid) { ++ if (oid == null) ++ return; ++ ++ int ch = '.'; ++ int start = 0; ++ int end = 0; ++ ++ // Calculate length of oid ++ componentLen = 0; ++ while ((end = oid.indexOf(ch, start)) != -1) { ++ start = end + 1; ++ componentLen += 1; ++ } ++ componentLen += 1; ++ components = new BigInteger[componentLen]; ++ ++ start = 0; ++ int i = 0; ++ String comp = null; ++ while ((end = oid.indexOf(ch, start)) != -1) { ++ comp = oid.substring(start, end); ++ components[i++] = new BigInteger(comp); ++ start = end + 1; ++ } ++ comp = oid.substring(start); ++ components[i] = new BigInteger(comp); ++ } ++ ++ /** ++ * Constructs an object ID from an array of integers. This ++ * is used to construct constant object IDs. ++ */ ++ public ObjectIdentifier(int values[]) { ++ try { ++ componentLen = values.length; ++ BigInteger[] tmp = new BigInteger[componentLen]; ++ ++ for (int i = 0; i < componentLen; i++) { ++ tmp[i] = BigInteger.valueOf(values[i]); ++ } ++ ++ components = tmp.clone(); ++ } catch (Throwable t) { ++ System.out.println("X509.ObjectIdentifier(), no cloning!"); ++ } ++ } ++ ++ public ObjectIdentifier(BigInteger values[]) { ++ try { ++ componentLen = values.length; ++ ++ componentLen = values.length; ++ BigInteger[] tmp = new BigInteger[componentLen]; ++ ++ for (int i = 0; i < componentLen; i++) { ++ tmp[i] = new BigInteger(values[i].toString()); ++ } ++ ++ components = tmp.clone(); ++ ++ } catch(Throwable t) { ++ System.out.println("X509.ObjectIdentifier(), no cloning!"); ++ } ++ } ++ ++ /** ++ * Constructs an object ID from an array of longs This ++ * is used to construct constant object IDs. ++ */ ++ public ObjectIdentifier(long values[]) { ++ try { ++ componentLen = values.length; ++ BigInteger[] tmp = new BigInteger[componentLen]; ++ ++ for (int i = 0; i < componentLen; i++) { ++ tmp[i] = BigInteger.valueOf(values[i]); ++ } ++ ++ components = tmp.clone(); ++ } catch (Throwable t) { ++ System.out.println("X509.ObjectIdentifier(), no cloning!"); ++ } ++ } ++ ++ ++ /** ++ * Constructs an object ID from an ASN.1 encoded input stream. ++ * The encoding of the ID in the stream uses "DER", a BER/1 subset. ++ * In this case, that means a triple { typeId, length, data }. ++ * ++ *

++ * NOTE: When an exception is thrown, the input stream has not been returned to its "initial" ++ * state. ++ * ++ * @param in DER-encoded data holding an object ID ++ * @exception IOException indicates a decoding error ++ */ ++ public ObjectIdentifier(DerInputStream in) ++ throws IOException { ++ byte type_id; ++ int bufferEnd; ++ ++ /* ++ * Object IDs are a "universal" type, and their tag needs only ++ * one byte of encoding. Verify that the tag of this datum ++ * is that of an object ID. ++ * ++ * Then get and check the length of the ID's encoding. We set ++ * up so that we can use in.available() to check for the end of ++ * this value in the data stream. ++ */ ++ type_id = (byte) in.getByte(); ++ if (type_id != DerValue.tag_ObjectId) ++ throw new IOException( ++ "X509.ObjectIdentifier() -- data isn't an object ID" ++ + " (tag = " + type_id + ")"); ++ ++ bufferEnd = in.available() - in.getLength() - 1; ++ if (bufferEnd < 0) ++ throw new IOException( ++ "X509.ObjectIdentifier() -- not enough data"); ++ ++ initFromEncoding(in, bufferEnd); ++ } ++ ++ /* ++ * Build the OID from the rest of a DER input buffer; the tag ++ * and length have been removed/verified ++ */ ++ ObjectIdentifier(DerInputBuffer buf) throws IOException { ++ initFromEncoding(new DerInputStream(buf), 0); ++ } ++ ++ ++ ++ /* ++ * Helper function -- get the OID from a stream, after tag and ++ * length are verified. ++ */ ++ private void initFromEncoding(DerInputStream in, int bufferEnd) ++ throws IOException { ++ ++ /* ++ * Now get the components ("sub IDs") one at a time. We fill a ++ * temporary buffer, resizing it as needed. ++ */ ++ BigInteger component; ++ boolean first_subid = true; ++ ++ for (components = new BigInteger[allocationQuantum], componentLen = 0; in.available() > bufferEnd;) { ++ component = getComponentBigInt(in); ++ ++ if (first_subid) { ++ long X, Y; ++ ++ /* ++ * The ISO root has three children (0, 1, 2) and those nodes ++ * aren't allowed to assign IDs larger than 39. These rules ++ * are memorialized by some special casing in the BER encoding ++ * of object IDs ... or maybe it's vice versa. ++ * ++ * NOTE: the allocation quantum is large enough that we know ++ * we don't have to reallocate here! ++ */ ++ if (component.intValue() < 40) ++ X = 0; ++ else if (component.intValue() < 80) ++ X = 1; ++ else ++ X = 2; ++ Y = component.intValue() - (X * 40); ++ ++ components[0] = BigInteger.valueOf(X); ++ components[1] = BigInteger.valueOf(Y); ++ componentLen = 2; ++ ++ first_subid = false; ++ ++ } else { ++ ++ /* ++ * Other components are encoded less exotically. The only ++ * potential trouble is the need to grow the array. ++ */ ++ if (componentLen >= components.length) { ++ BigInteger tmp_components[]; ++ ++ tmp_components = new BigInteger[components.length ++ + allocationQuantum]; ++ System.arraycopy(components, 0, tmp_components, 0, ++ components.length); ++ components = tmp_components; ++ } ++ components[componentLen++] = component; ++ } ++ } ++ ++ /* ++ * Final sanity check -- if we didn't use exactly the number of bytes ++ * specified, something's quite wrong. ++ */ ++ if (in.available() != bufferEnd) { ++ throw new IOException( ++ "X509.ObjectIdentifier() -- malformed input data"); ++ } ++ } ++ ++ /* ++ * n.b. the only public interface is DerOutputStream.putOID() ++ */ ++ void encode(DerOutputStream out) throws IOException { ++ DerOutputStream bytes = new DerOutputStream(); ++ int i; ++ ++ /* We can use the int here because we know we are dealing ++ with small numbers for the first byte ++ */ ++ bytes.write((components[0].intValue() * 40) + components[1].intValue()); ++ for (i = 2; i < componentLen; i++) ++ putComponentBigInt(bytes, components[i]); ++ ++ /* ++ * Now that we've constructed the component, encode ++ * it in the stream we were given. ++ */ ++ out.write(DerValue.tag_ObjectId, bytes); ++ } ++ ++ /* ++ * Tricky OID component parsing technique ... note that one bit ++ * per octet is lost, this returns at most 28 bits of component. ++ * Also, notice this parses in big-endian format. ++ */ ++ private static BigInteger getComponentBigInt(DerInputStream in) ++ throws IOException { ++ ++ BigInteger retval = BigInteger.valueOf(0); ++ int tmp; ++ ++ while (true) { ++ retval = retval.shiftLeft(7); ++ tmp = in.getByte(); ++ retval = retval.or(BigInteger.valueOf(tmp & 0x07f)); ++ if ((tmp & 0x080) == 0) ++ return retval; ++ } ++ ++ } ++ ++ /* ++ * Reverse of the above routine. Notice it needs to emit in ++ * big-endian form, so it buffers the output until it's ready. ++ * (Minimum length encoding is a DER requirement.) ++ */ ++ private static void putComponentBigInt(DerOutputStream out, BigInteger val) ++ throws IOException { ++ int i; ++ int blockSize = 100; ++ byte buf[] = new byte[blockSize]; ++ ++ BigInteger bigInt7f = BigInteger.valueOf(0x7f); ++ ++ BigInteger cur = new BigInteger(val.toString()); ++ for (i = 0;; i++) { ++ buf[i] = (cur.and(bigInt7f).byteValue()); ++ cur = cur.shiftRight(7); ++ if (cur.compareTo(BigInteger.ZERO) == 0 ) ++ break; ++ } ++ for (; i > 0; --i) ++ out.write(buf[i] | 0x080); ++ out.write(buf[0]); ++ } ++ ++ // XXX this API should probably facilitate the JDK sort utility ++ ++ /** ++ * Compares this identifier with another, for sorting purposes. ++ * An identifier does not precede itself. ++ * ++ * @param other identifer that may precede this one. ++ * @return true iff other precedes this one ++ * in a particular sorting order. ++ */ ++ public boolean precedes(ObjectIdentifier other) { ++ int i; ++ ++ // shorter IDs go first ++ if (other == this || componentLen < other.componentLen) ++ return false; ++ if (other.componentLen < componentLen) ++ return true; ++ ++ // for each component, the lesser component goes first ++ for (i = 0; i < componentLen; i++) { ++ if (other.components[i].compareTo(components[i]) > 0) ++ return true; ++ } ++ ++ // identical IDs don't precede each other ++ return false; ++ } ++ ++ public boolean equals(Object other) { ++ if (other instanceof ObjectIdentifier) ++ return equals((ObjectIdentifier) other); ++ else ++ return false; ++ } ++ ++ /** ++ * Compares this identifier with another, for equality. ++ * ++ * @return true iff the names are identical. ++ */ ++ public boolean equals(ObjectIdentifier other) { ++ int i; ++ ++ if (other == this) ++ return true; ++ if (componentLen != other.componentLen) ++ return false; ++ for (i = 0; i < componentLen; i++) { ++ if (components[i].compareTo(other.components[i]) != 0 ) ++ return false; ++ } ++ return true; ++ } ++ ++ public int hashCode() { ++ int h = 0; ++ int oflow = 0; ++ ++ for (int i = 0; i < componentLen; i++) { ++ oflow = (h & 0xff800000) >> 23; ++ h <<= 9; ++ h += components[i].intValue(); ++ h ^= oflow; ++ } ++ return h; ++ } ++ ++ /** ++ * Returns a string form of the object ID. The format is the ++ * conventional "dot" notation for such IDs, without any ++ * user-friendly descriptive strings, since those strings ++ * will not be understood everywhere. ++ */ ++ public String toString() { ++ StringBuffer retval = new StringBuffer(); ++ ++ int i; ++ ++ for (i = 0; i < componentLen; i++) { ++ if (i != 0) ++ retval.append("."); ++ retval.append(components[i]); ++ } ++ return retval.toString(); ++ } ++ ++ /* ++ * To simplify, we assume no individual component of an object ID is ++ * larger than 64 bits. Then we represent the path from the root as ++ * an array that's (usually) only filled at the beginning. ++ */ ++ private BigInteger components[]; // path from root ++ private int componentLen; // how much is used. ++ ++ private static final int allocationQuantum = 5; // >= 2 ++ ++ /** ++ * Netscape Enhancement: ++ * This function implements a object identifier factory. It ++ * should help reduces in-memory Object Identifier object. ++ * This function also provide additional checking on the OID. ++ * A valid OID should start with 0, 1, or 2. ++ * ++ * Notes: ++ * This function never returns null. IOException is raised ++ * in error conditions. ++ */ ++ public static Hashtable mOIDs = new Hashtable(); ++ ++ public static ObjectIdentifier getObjectIdentifier(String oid) ++ throws IOException { ++ int value; ++ ++ if (oid == null) ++ throw new IOException("empty object identifier"); ++ ++ oid = oid.trim(); ++ ++ ObjectIdentifier thisOID = mOIDs.get(oid); ++ if (thisOID != null) ++ return thisOID; ++ ++ StringTokenizer token = new StringTokenizer(oid, "."); ++ value = new Integer(token.nextToken()).intValue(); ++ /* First token should be 0, 1, 2 */ ++ if (value >= 0 && value <= 2) { ++ value = new Integer(token.nextToken()).intValue(); ++ /* Second token should be 0 <= && >= 39 */ ++ if (value >= 0 && value <= 39) { ++ thisOID = new ObjectIdentifier(oid); ++ if (thisOID.toString().equals(oid)) { ++ mOIDs.put(oid, thisOID); ++ return thisOID; ++ } ++ throw new IOException("invalid oid " + oid); ++ } else ++ throw new IOException("invalid oid " + oid); ++ } else ++ throw new IOException("invalid oid " + oid); ++ } ++ ++ public static ObjectIdentifier getObjectIdentifier(int values[]) ++ throws IOException { ++ StringBuffer retval = new StringBuffer(); ++ int i; ++ ++ for (i = 0; i < values.length; i++) { ++ if (i != 0) ++ retval.append("."); ++ retval.append(values[i]); ++ } ++ return getObjectIdentifier(retval.toString()); ++ } ++ ++ public static void main(String[] args) { ++ ++ long[] oid_components_long = { 1L, 3L,6L,1L,4L,1L,5000L,9L,1L,1L,1526913300628L, 1L}; ++ int[] oid_components_int = { 1, 3,6,1,4,1,2312,9,1,1,15269, 1, 1}; ++ BigInteger[] oid_components_big_int = { new BigInteger("1"), new BigInteger("3"), new BigInteger("6"), new BigInteger("1"), ++ new BigInteger("4"), new BigInteger("1"), new BigInteger("2312"), ++ new BigInteger("9"), new BigInteger("1"), ++ new BigInteger("152691330062899999999999997777788888888888888889999999999999999"), new BigInteger("1") ++ }; ++ ++ String oidIn = "1.3.6.1.4.1.2312.9.1.152691330062899999999999997777788888888888888889999999999999999.1"; ++ ObjectIdentifier oid = new ObjectIdentifier(oidIn); ++ ++ ObjectIdentifier fromDer = null; ++ ObjectIdentifier fromStaticMethod = null; ++ ObjectIdentifier fromComponentList = null; ++ ObjectIdentifier fromComponentListInt = null; ++ ObjectIdentifier fromComponentListBigInt = null; ++ ++ System.out.println("oid: " + oid.toString()); ++ ++ DerOutputStream out = new DerOutputStream(); ++ ++ try { ++ oid.encode(out); ++ DerInputStream in = new DerInputStream(out.toByteArray()); ++ fromDer = new ObjectIdentifier(in); ++ ++ System.out.println("fromDer: " + fromDer.toString()); ++ ++ fromStaticMethod = ObjectIdentifier.getObjectIdentifier(oidIn); ++ ++ System.out.println("fromStaticMethod: " + fromStaticMethod.toString()); ++ ++ fromComponentList = new ObjectIdentifier(oid_components_long); ++ ++ System.out.println("fromComponentList: " + fromComponentList.toString()); ++ ++ fromComponentListInt = new ObjectIdentifier(oid_components_int); ++ ++ System.out.println("fromComponentListInt: " + fromComponentListInt); ++ ++ fromComponentListBigInt = new ObjectIdentifier(oid_components_big_int); ++ ++ System.out.println("fromComponentListBigInt: " + fromComponentListBigInt); ++ ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/PrettyPrintFormat.java b/org/mozilla/jss/netscape/security/util/PrettyPrintFormat.java +new file mode 100644 +index 00000000..9bd8b43f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/PrettyPrintFormat.java +@@ -0,0 +1,164 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++ ++/** ++ * This class will display the certificate content in predefined ++ * format. ++ * ++ * @author Andrew Wnuk ++ * @version $Revision$, $Date$ ++ */ ++public class PrettyPrintFormat { ++ ++ /*========================================================== ++ * variables ++ *==========================================================*/ ++ private String mSeparator = ""; ++ private int mIndentSize = 0; ++ private int mLineLen = 0; ++ ++ /*========================================================== ++ * constants ++ * ++ *==========================================================*/ ++ private final static String spaces = ++ " " + ++ " " + ++ " " + ++ " " + ++ " "; ++ ++ /*========================================================== ++ * constructors ++ *==========================================================*/ ++ ++ public PrettyPrintFormat(String separator) { ++ mSeparator = separator; ++ } ++ ++ public PrettyPrintFormat(String separator, int lineLen) { ++ mSeparator = separator; ++ mLineLen = lineLen; ++ } ++ ++ public PrettyPrintFormat(String separator, int lineLen, int indentSize) { ++ mSeparator = separator; ++ mLineLen = lineLen; ++ mIndentSize = indentSize; ++ } ++ ++ /*========================================================== ++ * Private methods ++ *==========================================================*/ ++ ++ /*========================================================== ++ * public methods ++ *==========================================================*/ ++ ++ /** ++ * Provide white space indention ++ * stevep - speed improvements. Factor of 10 improvement ++ * ++ * @param numSpace number of white space to be returned ++ * @return white spaces ++ */ ++ public String indent(int size) { ++ return spaces.substring(0, size); ++ } ++ ++ private static final char[] hexdigits = { ++ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ++ 'A', 'B', 'C', 'D', 'E', 'F' ++ }; ++ ++ /** ++ * Convert Byte Array to Hex String Format ++ * stevep - speedup by factor of 8 ++ * ++ * @param byte array of data to hexify ++ * @param indentSize number of spaces to prepend before each line ++ * @param lineLen number of bytes to output on each line (0 ++ * means: put everything on one line ++ * @param separator the first character of this string will be used as ++ * the separator between bytes. ++ * @return string representation ++ */ ++ ++ public String toHexString(byte[] in, int indentSize, ++ int lineLen, String separator) { ++ if (in == null) { ++ return ""; ++ } ++ StringBuffer sb = new StringBuffer(); ++ int hexCount = 0; ++ char c[]; ++ int j = 0; ++ ++ if (lineLen == 0) { ++ c = new char[in.length * 3 + 1]; ++ } else { ++ c = new char[lineLen * 3 + 1]; ++ } ++ ++ char sep = separator.charAt(0); ++ ++ sb.append(indent(indentSize)); ++ for (int i = 0; i < in.length; i++) { ++ if (lineLen > 0 && hexCount == lineLen) { ++ c[j++] = '\n'; ++ sb.append(c, 0, j); ++ sb.append(indent(indentSize)); ++ hexCount = 0; ++ j = 0; ++ } ++ byte x = in[i]; ++ ++ // output hex digits to buffer ++ c[j++] = hexdigits[(char) ((x >> 4) & 0xf)]; ++ c[j++] = hexdigits[(char) (x & 0xf)]; ++ ++ // if not last char, output separator ++ if (i != in.length - 1) { ++ c[j++] = sep; ++ } ++ ++ hexCount++; ++ } ++ if (j > 0) { ++ c[j++] = '\n'; ++ sb.append(c, 0, j); ++ } ++ // sb.append("\n"); ++ ++ return sb.toString(); ++ } ++ ++ public String toHexString(byte[] in, int indentSize, int lineLen) { ++ return toHexString(in, indentSize, lineLen, mSeparator); ++ } ++ ++ public String toHexString(byte[] in, int indentSize) { ++ return toHexString(in, indentSize, mLineLen); ++ } ++ ++ public String toHexString(byte[] in) { ++ return toHexString(in, mIndentSize); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/PrettyPrintResources.java b/org/mozilla/jss/netscape/security/util/PrettyPrintResources.java +new file mode 100644 +index 00000000..5b4b6230 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/PrettyPrintResources.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 org.mozilla.jss.netscape.security.util; ++ ++import java.util.ListResourceBundle; ++ ++import org.mozilla.jss.netscape.security.extensions.NSCertTypeExtension; ++import org.mozilla.jss.netscape.security.x509.KeyUsageExtension; ++ ++/** ++ * Resource Boundle for the Pretty Print ++ * ++ * @author Jack Pan-Chen ++ * @version $Revision$, $Date$ ++ */ ++ ++public class PrettyPrintResources extends ListResourceBundle { ++ ++ /** ++ * Returns content ++ */ ++ public Object[][] getContents() { ++ return contents; ++ } ++ ++ /** ++ * Constants. The suffix represents the number of ++ * possible parameters. ++ */ ++ ++ //certificate pretty print ++ public final static String TOKEN_CERTIFICATE = "tokenCertificate"; ++ public final static String TOKEN_DATA = "tokenData"; ++ public final static String TOKEN_VERSION = "tokenVersion"; ++ public final static String TOKEN_SERIAL = "tokenSerial"; ++ public final static String TOKEN_SIGALG = "tokenSignatureAlgorithm"; ++ public final static String TOKEN_ISSUER = "tokenIssuer"; ++ public final static String TOKEN_VALIDITY = "tokenValidity"; ++ public final static String TOKEN_NOT_BEFORE = "tokenNotBefore"; ++ public final static String TOKEN_NOT_AFTER = "tokenNotAfter"; ++ public final static String TOKEN_SUBJECT = "tokenSubject"; ++ public final static String TOKEN_SPKI = "tokenSPKI"; ++ public final static String TOKEN_ALGORITHM = "tokenAlgorithm"; ++ public final static String TOKEN_PUBLIC_KEY = "tokenPublicKey"; ++ public final static String TOKEN_PUBLIC_KEY_MODULUS = "tokenPublicKeyModulus"; ++ public final static String TOKEN_PUBLIC_KEY_EXPONENT = "tokenPublicKeyExponent"; ++ public final static String TOKEN_EXTENSIONS = "tokenExtensions"; ++ public final static String TOKEN_SIGNATURE = "tokenSignature"; ++ ++ //extension pretty print ++ public final static String TOKEN_YES = "tokenYes"; ++ public final static String TOKEN_NO = "tokenNo"; ++ public final static String TOKEN_IDENTIFIER = "tokenIdentifier"; ++ public final static String TOKEN_CRITICAL = "tokenCritical"; ++ public final static String TOKEN_VALUE = "tokenValue"; ++ ++ //specific extension token ++ public final static String TOKEN_KEY_TYPE = "tokenKeyType"; ++ public final static String TOKEN_CERT_TYPE = "tokenCertType"; ++ public final static String TOKEN_SKI = "tokenSKI"; ++ public final static String TOKEN_AKI = "tokenAKI"; ++ public final static String TOKEN_ACCESS_DESC = "tokenAccessDesc"; ++ public final static String TOKEN_OCSP_NOCHECK = "tokenOcspNoCheck"; ++ public final static String TOKEN_EXTENDED_KEY_USAGE = "tokenExtendedKeyUsage"; ++ public final static String TOKEN_PRIVATE_KEY_USAGE = "tokenPrivateKeyUsage"; ++ public final static String TOKEN_PRESENCE_SERVER = "tokenPresenceServer"; ++ public final static String TOKEN_AIA = "tokenAIA"; ++ public final static String TOKEN_CERT_POLICIES = "tokenCertPolicies"; ++ public final static String TOKEN_SIA = "tokenSIA"; ++ public final static String TOKEN_KEY_USAGE = "tokenKeyUsage"; ++ public final static String TOKEN_CERT_USAGE = "tokenCertUsage"; ++ public final static String TOKEN_KEY_ID = "tokenKeyId"; ++ public final static String TOKEN_AUTH_NAME = "tokenAuthName"; ++ ++ public final static String TOKEN_CRL = "tokenCRL"; ++ public final static String TOKEN_THIS_UPDATE = "tokenThisUpdate"; ++ public final static String TOKEN_NEXT_UPDATE = "tokenNextUpdate"; ++ public final static String TOKEN_REVOKED_CERTIFICATES = "revokedCerts"; ++ public final static String TOKEN_REVOCATION_DATE = "revocationDate"; ++ ++ public final static String TOKEN_REVOCATION_REASON = "revocationReason"; ++ public final static String TOKEN_REASON = "reason"; ++ ++ public final static String TOKEN_BASIC_CONSTRAINTS = "basicConstraints"; ++ public final static String TOKEN_NAME_CONSTRAINTS = "tokenNameConstraints"; ++ public final static String TOKEN_NSC_COMMENT = "tokenNSCComment"; ++ public final static String TOKEN_IS_CA = "isCA"; ++ public final static String TOKEN_PATH_LEN = "pathLen"; ++ public final static String TOKEN_PATH_LEN_UNLIMITED = "pathLenUnlimited"; ++ public final static String TOKEN_PATH_LEN_UNDEFINED = "pathLenUndefined"; ++ public final static String TOKEN_PATH_LEN_INVALID = "pathLenInvalid"; ++ ++ public final static String TOKEN_CRL_NUMBER = "CRLNumber"; ++ public final static String TOKEN_NUMBER = "Number"; ++ ++ public final static String TOKEN_DELTA_CRL_INDICATOR = "DeltaCRLIndicator"; ++ public final static String TOKEN_BASE_CRL_NUMBER = "BaseCRLNumber"; ++ ++ public final static String TOKEN_CERT_SCOPE_OF_USE = "CertificateScopeOfUse"; ++ public final static String TOKEN_SCOPE_OF_USE = "ScopeOfUse"; ++ public final static String TOKEN_PORT = "Port"; ++ ++ public final static String TOKEN_ISSUER_ALT_NAME = "IssuerAlternativeName"; ++ public final static String TOKEN_ISSUER_NAMES = "IssuerNames"; ++ ++ public final static String TOKEN_SUBJECT_ALT_NAME = "SubjectAlternativeName"; ++ public final static String TOKEN_SUBJECT_NAME = "SubjectName"; ++ ++ public final static String TOKEN_DECODING_ERROR = "decodingError"; ++ ++ public final static String TOKEN_FRESHEST_CRL_EXT = "FreshestCRL"; ++ public final static String TOKEN_INHIBIT_ANY_POLICY_EXT = "InhibitAnyPolicy"; ++ public final static String TOKEN_SKIP_CERTS = "SkipCerts"; ++ ++ public final static String TOKEN_CRL_DP_EXT = "CRLDistributionPoints"; ++ public final static String TOKEN_CRLDP_NUMPOINTS = "CRLDP_NUMPOINTS"; ++ public final static String TOKEN_CRLDP_POINTN = "CRLDP_POINTN"; ++ public final static String TOKEN_CRLDP_DISTPOINT = "CRLDP_DISTPOINT"; ++ public final static String TOKEN_CRLDP_REASONS = "CRLDP_REASONS"; ++ public final static String TOKEN_CRLDP_CRLISSUER = "CRLDP_CRLISSUER"; ++ ++ public final static String TOKEN_ISSUING_DIST_POINT = "IssuingDistributionPoint"; ++ public final static String TOKEN_DIST_POINT_NAME = "DistributionPointName"; ++ public final static String TOKEN_FULL_NAME = "FullName"; ++ public final static String TOKEN_RELATIVE_NAME = "NameRelativeToCRLIssuer"; ++ public final static String TOKEN_ONLY_USER_CERTS = "OnlyContainsUserCerts"; ++ public final static String TOKEN_ONLY_CA_CERTS = "OnlyContainsCACerts"; ++ public final static String TOKEN_ONLY_SOME_REASONS = "OnlySomeReasons"; ++ public final static String TOKEN_INDIRECT_CRL = "IndirectCRL"; ++ ++ public final static String TOKEN_INVALIDITY_DATE = "invalidityDate"; ++ public final static String TOKEN_DATE_OF_INVALIDITY = "dateOfInvalidity"; ++ ++ public final static String TOKEN_CERTIFICATE_ISSUER = "CertificateIssuer"; ++ ++ public final static String TOKEN_HOLD_INSTRUCTION = "HoldInstruction"; ++ public final static String TOKEN_HOLD_INSTRUCTION_CODE = "HoldInstructionCode"; ++ public final static String TOKEN_POLICY_CONSTRAINTS = "PolicyConstraints"; ++ public final static String TOKEN_POLICY_MAPPINGS = "PolicyMappings"; ++ public final static String TOKEN_SUBJECT_DIR_ATTR = "SubjectDirectoryAttributes"; ++ ++ // policy constriants extension fields ++ public final static String TOKEN_INHIBIT_POLICY_MAPPING = "inhibitPolicyMapping"; ++ public final static String TOKEN_REQUIRE_EXPLICIT_POLICY = "requireExplicitPolicy"; ++ ++ // policy mappings extension fields ++ public final static String TOKEN_MAPPINGS = "mappings"; ++ public final static String TOKEN_MAP = "map"; ++ public final static String TOKEN_ISSUER_DOMAIN_POLICY = "issuerDomainPolicy"; ++ public final static String TOKEN_SUBJECT_DOMAIN_POLICY = "subjectDomainPolicy"; ++ ++ // subject directory attribute fields ++ public final static String TOKEN_ATTRIBUTES = "Attributes"; ++ public final static String TOKEN_ATTRIBUTE = "Attribute"; ++ public final static String TOKEN_VALUES = "Values"; ++ ++ // field values ++ public final static String TOKEN_NOT_SET = "notSet"; ++ public final static String TOKEN_NONE = "none"; ++ ++ public final static String TOKEN_CACHE_NOT_AVAILABLE = "cacheNotAvailable"; ++ public final static String TOKEN_CACHE_IS_EMPTY = "cacheIsEmpty"; ++ ++ //Tokens should have blank_space as trailer ++ static final Object[][] contents = { ++ { TOKEN_CERTIFICATE, "Certificate: " }, ++ { TOKEN_DATA, "Data: " }, ++ { TOKEN_VERSION, "Version: " }, ++ { TOKEN_SERIAL, "Serial Number: " }, ++ { TOKEN_SIGALG, "Signature Algorithm: " }, ++ { TOKEN_ISSUER, "Issuer: " }, ++ { TOKEN_VALIDITY, "Validity: " }, ++ { TOKEN_NOT_BEFORE, "Not Before: " }, ++ { TOKEN_NOT_AFTER, "Not After: " }, ++ { TOKEN_SUBJECT, "Subject: " }, ++ { TOKEN_SPKI, "Subject Public Key Info: " }, ++ { TOKEN_ALGORITHM, "Algorithm: " }, ++ { TOKEN_PUBLIC_KEY, "Public Key: " }, ++ { TOKEN_PUBLIC_KEY_MODULUS, "Public Key Modulus: " }, ++ { TOKEN_PUBLIC_KEY_EXPONENT, "Exponent: " }, ++ { TOKEN_EXTENSIONS, "Extensions: " }, ++ { TOKEN_SIGNATURE, "Signature: " }, ++ { TOKEN_YES, "yes " }, ++ { TOKEN_NO, "no " }, ++ { TOKEN_IDENTIFIER, "Identifier: " }, ++ { TOKEN_CRITICAL, "Critical: " }, ++ { TOKEN_VALUE, "Value: " }, ++ { TOKEN_KEY_TYPE, "Key Type " }, ++ { TOKEN_CERT_TYPE, "Netscape Certificate Type " }, ++ { TOKEN_SKI, "Subject Key Identifier " }, ++ { TOKEN_AKI, "Authority Key Identifier " }, ++ { TOKEN_ACCESS_DESC, "Access Description: " }, ++ { TOKEN_OCSP_NOCHECK, "OCSP NoCheck: " }, ++ { TOKEN_EXTENDED_KEY_USAGE, "Extended Key Usage: " }, ++ { TOKEN_PRIVATE_KEY_USAGE, "Private Key Usage: " }, ++ { TOKEN_PRESENCE_SERVER, "Presence Server: " }, ++ { TOKEN_AIA, "Authority Info Access: " }, ++ { TOKEN_CERT_POLICIES, "Certificate Policies: " }, ++ { TOKEN_SIA, "Subject Info Access: " }, ++ { TOKEN_KEY_USAGE, "Key Usage: " }, ++ { KeyUsageExtension.DIGITAL_SIGNATURE, "Digital Signature " }, ++ { KeyUsageExtension.NON_REPUDIATION, "Non Repudiation " }, ++ { KeyUsageExtension.KEY_ENCIPHERMENT, "Key Encipherment " }, ++ { KeyUsageExtension.DATA_ENCIPHERMENT, "Data Encipherment " }, ++ { KeyUsageExtension.KEY_AGREEMENT, "Key Agreement " }, ++ { KeyUsageExtension.KEY_CERTSIGN, "Key CertSign " }, ++ { KeyUsageExtension.CRL_SIGN, "Crl Sign " }, ++ { KeyUsageExtension.ENCIPHER_ONLY, "Encipher Only " }, ++ { KeyUsageExtension.DECIPHER_ONLY, "Decipher Only " }, ++ { TOKEN_CERT_USAGE, "Certificate Usage: " }, ++ { NSCertTypeExtension.SSL_CLIENT, "SSL Client " }, ++ { NSCertTypeExtension.SSL_SERVER, "SSL Server " }, ++ { NSCertTypeExtension.EMAIL, "Secure Email " }, ++ { NSCertTypeExtension.OBJECT_SIGNING, "Object Signing " }, ++ { NSCertTypeExtension.SSL_CA, "SSL CA " }, ++ { NSCertTypeExtension.EMAIL_CA, "Secure Email CA " }, ++ { NSCertTypeExtension.OBJECT_SIGNING_CA, "ObjectSigning CA " }, ++ { TOKEN_KEY_ID, "Key Identifier: " }, ++ { TOKEN_AUTH_NAME, "Authority Name: " }, ++ { TOKEN_CRL, "Certificate Revocation List: " }, ++ { TOKEN_THIS_UPDATE, "This Update: " }, ++ { TOKEN_NEXT_UPDATE, "Next Update: " }, ++ { TOKEN_REVOKED_CERTIFICATES, "Revoked Certificates: " }, ++ { TOKEN_REVOCATION_DATE, "Revocation Date: " }, ++ { TOKEN_REVOCATION_REASON, "Revocation Reason " }, ++ { TOKEN_REASON, "Reason: " }, ++ { TOKEN_BASIC_CONSTRAINTS, "Basic Constraints " }, ++ { TOKEN_NAME_CONSTRAINTS, "Name Constraints " }, ++ { TOKEN_NSC_COMMENT, "Netscape Comment " }, ++ { TOKEN_IS_CA, "Is CA: " }, ++ { TOKEN_PATH_LEN, "Path Length Constraint: " }, ++ { TOKEN_PATH_LEN_UNLIMITED, "UNLIMITED" }, ++ { TOKEN_PATH_LEN_UNDEFINED, "UNDEFINED" }, ++ { TOKEN_PATH_LEN_INVALID, "INVALID" }, ++ { TOKEN_CRL_NUMBER, "CRL Number " }, ++ { TOKEN_NUMBER, "Number: " }, ++ { TOKEN_DELTA_CRL_INDICATOR, "Delta CRL Indicator " }, ++ { TOKEN_BASE_CRL_NUMBER, "Base CRL Number: " }, ++ { TOKEN_CERT_SCOPE_OF_USE, "Certificate Scope of Use " }, ++ { TOKEN_SCOPE_OF_USE, "Scope of Use: " }, ++ { TOKEN_PORT, "Port: " }, ++ { TOKEN_ISSUER_ALT_NAME, "Issuer Alternative Name " }, ++ { TOKEN_ISSUER_NAMES, "Issuer Names: " }, ++ { TOKEN_SUBJECT_ALT_NAME, "Subject Alternative Name " }, ++ { TOKEN_DECODING_ERROR, "Decoding Error" }, ++ { TOKEN_FRESHEST_CRL_EXT, "Freshest CRL " }, ++ { TOKEN_INHIBIT_ANY_POLICY_EXT, "Inhibit Any-Policy " }, ++ { TOKEN_SKIP_CERTS, "Skip Certs: " }, ++ { TOKEN_CRL_DP_EXT, "CRL Distribution Points " }, ++ { TOKEN_CRLDP_NUMPOINTS, "Number of Points: " }, ++ { TOKEN_CRLDP_POINTN, "Point " }, ++ { TOKEN_CRLDP_DISTPOINT, "Distribution Point: " }, ++ { TOKEN_CRLDP_REASONS, "Reason Flags: " }, ++ { TOKEN_CRLDP_CRLISSUER, "CRL Issuer: " }, ++ { TOKEN_ISSUING_DIST_POINT, "Issuing Distribution Point " }, ++ { TOKEN_DIST_POINT_NAME, "Distribution Point: " }, ++ { TOKEN_FULL_NAME, "Full Name: " }, ++ { TOKEN_RELATIVE_NAME, "Name Relative To CRL Issuer: " }, ++ { TOKEN_ONLY_USER_CERTS, "Only Contains User Certificates: " }, ++ { TOKEN_ONLY_CA_CERTS, "Only Contains CA Certificates: " }, ++ { TOKEN_ONLY_SOME_REASONS, "Only Some Reasons: " }, ++ { TOKEN_INDIRECT_CRL, "Indirect CRL: " }, ++ { TOKEN_INVALIDITY_DATE, "Invalidity Date " }, ++ { TOKEN_DATE_OF_INVALIDITY, "Invalidity Date: " }, ++ { TOKEN_CERTIFICATE_ISSUER, "Certificate Issuer " }, ++ { TOKEN_HOLD_INSTRUCTION, "Hold Instruction Code " }, ++ { TOKEN_HOLD_INSTRUCTION_CODE, "Hold Instruction Code: " }, ++ { TOKEN_POLICY_CONSTRAINTS, "Policy Constraints " }, ++ { TOKEN_INHIBIT_POLICY_MAPPING, "Inhibit Policy Mapping: " }, ++ { TOKEN_REQUIRE_EXPLICIT_POLICY, "Require Explicit Policy: " }, ++ { TOKEN_POLICY_MAPPINGS, "Policy Mappings " }, ++ { TOKEN_MAPPINGS, "Mappings: " }, ++ { TOKEN_MAP, "Map " }, ++ { TOKEN_ISSUER_DOMAIN_POLICY, "Issuer Domain Policy: " }, ++ { TOKEN_SUBJECT_DOMAIN_POLICY, "Subject Domain Policy: " }, ++ { TOKEN_SUBJECT_DIR_ATTR, "Subject Directory Attributes " }, ++ { TOKEN_ATTRIBUTES, "Attributes:" }, ++ { TOKEN_ATTRIBUTE, "Attribute " }, ++ { TOKEN_VALUES, "Values: " }, ++ { TOKEN_NOT_SET, "not set" }, ++ { TOKEN_NONE, "none" }, ++ { TOKEN_CACHE_NOT_AVAILABLE, "CRL cache is not available. " }, ++ { TOKEN_CACHE_IS_EMPTY, "CRL cache is empty. " }, ++ }; ++ ++} +diff --git a/org/mozilla/jss/netscape/security/util/PrintableCharset.java b/org/mozilla/jss/netscape/security/util/PrintableCharset.java +new file mode 100644 +index 00000000..53777001 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/PrintableCharset.java +@@ -0,0 +1,46 @@ ++package org.mozilla.jss.netscape.security.util; ++ ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.CharsetEncoder; ++ ++public class PrintableCharset extends Charset { ++ ++ public PrintableCharset() { ++ super("ASN.1-Printable", null); ++ } ++ ++ public static boolean isPrintableChar(char c) { ++ if ((c < 'A' || c > 'Z') && ++ (c < 'a' || c > 'z') && ++ (c < '0' || c > '9') && ++ (c != ' ') && ++ (c != '\'') && ++ (c != '(') && ++ (c != ')') && ++ (c != '+') && ++ (c != ',') && ++ (c != '-') && ++ (c != '.') && ++ (c != '/') && ++ (c != ':') && ++ (c != '=') && ++ (c != '?')) { ++ return false; ++ } else { ++ return true; ++ } ++ } ++ ++ public boolean contains(Charset cs) { ++ return false; ++ } ++ ++ public CharsetDecoder newDecoder() { ++ return new PrintableCharsetDecoder(this); ++ } ++ ++ public CharsetEncoder newEncoder() { ++ return new PrintableCharsetEncoder(this); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/PrintableCharsetDecoder.java b/org/mozilla/jss/netscape/security/util/PrintableCharsetDecoder.java +new file mode 100644 +index 00000000..3c680a0e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/PrintableCharsetDecoder.java +@@ -0,0 +1,69 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.CoderResult; ++import java.nio.charset.CodingErrorAction; ++ ++/** ++ * Converts bytes in ASN.1 PrintableString character set to PrintableString ++ * characters. ++ * ++ * @author Lily Hsiao ++ * @author Slava Galperin ++ */ ++ ++public class PrintableCharsetDecoder extends CharsetDecoder { ++ ++ public PrintableCharsetDecoder(Charset cs) { ++ super(cs, 1, 1); ++ } ++ ++ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { ++ ++ while (true) { ++ ++ if (in.remaining() < 1) ++ return CoderResult.UNDERFLOW; ++ ++ in.mark(); ++ byte b = in.get(); ++ char c = (char) (b & 0x7f); ++ ++ if (CodingErrorAction.REPORT == unmappableCharacterAction() && ++ !PrintableCharset.isPrintableChar(c)) { ++ /* ++ "bug" fix for 359010 ++ return CoderResult.unmappableForLength(1); ++ */ ++ continue; ++ } ++ ++ if (out.remaining() < 1) { ++ in.reset(); ++ return CoderResult.OVERFLOW; ++ } ++ ++ out.put(c); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/PrintableCharsetEncoder.java b/org/mozilla/jss/netscape/security/util/PrintableCharsetEncoder.java +new file mode 100644 +index 00000000..f527020b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/PrintableCharsetEncoder.java +@@ -0,0 +1,71 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetEncoder; ++import java.nio.charset.CoderResult; ++import java.nio.charset.CodingErrorAction; ++ ++/** ++ * Converts characters in ASN.1 PrintableString character set to PrintableString ++ * bytes. ++ * ++ * @author Lily Hsiao ++ * @author Slava Galperin ++ */ ++ ++public class PrintableCharsetEncoder extends CharsetEncoder { ++ ++ public PrintableCharsetEncoder(Charset cs) { ++ super(cs, 1, 1); ++ } ++ ++ /* ++ * Converts an array of Unicode characters into an array of PrintableString ++ * bytes and returns the conversion result. ++ * @param in input character buffer to convert. ++ * @param out byte buffer to store output. ++ * @return encoding result. ++ */ ++ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { ++ ++ while (true) { ++ ++ if (in.remaining() < 1) ++ return CoderResult.UNDERFLOW; ++ ++ in.mark(); ++ char c = in.get(); ++ ++ if (CodingErrorAction.REPORT == unmappableCharacterAction() && ++ !PrintableCharset.isPrintableChar(c)) { ++ return CoderResult.unmappableForLength(1); ++ } ++ ++ if (out.remaining() < 1) { ++ in.reset(); ++ return CoderResult.OVERFLOW; ++ } ++ ++ out.put((byte) (c & 0x7f)); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/PubKeyPrettyPrint.java b/org/mozilla/jss/netscape/security/util/PubKeyPrettyPrint.java +new file mode 100644 +index 00000000..fde659ae +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/PubKeyPrettyPrint.java +@@ -0,0 +1,123 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.security.InvalidKeyException; ++import java.security.PublicKey; ++import java.util.Locale; ++import java.util.ResourceBundle; ++ ++import org.mozilla.jss.netscape.security.provider.RSAPublicKey; ++import org.mozilla.jss.netscape.security.x509.X509Key; ++ ++/** ++ * This class will display the certificate content in predefined ++ * format. ++ * ++ * @author Jack Pan-Chen ++ * @author Andrew Wnuk ++ * @version $Revision$, $Date$ ++ */ ++public class PubKeyPrettyPrint { ++ ++ /*========================================================== ++ * variables ++ *==========================================================*/ ++ private X509Key mX509Key = null; ++ private PrettyPrintFormat pp = null; ++ ++ /*========================================================== ++ * constructors ++ *==========================================================*/ ++ ++ public PubKeyPrettyPrint(PublicKey key) { ++ if (key instanceof X509Key) ++ mX509Key = (X509Key) key; ++ ++ pp = new PrettyPrintFormat(":"); ++ } ++ ++ /*========================================================== ++ * public methods ++ *==========================================================*/ ++ ++ /** ++ * This method return string representation of the certificate ++ * in predefined format using specified client local. I18N Support. ++ * ++ * @param clientLocale Locale to be used for localization ++ * @return string representation of the certificate ++ */ ++ public String toString(Locale clientLocale, int indentSize, int lineLen) { ++ ++ if (mX509Key != null) ++ return X509toString(clientLocale, indentSize, lineLen); ++ else ++ return null; ++ } ++ ++ public String X509toString(Locale clientLocale, int indentSize, int lineLen) { ++ ++ //get I18N resources ++ ResourceBundle resource = ResourceBundle.getBundle( ++ PrettyPrintResources.class.getName()); ++ ++ StringBuffer sb = new StringBuffer(); ++ ++ try { ++ String alg = mX509Key.getAlgorithm(); ++ ++ //XXX I18N Algorithm Name ? ++ sb.append(pp.indent(indentSize) + resource.getString( ++ PrettyPrintResources.TOKEN_ALGORITHM) + ++ alg + " - " + ++ mX509Key.getAlgorithmId().getOID().toString() + "\n"); ++ ++ if (alg.equals("RSA")) { ++ ++ RSAPublicKey rsakey = new RSAPublicKey(mX509Key.getEncoded()); ++ ++ sb.append(pp.indent(indentSize) + resource.getString( ++ PrettyPrintResources.TOKEN_PUBLIC_KEY) + "\n"); ++ sb.append(pp.indent(indentSize + 4) + resource.getString( ++ PrettyPrintResources.TOKEN_PUBLIC_KEY_EXPONENT) + ++ rsakey.getPublicExponent().toInt() + "\n"); ++ sb.append(pp.indent(indentSize + 4) + resource.getString( ++ PrettyPrintResources.TOKEN_PUBLIC_KEY_MODULUS) + ++ "(" + rsakey.getKeySize() + " bits) :\n"); ++ sb.append(pp.toHexString( ++ rsakey.getModulus().toByteArray(), ++ indentSize + 8, lineLen)); ++ } else { ++ ++ // DSAPublicKey is more complicated to decode, since ++ // the DSAParams (PQG) is not fully decoded. ++ // So, we just print the entire public key blob ++ ++ sb.append(pp.indent(indentSize) + resource.getString( ++ PrettyPrintResources.TOKEN_PUBLIC_KEY) + "\n"); ++ sb.append(pp.toHexString(mX509Key.getKey(), indentSize + 4, lineLen)); ++ } ++ ++ } catch(InvalidKeyException e){ ++ e.printStackTrace(); ++ } ++ ++ return sb.toString(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/UniversalCharset.java b/org/mozilla/jss/netscape/security/util/UniversalCharset.java +new file mode 100644 +index 00000000..3d2b2a03 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/UniversalCharset.java +@@ -0,0 +1,24 @@ ++package org.mozilla.jss.netscape.security.util; ++ ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.CharsetEncoder; ++ ++public class UniversalCharset extends Charset { ++ ++ public UniversalCharset() { ++ super("ASN.1-Universal", null); ++ } ++ ++ public boolean contains(Charset cs) { ++ return false; ++ } ++ ++ public CharsetDecoder newDecoder() { ++ return new UniversalCharsetDecoder(this); ++ } ++ ++ public CharsetEncoder newEncoder() { ++ return new UniversalCharsetEncoder(this); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/UniversalCharsetDecoder.java b/org/mozilla/jss/netscape/security/util/UniversalCharsetDecoder.java +new file mode 100644 +index 00000000..d1df9a47 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/UniversalCharsetDecoder.java +@@ -0,0 +1,98 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.CoderResult; ++import java.nio.charset.CodingErrorAction; ++ ++/** ++ * Converts bytes in ASN.1 UniversalString character set to UniversalString ++ * characters. ++ * ++ * @author Lily Hsiao ++ * @author Slava Galperin ++ */ ++ ++public class UniversalCharsetDecoder extends CharsetDecoder { ++ ++ public UniversalCharsetDecoder(Charset cs) { ++ super(cs, 0.25f, 1); ++ } ++ ++ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { ++ ++ while (true) { ++ // XXX we do not know what to do with truly UCS-4 characters here ++ // we also assumed network byte order ++ ++ if (in.remaining() < 4) ++ return CoderResult.UNDERFLOW; ++ ++ in.mark(); ++ byte b0 = in.get(); ++ byte b1 = in.get(); ++ byte b2 = in.get(); ++ byte b3 = in.get(); ++ ++ if (CodingErrorAction.REPORT == unmappableCharacterAction() && ++ !((b0 == 0 && b1 == 0) || (b2 == 0 && b3 == 0))) { ++ return CoderResult.unmappableForLength(4); ++ } ++ ++ char c; ++ if (b2 == 0 && b3 == 0) { ++ // Try to be a bit forgiving. If the byte order is ++ // reversed, we still try handle it. ++ ++ // Sample Date Set (1): ++ // 0000000 f 0 \0 \0 213 0 \0 \0 S 0 \0 \0 ++ // 0000014 ++ ++ // Sample Date Set (2): ++ // 0000000 w \0 \0 \0 w \0 \0 \0 w \0 \0 \0 . \0 \0 \0 ++ // 0000020 ( \0 \0 \0 t \0 \0 \0 o \0 \0 \0 b \0 \0 \0 ++ // 0000040 e \0 \0 \0 | \0 \0 \0 n \0 \0 \0 o \0 \0 \0 ++ // 0000060 t \0 \0 \0 t \0 \0 \0 o \0 \0 \0 b \0 \0 \0 ++ // 0000100 e \0 \0 \0 ) \0 \0 \0 . \0 \0 \0 c \0 \0 \0 ++ // 0000120 o \0 \0 \0 m \0 \0 \0 ++ // 0000130 ++ c = (char) (((b1 << 8) & 0xff00) + (b0 & 0x00ff)); ++ ++ } else { // (b0 == 0 && b1 == 0) ++ // This should be the right order. ++ // ++ // 0000000 0000 00c4 0000 0064 0000 006d 0000 0069 ++ // 0000020 0000 006e 0000 0020 0000 0051 0000 0041 ++ // 0000040 ++ ++ c = (char) (((b2 << 8) & 0xff00) + (b3 & 0x00ff)); ++ } ++ ++ if (out.remaining() < 1) { ++ in.reset(); ++ return CoderResult.OVERFLOW; ++ } ++ ++ out.put(c); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/UniversalCharsetEncoder.java b/org/mozilla/jss/netscape/security/util/UniversalCharsetEncoder.java +new file mode 100644 +index 00000000..1dc6e25c +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/UniversalCharsetEncoder.java +@@ -0,0 +1,68 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetEncoder; ++import java.nio.charset.CoderResult; ++ ++/** ++ * Converts characters in ASN.1 UniversalString character set to UniversalString ++ * bytes. ++ * ++ * @author Lily Hsiao ++ * @author Slava Galperin ++ */ ++ ++public class UniversalCharsetEncoder extends CharsetEncoder { ++ ++ public UniversalCharsetEncoder(Charset cs) { ++ super(cs, 4, 4, new byte[] { 0, 0, 0, 0 }); ++ } ++ ++ /* ++ * Converts an array of Unicode characters into an array of UniversalString ++ * bytes and returns the conversion result. ++ * @param in input character buffer to convert. ++ * @param out byte buffer to store output. ++ * @return encoding result. ++ */ ++ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { ++ ++ while (true) { ++ ++ if (in.remaining() < 1) ++ return CoderResult.UNDERFLOW; ++ ++ in.mark(); ++ char c = in.get(); ++ ++ if (out.remaining() < 4) { ++ in.reset(); ++ return CoderResult.OVERFLOW; ++ } ++ ++ out.put((byte) 0); ++ out.put((byte) 0); ++ out.put((byte) ((c >> 8) & 0xff)); ++ out.put((byte) (c & 0xff)); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/Utils.java b/org/mozilla/jss/netscape/security/util/Utils.java +new file mode 100644 +index 00000000..f9692315 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/Utils.java +@@ -0,0 +1,356 @@ ++// --- 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 org.mozilla.jss.netscape.security.util; ++ ++import java.io.BufferedReader; ++import java.io.BufferedWriter; ++import java.io.ByteArrayOutputStream; ++import java.io.File; ++import java.io.FileReader; ++import java.io.FileWriter; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.io.OutputStreamWriter; ++import java.io.PrintWriter; ++import java.net.InetAddress; ++import java.net.UnknownHostException; ++import java.text.SimpleDateFormat; ++import java.util.Date; ++import java.util.StringTokenizer; ++import java.util.Vector; ++ ++import org.apache.commons.codec.binary.Base64; ++ ++public class Utils { ++ /** ++ * Checks if this is NT. ++ */ ++ public static boolean isNT() { ++ return File.separator.equals("\\"); ++ } ++ ++ public static boolean isUnix() { ++ return File.separator.equals("/"); ++ } ++ ++ public static boolean exec(String cmd) { ++ 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 (IOException e) { ++ e.printStackTrace(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } ++ return false; ++ } ++ ++ public static String SpecialURLDecode(String s) { ++ if (s == null) ++ return null; ++ ByteArrayOutputStream out = new ByteArrayOutputStream(s.length()); ++ ++ for (int i = 0; i < s.length(); i++) { ++ int c = s.charAt(i); ++ ++ if (c == '+') { ++ out.write(' '); ++ } else if (c == '#') { ++ int c1 = Character.digit(s.charAt(++i), 16); ++ int c2 = Character.digit(s.charAt(++i), 16); ++ ++ out.write((char) (c1 * 16 + c2)); ++ } else { ++ out.write(c); ++ } ++ } // end for ++ return out.toString(); ++ } ++ ++ public static byte[] SpecialDecode(String s) { ++ if (s == null) ++ return null; ++ ByteArrayOutputStream out = new ByteArrayOutputStream(s.length()); ++ ++ for (int i = 0; i < s.length(); i++) { ++ int c = s.charAt(i); ++ ++ if (c == '+') { ++ out.write(' '); ++ } else if (c == '#') { ++ int c1 = Character.digit(s.charAt(++i), 16); ++ int c2 = Character.digit(s.charAt(++i), 16); ++ ++ out.write((char) (c1 * 16 + c2)); ++ } else { ++ out.write(c); ++ } ++ } // end for ++ return out.toByteArray(); ++ } ++ ++ public static String SpecialEncode(byte data[]) { ++ StringBuffer sb = new StringBuffer(); ++ for (int i = 0; i < data.length; i++) { ++ sb.append("%"); ++ if ((data[i] & 0xff) < 16) { ++ sb.append("0"); ++ } ++ sb.append(Integer.toHexString((data[i] & 0xff))); ++ } ++ return sb.toString().toUpperCase(); ++ } ++ ++ public static void checkHost(String hostname) throws UnknownHostException { ++ InetAddress.getByName(hostname); ++ } ++ ++ public static void copy(String orig, String dest) throws Exception { ++ BufferedReader in = null; ++ PrintWriter out = null; ++ try { ++ in = new BufferedReader(new FileReader(orig)); ++ out = new PrintWriter( ++ new BufferedWriter(new FileWriter(dest))); ++ String line = ""; ++ while (in.ready()) { ++ line = in.readLine(); ++ if (line != null) ++ out.println(line); ++ } ++ } catch (Exception ee) { ++ ee.printStackTrace(); ++ throw ee; ++ } finally { ++ if (in != null) { ++ try { ++ in.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ if (out != null) { ++ out.close(); ++ } ++ } ++ } ++ ++ public static void copyStream(InputStream in, OutputStream out) throws IOException { ++ byte[] buf = new byte[4096]; ++ int len; ++ ++ while ((len = in.read(buf)) != -1) { ++ out.write(buf, 0, len); ++ } ++ } ++ ++ public static void copyStream(BufferedReader in, OutputStreamWriter out) throws IOException { ++ char[] buf = new char[4096]; ++ int len; ++ ++ while ((len = in.read(buf)) != -1) { ++ out.write(buf, 0, len); ++ } ++ } ++ ++ /// Sorts an array of Strings. ++ // Java currently has no general sort function. Sorting Strings is ++ // common enough that it's worth making a special case. ++ public static void sortStrings(String[] strings) { ++ // Just does a bubblesort. ++ for (int i = 0; i < strings.length - 1; ++i) { ++ for (int j = i + 1; j < strings.length; ++j) { ++ if (strings[i].compareTo(strings[j]) > 0) { ++ String t = strings[i]; ++ ++ strings[i] = strings[j]; ++ strings[j] = t; ++ } ++ } ++ } ++ } ++ ++ /// Returns a date string formatted in Unix ls style - if it's within ++ // six months of now, Mmm dd hh:ss, else Mmm dd yyyy. ++ public static String lsDateStr(Date date) { ++ long dateTime = date.getTime(); ++ ++ if (dateTime == -1L) ++ return "------------"; ++ long nowTime = System.currentTimeMillis(); ++ SimpleDateFormat formatter = new SimpleDateFormat(); ++ ++ if (Math.abs(nowTime - dateTime) < 183L * 24L * 60L * 60L * 1000L) ++ formatter.applyPattern("MMM dd hh:ss"); ++ else ++ formatter.applyPattern("MMM dd yyyy"); ++ return formatter.format(date); ++ } ++ ++ /** ++ * compares contents two byte arrays returning true if exactly same. ++ */ ++ static public boolean byteArraysAreEqual(byte[] a, byte[] b) { ++ if (a.length != b.length) ++ return false; ++ for (int i = 0; i < a.length; i++) { ++ if (a[i] != b[i]) ++ return false; ++ } ++ return true; ++ } ++ ++ /** ++ * strips out double quotes around String parameter ++ * ++ * @param s the string potentially bracketed with double quotes ++ * @return string stripped of surrounding double quotes ++ */ ++ public static String stripQuotes(String s) { ++ if (s == null) { ++ return s; ++ } ++ ++ if ((s.startsWith("\"")) && (s.endsWith("\""))) { ++ return (s.substring(1, (s.length() - 1))); ++ } ++ ++ return s; ++ } ++ ++ /** ++ * returns an array of strings from a vector of Strings ++ * there'll be trouble if the Vector contains something other ++ * than just Strings ++ */ ++ public static String[] getStringArrayFromVector(Vector v) { ++ String s[] = new String[v.size()]; ++ ++ v.copyInto(s); ++ return s; ++ } ++ ++ /** ++ * Converts a byte array into a Base-64 encoded string. ++ * If the chunked option is true, the output will be split into ++ * multiple lines. Otherwise, the output will be a single line. ++ * ++ * @param bytes byte array ++ * @param chunked generate chunked output ++ * @return base-64 encoded data ++ */ ++ public static String base64encode(byte[] bytes, boolean chunked) { ++ if (chunked) { ++ return base64encodeMultiLine(bytes); ++ } else { ++ return base64encodeSingleLine(bytes); ++ } ++ } ++ ++ /** ++ * Converts a byte array into a multi-line Base-64 encoded string. ++ * Each line is at most 64-character long and terminated with CRLF. ++ * ++ * @param bytes byte array ++ * @param chunked TODO ++ * @return base-64 encoded data ++ */ ++ public static String base64encodeMultiLine(byte[] bytes) { ++ return new Base64(64).encodeToString(bytes); ++ } ++ ++ ++ /** ++ * Converts a byte array into a single-line Base-64 encoded string. ++ * The line is not terminated with CRLF. ++ * ++ * @param bytes byte array ++ * @return base-64 encoded data ++ */ ++ public static String base64encodeSingleLine(byte[] bytes) { ++ return new Base64().encodeToString(bytes); ++ } ++ ++ /** ++ * Converts a Base-64 encoded string into a byte array. ++ * ++ * @param string base-64 encoded data ++ * @return byte array ++ */ ++ public static byte[] base64decode(String string) { ++ return Base64.decodeBase64(string); ++ } ++ ++ /** ++ * Normalize B64 input String ++ * ++ * @pram string base-64 string ++ * @return normalized string ++ */ ++ public static String normalizeString(String string) { ++ if (string == null) { ++ return string; ++ } ++ ++ StringBuffer sb = new StringBuffer(); ++ StringTokenizer st = new StringTokenizer(string, "\r\n "); ++ ++ while (st.hasMoreTokens()) { ++ String nextLine = st.nextToken(); ++ nextLine = nextLine.trim(); ++ sb.append(nextLine); ++ } ++ return sb.toString(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/util/manifest.mn b/org/mozilla/jss/netscape/security/util/manifest.mn +new file mode 100644 +index 00000000..995d2082 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/util/manifest.mn +@@ -0,0 +1,9 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++CORE_DEPTH = ../../../../../.. ++ ++PACKAGE = org/mozilla/jss/netscape/security/util ++MODULE = jss +diff --git a/org/mozilla/jss/netscape/security/x509/ACertAttrSet.java b/org/mozilla/jss/netscape/security/x509/ACertAttrSet.java +new file mode 100755 +index 00000000..34598a41 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/ACertAttrSet.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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * A plain certattr set used by pkcs10 to parse an unknown attribute. ++ * ++ * @author Lily Hsiao ++ */ ++public class ACertAttrSet implements CertAttrSet { ++ ++ protected DerValue mDerValue = null; ++ ++ public ACertAttrSet(DerValue derValue) throws IOException { ++ mDerValue = derValue; ++ } ++ ++ public DerValue getDerValue() { ++ return mDerValue; ++ } ++ ++ /** ++ * Returns a short string describing this certificate attribute. ++ * ++ * @return value of this certificate attribute in ++ * printable form. ++ */ ++ public String toString() { ++ return "ACertAttrSet value " + (mDerValue == null ? "null" : "not null"); ++ } ++ ++ /** ++ * Encodes the attribute to the output stream in a format ++ * that can be parsed by the decode method. ++ * ++ * @param out the OutputStream to encode the attribute to. ++ * ++ * @exception CertificateException on encoding or validity errors. ++ * @exception IOException on other errors. ++ */ ++ public void encode(OutputStream out) ++ throws CertificateException, IOException { ++ mDerValue.encode((DerOutputStream) out); ++ } ++ ++ /** ++ * Decodes the attribute in the input stream. ++ * ++ * @param in the InputStream to read the encoded attribute from. ++ * ++ * @exception CertificateException on decoding or validity errors. ++ * @exception IOException on other errors. ++ */ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ throw new IOException("not supported"); ++ } ++ ++ /** ++ * Sets an attribute value within this CertAttrSet. ++ * ++ * @param name the name of the attribute (e.g. "x509.info.key") ++ * @param obj the attribute object. ++ * ++ * @exception CertificateException on attribute handling errors. ++ * @exception IOException on other errors. ++ */ ++ public void set(String name, Object obj) ++ throws CertificateException, IOException { ++ throw new IOException("not supported"); ++ } ++ ++ /** ++ * Gets an attribute value for this CertAttrSet. ++ * ++ * @param name the name of the attribute to return. ++ * ++ * @exception CertificateException on attribute handling errors. ++ * @exception IOException on other errors. ++ */ ++ public Object get(String name) ++ throws CertificateException, IOException { ++ throw new IOException("not supported"); ++ } ++ ++ /** ++ * Deletes an attribute value from this CertAttrSet. ++ * ++ * @param name the name of the attribute to delete. ++ * ++ * @exception CertificateException on attribute handling errors. ++ * @exception IOException on other errors. ++ */ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ throw new IOException("not supported"); ++ } ++ ++ /** ++ * Returns an enumeration of the names of the attributes existing within ++ * this attribute. ++ * ++ * @return an enumeration of the attribute names. ++ */ ++ public Enumeration getAttributeNames() { ++ return null; ++ } ++ ++ /** ++ * Returns the name (identifier) of this CertAttrSet. ++ * ++ * @return the name of this CertAttrSet. ++ */ ++ public String getName() { ++ return "Generic Extension"; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/AVA.java b/org/mozilla/jss/netscape/security/x509/AVA.java +new file mode 100644 +index 00000000..d3c76126 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/AVA.java +@@ -0,0 +1,311 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.UnsupportedCharsetException; ++ ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * X.500 Attribute-Value-Assertion (AVA): an attribute, as identified by ++ * some attribute ID, has some particular value. Values are as a rule ASN.1 ++ * printable strings. A conventional set of type IDs is recognized when ++ * parsing (and generating) RFC 1779 syntax strings. ++ * ++ *

++ * AVAs are components of X.500 relative names. Think of them as being individual fields of a database record. The ++ * attribute ID is how you identify the field, and the value is part of a particular record. ++ * ++ * @see X500Name ++ * @see RDN ++ * @see LdapDNStrConverter ++ * ++ * @version 1.14 ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++// public ... when RDN is public and X.500Names can be ++// constructed using RDNs, and all three classes are cleaner ++public final class AVA implements DerEncoder { ++ ObjectIdentifier oid; ++ DerValue value; ++ ++ /** ++ * Constructs an AVA from a Ldap DN string with one AVA component ++ * using the global default LdapDNStrConverter. ++ * ++ * @see LdapDNStrConverter ++ * @param avaString a Ldap DN string with one AVA component. ++ */ ++ public AVA(String avaString) ++ throws IOException { ++ AVA ava; ++ ava = LdapDNStrConverter.getDefault().parseAVA(avaString); ++ oid = ava.getOid(); ++ value = ava.getValue(); ++ } ++ ++ /** ++ * Like AVA(String) with a DER encoding order given for Directory Strings. ++ */ ++ public AVA(String avaString, byte[] tags) ++ throws IOException { ++ AVA ava; ++ ava = LdapDNStrConverter.getDefault().parseAVA(avaString, tags); ++ oid = ava.getOid(); ++ value = ava.getValue(); ++ } ++ ++ /** ++ * Constructs an AVA from a Ldap DN string containing one AVA ++ * component using the specified LdapDNStrConverter. ++ * ++ * @see LdapDNStrConverter ++ * @param avaString a Ldap DN string containing one AVA. ++ * @param ldapDNStrConverter a LdapDNStrConverter ++ */ ++ public AVA(String avaString, LdapDNStrConverter ldapDNStrConverter) ++ throws IOException { ++ AVA ava; ++ ava = ldapDNStrConverter.parseAVA(avaString); ++ oid = ava.getOid(); ++ value = ava.getValue(); ++ } ++ ++ /** ++ * Constructs an AVA from an OID and DerValue. ++ * ++ * @param type an ObjectIdentifier ++ * @param val a DerValue ++ */ ++ public AVA(ObjectIdentifier type, DerValue val) ++ throws IOException { ++ oid = type; ++ value = val; ++ } ++ ++ /** ++ * Constructs an AVA from an input stream of UTF8 bytes that form ++ * a Ldap DN string. Then parse the Ldap DN string using the global ++ * default LdapDNStrConverter.
++ * Parses an RFC 1779 style AVA string: CN=fee fie foe fum ++ * or perhaps with quotes. Not all defined AVA tags are supported; ++ * of current note are X.400 related ones (PRMD, ADMD, etc). ++ * ++ * This terminates at unescaped AVA separators ("+") or RDN ++ * separators (",", ";"), or DN terminators (">"), and removes ++ * cosmetic whitespace at the end of values. ++ * ++ * @see LdapDNStrConverter ++ * @param in the input stream. ++ */ ++ public AVA(InputStream in) throws IOException { ++ try { ++ // convert from UTF8 bytes to java string then parse it. ++ byte[] buffer = new byte[in.available()]; ++ in.read(buffer); ++ ++ Charset charset = Charset.forName("UTF-8"); ++ CharsetDecoder decoder = charset.newDecoder(); ++ ++ CharBuffer charBuffer = decoder.decode(ByteBuffer.wrap(buffer)); ++ ++ AVA a = LdapDNStrConverter.getDefault().parseAVA(charBuffer.toString()); ++ oid = a.getOid(); ++ value = a.getValue(); ++ ++ } catch (UnsupportedCharsetException e) { ++ throw new IOException("UTF8 encoding not supported", e); ++ } ++ } ++ ++ /** ++ * Constructs an AVA from a Der Input Stream. ++ * ++ * @param in the Der Input Stream. ++ */ ++ public AVA(DerInputStream in) throws IOException { ++ DerValue assertion = in.getDerValue(); ++ ++ /* ++ * Individual attribute value assertions are SEQUENCE of two values. ++ * That'd be a "struct" outside of ASN.1. ++ */ ++ if (assertion.tag != DerValue.tag_Sequence) ++ throw new CertParseError("X500 AVA, not a sequence"); ++ ++ ObjectIdentifier o = assertion.data.getOID(); ++ oid = X500NameAttrMap.getDefault().getOid(o); ++ if (oid == null) { ++ // NSCP #329837 ++ // if this OID is not recongized in our map (table), ++ // it is fine. we just store it as regular OID. ++ oid = o; ++ } ++ value = assertion.data.getDerValue(); ++ ++ if (assertion.data.available() != 0) ++ throw new CertParseError("AVA, extra bytes = " ++ + assertion.data.available()); ++ } ++ ++ // other public methods. ++ ++ /** ++ * Returns true if another AVA has the same OID and DerValue. ++ * ++ * @param other the other AVA. ++ * @return ture iff other AVA has same oid and value. ++ */ ++ public boolean equals(AVA other) { ++ return oid.equals(other.oid) && value.equals(other.value); ++ } ++ ++ /** ++ * Compares the AVA with an Object, returns true if the object is ++ * an AVA and has the same OID and value. ++ * ++ * @param other the other object. ++ * @return true iff other object is an AVA and has same oid and value. ++ */ ++ public boolean equals(Object other) { ++ if (other instanceof AVA) ++ return equals((AVA) other); ++ else ++ return false; ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + ((oid == null) ? 0 : oid.hashCode()); ++ result = prime * result + ((value == null) ? 0 : value.hashCode()); ++ return result; ++ } ++ ++ /** ++ * Encodes the AVA to a Der output stream. ++ * AVAs are encoded as a SEQUENCE of two elements. ++ * ++ * @param out The Der output stream. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ derEncode(out); ++ } ++ ++ /** ++ * DER encode this object onto an output stream. ++ * Implements the DerEncoder interface. ++ * ++ * @param out ++ * the output stream on which to write the DER encoding. ++ * ++ * @exception IOException on encoding error. ++ */ ++ public void derEncode(OutputStream out) throws IOException { ++ try (DerOutputStream tmp2 = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ tmp.putOID(oid); ++ value.encode(tmp); ++ tmp2.write(DerValue.tag_Sequence, tmp); ++ out.write(tmp2.toByteArray()); ++ } ++ } ++ ++ /** ++ * Returns a Ldap DN string with one AVA component using ++ * the global default LdapDNStrConverter. ++ * ++ * @return a Ldap DN string ++ * @exception IOException if an error occurs during conversion. ++ * @see LdapDNStrConverter ++ */ ++ public String toLdapDNString() ++ throws IOException { ++ LdapDNStrConverter v = LdapDNStrConverter.getDefault(); ++ return v.encodeAVA(this); ++ } ++ ++ /** ++ * Returns a Ldap DN string with one AVA component using the specified ++ * LdapDNStrConverter. ++ * ++ * @return a Ldap DN string ++ * @param ldapDNStrConverter a Ldap DN String Converter ++ * @exception IOException if an error occurs during the conversion. ++ * @see LdapDNStrConverter ++ */ ++ public String toLdapDNString(LdapDNStrConverter ldapDNStrConverter) ++ throws IOException { ++ return ldapDNStrConverter.encodeAVA(this); ++ } ++ ++ /** ++ * Returns a Ldap DN string with the AVA component using the global ++ * default LdapDNStrConverter, or null if an error occurs in conversion. ++ * ++ * @return a Ldap DN string containing the AVA, or null if an ++ * error occurs in the conversion. ++ */ ++ public String toString() { ++ String s; ++ try { ++ // NOTE that a LdapDNString is returned here to match the ++ // original source from sun. Could also return the raw value ++ // (before Ldap escaping) here. ++ s = toLdapDNString(); ++ } catch (IOException e) { ++ return null; ++ } ++ return s; ++ } ++ ++ /** ++ * Returns the OID in the AVA. ++ * ++ * @return the ObjectIdentifier in this AVA. ++ */ ++ public ObjectIdentifier getOid() { ++ return oid; ++ } ++ ++ /** ++ * Returns the value in this AVA as a DerValue ++ * ++ * @return attribute value in this AVA. ++ */ ++ public DerValue getValue() { ++ return value; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/AVAValueConverter.java b/org/mozilla/jss/netscape/security/x509/AVAValueConverter.java +new file mode 100644 +index 00000000..3438cb88 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/AVAValueConverter.java +@@ -0,0 +1,86 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Interface for classes that convert a attribute value string to a ++ * DER encoded ASN.1 value and vice versa. ++ * The converters are associated with attribute types, such as ++ * directory string, ia5string, etc. ++ * ++ *

++ * For example, to convert a string, such as an organization name for the "O" attribute to a DerValue, the "O" attribute ++ * is mapped to the DirStrConverter which is used to convert the organization name to a DER encoded Directory String ++ * which is a DerValue of a ASN.1 PrintableString, T.61String or UniversalString for the organization name. ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ */ ++ ++public interface AVAValueConverter { ++ /** ++ * Converts a string to a DER encoded attribute value. ++ * ++ * @param valueString An AVA value string not encoded in any form. ++ * ++ * @return A DerValue object. ++ * ++ * @exception IOException if an error occurs during the conversion. ++ */ ++ public DerValue getValue(String valueString) ++ throws IOException; ++ ++ /** ++ * Converts a string to a DER encoded attribute value. ++ * Specify the order of DER tags to use if more than one encoding is ++ * possible. Currently Directory Strings can have different order ++ * for backwards compatibility. By 2003 all should be UTF8String. ++ * ++ * @param valueString An AVA value string not encoded in any form. ++ * ++ * @return A DerValue object. ++ * ++ * @exception IOException if an error occurs during the conversion. ++ */ ++ public DerValue getValue(String valueString, byte[] tags) ++ throws IOException; ++ ++ /** ++ * Converts a BER encoded value to a DER encoded attribute value. ++ * ++ * @param berStream A byte array of the BER encoded AVA value. ++ * @return A DerValue object. ++ */ ++ public DerValue getValue(byte[] berStream) ++ throws IOException; ++ ++ /** ++ * Converts a DER encoded value to a string, not encoded in any form. ++ * ++ * @param avaValue A DerValue object. ++ * ++ * @return A string for the value or null if it can't be converted. ++ * ++ * @exception IOException if an error occurs during the conversion. ++ */ ++ public String getAsString(DerValue avaValue) ++ throws IOException; ++} +diff --git a/org/mozilla/jss/netscape/security/x509/AlgIdDSA.java b/org/mozilla/jss/netscape/security/x509/AlgIdDSA.java +new file mode 100644 +index 00000000..c706209f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/AlgIdDSA.java +@@ -0,0 +1,215 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.math.BigInteger; ++import java.security.ProviderException; ++import java.security.interfaces.DSAParams; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class identifies DSS/DSA Algorithm variants, which are distinguished ++ * by using different algorithm parameters P, Q, G. It uses the ++ * NIST/IETF standard DER encoding. These are used to implement the Digital ++ * Signature Standard (DSS), FIPS 186. ++ * ++ *

++ * NOTE: At this time, DSS/DSA Algorithm IDs must always ++ * include these parameters. Use of DSS/DSA in modes where parameters are ++ * either implicit (e.g. a default applicable to a site or a larger scope), ++ * or are derived from some Certificate Authority's DSS certificate, is ++ * not currently supported. ++ * ++ * @version 1.31 ++ * @author David Brownell ++ */ ++public final class AlgIdDSA extends AlgorithmId implements DSAParams { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 5978220691806461631L; ++ /* ++ * The three unsigned integer parameters. ++ */ ++ private BigInteger p, q, g; ++ ++ /** Returns the DSS/DSA parameter "P" */ ++ public BigInteger getP() { ++ return p; ++ } ++ ++ /** Returns the DSS/DSA parameter "Q" */ ++ public BigInteger getQ() { ++ return q; ++ } ++ ++ /** Returns the DSS/DSA parameter "G" */ ++ public BigInteger getG() { ++ return g; ++ } ++ ++ AlgIdDSA(DerValue val) throws IOException { ++ super(val.getOID()); ++ } ++ ++ /** ++ * Construct an AlgIdDSA from an X.509 encoded byte array. ++ */ ++ public AlgIdDSA(byte[] encodedAlg) throws IOException { ++ super(new DerValue(encodedAlg).getOID()); ++ } ++ ++ /** ++ * Constructs a DSS/DSA Algorithm ID from unsigned integers that ++ * define the algorithm parameters. Those integers are encoded ++ * as big-endian byte arrays. ++ * ++ * @param p the DSS/DSA paramter "P" ++ * @param q the DSS/DSA paramter "Q" ++ * @param g the DSS/DSA paramter "G" ++ */ ++ public AlgIdDSA(byte p[], byte q[], byte g[]) ++ throws IOException { ++ this(new BigInteger(1, p), ++ new BigInteger(1, q), ++ new BigInteger(1, g)); ++ } ++ ++ /** ++ * Constructs a DSS/DSA Algorithm ID from numeric parameters. ++ * ++ * @param p the DSS/DSA paramter "P" ++ * @param q the DSS/DSA paramter "Q" ++ * @param g the DSS/DSA paramter "G" ++ */ ++ public AlgIdDSA(BigInteger p, BigInteger q, BigInteger g) { ++ super(DSA_oid); ++ ++ try { ++ this.p = p; ++ this.q = q; ++ this.g = g; ++ initializeParams(); ++ ++ } catch (IOException e) { ++ /* this should not happen */ ++ throw new ProviderException("Construct DSS/DSA Algorithm ID"); ++ } ++ } ++ ++ /** ++ * Returns "DSA", indicating the Digital Signature Algorithm (DSA) as ++ * defined by the Digital Signature Standard (DSS), FIPS 186. ++ */ ++ public String getName() { ++ return "DSA"; ++ } ++ ++ /* ++ * For algorithm IDs which haven't been created from a DER encoded ++ * value, "params" must be created. ++ */ ++ private void initializeParams() ++ throws IOException { ++ try (DerOutputStream out = new DerOutputStream()) { ++ out.putInteger(new BigInt(p.toByteArray())); ++ out.putInteger(new BigInt(q.toByteArray())); ++ out.putInteger(new BigInt(g.toByteArray())); ++ params = new DerValue(DerValue.tag_Sequence, out.toByteArray()); ++ } ++ } ++ ++ /** ++ * Parses algorithm parameters P, Q, and G. They're found ++ * in the "params" member, which never needs to be changed. ++ */ ++ protected void decodeParams() ++ throws IOException { ++ if (params == null || params.tag != DerValue.tag_Sequence) ++ throw new IOException("DSA alg parsing error"); ++ ++ params.data.reset(); ++ ++ this.p = params.data.getInteger().toBigInteger(); ++ this.q = params.data.getInteger().toBigInteger(); ++ this.g = params.data.getInteger().toBigInteger(); ++ ++ if (params.data.available() != 0) ++ throw new IOException("AlgIdDSA params, extra=" + ++ params.data.available()); ++ } ++ ++ /* ++ * Returns a formatted string describing the parameters. ++ */ ++ public String toString() { ++ return paramsToString(); ++ } ++ ++ /* ++ * Returns a string describing the parameters. ++ */ ++ protected String paramsToString() { ++ return "\n p:\n" + (new BigInt(p)).toString() + ++ "\n q:\n" + (new BigInt(q)).toString() + ++ "\n g:\n" + (new BigInt(g)).toString() + ++ "\n"; ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + ((g == null) ? 0 : g.hashCode()); ++ result = prime * result + ((p == null) ? 0 : p.hashCode()); ++ result = prime * result + ((q == null) ? 0 : q.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (!super.equals(obj)) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ AlgIdDSA other = (AlgIdDSA) obj; ++ if (g == null) { ++ if (other.g != null) ++ return false; ++ } else if (!g.equals(other.g)) ++ return false; ++ if (p == null) { ++ if (other.p != null) ++ return false; ++ } else if (!p.equals(other.p)) ++ return false; ++ if (q == null) { ++ if (other.q != null) ++ return false; ++ } else if (!q.equals(other.q)) ++ return false; ++ return true; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/AlgorithmId.java b/org/mozilla/jss/netscape/security/x509/AlgorithmId.java +new file mode 100644 +index 00000000..5ea7f5e5 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/AlgorithmId.java +@@ -0,0 +1,805 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.security.AlgorithmParameters; ++import java.security.NoSuchAlgorithmException; ++ ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * This class identifies algorithms, such as cryptographic transforms, each ++ * of which may be associated with parameters. Instances of this base class ++ * are used when this runtime environment has no special knowledge of the ++ * algorithm type, and may also be used in other cases. Equivalence is ++ * defined according to OID and (where relevant) parameters. ++ * ++ *

++ * Subclasses may be used, for example when when the algorithm ID has associated parameters which some code (e.g. code ++ * using public keys) needs to have parsed. Two examples of such algorithms are Diffie-Hellman key exchange, and the ++ * Digital Signature Standard Algorithm (DSS/DSA). ++ * ++ *

++ * The OID constants defined in this class correspond to some widely used algorithms, for which conventional string ++ * names have been defined. This class is not a general repository for OIDs, or for such string names. Note that the ++ * mappings between algorithm IDs and algorithm names is not one-to-one. ++ * ++ * @version 1.70 ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class AlgorithmId implements Serializable, DerEncoder { ++ ++ /** use serialVersionUID from JDK 1.1. for interoperability */ ++ private static final long serialVersionUID = 7205873507486557157L; ++ ++ /** ++ * The object identitifer being used for this algorithm. ++ */ ++ private ObjectIdentifier algid = null; ++ ++ // The (parsed) parameters ++ private AlgorithmParameters algParams; ++ ++ /** ++ * Parameters for this algorithm. These are stored in unparsed ++ * DER-encoded form; subclasses can be made to automaticaly parse ++ * them so there is fast access to these parameters. ++ */ ++ protected DerValue params = null; ++ ++ protected String paramsString = null; ++ ++ public AlgorithmParameters getParameters() { ++ return this.algParams; ++ } ++ ++ public String getParametersString() { ++ return this.paramsString; ++ } ++ ++ public void setParametersString(String paramStr) { ++ ++ this.paramsString = paramStr; ++ } ++ ++ /** ++ * Returns one of the algorithm IDs most commonly associated ++ * with this algorithm name. ++ * ++ * @param algname the name being used ++ * @exception NoSuchAlgorithmException on error. ++ */ ++ public static AlgorithmId get(String algname) ++ throws NoSuchAlgorithmException { ++ ObjectIdentifier oid = algOID(algname); ++ ++ if (oid == null) ++ throw new NoSuchAlgorithmException("unrecognized algorithm name: " + algname); ++ ++ return new AlgorithmId(oid); ++ } ++ ++ /** ++ * Parse (unmarshal) an ID from a DER sequence input value. This form ++ * parsing might be used when expanding a value which has already been ++ * partially unmarshaled as a set or sequence member. ++ * ++ * @exception IOException on error. ++ * @param val the input value, which contains the algid and, if ++ * there are any parameters, those parameters. ++ * @return an ID for the algorithm. If the system is configured ++ * appropriately, this may be an instance of a class ++ * with some kind of special support for this algorithm. ++ * In that case, you may "narrow" the type of the ID. ++ */ ++ public static AlgorithmId parse(DerValue val) ++ throws IOException { ++ if (val.tag != DerValue.tag_Sequence) ++ throw new IOException("algid parse error, not a sequence"); ++ ++ /* ++ * Get the algorithm ID and any parameters. ++ */ ++ ObjectIdentifier algid; ++ DerValue params; ++ DerInputStream in = val.toDerInputStream(); ++ ++ algid = in.getOID(); ++ if (in.available() == 0) ++ params = null; ++ else { ++ params = in.getDerValue(); ++ if (params.tag == DerValue.tag_Null) ++ params = null; ++ } ++ ++ /* ++ * Figure out what class (if any) knows about this oid's ++ * parameters. Make one, and give it the data to decode. ++ */ ++ AlgorithmId alg = new AlgorithmId(algid, params); ++ if (params != null) ++ alg.decodeParams(); ++ ++ /* ++ * Set the raw params string in case ++ * higher level code might want the info ++ */ ++ ++ String paramStr = null; ++ ++ if (params != null) { ++ paramStr = params.toString(); ++ } ++ ++ alg.setParametersString(paramStr); ++ ++ return alg; ++ } ++ ++ public static AlgorithmId parse(byte[] val) ++ throws IOException { ++ return null; ++ } ++ ++ /** ++ * Constructs a parameterless algorithm ID. ++ * ++ * @param oid the identifier for the algorithm ++ */ ++ public AlgorithmId(ObjectIdentifier oid) { ++ algid = oid; ++ } ++ ++ private AlgorithmId(ObjectIdentifier oid, DerValue params) ++ throws IOException { ++ this.algid = oid; ++ this.params = params; ++ if (this.params != null) ++ decodeParams(); ++ } ++ ++ /** ++ * Constructs an algorithm ID which will be initialized ++ * separately, for example by deserialization. ++ * ++ * @deprecated use one of the other constructors. ++ */ ++ public AlgorithmId() { ++ } ++ ++ protected void decodeParams() throws IOException { ++ try { ++ this.algParams = AlgorithmParameters.getInstance ++ (this.algid.toString()); ++ } catch (NoSuchAlgorithmException e) { ++ /* ++ * This algorithm parameter type is not supported, so we cannot ++ * parse the parameters. ++ */ ++ this.algParams = null; ++ return; ++ } ++ // Decode (parse) the parameters ++ this.algParams.init(this.params.toByteArray()); ++ } ++ ++ /** ++ * Marshal a DER-encoded "AlgorithmID" sequence on the DER stream. ++ */ ++ public final void encode(DerOutputStream out) ++ throws IOException { ++ derEncode(out); ++ } ++ ++ /** ++ * DER encode this object onto an output stream. ++ * Implements the DerEncoder interface. ++ * ++ * @param out ++ * the output stream on which to write the DER encoding. ++ * ++ * @exception IOException on encoding error. ++ */ ++ public void derEncode(OutputStream out) throws IOException { ++ try (DerOutputStream tmp = new DerOutputStream()) { ++ DerOutputStream bytes = new DerOutputStream(); ++ bytes.putOID(algid); ++ ++ // omit parameter field for ECDSA ++ if (!algid.equals(sha224WithEC_oid) && ++ !algid.equals(sha256WithEC_oid) && ++ !algid.equals(sha384WithEC_oid) && ++ !algid.equals(sha512WithEC_oid)) { ++ if (params == null) { ++ bytes.putNull(); ++ } else ++ bytes.putDerValue(params); ++ } ++ ++ tmp.write(DerValue.tag_Sequence, bytes); ++ out.write(tmp.toByteArray()); ++ } ++ } ++ ++ // XXXX cleaning required ++ /** ++ * Returns the DER-encoded X.509 AlgorithmId as a byte array. ++ */ ++ public final byte[] encode() throws IOException { ++ try (DerOutputStream out = new DerOutputStream()) { ++ DerOutputStream bytes = new DerOutputStream(); ++ bytes.putOID(algid); ++ ++ // omit parameter field for ECDSA ++ if (!algid.equals(sha224WithEC_oid) && ++ !algid.equals(sha256WithEC_oid) && ++ !algid.equals(sha384WithEC_oid) && ++ !algid.equals(sha512WithEC_oid)) { ++ if (params == null) { ++ bytes.putNull(); ++ } else ++ bytes.putDerValue(params); ++ } ++ ++ out.write(DerValue.tag_Sequence, bytes); ++ return out.toByteArray(); ++ } ++ } ++ ++ /** ++ * Returns list of signing algorithms for a key algorithm such as ++ * RSA or DSA. ++ */ ++ public static String[] getSigningAlgorithms(AlgorithmId alg) { ++ ObjectIdentifier algOid = alg.getOID(); ++ //System.out.println("Key Alg oid "+algOid.toString()); ++ if (algOid.equals(DSA_oid) || algOid.equals(DSA_OIW_oid)) { ++ return DSA_SIGNING_ALGORITHMS; ++ } else if (algOid.equals(RSA_oid) || algOid.equals(RSAEncryption_oid)) { ++ return RSA_SIGNING_ALGORITHMS; ++ } else if (algOid.equals(ANSIX962_EC_Public_Key_oid) || algOid.equals(ANSIX962_SHA1_With_EC_oid)) { ++ return EC_SIGNING_ALGORITHMS; ++ } else { ++ return null; ++ } ++ } ++ ++ /* ++ * Translates from some common algorithm names to the ++ * OID with which they're usually associated ... this mapping ++ * is the reverse of the one below, except in those cases ++ * where synonyms are supported or where a given algorithm ++ * is commonly associated with multiple OIDs. ++ */ ++ private static ObjectIdentifier algOID(String name) { ++ // Digesting algorithms ++ ++ if (name.equals("MD5")) ++ return AlgorithmId.MD5_oid; ++ if (name.equals("MD2")) ++ return AlgorithmId.MD2_oid; ++ if (name.equals("SHA") || name.equals("SHA1") ++ || name.equals("SHA-1")) ++ return AlgorithmId.SHA_oid; ++ if (name.equals("SHA256") || name.equals("SHA-256")) ++ return AlgorithmId.SHA256_oid; ++ if (name.equals("SHA512") || name.equals("SHA-512")) ++ return AlgorithmId.SHA512_oid; ++ ++ // Various public key algorithms ++ ++ if (name.equals("RSA")) ++ return AlgorithmId.RSA_oid; ++ ++ if (name.equals("RSAEncryption")) ++ return AlgorithmId.RSAEncryption_oid; ++ if (name.equals("Diffie-Hellman") || name.equals("DH")) ++ return AlgorithmId.DH_oid; ++ if (name.equals("DSA")) ++ return AlgorithmId.DSA_oid; ++ ++ // Common signature types ++ ++ if (name.equals("SHA1withEC") || name.equals("SHA1/EC") ++ || name.equals("1.2.840.10045.4.1")) ++ return AlgorithmId.sha1WithEC_oid; ++ if (name.equals("SHA224withEC") || name.equals("SHA224/EC") ++ || name.equals("1.2.840.10045.4.3.1")) ++ return AlgorithmId.sha224WithEC_oid; ++ if (name.equals("SHA256withEC") || name.equals("SHA256/EC") ++ || name.equals("1.2.840.10045.4.3.2")) ++ return AlgorithmId.sha256WithEC_oid; ++ if (name.equals("SHA384withEC") || name.equals("SHA384/EC") ++ || name.equals("1.2.840.10045.4.3.3")) ++ return AlgorithmId.sha384WithEC_oid; ++ if (name.equals("SHA512withEC") || name.equals("SHA512/EC") ++ || name.equals("1.2.840.10045.4.3.4")) ++ return AlgorithmId.sha512WithEC_oid; ++ if (name.equals("SHA1withRSA") || name.equals("SHA1/RSA") ++ || name.equals("1.2.840.113549.1.1.5")) ++ return AlgorithmId.sha1WithRSAEncryption_oid; ++ if (name.equals("SHA256withRSA") || name.equals("SHA256/RSA") ++ || name.equals("1.2.840.113549.1.1.11")) ++ return AlgorithmId.sha256WithRSAEncryption_oid; ++ if (name.equals("SHA384withRSA") || name.equals("SHA384/RSA") ++ || name.equals("1.2.840.113549.1.1.12")) ++ return AlgorithmId.sha384WithRSAEncryption_oid; ++ if (name.equals("SHA512withRSA") || name.equals("SHA512/RSA") ++ || name.equals("1.2.840.113549.1.1.13")) ++ return AlgorithmId.sha512WithRSAEncryption_oid; ++ if (name.equals("MD5withRSA") || name.equals("MD5/RSA")) ++ return AlgorithmId.md5WithRSAEncryption_oid; ++ if (name.equals("MD2withRSA") || name.equals("MD2/RSA")) ++ return AlgorithmId.md2WithRSAEncryption_oid; ++ if (name.equals("SHAwithDSA") || name.equals("SHA1withDSA") ++ || name.equals("SHA/DSA") || name.equals("SHA1/DSA")) ++ return AlgorithmId.sha1WithDSA_oid; ++ ++ return null; ++ } ++ ++ /* ++ * For the inevitable cases where key or signature types are not ++ * configured in an environment which encounters such keys or ++ * signatures, we still attempt to provide user-friendly names ++ * for some of the most common algorithms. Subclasses can of ++ * course override getName(). ++ * ++ * Wherever possible, the names are those defined by the IETF. ++ * Such names are noted below. ++ */ ++ private String algName() { ++ // Common message digest algorithms ++ ++ if (algid.equals(AlgorithmId.MD5_oid)) ++ return "MD5"; // RFC 1423 ++ if (algid.equals(AlgorithmId.MD2_oid)) ++ return "MD2"; // RFC 1423 ++ if (algid.equals(AlgorithmId.SHA_oid)) ++ return "SHA"; ++ if (algid.equals(AlgorithmId.SHA256_oid)) ++ return "SHA256"; ++ if (algid.equals(AlgorithmId.SHA384_oid)) ++ return "SHA384"; ++ if (algid.equals(AlgorithmId.SHA512_oid)) ++ return "SHA512"; ++ ++ // Common key types ++ ++ if (algid.equals(AlgorithmId.ANSIX962_EC_Public_Key_oid)) ++ return "EC"; ++ if (algid.equals(AlgorithmId.RSAEncryption_oid) ++ || algid.equals(AlgorithmId.RSA_oid)) ++ return "RSA"; ++ if (algid.equals(AlgorithmId.DH_oid) ++ || algid.equals(AlgorithmId.DH_PKIX_oid)) ++ return "Diffie-Hellman"; ++ if (algid.equals(AlgorithmId.DSA_oid) ++ || algid.equals(AlgorithmId.DSA_OIW_oid)) ++ return "DSA"; ++ ++ // Common signature types ++ ++ if (algid.equals(AlgorithmId.sha1WithEC_oid)) ++ return "SHA1withEC"; ++ if (algid.equals(AlgorithmId.sha256WithEC_oid)) ++ return "SHA256withEC"; ++ if (algid.equals(AlgorithmId.sha384WithEC_oid)) ++ return "SHA384withEC"; ++ if (algid.equals(AlgorithmId.sha512WithEC_oid)) ++ return "SHA512withEC"; ++ if (algid.equals(AlgorithmId.md5WithRSAEncryption_oid)) ++ return "MD5withRSA"; ++ if (algid.equals(AlgorithmId.md2WithRSAEncryption_oid)) ++ return "MD2withRSA"; ++ if (algid.equals(AlgorithmId.sha1WithRSAEncryption_oid)) ++ return "SHA1withRSA"; ++ if (algid.equals(AlgorithmId.sha256WithRSAEncryption_oid)) ++ return "SHA256withRSA"; ++ if (algid.equals(AlgorithmId.sha384WithRSAEncryption_oid)) ++ return "SHA384withRSA"; ++ if (algid.equals(AlgorithmId.sha512WithRSAEncryption_oid)) ++ return "SHA512withRSA"; ++ if (algid.equals(AlgorithmId.sha1WithDSA_oid) ++ || algid.equals(AlgorithmId.sha1WithDSA_OIW_oid) ++ || algid.equals(AlgorithmId.shaWithDSA_OIW_oid)) ++ return "SHA1withDSA"; ++ ++ // default returns a dot-notation ID ++ ++ return "OID." + algid.toString(); ++ } ++ ++ /** ++ * Returns the ISO OID for this algorithm. This is usually converted ++ * to a string and used as part of an algorithm name, for example ++ * "OID.1.3.14.3.2.13" style notation. Use the getName call when you do not need to ensure cross-system ++ * portability ++ * of algorithm names, or need a user friendly name. ++ */ ++ final public ObjectIdentifier getOID() { ++ return algid; ++ } ++ ++ /** ++ * Returns a name for the algorithm which may be more intelligible ++ * to humans than the algorithm's OID, but which won't necessarily ++ * be comprehensible on other systems. For example, this might ++ * return a name such as "MD5withRSA" for a signature algorithm on ++ * some systems. It also returns names like "OID.1.2.3.4", when ++ * no particular name for the algorithm is known. ++ */ ++ public String getName() { ++ return algName(); ++ } ++ ++ /** ++ * Returns a string describing the algorithm and its parameters. ++ */ ++ public String toString() { ++ return (algName() + paramsToString()); ++ } ++ ++ /** ++ * Returns the DER encoded parameter, which can then be ++ * used to initialize java.security.AlgorithmParamters. ++ * ++ * @return DER encoded parameters, or null not present. ++ */ ++ public byte[] getEncodedParams() throws IOException { ++ if (params == null) ++ return null; ++ else ++ return params.toByteArray(); ++ } ++ ++ /** ++ * Provides a human-readable description of the algorithm parameters. ++ * This may be redefined by subclasses which parse those parameters. ++ */ ++ protected String paramsToString() { ++ if (params == null) { ++ return ""; ++ } else if (algParams != null) { ++ return algParams.toString(); ++ } else { ++ return ", params unparsed"; ++ } ++ } ++ ++ /** ++ * Returns true iff the argument indicates the same algorithm ++ * with the same parameters. ++ */ ++ public boolean equals(AlgorithmId other) { ++ if (!algid.equals(other.algid)) ++ return false; ++ else if (params == null && other.params == null) ++ return true; ++ else if (params == null) ++ return false; ++ else ++ return params.equals(other.params); ++ } ++ ++ /** ++ * Compares this AlgorithmID to another. If algorithm parameters are ++ * available, they are compared. Otherwise, just the object IDs ++ * for the algorithm are compared. ++ * ++ * @param other preferably an AlgorithmId, else an ObjectIdentifier ++ */ ++ public boolean equals(Object other) { ++ if (other instanceof AlgorithmId) ++ return equals((AlgorithmId) other); ++ else if (other instanceof ObjectIdentifier) ++ return equals((ObjectIdentifier) other); ++ else ++ return false; ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + ((algParams == null) ? 0 : algParams.hashCode()); ++ result = prime * result + ((algid == null) ? 0 : algid.hashCode()); ++ result = prime * result + ((params == null) ? 0 : params.hashCode()); ++ result = prime * result + ((paramsString == null) ? 0 : paramsString.hashCode()); ++ return result; ++ } ++ ++ /** ++ * Compares two algorithm IDs for equality. Returns true iff ++ * they are the same algorithm, ignoring algorithm parameters. ++ */ ++ public final boolean equals(ObjectIdentifier id) { ++ return algid.equals(id); ++ } ++ ++ ++ ++ /*****************************************************************/ ++ ++ /* ++ * HASHING ALGORITHMS ++ */ ++ private static final int MD2_data[] = { 1, 2, 840, 113549, 2, 2 }; ++ private static final int MD5_data[] = { 1, 2, 840, 113549, 2, 5 }; ++ // sha = { 1, 3, 14, 3, 2, 18 }; ++ private static final int SHA1_OIW_data[] = { 1, 3, 14, 3, 2, 26 }; ++ private static final int SHA256_data[] = { 2, 16, 840, 1, 101, 3, 4, 2, 1 }; ++ private static final int SHA384_data[] = { 2, 16, 840, 1, 101, 3, 4, 2, 2 }; ++ private static final int SHA512_data[] = { 2, 16, 840, 1, 101, 3, 4, 2, 3 }; ++ ++ /** ++ * Algorithm ID for the MD2 Message Digest Algorthm, from RFC 1319. ++ * OID = 1.2.840.113549.2.2 ++ */ ++ public static final ObjectIdentifier MD2_oid = new ObjectIdentifier(MD2_data); ++ ++ /** ++ * Algorithm ID for the MD5 Message Digest Algorthm, from RFC 1321. ++ * OID = 1.2.840.113549.2.5 ++ */ ++ public static final ObjectIdentifier MD5_oid = new ObjectIdentifier(MD5_data); ++ ++ /** ++ * Algorithm ID for the SHA1 Message Digest Algorithm, from FIPS 180-1. ++ * This is sometimes called "SHA", though that is often confusing since ++ * many people refer to FIPS 180 (which has an error) as defining SHA. ++ * OID = 1.3.14.3.2.26 ++ */ ++ public static final ObjectIdentifier SHA_oid = new ObjectIdentifier(SHA1_OIW_data); ++ ++ public static final ObjectIdentifier SHA256_oid = new ObjectIdentifier(SHA256_data); ++ public static final ObjectIdentifier SHA384_oid = new ObjectIdentifier(SHA384_data); ++ ++ public static final ObjectIdentifier SHA512_oid = new ObjectIdentifier(SHA512_data); ++ ++ /* ++ * COMMON PUBLIC KEY TYPES ++ */ ++ private static final int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 }; ++ private static final int DH_PKIX_data[] = { 1, 2, 840, 10046, 2, 1 }; ++ private static final int DSA_OIW_data[] = { 1, 3, 14, 3, 2, 12 }; ++ private static final int DSA_PKIX_data[] = { 1, 2, 840, 10040, 4, 1 }; ++ private static final int RSA_data[] = { 1, 2, 5, 8, 1, 1 }; ++ private static final int RSAEncryption_data[] = ++ { 1, 2, 840, 113549, 1, 1, 1 }; ++ private static final int ANSI_X962_public_key_data[] = ++ { 1, 2, 840, 10045, 2, 1 }; ++ private static final int ANSI_X962_sha1_with_ec_data[] = ++ { 1, 2, 840, 10045, 4, 1 }; ++ ++ public static final ObjectIdentifier ANSIX962_EC_Public_Key_oid = new ObjectIdentifier(ANSI_X962_public_key_data); ++ public static final ObjectIdentifier ANSIX962_SHA1_With_EC_oid = new ObjectIdentifier(ANSI_X962_sha1_with_ec_data); ++ ++ /* ++ * Note the preferred OIDs are named simply with no "OIW" or ++ * "PKIX" in them, even though they may point to data from these ++ * specs; e.g. SHA_oid, DH_oid, DSA_oid, SHA1WithDSA_oid... ++ */ ++ /** ++ * Algorithm ID for Diffie Hellman Key agreement, from PKCS #3. ++ * Parameters include public values P and G, and may optionally specify ++ * the length of the private key X. Alternatively, algorithm parameters ++ * may be derived from another source such as a Certificate Authority's ++ * certificate. ++ * OID = 1.2.840.113549.1.3.1 ++ */ ++ public static final ObjectIdentifier DH_oid = new ObjectIdentifier(DH_data); ++ ++ /** ++ * Algorithm ID for the Diffie Hellman Key Agreement (DH), from the ++ * IETF PKIX IPKI Part I. ++ * Parameters may include public values P and G. ++ * OID = 1.2.840.10046.2.1 ++ */ ++ public static final ObjectIdentifier DH_PKIX_oid = new ObjectIdentifier(DH_PKIX_data); ++ ++ /** ++ * Algorithm ID for the Digital Signing Algorithm (DSA), from the ++ * NIST OIW Stable Agreements part 12. ++ * Parameters may include public values P, Q, and G; or these may be ++ * derived from ++ * another source such as a Certificate Authority's certificate. ++ * OID = 1.3.14.3.2.12 ++ */ ++ public static final ObjectIdentifier DSA_OIW_oid = new ObjectIdentifier(DSA_OIW_data); ++ ++ /** ++ * Algorithm ID for the Digital Signing Algorithm (DSA), from the ++ * IETF PKIX IPKI Part I. ++ * Parameters may include public values P, Q, and G; or these may be ++ * derived from ++ * another source such as a Certificate Authority's certificate. ++ * OID = 1.2.840.10040.4.1 ++ */ ++ public static final ObjectIdentifier DSA_oid = new ObjectIdentifier(DSA_PKIX_data); ++ ++ /** ++ * Algorithm ID for RSA keys used for any purpose, as defined in X.509. ++ * The algorithm parameter is a single value, the number of bits in the ++ * public modulus. ++ * OID = 1.2.5.8.1.1 ++ */ ++ public static final ObjectIdentifier RSA_oid = new ObjectIdentifier(RSA_data); ++ ++ /** ++ * Algorithm ID for RSA keys used with RSA encryption, as defined ++ * in PKCS #1. There are no parameters associated with this algorithm. ++ * OID = 1.2.840.113549.1.1.1 ++ */ ++ public static final ObjectIdentifier RSAEncryption_oid = new ObjectIdentifier(RSAEncryption_data); ++ ++ /* ++ * COMMON SIGNATURE ALGORITHMS ++ */ ++ private static final int sha1WithEC_data[] = ++ { 1, 2, 840, 10045, 4, 1 }; ++ private static final int sha224WithEC_data[] = ++ { 1, 2, 840, 10045, 4, 3, 1 }; ++ private static final int sha256WithEC_data[] = ++ { 1, 2, 840, 10045, 4, 3, 2 }; ++ private static final int sha384WithEC_data[] = ++ { 1, 2, 840, 10045, 4, 3, 3 }; ++ private static final int sha512WithEC_data[] = ++ { 1, 2, 840, 10045, 4, 3, 4 }; ++ private static final int md2WithRSAEncryption_data[] = ++ { 1, 2, 840, 113549, 1, 1, 2 }; ++ private static final int md5WithRSAEncryption_data[] = ++ { 1, 2, 840, 113549, 1, 1, 4 }; ++ private static final int sha1WithRSAEncryption_data[] = ++ { 1, 2, 840, 113549, 1, 1, 5 }; ++ private static final int sha256WithRSAEncryption_data[] = ++ { 1, 2, 840, 113549, 1, 1, 11 }; ++ private static final int sha384WithRSAEncryption_data[] = ++ { 1, 2, 840, 113549, 1, 1, 12 }; ++ private static final int sha512WithRSAEncryption_data[] = ++ { 1, 2, 840, 113549, 1, 1, 13 }; ++ private static final int sha1WithRSAEncryption_OIW_data[] = ++ { 1, 3, 14, 3, 2, 29 }; ++ private static final int shaWithDSA_OIW_data[] = ++ { 1, 3, 14, 3, 2, 13 }; ++ private static final int sha1WithDSA_OIW_data[] = ++ { 1, 3, 14, 3, 2, 27 }; ++ private static final int dsaWithSHA1_PKIX_data[] = ++ { 1, 2, 840, 10040, 4, 3 }; ++ ++ public static final ObjectIdentifier sha1WithEC_oid = new ++ ObjectIdentifier(sha1WithEC_data); ++ ++ public static final ObjectIdentifier sha224WithEC_oid = new ++ ObjectIdentifier(sha224WithEC_data); ++ ++ public static final ObjectIdentifier sha256WithEC_oid = new ++ ObjectIdentifier(sha256WithEC_data); ++ ++ public static final ObjectIdentifier sha384WithEC_oid = new ++ ObjectIdentifier(sha384WithEC_data); ++ ++ public static final ObjectIdentifier sha512WithEC_oid = new ++ ObjectIdentifier(sha512WithEC_data); ++ ++ /** ++ * Identifies a signing algorithm where an MD2 digest is encrypted ++ * using an RSA private key; defined in PKCS #1. Use of this ++ * signing algorithm is discouraged due to MD2 vulnerabilities. ++ * OID = 1.2.840.113549.1.1.2 ++ */ ++ public static final ObjectIdentifier md2WithRSAEncryption_oid = new ++ ObjectIdentifier(md2WithRSAEncryption_data); ++ ++ /** ++ * Identifies a signing algorithm where an MD5 digest is ++ * encrypted using an RSA private key; defined in PKCS #1. ++ * OID = 1.2.840.113549.1.1.4 ++ */ ++ public static final ObjectIdentifier md5WithRSAEncryption_oid = new ++ ObjectIdentifier(md5WithRSAEncryption_data); ++ ++ /** ++ * The proper one for sha1/rsa ++ */ ++ public static final ObjectIdentifier sha1WithRSAEncryption_oid = new ++ ObjectIdentifier(sha1WithRSAEncryption_data); ++ ++ /** ++ * The proper one for sha256/rsa ++ */ ++ public static final ObjectIdentifier sha256WithRSAEncryption_oid = new ++ ObjectIdentifier(sha256WithRSAEncryption_data); ++ ++ /** ++ * The proper one for sha384/rsa ++ */ ++ public static final ObjectIdentifier sha384WithRSAEncryption_oid = new ++ ObjectIdentifier(sha384WithRSAEncryption_data); ++ ++ /** ++ * The proper one for sha512/rsa ++ */ ++ public static final ObjectIdentifier sha512WithRSAEncryption_oid = new ++ ObjectIdentifier(sha512WithRSAEncryption_data); ++ ++ /** ++ * Identifies a signing algorithm where an SHA1 digest is ++ * encrypted using an RSA private key; defined in NIST OIW. ++ * OID = 1.3.14.3.2.29 ++ */ ++ public static final ObjectIdentifier sha1WithRSAEncryption_OIW_oid = new ++ ObjectIdentifier(sha1WithRSAEncryption_OIW_data); ++ ++ /** ++ * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a ++ * SHA digest is signed using the Digital Signing Algorithm (DSA). ++ * This should not be used. ++ * OID = 1.3.14.3.2.13 ++ */ ++ public static final ObjectIdentifier shaWithDSA_OIW_oid = new ObjectIdentifier(shaWithDSA_OIW_data); ++ ++ /** ++ * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a ++ * SHA1 digest is signed using the Digital Signing Algorithm (DSA). ++ * OID = 1.3.14.3.2.27 ++ */ ++ public static final ObjectIdentifier sha1WithDSA_OIW_oid = new ObjectIdentifier(sha1WithDSA_OIW_data); ++ ++ /** ++ * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a ++ * SHA1 digest is signed using the Digital Signing Algorithm (DSA). ++ * OID = 1.2.840.10040.4.3 ++ */ ++ public static final ObjectIdentifier sha1WithDSA_oid = new ObjectIdentifier(dsaWithSHA1_PKIX_data); ++ ++ /** ++ * Supported signing algorithms for a DSA key. ++ */ ++ public static final String[] DSA_SIGNING_ALGORITHMS = new String[] ++ { "SHA1withDSA" }; ++ ++ /** ++ * Supported signing algorithms for a RSA key. ++ */ ++ public static final String[] RSA_SIGNING_ALGORITHMS = new String[] ++ { "SHA1withRSA", "SHA256withRSA", "SHA384withRSA", "SHA512withRSA", "MD5withRSA", "MD2withRSA" }; ++ ++ public static final String[] EC_SIGNING_ALGORITHMS = new String[] ++ { "SHA1withEC", "SHA256withEC", "SHA384withEC", "SHA512withEC" }; ++ ++ /** ++ * All supported signing algorithms. ++ */ ++ public static final String[] ALL_SIGNING_ALGORITHMS = new String[] ++ { ++ "SHA1withRSA", "MD5withRSA", "MD2withRSA", "SHA1withDSA", "SHA256withRSA", "SHA384withRSA", "SHA512withRSA", "SHA1withEC", ++ "SHA256withEC", "SHA384withEC", "SHA512withEC" }; ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/Attribute.java b/org/mozilla/jss/netscape/security/x509/Attribute.java +new file mode 100644 +index 00000000..f6deca5f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/Attribute.java +@@ -0,0 +1,327 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * An attribute, as identified by some attribute ID, has some particular values. ++ * Values are as a rule ASN.1 printable strings. A conventional set of type IDs ++ * is recognized when parsing. The following shows the syntax: ++ * ++ *

++ *
++ *    Attribute	::= SEQUENCE {
++ * type		AttributeType,
++ * 	value		SET OF AttributeValue
++ *              	-- at least one value is required --}
++ *
++ *    AttributeType	::= OBJECT IDENTIFIER
++ *
++ *    AttributeValue	::= ANY
++ *
++ * 
++ * ++ * Refer to draft-ietf-pkix-ipki-part1-11 for the support attributes listed on ++ * page 96 of the internet draft. The are listed here for easy reference: name, ++ * common name, surname, given name, initials, generation qualifier, dn qualifier, ++ * country name, locality name, state or province name, organization name, organization ++ * unit name, title, pkcs9 email. Not all the attributes are supported. Please check ++ * the X500NameAttrMap for defined attributes. ++ * ++ * @author Christine Ho ++ */ ++ ++public final class Attribute implements Serializable, DerEncoder { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -931486084625476764L; ++ //private variables ++ ObjectIdentifier oid; ++ Vector valueSet = new Vector(); ++ transient protected X500NameAttrMap attrMap; ++ ++ //========== CONSTRUCTOR ================================== ++ ++ /** ++ * Construct an attribute from attribute type and attribute value ++ * ++ * @param oid the object identifier of the attribute type ++ * @param value the value string ++ */ ++ public Attribute(ObjectIdentifier oid, String value) ++ throws IOException { ++ ++ //pre-condition verification ++ if ((oid == null) || (value == null)) ++ throw new IOException("Invalid Input - null passed"); ++ ++ attrMap = X500NameAttrMap.getDefault(); ++ this.oid = oid; ++ valueSet.addElement(value); ++ } ++ ++ /** ++ * Construct an attribute from attribute type and attribute values ++ * ++ * @param oid the object identifier of the attribute type ++ * @param values String value vector ++ */ ++ public Attribute(ObjectIdentifier oid, Vector values) ++ throws IOException { ++ ++ //pre-condition verification ++ if ((oid == null) || (values == null)) ++ throw new IOException("Invalid Input - null passed"); ++ ++ attrMap = X500NameAttrMap.getDefault(); ++ this.oid = oid; ++ ++ //copy the value into the valueSet list ++ Enumeration vals = values.elements(); ++ while (vals.hasMoreElements()) { ++ valueSet.addElement(vals.nextElement()); ++ } ++ } ++ ++ /** ++ * Construct an attribute from attribute type and attribute values ++ * ++ * @param oid attribute type string CN,OU,O,C,L,TITLE,ST,STREET,UID,MAIL,E,DC ++ * @param values String value vector ++ */ ++ public Attribute(String attr, Vector values) ++ throws IOException { ++ ++ //pre-condition verification ++ if ((attr == null) || (values == null)) ++ throw new IOException("Invalid Input - null passed"); ++ ++ ObjectIdentifier identifier = null; ++ try { ++ identifier = new ObjectIdentifier(attr); ++ } catch (Exception e) { ++ } ++ ++ ObjectIdentifier id = identifier; ++ if (identifier == null) { ++ attrMap = X500NameAttrMap.getDefault(); ++ id = attrMap.getOid(attr); ++ if (id == null) ++ throw new IOException("Attr is not supported - does not contain in attr map"); ++ } ++ this.oid = id; ++ ++ //copy the value into the valueSet list ++ Enumeration vals = values.elements(); ++ while (vals.hasMoreElements()) { ++ valueSet.addElement(vals.nextElement()); ++ } ++ } ++ ++ /** ++ * Construct an attribute from a der encoded object. This der ++ * der encoded value should represent the attribute object. ++ * ++ * @param value the attribute object in der encode form. ++ */ ++ public Attribute(DerValue val) ++ throws IOException { ++ ++ //pre-condition verification ++ if (val == null) ++ throw new IOException("Invalid Input - null passed"); ++ ++ attrMap = X500NameAttrMap.getDefault(); ++ ++ decodeThis(val); ++ ++ } ++ ++ //========== PUBLIC METHODS ================================== ++ ++ /** ++ * Returns the OID in the Attribute. ++ * ++ * @return the ObjectIdentifier in this Attribute. ++ */ ++ public ObjectIdentifier getOid() { ++ return oid; ++ } ++ ++ /** ++ * Returns enumeration of values in this attribute. ++ * ++ * @return Enumeration of values of this Attribute. ++ */ ++ public Enumeration getValues() { ++ if (valueSet == null) ++ return null; ++ return valueSet.elements(); ++ } ++ ++ /** ++ * Encodes the Attribute to a Der output stream. ++ * Attribute are encoded as a SEQUENCE of two elements. ++ * ++ * @param out The Der output stream. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ encodeThis(out); ++ } ++ ++ /** ++ * DER encode this object onto an output stream. ++ * Implements the DerEncoder interface. ++ * ++ * @param out ++ * the output stream on which to write the DER encoding. ++ * ++ * @exception IOException on encoding error. ++ */ ++ public void derEncode(OutputStream out) throws IOException { ++ encodeThis(out); ++ } ++ ++ /** ++ * Prints a string version of this extension. ++ */ ++ public String toString() { ++ String theoid = "Attribute: " + oid + "\n"; ++ StringBuffer values = new StringBuffer("Values: "); ++ Enumeration n = valueSet.elements(); ++ if (n.hasMoreElements()) { ++ values.append(n.nextElement()); ++ while (n.hasMoreElements()) ++ values.append("," + n.nextElement()); ++ } ++ return theoid + values.toString() + "\n"; ++ } ++ ++ //========== PRIVATE METHODS ================================== ++ ++ //encode the attribute object ++ private void encodeThis(OutputStream out) ++ throws IOException { ++ try (DerOutputStream tmp2 = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ tmp.putOID(oid); ++ encodeValueSet(tmp); ++ tmp2.write(DerValue.tag_Sequence, tmp); ++ out.write(tmp2.toByteArray()); ++ } ++ } ++ ++ //encode the attribute object ++ private void encodeValueSet(OutputStream out) ++ throws IOException { ++ try (DerOutputStream tmp2 = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ //get the attribute converter ++ AVAValueConverter converter = attrMap.getValueConverter(oid); ++ if (converter == null) { ++ converter = new GenericValueConverter(); ++ //throw new IOException("Converter not found: unsupported attribute type"); ++ } ++ ++ //loop through all the values and encode ++ Enumeration vals = valueSet.elements(); ++ while (vals.hasMoreElements()) { ++ String val = vals.nextElement(); ++ DerValue derobj = converter.getValue(val); ++ derobj.encode(tmp); ++ } ++ ++ tmp2.write(DerValue.tag_SetOf, tmp); ++ out.write(tmp2.toByteArray()); ++ } ++ } ++ ++ //decode the attribute object ++ private void decodeThis(DerValue val) ++ throws IOException { ++ ++ //pre-condition verification ++ if (val == null) { ++ throw new IOException("Invalid Input - null passed."); ++ } ++ ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for Attribute."); ++ } ++ ++ if (val.data.available() == 0) { ++ throw new IOException("No data available in " ++ + "passed DER encoded value."); ++ } ++ this.oid = val.data.getDerValue().getOID(); ++ ++ if (val.data.available() == 0) { ++ throw new IOException("Invalid encoding for Attribute - value missing"); ++ } ++ decodeValueSet(val.data.getDerValue()); ++ ++ if (this.oid == null) ++ throw new IOException("Invalid encoding for Attribute - OID missing"); ++ ++ } ++ ++ //decode the attribute value set ++ private void decodeValueSet(DerValue val) ++ throws IOException { ++ //pre-condition verification ++ if (val == null) { ++ throw new IOException("Invalid Input - null passed."); ++ } ++ ++ AVAValueConverter converter = attrMap.getValueConverter(this.oid); ++ if (converter == null) { ++ converter = new GenericValueConverter(); ++ //throw new IOException("Attribute is not supported - not in attr map"); ++ } ++ ++ if (val.tag != DerValue.tag_SetOf) { ++ throw new IOException("Invalid encoding for Attribute Value Set."); ++ } ++ ++ if (val.data.available() == 0) { ++ throw new IOException("No data available in " ++ + "passed DER encoded attribute value set."); ++ } ++ ++ //get the value set ++ while (val.data.available() != 0) { ++ DerValue value = val.data.getDerValue(); ++ valueSet.addElement(converter.getAsString(value)); ++ } ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/AuthorityKeyIdentifierExtension.java b/org/mozilla/jss/netscape/security/x509/AuthorityKeyIdentifierExtension.java +new file mode 100644 +index 00000000..cee776be +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/AuthorityKeyIdentifierExtension.java +@@ -0,0 +1,342 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class represents the Authority Key Identifier Extension. ++ * ++ *

++ * The authority key identifier extension provides a means of identifying the particular public key used to sign a ++ * certificate. This extension would be used where an issuer has multiple signing keys (either due to multiple ++ * concurrent key pairs or due to changeover). ++ *

++ * The ASN.1 syntax for this is: ++ * ++ *

++ * AuthorityKeyIdentifier ::= SEQUENCE {
++ *    keyIdentifier             [0] KeyIdentifier           OPTIONAL,
++ *    authorityCertIssuer       [1] GeneralNames            OPTIONAL,
++ *    authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL
++ * }
++ * KeyIdentifier ::= OCTET STRING
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.9 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class AuthorityKeyIdentifierExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -157913621972354170L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = ++ "x509.info.extensions.AuthorityKeyIdentifier"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "AuthorityKeyIdentifier"; ++ public static final String KEY_ID = "key_id"; ++ public static final String AUTH_NAME = "auth_name"; ++ public static final String SERIAL_NUMBER = "serial_number"; ++ ++ // Private data members ++ private static final byte TAG_ID = 0; ++ private static final byte TAG_NAMES = 1; ++ private static final byte TAG_SERIAL_NUM = 2; ++ ++ private KeyIdentifier id = null; ++ private GeneralNames names = null; ++ private SerialNumber serialNum = null; ++ ++ // Encode only the extension value ++ private void encodeThis() throws IOException { ++ try (DerOutputStream tmp = new DerOutputStream(); ++ DerOutputStream seq = new DerOutputStream()) { ++ ++ if (id != null) { ++ DerOutputStream tmp1 = new DerOutputStream(); ++ id.encode(tmp1); ++ tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_ID), tmp1); ++ } ++ try { ++ if (names != null) { ++ DerOutputStream tmp1 = new DerOutputStream(); ++ names.encode(tmp1); ++ tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, TAG_NAMES), tmp1); ++ } ++ } catch (Exception e) { ++ throw new IOException(e); ++ } ++ if (serialNum != null) { ++ DerOutputStream tmp1 = new DerOutputStream(); ++ serialNum.encode(tmp1); ++ tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_SERIAL_NUM), tmp1); ++ } ++ seq.write(DerValue.tag_Sequence, tmp); ++ this.extensionValue = seq.toByteArray(); ++ } ++ } ++ ++ /** ++ * Exposed critical parameter. 99/11/03 ++ */ ++ public AuthorityKeyIdentifierExtension(boolean critical, ++ KeyIdentifier kid, GeneralNames name, ++ SerialNumber sn) ++ throws IOException { ++ this.id = kid; ++ this.names = name; ++ this.serialNum = sn; ++ ++ this.extensionId = PKIXExtensions.AuthorityKey_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ /** ++ * The default constructor for this extension. Null parameters make ++ * the element optional (not present). ++ * ++ * @param id the KeyIdentifier associated with this extension. ++ * @param names the GeneralNames associated with this extension ++ * @param serialNum the CertificateSerialNumber associated with ++ * this extension. ++ * @exception IOException on error. ++ */ ++ public AuthorityKeyIdentifierExtension(KeyIdentifier kid, GeneralNames name, ++ SerialNumber sn) ++ throws IOException { ++ this.id = kid; ++ this.names = name; ++ this.serialNum = sn; ++ ++ this.extensionId = PKIXExtensions.AuthorityKey_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public AuthorityKeyIdentifierExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.AuthorityKey_Id; ++ this.critical = critical.booleanValue(); ++ ++ if (!(value instanceof byte[])) ++ throw new IOException("Illegal argument type"); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ System.arraycopy(value, 0, extValue, 0, len); ++ ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for " + ++ "AuthorityKeyIdentifierExtension."); ++ } ++ ++ // NB. this is always encoded with the IMPLICIT tag ++ // The checks only make sense if we assume implicit tagging, ++ // with explicit tagging the form is always constructed. ++ while (val.data.available() != 0) { ++ DerValue opt = val.data.getDerValue(); ++ ++ if (opt.isContextSpecific(TAG_ID) && !opt.isConstructed()) { ++ if (id != null) ++ throw new IOException("Duplicate KeyIdentifier in " + ++ "AuthorityKeyIdentifier."); ++ opt.resetTag(DerValue.tag_OctetString); ++ id = new KeyIdentifier(opt); ++ ++ } else if (opt.isContextSpecific(TAG_NAMES) && ++ opt.isConstructed()) { ++ if (names != null) ++ throw new IOException("Duplicate GeneralNames in " + ++ "AuthorityKeyIdentifier."); ++ try { ++ opt.resetTag(DerValue.tag_Sequence); ++ names = new GeneralNames(opt); ++ } catch (GeneralNamesException e) { ++ throw new IOException(e); ++ } ++ ++ } else if (opt.isContextSpecific(TAG_SERIAL_NUM) && ++ !opt.isConstructed()) { ++ if (serialNum != null) ++ throw new IOException("Duplicate SerialNumber in " + ++ "AuthorityKeyIdentifier."); ++ opt.resetTag(DerValue.tag_Integer); ++ serialNum = new SerialNumber(opt); ++ } else ++ throw new IOException("Invalid encoding of " + ++ "AuthorityKeyIdentifierExtension."); ++ } ++ } ++ ++ /** ++ * Return the object as a string. ++ */ ++ public String toString() { ++ String s = super.toString() + "AuthorityKeyIdentifier [\n"; ++ if (id != null) { ++ s += id.toString(); ++ } ++ if (names != null) { ++ s += names.toString() + "\n"; ++ } ++ if (serialNum != null) { ++ s += serialNum.toString() + "\n"; ++ } ++ return (s + "]\n"); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on error. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (this.extensionValue == null) { ++ extensionId = PKIXExtensions.AuthorityKey_Id; ++ critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(KEY_ID)) { ++ if (!(obj instanceof KeyIdentifier)) { ++ throw new IOException("Attribute value should be of " + ++ "type KeyIdentifier."); ++ } ++ id = (KeyIdentifier) obj; ++ } else if (name.equalsIgnoreCase(AUTH_NAME)) { ++ if (!(obj instanceof GeneralNames)) { ++ throw new IOException("Attribute value should be of " + ++ "type GeneralNames."); ++ } ++ names = (GeneralNames) obj; ++ } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { ++ if (!(obj instanceof SerialNumber)) { ++ throw new IOException("Attribute value should be of " + ++ "type SerialNumber."); ++ } ++ serialNum = (SerialNumber) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:AuthorityKeyIdentifier."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(KEY_ID)) { ++ return (id); ++ } else if (name.equalsIgnoreCase(AUTH_NAME)) { ++ return (names); ++ } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { ++ return (serialNum); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:AuthorityKeyIdentifier."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(KEY_ID)) { ++ id = null; ++ } else if (name.equalsIgnoreCase(AUTH_NAME)) { ++ names = null; ++ } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { ++ serialNum = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:AuthorityKeyIdentifier."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(KEY_ID); ++ elements.addElement(AUTH_NAME); ++ elements.addElement(SERIAL_NUMBER); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/BasicConstraintsExtension.java b/org/mozilla/jss/netscape/security/x509/BasicConstraintsExtension.java +new file mode 100644 +index 00000000..fc03a526 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/BasicConstraintsExtension.java +@@ -0,0 +1,296 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class represents the Basic Constraints Extension. ++ * ++ *

++ * The basic constraints extension identifies whether the subject of the certificate is a CA and how deep a ++ * certification path may exist through that CA. ++ * ++ *

++ * The ASN.1 syntax for this extension is:
++ * BasicConstraints ::= SEQUENCE {
++ *     cA                BOOLEAN DEFAULT FALSE,
++ *     pathLenConstraint INTEGER (0..MAX) OPTIONAL
++ * }
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.7 ++ * @see CertAttrSet ++ * @see Extension ++ */ ++public class BasicConstraintsExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 6213957094939885889L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.BasicConstraints"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "BasicConstraints"; ++ public static final String IS_CA = "is_ca"; ++ public static final String PATH_LEN = "path_len"; ++ ++ // Private data members ++ private boolean ca = false; ++ private int pathLen = -1; ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ try (DerOutputStream out = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (ca) { ++ tmp.putBoolean(ca); ++ } ++ if (pathLen >= 0) { ++ tmp.putInteger(new BigInt(pathLen)); ++ } ++ out.write(DerValue.tag_Sequence, tmp); ++ this.extensionValue = out.toByteArray(); ++ } ++ } ++ ++ /** ++ * Default constructor for this object. ++ * ++ * @param ca true, if the subject of the Certificate is a CA. ++ * @param len specifies the depth of the certification path. ++ */ ++ public BasicConstraintsExtension(boolean ca, int len) throws IOException { ++ this.ca = ca; ++ this.pathLen = len; ++ this.extensionId = PKIXExtensions.BasicConstraints_Id; ++ if (ca) { ++ critical = true; ++ } else { ++ critical = false; ++ } ++ encodeThis(); ++ } ++ ++ /** ++ * Default constructor for this object. ++ * ++ * @param ca true, if the subject of the Certificate is a CA. ++ * @param len specifies the depth of the certification path. ++ */ ++ public BasicConstraintsExtension(boolean ca, boolean critical, int len) throws IOException { ++ this.ca = ca; ++ this.pathLen = len; ++ this.extensionId = PKIXExtensions.BasicConstraints_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param extension the DER encoded value of the extension. ++ * @exception IOException on error. ++ */ ++ public BasicConstraintsExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.BasicConstraints_Id; ++ this.critical = critical.booleanValue(); ++ ++ if (value instanceof byte[]) { ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ System.arraycopy(value, 0, extValue, 0, len); ++ ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding of BasicConstraints"); ++ } ++ ++ // non-CA cert with no limit to certification path length ++ if (val.data == null || val.data.available() < 1) { ++ this.ca = false; ++ this.pathLen = -1; ++ return; ++ } ++ DerValue opt = val.data.getDerValue(); ++ if (opt.tag != DerValue.tag_Boolean) { ++ this.ca = false; ++ } else { ++ this.ca = true; ++ if (val.data.available() != 0) { ++ opt = val.data.getDerValue(); ++ } else { ++ this.pathLen = -1; ++ return; ++ } ++ } ++ if (opt.tag != DerValue.tag_Integer) { ++ throw new IOException("Invalid encoding of BasicConstraints"); ++ } ++ this.pathLen = (opt.getInteger()).toInt(); ++ /* ++ * Activate this check once again after PKIX profiling ++ * is a standard and this check no longer imposes an ++ * interoperability barrier. ++ * if (ca) { ++ * if (!this.critical) { ++ * throw new IOException("Criticality cannot be false for CA."); ++ * } ++ * } ++ */ ++ } else ++ throw new IOException("Invalid argument type"); ++ } ++ ++ /** ++ * Return user readable form of extension. ++ */ ++ public String toString() { ++ String s = super.toString() + "BasicConstraints:[\n"; ++ ++ s += ((ca) ? ("CA:true") : ("CA:false")) + "\n"; ++ if (pathLen >= 0) { ++ s += "PathLen:" + pathLen + "\n"; ++ } else { ++ s += "PathLen: undefined\n"; ++ } ++ return (s + "]\n"); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Encode this extension value to the output stream. ++ * ++ * @param out the DerOutputStream to encode the extension to. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ this.extensionId = PKIXExtensions.BasicConstraints_Id; ++ /* #57286 - so that profile can set critiality */ ++ /* ++ if (ca) { ++ critical = true; ++ } else { ++ critical = false; ++ } ++ */ ++ encodeThis(); ++ } ++ super.encode(tmp); ++ ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(IS_CA)) { ++ if (!(obj instanceof Boolean)) { ++ throw new IOException("Attribute value should be of type Boolean."); ++ } ++ ca = ((Boolean) obj).booleanValue(); ++ } else if (name.equalsIgnoreCase(PATH_LEN)) { ++ if (!(obj instanceof Integer)) { ++ throw new IOException("Attribute value should be of type Integer."); ++ } ++ pathLen = ((Integer) obj).intValue(); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:BasicConstraints."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(IS_CA)) { ++ return (Boolean.valueOf(ca)); ++ } else if (name.equalsIgnoreCase(PATH_LEN)) { ++ return (Integer.valueOf(pathLen)); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:BasicConstraints."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(IS_CA)) { ++ ca = false; ++ } else if (name.equalsIgnoreCase(PATH_LEN)) { ++ pathLen = -1; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:BasicConstraints."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(IS_CA); ++ elements.addElement(PATH_LEN); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CPSuri.java b/org/mozilla/jss/netscape/security/x509/CPSuri.java +new file mode 100644 +index 00000000..681095ae +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CPSuri.java +@@ -0,0 +1,66 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the CPSuri Qualifier. ++ * ++ * CPSuri ::= IA5String; ++ * ++ * @author Thomas Kwan ++ */ ++public class CPSuri extends Qualifier { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -2814961293159006960L; ++ private String mURI = null; ++ ++ /** ++ * Create a PolicyQualifierInfo ++ * ++ * @param id the ObjectIdentifier for the policy id. ++ */ ++ public CPSuri(String uri) { ++ mURI = uri; ++ } ++ ++ public CPSuri(DerValue val) throws IOException { ++ mURI = val.getIA5String(); ++ } ++ ++ /** ++ * Write the PolicyQualifier to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putIA5String(mURI); ++ } ++ ++ public String getURI() { ++ return mURI; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CRLDistributionPoint.java b/org/mozilla/jss/netscape/security/x509/CRLDistributionPoint.java +new file mode 100644 +index 00000000..abec5bc7 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CRLDistributionPoint.java +@@ -0,0 +1,478 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.ByteArrayOutputStream; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++import org.mozilla.jss.asn1.ANY; ++import org.mozilla.jss.asn1.ASN1Template; ++import org.mozilla.jss.asn1.ASN1Util; ++import org.mozilla.jss.asn1.ASN1Value; ++import org.mozilla.jss.asn1.BIT_STRING; ++import org.mozilla.jss.asn1.EXPLICIT; ++import org.mozilla.jss.asn1.InvalidBERException; ++import org.mozilla.jss.asn1.SEQUENCE; ++import org.mozilla.jss.asn1.Tag; ++ ++/** ++ *
++ * DistributionPoint ::= SEQUENCE {
++ *      distributionPoint       [0]     DistributionPointName OPTIONAL,
++ *      reasons                 [1]     ReasonFlags OPTIONAL,
++ *      cRLIssuer               [2]     GeneralNames OPTIONAL }
++ *
++ * DistributionPointName ::= CHOICE {
++ *      fullName                [0]     GeneralNames,
++ *      nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
++ *
++ * ReasonFlags ::= BIT STRING {
++ *      unused                  (0),
++ *      keyCompromise           (1),
++ *      cACompromise            (2),
++ *      affiliationChanged      (3),
++ *      superseded              (4),
++ *      cessationOfOperation    (5),
++ *      certificateHold         (6) }
++ * 
++ */ ++public class CRLDistributionPoint implements ASN1Value { ++ ++ // at most one of the two following may be specified: ++ private GeneralNames fullName; ++ private RDN relativeName; ++ ++ // cache encoding of fullName ++ private ANY fullNameEncoding; ++ ++ private BitArray reasons; // optional, may be null ++ private GeneralNames CRLIssuer; // optional, may be null ++ private ANY CRLIssuerEncoding; ++ ++ // default constructor does nothing. ++ ++ /** ++ * Returns the fullName of the DistributionPointName, which may be null. ++ */ ++ public GeneralNames getFullName() { ++ return fullName; ++ } ++ ++ /** ++ * Returns the relativeName of the DistributionPointName, which may be null. ++ */ ++ public RDN getRelativeName() { ++ return relativeName; ++ } ++ ++ /** ++ * Sets the fullName of the DistributionPointName. It may be set to null. ++ * If it is set to a non-null value, relativeName will be ++ * set to null, because at most one of these two attributes ++ * can be specified at a time. ++ * ++ * @exception GeneralNamesException If an error occurs encoding the ++ * name. ++ */ ++ public void setFullName(GeneralNames fullName) ++ throws GeneralNamesException, IOException { ++ this.fullName = fullName; ++ if (fullName != null) { ++ // encode the name to catch any problems with it ++ DerOutputStream derOut = new DerOutputStream(); ++ fullName.encode(derOut); ++ try { ++ ANY raw = new ANY(derOut.toByteArray()); ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ raw.encodeWithAlternateTag(Tag.get(0), bos); ++ fullNameEncoding = new ANY(bos.toByteArray()); ++ } catch (InvalidBERException e) { ++ // assume this won't happen, since it would imply a bug ++ // in DerOutputStream ++ throw new GeneralNamesException(e.toString()); ++ } ++ ++ this.relativeName = null; ++ } ++ } ++ ++ /** ++ * Sets the relativeName of the DistributionPointName. It may be set to null. ++ * If it is set to a non-null value, fullName will be ++ * set to null, because at most one of these two attributes ++ * can be specified at a time. ++ */ ++ public void setRelativeName(RDN relativeName) { ++ this.relativeName = relativeName; ++ if (relativeName != null) { ++ this.fullName = null; ++ } ++ } ++ ++ /** ++ * Returns the reason flags for this distribution point. May be null. ++ */ ++ public BitArray getReasons() { ++ return reasons; ++ } ++ ++ /** ++ * Sets the reason flags for this distribution point. May be set to null. ++ */ ++ public void setReasons(BitArray reasons) { ++ this.reasons = reasons; ++ } ++ ++ /** ++ * Returns the CRLIssuer for the CRL at this distribution point. ++ * May be null. ++ */ ++ public GeneralNames getCRLIssuer() { ++ return CRLIssuer; ++ } ++ ++ /** ++ * Sets the CRLIssuer for the CRL at this distribution point. ++ * May be set to null. ++ * ++ * @exception GeneralNamesException If an error occurs encoding the name. ++ */ ++ public void setCRLIssuer(GeneralNames CRLIssuer) ++ throws GeneralNamesException, IOException { ++ this.CRLIssuer = CRLIssuer; ++ ++ if (CRLIssuer != null) { ++ // encode the name to catch any problems with it ++ DerOutputStream derOut = new DerOutputStream(); ++ CRLIssuer.encode(derOut); ++ try { ++ ANY raw = new ANY(derOut.toByteArray()); ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ raw.encodeWithAlternateTag(Tag.get(2), bos); ++ CRLIssuerEncoding = new ANY(bos.toByteArray()); ++ } catch (InvalidBERException e) { ++ throw new GeneralNamesException(e.toString()); ++ } ++ } ++ } ++ ++ ///////////////////////////////////////////////////////////// ++ // DER encoding ++ ///////////////////////////////////////////////////////////// ++ ++ private static final Tag TAG = SEQUENCE.TAG; ++ ++ public Tag getTag() { ++ return TAG; ++ } ++ ++ public void encode(OutputStream ostream) throws IOException { ++ encode(TAG, ostream); ++ } ++ ++ public void encode(Tag implicitTag, OutputStream ostream) ++ throws IOException { ++ SEQUENCE seq = new SEQUENCE(); ++ DerOutputStream derOut; ++ ++ try { ++ ++ // Encodes the DistributionPointName. Because DistributionPointName ++ // is a CHOICE, the [0] tag is forced to be EXPLICIT. ++ if (fullName != null) { ++ EXPLICIT distPoint = new EXPLICIT(Tag.get(0), fullNameEncoding); ++ seq.addElement(distPoint); ++ } else if (relativeName != null) { ++ derOut = new DerOutputStream(); ++ relativeName.encode(derOut); ++ ANY rn = new ANY(derOut.toByteArray()); ++ EXPLICIT raw = new EXPLICIT(Tag.get(1), rn); ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ raw.encode(bos); ++ ANY distPointName = new ANY(bos.toByteArray()); ++ EXPLICIT distPoint = new EXPLICIT(Tag.get(0), distPointName); ++ seq.addElement(distPoint); ++ } ++ ++ // Encodes the ReasonFlags. ++ if (reasons != null) { ++ derOut = new DerOutputStream(); ++ derOut.putUnalignedBitString(reasons); ++ ANY raw = new ANY(derOut.toByteArray()); ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ raw.encodeWithAlternateTag(Tag.get(1), bos); ++ ANY reasonEncoding = new ANY(bos.toByteArray()); ++ seq.addElement(Tag.get(1), reasonEncoding); ++ } ++ ++ // Encodes the CRLIssuer ++ if (CRLIssuer != null) { ++ seq.addElement(Tag.get(2), CRLIssuerEncoding); ++ } ++ ++ seq.encode(implicitTag, ostream); ++ ++ } catch (InvalidBERException e) { ++ // this shouldn't happen unless there is a bug in one of ++ // the Sun encoding classes ++ throw new IOException(e.toString()); ++ } ++ } ++ ++ // Template singleton ++ private static Template templateInstance = new Template(); ++ ++ /** ++ * Returns an instance of a template for decoding a CRLDistributionPoint. ++ */ ++ public static Template getTemplate() { ++ return templateInstance; ++ } ++ ++ public static void main(String args[]) throws GeneralNamesException, IOException, InvalidBERException { ++ ByteArrayOutputStream bos = null; ++ FileOutputStream fos = null; ++ try { ++ if (args.length != 1) { ++ System.out.println("Usage: CRLDistributionPoint "); ++ System.exit(-1); ++ } ++ ++ bos = new ByteArrayOutputStream(); ++ ++ SEQUENCE cdps = new SEQUENCE(); ++ ++ // URI only ++ CRLDistributionPoint cdp = new CRLDistributionPoint(); ++ URIName uri = new URIName("http://www.mycrl.com/go/here"); ++ GeneralNames generalNames = new GeneralNames(); ++ generalNames.addElement(uri); ++ cdp.setFullName(generalNames); ++ cdps.addElement(cdp); ++ ++ // DN only ++ cdp = new CRLDistributionPoint(); ++ X500Name dn = new X500Name("CN=Otis Smith,E=otis@fedoraproject.org" + ++ ",OU=Certificate Server,O=Fedora,C=US"); ++ generalNames = new GeneralNames(); ++ generalNames.addElement(dn); ++ cdp.setFullName(generalNames); ++ cdps.addElement(cdp); ++ ++ // DN + reason ++ BitArray ba = new BitArray(5, new byte[] { (byte) 0x28 }); ++ cdp = new CRLDistributionPoint(); ++ cdp.setFullName(generalNames); ++ cdp.setReasons(ba); ++ cdps.addElement(cdp); ++ ++ // relative DN + reason + crlIssuer ++ cdp = new CRLDistributionPoint(); ++ RDN rdn = new RDN("OU=foobar dept"); ++ cdp.setRelativeName(rdn); ++ cdp.setReasons(ba); ++ cdp.setCRLIssuer(generalNames); ++ cdps.addElement(cdp); ++ ++ cdps.encode(bos); ++ ++ byte[] encoded = bos.toByteArray(); ++ fos = new FileOutputStream(args[0]); ++ fos.write(encoded); ++ ++ SEQUENCE.OF_Template seqt = new SEQUENCE.OF_Template(getTemplate()); ++ ++ cdps = (SEQUENCE) ASN1Util.decode(seqt, encoded); ++ ++ int size = cdps.size(); ++ System.out.println("Total number of CDPs: " + size); ++ for (int i = 0; i < size; i++) { ++ System.out.println("\nCDP " + i); ++ cdp = (CRLDistributionPoint) cdps.elementAt(i); ++ GeneralNames gn = cdp.getFullName(); ++ if (gn == null) { ++ System.out.println("No full name"); ++ } else { ++ System.out.println(gn); ++ } ++ rdn = cdp.getRelativeName(); ++ if (rdn == null) { ++ System.out.println("No relative name"); ++ } else { ++ System.out.println(rdn); ++ } ++ if (cdp.getReasons() == null) { ++ System.out.println("No reasons"); ++ } else { ++ System.out.println(cdp.getReasons()); ++ } ++ gn = cdp.getCRLIssuer(); ++ if (gn == null) { ++ System.out.println("No cRLIssuer"); ++ } else { ++ System.out.println(gn); ++ } ++ } ++ System.out.println("Done"); ++ ++ } finally { ++ if (bos != null) { ++ bos.close(); ++ } ++ if (fos != null) { ++ fos.close(); ++ } ++ if (fos != null) { ++ fos.close(); ++ } ++ } ++ } ++ ++ /** ++ * Template for decoding CRLDistributionPoint. ++ */ ++ public static class Template implements ASN1Template { ++ ++ public boolean tagMatch(Tag tag) { ++ return TAG.equals(tag); ++ } ++ ++ public ASN1Value decode(InputStream istream) ++ throws IOException, InvalidBERException { ++ return decode(TAG, istream); ++ } ++ ++ public ASN1Value decode(Tag implicitTag, InputStream istream) ++ throws IOException, InvalidBERException { ++ CRLDistributionPoint cdp = new CRLDistributionPoint(); ++ ++ // ++ // construct the top-level sequence ++ // ++ ++ SEQUENCE.Template seqt = SEQUENCE.getTemplate(); ++ ++ // distributionPoint ++ seqt.addOptionalElement( ++ new EXPLICIT.Template(Tag.get(0), ANY.getTemplate())); ++ ++ // reasons ++ seqt.addOptionalElement(Tag.get(1), BIT_STRING.getTemplate()); ++ ++ // cRLIssuer ++ // This will have a tag of 2, but we can't say that here ++ // because ANYs can't have implicit tags. We don't need to say ++ // it, because we do check the tags on the other two elements ++ // in the sequence, so we'll know if we get this one. ++ seqt.addOptionalElement(ANY.getTemplate()); ++ ++ // ++ // decode the top-level sequence ++ // ++ SEQUENCE top = (SEQUENCE) seqt.decode(implicitTag, istream); ++ ++ // decode the distribution point name ++ if (top.elementAt(0) != null) { ++ EXPLICIT exp = (EXPLICIT) top.elementAt(0); ++ ANY distPoint = (ANY) exp.getContent(); ++ if (distPoint.getTag().equals(Tag.get(0))) { ++ // fullName ++ try { ++ DerValue dv = new DerValue(distPoint.getEncoded()); ++ //toFile("encodedFullName", distPoint.getEncoded()); ++ dv.resetTag(DerValue.tag_Sequence); ++ cdp.setFullName(new GeneralNames(dv)); ++ } catch (GeneralNamesException e) { ++ throw new InvalidBERException("fullName: " + e.toString()); ++ } catch (IOException e) { ++ throw new InvalidBERException("fullName: " + e.toString()); ++ } ++ } else if (distPoint.getTag().equals(Tag.get(1))) { ++ // relative name ++ try { ++ DerValue dv = new DerValue(distPoint.getEncoded()); ++ /* dv is as follows: ++ 0 12: [1] { ++ 2 10: SET { ++ 4 8: SEQUENCE { ++ 6 3: OBJECT IDENTIFIER commonName (2 5 4 3) ++ 11 1: PrintableString 'x' ++ : } ++ : } ++ : } ++ */ ++ dv = dv.data.getDerValue(); // skipping the tag ++ /* after the skipping, we have: ++ 0 10: SET { ++ 2 8: SEQUENCE { ++ 4 3: OBJECT IDENTIFIER commonName (2 5 4 3) ++ 9 1: PrintableString 'x' ++ : } ++ : } ++ */ ++ dv.resetTag(DerValue.tag_Set); ++ cdp.setRelativeName(new RDN(dv)); ++ } catch (IOException e) { ++ throw new InvalidBERException("relativeName " + ++ e.toString()); ++ } ++ } else { ++ throw new InvalidBERException( ++ "Unknown tag " + distPoint.getTag() + ++ " in distributionPoint"); ++ } ++ } ++ ++ // decode the reasons ++ if (top.elementAt(1) != null) { ++ BIT_STRING bs = (BIT_STRING) top.elementAt(1); ++ byte[] bits = bs.getBits(); ++ cdp.setReasons( ++ new BitArray((bits.length * 8) - bs.getPadCount(), bits)); ++ } ++ ++ // decode the cRLIssuer ++ if (top.elementAt(2) != null) { ++ ANY issuer = (ANY) top.elementAt(2); ++ if (!issuer.getTag().equals(Tag.get(2))) { ++ throw new InvalidBERException("Invalid tag " + issuer.getTag()); ++ } ++ try { ++ DerValue dv = new DerValue(issuer.getEncoded()); ++ dv.resetTag(DerValue.tag_Sequence); ++ cdp.setCRLIssuer(new GeneralNames(dv)); ++ } catch (GeneralNamesException e) { ++ throw new InvalidBERException("cRLIssuer " + e.toString()); ++ } catch (IOException e) { ++ throw new InvalidBERException("cRLIssuer " + e.toString()); ++ } ++ } ++ ++ return cdp; ++ ++ } ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CRLDistributionPointsExtension.java b/org/mozilla/jss/netscape/security/x509/CRLDistributionPointsExtension.java +new file mode 100644 +index 00000000..96fb06f8 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CRLDistributionPointsExtension.java +@@ -0,0 +1,390 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.BufferedOutputStream; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.Vector; ++ ++import org.mozilla.jss.asn1.ASN1Util; ++import org.mozilla.jss.asn1.InvalidBERException; ++import org.mozilla.jss.asn1.SEQUENCE; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++ ++/** ++ * An extension that tells applications where to find the CRL for ++ * this certificate. ++ * ++ *
++ * cRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
++ *
++ * DistributionPoint ::= SEQUENCE {
++ *      distributionPoint       [0]     DistributionPointName OPTIONAL,
++ *      reasons                 [1]     ReasonFlags OPTIONAL,
++ *      cRLIssuer               [2]     GeneralNames OPTIONAL }
++ *
++ * DistributionPointName ::= CHOICE {
++ *      fullName                [0]     GeneralNames,
++ *      nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
++ *
++ * ReasonFlags ::= BIT STRING {
++ *      unused                  (0),
++ *      keyCompromise           (1),
++ *      cACompromise            (2),
++ *      affiliationChanged      (3),
++ *      superseded              (4),
++ *      cessationOfOperation    (5),
++ *      certificateHold         (6) }
++ * 
++ */ ++public class CRLDistributionPointsExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 8551761833349709229L; ++ // vector of CRLDistributionPoint ++ private SEQUENCE distributionPoints = new SEQUENCE(); ++ ++ // Cached DER-encoding to improve performance. ++ private byte[] cachedEncoding = null; ++ ++ /** ++ * This constructor is called by the CertificateExtensions class to decode ++ * an extension whose OID indicates it is a CRLDistributionsPoints ++ * extension. ++ */ ++ public CRLDistributionPointsExtension(Boolean critical, Object value) ++ //throws IOException ++ { ++ try { ++ ++ this.extensionId = PKIXExtensions.CRLDistributionPoints_Id; ++ this.critical = critical.booleanValue(); ++ this.extensionValue = ((byte[]) value).clone(); ++ ++ // decode the value ++ try { ++ SEQUENCE.OF_Template seqOfCRLDP = ++ new SEQUENCE.OF_Template(CRLDistributionPoint.getTemplate()); ++ ++ distributionPoints = ++ (SEQUENCE) ASN1Util.decode(seqOfCRLDP, extensionValue); ++ } catch (InvalidBERException e) { ++ throw new IOException("Invalid BER-encoding: " + e, e); ++ } ++ } catch (IOException e) { ++ System.out.println("Big error"); ++ System.out.println(e); ++ e.printStackTrace(); ++ //throw e; ++ } ++ } ++ ++ /** ++ * The Object Identifier for this extension. ++ */ ++ public static final String OID = "2.5.29.31"; ++ ++ /** ++ * Creates a new CRLDistributionPoints extension, with the given ++ * distribution point as the first element. ++ */ ++ public CRLDistributionPointsExtension(CRLDistributionPoint dp) { ++ this.extensionId = PKIXExtensions.CRLDistributionPoints_Id; ++ this.critical = false; ++ distributionPoints.addElement(dp); ++ } ++ ++ /** ++ * Adds an additional distribution point to the end of the sequence. ++ */ ++ public void addPoint(CRLDistributionPoint dp) { ++ distributionPoints.addElement(dp); ++ cachedEncoding = null; ++ } ++ ++ /** ++ * Returns the number of distribution points in the sequence. ++ */ ++ public int getNumPoints() { ++ return distributionPoints.size(); ++ } ++ ++ /** ++ * Returns the DistributionPoint at the given index in the sequence. ++ */ ++ public CRLDistributionPoint getPointAt(int index) { ++ return (CRLDistributionPoint) distributionPoints.elementAt(index); ++ } ++ ++ /** ++ * Sets the criticality of this extension. PKIX dictates that this ++ * extension SHOULD NOT be critical, so applications can make it critical ++ * if they have a very good reason. By default, the extension is not ++ * critical. ++ */ ++ public void setCritical(boolean critical) { ++ this.critical = critical; ++ } ++ ++ /** ++ * Encodes this extension to the given DerOutputStream. ++ * This method re-encodes each time it is called, so it is not very ++ * efficient. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ extensionValue = ASN1Util.encode(distributionPoints); ++ super.encode(out); ++ } ++ ++ /** ++ * Should be called if any change is made to this data structure ++ * so that the cached DER encoding can be discarded. ++ */ ++ public void flushCachedEncoding() { ++ cachedEncoding = null; ++ } ++ ++ ///////////////////////////////////////////////////////////// ++ // CertAttrSet interface ++ // This interface is not really appropriate for this extension ++ // because it is so complicated. Therefore, we only provide a ++ // minimal implementation. ++ ///////////////////////////////////////////////////////////// ++ public static final String NAME = "CRLDistributionPoints"; ++ ++ @Override ++ public String toString() { ++ return NAME; ++ } ++ ++ /** ++ * DER-encodes this extension to the given OutputStream. ++ */ ++ public void encode(OutputStream ostream) ++ throws CertificateException, IOException { ++ if (cachedEncoding == null) { ++ // only re-encode if necessary ++ DerOutputStream tmp = new DerOutputStream(); ++ encode(tmp); ++ cachedEncoding = tmp.toByteArray(); ++ } ++ ostream.write(cachedEncoding); ++ } ++ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ throw new IOException("Not supported"); ++ } ++ ++ public void set(String name, Object obj) ++ throws CertificateException, IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CRLDistributionPointsExtension"); ++ } ++ ++ public Object get(String name) ++ throws CertificateException, IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CRLDistributionPointsExtension"); ++ } ++ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CRLDistributionPointsExtension"); ++ } ++ ++ /* ++ * TODO use an empty collection to generate these ++ */ ++ public Enumeration getAttributeNames() { ++ return (new Vector()).elements(); ++ } ++ ++ public String getName() { ++ return NAME; ++ } ++ ++ /** ++ * Test driver. ++ */ ++ public static void main(String args[]) { ++ BufferedOutputStream bos = null; ++ try { ++ ++ if (args.length != 1) { ++ System.out.println("Usage: CRLDistributionPointsExtentions " + ++ ""); ++ System.exit(-1); ++ } ++ ++ bos = new BufferedOutputStream( ++ new FileOutputStream(args[0])); ++ ++ // URI only ++ CRLDistributionPoint cdp = new CRLDistributionPoint(); ++ URIName uri = new URIName("http://www.mycrl.com/go/here"); ++ GeneralNames generalNames = new GeneralNames(); ++ generalNames.addElement(uri); ++ cdp.setFullName(generalNames); ++ CRLDistributionPointsExtension crldpExt = ++ new CRLDistributionPointsExtension(cdp); ++ ++ // DN only ++ cdp = new CRLDistributionPoint(); ++ X500Name dn = new X500Name("CN=Otis Smith,E=otis@fedoraproject.org" + ++ ",OU=Certificate Server,O=Fedora,C=US"); ++ generalNames = new GeneralNames(); ++ generalNames.addElement(dn); ++ cdp.setFullName(generalNames); ++ crldpExt.addPoint(cdp); ++ ++ // DN + reason ++ BitArray ba = new BitArray(5, new byte[] { (byte) 0x28 }); ++ cdp = new CRLDistributionPoint(); ++ cdp.setFullName(generalNames); ++ cdp.setReasons(ba); ++ crldpExt.addPoint(cdp); ++ ++ // relative DN + reason + crlIssuer ++ cdp = new CRLDistributionPoint(); ++ RDN rdn = new RDN("OU=foobar dept"); ++ cdp.setRelativeName(rdn); ++ cdp.setReasons(ba); ++ cdp.setCRLIssuer(generalNames); ++ crldpExt.addPoint(cdp); ++ ++ crldpExt.setCritical(true); ++ crldpExt.encode(bos); ++ ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } finally { ++ if (bos != null) { ++ try { ++ bos.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++ ++ /** ++ * Represents a reason that a cert may be revoked. These reasons are ++ * expressed in a ReasonFlags bit string. ++ */ ++ public static class Reason { ++ ++ private String name; ++ private byte bitMask; ++ ++ private Reason() { ++ } ++ ++ private Reason(String name, byte bitMask) { ++ this.name = name; ++ this.bitMask = bitMask; ++ map.put(name, this); ++ list.addElement(this); ++ } ++ ++ private static Hashtable map = new Hashtable(); ++ private static Vector list = new Vector(); ++ ++ public static Reason fromString(String name) { ++ return map.get(name); ++ } ++ ++ public String getName() { ++ return name; ++ } ++ ++ public byte getBitMask() { ++ return bitMask; ++ } ++ ++ /** ++ * Given a bit array representing reason flags, extracts the reasons ++ * and returns them as an array. ++ * ++ * @param bitFlags A bit vector containing reason flags. ++ * @return An array of reasons contained in the bit vector. ++ * May be zero-length but will not be null. ++ */ ++ public static Reason[] bitArrayToReasonArray(byte bitFlags) { ++ return bitArrayToReasonArray(new byte[] { bitFlags }); ++ } ++ ++ /** ++ * Given a bit array representing reason flags, extracts the reasons ++ * and returns them as an array. Currently, only the first byte ++ * of the bitflags are examined. ++ * ++ * @param bitFlags A bit vector containing reason flags. The format ++ * is big-endian (MSB first). Only the first byte is examined. ++ * @return An array of reasons contained in the bit vector. ++ * May be zero-length but will not be null. ++ */ ++ public static Reason[] bitArrayToReasonArray(byte[] bitFlags) { ++ byte first = bitFlags[0]; ++ int size = list.size(); ++ Vector result = new Vector(); ++ for (int i = 0; i < size; i++) { ++ Reason r = list.elementAt(i); ++ byte b = r.getBitMask(); ++ if ((first & b) != 0) { ++ result.addElement(r); ++ } ++ } ++ size = result.size(); ++ Reason[] retval = new Reason[size]; ++ for (int i = 0; i < size; i++) { ++ retval[i] = result.elementAt(i); ++ } ++ return retval; ++ } ++ ++ public static final Reason UNUSED = ++ new Reason("unused", (byte) 0x80); ++ public static final Reason KEY_COMPROMISE = ++ new Reason("keyCompromise", (byte) 0x40); ++ public static final Reason CA_COMPROMISE = ++ new Reason("cACompromise", (byte) 0x20); ++ public static final Reason AFFILIATION_CHANGED = ++ new Reason("affiliationChanged", (byte) 0x10); ++ public static final Reason SUPERSEDED = ++ new Reason("superseded", (byte) 0x08); ++ public static final Reason CESSATION_OF_OPERATION = ++ new Reason("cessationOfOperation", (byte) 0x04); ++ public static final Reason CERTIFICATE_HOLD = ++ new Reason("certificateHold", (byte) 0x02); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CRLExtensions.java b/org/mozilla/jss/netscape/security/x509/CRLExtensions.java +new file mode 100755 +index 00000000..8537046e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CRLExtensions.java +@@ -0,0 +1,254 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.lang.reflect.Constructor; ++import java.lang.reflect.InvocationTargetException; ++import java.security.cert.CRLException; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the CRL Extensions. ++ * ++ * @author Hemma Prafullchandra ++ * @version 1.4 ++ */ ++public class CRLExtensions extends Vector { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 365767738692986418L; ++ private Hashtable map; ++ ++ // Parse the encoded extension ++ private void parseExtension(Extension ext) throws X509ExtensionException { ++ try { ++ Class extClass = OIDMap.getClass(ext.getExtensionId()); ++ if (extClass == null) { // Unsupported extension ++ if (ext.isCritical()) { ++ throw new IOException("Unsupported CRITICAL extension: " ++ + ext.getExtensionId()); ++ } else { ++ map.put(ext.getExtensionId().toString(), ext); ++ addElement(ext); ++ return; ++ } ++ } ++ Class[] params = { Boolean.class, Object.class }; ++ Constructor cons = extClass.getConstructor(params); ++ byte[] extData = ext.getExtensionValue(); ++ int extLen = extData.length; ++ Object value = Array.newInstance(byte.class, extLen); ++ ++ for (int i = 0; i < extLen; i++) { ++ Array.setByte(value, i, extData[i]); ++ } ++ Object[] passed = new Object[] { Boolean.valueOf(ext.isCritical()), ++ value }; ++ CertAttrSet crlExt = (CertAttrSet) cons.newInstance(passed); ++ map.put(crlExt.getName(), (Extension) crlExt); ++ addElement((Extension) crlExt); ++ ++ } catch (InvocationTargetException invk) { ++ throw new X509ExtensionException( ++ invk.getTargetException().getMessage()); ++ ++ } catch (Exception e) { ++ throw new X509ExtensionException(e.toString()); ++ } ++ } ++ ++ /** ++ * Default constructor. ++ */ ++ public CRLExtensions() { ++ map = new Hashtable(); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the Extension from. ++ * @exception CRLException on decoding errors. ++ * @exception X509ExtensionException on extension handling errors. ++ */ ++ public CRLExtensions(DerInputStream in) ++ throws CRLException, X509ExtensionException { ++ ++ map = new Hashtable(); ++ try { ++ DerValue[] exts = in.getSequence(5); ++ ++ for (int i = 0; i < exts.length; i++) { ++ Extension ext = new Extension(exts[i]); ++ parseExtension(ext); ++ } ++ } catch (IOException e) { ++ throw new CRLException("Parsing error: " + e.toString()); ++ } ++ } ++ ++ /** ++ * Decode the extensions from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception CRLException on decoding or validity errors. ++ * @exception X509ExtensionException on extension handling errors. ++ */ ++ public void decode(InputStream in) ++ throws CRLException, X509ExtensionException { ++ try { ++ DerValue val = new DerValue(in); ++ DerInputStream str = val.toDerInputStream(); ++ ++ map = new Hashtable(); ++ DerValue[] exts = str.getSequence(5); ++ ++ for (int i = 0; i < exts.length; i++) { ++ Extension ext = new Extension(exts[i]); ++ parseExtension(ext); ++ } ++ } catch (IOException e) { ++ throw new CRLException("Parsing error: " + e.toString()); ++ } ++ } ++ ++ /** ++ * Encode the extensions in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @param isExplicit the tag indicating whether this is an entry ++ * extension or a CRL extension. ++ * @exception CRLException on encoding errors. ++ */ ++ public void encode(OutputStream out, boolean isExplicit) ++ throws CRLException { ++ try (DerOutputStream tmp = new DerOutputStream()) { ++ // #381559 ++ if (size() == 0) ++ return; ++ DerOutputStream extOut = new DerOutputStream(); ++ for (int i = 0; i < size(); i++) { ++ Object thisOne = elementAt(i); ++ if (thisOne instanceof CertAttrSet) ++ ((CertAttrSet) thisOne).encode(extOut); ++ else if (thisOne instanceof Extension) ++ ((Extension) thisOne).encode(extOut); ++ else ++ throw new CRLException("Illegal extension object"); ++ } ++ ++ DerOutputStream seq = new DerOutputStream(); ++ seq.write(DerValue.tag_Sequence, extOut); ++ ++ if (isExplicit) { ++ tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, (byte) 0), seq); ++ out.write(tmp.toByteArray()); ++ } else { ++ out.write(seq.toByteArray()); ++ } ++ } catch (IOException e) { ++ throw new CRLException("Encoding error: " + e.toString()); ++ } catch (CertificateException e) { ++ throw new CRLException("Encoding error: " + e.toString()); ++ } ++ } ++ ++ /** ++ * Get the extension with this alias. ++ * ++ * @param alias the identifier string for the extension to retrieve. ++ * @exception X509ExtensionException on extension handling errors. ++ */ ++ public Extension get(String alias) throws X509ExtensionException { ++ X509AttributeName attr = new X509AttributeName(alias); ++ String name; ++ String id = attr.getPrefix(); ++ if (id.equalsIgnoreCase(X509CertImpl.NAME)) { // fully qualified ++ int index = alias.lastIndexOf("."); ++ name = alias.substring(index + 1); ++ } else ++ name = alias; ++ Extension ext = map.get(name); ++ if (ext == null) ++ throw new X509ExtensionException("No extension found with name: " ++ + alias); ++ return ext; ++ } ++ ++ /** ++ * Set the extension value with this alias. ++ * ++ * @param alias the identifier string for the extension to set. ++ * @param obj the Object to set the extension identified by the ++ * alias. ++ * @exception IOException on errors. ++ */ ++ public void set(String alias, Extension obj) throws IOException { ++ map.put(alias, obj); ++ addElement(obj); ++ } ++ ++ /** ++ * Return an enumeration of names of the extensions. ++ * ++ * @return an enumeration of the names of the extensions in this CRL. ++ */ ++ public Enumeration getElements() { ++ return (map.elements()); ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = super.hashCode(); ++ result = prime * result + ((map == null) ? 0 : map.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (!super.equals(obj)) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ CRLExtensions other = (CRLExtensions) obj; ++ if (map == null) { ++ if (other.map != null) ++ return false; ++ } else if (!map.equals(other.map)) ++ return false; ++ return true; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CRLNumberExtension.java b/org/mozilla/jss/netscape/security/x509/CRLNumberExtension.java +new file mode 100755 +index 00000000..3918158f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CRLNumberExtension.java +@@ -0,0 +1,227 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.math.BigInteger; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the CRL Number Extension. ++ * ++ *

++ * This extension, if present, conveys a monotonically increasing sequence number for each CRL issued by a given CA ++ * through a specific CA X.500 Directory entry or CRL distribution point. This extension allows users to easily ++ * determine when a particular CRL supersedes another CRL. ++ * ++ * @author Hemma Prafullchandra ++ * @version 1.2 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class CRLNumberExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 2992307666566322402L; ++ /** ++ * Attribute name. ++ */ ++ public static final String NAME = "CRLNumber"; ++ public static final String NUMBER = "value"; ++ ++ private BigInt crlNumber = null; ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ if (crlNumber == null) ++ throw new IOException("Unintialized CRL number extension"); ++ try (DerOutputStream os = new DerOutputStream()) { ++ os.putInteger(this.crlNumber); ++ this.extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Create a CRLNumberExtension with the integer value . ++ * The criticality is set to false. ++ * ++ * @param crlNum the value to be set for the extension. ++ */ ++ public CRLNumberExtension(int crlNum) throws IOException { ++ this.crlNumber = new BigInt(crlNum); ++ this.extensionId = PKIXExtensions.CRLNumber_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a CRLNumberExtension with the BigInteger value . ++ * The criticality is set to false. ++ * ++ * @param crlNum the value to be set for the extension. ++ */ ++ public CRLNumberExtension(BigInteger crlNum) throws IOException { ++ this.crlNumber = new BigInt(crlNum); ++ this.extensionId = PKIXExtensions.CRLNumber_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a CRLNumberExtension with the BigInteger value . ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param crlNum the value to be set for the extension. ++ */ ++ public CRLNumberExtension(Boolean critical, BigInteger crlNum) throws IOException { ++ this.crlNumber = new BigInt(crlNum); ++ this.extensionId = PKIXExtensions.CRLNumber_Id; ++ this.critical = critical.booleanValue(); ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public CRLNumberExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.CRLNumber_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ this.crlNumber = val.getInteger(); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (name.equalsIgnoreCase(NUMBER)) { ++ if (!(obj instanceof BigInteger)) { ++ throw new IOException("Attribute must be of type BigInteger."); ++ } ++ crlNumber = new BigInt((BigInteger) obj); ++ } else { ++ throw new IOException("Attribute name not recognized by" ++ + " CertAttrSet:CRLNumber."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(NUMBER)) { ++ if (crlNumber == null) ++ return null; ++ else ++ return crlNumber.toBigInteger(); ++ } else { ++ throw new IOException("Attribute name not recognized by" ++ + " CertAttrSet:CRLNumber."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(NUMBER)) { ++ crlNumber = null; ++ } else { ++ throw new IOException("Attribute name not recognized by" ++ + " CertAttrSet:CRLNumber."); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the CRLNumberExtension. ++ */ ++ public String toString() { ++ String s = super.toString() + "CRL Number: " + ++ ((crlNumber == null) ? "" : crlNumber.toString()) ++ + "\n"; ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ this.extensionId = PKIXExtensions.CRLNumber_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(NUMBER); ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CRLReasonExtension.java b/org/mozilla/jss/netscape/security/x509/CRLReasonExtension.java +new file mode 100644 +index 00000000..350ea481 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CRLReasonExtension.java +@@ -0,0 +1,243 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the CRLReason Extension of CRL entry. ++ * ++ *

++ * This extension, if present, defines the identifies the reason for the certificate revocation. ++ * ++ * @author galperin ++ * @version $Revision$, $Date$ ++ * @see Extension ++ * @see CertAttrSet ++ */ ++ ++public final class CRLReasonExtension extends Extension implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 4544973296866779535L; ++ /** ++ * Canned instances for all revocation reasons ++ */ ++ public static final CRLReasonExtension UNSPECIFIED = new CRLReasonExtension(RevocationReason.UNSPECIFIED); ++ public static final CRLReasonExtension KEY_COMPROMISE = new CRLReasonExtension(RevocationReason.KEY_COMPROMISE); ++ public static final CRLReasonExtension CA_COMPROMISE = new CRLReasonExtension(RevocationReason.CA_COMPROMISE); ++ public static final CRLReasonExtension AFFILIATION_CHANGED = new CRLReasonExtension( ++ RevocationReason.AFFILIATION_CHANGED); ++ public static final CRLReasonExtension SUPERSEDED = new CRLReasonExtension(RevocationReason.SUPERSEDED); ++ public static final CRLReasonExtension CESSATION_OF_OPERATION = new CRLReasonExtension( ++ RevocationReason.CESSATION_OF_OPERATION); ++ public static final CRLReasonExtension CERTIFICATE_HOLD = new CRLReasonExtension(RevocationReason.CERTIFICATE_HOLD); ++ public static final CRLReasonExtension REMOVE_FROM_CRL = new CRLReasonExtension(RevocationReason.REMOVE_FROM_CRL); ++ public static final CRLReasonExtension PRIVILEGE_WITHDRAWN = new CRLReasonExtension( ++ RevocationReason.PRIVILEGE_WITHDRAWN); ++ public static final CRLReasonExtension AA_COMPROMISE = new CRLReasonExtension(RevocationReason.AA_COMPROMISE); ++ ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "CRLReason"; ++ public static final String REASON = "value"; ++ ++ private RevocationReason mReason = null; ++ ++ public RevocationReason getReason() { ++ return mReason; ++ } ++ ++ /** ++ * Default constructor ++ * ++ */ ++ ++ public CRLReasonExtension() { ++ this.extensionId = PKIXExtensions.ReasonCode_Id; ++ this.critical = false; ++ mReason = null; ++ } ++ ++ /** ++ * Create extension value for specific revocation reason ++ * ++ */ ++ ++ public CRLReasonExtension(RevocationReason reason) { ++ this.extensionId = PKIXExtensions.ReasonCode_Id; ++ this.critical = false; ++ mReason = reason; ++ } ++ ++ public CRLReasonExtension(Boolean critical, RevocationReason reason) ++ throws IOException { ++ this.extensionId = PKIXExtensions.ReasonCode_Id; ++ this.critical = critical.booleanValue(); ++ mReason = reason; ++ } ++ ++ /** ++ * Create the object from the passed DER encoded value. ++ * ++ * @param derVal the DerValue decoded from the stream. ++ * @exception IOException on decoding errors. ++ */ ++ public CRLReasonExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.ReasonCode_Id; ++ this.critical = critical.booleanValue(); ++ ++ byte[] extValue = ((byte[]) value).clone(); ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ int reasonCode = val.getEnumerated(); ++ mReason = RevocationReason.fromInt(reasonCode); ++ if (mReason == null) ++ throw new IOException("Unknown revocation reason value " + reasonCode); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof RevocationReason)) { ++ throw new IOException("Attribute must be of type RevocationReason."); ++ } ++ ++ if (name.equalsIgnoreCase(REASON)) { ++ mReason = (RevocationReason) obj; ++ } else { ++ throw new IOException("Name not recognized by CRLReason"); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(REASON)) { ++ return mReason; ++ } else { ++ throw new IOException("Name not recognized by CRLReason"); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(REASON)) { ++ mReason = null; ++ } else { ++ throw new IOException("Name not recognized by CRLReason"); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the ReasonFlags. ++ */ ++ public String toString() { ++ String s = super.toString() + "CRL Reason [" + mReason + "]\n"; ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ if (mReason == null) ++ throw new IOException("Unintialized CRLReason extension"); ++ try (DerOutputStream os = new DerOutputStream()) { ++ os.putEnumerated(mReason.toInt()); ++ this.extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(REASON); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ public boolean equals(Object other) { ++ if (this == other) ++ return true; ++ else if (other instanceof CRLReasonExtension) ++ return ((CRLReasonExtension) other).mReason == mReason && ++ ((CRLReasonExtension) other).critical == critical; ++ else ++ return false; ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + ((mReason == null) ? 0 : mReason.hashCode()); ++ return result; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertAndKeyGen.java b/org/mozilla/jss/netscape/security/x509/CertAndKeyGen.java +new file mode 100644 +index 00000000..626bb796 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertAndKeyGen.java +@@ -0,0 +1,290 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.security.InvalidKeyException; ++import java.security.KeyPair; ++import java.security.KeyPairGenerator; ++import java.security.NoSuchAlgorithmException; ++import java.security.NoSuchProviderException; ++import java.security.PrivateKey; ++import java.security.PublicKey; ++import java.security.SecureRandom; ++import java.security.Signature; ++import java.security.SignatureException; ++import java.security.cert.CertificateEncodingException; ++import java.security.cert.CertificateException; ++import java.security.cert.X509Certificate; ++import java.util.Date; ++ ++import org.mozilla.jss.netscape.security.pkcs.PKCS10; ++ ++/** ++ * Generate a pair of keys, and provide access to them. This class is ++ * provided primarily for ease of use. ++ * ++ *

++ * This provides some simple certificate management functionality. Specifically, it allows you to create self-signed ++ * X.509 certificates as well as PKCS 10 based certificate signing requests. ++ * ++ *

++ * Keys for some public key signature algorithms have algorithm parameters, such as DSS/DSA. Some sites' Certificate ++ * Authorities adopt fixed algorithm parameters, which speeds up some operations including key generation and signing. ++ * At this time, this interface ++ * does not provide a way to provide such algorithm parameters, e.g. ++ * by providing the CA certificate which includes those parameters. ++ * ++ *

++ * Also, note that at this time only signature-capable keys may be acquired through this interface. Diffie-Hellman keys, ++ * used for secure key exchange, may be supported later. ++ * ++ * @author David Brownell ++ * @author Hemma Prafullchandra ++ * @version 1.44 ++ * @see PKCS10 ++ * @see X509CertImpl ++ */ ++public final class CertAndKeyGen { ++ /** ++ * Creates a CertAndKeyGen object for a particular key type ++ * and signature algorithm. ++ * ++ * @param keyType type of key, e.g. "RSA", "DSA" ++ * @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA", ++ * "MD2WithRSA", "SHAwithDSA". ++ * @exception NoSuchAlgorithmException on unrecognized algorithms. ++ */ ++ public CertAndKeyGen(String keyType, String sigAlg) ++ throws NoSuchAlgorithmException { ++ keyGen = KeyPairGenerator.getInstance(keyType); ++ this.sigAlg = sigAlg; ++ } ++ ++ /** ++ * Sets the source of random numbers used when generating keys. ++ * If you do not provide one, a system default facility is used. ++ * You may wish to provide your own source of random numbers ++ * to get a reproducible sequence of keys and signatures, or ++ * because you may be able to take advantage of strong sources ++ * of randomness/entropy in your environment. ++ * ++ * @deprecated All random numbers come from PKCS #11 now. ++ */ ++ public void setRandom(SecureRandom generator) { ++ } ++ ++ // want "public void generate (X509Certificate)" ... inherit DSA/D-H param ++ ++ /** ++ * Generates a random public/private key pair, with a given key ++ * size. Different algorithms provide different degrees of security ++ * for the same key size, because of the "work factor" involved in ++ * brute force attacks. As computers become faster, it becomes ++ * easier to perform such attacks. Small keys are to be avoided. ++ * ++ *

++ * Note that not all values of "keyBits" are valid for all algorithms, and not all public key algorithms are ++ * currently supported for use in X.509 certificates. If the algorithm you specified does not produce X.509 ++ * compatible keys, an invalid key exception is thrown. ++ * ++ * @param keyBits the number of bits in the keys. ++ * @exception InvalidKeyException if the environment does not ++ * provide X.509 public keys for this signature algorithm. ++ */ ++ public void generate(int keyBits) ++ throws InvalidKeyException { ++ KeyPair pair; ++ ++ try { ++ keyGen.initialize(keyBits); ++ pair = keyGen.generateKeyPair(); ++ ++ } catch (Exception e) { ++ throw new IllegalArgumentException(e.getMessage()); ++ } ++ ++ PublicKey publicKey = pair.getPublic(); ++ ++ if (publicKey instanceof X509Key) { ++ this.publicKey = (X509Key) publicKey; ++ ++ } else { ++ throw new InvalidKeyException("public key " + publicKey + ++ " not an X509Key."); ++ } ++ privateKey = pair.getPrivate(); ++ } ++ ++ /** ++ * Returns the public key of the generated key pair. ++ */ ++ public X509Key getPublicKey() { ++ return publicKey; ++ } ++ ++ /** ++ * Returns the private key of the generated key pair. ++ * ++ *

++ * Be extremely careful when handling private keys. ++ * When private keys are not kept secret, they lose their ability ++ * to securely authenticate specific entities ... that is a huge ++ * security risk! ++ */ ++ public PrivateKey getPrivateKey() { ++ return privateKey; ++ } ++ ++ /** ++ * Returns a self-signed X.509v1 certificate for the public key. ++ * The certificate is immediately valid. ++ * ++ *

++ * Such certificates normally are used to identify a "Certificate Authority" (CA). Accordingly, they will not always ++ * be accepted by other parties. However, such certificates are also useful when you are bootstrapping your security ++ * infrastructure, or deploying system prototypes. ++ * ++ * @deprecated Use the new ++ * ++ * @param myname X.500 name of the subject (who is also the issuer) ++ * @param validity how long the certificate should be valid, in seconds ++ */ ++ public X509Cert getSelfCert(X500Name myname, long validity) ++ throws InvalidKeyException, SignatureException, NoSuchAlgorithmException { ++ X509Certificate cert; ++ ++ try { ++ cert = getSelfCertificate(myname, validity); ++ return new X509Cert(cert.getEncoded()); ++ } catch (CertificateException e) { ++ throw new SignatureException(e.getMessage()); ++ } catch (NoSuchProviderException e) { ++ throw new NoSuchAlgorithmException(e.getMessage()); ++ } catch (IOException e) { ++ throw new SignatureException(e.getMessage()); ++ } ++ } ++ ++ /** ++ * Returns a self-signed X.509v3 certificate for the public key. ++ * The certificate is immediately valid. No extensions. ++ * ++ *

++ * Such certificates normally are used to identify a "Certificate Authority" (CA). Accordingly, they will not always ++ * be accepted by other parties. However, such certificates are also useful when you are bootstrapping your security ++ * infrastructure, or deploying system prototypes. ++ * ++ * @param myname X.500 name of the subject (who is also the issuer) ++ * @param validity how long the certificate should be valid, in seconds ++ * @exception CertificateException on certificate handling errors. ++ * @exception InvalidKeyException on key handling errors. ++ * @exception SignatureException on signature handling errors. ++ * @exception NoSuchAlgorithmException on unrecognized algorithms. ++ * @exception NoSuchProviderException on unrecognized providers. ++ */ ++ public X509Certificate getSelfCertificate(X500Name myname, long validity) ++ throws CertificateException, InvalidKeyException, SignatureException, ++ NoSuchAlgorithmException, NoSuchProviderException { ++ X500Signer issuer; ++ X509CertImpl cert; ++ Date firstDate, lastDate; ++ ++ try { ++ issuer = getSigner(myname); ++ ++ firstDate = new Date(); ++ lastDate = new Date(); ++ lastDate.setTime(lastDate.getTime() + validity * 1000); ++ ++ CertificateValidity interval = ++ new CertificateValidity(firstDate, lastDate); ++ ++ X509CertInfo info = new X509CertInfo(); ++ // Add all mandatory attributes ++ info.set(X509CertInfo.VERSION, ++ new CertificateVersion(CertificateVersion.V1)); ++ info.set(X509CertInfo.SERIAL_NUMBER, ++ new CertificateSerialNumber((int) (firstDate.getTime() / 1000))); ++ AlgorithmId algID = issuer.getAlgorithmId(); ++ info.set(X509CertInfo.ALGORITHM_ID, ++ new CertificateAlgorithmId(algID)); ++ info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(myname)); ++ info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey)); ++ info.set(X509CertInfo.VALIDITY, interval); ++ info.set(X509CertInfo.ISSUER, ++ new CertificateIssuerName(issuer.getSigner())); ++ ++ cert = new X509CertImpl(info); ++ cert.sign(privateKey, algID.getName()); ++ ++ return cert; ++ ++ } catch (IOException e) { ++ throw new CertificateEncodingException("getSelfCert: " + ++ e.getMessage()); ++ } ++ } ++ ++ /** ++ * Returns a PKCS #10 certificate request. The caller uses either PKCS10.print or ++ * PKCS10.toByteArray operations on the result, to get the request in an appropriate ++ * transmission format. ++ * ++ *

++ * PKCS #10 certificate requests are sent, along with some proof of identity, to Certificate Authorities (CAs) which ++ * then issue X.509 public key certificates. ++ * ++ * @param myname X.500 name of the subject ++ * @exception InvalidKeyException on key handling errors. ++ * @exception SignatureException on signature handling errors. ++ */ ++ public PKCS10 getCertRequest(X500Name myname) ++ throws InvalidKeyException, SignatureException { ++ PKCS10 req = new PKCS10(publicKey); ++ ++ try { ++ req.encodeAndSign(getSigner(myname)); ++ ++ } catch (CertificateException e) { ++ throw new SignatureException(sigAlg + " CertificateException"); ++ ++ } catch (IOException e) { ++ throw new SignatureException(sigAlg + " IOException"); ++ ++ } catch (NoSuchAlgorithmException e) { ++ // "can't happen" ++ throw new SignatureException(sigAlg + " unavailable?"); ++ } ++ return req; ++ } ++ ++ private X500Signer getSigner(X500Name me) ++ throws InvalidKeyException, NoSuchAlgorithmException { ++ Signature signature = Signature.getInstance(sigAlg); ++ ++ signature.initSign(privateKey); ++ return new X500Signer(signature, me); ++ } ++ ++ private String sigAlg; ++ private KeyPairGenerator keyGen; ++ private X509Key publicKey; ++ private PrivateKey privateKey; ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertAttrSet.java b/org/mozilla/jss/netscape/security/x509/CertAttrSet.java +new file mode 100755 +index 00000000..7527e5a4 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertAttrSet.java +@@ -0,0 +1,120 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++ ++/** ++ * This interface defines the methods required of a certificate attribute. ++ * Examples of X.509 certificate attributes are Validity, Issuer_Name, and ++ * Subject Name. A CertAttrSet may compromise one attribute or many ++ * attributes. ++ *

++ * A CertAttrSet itself can also be comprised of other sub-sets. In the case of X.509 V3 certificates, for example, the ++ * "extensions" attribute has subattributes, such as those for KeyUsage and AuthorityKeyIdentifier. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.9 ++ * @see CertificateException ++ */ ++public interface CertAttrSet { ++ /** ++ * Returns a short string describing this certificate attribute. ++ * ++ * @return value of this certificate attribute in ++ * printable form. ++ */ ++ String toString(); ++ ++ /** ++ * Encodes the attribute to the output stream in a format ++ * that can be parsed by the decode method. ++ * ++ * @param out the OutputStream to encode the attribute to. ++ * ++ * @exception CertificateException on encoding or validity errors. ++ * @exception IOException on other errors. ++ */ ++ void encode(OutputStream out) ++ throws CertificateException, IOException; ++ ++ /** ++ * Decodes the attribute in the input stream. ++ * ++ * @param in the InputStream to read the encoded attribute from. ++ * ++ * @exception CertificateException on decoding or validity errors. ++ * @exception IOException on other errors. ++ */ ++ void decode(InputStream in) ++ throws CertificateException, IOException; ++ ++ /** ++ * Sets an attribute value within this CertAttrSet. ++ * ++ * @param name the name of the attribute (e.g. "x509.info.key") ++ * @param obj the attribute object. ++ * ++ * @exception CertificateException on attribute handling errors. ++ * @exception IOException on other errors. ++ */ ++ void set(String name, Object obj) ++ throws CertificateException, IOException; ++ ++ /** ++ * Gets an attribute value for this CertAttrSet. ++ * ++ * @param name the name of the attribute to return. ++ * ++ * @exception CertificateException on attribute handling errors. ++ * @exception IOException on other errors. ++ */ ++ Object get(String name) ++ throws CertificateException, IOException; ++ ++ /** ++ * Deletes an attribute value from this CertAttrSet. ++ * ++ * @param name the name of the attribute to delete. ++ * ++ * @exception CertificateException on attribute handling errors. ++ * @exception IOException on other errors. ++ */ ++ void delete(String name) ++ throws CertificateException, IOException; ++ ++ /** ++ * Returns an enumeration of the names of the attributes existing within ++ * this attribute. ++ * ++ * @return an enumeration of the attribute names. ++ */ ++ Enumeration getAttributeNames(); ++ ++ /** ++ * Returns the name (identifier) of this CertAttrSet. ++ * ++ * @return the name of this CertAttrSet. ++ */ ++ String getName(); ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertException.java b/org/mozilla/jss/netscape/security/x509/CertException.java +new file mode 100644 +index 00000000..6696fd4a +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertException.java +@@ -0,0 +1,165 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++/** ++ * CertException indicates one of a variety of certificate problems. ++ * ++ * @version 1.18 ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class CertException extends SecurityException { ++ ++ // Zero is reserved. ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -4046189948107720588L; ++ ++ /** Indicates that the signature in the certificate is not valid. */ ++ public static final int verf_INVALID_SIG = 1; ++ ++ /** Indicates that the certificate was revoked, and so is invalid. */ ++ public static final int verf_INVALID_REVOKED = 2; ++ ++ /** Indicates that the certificate is not yet valid. */ ++ public static final int verf_INVALID_NOTBEFORE = 3; ++ ++ /** Indicates that the certificate has expired and so is not valid. */ ++ public static final int verf_INVALID_EXPIRED = 4; ++ ++ /** ++ * Indicates that a certificate authority in the certification ++ * chain is not trusted. ++ */ ++ public static final int verf_CA_UNTRUSTED = 5; ++ ++ /** Indicates that the certification chain is too long. */ ++ public static final int verf_CHAIN_LENGTH = 6; ++ ++ /** Indicates an error parsing the ASN.1/DER encoding of the certificate. */ ++ public static final int verf_PARSE_ERROR = 7; ++ ++ /** Indicates an error constructing a certificate or certificate chain. */ ++ public static final int err_CONSTRUCTION = 8; ++ ++ /** Indicates a problem with the public key */ ++ public static final int err_INVALID_PUBLIC_KEY = 9; ++ ++ /** Indicates a problem with the certificate version */ ++ public static final int err_INVALID_VERSION = 10; ++ ++ /** Indicates a problem with the certificate format */ ++ public static final int err_INVALID_FORMAT = 11; ++ ++ /** Indicates a problem with the certificate encoding */ ++ public static final int err_ENCODING = 12; ++ ++ // Private data members ++ private int verfCode; ++ private String moreData; ++ ++ /** ++ * Constructs a certificate exception using an error code ++ * (verf_*) and a string describing the context ++ * of the error. ++ */ ++ public CertException(int code, String moredata) { ++ verfCode = code; ++ moreData = moredata; ++ } ++ ++ /** ++ * Constructs a certificate exception using just an error code, ++ * without a string describing the context. ++ */ ++ public CertException(int code) { ++ verfCode = code; ++ } ++ ++ /** ++ * Returns the error code with which the exception was created. ++ */ ++ public int getVerfCode() { ++ return verfCode; ++ } ++ ++ /** ++ * Returns a string describing the context in which the exception ++ * was reported. ++ */ ++ public String getMoreData() { ++ return moreData; ++ } ++ ++ /** ++ * Return a string corresponding to the error code used to create ++ * this exception. ++ */ ++ public String getVerfDescription() { ++ switch (verfCode) { ++ case verf_INVALID_SIG: ++ return "The signature in the certificate is not valid."; ++ case verf_INVALID_REVOKED: ++ return "The certificate has been revoked."; ++ case verf_INVALID_NOTBEFORE: ++ return "The certificate is not yet valid."; ++ case verf_INVALID_EXPIRED: ++ return "The certificate has expired."; ++ case verf_CA_UNTRUSTED: ++ return "The Authority which issued the certificate is not trusted."; ++ case verf_CHAIN_LENGTH: ++ return "The certificate path to a trusted authority is too long."; ++ case verf_PARSE_ERROR: ++ return "The certificate could not be parsed."; ++ case err_CONSTRUCTION: ++ return "There was an error when constructing the certificate."; ++ case err_INVALID_PUBLIC_KEY: ++ return "The public key was not in the correct format."; ++ case err_INVALID_VERSION: ++ return "The certificate has an invalid version number."; ++ case err_INVALID_FORMAT: ++ return "The certificate has an invalid format."; ++ case err_ENCODING: ++ return "Problem encountered while encoding the data."; ++ ++ default: ++ return "Unknown code: " + verfCode; ++ } ++ } ++ ++ /** ++ * Returns a string describing the certificate exception. ++ */ ++ public String toString() { ++ return "[Certificate Exception: " + getMessage() + "]"; ++ } ++ ++ /** ++ * Returns a string describing the certificate exception. ++ */ ++ public String getMessage() { ++ return getVerfDescription() ++ + ((moreData != null) ++ ? ("\n (" + moreData + ")") : ""); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertParseError.java b/org/mozilla/jss/netscape/security/x509/CertParseError.java +new file mode 100644 +index 00000000..c03627a6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertParseError.java +@@ -0,0 +1,40 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++//back out these changes until backwards compatibility with ++//CertException is not an issue. ++//import java.security.CertificateException; ++ ++/** ++ * CertException indicates one of a variety of certificate problems. ++ * ++ * @version 1.7 ++ * @author David Brownell ++ */ ++ ++class CertParseError extends CertException { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -7623327377774730807L; ++ ++ CertParseError(String where) { ++ super(CertException.verf_PARSE_ERROR, where); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateAlgorithmId.java b/org/mozilla/jss/netscape/security/x509/CertificateAlgorithmId.java +new file mode 100644 +index 00000000..1071a6dd +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateAlgorithmId.java +@@ -0,0 +1,187 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the AlgorithmId for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.7 ++ */ ++public class CertificateAlgorithmId implements CertAttrSet, Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 6084780721443376563L; ++ ++ private AlgorithmId algId; ++ ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.algorithmID"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "algorithmID"; ++ public static final String ALGORITHM = "algorithm"; ++ ++ /** ++ * Default constructor for the certificate attribute. ++ * ++ * @param algId the Algorithm identifier ++ */ ++ public CertificateAlgorithmId(AlgorithmId algId) { ++ this.algId = algId; ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the serial number from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateAlgorithmId(DerInputStream in) throws IOException { ++ DerValue val = in.getDerValue(); ++ algId = AlgorithmId.parse(val); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the serial number from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateAlgorithmId(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ algId = AlgorithmId.parse(val); ++ } ++ ++ /** ++ * Return the algorithm identifier as user readable string. ++ */ ++ public String toString() { ++ if (algId == null) ++ return ""; ++ return (algId.toString() + ++ ", OID = " + (algId.getOID()).toString() + "\n"); ++ } ++ ++ private void writeObject(ObjectOutputStream stream) throws IOException { ++ encode(stream); ++ } ++ ++ private void readObject(ObjectInputStream stream) throws IOException { ++ decode(stream); ++ } ++ ++ /** ++ * Encode the algorithm identifier in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ algId.encode(tmp); ++ ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the algorithm identifier from the passed stream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ algId = AlgorithmId.parse(derVal); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof AlgorithmId)) { ++ throw new IOException("Attribute must be of type AlgorithmId."); ++ } ++ if (name.equalsIgnoreCase(ALGORITHM)) { ++ algId = (AlgorithmId) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateAlgorithmId."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(ALGORITHM)) { ++ return (algId); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateAlgorithmId."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(ALGORITHM)) { ++ algId = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateAlgorithmId."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(ALGORITHM); ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateChain.java b/org/mozilla/jss/netscape/security/x509/CertificateChain.java +new file mode 100644 +index 00000000..5e026772 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateChain.java +@@ -0,0 +1,139 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.security.cert.X509Certificate; ++ ++import org.mozilla.jss.netscape.security.pkcs.ContentInfo; ++import org.mozilla.jss.netscape.security.pkcs.PKCS7; ++import org.mozilla.jss.netscape.security.pkcs.SignerInfo; ++ ++public class CertificateChain implements Serializable { ++ public CertificateChain() { ++ } ++ ++ /** ++ * constructs a certificate chain from a certificate. ++ * ++ * @param cert a certificate ++ */ ++ public CertificateChain(X509Certificate cert) { ++ mChain = new X509Certificate[1]; ++ mChain[0] = cert; ++ } ++ ++ /** ++ * constructs a certificate chain from a X509 certificate array. ++ * ++ * @param chain a certificate array. ++ */ ++ public CertificateChain(X509Certificate[] chain) { ++ mChain = chain.clone(); ++ } ++ ++ /** ++ * returns the certificate at specified index in chain. ++ * ++ * @param index the index. ++ * @return the X509 certificate at the given index. ++ */ ++ public X509Certificate getCertificate(int index) { ++ return mChain[index]; ++ } ++ ++ /** ++ * returns the first certificate in chain. ++ * ++ * @return the X509 certificate at the given index. ++ */ ++ public X509Certificate getFirstCertificate() { ++ return mChain[0]; ++ } ++ ++ /** ++ * returns the certificate chain as an array of X509 certificates. ++ * ++ * @return an array of X509 Certificates. ++ */ ++ public X509Certificate[] getChain() { ++ return mChain.clone(); ++ } ++ ++ public void encode(OutputStream out) ++ throws IOException { ++ encode(out, true); ++ } ++ ++ /** ++ * encode in PKCS7 blob. ++ */ ++ public void encode(OutputStream out, boolean sort) ++ throws IOException { ++ PKCS7 p7 = new PKCS7(new AlgorithmId[0], ++ new ContentInfo(new byte[0]), mChain, ++ new SignerInfo[0]); ++ p7.encodeSignedData(out, sort); ++ } ++ ++ /** ++ * decode from PKCS7 blob. ++ */ ++ public void decode(InputStream in) ++ throws IOException { ++ PKCS7 p7 = new PKCS7(in); ++ mChain = p7.getCertificates(); ++ } ++ ++ /** ++ * for serialization ++ */ ++ private void writeObject(java.io.ObjectOutputStream out) ++ throws IOException { ++ encode(out); ++ } ++ ++ /** ++ * for serialization ++ */ ++ private void readObject(java.io.ObjectInputStream in) ++ throws IOException { ++ decode(in); ++ } ++ ++ /** ++ * Converts the certificate chain to a readable string. ++ */ ++ public String toString() { ++ ++ String s = "[\n"; ++ if (mChain == null) ++ return "[empty]"; ++ StringBuffer tempBuffer = new StringBuffer(); ++ for (int i = 0; i < mChain.length; i++) { ++ tempBuffer.append(mChain[i].toString()); ++ } ++ s += tempBuffer.toString() + "]\n"; ++ return s; ++ } ++ ++ private X509Certificate[] mChain = null; ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateExtensions.java b/org/mozilla/jss/netscape/security/x509/CertificateExtensions.java +new file mode 100644 +index 00000000..c449ad49 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateExtensions.java +@@ -0,0 +1,303 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.lang.reflect.Array; ++import java.lang.reflect.Constructor; ++import java.lang.reflect.InvocationTargetException; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the Extensions attribute for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.11 ++ * @see CertAttrSet ++ */ ++public class CertificateExtensions extends Vector ++ implements CertAttrSet, Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -7172635300185788849L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions"; ++ /** ++ * name ++ */ ++ public static final String NAME = "extensions"; ++ ++ private Hashtable map; ++ ++ // Parse the encoded extension ++ public void parseExtension(Extension ext) throws IOException { ++ try { ++ @SuppressWarnings("unchecked") ++ Class extClass = (Class) OIDMap.getClass(ext.getExtensionId()); ++ if (extClass == null) { // Unsupported extension ++ map.put(ext.getExtensionId().toString(), ext); ++ addElement(ext); ++ return; ++ } ++ Class[] params = { Boolean.class, Object.class }; ++ Constructor cons = extClass.getConstructor(params); ++ ++ byte[] extData = ext.getExtensionValue(); ++ int extLen = extData.length; ++ Object value = Array.newInstance(byte.class, extLen); ++ ++ for (int i = 0; i < extLen; i++) { ++ Array.setByte(value, i, extData[i]); ++ } ++ Object[] passed = new Object[] { Boolean.valueOf(ext.isCritical()), ++ value }; ++ CertAttrSet certExt = cons.newInstance(passed); ++ if (certExt != null && certExt.getName() != null) { ++ map.put(certExt.getName(), (Extension) certExt); ++ addElement((Extension) certExt); ++ } ++ ++ } catch (NoSuchMethodException e) { ++ throw new IOException(e); ++ ++ } catch (InvocationTargetException e) { ++ Throwable t = e.getTargetException(); ++ if (t instanceof IOException) { ++ throw (IOException)t; ++ } ++ throw new IOException(t); ++ ++ } catch (Exception e) { ++ throw new IOException(e); ++ } ++ } ++ ++ /** ++ * Default constructor for the certificate attribute. ++ */ ++ public CertificateExtensions() { ++ map = new Hashtable(); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the Extension from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateExtensions(DerInputStream in) ++ throws IOException { ++ ++ map = new Hashtable(); ++ DerValue[] exts = in.getSequence(5); ++ ++ for (int i = 0; i < exts.length; i++) { ++ Extension ext = new Extension(exts[i]); ++ parseExtension(ext); ++ } ++ } ++ ++ /** ++ * Decode the extensions from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ DerInputStream str = val.toDerInputStream(); ++ ++ map = new Hashtable(); ++ DerValue[] exts = str.getSequence(5); ++ ++ for (int i = 0; i < exts.length; i++) { ++ Extension ext = new Extension(exts[i]); ++ parseExtension(ext); ++ } ++ } ++ ++ /** ++ * Decode the extensions from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decodeEx(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ DerInputStream str = null; ++ if (val.isConstructed() && val.isContextSpecific((byte) 3)) { ++ str = val.data; ++ } else { ++ str = val.toDerInputStream(); ++ } ++ ++ map = new Hashtable(); ++ DerValue[] exts = str.getSequence(5); ++ ++ for (int i = 0; i < exts.length; i++) { ++ Extension ext = new Extension(exts[i]); ++ parseExtension(ext); ++ } ++ } ++ ++ private void writeObject(ObjectOutputStream stream) throws CertificateException, IOException { ++ encode(stream); ++ } ++ ++ private void readObject(ObjectInputStream stream) throws CertificateException, IOException { ++ decodeEx(stream); ++ } ++ ++ /** ++ * Encode the extensions in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception CertificateException on encoding errors. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) ++ throws CertificateException, IOException { ++ try (DerOutputStream tmp = new DerOutputStream()) { ++ DerOutputStream extOut = new DerOutputStream(); ++ for (int i = 0; i < size(); i++) { ++ Object thisOne = elementAt(i); ++ if (thisOne instanceof CertAttrSet) ++ ((CertAttrSet) thisOne).encode(extOut); ++ else if (thisOne instanceof Extension) ++ ((Extension) thisOne).encode(extOut); ++ else ++ throw new CertificateException("Invalid extension object"); ++ } ++ ++ DerOutputStream seq = new DerOutputStream(); ++ seq.write(DerValue.tag_Sequence, extOut); ++ ++ tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 3), ++ seq); ++ ++ out.write(tmp.toByteArray()); ++ } ++ } ++ ++ /** ++ * Set the attribute value. ++ * ++ * @param name the extension name used in the cache. ++ * @param obj the object to set. ++ * @exception IOException if the object could not be cached. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ map.put(name, (Extension) obj); ++ addElement((Extension) obj); ++ } ++ ++ /** ++ * Get the attribute value. ++ * ++ * @param name the extension name used in the lookup. ++ * @exception IOException if named extension is not found. ++ */ ++ public Object get(String name) throws IOException { ++ Object obj = map.get(name); ++ if (obj == null) { ++ throw new IOException("No extension found with name " + name); ++ } ++ return (obj); ++ } ++ ++ /** ++ * Delete the attribute value. ++ * ++ * @param name the extension name used in the lookup. ++ * @exception IOException if named extension is not found. ++ */ ++ public void delete(String name) throws IOException { ++ Object obj = map.get(name); ++ if (obj == null) { ++ throw new IOException("No extension found with name " + name); ++ } ++ map.remove(name); ++ removeElement(obj); ++ } ++ ++ public Enumeration getNames() { ++ return map.keys(); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributes() { ++ return (map.elements()); ++ } ++ ++ public Enumeration getAttributeNames() { ++ return (map.keys()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = super.hashCode(); ++ result = prime * result + ((map == null) ? 0 : map.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (!super.equals(obj)) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ CertificateExtensions other = (CertificateExtensions) obj; ++ if (map == null) { ++ if (other.map != null) ++ return false; ++ } else if (!map.equals(other.map)) ++ return false; ++ return true; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateIssuerExtension.java b/org/mozilla/jss/netscape/security/x509/CertificateIssuerExtension.java +new file mode 100644 +index 00000000..85d94b94 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateIssuerExtension.java +@@ -0,0 +1,241 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the CRL Certificate Issuer Extension. ++ * ++ *

++ * This CRL entry extension identifies the certificate issuer associated with an entry in an indirect CRL, i.e. a CRL ++ * that has the indirectCRL indicator set in its issuing distribution point extension. ++ * ++ * @see Extension ++ * @see CertAttrSet ++ */ ++ ++public class CertificateIssuerExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 8643788952936025986L; ++ /** ++ * Attribute name. ++ */ ++ public static final String NAME = "CertificateIssuer"; ++ public static final String CERTIFICATE_ISSUER = "value"; ++ ++ /** ++ * The Object Identifier for this extension. ++ */ ++ public static final String OID = "2.5.29.29"; ++ ++ // private data members ++ GeneralNames names = null; ++ ++ static { ++ try { ++ OIDMap.addAttribute(CertificateIssuerExtension.class.getName(), ++ OID, NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ // Encode this extension ++ private void encodeThis() throws IOException { ++ DerOutputStream os = new DerOutputStream(); ++ try { ++ names.encode(os); ++ } catch (GeneralNamesException e) { ++ throw new IOException(e); ++ } ++ this.extensionValue = os.toByteArray(); ++ } ++ ++ /** ++ * Create a CertificateIssuerExtension with the passed GeneralNames ++ * and criticality. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param names the GeneralNames for the issuer. ++ * @exception IOException on error. ++ */ ++ public CertificateIssuerExtension(Boolean critical, GeneralNames names) ++ throws IOException { ++ this.names = names; ++ this.extensionId = PKIXExtensions.CertificateIssuer_Id; ++ this.critical = critical.booleanValue(); ++ encodeThis(); ++ } ++ ++ /** ++ * Create a CertificateIssuerExtension with the passed GeneralNames. ++ * ++ * @param names the GeneralNames for the issuer. ++ * @exception IOException on error. ++ */ ++ public CertificateIssuerExtension(GeneralNames names) ++ throws IOException { ++ this.names = names; ++ this.extensionId = PKIXExtensions.CertificateIssuer_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a default CertificateIssuerExtension. ++ */ ++ public CertificateIssuerExtension() { ++ extensionId = PKIXExtensions.CertificateIssuer_Id; ++ critical = false; ++ names = new GeneralNames(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public CertificateIssuerExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.CertificateIssuer_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ try { ++ names = new GeneralNames(val); ++ } catch (GeneralNamesException e) { ++ throw new IOException("CertificateIssuerExtension: " + e, e); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the CertificateIssuerName. ++ */ ++ public String toString() { ++ if (names == null) ++ return ""; ++ String s = super.toString() + "CertificateIssuerName [\n" ++ + names.toString() + "]\n"; ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding error. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = PKIXExtensions.CertificateIssuer_Id; ++ critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (name.equalsIgnoreCase(CERTIFICATE_ISSUER)) { ++ if (!(obj instanceof GeneralNames)) { ++ throw new IOException("Attribute value should be of" + ++ " type GeneralNames."); ++ } ++ names = (GeneralNames) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateIssuerName."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(CERTIFICATE_ISSUER)) { ++ return (names); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateIssuerName."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(CERTIFICATE_ISSUER)) { ++ names = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateIssuerName."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(CERTIFICATE_ISSUER); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateIssuerName.java b/org/mozilla/jss/netscape/security/x509/CertificateIssuerName.java +new file mode 100644 +index 00000000..ee075ebd +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateIssuerName.java +@@ -0,0 +1,172 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the X500Name attribute for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.6 ++ * @see CertAttrSet ++ */ ++public class CertificateIssuerName implements CertAttrSet { ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.issuer"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "issuer"; ++ public static final String DN_NAME = "dname"; ++ ++ // Private data member ++ private X500Name dnName; ++ ++ /** ++ * Default constructor for the certificate attribute. ++ * ++ * @param name the X500Name ++ */ ++ public CertificateIssuerName(X500Name name) { ++ this.dnName = name; ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the X500Name from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateIssuerName(DerInputStream in) throws IOException { ++ dnName = new X500Name(in); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the X500Name from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateIssuerName(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ dnName = new X500Name(derVal); ++ } ++ ++ /** ++ * Return the name as user readable string. ++ */ ++ public String toString() { ++ if (dnName == null) ++ return ""; ++ return (dnName.toString()); ++ } ++ ++ /** ++ * Encode the name in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ dnName.encode(tmp); ++ ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the name in DER form from the stream. ++ * ++ * @param in the InputStream to marshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ dnName = new X500Name(derVal); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof X500Name)) { ++ throw new IOException("Attribute must be of type X500Name."); ++ } ++ if (name.equalsIgnoreCase(DN_NAME)) { ++ this.dnName = (X500Name) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateIssuerName."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(DN_NAME)) { ++ return (dnName); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateIssuerName."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(DN_NAME)) { ++ dnName = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateIssuerName."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(DN_NAME); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateIssuerUniqueIdentity.java b/org/mozilla/jss/netscape/security/x509/CertificateIssuerUniqueIdentity.java +new file mode 100644 +index 00000000..a0ea4ec0 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateIssuerUniqueIdentity.java +@@ -0,0 +1,185 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the subject/issuer unique identity attribute ++ * for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.6 ++ * @see CertAttrSet ++ */ ++public class CertificateIssuerUniqueIdentity implements CertAttrSet { ++ private UniqueIdentity id; ++ ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.issuerID"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "issuerID"; ++ public static final String ID = "id"; ++ ++ /** ++ * Default constructor for the certificate attribute. ++ * ++ * @param key the UniqueIdentity ++ */ ++ public CertificateIssuerUniqueIdentity(UniqueIdentity id) { ++ this.id = id; ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the UniqueIdentity from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateIssuerUniqueIdentity(DerInputStream in) ++ throws IOException { ++ id = new UniqueIdentity(in); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the UniqueIdentity from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateIssuerUniqueIdentity(InputStream in) ++ throws IOException { ++ DerValue val = new DerValue(in); ++ id = new UniqueIdentity(val); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER value. ++ * ++ * @param in the DerValue to read the UniqueIdentity from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateIssuerUniqueIdentity(DerValue val) ++ throws IOException { ++ id = new UniqueIdentity(val); ++ } ++ ++ /** ++ * Return the identity as user readable string. ++ */ ++ public String toString() { ++ if (id == null) ++ return ""; ++ return (id.toString()); ++ } ++ ++ /** ++ * Decode the identity in DER form from the stream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ id = new UniqueIdentity(val); ++ } ++ ++ /** ++ * Encode the identity in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ id.encode(tmp, DerValue.createTag(DerValue.TAG_CONTEXT, false, (byte) 1)); ++ ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof UniqueIdentity)) { ++ throw new IOException("Attribute must be of type UniqueIdentity."); ++ } ++ if (name.equalsIgnoreCase(ID)) { ++ id = (UniqueIdentity) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateIssuerUniqueIdentity."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(ID)) { ++ return (id); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateIssuerUniqueIdentity."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(ID)) { ++ id = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateIssuerUniqueIdentity."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(ID); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificatePoliciesExtension.java b/org/mozilla/jss/netscape/security/x509/CertificatePoliciesExtension.java +new file mode 100644 +index 00000000..b3427c22 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificatePoliciesExtension.java +@@ -0,0 +1,339 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Arrays; ++import java.util.Collections; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++import org.mozilla.jss.netscape.security.util.Utils; ++ ++/** ++ * This class defines the Certificate Policies Extension. ++ * ++ *

++ * The certificate policies extension conatins a sequence of policy information terms, each of which consists of an ++ * object identifier (OID) and optional qualifiers. These policy information terms indicate the policy under which the ++ * certificate has been issued and the purposes for which the certificate may be used. Aplications with specific policy ++ * requirements are expected to have a list of those policies which they will accept and to compare the policy OIDs in ++ * the certificate to that list. If this extension is critical, the path validation software must be able to interpret ++ * this extension, or must reject the certificate. ++ * ++ *

++ * CertificatePolicies ::= SEQUENECE OF PolicyInformation
++ * 
++ * ++ * @author Christine Ho ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class CertificatePoliciesExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -3729294064061837367L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.CertificatePolicies"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "CertificatePolicies"; ++ public static final String INFOS = "infos"; ++ ++ // Private data members ++ private Vector mInfos; ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ try (DerOutputStream os = new DerOutputStream();) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ for (int i = 0; i < mInfos.size(); i++) { ++ mInfos.elementAt(i).encode(tmp); ++ } ++ os.write(DerValue.tag_Sequence, tmp); ++ extensionValue = os.toByteArray(); ++ } ++ } ++ ++ public CertificatePoliciesExtension(boolean critical, Vector infos) throws IOException { ++ this.mInfos = infos; ++ this.extensionId = PKIXExtensions.CertificatePolicies_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a CertificatePolicies with the Vector of CertificatePolicyInfo. ++ * ++ * @param infos the Vector of CertificatePolicyInfo. ++ */ ++ public CertificatePoliciesExtension(Vector infos) throws IOException { ++ this.mInfos = infos; ++ this.extensionId = PKIXExtensions.CertificatePolicies_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a default CertificatePoliciesExtension. ++ */ ++ public CertificatePoliciesExtension() { ++ this.extensionId = PKIXExtensions.CertificatePolicies_Id; ++ critical = false; ++ mInfos = new Vector(1, 1); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public CertificatePoliciesExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.CertificatePolicies_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for " + ++ "CertificatePoliciesExtension."); ++ } ++ mInfos = new Vector(1, 1); ++ while (val.data.available() != 0) { ++ DerValue seq = val.data.getDerValue(); ++ CertificatePolicyInfo info = new CertificatePolicyInfo(seq); ++ mInfos.addElement(info); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the policy extension. ++ */ ++ public String toString() { ++ if (mInfos == null) ++ return ""; ++ String s = super.toString() + "Certificate Policies [\n" ++ + mInfos.toString() + "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = PKIXExtensions.CertificatePolicies_Id; ++ critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ @SuppressWarnings("unchecked") ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(INFOS)) { ++ if (!(obj instanceof Vector)) { ++ throw new IOException("Attribute value should be of" + ++ " type Vector."); ++ } ++ mInfos = (Vector) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificatePoliciesExtension."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(INFOS)) { ++ return (mInfos); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificatePoliciesExtension."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(INFOS)) { ++ mInfos = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificatePoliciesExtension."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration> getAttributes() { ++ Vector> elements = new Vector>(); ++ elements.addElement(mInfos); ++ return (elements.elements()); ++ } ++ ++ private static final String[] NAMES = { INFOS }; ++ ++ @Override ++ public Enumeration getAttributeNames() { ++ // TODO Auto-generated method stub ++ return Collections.enumeration(Arrays.asList(NAMES)); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ public static void main(String args[]) { ++ ++ /** ++ * From ASN.1 dump ++ * ++ * 0 30 133: SEQUENCE { ++ * 3 30 45: . SEQUENCE { ++ * 5 06 3: . . OBJECT IDENTIFIER '1 2 3 5' ++ * 10 30 38: . . SEQUENCE { ++ * 12 30 36: . . . SEQUENCE { ++ * 14 06 8: . . . . OBJECT IDENTIFIER cps (1 3 6 1 5 5 7 2 1) ++ * : . . . . . (PKIX policy qualifier) ++ * 24 16 24: . . . . IA5String 'http://home.netscape.com' ++ * : . . . . } ++ * : . . . } ++ * : . . } ++ * 50 30 84: . SEQUENCE { ++ * 52 06 2: . . OBJECT IDENTIFIER '2 3 5' ++ * 56 30 78: . . SEQUENCE { ++ * 58 30 36: . . . SEQUENCE { ++ * 60 06 8: . . . . OBJECT IDENTIFIER cps (1 3 6 1 5 5 7 2 1) ++ * : . . . . . (PKIX policy qualifier) ++ * 70 16 24: . . . . IA5String 'http://home.netscape.com' ++ * : . . . . } ++ * 96 30 38: . . . SEQUENCE { ++ * 98 06 8: . . . . OBJECT IDENTIFIER unotice (1 3 6 1 5 5 7 2 2) ++ * : . . . . . (PKIX policy qualifier) ++ * 108 30 26: . . . . SEQUENCE { ++ * 110 30 16: . . . . . SEQUENCE { ++ * 112 1E 8: . . . . . . BMPString (1993) '_..o.r.g' ++ * 122 02 1: . . . . . . INTEGER 1 ++ * 125 02 1: . . . . . . INTEGER 2 ++ * : . . . . . . } ++ * 128 1E 6: . . . . . BMPString (1993) '_..d.t' ++ * : . . . . . } ++ * : . . . . } ++ * : . . . } ++ * : . . } ++ * : . } ++ **/ ++ ++ CertificatePolicyId plcyId0 = new CertificatePolicyId( ++ new ObjectIdentifier("1.2.3.5") ++ ); ++ PolicyQualifiers qualifiers0 = new PolicyQualifiers(); ++ CPSuri cpsQualifier0 = new CPSuri("http://home.netscape.com"); ++ PolicyQualifierInfo qualifierInfo0 = new PolicyQualifierInfo( ++ PolicyQualifierInfo.QT_CPS, ++ cpsQualifier0 ++ ); ++ qualifiers0.add(qualifierInfo0); ++ CertificatePolicyInfo info0 = new CertificatePolicyInfo( ++ plcyId0, qualifiers0); ++ CertificatePolicyId plcyId1 = new CertificatePolicyId( ++ new ObjectIdentifier("2.3.5") ++ ); ++ PolicyQualifiers qualifiers1 = new PolicyQualifiers(); ++ DisplayText org1 = new DisplayText(DisplayText.tag_BMPString, ++ "org"); ++ int nums[] = { 1, 2 }; ++ NoticeReference nr1 = new NoticeReference(org1, nums); ++ DisplayText dt1 = new DisplayText(DisplayText.tag_BMPString, ++ "dt"); ++ UserNotice userNotice1 = new UserNotice(nr1, dt1); ++ PolicyQualifierInfo qualifierInfo1 = new PolicyQualifierInfo( ++ PolicyQualifierInfo.QT_UNOTICE, ++ userNotice1 ++ ); ++ qualifiers1.add(qualifierInfo0); ++ qualifiers1.add(qualifierInfo1); ++ CertificatePolicyInfo info1 = new CertificatePolicyInfo( ++ plcyId1, qualifiers1); ++ Vector infos = new Vector(); ++ infos.addElement(info0); ++ infos.addElement(info1); ++ try { ++ CertificatePoliciesExtension ext = ++ new CertificatePoliciesExtension(infos); ++ ++ // BASE64 encode the whole thing and write it to stdout ++ System.out.println(Utils.base64encode(ext.getExtensionValue(), true)); ++ } catch (IOException e) { ++ System.out.println(e.toString()); ++ } ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificatePolicyId.java b/org/mozilla/jss/netscape/security/x509/CertificatePolicyId.java +new file mode 100644 +index 00000000..6b86910f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificatePolicyId.java +@@ -0,0 +1,85 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * Represent the CertificatePolicyId ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.5 ++ */ ++public class CertificatePolicyId implements java.io.Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -2376810529862707757L; ++ private ObjectIdentifier id; ++ ++ /** ++ * Create a CertificatePolicyId with the ObjectIdentifier. ++ * ++ * @param id the ObjectIdentifier for the policy id. ++ */ ++ public CertificatePolicyId(ObjectIdentifier id) { ++ this.id = id; ++ } ++ ++ /** ++ * Create the object from its Der encoded value. ++ * ++ * @param val the DER encoded value for the same. ++ */ ++ public CertificatePolicyId(DerValue val) throws IOException { ++ this.id = val.getOID(); ++ } ++ ++ /** ++ * Return the value of the CertificatePolicyId as an ObjectIdentifier. ++ */ ++ public ObjectIdentifier getIdentifier() { ++ return (id); ++ } ++ ++ /** ++ * Returns a printable representation of the CertificatePolicyId. ++ */ ++ public String toString() { ++ String s = "CertificatePolicyId: [" ++ + id.toString() ++ + "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Write the CertificatePolicyId to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putOID(id); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificatePolicyInfo.java b/org/mozilla/jss/netscape/security/x509/CertificatePolicyInfo.java +new file mode 100644 +index 00000000..558a7fd1 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificatePolicyInfo.java +@@ -0,0 +1,110 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the CertificatePolicyInformation ASN.1 object. ++ * ++ * @author Christine Ho ++ */ ++public class CertificatePolicyInfo implements java.io.Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -8516006396099280477L; ++ private CertificatePolicyId mPolicyIdentifier; ++ private PolicyQualifiers mPolicyQualifiers; ++ ++ /** ++ * Create a CertificatePolicyInfo with the passed CertificatePolicyId's. ++ * ++ * @param id the CertificatePolicyId. ++ */ ++ public CertificatePolicyInfo(CertificatePolicyId id) { ++ this.mPolicyIdentifier = id; ++ this.mPolicyQualifiers = null; ++ } ++ ++ public CertificatePolicyInfo(CertificatePolicyId id, PolicyQualifiers qualifiers) { ++ this.mPolicyIdentifier = id; ++ this.mPolicyQualifiers = qualifiers; ++ } ++ ++ /** ++ * Create the CertificatePolicyInfo from the DER encoded value. ++ * ++ * @param val the DER encoded value of the same. ++ */ ++ public CertificatePolicyInfo(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for CertificatePolicyInfo"); ++ } ++ mPolicyIdentifier = new CertificatePolicyId(val.data.getDerValue()); ++ // The specification is not clear on whether qualifier is ++ // optional or not. GTE CyberTrust Root certificate has ++ // no qualifier. ++ if (val.data.available() == 0) { ++ mPolicyQualifiers = null; ++ } else { ++ mPolicyQualifiers = new PolicyQualifiers(val.data.getDerValue()); ++ } ++ } ++ ++ /** ++ * return the policy identifier of the policy info ++ */ ++ public CertificatePolicyId getPolicyIdentifier() { ++ return (mPolicyIdentifier); ++ } ++ ++ public PolicyQualifiers getPolicyQualifiers() { ++ return mPolicyQualifiers; ++ } ++ ++ /** ++ * Returns a printable representation of the CertificatePolicyId. ++ */ ++ public String toString() { ++ String s = "CertificatePolicyInfo: [\n" ++ + "PolicyIdentifier:" + mPolicyIdentifier.toString() ++ ++ + "]\n"; ++ return (s); ++ } ++ ++ /** ++ * Write the CertificatePolicyInfo to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ mPolicyIdentifier.encode(tmp); ++ if (mPolicyQualifiers != null) { ++ mPolicyQualifiers.encode(tmp); ++ } ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificatePolicyMap.java b/org/mozilla/jss/netscape/security/x509/CertificatePolicyMap.java +new file mode 100644 +index 00000000..9dc8d909 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificatePolicyMap.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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the CertificatePolicyMap ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.5 ++ */ ++public class CertificatePolicyMap { ++ private CertificatePolicyId issuerDomain; ++ private CertificatePolicyId subjectDomain; ++ ++ /** ++ * Create a CertificatePolicyMap with the passed CertificatePolicyId's. ++ * ++ * @param issuer the CertificatePolicyId for the issuer CA. ++ * @param subject the CertificatePolicyId for the subject CA. ++ */ ++ public CertificatePolicyMap(CertificatePolicyId issuer, ++ CertificatePolicyId subject) { ++ this.issuerDomain = issuer; ++ this.subjectDomain = subject; ++ } ++ ++ /** ++ * Create the CertificatePolicyMap from the DER encoded value. ++ * ++ * @param val the DER encoded value of the same. ++ */ ++ public CertificatePolicyMap(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for CertificatePolicyMap"); ++ } ++ issuerDomain = new CertificatePolicyId(val.data.getDerValue()); ++ subjectDomain = new CertificatePolicyId(val.data.getDerValue()); ++ } ++ ++ /** ++ * Return the issuer CA part of the policy map. ++ */ ++ public CertificatePolicyId getIssuerIdentifier() { ++ return (issuerDomain); ++ } ++ ++ /** ++ * Return the subject CA part of the policy map. ++ */ ++ public CertificatePolicyId getSubjectIdentifier() { ++ return (subjectDomain); ++ } ++ ++ /** ++ * Returns a printable representation of the CertificatePolicyId. ++ */ ++ public String toString() { ++ String s = "CertificatePolicyMap: [\n" ++ + "IssuerDomain:" + issuerDomain.toString() ++ + "SubjectDomain:" + subjectDomain.toString() ++ + "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Write the CertificatePolicyMap to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ issuerDomain.encode(tmp); ++ subjectDomain.encode(tmp); ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificatePolicySet.java b/org/mozilla/jss/netscape/security/x509/CertificatePolicySet.java +new file mode 100644 +index 00000000..4e2ab295 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificatePolicySet.java +@@ -0,0 +1,86 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the certificate policy set ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.4 ++ */ ++public class CertificatePolicySet { ++ private Vector ids; ++ ++ /** ++ * The default constructor for this class. ++ * ++ * @param ids the sequence of CertificatePolicyId's. ++ */ ++ public CertificatePolicySet(Vector ids) { ++ this.ids = ids; ++ } ++ ++ /** ++ * Create the object from the DerValue. ++ * ++ * @param in the passed DerInputStream. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificatePolicySet(DerInputStream in) throws IOException { ++ ids = new Vector(1, 1); ++ DerValue[] seq = in.getSequence(5); ++ ++ for (int i = 0; i < seq.length; i++) { ++ CertificatePolicyId id = new CertificatePolicyId(seq[i]); ++ ids.addElement(id); ++ } ++ } ++ ++ /** ++ * Return printable form of the object. ++ */ ++ public String toString() { ++ String s = "CertificatePolicySet:[\n" ++ + ids.toString() ++ + "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Encode the policy set to the output stream. ++ * ++ * @param out the DerOutputStream to encode the data to. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ for (int i = 0; i < ids.size(); i++) { ++ ids.elementAt(i).encode(tmp); ++ } ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateSerialNumber.java b/org/mozilla/jss/netscape/security/x509/CertificateSerialNumber.java +new file mode 100644 +index 00000000..ee21610b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateSerialNumber.java +@@ -0,0 +1,191 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.math.BigInteger; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the SerialNumber attribute for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.6 ++ * @see CertAttrSet ++ */ ++public class CertificateSerialNumber implements CertAttrSet { ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.serialNumber"; ++ ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "serialNumber"; ++ public static final String NUMBER = "number"; ++ ++ private SerialNumber serial; ++ ++ /** ++ * Default constructor for the certificate attribute. ++ * ++ * @param serial the serial number for the certificate. ++ */ ++ public CertificateSerialNumber(BigInteger num) { ++ this.serial = new SerialNumber(num); ++ } ++ ++ /** ++ * Default constructor for the certificate attribute. ++ * ++ * @param serial the serial number for the certificate. ++ */ ++ public CertificateSerialNumber(int num) { ++ this.serial = new SerialNumber(num); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the serial number from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateSerialNumber(DerInputStream in) throws IOException { ++ serial = new SerialNumber(in); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the serial number from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateSerialNumber(InputStream in) throws IOException { ++ serial = new SerialNumber(in); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DerValue. ++ * ++ * @param val the DER encoded value. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateSerialNumber(DerValue val) throws IOException { ++ serial = new SerialNumber(val); ++ } ++ ++ /** ++ * Return the serial number as user readable string. ++ */ ++ public String toString() { ++ if (serial == null) ++ return ""; ++ return (serial.toString()); ++ } ++ ++ /** ++ * Encode the serial number in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ serial.encode(tmp); ++ ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the serial number in DER form from the stream. ++ * ++ * @param in the InputStream to marshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ serial = new SerialNumber(derVal); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof SerialNumber)) { ++ throw new IOException("Attribute must be of type SerialNumber."); ++ } ++ if (name.equalsIgnoreCase(NUMBER)) { ++ serial = (SerialNumber) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateSerialNumber."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(NUMBER)) { ++ return (serial); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateSerialNumber."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(NUMBER)) { ++ serial = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateSerialNumber."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(NUMBER); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateSubjectName.java b/org/mozilla/jss/netscape/security/x509/CertificateSubjectName.java +new file mode 100644 +index 00000000..63150779 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateSubjectName.java +@@ -0,0 +1,211 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the X500Name attribute for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.6 ++ * @see CertAttrSet ++ */ ++public class CertificateSubjectName implements CertAttrSet, Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 503643453152834350L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.subject"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "subject"; ++ public static final String DN_NAME = "dname"; ++ ++ // Private data member ++ private X500Name dnName; ++ ++ /** ++ * Default constructor for the certificate attribute. ++ * ++ * @param name the X500Name ++ */ ++ public CertificateSubjectName(X500Name name) { ++ this.dnName = name; ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the X500Name from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateSubjectName(DerInputStream in) throws IOException { ++ dnName = new X500Name(in); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the X500Name from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateSubjectName(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ dnName = new X500Name(derVal); ++ } ++ ++ /** ++ * Return the name as user readable string. ++ */ ++ public String toString() { ++ if (dnName == null) ++ return ""; ++ return (dnName.toString()); ++ } ++ ++ private void writeObject(ObjectOutputStream stream) throws IOException { ++ encode(stream); ++ } ++ ++ private void readObject(ObjectInputStream stream) throws IOException { ++ decodeEx(stream); ++ } ++ ++ /** ++ * Encode the name in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ dnName.encode(tmp); ++ ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the name in DER form from the stream. ++ * ++ * @param in the InputStream to marshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decodeEx(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ ++ // dnName = new X500Name(derVal); ++ dnName = new X500Name(derVal.toByteArray()); ++ } ++ ++ /** ++ * Decode the name in DER form from the stream. ++ * ++ * @param in the InputStream to marshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ ++ dnName = new X500Name(derVal); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof X500Name)) { ++ throw new IOException("Attribute must be of type X500Name."); ++ } ++ if (name.equalsIgnoreCase(DN_NAME)) { ++ this.dnName = (X500Name) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateSubjectName."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(DN_NAME)) { ++ return (dnName); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateSubjectName."); ++ } ++ } ++ ++ /** ++ * Get underlying X500Name value. ++ * ++ * Where the type is known to be CertificateSubjectName, use ++ * this method instead of 'get' to avoid casts and exceptions. ++ */ ++ public X500Name getX500Name() { ++ return dnName; ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(DN_NAME)) { ++ dnName = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:CertificateSubjectName."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(DN_NAME); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateSubjectUniqueIdentity.java b/org/mozilla/jss/netscape/security/x509/CertificateSubjectUniqueIdentity.java +new file mode 100644 +index 00000000..1033621f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateSubjectUniqueIdentity.java +@@ -0,0 +1,185 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the subject/issuer unique identity attribute ++ * for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.6 ++ * @see CertAttrSet ++ */ ++public class CertificateSubjectUniqueIdentity implements CertAttrSet { ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.subjectID"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "subjectID"; ++ public static final String ID = "id"; ++ ++ private UniqueIdentity id; ++ ++ /** ++ * Default constructor for the certificate attribute. ++ * ++ * @param key the UniqueIdentity ++ */ ++ public CertificateSubjectUniqueIdentity(UniqueIdentity id) { ++ this.id = id; ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the UniqueIdentity from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateSubjectUniqueIdentity(DerInputStream in) ++ throws IOException { ++ id = new UniqueIdentity(in); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the UniqueIdentity from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateSubjectUniqueIdentity(InputStream in) ++ throws IOException { ++ DerValue val = new DerValue(in); ++ id = new UniqueIdentity(val); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER value. ++ * ++ * @param in the DerValue to read the UniqueIdentity from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateSubjectUniqueIdentity(DerValue val) ++ throws IOException { ++ id = new UniqueIdentity(val); ++ } ++ ++ /** ++ * Return the identity as user readable string. ++ */ ++ public String toString() { ++ if (id == null) ++ return ""; ++ return (id.toString()); ++ } ++ ++ /** ++ * Decode the identity in DER form from the stream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ id = new UniqueIdentity(val); ++ } ++ ++ /** ++ * Encode the identity in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ id.encode(tmp, DerValue.createTag(DerValue.TAG_CONTEXT, false, (byte) 2)); ++ ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof UniqueIdentity)) { ++ throw new IOException("Attribute must be of type UniqueIdentity."); ++ } ++ if (name.equalsIgnoreCase(ID)) { ++ id = (UniqueIdentity) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateSubjectUniqueIdentity."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(ID)) { ++ return (id); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateSubjectUniqueIdentity."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(ID)) { ++ id = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateSubjectUniqueIdentity."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(ID); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateValidity.java b/org/mozilla/jss/netscape/security/x509/CertificateValidity.java +new file mode 100644 +index 00000000..b8171c89 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateValidity.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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.security.cert.CertificateExpiredException; ++import java.security.cert.CertificateNotYetValidException; ++import java.util.Date; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the interval for which the certificate is valid. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.12 ++ * @see CertAttrSet ++ */ ++public class CertificateValidity implements CertAttrSet, Serializable { ++ ++ private static final long serialVersionUID = 8277703278213804194L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.validity"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "validity"; ++ public static final String NOT_BEFORE = "notBefore"; ++ public static final String NOT_AFTER = "notAfter"; ++ private static final long YR_2050 = 2524636800000L; ++ ++ // Private data members ++ private Date notBefore; ++ private Date notAfter; ++ ++ // Returns the first time the certificate is valid. ++ private Date getNotBefore() { ++ return (new Date(notBefore.getTime())); ++ } ++ ++ // Returns the last time the certificate is valid. ++ private Date getNotAfter() { ++ return (new Date(notAfter.getTime())); ++ } ++ ++ // Construct the class from the DerValue ++ private void construct(DerValue derVal) throws IOException { ++ if (derVal.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoded CertificateValidity, " + ++ "starting sequence tag missing."); ++ } ++ // check if UTCTime encoded or GeneralizedTime ++ if (derVal.data.available() == 0) ++ throw new IOException("No data encoded for CertificateValidity"); ++ ++ DerInputStream derIn = new DerInputStream(derVal.toByteArray()); ++ DerValue[] seq = derIn.getSequence(2); ++ if (seq.length != 2) ++ throw new IOException("Invalid encoding for CertificateValidity"); ++ ++ if (seq[0].tag == DerValue.tag_UtcTime) { ++ notBefore = derVal.data.getUTCTime(); ++ } else if (seq[0].tag == DerValue.tag_GeneralizedTime) { ++ notBefore = derVal.data.getGeneralizedTime(); ++ } else { ++ throw new IOException("Invalid encoding for CertificateValidity"); ++ } ++ ++ if (seq[1].tag == DerValue.tag_UtcTime) { ++ notAfter = derVal.data.getUTCTime(); ++ } else if (seq[1].tag == DerValue.tag_GeneralizedTime) { ++ notAfter = derVal.data.getGeneralizedTime(); ++ } else { ++ throw new IOException("Invalid encoding for CertificateValidity"); ++ } ++ } ++ ++ /** ++ * Default constructor for the class. ++ */ ++ public CertificateValidity() { ++ } ++ ++ /** ++ * The default constructor for this class for the specified interval. ++ * ++ * @param notBefore the date and time before which the certificate ++ * is not valid. ++ * @param notAfter the date and time after which the certificate is ++ * not valid. ++ */ ++ public CertificateValidity(Date notBefore, Date notAfter) { ++ this.notBefore = notBefore; ++ this.notAfter = notAfter; ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the CertificateValidity from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateValidity(DerInputStream in) throws IOException { ++ DerValue derVal = in.getDerValue(); ++ construct(derVal); ++ } ++ ++ /** ++ * Return the validity period as user readable string. ++ */ ++ public String toString() { ++ if (notBefore == null || notAfter == null) ++ return ""; ++ return ("Validity: [From: " + notBefore.toString() + ++ ",\n To: " + notAfter.toString() + "]"); ++ } ++ ++ /** ++ * Decode the CertificateValidity period from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ construct(derVal); ++ } ++ ++ private void writeObject(ObjectOutputStream stream) throws IOException { ++ encode(stream); ++ } ++ ++ private void readObject(ObjectInputStream stream) throws IOException { ++ decode(stream); ++ } ++ ++ /** ++ * Encode the CertificateValidity period in DER form to the stream. ++ * ++ * @param out the OutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ // in cases where default constructor is used check for ++ // null values ++ if (notBefore == null || notAfter == null) { ++ throw new IOException("CertAttrSet:CertificateValidity:" + ++ " null values to encode.\n"); ++ } ++ try (DerOutputStream pair = new DerOutputStream(); ++ DerOutputStream seq = new DerOutputStream()) { ++ if (notBefore.getTime() < YR_2050) { ++ pair.putUTCTime(notBefore); ++ } else ++ pair.putGeneralizedTime(notBefore); ++ ++ if (notAfter.getTime() < YR_2050) { ++ pair.putUTCTime(notAfter); ++ } else { ++ pair.putGeneralizedTime(notAfter); ++ } ++ seq.write(DerValue.tag_Sequence, pair); ++ ++ out.write(seq.toByteArray()); ++ } ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof Date)) { ++ throw new IOException("Attribute must be of type Date."); ++ } ++ if (name.equalsIgnoreCase(NOT_BEFORE)) { ++ notBefore = (Date) obj; ++ } else if (name.equalsIgnoreCase(NOT_AFTER)) { ++ notAfter = (Date) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateValidity."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(NOT_BEFORE)) { ++ return (getNotBefore()); ++ } else if (name.equalsIgnoreCase(NOT_AFTER)) { ++ return (getNotAfter()); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateValidity."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(NOT_BEFORE)) { ++ notBefore = null; ++ } else if (name.equalsIgnoreCase(NOT_AFTER)) { ++ notAfter = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateValidity."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(NOT_BEFORE); ++ elements.addElement(NOT_AFTER); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ /** ++ * Verify that the current time is within the validity period. ++ * ++ * @exception CertificateExpiredException if the certificate has expired. ++ * @exception CertificateNotYetValidException if the certificate is not ++ * yet valid. ++ */ ++ public void valid() ++ throws CertificateNotYetValidException, CertificateExpiredException { ++ Date now = new Date(); ++ valid(now); ++ } ++ ++ /** ++ * Verify that the passed time is within the validity period. ++ * ++ * @param now the Date against which to compare the validity ++ * period. ++ * ++ * @exception CertificateExpiredException if the certificate has expired ++ * with respect to the Date supplied. ++ * @exception CertificateNotYetValidException if the certificate is not ++ * yet valid with respect to the Date supplied. ++ * ++ */ ++ public void valid(Date now) ++ throws CertificateNotYetValidException, CertificateExpiredException { ++ /* ++ * we use the internal Dates rather than the passed in Date ++ * because someone could override the Date methods after() ++ * and before() to do something entirely different. ++ */ ++ if (notBefore.after(now)) { ++ throw new CertificateNotYetValidException("NotBefore: " + ++ notBefore.toString()); ++ } ++ if (notAfter.before(now)) { ++ throw new CertificateExpiredException("NotAfter: " + ++ notAfter.toString()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateVersion.java b/org/mozilla/jss/netscape/security/x509/CertificateVersion.java +new file mode 100644 +index 00000000..27b1473b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateVersion.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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the version of the X509 Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.8 ++ * @see CertAttrSet ++ */ ++public class CertificateVersion implements CertAttrSet { ++ /** ++ * X509Certificate Version 1 ++ */ ++ public static final int V1 = 0; ++ /** ++ * X509Certificate Version 2 ++ */ ++ public static final int V2 = 1; ++ /** ++ * X509Certificate Version 3 ++ */ ++ public static final int V3 = 2; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.version"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "version"; ++ public static final String VERSION = "number"; ++ ++ // Private data members ++ int version = V1; ++ ++ // Returns the version number. ++ private int getVersion() { ++ return (version); ++ } ++ ++ // Construct the class from the passed DerValue ++ private void construct(DerValue derVal) throws IOException { ++ if (derVal.isConstructed() && derVal.isContextSpecific()) { ++ derVal = derVal.data.getDerValue(); ++ version = derVal.getInteger().toInt(); ++ if (derVal.data.available() != 0) { ++ throw new IOException("X.509 version, bad format"); ++ } ++ } ++ } ++ ++ /** ++ * The default constructor for this class, ++ * sets the version to 0 (i.e. X.509 version 1). ++ */ ++ public CertificateVersion() { ++ version = V1; ++ } ++ ++ /** ++ * The constructor for this class for the required version. ++ * ++ * @param version the version for the certificate. ++ * @exception IOException if the version is not valid. ++ */ ++ public CertificateVersion(int version) throws IOException { ++ ++ // check that it is a valid version ++ if (version == V1 || version == V2 || version == V3) ++ this.version = version; ++ else { ++ throw new IOException("X.509 Certificate version " + ++ version + " not supported.\n"); ++ } ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the CertificateVersion from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateVersion(DerInputStream in) throws IOException { ++ version = V1; ++ DerValue derVal = in.getDerValue(); ++ ++ construct(derVal); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the CertificateVersion from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateVersion(InputStream in) throws IOException { ++ version = V1; ++ DerValue derVal = new DerValue(in); ++ ++ construct(derVal); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DerValue. ++ * ++ * @param val the Der encoded value. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateVersion(DerValue val) throws IOException { ++ version = V1; ++ ++ construct(val); ++ } ++ ++ /** ++ * Return the version number of the certificate. ++ */ ++ public String toString() { ++ return ("Version: V" + (version + 1)); ++ } ++ ++ /** ++ * Encode the CertificateVersion period in DER form to the stream. ++ * ++ * @param out the OutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ // Nothing for default ++ if (version == V1) { ++ return; ++ } ++ try (DerOutputStream tmp = new DerOutputStream(); ++ DerOutputStream seq = new DerOutputStream()) { ++ tmp.putInteger(new BigInt(version)); ++ ++ seq.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), ++ tmp); ++ ++ out.write(seq.toByteArray()); ++ } ++ } ++ ++ /** ++ * Decode the CertificateVersion period in DER form from the stream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ construct(derVal); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof Integer)) { ++ throw new IOException("Attribute must be of type Integer."); ++ } ++ if (name.equalsIgnoreCase(VERSION)) { ++ version = ((Integer) obj).intValue(); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateVersion."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(VERSION)) { ++ return (Integer.valueOf(getVersion())); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateVersion."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(VERSION)) { ++ version = V1; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateVersion."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(VERSION); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ /** ++ * Compare versions. ++ */ ++ public int compare(int vers) { ++ return (version - vers); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/CertificateX509Key.java b/org/mozilla/jss/netscape/security/x509/CertificateX509Key.java +new file mode 100644 +index 00000000..d47b2b0a +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/CertificateX509Key.java +@@ -0,0 +1,188 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the X509Key attribute for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.5 ++ * @see CertAttrSet ++ */ ++public class CertificateX509Key implements CertAttrSet, Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 6718749024328681131L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.key"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "key"; ++ public static final String KEY = "value"; ++ ++ // Private data member ++ private X509Key key; ++ ++ /** ++ * Default constructor for the certificate attribute. ++ * ++ * @param key the X509Key ++ */ ++ public CertificateX509Key(X509Key key) { ++ this.key = key; ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the X509Key from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateX509Key(DerInputStream in) throws IOException { ++ DerValue val = in.getDerValue(); ++ key = X509Key.parse(val); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the X509Key from. ++ * @exception IOException on decoding errors. ++ */ ++ public CertificateX509Key(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ key = X509Key.parse(val); ++ } ++ ++ /** ++ * Return the key as printable string. ++ */ ++ public String toString() { ++ if (key == null) ++ return ""; ++ return (key.toString()); ++ } ++ ++ /** ++ * Decode the key in DER form from the stream. ++ * ++ * @param in the InputStream to unmarshal the contents from ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ key = X509Key.parse(val); ++ } ++ ++ private void writeObject(ObjectOutputStream stream) throws IOException { ++ encode(stream); ++ } ++ ++ private void readObject(ObjectInputStream stream) throws IOException { ++ decode(stream); ++ } ++ ++ /** ++ * Encode the key in DER form to the stream. ++ * ++ * @param out the OutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ key.encode(tmp); ++ ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof X509Key)) { ++ throw new IOException("Attribute must be of type X509Key."); ++ } ++ if (name.equalsIgnoreCase(KEY)) { ++ this.key = (X509Key) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateX509Key."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(KEY)) { ++ return (key); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateX509Key."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(KEY)) { ++ key = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet: CertificateX509Key."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(KEY); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/DNSName.java b/org/mozilla/jss/netscape/security/x509/DNSName.java +new file mode 100644 +index 00000000..3483cca2 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/DNSName.java +@@ -0,0 +1,90 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class implements the DNSName as required by the GeneralNames ++ * ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.4 ++ */ ++public class DNSName implements GeneralNameInterface { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -2907649488092607056L; ++ private String name; ++ ++ /** ++ * Create the DNSName object from the passed encoded Der value. ++ * ++ * @param derValue the encoded DER DNSName. ++ * @exception IOException on error. ++ */ ++ public DNSName(DerValue derValue) throws IOException { ++ name = derValue.getIA5String(); ++ } ++ ++ /** ++ * Create the DNSName object with the specified name. ++ * ++ * @param name the DNSName. ++ */ ++ public DNSName(String name) { ++ this.name = name; ++ } ++ ++ /** ++ * Return the type of the GeneralName. ++ */ ++ public int getType() { ++ return (GeneralNameInterface.NAME_DNS); ++ } ++ ++ /** ++ * Encode the DNS name into the DerOutputStream. ++ * ++ * @param out the DER stream to encode the DNSName to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putIA5String(name); ++ } ++ ++ /** ++ * Convert the name into user readable string. ++ */ ++ public String toString() { ++ return ("DNSName: " + name); ++ } ++ ++ /** ++ * Get the raw DNSName value. ++ */ ++ public String getValue() { ++ return name; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/DeltaCRLIndicatorExtension.java b/org/mozilla/jss/netscape/security/x509/DeltaCRLIndicatorExtension.java +new file mode 100755 +index 00000000..359ecbd6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/DeltaCRLIndicatorExtension.java +@@ -0,0 +1,240 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.math.BigInteger; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the Delta CRL Indicator Extension. ++ * ++ *

++ * The delta CRL indicator is a critical CRL extension that identifies a delta-CRL. The value of BaseCRLNumber ++ * identifies the CRL number of the base CRL that was used as the starting point in the generation of this delta- CRL. ++ * The delta-CRL contains the changes between the base CRL and the current CRL issued along with the delta-CRL. ++ * ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class DeltaCRLIndicatorExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 7182919216525364676L; ++ /** ++ * Attribute name. ++ */ ++ public static final String NAME = "DeltaCRLIndicator"; ++ public static final String NUMBER = "value"; ++ ++ /** ++ * The Object Identifier for this extension. ++ */ ++ public static final String OID = "2.5.29.27"; ++ ++ private BigInt baseCRLNumber = null; ++ ++ static { ++ try { ++ OIDMap.addAttribute(DeltaCRLIndicatorExtension.class.getName(), ++ OID, NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ if (baseCRLNumber == null) ++ throw new IOException("Unintialized delta CRL indicator extension"); ++ try (DerOutputStream os = new DerOutputStream()) { ++ os.putInteger(this.baseCRLNumber); ++ this.extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Create a DeltaCRLIndicatorExtension with the integer value. ++ * The criticality is set to true. ++ * ++ * @param baseCRLNum the value to be set for the extension. ++ */ ++ public DeltaCRLIndicatorExtension(int baseCRLNum) throws IOException { ++ this.baseCRLNumber = new BigInt(baseCRLNum); ++ this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a DeltaCRLIndicatorExtension with the BigInteger value. ++ * The criticality is set to true. ++ * ++ * @param baseCRLNum the value to be set for the extension. ++ */ ++ public DeltaCRLIndicatorExtension(BigInteger baseCRLNum) throws IOException { ++ this.baseCRLNumber = new BigInt(baseCRLNum); ++ this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a DeltaCRLIndicatorExtension with the BigInteger value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param baseCRLNum the value to be set for the extension. ++ */ ++ public DeltaCRLIndicatorExtension(Boolean critical, BigInteger baseCRLNum) ++ throws IOException { ++ this.baseCRLNumber = new BigInt(baseCRLNum); ++ this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; ++ this.critical = critical.booleanValue(); ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public DeltaCRLIndicatorExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ this.baseCRLNumber = val.getInteger(); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (name.equalsIgnoreCase(NUMBER)) { ++ if (!(obj instanceof BigInteger)) { ++ throw new IOException("Attribute must be of type BigInteger."); ++ } ++ baseCRLNumber = new BigInt((BigInteger) obj); ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:DeltaCRLIndicator."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(NUMBER)) { ++ if (baseCRLNumber == null) ++ return null; ++ else ++ return baseCRLNumber.toBigInteger(); ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:DeltaCRLIndicator."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(NUMBER)) { ++ baseCRLNumber = null; ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:DeltaCRLIndicator."); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the DeltaCRLIndicatorExtension. ++ */ ++ public String toString() { ++ String s = super.toString() + "Delta CRL Indicator: " + ++ ((baseCRLNumber == null) ? "" : baseCRLNumber.toString()) ++ + "\n"; ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(NUMBER); ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/DirStrConverter.java b/org/mozilla/jss/netscape/security/x509/DirStrConverter.java +new file mode 100644 +index 00000000..5bdb30c1 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/DirStrConverter.java +@@ -0,0 +1,172 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.CharacterCodingException; ++import java.nio.charset.CharsetEncoder; ++ ++import org.mozilla.jss.netscape.security.util.ASN1CharStrConvMap; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * A DirStrConverter converts a string to a DerValue of ASN.1 Directory String, ++ * which is a CHOICE of Printable (subset of ASCII), T.61 (Teletex) or ++ * Universal String (UCS-4), and vice versa. ++ * ++ *

++ * The string to DerValue conversion is done as follows. If the string has only PrintableString characters it is ++ * converted to a ASN.1 Printable String using the PrintableString encoder from the global default ASN1CharStrConvMap. ++ * If it has only characters covered in the PrintableString or T.61 character set it is converted to a ASN.1 T.61 string ++ * using the T.61 encoder from the ASN1CharStrCovnMap. Otherwise it is converted to a ASN.1 UniversalString (UCS-4 ++ * character set) which covers all characters. ++ * ++ * @see AVAValueConverter ++ * @see ASN1CharStrConvMap ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ */ ++ ++public class DirStrConverter implements AVAValueConverter { ++ // public constructors ++ ++ /** ++ * Constructs a DirStrConverter. ++ */ ++ public DirStrConverter() { ++ } ++ ++ // public functions ++ ++ /** ++ * Converts a string to a DER encoded ASN1 Directory String, which is a ++ * CHOICE of PrintableString, T.61String or UniversalString. ++ * The string is taken as is i.e. should not be in Ldap DN string syntax. ++ * ++ * @param ds a string representing a directory string value. ++ * ++ * @return a DerValue ++ * ++ * @exception IOException if the string cannot be converted, such as ++ * when a UniversalString encoder ++ * isn't available and the string contains ++ * characters covered only in the universal ++ * string (or UCS-4) character set. ++ */ ++ private static byte[] DefEncodingOrder = ++ new byte[] { ++ DerValue.tag_UTF8String, ++ DerValue.tag_PrintableString, ++ DerValue.tag_T61String, ++ DerValue.tag_UniversalString ++ }; ++ ++ public static synchronized void ++ setDefEncodingOrder(byte[] defEncodingOrder) { ++ DefEncodingOrder = defEncodingOrder; ++ } ++ ++ public DerValue getValue(String ds) ++ throws IOException { ++ return getValue(ds, DefEncodingOrder); ++ } ++ ++ /** ++ * Like getValue(String) with specified DER tags as encoding order. ++ */ ++ public DerValue getValue(String valueString, byte[] tags) throws IOException { ++ // try to convert to printable, then t61 the universal - ++ // i.e. from minimal to the most liberal. ++ ++ if (tags == null || tags.length == 0) ++ tags = DefEncodingOrder; ++ ++ for (int i = 0; i < tags.length; i++) { ++ try { ++ CharsetEncoder encoder = ASN1CharStrConvMap.getDefault().getEncoder(tags[i]); ++ if (encoder == null) ++ continue; ++ ++ CharBuffer charBuffer = CharBuffer.wrap(valueString.toCharArray()); ++ ByteBuffer byteBuffer = encoder.encode(charBuffer); ++ ++ return new DerValue(tags[i], byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.limit()); ++ ++ } catch (CharacterCodingException e) { ++ continue; ++ } ++ } ++ ++ throw new IOException( ++ "Cannot convert the directory string value to a ASN.1 type"); ++ } ++ ++ /** ++ * Creates a DerValue from a BER encoded value, obtained from for example ++ * a attribute value in octothorpe form of a Ldap DN string. ++ * Checks if the BER encoded value is legal for a DirectoryString. ++ * ++ * NOTE: currently only supports DER encoding for the BER encoded value. ++ * ++ * @param berStream Byte array of a BER encoded value. ++ * ++ * @return DerValue object. ++ * ++ * @exception IOException If the BER value cannot be converted to a ++ * valid Directory String DER value. ++ */ ++ public DerValue getValue(byte[] berByteStream) ++ throws IOException { ++ DerValue value = new DerValue(berByteStream); ++ ++ /* ++ if (value.tag != DerValue.tag_PrintableString && ++ value.tag != DerValue.tag_T61String && ++ value.tag != DerValue.tag_UniversalString) ++ throw new IOException("Invalid Directory String AVA Value"); ++ */ ++ ++ return value; ++ } ++ ++ /** ++ * Converts a DerValue to a string. ++ * The string is not in any syntax, such as RFC1779 string syntax. ++ * ++ * @param avaValue a DerValue ++ * @return a string if the value can be converted. ++ * @exception IOException if a decoder needed for the ++ * conversion is not available. ++ */ ++ public String getAsString(DerValue avaValue) ++ throws IOException { ++ /* ++ if (avaValue.tag != DerValue.tag_PrintableString && ++ avaValue.tag != DerValue.tag_BMPString && ++ avaValue.tag != DerValue.tag_UniversalString && ++ avaValue.tag != DerValue.tag_T61String) ++ throw new IllegalArgumentException( ++ "Invalid Directory String value"); ++ // NOTE will return null if a decoder is not available. ++ */ ++ return avaValue.getASN1CharString(); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/DisplayText.java b/org/mozilla/jss/netscape/security/x509/DisplayText.java +new file mode 100644 +index 00000000..76610400 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/DisplayText.java +@@ -0,0 +1,85 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.Serializable; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the DisplayText. ++ * ++ * DisplayText ::= CHOICE { ++ * visibleString VisibleString (SIZE (1..200)), ++ * bmpString BMPString (SIZE (1..200)), ++ * utf8String UTF8String (SIZE (1..200)), ++ * } ++ * ++ * @author Thomas Kwan ++ */ ++public class DisplayText implements Serializable { ++ ++ private static final long serialVersionUID = -6521458152495173328L; ++ ++ /** Tag value indicating an ASN.1 "BMPString" value. */ ++ public final static byte tag_IA5String = 0x16; ++ public final static byte tag_BMPString = 0x1E; ++ public final static byte tag_VisibleString = 0x1A; ++ public final static byte tag_UTF8String = 0x0C; ++ ++ private byte mTag; ++ private String mS = null; ++ ++ public DisplayText(byte tag, String s) { ++ mTag = tag; ++ mS = s; ++ } ++ ++ public DisplayText(DerValue val) throws IOException { ++ mTag = val.tag; ++ mS = val.getAsString(); ++ } ++ ++ /** ++ * Write the DisplayText to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putStringType(mTag, mS); ++ } ++ ++ public String getText() { ++ return mS; ++ } ++ ++ public String toString() { ++ if (mTag == tag_IA5String) { ++ return "IA5String: " + mS; ++ } else if (mTag == tag_BMPString) { ++ return "BMPString: " + mS; ++ } else if (mTag == tag_VisibleString) { ++ return "VisibleString: " + mS; ++ } else { ++ return "UTF8String: " + mS; ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/EDIPartyName.java b/org/mozilla/jss/netscape/security/x509/EDIPartyName.java +new file mode 100644 +index 00000000..dedd3a7f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/EDIPartyName.java +@@ -0,0 +1,157 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the EDIPartyName of the GeneralName choice. ++ * The ASN.1 syntax for this is: ++ * ++ *

++ * EDIPartyName ::= SEQUENCE {
++ *     nameAssigner  [0]  DirectoryString OPTIONAL,
++ *     partyName     [1]  DirectoryString }
++ * 
++ * ++ * @author Hemma Prafullchandra ++ * @version 1.2 ++ * @see GeneralName ++ * @see GeneralNames ++ * @see GeneralNameInterface ++ */ ++public class EDIPartyName implements GeneralNameInterface { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -8669257424766789063L; ++ // Private data members ++ private static final byte TAG_ASSIGNER = 0; ++ private static final byte TAG_PARTYNAME = 1; ++ ++ private String assigner = null; ++ private String party = null; ++ ++ /** ++ * Create the EDIPartyName object from the specified names. ++ * ++ * @param assignerName the name of the assigner ++ * @param partyName the name of the EDI party. ++ */ ++ public EDIPartyName(String assignerName, String partyName) { ++ this.assigner = assignerName; ++ this.party = partyName; ++ } ++ ++ /** ++ * Create the EDIPartyName object from the specified name. ++ * ++ * @param partyName the name of the EDI party. ++ */ ++ public EDIPartyName(String partyName) { ++ this.party = partyName; ++ } ++ ++ /** ++ * Create the EDIPartyName object from the passed encoded Der value. ++ * ++ * @param derValue the encoded DER EDIPartyName. ++ * @exception IOException on error. ++ */ ++ public EDIPartyName(DerValue derValue) throws IOException { ++ DerInputStream in = new DerInputStream(derValue.toByteArray()); ++ DerValue[] seq = in.getSequence(2); ++ ++ int len = seq.length; ++ if (len < 1 || len > 2) ++ throw new IOException("Invalid encoding of EDIPartyName"); ++ ++ for (int i = 0; i < len; i++) { ++ DerValue opt = seq[i]; ++ if (opt.isContextSpecific(TAG_ASSIGNER) && ++ !opt.isConstructed()) { ++ if (assigner != null) ++ throw new IOException("Duplicate nameAssigner found in" ++ + " EDIPartyName"); ++ opt = opt.data.getDerValue(); ++ assigner = opt.getAsString(); ++ } ++ if (opt.isContextSpecific(TAG_PARTYNAME) && ++ !opt.isConstructed()) { ++ if (party != null) ++ throw new IOException("Duplicate partyName found in" ++ + " EDIPartyName"); ++ opt = opt.data.getDerValue(); ++ party = opt.getAsString(); ++ } ++ } ++ } ++ ++ /** ++ * Return the type of the GeneralName. ++ */ ++ public int getType() { ++ return (GeneralNameInterface.NAME_EDI); ++ } ++ ++ /** ++ * Encode the EDI party name into the DerOutputStream. ++ * ++ * @param out the DER stream to encode the EDIPartyName to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tagged = new DerOutputStream(); ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (assigner != null) { ++ DerOutputStream tmp2 = new DerOutputStream(); ++ // XXX - shd check is chars fit into PrintableString ++ tmp2.putPrintableString(assigner); ++ tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_ASSIGNER), tmp2); ++ } ++ if (party == null) { ++ tagged.close(); ++ tmp.close(); ++ throw new IOException("Cannot have null partyName"); ++ } ++ ++ // XXX - shd check is chars fit into PrintableString ++ tmp.putPrintableString(party); ++ tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_PARTYNAME), tmp); ++ ++ out.write(DerValue.tag_Sequence, tagged); ++ } ++ ++ /** ++ * Return the printable string. ++ */ ++ public String toString() { ++ return ("EDIPartyName: " + ++ ((assigner == null) ? "" : ++ (" nameAssigner = " + assigner + ",")) ++ + " partyName = " + party); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/Extension.java b/org/mozilla/jss/netscape/security/x509/Extension.java +new file mode 100644 +index 00000000..824b0456 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/Extension.java +@@ -0,0 +1,195 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.Serializable; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * Represent a X509 Extension Attribute. ++ * ++ *

++ * Extensions are addiitonal attributes which can be inserted in a X509 v3 certificate. For example a ++ * "Driving License Certificate" could have the driving license number as a extension. ++ * ++ *

++ * Extensions are represented as a sequence of the extension identifier (Object Identifier), a boolean flag stating ++ * whether the extension is to be treated as being critical and the extension value itself (this is again a DER encoding ++ * of the extension value). ++ * ++ *

++ * ASN.1 definition of Extension:
++ * Extension ::= SEQUENCE {
++ * ExtensionId	OBJECT IDENTIFIER,
++ * critical	BOOLEAN DEFAULT FALSE,
++ * extensionValue	OCTET STRING
++ * }
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.9 ++ */ ++public class Extension implements Serializable { ++ private static final long serialVersionUID = -643549610716024753L; ++ protected ObjectIdentifier extensionId = null; ++ protected boolean critical = false; ++ protected byte[] extensionValue = null; ++ ++ /** ++ * Default constructor. Used only by sub-classes. ++ */ ++ public Extension() { ++ } ++ ++ /** ++ * Constructs an extension from a DER encoded array of bytes. ++ */ ++ public Extension(DerValue derVal) throws IOException { ++ ++ DerInputStream in = derVal.toDerInputStream(); ++ ++ // Object identifier ++ extensionId = in.getOID(); ++ ++ // If the criticality flag was false, it will not have been encoded. ++ DerValue val = in.getDerValue(); ++ if (val.tag == DerValue.tag_Boolean) { ++ critical = val.getBoolean(); ++ ++ // Extension value (DER encoded) ++ val = in.getDerValue(); ++ extensionValue = val.getOctetString(); ++ } else { ++ critical = false; ++ extensionValue = val.getOctetString(); ++ } ++ } ++ ++ /** ++ * Constructs an Extension from individual components of ObjectIdentifier, ++ * criticality and the DER encoded OctetString. ++ * ++ * @param extensionId the ObjectIdentifier of the extension ++ * @param critical the boolean indicating if the extension is critical ++ * @param extensionValue the DER encoded octet string of the value. ++ */ ++ public Extension(ObjectIdentifier extensionId, boolean critical, ++ byte[] extensionValue) throws IOException { ++ this.extensionId = extensionId; ++ this.critical = critical; ++ // passed in a DER encoded octet string, strip off the tag ++ // and length ++ DerValue inDerVal = new DerValue(extensionValue); ++ this.extensionValue = inDerVal.getOctetString(); ++ } ++ ++ /** ++ * Constructs an Extension from another extension. To be used for ++ * creating decoded subclasses. ++ * ++ * @param ext the extension to create from. ++ */ ++ public Extension(Extension ext) { ++ this.extensionId = ext.extensionId; ++ this.critical = ext.critical; ++ this.extensionValue = ext.extensionValue; ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ if (extensionId == null) ++ throw new IOException("Null OID to encode for the extension!"); ++ ++ DerOutputStream bytes = new DerOutputStream(); ++ bytes.putOID(extensionId); ++ if (critical) ++ bytes.putBoolean(critical); ++ if (extensionValue != null) ++ bytes.putOctetString(extensionValue); ++ ++ out.write(DerValue.tag_Sequence, bytes); ++ } ++ ++ /** ++ * Returns true if extension is critical. ++ */ ++ public boolean isCritical() { ++ return (critical); ++ } ++ ++ public void setCritical(boolean c) { ++ critical = c; ++ } ++ ++ public void clearValue() { ++ extensionValue = null; ++ } ++ ++ /** ++ * Returns the ObjectIdentifier of the extension. ++ */ ++ public ObjectIdentifier getExtensionId() { ++ return (extensionId); ++ } ++ ++ public void setExtensionId(ObjectIdentifier oid) { ++ extensionId = oid; ++ } ++ ++ /** ++ * Returns the extension value as an byte array for further processing. ++ * Note, this is the raw DER value of the extension, not the DER ++ * encoded octet string which is in the certificate. ++ */ ++ public byte[] getExtensionValue() { ++ if (extensionValue == null) ++ return null; ++ ++ byte[] dup = new byte[extensionValue.length]; ++ System.arraycopy(extensionValue, 0, dup, 0, dup.length); ++ return dup; ++ } ++ ++ public void setExtensionValue(byte value[]) { ++ extensionValue = value; ++ } ++ ++ /** ++ * Returns the Extension in user readable form. ++ */ ++ public String toString() { ++ String s = "ObjectId: " + extensionId.toString(); ++ if (critical) { ++ s += " Criticality=true\n"; ++ } else { ++ s += " Criticality=false\n"; ++ } ++ return (s); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/Extensions.java b/org/mozilla/jss/netscape/security/x509/Extensions.java +new file mode 100644 +index 00000000..fd464d50 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/Extensions.java +@@ -0,0 +1,252 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.lang.reflect.Constructor; ++import java.lang.reflect.InvocationTargetException; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the Extensions attribute for the Certificate. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.11 ++ * @see CertAttrSet ++ */ ++public class Extensions extends Vector ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 4597917347772057433L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions"; ++ /** ++ * name ++ */ ++ public static final String NAME = "extensions"; ++ ++ private Hashtable map; ++ ++ // Parse the encoded extension ++ public void parseExtension(Extension ext) throws IOException { ++ try { ++ @SuppressWarnings("unchecked") ++ Class extClass = (Class) OIDMap.getClass(ext.getExtensionId()); ++ if (extClass == null) { // Unsupported extension ++ if (ext.isCritical()) { ++ throw new IOException("Unsupported CRITICAL extension: " ++ + ext.getExtensionId()); ++ } else { ++ map.put(ext.getExtensionId().toString(), ext); ++ addElement(ext); ++ return; ++ } ++ } ++ Class[] params = { Boolean.class, Object.class }; ++ Constructor cons = extClass.getConstructor(params); ++ ++ byte[] extData = ext.getExtensionValue(); ++ int extLen = extData.length; ++ Object value = Array.newInstance(byte.class, extLen); ++ ++ for (int i = 0; i < extLen; i++) { ++ Array.setByte(value, i, extData[i]); ++ } ++ Object[] passed = new Object[] { Boolean.valueOf(ext.isCritical()), ++ value }; ++ CertAttrSet certExt = cons.newInstance(passed); ++ map.put(certExt.getName(), (Extension) certExt); ++ addElement((Extension) certExt); ++ ++ } catch (NoSuchMethodException nosuch) { ++ throw new IOException(nosuch.toString()); ++ } catch (InvocationTargetException invk) { ++ throw new IOException(invk.getTargetException().toString()); ++ } catch (Exception e) { ++ throw new IOException(e.toString()); ++ } ++ } ++ ++ /** ++ * Default constructor for the certificate attribute. ++ */ ++ public Extensions() { ++ map = new Hashtable(); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the Extension from. ++ * @exception IOException on decoding errors. ++ */ ++ public Extensions(DerInputStream in) ++ throws IOException { ++ ++ map = new Hashtable(); ++ DerValue[] exts = in.getSequence(5); ++ ++ for (int i = 0; i < exts.length; i++) { ++ Extension ext = new Extension(exts[i]); ++ parseExtension(ext); ++ } ++ } ++ ++ /** ++ * Decode the extensions from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ DerInputStream str = val.toDerInputStream(); ++ ++ map = new Hashtable(); ++ DerValue[] exts = str.getSequence(5); ++ ++ for (int i = 0; i < exts.length; i++) { ++ Extension ext = new Extension(exts[i]); ++ parseExtension(ext); ++ } ++ } ++ ++ /** ++ * Encode the extensions in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception CertificateException on encoding errors. ++ * @exception IOException on errors. ++ */ ++ public void encode(OutputStream out) ++ throws CertificateException, IOException { ++ DerOutputStream extOut = new DerOutputStream(); ++ for (int i = 0; i < size(); i++) { ++ Object thisOne = elementAt(i); ++ if (thisOne instanceof CertAttrSet) ++ ((CertAttrSet) thisOne).encode(extOut); ++ else if (thisOne instanceof Extension) ++ ((Extension) thisOne).encode(extOut); ++ else ++ throw new CertificateException("Invalid extension object"); ++ } ++ ++ try (DerOutputStream seq = new DerOutputStream()) { ++ seq.write(DerValue.tag_Sequence, extOut); ++ out.write(seq.toByteArray()); ++ } ++ } ++ ++ /** ++ * Set the attribute value. ++ * ++ * @param name the extension name used in the cache. ++ * @param obj the object to set. ++ * @exception IOException if the object could not be cached. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ map.put(name, (Extension) obj); ++ addElement((Extension) obj); ++ } ++ ++ /** ++ * Get the attribute value. ++ * ++ * @param name the extension name used in the lookup. ++ * @exception IOException if named extension is not found. ++ */ ++ public Object get(String name) throws IOException { ++ Object obj = map.get(name); ++ if (obj == null) { ++ throw new IOException("No extension found with name " + name); ++ } ++ return (obj); ++ } ++ ++ /** ++ * Delete the attribute value. ++ * ++ * @param name the extension name used in the lookup. ++ * @exception IOException if named extension is not found. ++ */ ++ public void delete(String name) throws IOException { ++ Object obj = map.get(name); ++ if (obj == null) { ++ throw new IOException("No extension found with name " + name); ++ } ++ map.remove(name); ++ removeElement(obj); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ return map.keys(); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = super.hashCode(); ++ result = prime * result + ((map == null) ? 0 : map.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (!super.equals(obj)) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ Extensions other = (Extensions) obj; ++ if (map == null) { ++ if (other.map != null) ++ return false; ++ } else if (!map.equals(other.map)) ++ return false; ++ return true; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/FreshestCRLExtension.java b/org/mozilla/jss/netscape/security/x509/FreshestCRLExtension.java +new file mode 100644 +index 00000000..85301fc1 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/FreshestCRLExtension.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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.BufferedOutputStream; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.Vector; ++ ++import org.mozilla.jss.asn1.ASN1Util; ++import org.mozilla.jss.asn1.InvalidBERException; ++import org.mozilla.jss.asn1.SEQUENCE; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++ ++/** ++ * An extension that tells applications where to find ++ * the latest (freshest) delta CRL for this certificate ++ * or full CRL. ++ * ++ *
++ * cRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
++ *
++ * DistributionPoint ::= SEQUENCE {
++ *      distributionPoint       [0]     DistributionPointName OPTIONAL,
++ *      reasons                 [1]     ReasonFlags OPTIONAL,
++ *      cRLIssuer               [2]     GeneralNames OPTIONAL }
++ *
++ * DistributionPointName ::= CHOICE {
++ *      fullName                [0]     GeneralNames,
++ *      nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
++ *
++ * ReasonFlags ::= BIT STRING {
++ *      unused                  (0),
++ *      keyCompromise           (1),
++ *      cACompromise            (2),
++ *      affiliationChanged      (3),
++ *      superseded              (4),
++ *      cessationOfOperation    (5),
++ *      certificateHold         (6) }
++ * 
++ */ ++public class FreshestCRLExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -8040203589629281781L; ++ ++ // vector of CRLDistributionPoint ++ private SEQUENCE distributionPoints = new SEQUENCE(); ++ ++ public FreshestCRLExtension() { ++ this.extensionId = PKIXExtensions.FreshestCRL_Id; ++ this.critical = false; ++ } ++ ++ // Cached DER-encoding to improve performance. ++ private byte[] cachedEncoding = null; ++ ++ // Attribute name ++ public static final String NAME = "FreshestCRL"; ++ ++ // The Object Identifier for this extension. ++ public static final String OID = "2.5.29.46"; ++ ++ static { ++ try { ++ OIDMap.addAttribute(FreshestCRLExtension.class.getName(), ++ OID, NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ /** ++ * This constructor is called by the CertificateExtensions class to decode ++ * an extension whose OID indicates it is a CRLDistributionsPoints ++ * extension. ++ */ ++ public FreshestCRLExtension(Boolean critical, Object value) ++ //throws IOException ++ { ++ try { ++ this.extensionId = PKIXExtensions.FreshestCRL_Id; ++ this.critical = critical.booleanValue(); ++ this.extensionValue = ((byte[]) value).clone(); ++ ++ // decode the value ++ try { ++ SEQUENCE.OF_Template seqOfCRLDP = ++ new SEQUENCE.OF_Template(CRLDistributionPoint.getTemplate()); ++ ++ distributionPoints = ++ (SEQUENCE) ASN1Util.decode(seqOfCRLDP, extensionValue); ++ } catch (InvalidBERException e) { ++ throw new IOException("Invalid BER-encoding: " + e, e); ++ } ++ } catch (IOException e) { ++ System.out.println("Big error"); ++ System.out.println(e); ++ e.printStackTrace(); ++ //throw e; ++ } ++ } ++ ++ /** ++ * Creates a new FreshestCRL extension, with the given ++ * distribution point as the first element. ++ */ ++ public FreshestCRLExtension(CRLDistributionPoint dp) { ++ this.extensionId = PKIXExtensions.FreshestCRL_Id; ++ this.critical = false; ++ distributionPoints.addElement(dp); ++ } ++ ++ /** ++ * Adds an additional distribution point to the end of the sequence. ++ */ ++ public void addPoint(CRLDistributionPoint dp) { ++ distributionPoints.addElement(dp); ++ cachedEncoding = null; ++ } ++ ++ /** ++ * Returns the number of distribution points in the sequence. ++ */ ++ public int getNumPoints() { ++ return distributionPoints.size(); ++ } ++ ++ /** ++ * Returns the DistributionPoint at the given index in the sequence. ++ */ ++ public CRLDistributionPoint getPointAt(int index) { ++ return (CRLDistributionPoint) distributionPoints.elementAt(index); ++ } ++ ++ /** ++ * Sets the criticality of this extension. PKIX dictates that this ++ * extension SHOULD NOT be critical, so applications can make it critical ++ * if they have a very good reason. By default, the extension is not ++ * critical. ++ */ ++ public void setCritical(boolean critical) { ++ this.critical = critical; ++ } ++ ++ /** ++ * Encodes this extension to the given DerOutputStream. ++ * This method re-encodes each time it is called, so it is not very ++ * efficient. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ extensionValue = ASN1Util.encode(distributionPoints); ++ super.encode(out); ++ } ++ ++ /** ++ * Should be called if any change is made to this data structure ++ * so that the cached DER encoding can be discarded. ++ */ ++ public void flushCachedEncoding() { ++ cachedEncoding = null; ++ } ++ ++ ///////////////////////////////////////////////////////////// ++ // CertAttrSet interface ++ // This interface is not really appropriate for this extension ++ // because it is so complicated. Therefore, we only provide a ++ // minimal implementation. ++ ///////////////////////////////////////////////////////////// ++ @Override ++ public String toString() { ++ return NAME; ++ } ++ ++ /** ++ * DER-encodes this extension to the given OutputStream. ++ */ ++ public void encode(OutputStream ostream) ++ throws CertificateException, IOException { ++ if (cachedEncoding == null) { ++ // only re-encode if necessary ++ DerOutputStream tmp = new DerOutputStream(); ++ encode(tmp); ++ cachedEncoding = tmp.toByteArray(); ++ } ++ ostream.write(cachedEncoding); ++ } ++ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ throw new IOException("Not supported"); ++ } ++ ++ public void set(String name, Object obj) ++ throws CertificateException, IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:FreshestCRLExtension"); ++ } ++ ++ public Object get(String name) ++ throws CertificateException, IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:FreshestCRLExtension"); ++ } ++ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:FreshestCRLExtension"); ++ } ++ ++ /* ++ * TODO replacewith empty collection ++ */ ++ public Enumeration getAttributeNames() { ++ return (new Vector()).elements(); ++ } ++ ++ public String getName() { ++ return NAME; ++ } ++ ++ /** ++ * Test driver. ++ */ ++ public static void main(String args[]) { ++ BufferedOutputStream bos = null; ++ try { ++ ++ if (args.length != 1) { ++ System.out.println("Usage: FreshestCRLExtentions " + ++ ""); ++ System.exit(-1); ++ } ++ ++ bos = new BufferedOutputStream( ++ new FileOutputStream(args[0])); ++ ++ // URI only ++ CRLDistributionPoint cdp = new CRLDistributionPoint(); ++ URIName uri = new URIName("http://www.mycrl.com/go/here"); ++ GeneralNames generalNames = new GeneralNames(); ++ generalNames.addElement(uri); ++ cdp.setFullName(generalNames); ++ FreshestCRLExtension crldpExt = ++ new FreshestCRLExtension(cdp); ++ ++ // DN only ++ cdp = new CRLDistributionPoint(); ++ X500Name dn = new X500Name("CN=Otis Smith,E=otis@fedoraproject.org" + ++ ",OU=Certificate Server,O=Fedora,C=US"); ++ generalNames = new GeneralNames(); ++ generalNames.addElement(dn); ++ cdp.setFullName(generalNames); ++ crldpExt.addPoint(cdp); ++ ++ // DN + reason ++ BitArray ba = new BitArray(5, new byte[] { (byte) 0x28 }); ++ cdp = new CRLDistributionPoint(); ++ cdp.setFullName(generalNames); ++ cdp.setReasons(ba); ++ crldpExt.addPoint(cdp); ++ ++ // relative DN + reason + crlIssuer ++ cdp = new CRLDistributionPoint(); ++ RDN rdn = new RDN("OU=foobar dept"); ++ cdp.setRelativeName(rdn); ++ cdp.setReasons(ba); ++ cdp.setCRLIssuer(generalNames); ++ crldpExt.addPoint(cdp); ++ ++ crldpExt.setCritical(true); ++ crldpExt.encode(bos); ++ ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } finally { ++ if (bos != null) { ++ try { ++ bos.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++ ++ /** ++ * Represents a reason that a cert may be revoked. These reasons are ++ * expressed in a ReasonFlags bit string. ++ */ ++ public static class Reason { ++ ++ private String name; ++ private byte bitMask; ++ ++ private Reason() { ++ } ++ ++ private Reason(String name, byte bitMask) { ++ this.name = name; ++ this.bitMask = bitMask; ++ map.put(name, this); ++ list.addElement(this); ++ } ++ ++ private static Hashtable map = new Hashtable(); ++ private static Vector list = new Vector(); ++ ++ public static Reason fromString(String name) { ++ return map.get(name); ++ } ++ ++ public String getName() { ++ return name; ++ } ++ ++ public byte getBitMask() { ++ return bitMask; ++ } ++ ++ /** ++ * Given a bit array representing reason flags, extracts the reasons ++ * and returns them as an array. ++ * ++ * @param bitFlags A bit vector containing reason flags. ++ * @return An array of reasons contained in the bit vector. ++ * May be zero-length but will not be null. ++ */ ++ public static Reason[] bitArrayToReasonArray(byte bitFlags) { ++ return bitArrayToReasonArray(new byte[] { bitFlags }); ++ } ++ ++ /** ++ * Given a bit array representing reason flags, extracts the reasons ++ * and returns them as an array. Currently, only the first byte ++ * of the bitflags are examined. ++ * ++ * @param bitFlags A bit vector containing reason flags. The format ++ * is big-endian (MSB first). Only the first byte is examined. ++ * @return An array of reasons contained in the bit vector. ++ * May be zero-length but will not be null. ++ */ ++ public static Reason[] bitArrayToReasonArray(byte[] bitFlags) { ++ byte first = bitFlags[0]; ++ int size = list.size(); ++ Vector result = new Vector(); ++ for (int i = 0; i < size; i++) { ++ Reason r = list.elementAt(i); ++ byte b = r.getBitMask(); ++ if ((first & b) != 0) { ++ result.addElement(r); ++ } ++ } ++ size = result.size(); ++ Reason[] retval = new Reason[size]; ++ for (int i = 0; i < size; i++) { ++ retval[i] = result.elementAt(i); ++ } ++ return retval; ++ } ++ ++ public static final Reason UNUSED = ++ new Reason("unused", (byte) 0x80); ++ public static final Reason KEY_COMPROMISE = ++ new Reason("keyCompromise", (byte) 0x40); ++ public static final Reason CA_COMPROMISE = ++ new Reason("cACompromise", (byte) 0x20); ++ public static final Reason AFFILIATION_CHANGED = ++ new Reason("affiliationChanged", (byte) 0x10); ++ public static final Reason SUPERSEDED = ++ new Reason("superseded", (byte) 0x08); ++ public static final Reason CESSATION_OF_OPERATION = ++ new Reason("cessationOfOperation", (byte) 0x04); ++ public static final Reason CERTIFICATE_HOLD = ++ new Reason("certificateHold", (byte) 0x02); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/GeneralName.java b/org/mozilla/jss/netscape/security/x509/GeneralName.java +new file mode 100644 +index 00000000..c3298828 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/GeneralName.java +@@ -0,0 +1,214 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class implements the ASN.1 GeneralName object class. ++ *

++ * The ASN.1 syntax for this is: ++ * ++ *

++ * GeneralName ::= CHOICE {
++ *    otherName                       [0]     OtherName,
++ *    rfc822Name                      [1]     IA5String,
++ *    dNSName                         [2]     IA5String,
++ *    x400Address                     [3]     ORAddress,
++ *    directoryName                   [4]     Name,
++ *    ediPartyName                    [5]     EDIPartyName,
++ *    uniformResourceIdentifier       [6]     IA5String,
++ *    iPAddress                       [7]     OCTET STRING,
++ *    registeredID                    [8]     OBJECT IDENTIFIER
++ * }
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.7 ++ */ ++public class GeneralName implements GeneralNameInterface { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 2244101501095555042L; ++ // Private data members ++ private GeneralNameInterface name = null; ++ ++ /** ++ * Default constructor for the class. ++ * ++ * @param name the selected CHOICE from the list. ++ */ ++ public GeneralName(GeneralNameInterface name) { ++ this.name = name; ++ } ++ ++ /** ++ * Create the object from its DER encoded value. ++ * ++ * @param encName the DER encoded GeneralName. ++ */ ++ public GeneralName(DerValue encName) throws IOException { ++ short tag = (byte) (encName.tag & 0x1f); ++ ++ // NB. this is always encoded with the IMPLICIT tag ++ // The checks only make sense if we assume implicit tagging, ++ // with explicit tagging the form is always constructed. ++ switch (tag) { ++ case GeneralNameInterface.NAME_RFC822: ++ if (encName.isContextSpecific() && !encName.isConstructed()) { ++ encName.resetTag(DerValue.tag_IA5String); ++ name = new RFC822Name(encName); ++ } else ++ throw new IOException("Invalid encoding of RFC822 name"); ++ break; ++ ++ case GeneralNameInterface.NAME_DNS: ++ if (encName.isContextSpecific() && !encName.isConstructed()) { ++ encName.resetTag(DerValue.tag_IA5String); ++ name = new DNSName(encName); ++ } else ++ throw new IOException("Invalid encoding of DNS name"); ++ break; ++ ++ case GeneralNameInterface.NAME_URI: ++ if (encName.isContextSpecific() && !encName.isConstructed()) { ++ encName.resetTag(DerValue.tag_IA5String); ++ name = new URIName(encName); ++ } else ++ throw new IOException("Invalid encoding of URI"); ++ break; ++ ++ case GeneralNameInterface.NAME_IP: ++ if (encName.isContextSpecific() && !encName.isConstructed()) { ++ encName.resetTag(DerValue.tag_OctetString); ++ name = new IPAddressName(encName); ++ } else ++ throw new IOException("Invalid encoding of IP address"); ++ break; ++ ++ case GeneralNameInterface.NAME_ANY: ++ if (encName.isContextSpecific() && encName.isConstructed()) { ++ encName.resetTag(DerValue.tag_OctetString); ++ name = new OtherName(encName); ++ } else ++ throw new IOException("Invalid encoding of other name"); ++ break; ++ ++ case GeneralNameInterface.NAME_OID: ++ if (encName.isContextSpecific() && !encName.isConstructed()) { ++ encName.resetTag(DerValue.tag_ObjectId); ++ name = new OIDName(encName); ++ } else ++ throw new IOException("Invalid encoding of OID name"); ++ break; ++ ++ case GeneralNameInterface.NAME_DIRECTORY: ++ if (encName.isContextSpecific() && encName.isConstructed()) { ++ // Unlike the other cases, DirectoryName is EXPLICITly ++ // tagged, because the X.500 Name type is a CHOICE. ++ // Therefore, the sequence is actually nested in the ++ // content of this value. We'll pretend it's an octet ++ // string so we can get at the content bytes. ++ encName.resetTag(DerValue.tag_OctetString); ++ byte[] content = encName.getOctetString(); ++ name = new X500Name(content); ++ } else ++ throw new IOException("Invalid encoding of Directory name"); ++ break; ++ ++ case GeneralNameInterface.NAME_EDI: ++ if (encName.isContextSpecific() && encName.isConstructed()) { ++ encName.resetTag(DerValue.tag_Sequence); ++ name = new EDIPartyName(encName); ++ } else ++ throw new IOException("Invalid encoding of EDI name"); ++ break; ++ ++ default: ++ throw new IOException("Unrecognized GeneralName tag, (" ++ + tag + ")"); ++ } ++ } ++ ++ /** ++ * Return the type of the general name. ++ */ ++ public int getType() { ++ return (name.getType()); ++ } ++ ++ /** ++ * Return the name as user readable string ++ */ ++ public String toString() { ++ return (name.toString()); ++ } ++ ++ /** ++ * Encode the name to the specified DerOutputStream. ++ * ++ * @param out the DerOutputStream to encode the the GeneralName to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ name.encode(tmp); ++ int nameType = name.getType(); ++ boolean constructedForm; ++ ++ if (nameType == GeneralNameInterface.NAME_ANY || ++ nameType == GeneralNameInterface.NAME_X400 || ++ nameType == GeneralNameInterface.NAME_DIRECTORY || ++ nameType == GeneralNameInterface.NAME_EDI) { ++ constructedForm = true; ++ } else { ++ constructedForm = false; ++ } ++ ++ if (nameType == GeneralNameInterface.NAME_DIRECTORY) { ++ // EXPLICIT tag, because Name is a CHOICE type ++ out.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ constructedForm, (byte) nameType), tmp); ++ } else { ++ // IMPLICIT tag, the default ++ out.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ constructedForm, (byte) nameType), tmp); ++ } ++ } ++ ++ /** ++ * Unwrap this GeneralName until we reach something that is not ++ * a GeneralName. ++ */ ++ public GeneralNameInterface unwrap() { ++ if (this == name) ++ return null; // can't happen, but just in case... ++ ++ if (name instanceof GeneralName) ++ return ((GeneralName) name).unwrap(); ++ else ++ return name; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/GeneralNameInterface.java b/org/mozilla/jss/netscape/security/x509/GeneralNameInterface.java +new file mode 100644 +index 00000000..d23a36ad +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/GeneralNameInterface.java +@@ -0,0 +1,60 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++ ++/** ++ * This interface specifies the abstract methods which have to be ++ * implemented by all the members of the GeneralNames ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.6 ++ */ ++public interface GeneralNameInterface extends java.io.Serializable { ++ /** ++ * The list of names supported. ++ */ ++ public static final int NAME_ANY = 0; ++ public static final int NAME_RFC822 = 1; ++ public static final int NAME_DNS = 2; ++ public static final int NAME_X400 = 3; ++ public static final int NAME_DIRECTORY = 4; ++ public static final int NAME_EDI = 5; ++ public static final int NAME_URI = 6; ++ public static final int NAME_IP = 7; ++ public static final int NAME_OID = 8; ++ ++ /** ++ * Return the type of the general name, as ++ * defined above. ++ */ ++ int getType(); ++ ++ /** ++ * Encode the name to the specified DerOutputStream. ++ * ++ * @param out the DerOutputStream to encode the GeneralName to. ++ * @exception IOException thrown if the GeneralName could not be ++ * encoded. ++ */ ++ void encode(DerOutputStream out) throws IOException; ++} +diff --git a/org/mozilla/jss/netscape/security/x509/GeneralNames.java b/org/mozilla/jss/netscape/security/x509/GeneralNames.java +new file mode 100644 +index 00000000..96347804 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/GeneralNames.java +@@ -0,0 +1,150 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This object class represents the GeneralNames type required in ++ * X509 certificates. ++ *

++ * The ASN.1 syntax for this is: ++ * ++ *

++ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.7 ++ */ ++public class GeneralNames extends Vector { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 3204492869396713312L; ++ ++ /** ++ * Create the GeneralNames, decoding from the passed DerValue. ++ * ++ * Caution when using this constructor. It may be broken! ++ * Better to call addElement(gni) directly where gni is ++ * a GeneralNameInterface object ++ * ++ * @param derVal the DerValue to construct the GeneralNames from. ++ * @exception GeneralNamesException on decoding error. ++ * @exception IOException on error. ++ */ ++ public GeneralNames(DerValue derVal) ++ throws IOException, GeneralNamesException { ++ if (derVal.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for GeneralNames."); ++ } ++ if (derVal.data.available() == 0) { ++ throw new GeneralNamesException("No data available in " ++ + "passed DER encoded value."); ++ } ++ // Decode all the GeneralName's ++ while (derVal.data.available() != 0) { ++ DerValue encName = derVal.data.getDerValue(); ++ ++ GeneralName name = new GeneralName(encName); ++ addElement(name); ++ } ++ } ++ ++ /** ++ * Create the GeneralNames ++ * ++ * @param names a non-empty array of names to put into the ++ * generalNames ++ */ ++ ++ public GeneralNames(GeneralNameInterface[] names) ++ throws GeneralNamesException { ++ if (names == null || names.length == 0) ++ throw new GeneralNamesException("Cannot create empty GeneralNames"); ++ ++ for (int i = 0; i < names.length; i++) { ++ addElement(names[i]); ++ } ++ } ++ ++ /** ++ * The default constructor for this class. ++ */ ++ public GeneralNames() { ++ super(1, 1); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception GeneralNamesException on encoding error. ++ * @exception IOException on error. ++ */ ++ public void encode(DerOutputStream out) ++ throws IOException, GeneralNamesException { ++ if (size() == 0) { ++ return; ++ } ++ ++ Enumeration names = elements(); ++ DerOutputStream temp = new DerOutputStream(); ++ ++ while (names.hasMoreElements()) { ++ Object obj = names.nextElement(); ++ if (!(obj instanceof GeneralNameInterface)) { ++ throw new GeneralNamesException("Element in GeneralNames " ++ + "not of type GeneralName."); ++ } ++ GeneralNameInterface intf = (GeneralNameInterface) obj; ++ if (obj instanceof GeneralName) { ++ intf.encode(temp); ++ } else { ++ DerOutputStream gname = new DerOutputStream(); ++ intf.encode(gname); ++ int nameType = intf.getType(); ++ // constructed form ++ if (nameType == GeneralNameInterface.NAME_ANY || ++ nameType == GeneralNameInterface.NAME_X400 || ++ nameType == GeneralNameInterface.NAME_EDI) { ++ ++ temp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, (byte) nameType), gname); ++ } else if (nameType == GeneralNameInterface.NAME_DIRECTORY) { ++ // EXPLICIT tag because directoryName is a CHOICE ++ temp.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, (byte) nameType), gname); ++ } else ++ // primitive form ++ temp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, (byte) nameType), gname); ++ } ++ ++ } ++ ++ out.write(DerValue.tag_Sequence, temp); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/GeneralNamesException.java b/org/mozilla/jss/netscape/security/x509/GeneralNamesException.java +new file mode 100644 +index 00000000..5598ef8f +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/GeneralNamesException.java +@@ -0,0 +1,50 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.security.GeneralSecurityException; ++ ++/** ++ * Generic General Names Exception. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.4 ++ */ ++public class GeneralNamesException extends GeneralSecurityException { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -8320001725384815795L; ++ ++ /** ++ * Constructs a GeneralNamesException with no detail message. ++ */ ++ public GeneralNamesException() { ++ super(); ++ } ++ ++ /** ++ * Constructs the exception with the specified error message. ++ * ++ * @param message the requisite error message. ++ */ ++ public GeneralNamesException(String message) { ++ super(message); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/GeneralSubtree.java b/org/mozilla/jss/netscape/security/x509/GeneralSubtree.java +new file mode 100644 +index 00000000..9305e6fc +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/GeneralSubtree.java +@@ -0,0 +1,162 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.Serializable; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.PrettyPrintFormat; ++ ++/** ++ * Represent the GeneralSubtree ASN.1 object, whose syntax is: ++ * ++ *
++ * GeneralSubtree ::= SEQUENCE {
++ *    base             GeneralName,
++ *    minimum  [0]     BaseDistance DEFAULT 0,
++ *    maximum  [1]     BaseDistance OPTIONAL
++ * }
++ * BaseDistance ::= INTEGER (0..MAX)
++ * 
++ * ++ * @version 1.5 ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class GeneralSubtree implements Serializable { ++ ++ private static final long serialVersionUID = -2835481424013062770L; ++ private static final byte TAG_MIN = 0; ++ private static final byte TAG_MAX = 1; ++ private static final int MIN_DEFAULT = 0; ++ ++ private GeneralName name; ++ private int minimum = MIN_DEFAULT; ++ private int maximum = -1; ++ ++ private transient PrettyPrintFormat pp = new PrettyPrintFormat(":"); ++ ++ /** ++ * The default constructor for the class. ++ * ++ * @param name the GeneralName ++ * @param min the minimum BaseDistance ++ * @param max the maximum BaseDistance ++ */ ++ public GeneralSubtree(GeneralName name, int min, int max) { ++ this.name = name; ++ this.minimum = min; ++ this.maximum = max; ++ } ++ ++ /** ++ * Create the object from its DER encoded form. ++ * ++ * @param val the DER encoded from of the same. ++ */ ++ public GeneralSubtree(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for GeneralSubtree."); ++ } ++ name = new GeneralName(val.data.getDerValue()); ++ ++ // NB. this is always encoded with the IMPLICIT tag ++ // The checks only make sense if we assume implicit tagging, ++ // with explicit tagging the form is always constructed. ++ while (val.data.available() != 0) { ++ DerValue opt = val.data.getDerValue(); ++ ++ if (opt.isContextSpecific(TAG_MIN) && !opt.isConstructed()) { ++ opt.resetTag(DerValue.tag_Integer); ++ minimum = (opt.getInteger()).toInt(); ++ ++ } else if (opt.isContextSpecific(TAG_MAX) && !opt.isConstructed()) { ++ opt.resetTag(DerValue.tag_Integer); ++ maximum = (opt.getInteger()).toInt(); ++ } else ++ throw new IOException("Invalid encoding of GeneralSubtree."); ++ } ++ } ++ ++ /** ++ * Return a printable string of the GeneralSubtree. ++ */ ++ public String toString() { ++ String s = "\n GeneralSubtree: [\n" + ++ " GeneralName: " + ((name == null) ? "" : name.toString()) + ++ "\n Minimum: " + minimum; ++ if (maximum == -1) { ++ s += "\t Maximum: undefined"; ++ } else ++ s += "\t Maximum: " + maximum; ++ s += " ]\n"; ++ return (s); ++ } ++ ++ public String toPrint(int indent) { ++ String s = "\n" + pp.indent(indent) + "GeneralSubtree: [\n" + pp.indent(indent + 2) + ++ "GeneralName: " + ((name == null) ? "" : name.toString()) + ++ "\n" + pp.indent(indent + 2) + "Minimum: " + minimum; ++ if (maximum == -1) { ++ s += "\n" + pp.indent(indent + 2) + "Maximum: undefined"; ++ } else ++ s += "\n" + pp.indent(indent + 2) + "Maximum: " + maximum; ++ s += "]\n"; ++ return (s); ++ } ++ ++ /** ++ * Encode the GeneralSubtree. ++ * ++ * @param out the DerOutputStream to encode this object to. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream seq = new DerOutputStream(); ++ ++ name.encode(seq); ++ ++ if (minimum != MIN_DEFAULT) { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putInteger(new BigInt(minimum)); ++ seq.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_MIN), tmp); ++ } ++ if (maximum != -1) { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putInteger(new BigInt(maximum)); ++ seq.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_MAX), tmp); ++ } ++ out.write(DerValue.tag_Sequence, seq); ++ } ++ ++ public GeneralName getGeneralName() { ++ return name; ++ } ++ ++ public int getMaxValue() { ++ return maximum; ++ } ++ ++ public int getMinValue() { ++ return minimum; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/GeneralSubtrees.java b/org/mozilla/jss/netscape/security/x509/GeneralSubtrees.java +new file mode 100644 +index 00000000..a27ff8b5 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/GeneralSubtrees.java +@@ -0,0 +1,109 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.Serializable; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.PrettyPrintFormat; ++ ++/** ++ * Represent the GeneralSubtrees ASN.1 object. ++ * ++ * @version 1.4 ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class GeneralSubtrees implements Serializable { ++ ++ private static final long serialVersionUID = 6308776640697100848L; ++ private Vector trees; ++ private transient PrettyPrintFormat pp = new PrettyPrintFormat(":"); ++ ++ /** ++ * The default constructor for the class. ++ * ++ * @param trees the sequence of GeneralSubtree. ++ */ ++ public GeneralSubtrees(Vector trees) { ++ this.trees = trees; ++ } ++ ++ /** ++ * Create the object from the passed DER encoded form. ++ * ++ * @param val the DER encoded form of the same. ++ */ ++ public GeneralSubtrees(DerValue val) throws IOException { ++ trees = new Vector(1, 1); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding of GeneralSubtrees."); ++ } ++ while (val.data.available() != 0) { ++ DerValue opt = val.data.getDerValue(); ++ GeneralSubtree tree = new GeneralSubtree(opt); ++ trees.addElement(tree); ++ } ++ } ++ ++ /** ++ * Return a printable string of the GeneralSubtree. ++ */ ++ public String toString() { ++ String s = " GeneralSubtrees:\n" + trees.toString() ++ + "\n"; ++ ++ return (s); ++ } ++ ++ public String toPrint(int indent) { ++ ++ StringBuffer s = new StringBuffer(); ++ GeneralSubtree element; ++ ++ for (Enumeration e = trees.elements(); e.hasMoreElements();) { ++ element = e.nextElement(); ++ s.append(pp.indent(indent + 4) + element.toPrint(indent) + "\n"); ++ } ++ ++ return (s.toString()); ++ } ++ ++ /** ++ * Encode the GeneralSubtrees. ++ * ++ * @param out the DerOutputStrean to encode this object to. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream seq = new DerOutputStream(); ++ ++ for (int i = 0; i < trees.size(); i++) { ++ trees.elementAt(i).encode(seq); ++ } ++ out.write(DerValue.tag_Sequence, seq); ++ } ++ ++ public Vector getSubtrees() { ++ return trees; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/GenericValueConverter.java b/org/mozilla/jss/netscape/security/x509/GenericValueConverter.java +new file mode 100644 +index 00000000..2c7d9f54 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/GenericValueConverter.java +@@ -0,0 +1,143 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.CharacterCodingException; ++import java.nio.charset.CharsetEncoder; ++ ++import org.mozilla.jss.netscape.security.util.ASN1CharStrConvMap; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * A GenericValueConverter converts a string that is not associated with ++ * a particular attribute to a DER encoded ASN.1 character string type. ++ * Currently supports PrintableString, IA5String, BMPString T.61String and ++ * Universal String. ++ * ++ *

++ * The conversion is done as follows. An encoder is obtained for the all the character sets from the global default ++ * ASN1CharStrConvMap. The encoders are then used to convert the string to the smallest character set first -- ++ * printableString. If the string contains characters outside of that character set, it is converted to the next ++ * character set -- IA5String character set. If that is not enough it is converted to a BMPString, then Universal String ++ * which contains all characters. ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ * ++ */ ++ ++public class GenericValueConverter implements AVAValueConverter { ++ public GenericValueConverter() { ++ } ++ ++ /** ++ * Converts a string to a DER encoded ASN.1 primtable string, defined here ++ * as a PrintableString, IA5String, T.61String, BMPString or ++ * UniversalString. The string is not expected to be encoded in any form. ++ * ++ *

++ * If an encoder is not available for a character set that is needed to convert the string, the string cannot be ++ * converted and an IOException is thrown. For example, if the string contains characters outside the ++ * PrintableString character and only a PrintableString encoder is available then an IOException is thrown. ++ * ++ * @param s A string representing a generic attribute string value. ++ * ++ * @return The DER value of the attribute. ++ * ++ * @exception IOException if the string cannot be converted, such as ++ * when an encoder needed is ++ * unavailable. ++ */ ++ public DerValue getValue(String s) ++ throws IOException { ++ return getValue(s, null); ++ } ++ ++ public DerValue getValue(String valueString, byte[] tags) throws IOException { ++ // try to convert to printable, then t61 the universal - ++ // i.e. from minimal coverage to the broadest. ++ ++ if (tags == null || tags.length == 0) ++ tags = DefEncodingTags; ++ ++ for (int i = 0; i < tags.length; i++) { ++ try { ++ CharsetEncoder encoder = ASN1CharStrConvMap.getDefault().getEncoder(tags[i]); ++ if (encoder == null) ++ continue; ++ ++ CharBuffer charBuffer = CharBuffer.wrap(valueString.toCharArray()); ++ ByteBuffer byteBuffer = encoder.encode(charBuffer); ++ ++ return new DerValue(tags[i], byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.limit()); ++ ++ } catch (CharacterCodingException e) { ++ continue; ++ } ++ } ++ ++ throw new IOException( ++ "Cannot convert the string value to a ASN.1 type"); ++ } ++ ++ /** ++ * Creates a DerValue from the byte array of BER encoded value. ++ * ++ * NOTE: currently only supports DER encoding (a form of BER) on input . ++ * ++ * @param berStream Byte array of a BER encoded value. ++ * ++ * @return DerValue object. ++ * ++ * @exception IOException If the BER value cannot be converted to a ++ * valid Directory String DER value. ++ */ ++ public DerValue getValue(byte[] berByteStream) ++ throws IOException { ++ // accepts any tag. ++ DerValue value = new DerValue(berByteStream); ++ return value; ++ } ++ ++ /** ++ * Converts a DerValue of ASN1 Character string type to a java string ++ * (the string is not encoded in any form). ++ * ++ * @param avaValue A DerValue ++ * @return A string representing the attribute value. ++ * @exception IOException if a decoder needed for the ++ * conversion is not available or if BER value ++ * is not one of the ASN1 character string types ++ * here. ++ */ ++ public String getAsString(DerValue avaValue) ++ throws IOException { ++ return avaValue.getASN1CharString(); ++ } ++ ++ private static byte DefEncodingTags[] = { ++ DerValue.tag_PrintableString, ++ DerValue.tag_IA5String, ++ DerValue.tag_BMPString, ++ DerValue.tag_UTF8String, ++ DerValue.tag_T61String, ++ DerValue.tag_UniversalString ++ }; ++} +diff --git a/org/mozilla/jss/netscape/security/x509/HoldInstructionExtension.java b/org/mozilla/jss/netscape/security/x509/HoldInstructionExtension.java +new file mode 100644 +index 00000000..b6f55800 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/HoldInstructionExtension.java +@@ -0,0 +1,356 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * Represent the CRL Hold Instruction Code Extension. ++ * ++ *

++ * The hold instruction code is a non-critical CRL entry extension that provides a registered instruction identifier ++ * which indicates the action to be taken after encountering a certificate that has been placed on hold. ++ * ++ * @see Extension ++ * @see CertAttrSet ++ */ ++ ++public class HoldInstructionExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -6706557233070964984L; ++ /** ++ * Attribute name. ++ */ ++ public static final String NAME = "HoldInstruction"; ++ public static final String HOLD_INSTRUCTION = "value"; ++ ++ /** ++ * The Object Identifier for this extension. ++ */ ++ public static final String OID = "2.5.29.23"; ++ ++ public static final String NONE_HOLD_INSTR_OID_STR = ++ "1.2.840.10040.2.1"; ++ public static final ObjectIdentifier NONE_HOLD_INSTR_OID = ++ new ObjectIdentifier(NONE_HOLD_INSTR_OID_STR); ++ ++ public static final String CALL_ISSUER_HOLD_INSTR_OID_STR = ++ "1.2.840.10040.2.2"; ++ public static final ObjectIdentifier CALL_ISSUER_HOLD_INSTR_OID = ++ new ObjectIdentifier(CALL_ISSUER_HOLD_INSTR_OID_STR); ++ ++ public static final String REJECT_HOLD_INSTR_OID_STR = ++ "1.2.840.10040.2.3"; ++ public static final ObjectIdentifier REJECT_HOLD_INSTR_OID = ++ new ObjectIdentifier(REJECT_HOLD_INSTR_OID_STR); ++ ++ private ObjectIdentifier holdInstructionCodeOIDs[] = { NONE_HOLD_INSTR_OID, ++ CALL_ISSUER_HOLD_INSTR_OID, ++ REJECT_HOLD_INSTR_OID }; ++ private ObjectIdentifier holdInstructionCodeOID = null; ++ ++ private String holdInstructionDescription[] = { "None", ++ "Call Issuer", ++ "Reject" }; ++ ++ static { ++ try { ++ OIDMap.addAttribute(HoldInstructionExtension.class.getName(), ++ OID, NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ private int getHoldInstructionCodeFromOID(ObjectIdentifier oid) { ++ for (int i = 0; i < holdInstructionCodeOIDs.length; i++) { ++ if (oid.equals(holdInstructionCodeOIDs[i])) ++ return (i + 1); ++ } ++ return 0; ++ } ++ ++ private String getHoldInstructionDescription(ObjectIdentifier oid) { ++ String description = "Invalid"; ++ if (oid != null) { ++ int i = getHoldInstructionCodeFromOID(oid); ++ if (i > 0 && i < 4) ++ description = holdInstructionDescription[i - 1]; ++ } ++ return (description); ++ } ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ if (holdInstructionCodeOID == null) ++ throw new IOException("Unintialized hold instruction extension"); ++ ++ try (DerOutputStream os = new DerOutputStream()) { ++ os.putOID(holdInstructionCodeOID); ++ this.extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Create a HoldInstructionExtension with the date. ++ * The criticality is set to false. ++ * ++ * @param code the value to be set for the extension. ++ */ ++ public HoldInstructionExtension(int code) ++ throws IOException { ++ if (code < 1 || code > 3) ++ throw new IOException("Invalid hold instruction code"); ++ holdInstructionCodeOID = holdInstructionCodeOIDs[code - 1]; ++ this.extensionId = PKIXExtensions.HoldInstructionCode_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a HoldInstructionExtension with the date. ++ * The criticality is set to false. ++ * ++ * @param oidStr the value to be set for the extension. ++ */ ++ public HoldInstructionExtension(String oidStr) ++ throws IOException { ++ ObjectIdentifier oid = new ObjectIdentifier(oidStr); ++ if (oid == null || getHoldInstructionCodeFromOID(oid) == 0) ++ throw new IOException("Invalid hold instruction code"); ++ holdInstructionCodeOID = oid; ++ this.extensionId = PKIXExtensions.HoldInstructionCode_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a HoldInstructionExtension with the date. ++ * The criticality is set to false. ++ * ++ * @param oid the value to be set for the extension. ++ */ ++ public HoldInstructionExtension(ObjectIdentifier oid) ++ throws IOException { ++ if (getHoldInstructionCodeFromOID(oid) == 0) ++ throw new IOException("Invalid hold instruction code"); ++ holdInstructionCodeOID = oid; ++ this.extensionId = PKIXExtensions.HoldInstructionCode_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a HoldInstructionExtension with the date. ++ * The criticality is set to false. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param code the value to be set for the extension. ++ */ ++ public HoldInstructionExtension(Boolean critical, int code) ++ throws IOException { ++ if (code < 1 || code > 3) ++ throw new IOException("Invalid hold instruction code"); ++ holdInstructionCodeOID = holdInstructionCodeOIDs[code - 1]; ++ this.extensionId = PKIXExtensions.HoldInstructionCode_Id; ++ this.critical = critical.booleanValue(); ++ encodeThis(); ++ } ++ ++ /** ++ * Create a HoldInstructionExtension with the date. ++ * The criticality is set to false. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param oidStr the value to be set for the extension. ++ */ ++ public HoldInstructionExtension(Boolean critical, String oidStr) ++ throws IOException { ++ ObjectIdentifier oid = new ObjectIdentifier(oidStr); ++ if (oid == null || getHoldInstructionCodeFromOID(oid) == 0) ++ throw new IOException("Invalid hold instruction code"); ++ holdInstructionCodeOID = oid; ++ this.extensionId = PKIXExtensions.HoldInstructionCode_Id; ++ this.critical = critical.booleanValue(); ++ encodeThis(); ++ } ++ ++ /** ++ * Create a HoldInstructionExtension with the date. ++ * The criticality is set to false. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param oid the value to be set for the extension. ++ */ ++ public HoldInstructionExtension(Boolean critical, ObjectIdentifier oid) ++ throws IOException { ++ if (getHoldInstructionCodeFromOID(oid) == 0) ++ throw new IOException("Invalid hold instruction code"); ++ holdInstructionCodeOID = oid; ++ this.extensionId = PKIXExtensions.HoldInstructionCode_Id; ++ this.critical = critical.booleanValue(); ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public HoldInstructionExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.HoldInstructionCode_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ if (val.tag == DerValue.tag_ObjectId) { ++ DerInputStream derInputStream = new DerInputStream(val.toByteArray()); ++ holdInstructionCodeOID = derInputStream.getOID(); ++ if (getHoldInstructionCodeFromOID(holdInstructionCodeOID) == 0) ++ throw new IOException("Invalid encoding for HoldInstructionExtension"); ++ } else { ++ throw new IOException("Invalid encoding for HoldInstructionExtension"); ++ } ++ } ++ ++ /** ++ * Get the hold instruction code. ++ */ ++ public ObjectIdentifier getHoldInstructionCode() { ++ return holdInstructionCodeOID; ++ } ++ ++ public String getHoldInstructionCodeDescription() { ++ return getHoldInstructionDescription(holdInstructionCodeOID); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (name.equalsIgnoreCase(HOLD_INSTRUCTION)) { ++ if (!(obj instanceof ObjectIdentifier)) { ++ throw new IOException("Attribute must be of type String."); ++ } ++ holdInstructionCodeOID = (ObjectIdentifier) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:HoldInstructionCode."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(HOLD_INSTRUCTION)) { ++ return holdInstructionCodeOID; ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:HoldInstructionCode."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(HOLD_INSTRUCTION)) { ++ holdInstructionCodeOID = null; ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:HoldInstructionCode."); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the HoldInstructionExtension. ++ */ ++ public String toString() { ++ String s = super.toString() + "Hold Instruction Code: " + ++ getHoldInstructionDescription(holdInstructionCodeOID) + "\n"; ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ this.extensionId = PKIXExtensions.HoldInstructionCode_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(HOLD_INSTRUCTION); ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/IA5StringConverter.java b/org/mozilla/jss/netscape/security/x509/IA5StringConverter.java +new file mode 100644 +index 00000000..125e4b7e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/IA5StringConverter.java +@@ -0,0 +1,123 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.CharacterCodingException; ++import java.nio.charset.CharsetEncoder; ++ ++import org.mozilla.jss.netscape.security.util.ASN1CharStrConvMap; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * A AVAValueConverter that converts a IA5String attribute to a DerValue ++ * and vice versa. An example an attribute that is a IA5String string is "E". ++ * ++ * @see AVAValueConverter ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ */ ++ ++public class IA5StringConverter implements AVAValueConverter { ++ // public constructors ++ ++ /* ++ * Contructs a IA5String Converter. ++ */ ++ public IA5StringConverter() { ++ } ++ ++ /* ++ * Converts a string with ASN.1 IA5String characters to a DerValue. ++ * ++ * @param valueString a string with IA5String characters. ++ * ++ * @return a DerValue. ++ * ++ * @exception IOException if a IA5String encoder is not ++ * available for the conversion. ++ */ ++ public DerValue getValue(String valueString) ++ throws IOException { ++ return getValue(valueString, null); ++ } ++ ++ public DerValue getValue(String valueString, byte[] tags) throws IOException { ++ try { ++ CharsetEncoder encoder = ASN1CharStrConvMap.getDefault().getEncoder(DerValue.tag_IA5String); ++ if (encoder == null) ++ throw new IOException("No encoder for IA5String"); ++ ++ CharBuffer charBuffer = CharBuffer.wrap(valueString.toCharArray()); ++ ByteBuffer byteBuffer = encoder.encode(charBuffer); ++ ++ return new DerValue(DerValue.tag_IA5String, ++ byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.limit()); ++ ++ } catch (CharacterCodingException e) { ++ throw new IllegalArgumentException("Invalid IA5String AVA Value string"); ++ } ++ } ++ ++ /* ++ * Converts a BER encoded value of IA5String to a DER encoded value. ++ * Checks if the BER encoded value is a IA5String. ++ * NOTE only DER encoding is currently supported on for the BER ++ * encoded value. ++ * ++ * @param berStream a byte array of the BER encoded value. ++ * ++ * @return a DerValue. ++ * ++ * @exception IOException if the BER value cannot be converted ++ * to a IA5String DER value. ++ */ ++ public DerValue getValue(byte[] berStream) ++ throws IOException { ++ DerValue value = new DerValue(berStream); ++ if (value.tag == DerValue.tag_IA5String) ++ return value; ++ if (value.tag == DerValue.tag_PrintableString) ++ return value; ++ throw new IOException("Invalid IA5String AVA Value."); ++ } ++ ++ /* ++ * Converts a DerValue of IA5String to a java string with IA5String ++ * characters. ++ * ++ * @param avaValue a DerValue. ++ * ++ * @return a string with IA5String characters. ++ * ++ * @exception IOException if the DerValue is not a IA5String i.e. ++ * The DerValue cannot be converted to a string ++ * with IA5String characters. ++ */ ++ public String getAsString(DerValue avaValue) ++ throws IOException { ++ if (avaValue.tag == DerValue.tag_IA5String) ++ return avaValue.getIA5String(); ++ if (avaValue.tag == DerValue.tag_PrintableString) ++ return avaValue.getPrintableString(); ++ throw new IOException("Invalid IA5String AVA Value."); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/IPAddressName.java b/org/mozilla/jss/netscape/security/x509/IPAddressName.java +new file mode 100644 +index 00000000..efc14fa3 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/IPAddressName.java +@@ -0,0 +1,277 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.util.StringTokenizer; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class implements the IPAddressName as required by the GeneralNames ++ * ASN.1 object. ++ * ++ * @see GeneralName ++ * @see GeneralNameInterface ++ * @see GeneralNames ++ * ++ * @version 1.2 ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class IPAddressName implements GeneralNameInterface { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -4240184399679453666L; ++ private byte[] address; ++ ++ /** ++ * Create the IPAddressName object from the passed encoded Der value. ++ * ++ * @param derValue the encoded DER IPAddressName. ++ * @exception IOException on error. ++ */ ++ public IPAddressName(DerValue derValue) throws IOException { ++ address = derValue.getOctetString(); ++ } ++ ++ /** ++ * Create the IPAddressName object with the specified name. ++ * ++ * @param name the IPAddressName. ++ */ ++ public IPAddressName(byte[] address) { ++ this.address = address; ++ } ++ ++ protected static final char IPv4_LEN = 4; ++ protected static final char IPv6_LEN = 16; ++ protected static final IPAddr IPv4 = new IPv4Addr(); ++ protected static final IPAddr IPv6 = new IPv6Addr(); ++ ++ /** ++ * Create the IPAddressName object with a string representing the ++ * ip address and a string representing the netmask, with encoding ++ * having ip address encoding followed by the netmask encoding. ++ * This form is needed for name constraints extension. ++ * ++ * @param s the ip address in the format: n.n.n.n or x:x:x:x:x:x:x:x (RFC 1884) ++ * @param netmask the netmask address in the format: n.n.n.n or x:x:x:x:x:x:x:x (RFC 1884) ++ */ ++ public IPAddressName(String s, String netmask) { ++ // Based on PKIX RFC2459. IPAddress has ++ // 8 bytes (instead of 4 bytes) in the ++ // context of NameConstraints ++ IPAddr ipAddr = null; ++ if (s.indexOf(':') != -1) { ++ ipAddr = IPv6; ++ address = new byte[IPv6_LEN * 2]; ++ } else { ++ ipAddr = IPv4; ++ address = new byte[IPv4_LEN * 2]; ++ } ++ StringTokenizer st = new StringTokenizer(s, ","); ++ int numFilled = ipAddr.getIPAddr(st.nextToken(), address, 0); ++ if (st.hasMoreTokens()) { ++ ipAddr.getIPAddr(st.nextToken(), address, numFilled); ++ } else { ++ for (int i = numFilled; i < address.length; i++) ++ address[i] = (byte) 0xff; ++ } ++ } ++ ++ /** ++ * Create the IPAddressName object with a string representing the ++ * ip address. ++ * ++ * @param s the ip address in the format: n.n.n.n or x:x:x:x:x:x:x:x ++ */ ++ public IPAddressName(String s) { ++ IPAddr ipAddr = null; ++ if (s.indexOf(':') != -1) { ++ ipAddr = IPv6; ++ address = new byte[IPv6_LEN]; ++ } else { ++ ipAddr = IPv4; ++ address = new byte[IPv4_LEN]; ++ } ++ ipAddr.getIPAddr(s, address, 0); ++ } ++ ++ /** ++ * Return the type of the GeneralName. ++ */ ++ public int getType() { ++ return (GeneralNameInterface.NAME_IP); ++ } ++ ++ /** ++ * Encode the IPAddress name into the DerOutputStream. ++ * ++ * @param out the DER stream to encode the IPAddressName to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putOctetString(address); ++ } ++ ++ /** ++ * Return a printable string of IPaddress ++ */ ++ public String toString() { ++ if (address.length == 4) { ++ return ("IPAddress: " + (address[0] & 0xff) + "." ++ + (address[1] & 0xff) + "." ++ + (address[2] & 0xff) + "." + (address[3] & 0xff)); ++ } else { ++ StringBuffer r = new StringBuffer("IPAddress: " + Integer.toHexString(address[0] & 0xff)); ++ String hexString = Integer.toHexString(address[1] & 0xff); ++ if (hexString.length() == 1) { ++ r.append("0" + hexString); ++ } else { ++ r.append(hexString); ++ } ++ for (int i = 2; i < address.length;) { ++ r.append(":" + Integer.toHexString(address[i] & 0xff)); ++ hexString = Integer.toHexString(address[i + 1] & 0xff); ++ if (hexString.length() == 1) { ++ r.append("0" + hexString); ++ } else { ++ r.append(hexString); ++ } ++ i += 2; ++ } ++ return r.toString(); ++ } ++ } ++} ++ ++interface IPAddr { ++ public int getIPAddr(String s, byte[] address, int start); ++ ++ public int getLength(); ++} ++ ++class IPv4Addr implements IPAddr { ++ protected static final int IPv4_LEN = 4; ++ ++ /** ++ * Gets an IP v4 address in the form n.n.n.n. ++ */ ++ public int getIPAddr(String s, byte[] address, int start) { ++ StringTokenizer st = new StringTokenizer(s, "."); ++ int nt = st.countTokens(); ++ if (nt != IPv4_LEN) ++ throw new InvalidIPAddressException(s); ++ try { ++ int end = start + nt; ++ for (int i = start; i < end; i++) { ++ Integer j = new Integer(st.nextToken()); ++ address[i] = (byte) j.intValue(); ++ } ++ } catch (NumberFormatException e) { ++ throw new InvalidIPAddressException(s); ++ } ++ return nt; ++ } ++ ++ public int getLength() { ++ return IPv4_LEN; ++ } ++} ++ ++class IPv6Addr implements IPAddr { ++ /** ++ * Gets an IP address in the forms as defined in RFC1884:
++ *

    ++ *
  • x:x:x:x:x:x:x:x ++ *
  • ...::xxx (using :: shorthand) ++ *
  • ...:n.n.n.n (with n.n.n.n at the end) ++ *
++ */ ++ public int getIPAddr(String s, byte[] address, int start) { ++ int lastcolon = -2; ++ int end = start + 16; ++ int idx = start; ++ for (int i = start; i < address.length; i++) ++ address[i] = 0; ++ if (s.indexOf('.') != -1) { // has n.n.n.n at the end ++ lastcolon = s.lastIndexOf(':'); ++ if (lastcolon == -1) ++ throw new InvalidIPAddressException(s); ++ end -= 4; ++ IPAddressName.IPv4.getIPAddr( ++ s.substring(lastcolon + 1), address, end); ++ } ++ try { ++ String s1 = s; ++ if (lastcolon != -2) ++ s1 = s.substring(0, lastcolon + 1); ++ int lastDoubleColon = s1.indexOf("::"); ++ String l = s1, r = null; ++ StringTokenizer lt = null, rt = null; ++ if (lastDoubleColon != -1) { ++ l = s1.substring(0, lastDoubleColon); ++ r = s1.substring(lastDoubleColon + 2); ++ if (l.length() == 0) ++ l = null; ++ if (r.length() == 0) ++ r = null; ++ } ++ int at = 0; ++ if (l != null) { ++ lt = new StringTokenizer(l, ":", false); ++ at += lt.countTokens(); ++ } ++ if (r != null) { ++ rt = new StringTokenizer(r, ":", false); ++ at += rt.countTokens(); ++ } ++ if (at > 8 || ++ (lastcolon != -2 && (at > 6 || (lastDoubleColon == -1 && at != 6)))) ++ throw new InvalidIPAddressException(s); ++ if (l != null) { ++ while (lt.hasMoreTokens()) { ++ String tok = lt.nextToken(); ++ int j = Integer.parseInt(tok, 16); ++ address[idx++] = (byte) ((j >> 8) & 0xFF); ++ address[idx++] = (byte) (j & 0xFF); ++ } ++ } ++ if (r != null) { ++ idx = end - (rt.countTokens() * 2); ++ while (rt.hasMoreTokens()) { ++ String tok = rt.nextToken(); ++ int j = Integer.parseInt(tok, 16); ++ address[idx++] = (byte) ((j >> 8) & 0xFF); ++ address[idx++] = (byte) (j & 0xFF); ++ } ++ } ++ } catch (NumberFormatException e) { ++ throw new InvalidIPAddressException(s); ++ } ++ return 16; ++ } ++ ++ public int getLength() { ++ return 16; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/InvalidIPAddressException.java b/org/mozilla/jss/netscape/security/x509/InvalidIPAddressException.java +new file mode 100644 +index 00000000..72cadca4 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/InvalidIPAddressException.java +@@ -0,0 +1,33 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++public class InvalidIPAddressException extends RuntimeException { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -1601934234587845028L; ++ ++ public InvalidIPAddressException() { ++ super(); ++ } ++ ++ public InvalidIPAddressException(String ip) { ++ super("Invalid IP Address '" + ip + "'"); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/InvalidityDateExtension.java b/org/mozilla/jss/netscape/security/x509/InvalidityDateExtension.java +new file mode 100755 +index 00000000..cc63c148 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/InvalidityDateExtension.java +@@ -0,0 +1,242 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.security.cert.CertificateException; ++import java.util.Date; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the CRL Invalidity Date Extension. ++ * ++ *

++ * This CRL entry extension, if present, provides the date on which it is known or suspected that the private key was ++ * compromised or that the certificate otherwise became invalid. Invalidity date may be earlier than the revocation ++ * date. ++ * ++ * @see Extension ++ * @see CertAttrSet ++ */ ++ ++public class InvalidityDateExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 2191026017389643053L; ++ /** ++ * Attribute name. ++ */ ++ public static final String NAME = "InvalidityDate"; ++ public static final String INVALIDITY_DATE = "value"; ++ ++ /** ++ * The Object Identifier for this extension. ++ */ ++ public static final String OID = "2.5.29.24"; ++ ++ private Date invalidityDate = null; ++ ++ static { ++ try { ++ OIDMap.addAttribute(InvalidityDateExtension.class.getName(), ++ OID, NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ if (invalidityDate == null) ++ throw new IOException("Unintialized invalidity date extension"); ++ try (DerOutputStream os = new DerOutputStream()) { ++ os.putGeneralizedTime(this.invalidityDate); ++ this.extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Create a InvalidityDateExtension with the date. ++ * The criticality is set to false. ++ * ++ * @param dateOfInvalidity the value to be set for the extension. ++ */ ++ public InvalidityDateExtension(Date dateOfInvalidity) ++ throws IOException { ++ this.invalidityDate = dateOfInvalidity; ++ this.extensionId = PKIXExtensions.InvalidityDate_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a InvalidityDateExtension with the date. ++ * The criticality is set to false. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param dateOfInvalidity the value to be set for the extension. ++ */ ++ public InvalidityDateExtension(Boolean critical, Date dateOfInvalidity) ++ throws IOException { ++ this.invalidityDate = dateOfInvalidity; ++ this.extensionId = PKIXExtensions.InvalidityDate_Id; ++ this.critical = critical.booleanValue(); ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public InvalidityDateExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.InvalidityDate_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ if (val.tag == DerValue.tag_GeneralizedTime) { ++ DerInputStream derInputStream = new DerInputStream(val.toByteArray()); ++ this.invalidityDate = derInputStream.getGeneralizedTime(); ++ } else { ++ throw new IOException("Invalid encoding for InvalidityDateExtension"); ++ } ++ } ++ ++ /** ++ * Get the invalidity date. ++ */ ++ public Date getInvalidityDate() { ++ return invalidityDate; ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (name.equalsIgnoreCase(INVALIDITY_DATE)) { ++ if (!(obj instanceof Date)) { ++ throw new IOException("Attribute must be of type Date."); ++ } ++ invalidityDate = (Date) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:InvalidityDate."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(INVALIDITY_DATE)) { ++ if (invalidityDate == null) ++ return null; ++ else ++ return invalidityDate; ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:InvalidityDate."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(INVALIDITY_DATE)) { ++ invalidityDate = null; ++ } else { ++ throw new IOException("Attribute name not recognized by" + ++ " CertAttrSet:InvalidityDate."); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the InvalidityDateExtension. ++ */ ++ public String toString() { ++ String s = super.toString() + "Invalidity Date: " + ++ ((invalidityDate == null) ? "" : invalidityDate.toString()) ++ + "\n"; ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ this.extensionId = PKIXExtensions.InvalidityDate_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(INVALIDITY_DATE); ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/IssuerAlternativeNameExtension.java b/org/mozilla/jss/netscape/security/x509/IssuerAlternativeNameExtension.java +new file mode 100644 +index 00000000..84556d78 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/IssuerAlternativeNameExtension.java +@@ -0,0 +1,239 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This represents the Issuer Alternative Name Extension. ++ * ++ * This extension, if present, allows the issuer to specify multiple ++ * alternative names. ++ * ++ *

++ * Extensions are represented as a sequence of the extension identifier (Object Identifier), a boolean flag stating ++ * whether the extension is to be treated as being critical and the extension value itself (this is again a DER encoding ++ * of the extension value). ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.7 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class IssuerAlternativeNameExtension ++ extends Extension implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -269518027483586255L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = ++ "x509.info.extensions.IssuerAlternativeName"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "IssuerAlternativeName"; ++ public static final String ISSUER_NAME = "issuer_name"; ++ ++ // private data members ++ GeneralNames names; ++ ++ // Encode this extension ++ private void encodeThis() throws IOException { ++ DerOutputStream os = new DerOutputStream(); ++ try { ++ names.encode(os); ++ } catch (GeneralNamesException e) { ++ throw new IOException(e); ++ } ++ this.extensionValue = os.toByteArray(); ++ } ++ ++ /** ++ * Create a IssuerAlternativeNameExtension with the passed GeneralNames. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param names the GeneralNames for the issuer. ++ * @exception IOException on error. ++ */ ++ public IssuerAlternativeNameExtension(Boolean critical, GeneralNames names) ++ throws IOException { ++ this.names = names; ++ this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; ++ this.critical = critical.booleanValue(); ++ encodeThis(); ++ } ++ ++ /** ++ * Create a IssuerAlternativeNameExtension with the passed GeneralNames. ++ * ++ * @param names the GeneralNames for the issuer. ++ * @exception IOException on error. ++ */ ++ public IssuerAlternativeNameExtension(GeneralNames names) ++ throws IOException { ++ this.names = names; ++ this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a default IssuerAlternativeNameExtension. ++ */ ++ public IssuerAlternativeNameExtension() { ++ extensionId = PKIXExtensions.IssuerAlternativeName_Id; ++ critical = false; ++ names = new GeneralNames(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public IssuerAlternativeNameExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ try { ++ names = new GeneralNames(val); ++ } catch (GeneralNamesException e) { ++ throw new IOException("IssuerAlternativeNameExtension: " + e, e); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the IssuerAlternativeName. ++ */ ++ public String toString() { ++ if (names == null) ++ return ""; ++ String s = super.toString() + "IssuerAlternativeName [\n" ++ + names.toString() + "]\n"; ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding error. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = PKIXExtensions.IssuerAlternativeName_Id; ++ critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(ISSUER_NAME)) { ++ if (!(obj instanceof GeneralNames)) { ++ throw new IOException("Attribute value should be of" + ++ " type GeneralNames."); ++ } ++ names = (GeneralNames) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:IssuerAlternativeName."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(ISSUER_NAME)) { ++ return (names); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:IssuerAlternativeName."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(ISSUER_NAME)) { ++ names = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:IssuerAlternativeName."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(ISSUER_NAME); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/IssuingDistributionPoint.java b/org/mozilla/jss/netscape/security/x509/IssuingDistributionPoint.java +new file mode 100644 +index 00000000..8125bfca +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/IssuingDistributionPoint.java +@@ -0,0 +1,323 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.BufferedOutputStream; ++import java.io.ByteArrayOutputStream; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.OutputStream; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++ ++import org.mozilla.jss.asn1.ANY; ++import org.mozilla.jss.asn1.ASN1Value; ++import org.mozilla.jss.asn1.BOOLEAN; ++import org.mozilla.jss.asn1.EXPLICIT; ++import org.mozilla.jss.asn1.InvalidBERException; ++import org.mozilla.jss.asn1.SEQUENCE; ++import org.mozilla.jss.asn1.Tag; ++ ++/** ++ *

++ * issuingDistributionPoint ::= SEQUENCE {
++ *         distributionPoint       [0] DistributionPointName OPTIONAL,
++ *         onlyContainsUserCerts   [1] BOOLEAN DEFAULT FALSE,
++ *         onlyContainsCACerts     [2] BOOLEAN DEFAULT FALSE,
++ *         onlySomeReasons         [3] ReasonFlags OPTIONAL,
++ *         indirectCRL             [4] BOOLEAN DEFAULT FALSE }
++ *
++ * DistributionPointName ::= CHOICE {
++ *         fullName                [0]     GeneralNames,
++ *         nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
++ *
++ * ReasonFlags ::= BIT STRING {
++ *         unused                  (0),
++ *         keyCompromise           (1),
++ *         cACompromise            (2),
++ *         affiliationChanged      (3),
++ *         superseded              (4),
++ *         cessationOfOperation    (5),
++ *         certificateHold         (6) }
++ *
++ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
++ *
++ * GeneralName ::= CHOICE {
++ *         otherName                       [0]     OtherName,
++ *         rfc822Name                      [1]     IA5String,
++ *         dNSName                         [2]     IA5String,
++ *         x400Address                     [3]     ORAddress,
++ *         directoryName                   [4]     Name,
++ *         ediPartyName                    [5]     EDIPartyName,
++ *         uniformResourceIdentifier       [6]     IA5String,
++ *         iPAddress                       [7]     OCTET STRING,
++ *         registeredID                    [8]     OBJECT IDENTIFIER}
++ *
++ * OtherName ::= SEQUENCE {
++ *         type-id    OBJECT IDENTIFIER,
++ *         value      [0] EXPLICIT ANY DEFINED BY type-id }
++ *
++ * EDIPartyName ::= SEQUENCE {
++ *         nameAssigner            [0]     DirectoryString OPTIONAL,
++ *         partyName               [1]     DirectoryString }
++ *
++ * RelativeDistinguishedName ::=
++ *         SET OF AttributeTypeAndValue
++ *
++ * AttributeTypeAndValue ::= SEQUENCE {
++ *         type     AttributeType,
++ *         value    AttributeValue }
++ *
++ * AttributeType ::= OBJECT IDENTIFIER
++ *
++ * AttributeValue ::= ANY DEFINED BY AttributeType
++ * 
++ * ++ * See the documentation in CRLDistributionPoint for ++ * the DistributionPointName and ReasonFlags ASN.1 types. ++ */ ++public class IssuingDistributionPoint implements ASN1Value { ++ ++ // at most one of the following two may be specified. One or both can ++ // be null. ++ private GeneralNames fullName = null; ++ private RDN relativeName = null; ++ ++ private boolean onlyContainsUserCerts = false; // DEFAULT FALSE ++ private boolean onlyContainsCACerts = false; // DEFAULT FALSE ++ private BitArray onlySomeReasons = null; // optional, may be null ++ private boolean indirectCRL = false; // DEFAULT FALSE ++ ++ // cache encoding of fullName ++ private ANY fullNameEncoding; ++ ++ /** ++ * Returns the fullName of the DistributionPointName, which may be null. ++ */ ++ public GeneralNames getFullName() { ++ return fullName; ++ } ++ ++ /** ++ * Returns the relativeName of the DistributionPointName, which may be null. ++ */ ++ public RDN getRelativeName() { ++ return relativeName; ++ } ++ ++ /** ++ * Sets the fullName of the DistributionPointName. It may be set to null. ++ * If it is set to a non-null value, relativeName will be ++ * set to null, because at most one of these two attributes ++ * can be specified at a time. ++ * ++ * @exception GeneralNamesException If an error occurs encoding the ++ * name. ++ */ ++ public void setFullName(GeneralNames fullName) ++ throws GeneralNamesException, IOException { ++ this.fullName = fullName; ++ if (fullName != null) { ++ // encode the name to catch any problems with it ++ DerOutputStream derOut = new DerOutputStream(); ++ fullName.encode(derOut); ++ try { ++ ANY raw = new ANY(derOut.toByteArray()); ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ raw.encodeWithAlternateTag(Tag.get(0), bos); ++ fullNameEncoding = new ANY(bos.toByteArray()); ++ } catch (InvalidBERException e) { ++ // assume this won't happen, since it would imply a bug ++ // in DerOutputStream ++ throw new GeneralNamesException(e.toString()); ++ } ++ ++ this.relativeName = null; ++ } ++ } ++ ++ /** ++ * Sets the relativeName of the DistributionPointName. It may be set to null. ++ * If it is set to a non-null value, fullName will be ++ * set to null, because at most one of these two attributes ++ * can be specified at a time. ++ */ ++ public void setRelativeName(RDN relativeName) { ++ this.relativeName = relativeName; ++ if (relativeName != null) { ++ this.fullName = null; ++ } ++ } ++ ++ public boolean getOnlyContainsUserCerts() { ++ return onlyContainsUserCerts; ++ } ++ ++ public void setOnlyContainsUserCerts(boolean b) { ++ onlyContainsUserCerts = b; ++ } ++ ++ public boolean getOnlyContainsCACerts() { ++ return onlyContainsCACerts; ++ } ++ ++ public void setOnlyContainsCACerts(boolean b) { ++ onlyContainsCACerts = b; ++ } ++ ++ /** ++ * Returns the reason flags for this distribution point. May be null. ++ */ ++ public BitArray getOnlySomeReasons() { ++ return onlySomeReasons; ++ } ++ ++ /** ++ * Sets the reason flags for this distribution point. May be set to null. ++ */ ++ public void setOnlySomeReasons(BitArray reasons) { ++ this.onlySomeReasons = reasons; ++ } ++ ++ public boolean getIndirectCRL() { ++ return indirectCRL; ++ } ++ ++ public void setIndirectCRL(boolean b) { ++ indirectCRL = b; ++ } ++ ++ ///////////////////////////////////////////////////////////// ++ // DER encoding ++ ///////////////////////////////////////////////////////////// ++ private static final Tag TAG = SEQUENCE.TAG; ++ ++ public Tag getTag() { ++ return TAG; ++ } ++ ++ public void encode(OutputStream ostream) throws IOException { ++ encode(TAG, ostream); ++ } ++ ++ public void encode(Tag implicitTag, OutputStream ostream) ++ throws IOException { ++ ++ SEQUENCE seq = new SEQUENCE(); ++ DerOutputStream derOut; ++ ++ try { ++ ++ // Encodes the DistributionPointName. Because DistributionPointName ++ // is a CHOICE, the [0] tag is forced to be EXPLICIT. ++ if (fullName != null) { ++ EXPLICIT distPoint = new EXPLICIT(Tag.get(0), fullNameEncoding); ++ seq.addElement(distPoint); ++ } else if (relativeName != null) { ++ derOut = new DerOutputStream(); ++ relativeName.encode(derOut); ++ ANY raw = new ANY(derOut.toByteArray()); ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ raw.encodeWithAlternateTag(Tag.get(1), bos); ++ ANY distPointName = new ANY(bos.toByteArray()); ++ EXPLICIT distPoint = new EXPLICIT(Tag.get(0), distPointName); ++ seq.addElement(distPoint); ++ } ++ ++ if (onlyContainsUserCerts != false) { ++ seq.addElement(Tag.get(1), new BOOLEAN(true)); ++ } ++ if (onlyContainsCACerts != false) { ++ seq.addElement(Tag.get(2), new BOOLEAN(true)); ++ } ++ ++ // Encodes the ReasonFlags. ++ if (onlySomeReasons != null) { ++ derOut = new DerOutputStream(); ++ derOut.putUnalignedBitString(onlySomeReasons); ++ ANY raw = new ANY(derOut.toByteArray()); ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ raw.encodeWithAlternateTag(Tag.get(3), bos); ++ ANY reasonEncoding = new ANY(bos.toByteArray()); ++ seq.addElement(reasonEncoding); ++ } ++ ++ if (indirectCRL != false) { ++ seq.addElement(Tag.get(4), new BOOLEAN(true)); ++ } ++ ++ seq.encode(implicitTag, ostream); ++ ++ } catch (InvalidBERException e) { ++ // this shouldn't happen unless there is a bug in one of ++ // the Sun encoding classes ++ throw new IOException(e.toString()); ++ } ++ } ++ ++ public static void main(String args[]) { ++ BufferedOutputStream bos = null; ++ ++ try { ++ if (args.length != 1) { ++ System.out.println("Usage: IssuingDistributionPoint "); ++ System.exit(-1); ++ } ++ ++ bos = new BufferedOutputStream( ++ new FileOutputStream(args[0])); ++ ++ SEQUENCE idps = new SEQUENCE(); ++ ++ IssuingDistributionPoint idp = new IssuingDistributionPoint(); ++ ++ X500Name dn = new X500Name("CN=Skovw Wjasldk,E=nicolson@netscape.com" + ++ ",OU=Certificate Server,O=Netscape,C=US"); ++ GeneralNames generalNames = new GeneralNames(); ++ generalNames.addElement(dn); ++ idp.setFullName(generalNames); ++ idps.addElement(idp); ++ ++ idp = new IssuingDistributionPoint(); ++ URIName uri = new URIName("http://www.mycrl.com/go/here"); ++ generalNames = new GeneralNames(); ++ generalNames.addElement(uri); ++ idp.setFullName(generalNames); ++ idp.setOnlyContainsUserCerts(true); ++ idp.setOnlyContainsCACerts(true); ++ idp.setIndirectCRL(true); ++ BitArray ba = new BitArray(5, new byte[] { (byte) 0x28 }); ++ idp.setOnlySomeReasons(ba); ++ idps.addElement(idp); ++ ++ idps.encode(bos); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } finally { ++ if (bos != null) { ++ try { ++ bos.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/IssuingDistributionPointExtension.java b/org/mozilla/jss/netscape/security/x509/IssuingDistributionPointExtension.java +new file mode 100644 +index 00000000..4cc26d93 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/IssuingDistributionPointExtension.java +@@ -0,0 +1,425 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.BufferedOutputStream; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.security.cert.CertificateException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.asn1.ASN1Util; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * A critical CRL extension that identifies the CRL distribution point ++ * for a particular CRL ++ * ++ *
++ * issuingDistributionPoint ::= SEQUENCE {
++ *         distributionPoint       [0] DistributionPointName OPTIONAL,
++ *         onlyContainsUserCerts   [1] BOOLEAN DEFAULT FALSE,
++ *         onlyContainsCACerts     [2] BOOLEAN DEFAULT FALSE,
++ *         onlySomeReasons         [3] ReasonFlags OPTIONAL,
++ *         indirectCRL             [4] BOOLEAN DEFAULT FALSE }
++ *
++ * DistributionPointName ::= CHOICE {
++ *         fullName                [0]     GeneralNames,
++ *         nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
++ *
++ * ReasonFlags ::= BIT STRING {
++ *         unused                  (0),
++ *         keyCompromise           (1),
++ *         cACompromise            (2),
++ *         affiliationChanged      (3),
++ *         superseded              (4),
++ *         cessationOfOperation    (5),
++ *         certificateHold         (6) }
++ *
++ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
++ *
++ * GeneralName ::= CHOICE {
++ *         otherName                       [0]     OtherName,
++ *         rfc822Name                      [1]     IA5String,
++ *         dNSName                         [2]     IA5String,
++ *         x400Address                     [3]     ORAddress,
++ *         directoryName                   [4]     Name,
++ *         ediPartyName                    [5]     EDIPartyName,
++ *         uniformResourceIdentifier       [6]     IA5String,
++ *         iPAddress                       [7]     OCTET STRING,
++ *         registeredID                    [8]     OBJECT IDENTIFIER}
++ *
++ * OtherName ::= SEQUENCE {
++ *         type-id    OBJECT IDENTIFIER,
++ *         value      [0] EXPLICIT ANY DEFINED BY type-id }
++ *
++ * EDIPartyName ::= SEQUENCE {
++ *         nameAssigner            [0]     DirectoryString OPTIONAL,
++ *         partyName               [1]     DirectoryString }
++ *
++ * RelativeDistinguishedName ::=
++ *         SET OF AttributeTypeAndValue
++ *
++ * AttributeTypeAndValue ::= SEQUENCE {
++ *         type     AttributeType,
++ *         value    AttributeValue }
++ *
++ * AttributeType ::= OBJECT IDENTIFIER
++ *
++ * AttributeValue ::= ANY DEFINED BY AttributeType
++ * 
++ */ ++public class IssuingDistributionPointExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -1281544042375527550L; ++ ++ /** ++ * The Object Identifier for this extension. ++ */ ++ public static final String OID = "2.5.29.28"; ++ ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "IssuingDistributionPoint"; ++ public static final String ISSUING_DISTRIBUTION_POINT = "issuing_distribution_point"; ++ ++ // Private data members ++ private IssuingDistributionPoint issuingDistributionPoint = null; ++ ++ // Cached DER-encoding to improve performance. ++ private byte[] cachedEncoding = null; ++ ++ static { ++ try { ++ OIDMap.addAttribute(IssuingDistributionPointExtension.class.getName(), ++ OID, NAME); ++ } catch (CertificateException e) { ++ } ++ } ++ ++ /** ++ * This constructor is very important, since it will be called ++ * by the system. ++ */ ++ public IssuingDistributionPointExtension(Boolean critical, Object value) ++ throws IOException { ++ ++ this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; ++ this.critical = critical.booleanValue(); ++ this.extensionValue = ((byte[]) value).clone(); ++ ++ byte[] extValue = this.extensionValue; ++ issuingDistributionPoint = new IssuingDistributionPoint(); ++ DerValue val = new DerValue(extValue); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint"); ++ } ++ ++ while (val.data.available() != 0) { ++ DerValue opt = val.data.getDerValue(); ++ ++ if (opt != null) { ++ for (int i = 0; i < 5; i++) { ++ if (opt.isContextSpecific((byte) i)) { ++ if ((i == 0 && opt.isConstructed() && opt.data.available() != 0) || ++ (i != 0 && (!opt.isConstructed()) && opt.data.available() != 0)) { ++ ++ if (i == 0) { ++ DerValue opt1 = opt.data.getDerValue(); ++ if (opt1 != null) { ++ if (opt1.isContextSpecific((byte) 0)) { ++ if (opt1.isConstructed() && opt1.data.available() != 0) { ++ opt1.resetTag(DerValue.tag_Sequence); ++ ++ try { ++ GeneralNames fullName = new GeneralNames(opt1); ++ if (fullName != null) { ++ issuingDistributionPoint.setFullName(fullName); ++ } ++ } catch (GeneralNamesException e) { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint " + e, e); ++ } catch (IOException e) { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint " + e, e); ++ } ++ } else { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint"); ++ } ++ ++ } else if (opt1.isContextSpecific((byte) 1)) { ++ if (opt1.isConstructed() && opt1.data.available() != 0) { ++ opt1.resetTag(DerValue.tag_Set); ++ ++ try { ++ RDN relativeName = new RDN(opt1); ++ if (relativeName != null) { ++ issuingDistributionPoint.setRelativeName(relativeName); ++ } ++ } catch (IOException e) { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint " + e, e); ++ } ++ } else { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint"); ++ } ++ } ++ } ++ ++ } else if (i == 3) { ++ opt.resetTag(DerValue.tag_BitString); ++ try { ++ BitArray reasons = opt.getUnalignedBitString(); ++ if (reasons == null) { ++ throw new IOException("Unable to get the unaligned bit string."); ++ } ++ issuingDistributionPoint.setOnlySomeReasons(reasons); ++ ++ @SuppressWarnings("unused") ++ byte[] a = reasons.toByteArray(); // check for errors ++ } catch (IOException e) { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint " + e, e); ++ } ++ ++ } else { ++ opt.resetTag(DerValue.tag_Boolean); ++ try { ++ boolean b = opt.getBoolean(); ++ if (i == 1) { ++ issuingDistributionPoint.setOnlyContainsUserCerts(b); ++ } else if (i == 2) { ++ issuingDistributionPoint.setOnlyContainsCACerts(b); ++ } else if (i == 4) { ++ issuingDistributionPoint.setIndirectCRL(b); ++ } ++ } catch (IOException e) { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint " + e, e); ++ } ++ } ++ } else { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint"); ++ } ++ } ++ } ++ } else { ++ throw new IOException("Invalid encoding of IssuingDistributionPoint"); ++ } ++ } ++ ++ } ++ ++ /** ++ * Creates a new IssuingDistributionPoint extension, with the given ++ * issuing distribution point as the first element. ++ */ ++ public IssuingDistributionPointExtension(IssuingDistributionPoint idp) { ++ this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; ++ this.critical = true; ++ issuingDistributionPoint = idp; ++ } ++ ++ /** ++ * Returns the issuing distribution point. ++ */ ++ public IssuingDistributionPoint getIssuingDistributionPoint() { ++ return issuingDistributionPoint; ++ } ++ ++ /** ++ * Sets the criticality of this extension. PKIX dictates that this ++ * extension SHOULD be critical, so applications can make it not critical ++ * if they have a very good reason. By default, the extension is critical. ++ */ ++ public void setCritical(boolean critical) { ++ this.critical = critical; ++ } ++ ++ /** ++ * Gets the criticality of this extension. PKIX dictates that this ++ * extension SHOULD be critical, so by default, the extension is critical. ++ */ ++ public boolean getCritical(boolean critical) { ++ return this.critical; ++ } ++ ++ /** ++ * Encodes this extension to the given DerOutputStream. ++ * This method re-encodes each time it is called, so it is not very ++ * efficient. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ extensionValue = ASN1Util.encode(issuingDistributionPoint); ++ super.encode(out); ++ } ++ ++ /** ++ * Should be called if any change is made to this data structure ++ * so that the cached DER encoding can be discarded. ++ */ ++ public void flushCachedEncoding() { ++ cachedEncoding = null; ++ } ++ ++ /** ++ * Returns a printable representation of the IssuingDistributionPointExtension ++ */ ++ @Override ++ public String toString() { ++ return NAME; ++ } ++ ++ /** ++ * DER-encodes this extension to the given OutputStream. ++ */ ++ public void encode(OutputStream ostream) ++ throws CertificateException, IOException { ++ if (cachedEncoding == null) { ++ // only re-encode if necessary ++ DerOutputStream tmp = new DerOutputStream(); ++ encode(tmp); ++ cachedEncoding = tmp.toByteArray(); ++ } ++ ostream.write(cachedEncoding); ++ } ++ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ throw new IOException("Not supported"); ++ } ++ ++ public void set(String name, Object obj) ++ throws CertificateException, IOException { ++ if (name.equalsIgnoreCase(ISSUING_DISTRIBUTION_POINT)) { ++ if (!(obj instanceof IssuingDistributionPoint)) { ++ throw new IOException("Attribute value should be of type IssuingDistributionPoint."); ++ } ++ issuingDistributionPoint = (IssuingDistributionPoint) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:IssuingDistributionPointExtension"); ++ } ++ } ++ ++ public Object get(String name) ++ throws CertificateException, IOException { ++ if (name.equalsIgnoreCase(ISSUING_DISTRIBUTION_POINT)) { ++ return issuingDistributionPoint; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:IssuingDistributionPointExtension"); ++ } ++ } ++ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ if (name.equalsIgnoreCase(ISSUING_DISTRIBUTION_POINT)) { ++ issuingDistributionPoint = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:IssuingDistributionPointExtension"); ++ } ++ } ++ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(ISSUING_DISTRIBUTION_POINT); ++ return (elements.elements()); ++ // return (new Vector()).elements(); ++ } ++ ++ public String getName() { ++ return NAME; ++ } ++ ++ /** ++ * Test driver. ++ */ ++ public static void main(String args[]) { ++ ++ BufferedOutputStream bos = null; ++ try { ++ ++ if (args.length != 1) { ++ System.out.println("Usage: IssuingDistributionPointExtension " + ++ ""); ++ System.exit(-1); ++ } ++ ++ bos = new BufferedOutputStream( ++ new FileOutputStream(args[0])); ++ ++ // URI only ++ IssuingDistributionPoint idp = new IssuingDistributionPoint(); ++ URIName uri = new URIName("http://www.mycrl.com/go/here"); ++ GeneralNames generalNames = new GeneralNames(); ++ generalNames.addElement(uri); ++ idp.setFullName(generalNames); ++ IssuingDistributionPointExtension idpExt = ++ new IssuingDistributionPointExtension(idp); ++ ++ // DN only ++ idp = new IssuingDistributionPoint(); ++ X500Name dn = new X500Name("CN=Otis Smith,E=otis@fedoraproject.org" + ++ ",OU=Certificate Server,O=Fedora,C=US"); ++ generalNames = new GeneralNames(); ++ generalNames.addElement(dn); ++ idp.setFullName(generalNames); ++ idpExt.set(IssuingDistributionPointExtension.ISSUING_DISTRIBUTION_POINT, idp); ++ ++ // DN + reason ++ BitArray ba = new BitArray(5, new byte[] { (byte) 0x28 }); ++ idp = new IssuingDistributionPoint(); ++ idp.setFullName(generalNames); ++ idp.setOnlySomeReasons(ba); ++ idpExt.set(IssuingDistributionPointExtension.ISSUING_DISTRIBUTION_POINT, idp); ++ ++ // relative DN + reason + crlIssuer ++ idp = new IssuingDistributionPoint(); ++ RDN rdn = new RDN("OU=foobar dept"); ++ idp.setRelativeName(rdn); ++ idp.setOnlySomeReasons(ba); ++ idp.setOnlyContainsCACerts(true); ++ idp.setOnlyContainsUserCerts(true); ++ idp.setIndirectCRL(true); ++ idpExt.set(IssuingDistributionPointExtension.ISSUING_DISTRIBUTION_POINT, idp); ++ ++ idpExt.setCritical(false); ++ idpExt.encode(bos); ++ ++ ++ ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } finally { ++ if (bos != null) { ++ try { ++ bos.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/KeyIdentifier.java b/org/mozilla/jss/netscape/security/x509/KeyIdentifier.java +new file mode 100644 +index 00000000..5d4c183d +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/KeyIdentifier.java +@@ -0,0 +1,87 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the Key Identifier ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.4 ++ */ ++public class KeyIdentifier implements java.io.Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 2412286879441154979L; ++ private byte[] octetString; ++ ++ /** ++ * Create a KeyIdentifier with the passed bit settings. ++ * ++ * @param octetString the octet string identifying the key identifier. ++ */ ++ public KeyIdentifier(byte[] octetString) { ++ this.octetString = octetString; ++ } ++ ++ /** ++ * Create a KeyIdentifier from the DER encoded value. ++ * ++ * @param val the DerValue ++ */ ++ public KeyIdentifier(DerValue val) throws IOException { ++ octetString = val.getOctetString(); ++ } ++ ++ /** ++ * Return the value of the KeyIdentifier as byte array. ++ */ ++ public byte[] getIdentifier() { ++ return octetString.clone(); ++ } ++ ++ /** ++ * Returns a printable representation of the KeyUsage. ++ */ ++ public String toString() { ++ org.mozilla.jss.netscape.security.util.PrettyPrintFormat pp = ++ new org.mozilla.jss.netscape.security.util.PrettyPrintFormat(" ", 20); ++ String octetbits = pp.toHexString(octetString); ++ ++ String s = "KeyIdentifier [\n"; ++ s += octetbits; ++ s += "]\n"; ++ return (s); ++ } ++ ++ /** ++ * Write the KeyIdentifier to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException ++ */ ++ void encode(DerOutputStream out) throws IOException { ++ out.putOctetString(octetString); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/KeyUsageExtension.java b/org/mozilla/jss/netscape/security/x509/KeyUsageExtension.java +new file mode 100644 +index 00000000..6baf8d55 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/KeyUsageExtension.java +@@ -0,0 +1,420 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the Key Usage Extension. ++ * ++ *

++ * This extension, if present, defines the purpose (e.g., encipherment, signature, certificate signing) of the key ++ * contained in the certificate. The usage restriction might be employed when a multipurpose key is to be restricted ++ * (e.g., when an RSA key should be used only for signing or only for key encipherment). ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.9 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class KeyUsageExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 2899719374157256708L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.KeyUsage"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "KeyUsage"; ++ public static final String DIGITAL_SIGNATURE = "digital_signature"; ++ public static final String NON_REPUDIATION = "non_repudiation"; ++ public static final String KEY_ENCIPHERMENT = "key_encipherment"; ++ public static final String DATA_ENCIPHERMENT = "data_encipherment"; ++ public static final String KEY_AGREEMENT = "key_agreement"; ++ public static final String KEY_CERTSIGN = "key_certsign"; ++ public static final String CRL_SIGN = "crl_sign"; ++ public static final String ENCIPHER_ONLY = "encipher_only"; ++ public static final String DECIPHER_ONLY = "decipher_only"; ++ ++ public static final int DIGITAL_SIGNATURE_BIT = 0; ++ public static final int NON_REPUDIATION_BIT = 1; ++ public static final int KEY_ENCIPHERMENT_BIT = 2; ++ public static final int DATA_ENCIPHERMENT_BIT = 3; ++ public static final int KEY_AGREEMENT_BIT = 4; ++ public static final int KEY_CERTSIGN_BIT = 5; ++ public static final int CRL_SIGN_BIT = 6; ++ public static final int ENCIPHER_ONLY_BIT = 7; ++ public static final int DECIPHER_ONLY_BIT = 8; ++ ++ public static final int NBITS = 9; ++ ++ public static String[] names = new String[NBITS]; ++ ++ static { ++ names[DIGITAL_SIGNATURE_BIT] = DIGITAL_SIGNATURE; ++ names[NON_REPUDIATION_BIT] = NON_REPUDIATION; ++ names[KEY_ENCIPHERMENT_BIT] = KEY_ENCIPHERMENT; ++ names[DATA_ENCIPHERMENT_BIT] = DATA_ENCIPHERMENT; ++ names[KEY_AGREEMENT_BIT] = KEY_AGREEMENT; ++ names[KEY_CERTSIGN_BIT] = KEY_CERTSIGN; ++ names[CRL_SIGN_BIT] = CRL_SIGN; ++ names[ENCIPHER_ONLY_BIT] = ENCIPHER_ONLY; ++ names[DECIPHER_ONLY_BIT] = DECIPHER_ONLY; ++ } ++ ++ // Private data members ++ private boolean[] bitString; ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ try (DerOutputStream os = new DerOutputStream()) { ++ os.putUnalignedBitString(this.bitString); ++ this.extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Check if bit is set. ++ * ++ * @param position the position in the bit string to check. ++ */ ++ private boolean isSet(int position) { ++ if (bitString.length <= position) ++ return false; ++ return bitString[position]; ++ } ++ ++ /** ++ * Set the bit at the specified position. ++ */ ++ private void set(int position, boolean val) { ++ // enlarge bitString if necessary ++ if (position >= bitString.length) { ++ boolean[] tmp = new boolean[position + 1]; ++ System.arraycopy(bitString, 0, tmp, 0, bitString.length); ++ bitString = tmp; ++ } ++ bitString[position] = val; ++ } ++ ++ /** ++ * Create a KeyUsageExtension with the passed bit settings. The criticality ++ * is set to true. ++ * ++ * @param bitString the bits to be set for the extension. ++ */ ++ public KeyUsageExtension(boolean critical, byte[] bitString) throws IOException { ++ this.bitString = ++ new BitArray(bitString.length * 8, bitString).toBooleanArray(); ++ this.extensionId = PKIXExtensions.KeyUsage_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ public KeyUsageExtension(byte[] bitString) throws IOException { ++ this.bitString = ++ new BitArray(bitString.length * 8, bitString).toBooleanArray(); ++ this.extensionId = PKIXExtensions.KeyUsage_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a KeyUsageExtension with the passed bit settings. The criticality ++ * is set to true. ++ * ++ * @param bitString the bits to be set for the extension. ++ */ ++ public KeyUsageExtension(boolean critical, boolean[] bitString) throws IOException { ++ this.bitString = bitString; ++ this.extensionId = PKIXExtensions.KeyUsage_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ public KeyUsageExtension(boolean[] bitString) throws IOException { ++ this.bitString = bitString; ++ this.extensionId = PKIXExtensions.KeyUsage_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a KeyUsageExtension with the passed bit settings. The criticality ++ * is set to true. ++ * ++ * @param bitString the bits to be set for the extension. ++ */ ++ public KeyUsageExtension(BitArray bitString) throws IOException { ++ this.bitString = bitString.toBooleanArray(); ++ this.extensionId = PKIXExtensions.KeyUsage_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value of the same. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public KeyUsageExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.KeyUsage_Id; ++ this.critical = critical.booleanValue(); ++ /* ++ * The following check should be activated again after ++ * the PKIX profiling work becomes standard and the check ++ * is not a barrier to interoperability ! ++ * if (!this.critical) { ++ * throw new IOException("KeyUsageExtension not marked critical," ++ * + " invalid profile."); ++ * } ++ */ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ BitArray bitArray = val.getUnalignedBitString(); ++ if (bitArray == null) { ++ throw new IOException("Invalid bit string"); ++ } ++ this.bitString = bitArray.toBooleanArray(); ++ ++ } ++ ++ /** ++ * Create a default key usage. ++ */ ++ public KeyUsageExtension() { ++ extensionId = PKIXExtensions.KeyUsage_Id; ++ critical = true; ++ bitString = new boolean[0]; ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (!(obj instanceof Boolean)) { ++ throw new IOException("Attribute must be of type Boolean."); ++ } ++ boolean val = ((Boolean) obj).booleanValue(); ++ if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { ++ set(0, val); ++ } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { ++ set(1, val); ++ } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { ++ set(2, val); ++ } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { ++ set(3, val); ++ } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { ++ set(4, val); ++ } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { ++ set(5, val); ++ } else if (name.equalsIgnoreCase(CRL_SIGN)) { ++ set(6, val); ++ } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { ++ set(7, val); ++ } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { ++ set(8, val); ++ } else { ++ throw new IOException("Attribute name not recognized by" ++ + " CertAttrSet:KeyUsage."); ++ } ++ encodeThis(); ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { ++ return Boolean.valueOf(isSet(0)); ++ } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { ++ return Boolean.valueOf(isSet(1)); ++ } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { ++ return Boolean.valueOf(isSet(2)); ++ } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { ++ return Boolean.valueOf(isSet(3)); ++ } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { ++ return Boolean.valueOf(isSet(4)); ++ } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { ++ return Boolean.valueOf(isSet(5)); ++ } else if (name.equalsIgnoreCase(CRL_SIGN)) { ++ return Boolean.valueOf(isSet(6)); ++ } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { ++ return Boolean.valueOf(isSet(7)); ++ } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { ++ return Boolean.valueOf(isSet(8)); ++ } else { ++ throw new IOException("Attribute name not recognized by" ++ + " CertAttrSet:KeyUsage."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { ++ set(0, false); ++ } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { ++ set(1, false); ++ } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { ++ set(2, false); ++ } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { ++ set(3, false); ++ } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { ++ set(4, false); ++ } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { ++ set(5, false); ++ } else if (name.equalsIgnoreCase(CRL_SIGN)) { ++ set(6, false); ++ } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { ++ set(7, false); ++ } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { ++ set(8, false); ++ } else { ++ throw new IOException("Attribute name not recognized by" ++ + " CertAttrSet:KeyUsage."); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the KeyUsage. ++ */ ++ public String toString() { ++ String s = super.toString() + "KeyUsage [\n"; ++ ++ try { ++ if (isSet(0)) { ++ s += " DigitalSignature\n"; ++ } ++ if (isSet(1)) { ++ s += " Non_repudiation\n"; ++ } ++ if (isSet(2)) { ++ s += " Key_Encipherment\n"; ++ } ++ if (isSet(3)) { ++ s += " Data_Encipherment\n"; ++ } ++ if (isSet(4)) { ++ s += " Key_Agreement\n"; ++ } ++ if (isSet(5)) { ++ s += " Key_CertSign\n"; ++ } ++ if (isSet(6)) { ++ s += " Crl_Sign\n"; ++ } ++ if (isSet(7)) { ++ s += " Encipher_Only\n"; ++ } ++ if (isSet(8)) { ++ s += " Decipher_Only\n"; ++ } ++ } catch (ArrayIndexOutOfBoundsException ex) { ++ } ++ ++ s += "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ if (this.extensionValue == null) { ++ this.extensionId = PKIXExtensions.KeyUsage_Id; ++ this.critical = true; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(DIGITAL_SIGNATURE); ++ elements.addElement(NON_REPUDIATION); ++ elements.addElement(KEY_ENCIPHERMENT); ++ elements.addElement(DATA_ENCIPHERMENT); ++ elements.addElement(KEY_AGREEMENT); ++ elements.addElement(KEY_CERTSIGN); ++ elements.addElement(CRL_SIGN); ++ elements.addElement(ENCIPHER_ONLY); ++ elements.addElement(DECIPHER_ONLY); ++ ++ return (elements.elements()); ++ } ++ ++ public boolean[] getBits() { ++ return bitString.clone(); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/LdapDNStrConverter.java b/org/mozilla/jss/netscape/security/x509/LdapDNStrConverter.java +new file mode 100644 +index 00000000..2c2d421c +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/LdapDNStrConverter.java +@@ -0,0 +1,144 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++/** ++ * Abstract class that converts a Ldap DN String to an X500Name, RDN or AVA ++ * and vice versa, except the string is a java string in unicode. ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ */ ++ ++public abstract class LdapDNStrConverter { ++ // ++ // public parsing methods. ++ // ++ ++ /** ++ * Converts a Ldap DN string to a X500Name object. ++ * ++ * @param dn a Ldap DN String. ++ * ++ * @return an X500Name object for the Ldap DN String. ++ */ ++ public abstract X500Name parseDN(String dn) ++ throws IOException; ++ ++ /** ++ * Like parseDN with a specified DER encoding order for Directory Strings. ++ */ ++ public abstract X500Name parseDN(String dn, byte[] tags) ++ throws IOException; ++ ++ /** ++ * Converts a Ldap DN string to a RDN object. ++ * ++ * @param rdn a Ldap DN String ++ * ++ * @return an RDN object. ++ */ ++ public abstract RDN parseRDN(String rdn) ++ throws IOException; ++ ++ /** ++ * Like parseRDN with a specified DER encoding order for Directory Strings. ++ */ ++ public abstract RDN parseRDN(String rdn, byte[] tags) ++ throws IOException; ++ ++ /** ++ * Converts a Ldap DN string to a AVA object. ++ * ++ * @param ava a Ldap DN string. ++ * @return an AVA object. ++ */ ++ public abstract AVA parseAVA(String ava) ++ throws IOException; ++ ++ /** ++ * Like parseAVA with a specified DER encoding order for Directory Strings. ++ */ ++ public abstract AVA parseAVA(String rdn, byte[] tags) ++ throws IOException; ++ ++ // ++ // public encoding methods. ++ // ++ ++ /** ++ * Converts a X500Name object to a Ldap dn string. ++ * ++ * @param dn an X500Name object. ++ * @return a Ldap DN String. ++ */ ++ public abstract String encodeDN(X500Name dn) throws IOException; ++ ++ /** ++ * Converts an RDN object to a Ldap dn string. ++ * ++ * @param rdn an RDN object. ++ * @return a Ldap dn string. ++ */ ++ public abstract String encodeRDN(RDN rdn) throws IOException; ++ ++ /** ++ * Converts an AVA object to a Ldap dn string. ++ * ++ * @param ava An AVA object. ++ * @return A Ldap dn string. ++ */ ++ public abstract String encodeAVA(AVA ava) throws IOException; ++ ++ // ++ // public static methods ++ // ++ ++ /** ++ * Gets a global default Ldap DN String converter. ++ * Currently it is LdapV3DNStrConverter object using the default ++ * X500NameAttrMap and accepts unknown OIDs. ++ * ++ * @see netscape.security.x509.LdapV3DNStrConverter ++ * ++ * @return The global default LdapDNStrConverter instance. ++ */ ++ public static LdapDNStrConverter getDefault() { ++ return defaultConverter; ++ } ++ ++ /** ++ * Set the global default LdapDNStrConverter object. ++ * ++ * @param defConverter A LdapDNStrConverter object to become ++ * the global default. ++ */ ++ public static void setDefault(LdapDNStrConverter defConverter) { ++ if (defConverter == null) ++ throw new IllegalArgumentException( ++ "The default Ldap DN String converter cannot be set to null."); ++ defaultConverter = defConverter; ++ } ++ ++ // ++ // private static variables ++ // ++ ++ private static LdapDNStrConverter defaultConverter = new LdapV3DNStrConverter(); ++} +diff --git a/org/mozilla/jss/netscape/security/x509/LdapV3DNStrConverter.java b/org/mozilla/jss/netscape/security/x509/LdapV3DNStrConverter.java +new file mode 100644 +index 00000000..2e8829b7 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/LdapV3DNStrConverter.java +@@ -0,0 +1,824 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.ByteArrayOutputStream; ++import java.io.CharArrayWriter; ++import java.io.IOException; ++import java.io.PushbackReader; ++import java.io.StringReader; ++import java.io.UnsupportedEncodingException; ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.CharacterCodingException; ++import java.nio.charset.Charset; ++import java.nio.charset.CharsetDecoder; ++import java.nio.charset.UnsupportedCharsetException; ++import java.util.Arrays; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * A converter that converts Ldap v3 DN strings as specified in ++ * draft-ietf-asid-ldapv3-dn-03.txt to a X500Name, RDN or AVA and ++ * vice versa. ++ * ++ * @see LdapDNStrConverter ++ * @see X500Name ++ * @see RDN ++ * @see AVA ++ * @see X500NameAttrMap ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ */ ++ ++public class LdapV3DNStrConverter extends LdapDNStrConverter { ++ // ++ // Constructors ++ // ++ ++ /** ++ * Constructs a LdapV3DNStrConverter using the global default ++ * X500NameAttrMap and accept OIDs not in the default X500NameAttrMap. ++ * ++ * @see X500NameAttrMap ++ */ ++ public LdapV3DNStrConverter() { ++ attrMap = X500NameAttrMap.getDefault(); ++ ++ acceptUnknownOids = true; ++ } ++ ++ /** ++ * Constructs a LdapV3DNStrConverter using the specified X500NameAttrMap ++ * and a boolean indicating whether to accept OIDs not listed in the ++ * X500NameAttrMap. ++ * ++ * @param attributeMap a X500NameAttrMap ++ * @param doAcceptUnknownOids whether to convert unregistered OIDs ++ * (oids not in the X500NameAttrMap) ++ * @see X500NameAttrMap ++ */ ++ public LdapV3DNStrConverter(X500NameAttrMap attributeMap, ++ boolean doAcceptUnknownOids) { ++ attrMap = attributeMap; ++ acceptUnknownOids = doAcceptUnknownOids; ++ ++ } ++ ++ // ++ // public parsing methods ++ // From LdapDNStrConverter interface ++ // ++ ++ /** ++ * Parse a Ldap v3 DN string to a X500Name. ++ * ++ * @param dn a LDAP v3 DN String ++ * @return a X500Name ++ * @exception IOException if an error occurs during the conversion. ++ */ ++ public X500Name parseDN(String dn) ++ throws IOException { ++ return parseDN(dn, null); ++ } ++ ++ /** ++ * Like parseDN(String) with a DER encoding order given as argument for ++ * Directory Strings. ++ */ ++ public X500Name parseDN(String dn, byte[] encodingOrder) ++ throws IOException { ++ StringReader dn_reader = new StringReader(dn); ++ PushbackReader in = new PushbackReader(dn_reader, 5); ++ ++ return parseDN(in, encodingOrder); ++ } ++ ++ /** ++ * Parse a Ldap v3 DN string with a RDN component to a RDN ++ * ++ * @param rdn a LDAP v3 DN String ++ * @return a RDN ++ * @exception IOException if an error occurs during the conversion. ++ */ ++ public RDN parseRDN(String rdn) ++ throws IOException { ++ return parseRDN(rdn, null); ++ } ++ ++ /** ++ * Like parseRDN(String) with a DER encoding order given as argument for ++ * Directory Strings. ++ */ ++ public RDN parseRDN(String rdn, byte[] encodingOrder) ++ throws IOException { ++ StringReader rdn_reader = new StringReader(rdn); ++ PushbackReader in = new PushbackReader(rdn_reader, 5); ++ ++ return parseRDN(in, null); ++ } ++ ++ /** ++ * Parse a Ldap v3 DN string with a AVA component to a AVA. ++ * ++ * @param ava a LDAP v3 DN string ++ * @return a AVA ++ */ ++ public AVA parseAVA(String ava) ++ throws IOException { ++ return parseAVA(ava, null); ++ } ++ ++ /** ++ * Like parseDN(String) with a DER encoding order given as argument for ++ * Directory Strings. ++ */ ++ public AVA parseAVA(String ava, byte[] encodingOrder) ++ throws IOException { ++ StringReader ava_reader = new StringReader(ava); ++ PushbackReader in = new PushbackReader(ava_reader, 5); ++ ++ return parseAVA(in, encodingOrder); ++ } ++ ++ // ++ // public parsing methods called by other methods. ++ // ++ ++ /** ++ * Parses a Ldap DN string in a string reader to a X500Name. ++ * ++ * @param in Pushback string reader for a Ldap DN string. ++ * The pushback reader must have a pushback buffer size > 2. ++ * ++ * @return a X500Name ++ * ++ * @exception IOException if any reading or parsing error occurs. ++ */ ++ public X500Name parseDN(PushbackReader in) ++ throws IOException { ++ return parseDN(in, null); ++ } ++ ++ /** ++ * Like parseDN(PushbackReader in) with a DER encoding order given as ++ * argument for Directory Strings. ++ */ ++ public X500Name parseDN(PushbackReader in, byte[] encodingOrder) ++ throws IOException { ++ RDN rdn; ++ int lastChar; ++ Vector rdnVector = new Vector(); ++ RDN names[]; ++ int i, j; ++ ++ do { ++ rdn = parseRDN(in, encodingOrder); ++ rdnVector.addElement(rdn); ++ lastChar = in.read(); ++ } while (lastChar == ',' || lastChar == ';'); ++ ++ names = new RDN[rdnVector.size()]; ++ for (i = 0, j = rdnVector.size() - 1; i < rdnVector.size(); i++, j--) ++ names[j] = rdnVector.elementAt(i); ++ return new X500Name(names); ++ } ++ ++ /** ++ * Parses Ldap DN string with a rdn component ++ * from a string reader to a RDN. The string reader will point ++ * to the separator after the rdn component or -1 if at end of string. ++ * ++ * @param in Pushback string reader containing a Ldap DN string with ++ * at least one rdn component. ++ * The pushback reader must have a pushback buffer size > 2. ++ * ++ * @return RDN object of the first rdn component in the Ldap DN string. ++ * ++ * @exception IOException if any read or parse error occurs. ++ */ ++ public RDN parseRDN(PushbackReader in) ++ throws IOException { ++ return parseRDN(in, null); ++ } ++ ++ /** ++ * Like parseRDN(PushbackReader) with a DER encoding order given as ++ * argument for Directory Strings. ++ */ ++ public RDN parseRDN(PushbackReader in, byte[] encodingOrder) ++ throws IOException { ++ Vector avaVector = new Vector(); ++ AVA ava; ++ int lastChar; ++ AVA assertion[]; ++ ++ do { ++ ava = parseAVA(in, encodingOrder); ++ avaVector.addElement(ava); ++ lastChar = in.read(); ++ } while (lastChar == '+'); ++ ++ if (lastChar != -1) ++ in.unread(lastChar); ++ ++ assertion = new AVA[avaVector.size()]; ++ for (int i = 0; i < avaVector.size(); i++) ++ assertion[i] = avaVector.elementAt(i); ++ return new RDN(assertion); ++ } ++ ++ /** ++ * Parses a Ldap DN string with a AVA component ++ * from a string reader to an AVA. The string reader will point ++ * to the AVA separator after the ava string or -1 if end of string. ++ * ++ * @param in a Pushback reader containg a Ldap string with ++ * at least one AVA component. ++ * The Pushback reader must have a pushback buffer size > 2. ++ * ++ * @return AVA object of the first AVA component in the Ldap DN string. ++ */ ++ public AVA parseAVA(PushbackReader in) ++ throws IOException { ++ return parseAVA(in, null); ++ } ++ ++ /** ++ * Like parseAVA(PushbackReader) with a DER encoding order given as ++ * argument for Directory Strings. ++ */ ++ public AVA parseAVA(PushbackReader in, byte[] encodingOrder) ++ throws IOException { ++ int c; ++ ObjectIdentifier oid; ++ DerValue value; ++ StringBuffer keywordBuf; ++ StringBuffer valueBuf; ++ ByteArrayOutputStream berStream; ++ char hexChar1, hexChar2; ++ CharArrayWriter hexCharsBuf; ++ String endChars; ++ ++ /* First get the keyword indicating the attribute's type, ++ * and map it to the appropriate OID. ++ */ ++ keywordBuf = new StringBuffer(); ++ for (;;) { ++ c = in.read(); ++ if (c == '=') ++ break; ++ if (c == -1) { ++ throw new IOException("Bad AVA format: Missing '='"); ++ } ++ keywordBuf.append((char) c); ++ } ++ oid = parseAVAKeyword(keywordBuf.toString()); ++ ++ /* Now parse the value. "#hex", a quoted string, or a string ++ * terminated by "+", ",", ";", ">". Whitespace before or after ++ * the value is stripped. ++ */ ++ for (c = in.read(); c == ' '; c = in.read()) ++ continue; ++ if (c == -1) ++ throw new IOException("Bad AVA format: Missing attribute value"); ++ ++ if (c == '#') { ++ /* ++ * NOTE per LDAPv3 dn string ietf standard the value represented ++ * by this form is a BER value. But we only support DER value here ++ * which is only a form of BER. ++ */ ++ berStream = new ByteArrayOutputStream(); ++ int b; ++ for (;;) { ++ hexChar1 = (char) (c = in.read()); ++ if (c == -1 || octoEndChars.indexOf(c) > 0) // end of value ++ break; ++ hexChar2 = (char) (c = in.read()); ++ if (hexDigits.indexOf(hexChar1) == -1 || ++ hexDigits.indexOf(hexChar2) == -1) ++ throw new IOException("Bad AVA value: bad hex value."); ++ b = (Character.digit(hexChar1, 16) << 4) + ++ Character.digit(hexChar2, 16); ++ berStream.write(b); ++ } ++ if (berStream.size() == 0) ++ throw new IOException("bad AVA format: invalid hex value"); ++ ++ value = parseAVAValue(berStream.toByteArray(), oid); ++ ++ while (c == ' ' && c != -1) ++ c = in.read(); ++ } else { ++ valueBuf = new StringBuffer(); ++ boolean quoted = false; ++ if (c == '"') { ++ quoted = true; ++ endChars = quotedEndChars; ++ if ((c = in.read()) == -1) ++ throw new IOException("Bad AVA format: Missing attrValue"); ++ } else { ++ endChars = valueEndChars; ++ } ++ ++ // QUOTATION * ( quotechar / pair ) QUOTATION ++ // quotechar = any character except '\' or QUOTATION ++ // pair = '\' ( special | '\' | QUOTATION | hexpair ) ++ while (c != -1 && endChars.indexOf(c) == -1) { ++ if (c == '\\') { ++ if ((c = in.read()) == -1) ++ throw new IOException("Bad AVA format: expecting " + ++ "escaped char."); ++ // expect escaping of special chars, space and CR. ++ if (specialChars.indexOf((char) c) != -1 || c == '\n' || ++ c == '\\' || c == '"' || c == ' ') { ++ valueBuf.append((char) c); ++ } else if (hexDigits.indexOf(c) != -1) { ++ hexCharsBuf = new CharArrayWriter(); ++ // handle sequence of '\' hexpair ++ do { ++ hexChar1 = (char) c; ++ hexChar2 = (char) (c = in.read()); ++ if (hexDigits.indexOf((char) c) == -1) ++ throw new IOException("Bad AVA format: " + ++ "invalid escaped hex pair"); ++ hexCharsBuf.write(hexChar1); ++ hexCharsBuf.write(hexChar2); ++ // read ahead to next '\' hex-char if any. ++ if ((c = in.read()) == -1) ++ break; ++ if (c != '\\') { ++ in.unread(c); ++ break; ++ } ++ if ((c = in.read()) == -1) ++ throw new IOException("Bad AVA format: " + ++ "expecting escaped char."); ++ if (hexDigits.indexOf((char) c) == -1) { ++ in.unread(c); ++ in.unread('\\'); ++ break; ++ } ++ } while (true); ++ valueBuf.append( ++ getStringFromHexpairs(hexCharsBuf.toCharArray())); ++ } else { ++ throw new IOException("Bad AVA format: " + ++ "invalid escaping"); ++ } ++ } else ++ valueBuf.append((char) c); ++ c = in.read(); ++ } ++ ++ value = parseAVAValue( ++ valueBuf.toString().trim(), oid, encodingOrder); ++ ++ if (quoted) { // move to next non-white space ++ do { ++ c = in.read(); ++ } while (c == ' '); ++ if (c != -1 && valueEndChars.indexOf(c) == -1) ++ throw new IOException( ++ "Bad AVA format: separator expected at end of ava."); ++ } ++ } ++ ++ if (c != -1) ++ in.unread(c); ++ ++ return new AVA(oid, value); ++ } ++ ++ /** ++ * Converts a AVA keyword from a Ldap DN string to an ObjectIdentifier ++ * from the attribute map or, if this keyword is an OID not ++ * in the attribute map, create a new ObjectIdentifier for the keyword ++ * if acceptUnknownOids is true. ++ * ++ * @param avaKeyword AVA keyword from a Ldap DN string. ++ * ++ * @return a ObjectIdentifier object ++ * @exception IOException if the keyword is an OID not in the attribute ++ * map and acceptUnknownOids is false, or ++ * if an error occurs during conversion. ++ */ ++ public ObjectIdentifier parseAVAKeyword(String avaKeyword) ++ throws IOException { ++ String keyword = avaKeyword.toUpperCase().trim(); ++ String oid_str = null; ++ ObjectIdentifier oid, new_oid; ++ ++ if (Character.digit(keyword.charAt(0), 10) != -1) { ++ // value is an oid string of 1.2.3.4 ++ oid_str = keyword; ++ } else if (keyword.startsWith("oid.") || keyword.startsWith("OID.")) { ++ // value is an oid string of oid.1.2.3.4 or OID.1.2... ++ oid_str = keyword.substring(4); ++ } ++ ++ if (oid_str != null) { ++ // value is an oid string of 1.2.3.4 or oid.1.2.3.4 or OID.1.2... ++ new_oid = new ObjectIdentifier(oid_str); ++ oid = attrMap.getOid(new_oid); ++ if (oid == null) { ++ if (!acceptUnknownOids) ++ throw new IOException("Unknown AVA OID."); ++ oid = new_oid; ++ } ++ } else { ++ oid = attrMap.getOid(keyword); ++ if (oid == null) ++ throw new IOException("Unknown AVA keyword '" + keyword + "'."); ++ } ++ ++ return oid; ++ } ++ ++ /** ++ * Converts a AVA value from a Ldap dn string to a ++ * DerValue according the attribute type. For example, a value for ++ * CN, OU or O is expected to be a Directory String and will be converted ++ * to a DerValue of ASN.1 type PrintableString, T61String or ++ * UniversalString. A Directory String is a ASN.1 CHOICE of Printable, ++ * T.61 or Universal string. ++ * ++ * @param avaValueString a attribute value from a Ldap DN string. ++ * @param oid OID of the attribute. ++ * ++ * @return DerValue for the value. ++ * ++ * @exception IOException if an error occurs during conversion. ++ * @see AVAValueConverter ++ */ ++ public DerValue parseAVAValue(String avaValueString, ObjectIdentifier oid) ++ throws IOException { ++ return parseAVAValue(avaValueString, oid, null); ++ } ++ ++ /** ++ * Like parseAVAValue(String) with a DER encoding order given as argument ++ * for Directory Strings. ++ */ ++ public DerValue parseAVAValue( ++ String avaValueString, ObjectIdentifier oid, byte[] encodingOrder) ++ throws IOException { ++ AVAValueConverter valueConverter = attrMap.getValueConverter(oid); ++ if (valueConverter == null) { ++ if (!acceptUnknownOids) { ++ throw new IllegalArgumentException( ++ "Unrecognized OID for AVA value conversion"); ++ } else { ++ valueConverter = new GenericValueConverter(); ++ } ++ } ++ return valueConverter.getValue(avaValueString, encodingOrder); ++ } ++ ++ /** ++ * Converts a value in BER encoding, for example given in octothorpe form ++ * in a Ldap v3 dn string, to a DerValue. Checks if the BER encoded value ++ * is a legal value for the attribute. ++ *

++ * NOTE: only DER encoded values are supported for the BER encoded value. ++ * ++ * @param berValue a value in BER encoding ++ * @param oid ObjectIdentifier of the attribute. ++ * ++ * @return DerValue for the BER encoded value ++ * @exception IOException if an error occurs during conversion. ++ */ ++ public DerValue parseAVAValue(byte[] berValue, ObjectIdentifier oid) ++ throws IOException { ++ AVAValueConverter valueConverter = attrMap.getValueConverter(oid); ++ if (valueConverter == null && !acceptUnknownOids) { ++ throw new IllegalArgumentException( ++ "Unrecognized OID for AVA value conversion"); ++ } else { ++ valueConverter = new GenericValueConverter(); ++ } ++ return valueConverter.getValue(berValue); ++ } ++ ++ // ++ // public encoding methods. ++ // ++ ++ /** ++ * Converts a X500Name object to a Ldap v3 DN string (except in unicode). ++ * ++ * @param x500name a X500Name ++ * ++ * @return a Ldap v3 DN String (except in unicode). ++ * ++ * @exception IOException if an error is encountered during conversion. ++ */ ++ public String encodeDN(X500Name x500name) ++ throws IOException { ++ RDN[] rdns = x500name.getNames(); ++ // String fullname = null; ++ StringBuffer fullname = new StringBuffer(); ++ String s; ++ int i; ++ if (rdns.length == 0) ++ return ""; ++ i = rdns.length - 1; ++ fullname.append(encodeRDN(rdns[i--])); ++ while (i >= 0) { ++ s = encodeRDN(rdns[i--]); ++ fullname.append(","); ++ fullname.append(s); ++ } ++ ; ++ return fullname.toString(); ++ } ++ ++ /** ++ * Converts a RDN to a Ldap v3 DN string (except in unicode). ++ * ++ * @param rdn a RDN ++ * ++ * @return a LDAP v3 DN string (except in unicode). ++ * ++ * @exception IOException if an error is encountered during conversion. ++ */ ++ public String encodeRDN(RDN rdn) ++ throws IOException { ++ AVA[] avas = rdn.getAssertion(); ++ // String relname = null; ++ StringBuffer relname = new StringBuffer(); ++ String s; ++ int i = 0; ++ ++ relname.append(encodeAVA(avas[i++])); ++ while (i < avas.length) { ++ s = encodeAVA(avas[i++]); ++ relname.append("+"); ++ relname.append(s); ++ } ++ ; ++ return relname.toString(); ++ } ++ ++ /** ++ * Converts a AVA to a Ldap v3 DN String (except in unicode). ++ * ++ * @param ava an AVA ++ * ++ * @return a Ldap v3 DN string (except in unicode). ++ * ++ * @exception IOException If an error is encountered during exception. ++ */ ++ public String encodeAVA(AVA ava) ++ throws IOException { ++ if (ava == null) { ++ return ""; ++ } ++ ObjectIdentifier oid = ava.getOid(); ++ DerValue value = ava.getValue(); ++ String keyword, valueStr; ++ ++ // get attribute name ++ ++ keyword = encodeOID(oid); ++ valueStr = encodeValue(value, oid); ++ ++ return keyword + "=" + valueStr; ++ } ++ ++ /** ++ * Converts an OID to a attribute keyword in a Ldap v3 DN string ++ * - either a keyword if known or a string of "1.2.3.4" syntax. ++ * ++ * @param oid a ObjectIdentifier ++ * ++ * @return a keyword to use in a Ldap V3 DN string. ++ * ++ * @exception IOException if an error is encountered during conversion. ++ */ ++ public String encodeOID(ObjectIdentifier oid) ++ throws IOException { ++ String keyword = attrMap.getName(oid); ++ if (keyword == null) { ++ if (acceptUnknownOids) ++ keyword = oid.toString(); ++ else ++ throw new IOException("Unknown OID"); ++ } ++ return keyword; ++ } ++ ++ /** ++ * Converts a value as a DerValue to a string in a Ldap V3 DN String. ++ * If the value cannot be converted to a string it will be encoded in ++ * octothorpe form. ++ * ++ * @param attrValue a value as a DerValue. ++ * @param oid OID for the attribute. ++ * @return a string for the value in a LDAP v3 DN String ++ * @exception IOException if an error occurs during conversion. ++ */ ++ public String encodeValue(DerValue attrValue, ObjectIdentifier oid) ++ throws IOException { ++ /* ++ * Construct the value with as little copying and garbage ++ * production as practical. ++ */ ++ StringBuffer retval = new StringBuffer(30); ++ int i; ++ String temp = null; ++ AVAValueConverter valueConverter; ++ ++ X500NameAttrMap lAttrMap = attrMap; ++ ++ if (attrValue.tag == DerValue.tag_UTF8String) { ++ lAttrMap = X500NameAttrMap.getDirDefault(); ++ ++ } ++ ++ valueConverter = lAttrMap.getValueConverter(oid); ++ if (valueConverter == null) { ++ if (acceptUnknownOids) ++ valueConverter = new GenericValueConverter(); ++ else ++ throw new IOException( ++ "Unknown AVA type for encoding AVA value"); ++ } ++ ++ try { ++ temp = valueConverter.getAsString(attrValue); ++ ++ if (temp == null) { ++ // convert to octothorpe form. ++ byte data[] = attrValue.toByteArray(); ++ ++ retval.append('#'); ++ for (i = 0; i < data.length; i++) { ++ retval.append(hexDigits.charAt((data[i] >> 4) & 0x0f)); ++ retval.append(hexDigits.charAt(data[i] & 0x0f)); ++ } ++ ++ } else { ++ ++ retval.append(encodeString(temp)); ++ ++ } ++ } catch (IOException e) { ++ throw new IllegalArgumentException("malformed AVA DER Value"); ++ } ++ ++ return retval.toString(); ++ } ++ ++ /** ++ * converts a raw value string to a string in Ldap V3 DN string format. ++ * ++ * @param valueStr a 'raw' value string. ++ * @return a attribute value string in Ldap V3 DN string format. ++ */ ++ public String encodeString(String valueStr) { ++ int i, j; ++ int len; ++ StringBuffer retval = new StringBuffer(); ++ ++ /* ++ * generate string according to ldapv3 DN. escaping is used. ++ * Strings generated this way are acceptable by rfc1779 ++ * implementations. ++ */ ++ len = valueStr.length(); ++ ++ // get index of first space at the end of the string. ++ for (j = len - 1; j >= 0 && valueStr.charAt(j) == ' '; j--) ++ continue; ++ ++ // escape spaces at the beginning of the string. ++ for (i = 0; i <= j && valueStr.charAt(i) == ' '; i++) { ++ retval.append('\\'); ++ retval.append(valueStr.charAt(i)); ++ } ++ ++ // escape special characters in the middle of the string. ++ for (; i <= j; i++) { ++ if (valueStr.charAt(i) == '\\') { ++ retval.append('\\'); ++ retval.append(valueStr.charAt(i)); ++ } else if (specialChars.indexOf(valueStr.charAt(i)) != -1) { ++ retval.append('\\'); ++ retval.append(valueStr.charAt(i)); ++ } else if (valueStr.charAt(i) == '"') { ++ retval.append('\\'); ++ retval.append(valueStr.charAt(i)); ++ } else ++ retval.append(valueStr.charAt(i)); ++ } ++ ++ // esacape spaces at the end. ++ for (; i < valueStr.length(); i++) { ++ retval.append('\\'); ++ retval.append(' '); ++ } ++ ++ return retval.toString(); ++ } ++ ++ // ++ // public get/set methods ++ // ++ ++ /** ++ * gets the X500NameAttrMap used by the converter. ++ * ++ * @return X500NameAttrMap used by this converter. ++ */ ++ public X500NameAttrMap getAttrMap() { ++ return attrMap; ++ } ++ ++ /** ++ * returns true if the converter accepts unregistered attributes i.e. ++ * OIDS not in the X500NameAttrMap. ++ * ++ * @return true if converter converts attributes not in the ++ * X500NameAttrMap. ++ */ ++ public boolean getAcceptUnknownOids() { ++ return acceptUnknownOids; ++ } ++ ++ // ++ // private and protected variables ++ // ++ ++ protected X500NameAttrMap attrMap; ++ protected boolean acceptUnknownOids; ++ ++ // ++ // private and protected static variables & methods. ++ // ++ ++ protected static final String specialChars = ",+=<>#;"; ++ ++ protected static final String valueEndChars = "+,;>"; ++ protected static final String quotedEndChars = "\""; ++ protected static final String octoEndChars = " " + valueEndChars; ++ ++ /* ++ * Values that aren't printable strings are emitted as BER-encoded ++ * hex data. ++ */ ++ protected static final String hexDigits = "0123456789ABCDEFabcdef"; ++ ++ /** ++ * Parse a sequence of hex pairs, each pair a UTF8 byte to a java string. ++ * For example, "4C75C48D" is "Luc", the last c with caron. ++ */ ++ protected static char[] getStringFromHexpairs(char[] hexPairs) throws UnsupportedEncodingException { ++ try { ++ byte[] buffer = new byte[hexPairs.length / 2]; ++ ++ for (int i = 0; i < buffer.length; i++) { ++ buffer[i] = (byte) ++ ((Character.digit(hexPairs[i * 2], 16) << 4) + ++ Character.digit(hexPairs[i * 2 + 1], 16)); ++ } ++ ++ Charset charset = Charset.forName("UTF-8"); ++ CharsetDecoder decoder = charset.newDecoder(); ++ ++ CharBuffer charBuffer = decoder.decode(ByteBuffer.wrap(buffer)); ++ ++ return Arrays.copyOfRange(charBuffer.array(), ++ charBuffer.arrayOffset(), charBuffer.arrayOffset() + charBuffer.limit()); ++ ++ } catch (UnsupportedCharsetException e) { ++ throw new UnsupportedEncodingException( ++ "No UTF8 byte to char converter to use for " + ++ "parsing LDAP DN String"); ++ ++ } catch (CharacterCodingException e) { ++ throw new IllegalArgumentException( ++ "Invalid hex pair in LDAP DN String."); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/Makefile b/org/mozilla/jss/netscape/security/x509/Makefile +new file mode 100644 +index 00000000..8fd7054b +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/Makefile +@@ -0,0 +1,45 @@ ++#! gmake ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### +diff --git a/org/mozilla/jss/netscape/security/x509/NSCCommentExtension.java b/org/mozilla/jss/netscape/security/x509/NSCCommentExtension.java +new file mode 100644 +index 00000000..57fa8550 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/NSCCommentExtension.java +@@ -0,0 +1,229 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++import org.mozilla.jss.netscape.security.util.PrettyPrintFormat; ++ ++/** ++ * This class defines the NSCCommentExtension ++ * ++ * @author asondhi ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class NSCCommentExtension extends Extension implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 4066287070285105375L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.CommentExtension"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "NSCCommentExtension"; ++ public static final String INFOS = "infos"; ++ public static final ObjectIdentifier OID = ++ new ObjectIdentifier("2.16.840.1.113730.1.13"); ++ public String mComment = null; ++ ++ // Private data members ++ private Vector mInfos; ++ ++ private transient PrettyPrintFormat pp = new PrettyPrintFormat(":"); ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ try (DerOutputStream os = new DerOutputStream()) { ++ os.putIA5String(mComment); ++ // DerOutputStream tmp = new DerOutputStream(); ++ // os.write(DerValue.tag_Sequence,tmp); ++ extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Create a NSCCommentExtension with the Vector of CertificatePolicyInfo. ++ * ++ * @param infos the Vector of CertificatePolicyInfo. ++ */ ++ public NSCCommentExtension(boolean critical, String comment) throws IOException { ++ this.mComment = comment; ++ this.extensionId = new ObjectIdentifier("2.16.840.1.113730.1.13"); ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a default NSCCommentExtension. ++ */ ++ public NSCCommentExtension(boolean critical) { ++ this.extensionId = new ObjectIdentifier("2.16.840.1.113730.1.13"); ++ this.critical = critical; ++ mInfos = new Vector(1, 1); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public NSCCommentExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = new ObjectIdentifier("2.16.840.1.113730.1.13"); ++ ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ ++ mComment = val.getIA5String(); ++ } ++ ++ /** ++ * Returns a printable representation of the policy extension. ++ */ ++ public String toString() { ++ if (mInfos == null) ++ return ""; ++ String s = super.toString() + "Netscape Comment [\n" ++ + mInfos.toString() + "]\n"; ++ ++ return (s); ++ } ++ ++ public String toPrint(int indent) { ++ String s; ++ s = "Comment :\n" + pp.indent(indent + 4) + ++ ((mComment == null) ? "" : mComment.trim()) + "\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = new ObjectIdentifier("2.16.840.1.113730.1.13"); ++ critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ public String getComment() { ++ return mComment; ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ @SuppressWarnings("unchecked") ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(INFOS)) { ++ if (!(obj instanceof Vector)) { ++ throw new IOException("Attribute value should be of" + ++ " type Vector."); ++ } ++ mInfos = (Vector) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:NSCCommentExtension."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(INFOS)) { ++ return (mInfos); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:NSCCommentExtension."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(INFOS)) { ++ mInfos = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:NSCCommentExtension."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(INFOS); ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/NameConstraintsExtension.java b/org/mozilla/jss/netscape/security/x509/NameConstraintsExtension.java +new file mode 100644 +index 00000000..7712249a +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/NameConstraintsExtension.java +@@ -0,0 +1,315 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.PrettyPrintFormat; ++ ++/** ++ * This class defines the Name Constraints Extension. ++ *

++ * The name constraints extension provides permitted and excluded subtrees that place restrictions on names that may be ++ * included within a certificate issued by a given CA. Restrictions may apply to the subject distinguished name or ++ * subject alternative names. Any name matching a restriction in the excluded subtrees field is invalid regardless of ++ * information appearing in the permitted subtrees. ++ *

++ * The ASN.1 syntax for this is: ++ * ++ *

++ * NameConstraints ::= SEQUENCE {
++ *    permittedSubtrees [0]  GeneralSubtrees OPTIONAL,
++ *    excludedSubtrees  [1]  GeneralSubtrees OPTIONAL
++ * }
++ * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
++ * GeneralSubtree ::== SEQUENCE {
++ *    base                   GeneralName,
++ *    minimum           [0]  BaseDistance DEFAULT 0,
++ *    maximum           [1]  BaseDistance OPTIONAL }
++ * BaseDistance ::== INTEGER (0..MAX)
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.10 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class NameConstraintsExtension extends Extension implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -3506940192931244539L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.NameConstraints"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "NameConstraints"; ++ public static final String PERMITTED_SUBTREES = "permitted_subtrees"; ++ public static final String EXCLUDED_SUBTREES = "excluded_subtrees"; ++ ++ // Private data members ++ private static final byte TAG_PERMITTED = 0; ++ private static final byte TAG_EXCLUDED = 1; ++ ++ private GeneralSubtrees permitted; ++ private GeneralSubtrees excluded; ++ ++ private transient PrettyPrintFormat pp = new PrettyPrintFormat(":"); ++ ++ // Encode this extension value. ++ private void encodeThis() throws IOException { ++ try (DerOutputStream seq = new DerOutputStream()) { ++ ++ DerOutputStream tagged = new DerOutputStream(); ++ if ((permitted != null) && (permitted.getSubtrees().size() > 0)) { ++ DerOutputStream tmp = new DerOutputStream(); ++ permitted.encode(tmp); ++ tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, TAG_PERMITTED), tmp); ++ } ++ if ((excluded != null) && (excluded.getSubtrees().size() > 0)) { ++ DerOutputStream tmp = new DerOutputStream(); ++ excluded.encode(tmp); ++ tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, TAG_EXCLUDED), tmp); ++ } ++ if (permitted == null && excluded == null) { ++ extensionValue = null; // no need to encode this extension ++ } else { ++ seq.write(DerValue.tag_Sequence, tagged); ++ this.extensionValue = seq.toByteArray(); ++ } ++ } ++ } ++ ++ /** ++ * The default constructor for this class. Either parameter ++ * can be set to null to indicate it is omitted but both ++ * cannot be null. ++ * ++ * @param permitted the permitted GeneralSubtrees (null for optional). ++ * @param excluded the excluded GeneralSubtrees (null for optional). ++ */ ++ public NameConstraintsExtension(GeneralSubtrees permitted, ++ GeneralSubtrees excluded) ++ throws IOException { ++ init(false, permitted, excluded); ++ } ++ ++ public NameConstraintsExtension(boolean critical, ++ GeneralSubtrees permitted, GeneralSubtrees excluded) ++ throws IOException { ++ init(critical, permitted, excluded); ++ } ++ ++ private void init(boolean critical, ++ GeneralSubtrees permitted, GeneralSubtrees excluded) ++ throws IOException { ++ if (permitted == null && excluded == null) { ++ throw new IOException("NameConstraints: Invalid arguments"); ++ } ++ this.permitted = permitted; ++ this.excluded = excluded; ++ ++ this.extensionId = PKIXExtensions.NameConstraints_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public NameConstraintsExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.NameConstraints_Id; ++ this.critical = critical.booleanValue(); ++ ++ if (!(value instanceof byte[])) ++ throw new IOException("Illegal argument type"); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ System.arraycopy(value, 0, extValue, 0, len); ++ ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for" + ++ " NameConstraintsExtension."); ++ } ++ ++ // NB. this is always encoded with the IMPLICIT tag ++ // The checks only make sense if we assume implicit tagging, ++ // with explicit tagging the form is always constructed. ++ while (val.data.available() != 0) { ++ DerValue opt = val.data.getDerValue(); ++ ++ if (opt.isContextSpecific(TAG_PERMITTED) && opt.isConstructed()) { ++ if (permitted != null) { ++ throw new IOException("Duplicate permitted " + ++ "GeneralSubtrees in NameConstraintsExtension."); ++ } ++ opt.resetTag(DerValue.tag_Sequence); ++ permitted = new GeneralSubtrees(opt); ++ ++ } else if (opt.isContextSpecific(TAG_EXCLUDED) && ++ opt.isConstructed()) { ++ if (excluded != null) { ++ throw new IOException("Duplicate excluded " + ++ "GeneralSubtrees in NameConstraintsExtension."); ++ } ++ opt.resetTag(DerValue.tag_Sequence); ++ excluded = new GeneralSubtrees(opt); ++ } else ++ throw new IOException("Invalid encoding of " + ++ "NameConstraintsExtension."); ++ } ++ } ++ ++ /** ++ * Return the printable string. ++ */ ++ public String toString() { ++ return (super.toString() + "NameConstraints: [" + ++ ((permitted == null) ? "" : ++ ("\n Permitted:" + permitted.toString())) + ++ ((excluded == null) ? "" : ++ ("\n Excluded:" + excluded.toString())) + " ]\n"); ++ } ++ ++ public String toPrint(int indent) { ++ return ("GeneralSubtrees: " + ++ ((permitted == null) ? "" : ++ ("\n" + pp.indent(indent + 2) + "Permitted:" + permitted.toPrint(indent + 4))) + ++ ((excluded == null) ? "" : ++ ("\n" + pp.indent(indent + 2) + "Excluded:" + excluded.toPrint(indent + 4))) + "\n"); ++ ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (this.extensionValue == null) { ++ this.extensionId = PKIXExtensions.NameConstraints_Id; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { ++ if (!(obj instanceof GeneralSubtrees)) { ++ throw new IOException("Attribute value should be" ++ + " of type GeneralSubtrees."); ++ } ++ permitted = (GeneralSubtrees) obj; ++ } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { ++ if (!(obj instanceof GeneralSubtrees)) { ++ throw new IOException("Attribute value should be " ++ + "of type GeneralSubtrees."); ++ } ++ excluded = (GeneralSubtrees) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:NameConstraintsExtension."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { ++ return (permitted); ++ } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { ++ return (excluded); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:NameConstraintsExtension."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { ++ permitted = null; ++ } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { ++ excluded = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:NameConstraintsExtension."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(PERMITTED_SUBTREES); ++ elements.addElement(EXCLUDED_SUBTREES); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/NoticeReference.java b/org/mozilla/jss/netscape/security/x509/NoticeReference.java +new file mode 100644 +index 00000000..1855ee31 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/NoticeReference.java +@@ -0,0 +1,96 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.Serializable; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the NoticeReference. ++ * ++ * NoticeReference ::= SEQUENCE { ++ * organization DisplayText, ++ * noticeNumbers SEQUENCE OF INTEGER ++ * } ++ * ++ * @author Thomas Kwan ++ */ ++public class NoticeReference implements Serializable { ++ ++ private static final long serialVersionUID = 1986080941078808200L; ++ private DisplayText mOrg = null; ++ private int mNumbers[] = null; ++ ++ public NoticeReference(DisplayText org, int numbers[]) { ++ mOrg = org; ++ mNumbers = numbers; ++ } ++ ++ public NoticeReference(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for NoticeReference"); ++ } ++ mOrg = new DisplayText(val.data.getDerValue()); ++ DerValue integers = val.data.getDerValue(); ++ if (integers.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for NoticeReference (integers)"); ++ } ++ Vector num = new Vector(); ++ while (integers.data.available() != 0) { ++ DerValue i = integers.data.getDerValue(); ++ BigInt bigI = i.getInteger(); ++ num.addElement(bigI); ++ } ++ if (num.size() <= 0) ++ return; ++ mNumbers = new int[num.size()]; ++ for (int i = 0; i < num.size(); i++) { ++ mNumbers[i] = num.elementAt(i).toInt(); ++ } ++ } ++ ++ public DisplayText getOrganization() { ++ return mOrg; ++ } ++ ++ public int[] getNumbers() { ++ return mNumbers; ++ } ++ ++ /** ++ * Write the NoticeReference to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ mOrg.encode(tmp); ++ DerOutputStream iseq = new DerOutputStream(); ++ for (int i = 0; i < mNumbers.length; i++) { ++ iseq.putInteger(new BigInt(mNumbers[i])); ++ } ++ tmp.write(DerValue.tag_Sequence, iseq); ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/OIDMap.java b/org/mozilla/jss/netscape/security/x509/OIDMap.java +new file mode 100644 +index 00000000..750bc80a +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/OIDMap.java +@@ -0,0 +1,337 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.File; ++import java.io.FileInputStream; ++import java.io.IOException; ++import java.security.cert.CertificateException; ++import java.util.Hashtable; ++import java.util.Iterator; ++import java.util.Properties; ++ ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * This class defines the mapping from OID & name to classes and vice ++ * versa. Used by CertificateExtensions & PKCS10 to get the java ++ * classes associated with a particular OID/name. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.12 ++ */ ++public class OIDMap { ++ ++ /** ++ * Location for where the OID/Classes maps are stored on ++ * the local system. ++ */ ++ public static final String EXTENSIONS_HOME = ++ (System.getProperty("java.home") + File.separator + "lib" ++ + File.separator + "security" + File.separator + "cert" ++ + File.separator); ++ /** ++ * File names for where OIDs and Classes are registered ++ * for V3 extensions. ++ */ ++ public static final String EXTENSIONS_OIDS = "x509extensions.oid"; ++ public static final String EXTENSIONS_CLASSES = "x509extensions.classes"; ++ ++ // Make default names easier ++ private static final String ROOT = X509CertImpl.NAME + "." + ++ X509CertInfo.NAME + "." + ++ X509CertInfo.EXTENSIONS; ++ private static final String AUTH_KEY_IDENTIFIER = ROOT + "." + ++ AuthorityKeyIdentifierExtension.NAME; ++ private static final String SUB_KEY_IDENTIFIER = ROOT + "." + ++ SubjectKeyIdentifierExtension.NAME; ++ private static final String KEY_USAGE = ROOT + "." + ++ KeyUsageExtension.NAME; ++ private static final String PRIVATE_KEY_USAGE = ROOT + "." + ++ PrivateKeyUsageExtension.NAME; ++ private static final String POLICY_MAPPINGS = ROOT + "." + ++ PolicyMappingsExtension.NAME; ++ private static final String SUB_ALT_NAME = ROOT + "." + ++ SubjectAlternativeNameExtension.NAME; ++ private static final String ISSUER_ALT_NAME = ROOT + "." + ++ IssuerAlternativeNameExtension.NAME; ++ private static final String BASIC_CONSTRAINTS = ROOT + "." + ++ BasicConstraintsExtension.NAME; ++ private static final String NAME_CONSTRAINTS = ROOT + "." + ++ NameConstraintsExtension.NAME; ++ private static final String POLICY_CONSTRAINTS = ROOT + "." + ++ PolicyConstraintsExtension.NAME; ++ private static final String CERT_POLICIES = //ROOT + "." + ++ CertificatePoliciesExtension.NAME; ++ private static final String SUBJ_DIR_ATTR = //ROOT + "." + ++ SubjectDirAttributesExtension.NAME; ++ public static final String EXT_KEY_USAGE_NAME = "ExtendedKeyUsageExtension"; ++ public static final String EXT_INHIBIT_ANY_POLICY_NAME = "InhibitAnyPolicyExtension"; ++ private static final String EXT_KEY_USAGE = //ROOT + "." + ++ EXT_KEY_USAGE_NAME; ++ ++ private static final String CRL_NUMBER = ROOT + "." + ++ CRLNumberExtension.NAME; ++ private static final String CRL_REASON = ROOT + "." + ++ CRLReasonExtension.NAME; ++ ++ private static final Hashtable oid2Name = new Hashtable(); ++ private static final Hashtable name2OID = new Hashtable(); ++ private static final Hashtable name2Class = new Hashtable(); ++ ++ // Initialize recognized extensions from EXTENSIONS_{OIDS/CLASSES} files ++ static { ++ loadNames(); ++ loadClasses(); ++ addClass(CRLDistributionPointsExtension.class); ++ } ++ ++ // Load the default name to oid map (EXTENSIONS_OIDS) ++ private static void loadNamesDefault(Properties props) { ++ props.put(SUB_KEY_IDENTIFIER, "2.5.29.14"); ++ props.put(KEY_USAGE, "2.5.29.15"); ++ props.put(PRIVATE_KEY_USAGE, "2.5.29.16"); ++ props.put(SUB_ALT_NAME, "2.5.29.17"); ++ props.put(ISSUER_ALT_NAME, "2.5.29.18"); ++ props.put(BASIC_CONSTRAINTS, "2.5.29.19"); ++ props.put(CRL_NUMBER, "2.5.29.20"); ++ props.put(CRL_REASON, "2.5.29.21"); ++ props.put(NAME_CONSTRAINTS, "2.5.29.30"); ++ props.put(POLICY_MAPPINGS, "2.5.29.33"); ++ props.put(POLICY_CONSTRAINTS, "2.5.29.36"); ++ props.put(CERT_POLICIES, "2.5.29.32"); ++ props.put(AUTH_KEY_IDENTIFIER, "2.5.29.35"); ++ props.put(SUBJ_DIR_ATTR, "2.5.29.9"); ++ props.put(EXT_KEY_USAGE, "2.5.29.37"); ++ } ++ ++ // Load the default name to class map (EXTENSIONS_CLASSES) ++ private static void loadClassDefault(Properties props) { ++ props.put(AUTH_KEY_IDENTIFIER, ++ "org.mozilla.jss.netscape.security.x509.AuthorityKeyIdentifierExtension"); ++ props.put(SUB_KEY_IDENTIFIER, ++ "org.mozilla.jss.netscape.security.x509.SubjectKeyIdentifierExtension"); ++ props.put(KEY_USAGE, ++ "org.mozilla.jss.netscape.security.x509.KeyUsageExtension"); ++ props.put(PRIVATE_KEY_USAGE, ++ "org.mozilla.jss.netscape.security.x509.PrivateKeyUsageExtension"); ++ props.put(POLICY_MAPPINGS, ++ "org.mozilla.jss.netscape.security.x509.PolicyMappingsExtension"); ++ props.put(SUB_ALT_NAME, ++ "org.mozilla.jss.netscape.security.x509.SubjectAlternativeNameExtension"); ++ props.put(ISSUER_ALT_NAME, ++ "org.mozilla.jss.netscape.security.x509.IssuerAlternativeNameExtension"); ++ props.put(BASIC_CONSTRAINTS, ++ "org.mozilla.jss.netscape.security.x509.BasicConstraintsExtension"); ++ props.put(NAME_CONSTRAINTS, ++ "org.mozilla.jss.netscape.security.x509.NameConstraintsExtension"); ++ props.put(POLICY_CONSTRAINTS, ++ "org.mozilla.jss.netscape.security.x509.PolicyConstraintsExtension"); ++ props.put(CERT_POLICIES, ++ "org.mozilla.jss.netscape.security.x509.CertificatePoliciesExtension"); ++ props.put(SUBJ_DIR_ATTR, ++ "org.mozilla.jss.netscape.security.x509.SubjectDirAttributesExtension"); ++ props.put(EXT_KEY_USAGE, ++ "org.mozilla.jss.netscape.security.extensions.ExtendedKeyUsageExtension"); ++ props.put(CRL_NUMBER, "org.mozilla.jss.netscape.security.x509.CRLNumberExtension"); ++ props.put(CRL_REASON, "org.mozilla.jss.netscape.security.x509.CRLReasonExtension"); ++ } ++ ++ // Return the file along with location ++ private static File certificatePropFile(String fileName) { ++ return (new File(EXTENSIONS_HOME + fileName)); ++ } ++ ++ // Load the names to oid map ++ private static void loadNames() { ++ Properties props = new Properties(); ++ File namesMap = certificatePropFile(EXTENSIONS_OIDS); ++ ++ if (!namesMap.exists()) { ++ loadNamesDefault(props); ++ } else { ++ FileInputStream fis = null; ++ try { ++ fis = new FileInputStream(namesMap); ++ props.load(fis); ++ } catch (IOException e) { ++ loadNamesDefault(props); ++ } finally { ++ if (fis != null) { ++ try { ++ fis.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++ ++ Iterator names = props.stringPropertyNames().iterator(); ++ while (names.hasNext()) { ++ String name = names.next(); ++ String oidName = props.getProperty(name); ++ ObjectIdentifier oid = new ObjectIdentifier(oidName); ++ ++ name2OID.put(name, oid); ++ oid2Name.put(oid, name); ++ } ++ } ++ ++ // Load the names to classes map ++ private static void loadClasses() { ++ Properties props = new Properties(); ++ File classMap = certificatePropFile(EXTENSIONS_CLASSES); ++ ++ if (!classMap.exists()) { ++ loadClassDefault(props); ++ } else { ++ FileInputStream fis = null; ++ try { ++ fis = new FileInputStream(classMap); ++ props.load(fis); ++ } catch (IOException e) { ++ loadClassDefault(props); ++ } finally { ++ if (fis != null) { ++ try { ++ fis.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++ ++ Iterator names = props.stringPropertyNames().iterator(); ++ while (names.hasNext()) { ++ String name = names.next(); ++ String className = props.getProperty(name); ++ ++ name2Class.put(name, className); ++ } ++ } ++ ++ /** ++ * Add an extension to the OIDMap. ++ * ++ * Assumes existence of static OID and NAME fields with unique values. ++ */ ++ public static void addClass(Class clazz) { ++ try { ++ addAttribute(clazz.getName(), ++ (String) clazz.getField("OID").get(null), ++ (String) clazz.getField("NAME").get(null)); ++ } catch (Throwable e) { ++ System.out.println( ++ "Error adding class " + clazz.getName() + " to OIDMap: " + e); ++ } ++ } ++ ++ /** ++ * Add a name to lookup table. ++ * ++ * @param className the name of the fully qualified class implementing ++ * the asn object. ++ * @param oid the string representation of the object identifier for ++ * the class. ++ * @param name the name of the attribute. ++ * @exception CertificateException on errors. ++ */ ++ public static void addAttribute(String className, String oid, String name) ++ throws CertificateException { ++ ObjectIdentifier objId = new ObjectIdentifier(oid); ++ if (oid2Name.get(objId) != null) { ++ throw new CertificateException("Object identifier already exists."); ++ } ++ if (name2OID.get(name) != null) { ++ throw new CertificateException("Name already exists."); ++ } ++ if (name2Class.get(className) != null) { ++ throw new CertificateException("Class already exists."); ++ } ++ oid2Name.put(objId, name); ++ name2OID.put(name, objId); ++ name2Class.put(name, className); ++ } ++ ++ /** ++ * Return user friendly name associated with the OID. ++ * ++ * @param oid the name of the object identifier to be returned. ++ * @return the user friendly name or null if no name ++ * is registered for this oid. ++ */ ++ public static String getName(ObjectIdentifier oid) { ++ return oid2Name.get(oid); ++ } ++ ++ /** ++ * Return Object identifier for user friendly name. ++ * ++ * @param name the user friendly name. ++ * @return the Object Identifier or null if no oid ++ * is registered for this name. ++ */ ++ public static ObjectIdentifier getOID(String name) { ++ return name2OID.get(name); ++ } ++ ++ /** ++ * Return the java class object associated with the user friendly name. ++ * ++ * @param name the user friendly name. ++ * @exception CertificateException if class cannot be instantiated. ++ */ ++ public static Class getClass(String name) throws CertificateException { ++ String className = name2Class.get(name); ++ if (className == null) ++ return null; ++ try { ++ Class extClass = Class.forName(className); ++ return (extClass); ++ } catch (Exception e) { ++ throw new CertificateException("Error instantiating class for " ++ + name + " " + e.toString()); ++ } ++ } ++ ++ /** ++ * Return the java class object associated with the object identifier.. ++ * ++ * @param oid the name of the object identifier to be returned. ++ * @exception CertificateException if class cannot be instatiated. ++ */ ++ public static Class getClass(ObjectIdentifier oid) ++ throws CertificateException { ++ String name = getName(oid); ++ if (name == null) ++ return null; ++ String className = name2Class.get(name); ++ if (className == null) ++ return null; ++ try { ++ Class extClass = Class.forName(className); ++ return (extClass); ++ } catch (Exception e) { ++ throw new CertificateException("Error instantiating class for " ++ + name + " " + e.toString()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/OIDName.java b/org/mozilla/jss/netscape/security/x509/OIDName.java +new file mode 100644 +index 00000000..a9b05f74 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/OIDName.java +@@ -0,0 +1,90 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * This class implements the OIDName as required by the GeneralNames ++ * ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.3 ++ * @see GeneralName ++ * @see GeneralNames ++ * @see GeneralNameInterface ++ */ ++public class OIDName implements GeneralNameInterface { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 9198510631835117121L; ++ private ObjectIdentifier oid; ++ ++ /** ++ * Create the OIDName object from the passed encoded Der value. ++ * ++ * @param derValue the encoded DER OIDName. ++ * @exception IOException on error. ++ */ ++ public OIDName(DerValue derValue) throws IOException { ++ oid = derValue.getOID(); ++ } ++ ++ /** ++ * Create the OIDName object with the specified name. ++ * ++ * @param name the OIDName. ++ */ ++ public OIDName(ObjectIdentifier oid) { ++ this.oid = oid; ++ } ++ ++ public OIDName(String oid) { ++ this.oid = new ObjectIdentifier(oid); ++ } ++ ++ /** ++ * Return the type of the GeneralName. ++ */ ++ public int getType() { ++ return (GeneralNameInterface.NAME_OID); ++ } ++ ++ /** ++ * Encode the OID name into the DerOutputStream. ++ * ++ * @param out the DER stream to encode the OIDName to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putOID(oid); ++ } ++ ++ /** ++ * Convert the name into user readable string. ++ */ ++ public String toString() { ++ return ("OIDName: " + oid.toString()); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/OtherName.java b/org/mozilla/jss/netscape/security/x509/OtherName.java +new file mode 100644 +index 00000000..a0a3a4cb +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/OtherName.java +@@ -0,0 +1,214 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * This class implements the OtherName as required by the GeneralNames ++ * ASN.1 object. ++ * ++ * OtherName ::= SEQUENCE { ++ * type-id OBJECT IDENTIFIER, ++ * value [0] EXPLICIT ANY DEFINED BY type-id ++ * } ++ * ++ * @see GeneralName ++ * @see GeneralNameInterface ++ * @see GeneralNames ++ * ++ * @version 1.2 ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class OtherName implements GeneralNameInterface { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -3533614377346132611L; ++ private ObjectIdentifier mOID = null; ++ private byte[] mData = null; ++ ++ /** ++ * Create the IPAddressName object from the passed encoded Der value. ++ * ++ * @param derValue the encoded DER IPAddressName. ++ * @exception IOException on error. ++ */ ++ public OtherName(DerValue derValue) throws IOException { ++ decodeThis(derValue); ++ } ++ ++ public OtherName(ObjectIdentifier oid, byte data[]) throws IOException { ++ mOID = oid; ++ DerOutputStream dos = new DerOutputStream(); ++ try { ++ dos.putDerValue(new DerValue(data)); ++ } catch (IOException e) { ++ } finally { ++ dos.close(); ++ } ++ mData = dos.toByteArray(); ++ } ++ ++ /** ++ * Constructs a string-based other name. ++ */ ++ public OtherName(ObjectIdentifier oid, byte tag, String value) throws IOException { ++ mOID = oid; ++ DerOutputStream dos = new DerOutputStream(); ++ try { ++ if (tag == DerValue.tag_PrintableString) { ++ dos.putPrintableString(value); ++ } else if (tag == DerValue.tag_IA5String) { ++ dos.putIA5String(value); ++ } else if (tag == DerValue.tag_BMPString) { ++ dos.putBMPString(value); ++ } else if (tag == DerValue.tag_UTF8String) { ++ dos.putUTF8String(value); ++ } ++ } catch (IOException e) { ++ } finally { ++ dos.close(); ++ } ++ mData = dos.toByteArray(); ++ } ++ ++ public OtherName(ObjectIdentifier oid, String value) throws IOException { ++ mOID = oid; ++ DerOutputStream dos = new DerOutputStream(); ++ try { ++ dos.putPrintableString(value); ++ } catch (IOException e) { ++ } finally { ++ dos.close(); ++ } ++ mData = dos.toByteArray(); ++ } ++ ++ /** ++ * Create the IPAddressName object with the specified name. ++ * ++ * @param name the IPAddressName. ++ */ ++ public OtherName(byte[] data) { ++ try { ++ decodeThis(new DerValue(data)); ++ } catch (IOException e) { ++ } ++ } ++ ++ public ObjectIdentifier getOID() { ++ return mOID; ++ } ++ ++ /** ++ * Return the type of the GeneralName. ++ */ ++ public int getType() { ++ return (GeneralNameInterface.NAME_ANY); ++ } ++ ++ /** ++ * Encode the IPAddress name into the DerOutputStream. ++ * ++ * @param out the DER stream to encode the IPAddressName to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ //encoding the attributes ++ tmp.putOID(mOID); ++ DerOutputStream tmp1 = new DerOutputStream(); ++ tmp1.write(mData); ++ tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, ++ (byte) 0x80), tmp1); ++ ++ out.write(DerValue.tag_SequenceOf, tmp); ++ } ++ ++ public void decode(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ decodeThis(val); ++ } ++ ++ // Decode this extension value ++ private void decodeThis(DerValue derVal) throws IOException { ++ ++ // if (derVal.tag != DerValue.tag_Sequence) { ++ // throw new IOException("Invalid encoding for other name"); ++ // } ++ ++ // Decode all the Attributes ++ mOID = derVal.data.getOID(); ++ // skip tag ++ DerValue tag = derVal.data.getDerValue(); ++ // read data ++ DerValue data = tag.data.getDerValue(); ++ mData = data.toByteArray(); ++ } ++ ++ public byte[] getValue() { ++ return mData; ++ } ++ ++ /** ++ * Return a printable string of IPaddress ++ */ ++ public String toString() { ++ if (mData != null) { ++ try { ++ DerValue data = new DerValue(mData); ++ if (data.tag == DerValue.tag_PrintableString) { ++ return "OtherName: (PrintableString)" + mOID + "," + data.getPrintableString(); ++ } else if (data.tag == DerValue.tag_IA5String) { ++ return "OtherName: (IA5String)" + mOID + "," + data.getIA5String(); ++ } else if (data.tag == DerValue.tag_BMPString) { ++ return "OtherName: (BMPString)" + mOID + "," + data.getIA5String(); ++ } else if (data.tag == DerValue.tag_UTF8String) { ++ return "OtherName: (UTF8String)" + mOID + "," + data.getUTF8String(); ++ } else { ++ return "OtherName: (Any)" + mOID + "," + toStr(data.toByteArray()); ++ } ++ } catch (IOException e) { ++ ++ return "OtherName: (Any)" + mOID + "," + toStr(mData); ++ } ++ } else { ++ return "OtherName: "; ++ } ++ } ++ ++ public String toStr(byte data[]) { ++ StringBuffer b = new StringBuffer(); ++ for (int i = 0; i < data.length; i++) { ++ if ((data[i] & 0xff) < 16) { ++ b.append("0"); ++ } ++ b.append(Integer.toString((data[i] & 0xff), 0x10)); ++ } ++ return b.toString(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/PKIXExtensions.java b/org/mozilla/jss/netscape/security/x509/PKIXExtensions.java +new file mode 100644 +index 00000000..61dc9c8e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/PKIXExtensions.java +@@ -0,0 +1,185 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * Lists all the object identifiers of the X509 extensions of the PKIX profile. ++ * ++ *

++ * Extensions are addiitonal attributes which can be inserted in a X509 v3 certificate. For example a ++ * "Driving License Certificate" could have the driving license number as a extension. ++ * ++ *

++ * Extensions are represented as a sequence of the extension identifier (Object Identifier), a boolean flag stating ++ * whether the extension is to be treated as being critical and the extension value itself (this is again a DER encoding ++ * of the extension value). ++ * ++ * @see Extension ++ * ++ * @version 1.4 ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public class PKIXExtensions { ++ // The object identifiers ++ private static final int AuthorityKey_data[] = { 2, 5, 29, 35 }; ++ private static final int SubjectKey_data[] = { 2, 5, 29, 14 }; ++ private static final int KeyUsage_data[] = { 2, 5, 29, 15 }; ++ private static final int PrivateKeyUsage_data[] = { 2, 5, 29, 16 }; ++ private static final int CertificatePolicies_data[] = { 2, 5, 29, 32 }; ++ private static final int PolicyMappings_data[] = { 2, 5, 29, 33 }; ++ private static final int SubjectAlternativeName_data[] = { 2, 5, 29, 17 }; ++ private static final int IssuerAlternativeName_data[] = { 2, 5, 29, 18 }; ++ private static final int SubjectDirectoryAttributes_data[] = { 2, 5, 29, 9 }; ++ private static final int BasicConstraints_data[] = { 2, 5, 29, 19 }; ++ private static final int NameConstraints_data[] = { 2, 5, 29, 30 }; ++ private static final int PolicyConstraints_data[] = { 2, 5, 29, 36 }; ++ private static final int CRLDistributionPoints_data[] = { 2, 5, 29, 31 }; ++ private static final int CRLNumber_data[] = { 2, 5, 29, 20 }; ++ private static final int IssuingDistributionPoint_data[] = { 2, 5, 29, 28 }; ++ private static final int DeltaCRLIndicator_data[] = { 2, 5, 29, 27 }; ++ private static final int ReasonCode_data[] = { 2, 5, 29, 21 }; ++ private static final int HoldInstructionCode_data[] = { 2, 5, 29, 23 }; ++ private static final int InvalidityDate_data[] = { 2, 5, 29, 24 }; ++ private static final int CertificateIssuer_data[] = { 2, 5, 29, 29 }; ++ private static final int FreshestCRL_data[] = { 2, 5, 29, 46 }; ++ ++ /** ++ * Identifies the particular public key used to sign the certificate. ++ */ ++ public static final ObjectIdentifier AuthorityKey_Id = new ObjectIdentifier(AuthorityKey_data); ++ ++ /** ++ * Identifies the particular public key used in an application. ++ */ ++ public static final ObjectIdentifier SubjectKey_Id = new ObjectIdentifier(SubjectKey_data); ++ ++ /** ++ * Defines the purpose of the key contained in the certificate. ++ */ ++ public static final ObjectIdentifier KeyUsage_Id = new ObjectIdentifier(KeyUsage_data); ++ ++ /** ++ * Allows the certificate issuer to specify a different validity period ++ * for the private key than the certificate. ++ */ ++ public static final ObjectIdentifier PrivateKeyUsage_Id = new ObjectIdentifier(PrivateKeyUsage_data); ++ ++ /** ++ * Contains the sequence of policy information terms. ++ */ ++ public static final ObjectIdentifier CertificatePolicies_Id = new ObjectIdentifier(CertificatePolicies_data); ++ ++ /** ++ * Lists pairs of objectidentifiers of policies considered equivalent by the ++ * issuing CA to the subject CA. ++ */ ++ public static final ObjectIdentifier PolicyMappings_Id = new ObjectIdentifier(PolicyMappings_data); ++ ++ /** ++ * Allows additional identities to be bound to the subject of the certificate. ++ */ ++ public static final ObjectIdentifier SubjectAlternativeName_Id = new ObjectIdentifier(SubjectAlternativeName_data); ++ ++ /** ++ * Allows additional identities to be associated with the certificate issuer. ++ */ ++ public static final ObjectIdentifier IssuerAlternativeName_Id = ++ new ObjectIdentifier(IssuerAlternativeName_data); ++ ++ /** ++ * Identifies additional directory attributes. ++ * This extension is always non-critical. ++ */ ++ public static final ObjectIdentifier SubjectDirectoryAttributes_Id = new ObjectIdentifier( ++ SubjectDirectoryAttributes_data); ++ ++ /** ++ * Identifies whether the subject of the certificate is a CA and how deep ++ * a certification path may exist through that CA. ++ */ ++ public static final ObjectIdentifier BasicConstraints_Id = ++ new ObjectIdentifier(BasicConstraints_data); ++ ++ /** ++ * Provides for permitted and excluded subtrees that place restrictions ++ * on names that may be included within a certificate issued by a given CA. ++ */ ++ public static final ObjectIdentifier NameConstraints_Id = new ObjectIdentifier(NameConstraints_data); ++ ++ /** ++ * Used to either prohibit policy mapping or limit the set of policies ++ * that can be in subsequent certificates. ++ */ ++ public static final ObjectIdentifier PolicyConstraints_Id = new ObjectIdentifier(PolicyConstraints_data); ++ ++ /** ++ * Identifies how CRL information is obtained. ++ */ ++ public static final ObjectIdentifier CRLDistributionPoints_Id = new ObjectIdentifier(CRLDistributionPoints_data); ++ ++ /** ++ * Conveys a monotonically increasing sequence number for each CRL ++ * issued by a given CA. ++ */ ++ public static final ObjectIdentifier CRLNumber_Id = new ObjectIdentifier(CRLNumber_data); ++ ++ /** ++ * Identifies the CRL distribution point for a particular CRL. ++ */ ++ public static final ObjectIdentifier IssuingDistributionPoint_Id = new ObjectIdentifier( ++ IssuingDistributionPoint_data); ++ ++ /** ++ * Identifies the delta CRL. ++ */ ++ public static final ObjectIdentifier DeltaCRLIndicator_Id = new ObjectIdentifier(DeltaCRLIndicator_data); ++ ++ /** ++ * Identifies the reason for the certificate revocation. ++ */ ++ public static final ObjectIdentifier ReasonCode_Id = new ObjectIdentifier(ReasonCode_data); ++ ++ /** ++ * This extension provides a registered instruction identifier indicating ++ * the action to be taken, after encountering a certificate that has been ++ * placed on hold. ++ */ ++ public static final ObjectIdentifier HoldInstructionCode_Id = new ObjectIdentifier(HoldInstructionCode_data); ++ ++ /** ++ * Identifies the date on which it is known or suspected that the private ++ * key was compromised or that the certificate otherwise became invalid. ++ */ ++ public static final ObjectIdentifier InvalidityDate_Id = new ObjectIdentifier(InvalidityDate_data); ++ ++ /** ++ * Identifies the date on which it is known or suspected that the private ++ * key was compromised or that the certificate otherwise became invalid. ++ */ ++ public static final ObjectIdentifier CertificateIssuer_Id = new ObjectIdentifier(CertificateIssuer_data); ++ ++ /** ++ * Identifies how delta CRL information is obtained. ++ */ ++ public static final ObjectIdentifier FreshestCRL_Id = new ObjectIdentifier(FreshestCRL_data); ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/PolicyConstraint.java b/org/mozilla/jss/netscape/security/x509/PolicyConstraint.java +new file mode 100644 +index 00000000..e7f2c1dd +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/PolicyConstraint.java +@@ -0,0 +1,136 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the PolicyConstraint ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.4 ++ */ ++public class PolicyConstraint { ++ private static final byte TAG_SET = 0; ++ private static final byte TAG_REQUIRE = 1; ++ private static final byte TAG_INHIBIT = 2; ++ ++ private CertificatePolicySet set = null; ++ private int require = -1; ++ private int inhibit = -1; ++ ++ /** ++ * The default constructor for this object ++ * ++ * @param set the CertificatePolicySet (null for optional). ++ * @param require require explicit policy (-1 for optional). ++ * @param inhibit inhibit policy mapping (-1 for optional). ++ */ ++ public PolicyConstraint(CertificatePolicySet set, int require, int inhibit) { ++ this.set = set; ++ this.require = require; ++ this.inhibit = inhibit; ++ } ++ ++ /** ++ * Create the PolicyConstraint from the DerValue. ++ * ++ * @param val the DerValue of the PolicyConstraint. ++ * @exception IOException on decoding errors. ++ */ ++ public PolicyConstraint(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Sequence tag missing for PolicyConstraint."); ++ } ++ DerInputStream in = val.data; ++ while (in != null && in.available() != 0) { ++ DerValue next = in.getDerValue(); ++ switch (next.tag & 0x1f) { ++ case TAG_SET: ++ this.set = new CertificatePolicySet(next.data); ++ break; ++ ++ case TAG_REQUIRE: ++ next = next.data.getDerValue(); ++ this.require = (next.getInteger()).toInt(); ++ break; ++ ++ case TAG_INHIBIT: ++ next = next.data.getDerValue(); ++ this.inhibit = (next.getInteger()).toInt(); ++ break; ++ ++ default: ++ throw new IOException("Invalid tag option for PolicyConstraint."); ++ } ++ } ++ } ++ ++ /** ++ * Return user readable form of the object. ++ */ ++ public String toString() { ++ String s = ((set != null) ? ++ "PolicyConstraint: [\n" ++ + " PolicySet:[" + set.toString() + "]\n" ++ + " Require:" + require + "\n" ++ + " Inhibit:" + inhibit + "\n" ++ + "]\n" : ++ "PolicyConstraint: [\n" ++ + " PolicySet:[null]\n" ++ + " Require:" + require + "\n" ++ + " Inhibit:" + inhibit + "\n" ++ + "]\n"); ++ return (s); ++ } ++ ++ /** ++ * Encode the object to the output stream. ++ * ++ * @param out the DerOutputStream to encode the object to. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tagged = new DerOutputStream(); ++ ++ if (set != null) { ++ DerOutputStream tmp = new DerOutputStream(); ++ set.encode(tmp); ++ tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, TAG_SET), tmp); ++ } ++ if (require != -1) { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putInteger(new BigInt(require)); ++ tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, TAG_REQUIRE), tmp); ++ } ++ if (inhibit != -1) { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putInteger(new BigInt(inhibit)); ++ tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, ++ true, TAG_INHIBIT), tmp); ++ } ++ out.write(DerValue.tag_Sequence, tagged); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/PolicyConstraintsExtension.java b/org/mozilla/jss/netscape/security/x509/PolicyConstraintsExtension.java +new file mode 100644 +index 00000000..8146e712 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/PolicyConstraintsExtension.java +@@ -0,0 +1,307 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the certificate extension which specifies the ++ * Policy constraints. ++ *

++ * The policy constraints extension can be used in certificates issued to CAs. The policy constraints extension ++ * constrains path validation in two ways. It can be used to prohibit policy mapping or require that each certificate in ++ * a path contain an acceptable policy identifier. ++ *

++ * The ASN.1 syntax for this is (IMPLICIT tagging is defined in the module definition): ++ * ++ *

++ * PolicyConstraints ::= SEQUENCE {
++ *     requireExplicitPolicy [0] SkipCerts OPTIONAL,
++ *     inhibitPolicyMapping  [1] SkipCerts OPTIONAL
++ * }
++ * SkipCerts ::= INTEGER (0..MAX)
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.9 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class PolicyConstraintsExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -3723759691127622370L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.PolicyConstraints"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "PolicyConstraints"; ++ public static final String REQUIRE = "require"; ++ public static final String INHIBIT = "inhibit"; ++ ++ private static final byte TAG_REQUIRE = 0; ++ private static final byte TAG_INHIBIT = 1; ++ ++ private int require = -1; ++ private int inhibit = -1; ++ ++ // Encode this extension value. ++ private void encodeThis() throws IOException { ++ try (DerOutputStream seq = new DerOutputStream()) { ++ DerOutputStream tagged = new DerOutputStream(); ++ ++ if (require != -1) { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putInteger(new BigInt(require)); ++ tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_REQUIRE), tmp); ++ } ++ if (inhibit != -1) { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putInteger(new BigInt(inhibit)); ++ tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_INHIBIT), tmp); ++ } ++ seq.write(DerValue.tag_Sequence, tagged); ++ extensionValue = seq.toByteArray(); ++ } ++ } ++ ++ /** ++ * Create a PolicyConstraintsExtension object with criticality and ++ * both require explicit policy and inhibit policy mapping. ++ * ++ * @param critical whether this extension should be critical ++ * @param require require explicit policy (-1 for optional). ++ * @param inhibit inhibit policy mapping (-1 for optional). ++ */ ++ public PolicyConstraintsExtension(boolean crit, int require, int inhibit) ++ throws IOException { ++ init(crit, require, inhibit); ++ } ++ ++ /** ++ * Create a PolicyConstraintsExtension object with both ++ * require explicit policy and inhibit policy mapping. ++ * ++ * @param require require explicit policy (-1 for optional). ++ * @param inhibit inhibit policy mapping (-1 for optional). ++ */ ++ public PolicyConstraintsExtension(int require, int inhibit) ++ throws IOException { ++ init(false, require, inhibit); ++ } ++ ++ private void init(boolean crit, int require, int inhibit) ++ throws IOException { ++ this.require = require; ++ this.inhibit = inhibit; ++ this.extensionId = PKIXExtensions.PolicyConstraints_Id; ++ this.critical = crit; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from its DER encoded value and criticality. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public PolicyConstraintsExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.PolicyConstraints_Id; ++ this.critical = critical.booleanValue(); ++ ++ if (!(value instanceof byte[])) ++ throw new IOException("Illegal argument type"); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ System.arraycopy(value, 0, extValue, 0, len); ++ ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Sequence tag missing for PolicyConstraint."); ++ } ++ DerInputStream in = val.data; ++ while (in != null && in.available() != 0) { ++ DerValue next = in.getDerValue(); ++ ++ if (next.isContextSpecific(TAG_REQUIRE) && !next.isConstructed()) { ++ if (this.require != -1) ++ throw new IOException("Duplicate requireExplicitPolicy" + ++ "found in the PolicyConstraintsExtension"); ++ next.resetTag(DerValue.tag_Integer); ++ this.require = (next.getInteger()).toInt(); ++ ++ } else if (next.isContextSpecific(TAG_INHIBIT) && ++ !next.isConstructed()) { ++ if (this.inhibit != -1) ++ throw new IOException("Duplicate inhibitPolicyMapping" + ++ "found in the PolicyConstraintsExtension"); ++ next.resetTag(DerValue.tag_Integer); ++ this.inhibit = (next.getInteger()).toInt(); ++ } else ++ throw new IOException("Invalid encoding of PolicyConstraint"); ++ } ++ } ++ ++ /** ++ * Return the extension as user readable string. ++ */ ++ public String toString() { ++ String s; ++ s = super.toString() + "PolicyConstraints: [" + " Require: "; ++ if (require == -1) ++ s += "unspecified;"; ++ else ++ s += require + ";"; ++ s += "\tInhibit: "; ++ if (inhibit == -1) ++ s += "unspecified"; ++ else ++ s += inhibit; ++ s += " ]\n"; ++ return s; ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = PKIXExtensions.PolicyConstraints_Id; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (!(obj instanceof Integer)) { ++ throw new IOException("Attribute value should be of type Integer."); ++ } ++ if (name.equalsIgnoreCase(REQUIRE)) { ++ require = ((Integer) obj).intValue(); ++ } else if (name.equalsIgnoreCase(INHIBIT)) { ++ inhibit = ((Integer) obj).intValue(); ++ } else { ++ throw new IOException("Attribute name " + "[" + name + "]" + ++ " not recognized by " + ++ "CertAttrSet:PolicyConstraints."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(REQUIRE)) { ++ return Integer.valueOf(require); ++ } else if (name.equalsIgnoreCase(INHIBIT)) { ++ return Integer.valueOf(inhibit); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:PolicyConstraints."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(REQUIRE)) { ++ require = -1; ++ } else if (name.equalsIgnoreCase(INHIBIT)) { ++ inhibit = -1; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:PolicyConstraints."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(REQUIRE); ++ elements.addElement(INHIBIT); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ /** ++ * returns the requireExplicitMapping parameter. ++ */ ++ public int getRequireExplicitMapping() { ++ return require; ++ } ++ ++ /** ++ * returns the inhibitPolicyMapping parameter. ++ */ ++ public int getInhibitPolicyMapping() { ++ return inhibit; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/PolicyMappingsExtension.java b/org/mozilla/jss/netscape/security/x509/PolicyMappingsExtension.java +new file mode 100644 +index 00000000..b15d7c40 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/PolicyMappingsExtension.java +@@ -0,0 +1,259 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the Policy Mappings Extension. ++ * ++ * This extension, if present, identifies the certificate policies considered ++ * identical between the issuing and the subject CA. ++ *

++ * Extensions are addiitonal attributes which can be inserted in a X509 v3 certificate. For example a ++ * "Driving License Certificate" could have the driving license number as a extension. ++ * ++ *

++ * Extensions are represented as a sequence of the extension identifier (Object Identifier), a boolean flag stating ++ * whether the extension is to be treated as being critical and the extension value itself (this is again a DER encoding ++ * of the extension value). ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.7 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class PolicyMappingsExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -4023336164621135851L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.PolicyMappings"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "PolicyMappings"; ++ public static final String MAP = "map"; ++ ++ // Private data members ++ private Vector maps = null; ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ try (DerOutputStream os = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ for (int i = 0; i < maps.size(); i++) { ++ maps.elementAt(i).encode(tmp); ++ } ++ os.write(DerValue.tag_Sequence, tmp); ++ extensionValue = os.toByteArray(); ++ } ++ } ++ ++ /** ++ * Create a PolicyMappings with the Vector of CertificatePolicyMap. ++ * ++ * @param maps the Vector of CertificatePolicyMap. ++ */ ++ public PolicyMappingsExtension(Vector map) throws IOException { ++ init(false, map); ++ } ++ ++ /** ++ * Create a PolicyMappings with the Vector of CertificatePolicyMap. ++ * ++ * @param maps the Vector of CertificatePolicyMap. ++ */ ++ public PolicyMappingsExtension(boolean critical, Vector map) ++ throws IOException { ++ init(critical, map); ++ } ++ ++ /** ++ * init policy with criticality and map. ++ */ ++ private void init(boolean critical, Vector map) throws IOException { ++ this.maps = map; ++ this.extensionId = PKIXExtensions.PolicyMappings_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a default PolicyMappingsExtension. ++ */ ++ public PolicyMappingsExtension() { ++ extensionId = PKIXExtensions.PolicyMappings_Id; ++ critical = false; ++ maps = new Vector(1, 1); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public PolicyMappingsExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.PolicyMappings_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for " + ++ "PolicyMappingsExtension."); ++ } ++ maps = new Vector(1, 1); ++ while (val.data.available() != 0) { ++ DerValue seq = val.data.getDerValue(); ++ CertificatePolicyMap map = new CertificatePolicyMap(seq); ++ maps.addElement(map); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the policy map. ++ */ ++ public String toString() { ++ if (maps == null) ++ return ""; ++ String s = super.toString() + "PolicyMappings [\n" ++ + maps.toString() + "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = PKIXExtensions.PolicyMappings_Id; ++ critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ @SuppressWarnings("unchecked") ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(MAP)) { ++ if (!(obj instanceof Vector)) { ++ throw new IOException("Attribute value should be of" + ++ " type Vector."); ++ } ++ maps = (Vector) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:PolicyMappingsExtension."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(MAP)) { ++ return (maps); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:PolicyMappingsExtension."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(MAP)) { ++ maps = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:PolicyMappingsExtension."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(MAP); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ /** ++ * Returns an enumeration of the mappings in the extension. ++ */ ++ public Enumeration getMappings() { ++ if (maps == null) ++ return null; ++ return maps.elements(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/PolicyQualifierInfo.java b/org/mozilla/jss/netscape/security/x509/PolicyQualifierInfo.java +new file mode 100644 +index 00000000..bb3c30cf +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/PolicyQualifierInfo.java +@@ -0,0 +1,118 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * Represent the PolicyQualifierInfo. ++ * ++ * policyQualifierInfo ::= SEQUENCE { ++ * policyQualifierId PolicyQualifierId ++ * qualifier ANY DEFINED BY policyQualifierId ++ * } ++ * ++ * @author Thomas Kwan ++ */ ++public class PolicyQualifierInfo implements java.io.Serializable { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -2930016944517192379L; ++ public static final int OID_CPS[] = { 1, 3, 6, 1, 5, 5, 7, 2, 1 }; ++ public static final ObjectIdentifier QT_CPS = new ++ ObjectIdentifier(OID_CPS); ++ ++ public static final int OID_UNOTICE[] = { 1, 3, 6, 1, 5, 5, 7, 2, 2 }; ++ public static final ObjectIdentifier QT_UNOTICE = new ++ ObjectIdentifier(OID_UNOTICE); ++ ++ private ObjectIdentifier mId = null; ++ private Qualifier mQualifier = null; ++ ++ /** ++ * Create a PolicyQualifierInfo ++ * ++ * @param id the ObjectIdentifier for the policy id. ++ */ ++ public PolicyQualifierInfo(ObjectIdentifier id, Qualifier qualifier) { ++ mId = id; ++ mQualifier = qualifier; ++ } ++ ++ /** ++ * Create the object from its Der encoded value. ++ * ++ * @param val the DER encoded value for the same. ++ */ ++ public PolicyQualifierInfo(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for PolicyQualifierInfo."); ++ } ++ DerValue did = val.data.getDerValue(); ++ mId = did.getOID(); ++ if (val.data.available() != 0) { ++ DerValue qualifier = val.data.getDerValue(); ++ if (qualifier.tag == DerValue.tag_IA5String) { ++ mQualifier = new CPSuri(qualifier); ++ } else { ++ mQualifier = new UserNotice(qualifier); ++ } ++ } ++ } ++ ++ public ObjectIdentifier getId() { ++ return mId; ++ } ++ ++ /** ++ * Returns object of type CPSuri or UserNotice. ++ */ ++ public Qualifier getQualifier() { ++ return mQualifier; ++ } ++ ++ /** ++ * Returns a printable representation of the CertificatePolicyId. ++ */ ++ public String toString() { ++ String s = "PolicyQualifierInfo: ["; ++ s = s + getId() + " " + getQualifier(); ++ s = s + "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Write the PolicyQualifier to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putOID(mId); ++ mQualifier.encode(tmp); ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/PolicyQualifiers.java b/org/mozilla/jss/netscape/security/x509/PolicyQualifiers.java +new file mode 100644 +index 00000000..3c15a1d3 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/PolicyQualifiers.java +@@ -0,0 +1,107 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the PolicyQualifiers. ++ * ++ * policyQualifiers ::= SEQUENCE SIZE (1..MAX) OF PolicyQualifierInfo ++ * ++ * @author Thomas Kwan ++ */ ++public class PolicyQualifiers implements java.io.Serializable { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 6932694408774694516L; ++ private Vector mInfo = new Vector(); ++ ++ /** ++ * Create a PolicyQualifiers with the ObjectIdentifier. ++ * ++ * @param id the ObjectIdentifier for the policy id. ++ */ ++ public PolicyQualifiers() { ++ } ++ ++ /** ++ * Create the object from its Der encoded value. ++ * ++ * @param val the DER encoded value for the same. ++ */ ++ public PolicyQualifiers(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for " + "PolicyQualifiers."); ++ } ++ while (val.data.available() != 0) { ++ DerValue pq = val.data.getDerValue(); ++ PolicyQualifierInfo info = new PolicyQualifierInfo(pq); ++ add(info); ++ } ++ } ++ ++ public void add(PolicyQualifierInfo info) { ++ mInfo.addElement(info); ++ } ++ ++ public int size() { ++ return mInfo.size(); ++ } ++ ++ public PolicyQualifierInfo getInfoAt(int i) { ++ return mInfo.elementAt(i); ++ } ++ ++ /** ++ * Returns a printable representation of the CertificatePolicyId. ++ */ ++ public String toString() { ++ StringBuffer s = new StringBuffer("PolicyQualifiers: ["); ++ for (int i = 0; i < mInfo.size(); i++) { ++ PolicyQualifierInfo pq = mInfo.elementAt(i); ++ s.append(pq.toString()); ++ } ++ s.append("]\n"); ++ ++ return s.toString(); ++ } ++ ++ /** ++ * Write the PolicyQualifiers to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ for (int i = 0; i < mInfo.size(); i++) { ++ PolicyQualifierInfo pq = mInfo.elementAt(i); ++ pq.encode(tmp); ++ } ++ ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/PrintableConverter.java b/org/mozilla/jss/netscape/security/x509/PrintableConverter.java +new file mode 100644 +index 00000000..4d52df45 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/PrintableConverter.java +@@ -0,0 +1,114 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.CharacterCodingException; ++import java.nio.charset.CharsetEncoder; ++ ++import org.mozilla.jss.netscape.security.util.ASN1CharStrConvMap; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * A AVAValueConverter that converts a Printable String attribute to a DerValue ++ * and vice versa. An example an attribute that is a printable string is "C". ++ * ++ * @see ASN1CharStrConvMap ++ * @see AVAValueConverter ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ */ ++ ++public class PrintableConverter implements AVAValueConverter { ++ // public constructors. ++ ++ public PrintableConverter() { ++ } ++ ++ /** ++ * Converts a string with ASN.1 Printable characters to a DerValue. ++ * ++ * @param valueString a string with Printable characters. ++ * ++ * @return a DerValue. ++ * ++ * @exception IOException if a Printable encoder is not ++ * available for the conversion. ++ */ ++ public DerValue getValue(String valueString) ++ throws IOException { ++ return getValue(valueString, null); ++ } ++ ++ public DerValue getValue(String valueString, byte[] tags) throws IOException { ++ try { ++ CharsetEncoder encoder = ASN1CharStrConvMap.getDefault().getEncoder(DerValue.tag_PrintableString); ++ if (encoder == null) ++ throw new IOException("No encoder for printable"); ++ ++ CharBuffer charBuffer = CharBuffer.wrap(valueString.toCharArray()); ++ ByteBuffer byteBuffer = encoder.encode(charBuffer); ++ ++ return new DerValue(DerValue.tag_PrintableString, ++ byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.limit()); ++ ++ } catch (CharacterCodingException e) { ++ throw new IllegalArgumentException("Invalid Printable String AVA Value", e); ++ } ++ } ++ ++ /** ++ * Converts a BER encoded value of PrintableString to a DER encoded value. ++ * Checks if the BER encoded value is a PrintableString. ++ * NOTE only DER encoded values are currently accepted on input. ++ * ++ * @param berStream A byte array of the BER encoded value. ++ * ++ * @return A DerValue. ++ * ++ * @exception IOException if the BER value cannot be converted to a ++ * PrintableString DER value. ++ */ ++ public DerValue getValue(byte[] berStream) ++ throws IOException { ++ DerValue value = new DerValue(berStream); ++ if (value.tag != DerValue.tag_PrintableString) ++ throw new IOException("Invalid Printable String AVA Value"); ++ return value; ++ } ++ ++ /** ++ * Converts a DerValue of PrintableString to a java string with ++ * PrintableString characters. ++ * ++ * @param avaValue a DerValue. ++ * ++ * @return a string with PrintableString characters. ++ * ++ * @exception IOException if the DerValue is not a PrintableString i.e. ++ * The DerValue cannot be converted to a string ++ * with PrintableString characters. ++ */ ++ public String getAsString(DerValue avaValue) ++ throws IOException { ++ return avaValue.getPrintableString(); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/PrivateKeyUsageExtension.java b/org/mozilla/jss/netscape/security/x509/PrivateKeyUsageExtension.java +new file mode 100644 +index 00000000..a14630c7 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/PrivateKeyUsageExtension.java +@@ -0,0 +1,340 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.security.cert.CertificateException; ++import java.security.cert.CertificateExpiredException; ++import java.security.cert.CertificateNotYetValidException; ++import java.security.cert.CertificateParsingException; ++import java.util.Date; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the Private Key Usage Extension. ++ * ++ *

++ * The Private Key Usage Period extension allows the certificate issuer to specify a different validity period for the ++ * private key than the certificate. This extension is intended for use with digital signature keys. This extension ++ * consists of two optional components notBefore and notAfter. The private key associated with the certificate should ++ * not be used to sign objects before or after the times specified by the two components, respectively. ++ * ++ *

++ * PrivateKeyUsagePeriod ::= SEQUENCE {
++ *     notBefore  [0]  GeneralizedTime OPTIONAL,
++ *     notAfter   [1]  GeneralizedTime OPTIONAL }
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.12 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class PrivateKeyUsageExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -7623695233957629936L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info.extensions.PrivateKeyUsage"; ++ /** ++ * Sub attributes name for this CertAttrSet. ++ */ ++ public static final String NAME = "PrivateKeyUsage"; ++ public static final String NOT_BEFORE = "not_before"; ++ public static final String NOT_AFTER = "not_after"; ++ ++ // Private data members ++ private static final byte TAG_BEFORE = 0; ++ private static final byte TAG_AFTER = 1; ++ ++ private Date notBefore; ++ private Date notAfter; ++ ++ // Encode this extension value. ++ private void encodeThis() throws IOException { ++ try (DerOutputStream seq = new DerOutputStream()) { ++ ++ DerOutputStream tagged = new DerOutputStream(); ++ if (notBefore != null) { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putGeneralizedTime(notBefore); ++ tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_BEFORE), tmp); ++ } ++ if (notAfter != null) { ++ DerOutputStream tmp = new DerOutputStream(); ++ tmp.putGeneralizedTime(notAfter); ++ tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ++ false, TAG_AFTER), tmp); ++ } ++ seq.write(DerValue.tag_Sequence, tagged); ++ extensionValue = seq.toByteArray(); ++ } ++ } ++ ++ /** ++ * The default constructor for PrivateKeyUsageExtension. ++ * ++ * @param notBefore the date/time before which the private key ++ * should not be used. ++ * @param notAfter the date/time after which the private key ++ * should not be used. ++ */ ++ public PrivateKeyUsageExtension(Date notBefore, Date notAfter) ++ throws IOException { ++ this.notBefore = notBefore; ++ this.notAfter = notAfter; ++ ++ this.extensionId = PKIXExtensions.PrivateKeyUsage_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * ++ * @exception CertificateException on certificate parsing errors. ++ * @exception IOException on error. ++ */ ++ public PrivateKeyUsageExtension(Boolean critical, Object value) ++ throws CertificateException, IOException { ++ this.extensionId = PKIXExtensions.PrivateKeyUsage_Id; ++ this.critical = critical.booleanValue(); ++ ++ if (!(value instanceof byte[])) ++ throw new CertificateException("Illegal argument type"); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ System.arraycopy(value, 0, extValue, 0, len); ++ ++ this.extensionValue = extValue; ++ DerInputStream str = new DerInputStream(extValue); ++ DerValue[] seq = str.getSequence(2); ++ ++ // NB. this is always encoded with the IMPLICIT tag ++ // The checks only make sense if we assume implicit tagging, ++ // with explicit tagging the form is always constructed. ++ for (int i = 0; i < seq.length; i++) { ++ DerValue opt = seq[i]; ++ ++ if (opt.isContextSpecific(TAG_BEFORE) && ++ !opt.isConstructed()) { ++ if (notBefore != null) { ++ throw new CertificateParsingException( ++ "Duplicate notBefore in PrivateKeyUsage."); ++ } ++ opt.resetTag(DerValue.tag_GeneralizedTime); ++ str = new DerInputStream(opt.toByteArray()); ++ notBefore = str.getGeneralizedTime(); ++ ++ } else if (opt.isContextSpecific(TAG_AFTER) && ++ !opt.isConstructed()) { ++ if (notAfter != null) { ++ throw new CertificateParsingException( ++ "Duplicate notAfter in PrivateKeyUsage."); ++ } ++ opt.resetTag(DerValue.tag_GeneralizedTime); ++ str = new DerInputStream(opt.toByteArray()); ++ notAfter = str.getGeneralizedTime(); ++ } else ++ throw new IOException("Invalid encoding of " + ++ "PrivateKeyUsageExtension"); ++ } ++ } ++ ++ /** ++ * Return the printable string. ++ */ ++ public String toString() { ++ return (super.toString() + ++ "PrivateKeyUsage: [From: " + ++ ((notBefore == null) ? "" : notBefore.toString()) + ++ ", To: " + ++ ((notAfter == null) ? "" : notAfter.toString()) + "]\n"); ++ } ++ ++ /** ++ * Return notBefore date ++ */ ++ public Date getNotBefore() { ++ return (notBefore); ++ } ++ ++ /** ++ * Return notAfter date ++ */ ++ public Date getNotAfter() { ++ return (notAfter); ++ } ++ ++ /** ++ * Verify that that the current time is within the validity period. ++ * ++ * @exception CertificateExpiredException if the certificate has expired. ++ * @exception CertificateNotYetValidException if the certificate is not ++ * yet valid. ++ */ ++ public void valid() ++ throws CertificateNotYetValidException, CertificateExpiredException { ++ Date now = new Date(); ++ valid(now); ++ } ++ ++ /** ++ * Verify that that the passed time is within the validity period. ++ * ++ * @exception CertificateExpiredException if the certificate has expired ++ * with respect to the Date supplied. ++ * @exception CertificateNotYetValidException if the certificate is not ++ * yet valid with respect to the Date supplied. ++ * ++ */ ++ public void valid(Date now) ++ throws CertificateNotYetValidException, CertificateExpiredException { ++ /* ++ * we use the internal Dates rather than the passed in Date ++ * because someone could override the Date methods after() ++ * and before() to do something entirely different. ++ */ ++ if (notBefore.after(now)) { ++ throw new CertificateNotYetValidException("NotBefore: " + ++ notBefore.toString()); ++ } ++ if (notAfter.before(now)) { ++ throw new CertificateExpiredException("NotAfter: " + ++ notAfter.toString()); ++ } ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = PKIXExtensions.PrivateKeyUsage_Id; ++ critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception CertificateException on decoding errors. ++ */ ++ public void decode(InputStream in) throws CertificateException { ++ throw new CertificateException("Method not to be called directly."); ++ } ++ ++ /** ++ * Set the attribute value. ++ * ++ * @exception CertificateException on attribute handling errors. ++ */ ++ public void set(String name, Object obj) ++ throws CertificateException { ++ clearValue(); ++ if (!(obj instanceof Date)) { ++ throw new CertificateException("Attribute must be of type Date."); ++ } ++ if (name.equalsIgnoreCase(NOT_BEFORE)) { ++ notBefore = (Date) obj; ++ } else if (name.equalsIgnoreCase(NOT_AFTER)) { ++ notAfter = (Date) obj; ++ } else { ++ throw new CertificateException("Attribute name not recognized by" ++ + " CertAttrSet:PrivateKeyUsage."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ * ++ * @exception CertificateException on attribute handling errors. ++ */ ++ public Object get(String name) throws CertificateException { ++ if (name.equalsIgnoreCase(NOT_BEFORE)) { ++ return (new Date(notBefore.getTime())); ++ } else if (name.equalsIgnoreCase(NOT_AFTER)) { ++ return (new Date(notAfter.getTime())); ++ } else { ++ throw new CertificateException("Attribute name not recognized by" ++ + " CertAttrSet:PrivateKeyUsage."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ * ++ * @exception CertificateException on attribute handling errors. ++ */ ++ public void delete(String name) throws CertificateException { ++ if (name.equalsIgnoreCase(NOT_BEFORE)) { ++ notBefore = null; ++ } else if (name.equalsIgnoreCase(NOT_AFTER)) { ++ notAfter = null; ++ } else { ++ throw new CertificateException("Attribute name not recognized by" ++ + " CertAttrSet:PrivateKeyUsage."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(NOT_BEFORE); ++ elements.addElement(NOT_AFTER); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/Qualifier.java b/org/mozilla/jss/netscape/security/x509/Qualifier.java +new file mode 100644 +index 00000000..353983ea +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/Qualifier.java +@@ -0,0 +1,63 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the Qualifier. ++ * ++ * Qualifier ::= CHOICE { ++ * cPRuri CPSuri, ++ * userNotice UserNotice ++ * } ++ * ++ * @author Thomas Kwan ++ */ ++public class Qualifier implements java.io.Serializable { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 2214531407387992974L; ++ ++ /** ++ * Create a PolicyQualifierInfo ++ * ++ * @param id the ObjectIdentifier for the policy id. ++ */ ++ public Qualifier() { ++ } ++ ++ public Qualifier(DerValue val) throws IOException { ++ // needs to override this ++ } ++ ++ /** ++ * Write the PolicyQualifier to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ // needs to override this ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/RDN.java b/org/mozilla/jss/netscape/security/x509/RDN.java +new file mode 100644 +index 00000000..ceb8872a +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/RDN.java +@@ -0,0 +1,304 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.util.Arrays; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * RDNs are a set of {attribute = value} assertions. Some of those ++ * attributes are "distinguished" (unique w/in context). Order is ++ * never relevant. ++ * ++ * Some X.500 names include only a single distinguished attribute ++ * per RDN. This style is currently common. ++ * ++ * Note that DER-encoded RDNs sort AVAs by assertion OID ... so that ++ * when we parse this data we don't have to worry about canonicalizing ++ * it, but we'll need to sort them when we expose the RDN class more. ++ * ++ * @see X500Name ++ * @see AVA ++ * @see LdapDNStrConverter ++ */ ++ ++public class RDN { ++ // public constructors ++ ++ /** ++ * Constructs a RDN from a Ldap DN String with one RDN component ++ * using the global default LdapDNStrConverter. ++ * ++ * @see LdapDNStrConverter ++ * @param rdnString a Ldap DN string with one RDN component, e.g. as ++ * defined in RFC1779. ++ * @exception IOException if error occurs while parsing the string. ++ */ ++ public RDN(String rdnString) ++ throws IOException { ++ RDN rdn = LdapDNStrConverter.getDefault().parseRDN(rdnString); ++ assertion = rdn.getAssertion(); ++ } ++ ++ /** ++ * Like RDN(String) with a DER encoding order given as argument for ++ * Directory Strings. ++ */ ++ public RDN(String rdnString, byte[] tags) ++ throws IOException { ++ RDN rdn = LdapDNStrConverter.getDefault().parseRDN(rdnString, tags); ++ assertion = rdn.getAssertion(); ++ } ++ ++ /** ++ * Constructs a RDN from a Ldap DN string with one RDN component ++ * using the specified Ldap DN Str converter. ++ * For example, RFC1779StrConverter can be passed to parse a Ldap ++ * DN string in RFC1779 format. ++ * ++ * @see LdapDNStrConverter ++ * @param rdnString Ldap DN string. ++ * @param ldapDNStrConverter a LdapDNStrConverter. ++ */ ++ public RDN(String rdnString, LdapDNStrConverter ldapDNStrConverter) ++ throws IOException { ++ RDN rdn = ldapDNStrConverter.parseRDN(rdnString); ++ assertion = rdn.getAssertion(); ++ } ++ ++ /** ++ * Constructs a RDN from a DerValue. ++ * ++ * @param set Der value of a set of AVAs. ++ */ ++ public RDN(DerValue set) throws IOException { ++ if (set.tag != DerValue.tag_Set) ++ throw new CertParseError("X500 RDN"); ++ ++ int j_max = 50; // XXX j_max = f(data)!! ++ int j; ++ int i; ++ ++ AVA[] avas = new AVA[j_max]; ++ ++ // create a temporary array big enough for a huge set of AVA's ++ for (j = 0; j < j_max; j++) { ++ avas[j] = new AVA(set.data); ++ if (set.data.available() == 0) ++ break; ++ } ++ ++ // copy the elements into it ++ if (j >= j_max - 1) { ++ assertion = new AVA[j + 1]; ++ } else { ++ assertion = new AVA[j + 1]; ++ for (i = 0; i < (j + 1); i++) { ++ assertion[i] = avas[i]; ++ } ++ } ++ ++ /* ++ if (set.data.available () != 0) ++ // throw new CertParseError ("X500 RDN 2"); ++ System.out.println (" ... RDN parse, ignored bytes = " ++ + set.data.available ()); ++ */ ++ } ++ ++ /** ++ * Constructs a RDN from a Der Input Stream. ++ * ++ * @param in a Der Input Stream. ++ */ ++ public RDN(DerInputStream in) throws IOException { ++ /* an RDN is a SET of avas */ ++ DerValue avaset[] = in.getSet(1); ++ int i; ++ assertion = new AVA[avaset.length]; ++ for (i = 0; i < assertion.length; i++) ++ assertion[i] = new AVA(avaset[i].data); ++ } ++ ++ /** ++ * Constructs a RDN from an array of AVA. ++ * ++ * @param avas a AVA Array. ++ */ ++ public RDN(AVA avas[]) { ++ assertion = avas.clone(); ++ } ++ ++ /** ++ * convenience method. ++ */ ++ public RDN(Vector avaVector) { ++ int size = avaVector.size(); ++ assertion = new AVA[size]; ++ for (int i = 0; i < size; i++) { ++ assertion[i] = avaVector.elementAt(i); ++ } ++ } ++ ++ /** ++ * returns an array of AVA in the RDN. ++ * ++ * @return array of AVA in this RDN. ++ */ ++ public AVA[] getAssertion() { ++ return assertion.clone(); ++ } ++ ++ /** ++ * returns the number of AVAs in the RDN. ++ * ++ * @return number of AVAs in this RDN. ++ */ ++ public int getAssertionLength() { ++ return assertion.length; ++ } ++ ++ private AVA assertion[]; ++ ++ private class AVAEnumerator implements Enumeration { ++ private int index; ++ ++ public AVAEnumerator() { ++ index = 0; ++ } ++ ++ public boolean hasMoreElements() { ++ return (index < assertion.length); ++ } ++ ++ public AVA nextElement() { ++ if (index >= assertion.length) ++ return null; ++ return assertion[index++]; ++ } ++ } ++ ++ // other public methods. ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + Arrays.hashCode(assertion); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (obj == null) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ RDN other = (RDN) obj; ++ if (!Arrays.equals(assertion, other.assertion)) ++ return false; ++ return true; ++ } ++ ++ DerValue findAttribute(ObjectIdentifier oid) { ++ int i; ++ ++ for (i = 0; i < assertion.length; i++) ++ if (assertion[i].oid.equals(oid)) ++ return assertion[i].value; ++ return null; ++ } ++ ++ /** ++ * Encodes this RDN to a Der output stream. ++ * ++ * @param out the Der Output Stream. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ int i; ++ ++ for (i = 0; i < assertion.length; i++) ++ assertion[i].encode(tmp); ++ out.write(DerValue.tag_Set, tmp); ++ } ++ ++ /** ++ * returns an enumeration of AVAs that make up this RDN. ++ * ++ * @return an enumeration of AVAs that make up this RDN. ++ */ ++ public Enumeration getAVAs() { ++ return new AVAEnumerator(); ++ } ++ ++ /** ++ * Returns a Ldap DN string with one RDN component using the ++ * global default LdapDNStrConverter. ++ * ++ * @see LdapDNStrConverter ++ * @return the Ldap DN String of this RDN. ++ * @exception IOException if an error occurs during the conversion. ++ */ ++ public String toLdapDNString() ++ throws IOException { ++ return LdapDNStrConverter.getDefault().encodeRDN(this); ++ } ++ ++ /** ++ * Returns a Ldap DN String with this RDN component using the specified ++ * LdapDNStrConverter. ++ * ++ * @see LdapDNStrConverter ++ * @param ldapDNStrConverter a LdapDNStrConverter. ++ * @return a Ldap DN String. ++ * @exception IOException if an error occurs in the conversion. ++ */ ++ public String toLdapDNString(LdapDNStrConverter ldapDNStrConverter) ++ throws IOException { ++ return ldapDNStrConverter.encodeRDN(this); ++ } ++ ++ /** ++ * Returns a Ldap DN string with this RDN component using the global ++ * default LdapDNStrConverter. ++ * ++ * @see LdapDNStrConverter ++ * @return the Ldap DN String with this RDN component, null if an error ++ * occurs in the conversion. ++ */ ++ public String toString() { ++ String s; ++ try { ++ s = toLdapDNString(); ++ } catch (IOException e) { ++ return null; ++ } ++ return s; ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/RFC1779StrConverter.java b/org/mozilla/jss/netscape/security/x509/RFC1779StrConverter.java +new file mode 100644 +index 00000000..1919270e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/RFC1779StrConverter.java +@@ -0,0 +1,102 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * Converts a RFC 1779 string to a X500Name, RDN or AVA object and vice versa. ++ * ++ * @see LdapDNStrConverter ++ * @see LdapV3DNStrConverter ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ */ ++ ++public class RFC1779StrConverter extends LdapV3DNStrConverter { ++ // ++ // Constructors. ++ // ++ ++ /** ++ * Constructs a RFC1779StrConverter using the global default ++ * X500NameAttrMap and accepts OIDs not listed in the attribute map. ++ */ ++ public RFC1779StrConverter() { ++ super(); ++ } ++ ++ /** ++ * Constructs a RFC1779StrConverter using the specified X500NameAttrMap ++ * and boolean for whether to accept OIDs not in the X500NameAttrMap. ++ * ++ * @param attributeMap A X500NameAttrMap to use for this converter. ++ * @param doAcceptUnknownOids Accept unregistered attributes, i.e. OIDs ++ * not in the map). ++ */ ++ public RFC1779StrConverter(X500NameAttrMap attributeMap, ++ boolean doAcceptUnknownOids) { ++ super(attributeMap, doAcceptUnknownOids); ++ } ++ ++ // ++ // overriding methods. ++ // ++ ++ /** ++ * Converts a OID to a attribute keyword in a Ldap DN string or ++ * to a "OID.1.2.3.4" string syntax as defined in RFC1779. ++ * ++ * @param oid an ObjectIdentifier. ++ * ++ * @return a attribute keyword or "OID.1.2.3.4" string. ++ * ++ * @exception IOException if an error occurs during the conversion. ++ */ ++ public String encodeOID(ObjectIdentifier oid) ++ throws IOException { ++ String keyword = attrMap.getName(oid); ++ if (keyword == null) ++ if (!acceptUnknownOids) ++ throw new IllegalArgumentException("Unrecognized OID"); ++ else ++ keyword = "OID" + "." + oid.toString(); ++ return keyword; ++ } ++ ++ /** ++ * Converts a attribute value as a DerValue to a string in a ++ * RFC1779 Ldap DN string. ++ * ++ * @param attrValue an attribute value. ++ * @param oid ObjectIdentifier for the attribute. ++ * @return a string in RFC1779 syntax. ++ * @exception IOException if an error occurs during the conversion. ++ */ ++ public String encodeValue(DerValue attrValue, ObjectIdentifier oid) ++ throws IOException { ++ String s = super.encodeValue(attrValue, oid); ++ if (s.indexOf('\n') != -1) ++ return "\"" + s + "\""; ++ else ++ return s; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/RFC822Name.java b/org/mozilla/jss/netscape/security/x509/RFC822Name.java +new file mode 100644 +index 00000000..e33ff330 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/RFC822Name.java +@@ -0,0 +1,85 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class implements the RFC822Name as required by the GeneralNames ++ * ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.3 ++ * @see GeneralName ++ * @see GeneralNames ++ * @see GeneralNameInterface ++ */ ++public class RFC822Name implements GeneralNameInterface { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -1453025303548809007L; ++ private String name; ++ ++ /** ++ * Create the RFC822Name object from the passed encoded Der value. ++ * ++ * @param derValue the encoded DER RFC822Name. ++ * @exception IOException on error. ++ */ ++ public RFC822Name(DerValue derValue) throws IOException { ++ name = derValue.getIA5String(); ++ } ++ ++ /** ++ * Create the RFC822Name object with the specified name. ++ * ++ * @param name the RFC822Name. ++ */ ++ public RFC822Name(String name) { ++ this.name = name; ++ } ++ ++ /** ++ * Return the type of the GeneralName. ++ */ ++ public int getType() { ++ return (GeneralNameInterface.NAME_RFC822); ++ } ++ ++ /** ++ * Encode the RFC822 name into the DerOutputStream. ++ * ++ * @param out the DER stream to encode the RFC822Name to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putIA5String(name); ++ } ++ ++ /** ++ * Convert the name into user readable string. ++ */ ++ public String toString() { ++ return ("RFC822Name: " + name); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/ReasonFlags.java b/org/mozilla/jss/netscape/security/x509/ReasonFlags.java +new file mode 100755 +index 00000000..d1e93e84 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/ReasonFlags.java +@@ -0,0 +1,283 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the CRL Reason Flags. ++ * ++ *

++ * This extension, if present, defines the identifies the reason for the certificate revocation. ++ * ++ * @author Hemma Prafullchandra ++ * @version 1.3 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class ReasonFlags { ++ ++ /** ++ * Reasons ++ */ ++ public static final String UNUSED = "unused"; ++ public static final String KEY_COMPROMISE = "key_compromise"; ++ public static final String CA_COMPROMISE = "ca_compromise"; ++ public static final String AFFLIATION_CHANGED = "affliation_changed"; ++ public static final String SUPERSEDED = "superseded"; ++ public static final String CESSATION_OF_OPERATION = "cessation_of_operation"; ++ public static final String CERTIFICATION_HOLD = "certification_hold"; ++ public static final String PRIVILEGE_WITHDRAWN = "privilege_withdrawn"; ++ public static final String AA_COMPROMISE = "aa_compromise"; ++ ++ // Private data members ++ private boolean[] bitString; ++ ++ /** ++ * Check if bit is set. ++ * ++ * @param position the position in the bit string to check. ++ */ ++ private boolean isSet(int position) { ++ return bitString[position]; ++ } ++ ++ /** ++ * Set the bit at the specified position. ++ */ ++ private void set(int position, boolean val) { ++ // enlarge bitString if necessary ++ if (position >= bitString.length) { ++ boolean[] tmp = new boolean[position + 1]; ++ System.arraycopy(bitString, 0, tmp, 0, bitString.length); ++ bitString = tmp; ++ } ++ bitString[position] = val; ++ } ++ ++ /** ++ * Create a ReasonFlags with the passed bit settings. ++ * ++ * @param reasons the bits to be set for the ReasonFlags. ++ */ ++ public ReasonFlags(byte[] reasons) { ++ bitString = new BitArray(reasons.length * 8, reasons).toBooleanArray(); ++ } ++ ++ /** ++ * Create a ReasonFlags with the passed bit settings. ++ * ++ * @param reasons the bits to be set for the ReasonFlags. ++ */ ++ public ReasonFlags(boolean[] reasons) { ++ this.bitString = reasons; ++ } ++ ++ /** ++ * Create a ReasonFlags with the passed bit settings. ++ * ++ * @param reasons the bits to be set for the ReasonFlags. ++ */ ++ public ReasonFlags(BitArray reasons) { ++ this.bitString = reasons.toBooleanArray(); ++ } ++ ++ /** ++ * Create the object from the passed DER encoded value. ++ * ++ * @param in the DerInputStream to read the ReasonFlags from. ++ * @exception IOException on decoding errors. ++ */ ++ public ReasonFlags(DerInputStream in) throws IOException { ++ DerValue derVal = in.getDerValue(); ++ this.bitString = derVal.getUnalignedBitString(true).toBooleanArray(); ++ } ++ ++ /** ++ * Create the object from the passed DER encoded value. ++ * ++ * @param derVal the DerValue decoded from the stream. ++ * @exception IOException on decoding errors. ++ */ ++ public ReasonFlags(DerValue derVal) throws IOException { ++ this.bitString = derVal.getUnalignedBitString(true).toBooleanArray(); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ if (!(obj instanceof Boolean)) { ++ throw new IOException("Attribute must be of type Boolean."); ++ } ++ boolean val = ((Boolean) obj).booleanValue(); ++ if (name.equalsIgnoreCase(UNUSED)) { ++ set(0, val); ++ } else if (name.equalsIgnoreCase(KEY_COMPROMISE)) { ++ set(1, val); ++ } else if (name.equalsIgnoreCase(CA_COMPROMISE)) { ++ set(2, val); ++ } else if (name.equalsIgnoreCase(AFFLIATION_CHANGED)) { ++ set(3, val); ++ } else if (name.equalsIgnoreCase(SUPERSEDED)) { ++ set(4, val); ++ } else if (name.equalsIgnoreCase(CESSATION_OF_OPERATION)) { ++ set(5, val); ++ } else if (name.equalsIgnoreCase(CERTIFICATION_HOLD)) { ++ set(6, val); ++ } else if (name.equalsIgnoreCase(PRIVILEGE_WITHDRAWN)) { ++ set(7, val); ++ } else if (name.equalsIgnoreCase(AA_COMPROMISE)) { ++ set(8, val); ++ } else { ++ throw new IOException("Name not recognized by ReasonFlags"); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(UNUSED)) { ++ return Boolean.valueOf(isSet(0)); ++ } else if (name.equalsIgnoreCase(KEY_COMPROMISE)) { ++ return Boolean.valueOf(isSet(1)); ++ } else if (name.equalsIgnoreCase(CA_COMPROMISE)) { ++ return Boolean.valueOf(isSet(2)); ++ } else if (name.equalsIgnoreCase(AFFLIATION_CHANGED)) { ++ return Boolean.valueOf(isSet(3)); ++ } else if (name.equalsIgnoreCase(SUPERSEDED)) { ++ return Boolean.valueOf(isSet(4)); ++ } else if (name.equalsIgnoreCase(CESSATION_OF_OPERATION)) { ++ return Boolean.valueOf(isSet(5)); ++ } else if (name.equalsIgnoreCase(CERTIFICATION_HOLD)) { ++ return Boolean.valueOf(isSet(6)); ++ } else if (name.equalsIgnoreCase(PRIVILEGE_WITHDRAWN)) { ++ return Boolean.valueOf(isSet(7)); ++ } else if (name.equalsIgnoreCase(AA_COMPROMISE)) { ++ return Boolean.valueOf(isSet(8)); ++ } else { ++ throw new IOException("Name not recognized by ReasonFlags"); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(UNUSED)) { ++ set(0, false); ++ } else if (name.equalsIgnoreCase(KEY_COMPROMISE)) { ++ set(1, false); ++ } else if (name.equalsIgnoreCase(CA_COMPROMISE)) { ++ set(2, false); ++ } else if (name.equalsIgnoreCase(AFFLIATION_CHANGED)) { ++ set(3, false); ++ } else if (name.equalsIgnoreCase(SUPERSEDED)) { ++ set(4, false); ++ } else if (name.equalsIgnoreCase(CESSATION_OF_OPERATION)) { ++ set(5, false); ++ } else if (name.equalsIgnoreCase(CERTIFICATION_HOLD)) { ++ set(6, false); ++ } else if (name.equalsIgnoreCase(PRIVILEGE_WITHDRAWN)) { ++ set(7, false); ++ } else if (name.equalsIgnoreCase(AA_COMPROMISE)) { ++ set(8, false); ++ } else { ++ throw new IOException("Name not recognized by ReasonFlags"); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the ReasonFlags. ++ */ ++ public String toString() { ++ String s = super.toString() + "Reason Flags [\n"; ++ ++ try { ++ if (isSet(0)) { ++ s += " Unused\n"; ++ } ++ if (isSet(1)) { ++ s += " Key Compromise\n"; ++ } ++ if (isSet(2)) { ++ s += " CA_Compromise\n"; ++ } ++ if (isSet(3)) { ++ s += " Affiliation_Changed\n"; ++ } ++ if (isSet(4)) { ++ s += " Superseded\n"; ++ } ++ if (isSet(5)) { ++ s += " Cessation Of Operation\n"; ++ } ++ if (isSet(6)) { ++ s += " Certificate Hold\n"; ++ } ++ if (isSet(7)) { ++ s += " Privilege Withdrawn\n"; ++ } ++ if (isSet(8)) { ++ s += " AA Compromise\n"; ++ } ++ } catch (ArrayIndexOutOfBoundsException ex) { ++ } ++ ++ s += "]\n"; ++ ++ return (s); ++ } ++ ++ /** ++ * Write the extension to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putUnalignedBitString(new BitArray(this.bitString)); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getElements() { ++ Vector elements = new Vector(); ++ elements.addElement(UNUSED); ++ elements.addElement(KEY_COMPROMISE); ++ elements.addElement(CA_COMPROMISE); ++ elements.addElement(AFFLIATION_CHANGED); ++ elements.addElement(SUPERSEDED); ++ elements.addElement(CESSATION_OF_OPERATION); ++ elements.addElement(CERTIFICATION_HOLD); ++ elements.addElement(PRIVILEGE_WITHDRAWN); ++ elements.addElement(AA_COMPROMISE); ++ ++ return (elements.elements()); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/RevocationReason.java b/org/mozilla/jss/netscape/security/x509/RevocationReason.java +new file mode 100644 +index 00000000..32eb3600 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/RevocationReason.java +@@ -0,0 +1,129 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.Serializable; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.LinkedHashMap; ++import java.util.Map; ++ ++/** ++ * Represent the enumerated type used in CRLReason Extension of CRL entry. ++ * ++ * ++ * @author galperin ++ * @version $Revision$, $Date$ ++ */ ++ ++public final class RevocationReason implements Serializable { ++ private static final long serialVersionUID = -2582403666913588806L; ++ ++ public static final Collection INSTANCES = new ArrayList(); ++ public static final Map CODES = new LinkedHashMap(); ++ public static final Map LABELS = new LinkedHashMap(); ++ ++ /** ++ * Reasons ++ */ ++ public static final RevocationReason UNSPECIFIED = new RevocationReason(0, "Unspecified"); ++ public static final RevocationReason KEY_COMPROMISE = new RevocationReason(1, "Key_Compromise"); ++ public static final RevocationReason CA_COMPROMISE = new RevocationReason(2, "CA_Compromise"); ++ public static final RevocationReason AFFILIATION_CHANGED = new RevocationReason(3, "Affiliation_Changed"); ++ public static final RevocationReason SUPERSEDED = new RevocationReason(4, "Superseded"); ++ public static final RevocationReason CESSATION_OF_OPERATION = new RevocationReason(5, "Cessation_of_Operation"); ++ public static final RevocationReason CERTIFICATE_HOLD = new RevocationReason(6, "Certificate_Hold"); ++ public static final RevocationReason REMOVE_FROM_CRL = new RevocationReason(8, "Remove_from_CRL"); ++ public static final RevocationReason PRIVILEGE_WITHDRAWN = new RevocationReason(9, "Privilege_Withdrawn"); ++ public static final RevocationReason AA_COMPROMISE = new RevocationReason(10, "AA_Compromise"); ++ ++ // Private data members ++ private int code; ++ private String label; ++ ++ /** ++ * Create a RevocationReason with the passed integer value and string label. ++ * ++ * @param reason integer value of the enumeration alternative. ++ * @param label string value of the enumeration alternative. ++ */ ++ private RevocationReason(int reason, String label) { ++ this.code = reason; ++ this.label = label; ++ ++ INSTANCES.add(this); ++ CODES.put(reason, this); ++ LABELS.put(label.toLowerCase(), this); ++ } ++ ++ public int getCode() { ++ return code; ++ } ++ ++ public String getLabel() { ++ return label; ++ } ++ ++ public static RevocationReason fromInt(int reason) { ++ return valueOf(reason); ++ } ++ ++ public static RevocationReason valueOf(int reason) { ++ return CODES.get(reason); ++ } ++ ++ public static RevocationReason valueOf(String string) { ++ return LABELS.get(string.toLowerCase()); ++ } ++ ++ public int toInt() { ++ return code; ++ } ++ ++ public String toString() { ++ return label; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (obj == null) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ RevocationReason other = (RevocationReason) obj; ++ if (code != other.code) ++ return false; ++ if (label == null) { ++ if (other.label != null) ++ return false; ++ } else if (!label.equals(other.label)) ++ return false; ++ return true; ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + code; ++ result = prime * result + ((label == null) ? 0 : label.hashCode()); ++ return result; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/RevocationReasonAdapter.java b/org/mozilla/jss/netscape/security/x509/RevocationReasonAdapter.java +new file mode 100644 +index 00000000..b3d7d49c +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/RevocationReasonAdapter.java +@@ -0,0 +1,38 @@ ++// --- 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) 2012 Red Hat, Inc. ++// All rights reserved. ++// --- END COPYRIGHT BLOCK --- ++package org.mozilla.jss.netscape.security.x509; ++ ++import javax.xml.bind.annotation.adapters.XmlAdapter; ++ ++import org.apache.commons.lang.StringUtils; ++ ++/** ++ * The RevocationReasonAdapter class provides custom marshaling for RevocationReason. ++ * ++ * @author Endi S. Dewata ++ */ ++public class RevocationReasonAdapter extends XmlAdapter { ++ ++ public RevocationReason unmarshal(String value) throws Exception { ++ return StringUtils.isEmpty(value) ? null : RevocationReason.valueOf(value); ++ } ++ ++ public String marshal(RevocationReason value) throws Exception { ++ return value == null ? null : value.toString(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/RevokedCertImpl.java b/org/mozilla/jss/netscape/security/x509/RevokedCertImpl.java +new file mode 100755 +index 00000000..40bd630e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/RevokedCertImpl.java +@@ -0,0 +1,447 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.Serializable; ++import java.math.BigInteger; ++import java.security.cert.CRLException; ++import java.util.Date; ++import java.util.Enumeration; ++import java.util.LinkedHashSet; ++import java.util.Set; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ *

++ * Abstract class for a revoked certificate in a CRL. This class is for each entry in the ++ * revokedCertificates, so it deals with the inner SEQUENCE. The ASN.1 definition for this is: ++ * ++ *

++ * revokedCertificates    SEQUENCE OF SEQUENCE  {
++ *     userCertificate    CertificateSerialNumber,
++ *     revocationDate     ChoiceOfTime,
++ *     crlEntryExtensions Extensions OPTIONAL
++ *                        -- if present, must be v2
++ * }  OPTIONAL
++ *
++ * CertificateSerialNumber  ::=  INTEGER
++ *
++ * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
++ *
++ * Extension  ::=  SEQUENCE  {
++ *     extnId        OBJECT IDENTIFIER,
++ *     critical      BOOLEAN DEFAULT FALSE,
++ *     extnValue     OCTET STRING
++ *                   -- contains a DER encoding of a value
++ *                   -- of the type registered for use with
++ *                   -- the extnId object identifier value
++ * }
++ * 
++ * ++ * @author Hemma Prafullchandra ++ * @version 1.6 97/12/10 ++ */ ++ ++public class RevokedCertImpl extends RevokedCertificate implements Serializable { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -3449642360223397701L; ++ ++ private SerialNumber serialNumber; ++ private Date revocationDate; ++ private CRLExtensions extensions = null; ++ private byte[] revokedCert; ++ private final static boolean isExplicit = false; ++ ++ /** ++ * Default constructor. ++ */ ++ public RevokedCertImpl() { ++ } ++ ++ /** ++ * Constructs a revoked certificate entry using the serial number and ++ * revocation date. ++ * ++ * @param num ++ * the serial number of the revoked certificate. ++ * @param date ++ * the Date on which revocation took place. ++ */ ++ public RevokedCertImpl(BigInteger num, Date date) { ++ this.serialNumber = new SerialNumber(num); ++ this.revocationDate = date; ++ } ++ ++ /** ++ * Constructs a revoked certificate entry using the serial number, ++ * revocation date and the entry extensions. ++ * ++ * @param num ++ * the serial number of the revoked certificate. ++ * @param date ++ * the Date on which revocation took place. ++ * @param crlEntryExts ++ * the extensions for this entry. ++ */ ++ public RevokedCertImpl(BigInteger num, Date date, CRLExtensions crlEntryExts) { ++ this.serialNumber = new SerialNumber(num); ++ this.revocationDate = date; ++ this.extensions = crlEntryExts; ++ } ++ ++ public byte[] getEncoded() throws CRLException { ++ // XXX NOT IMPLEMENTED ++ if (revokedCert == null) { ++ DerOutputStream os = new DerOutputStream(); ++ try { ++ encode(os); ++ } catch (Exception e) { ++ // revokedCert = null; ++ } ++ revokedCert = os.toByteArray(); ++ } ++ return revokedCert; ++ } ++ ++ public boolean hasUnsupportedCriticalExtension() { ++ // XXX NOT IMPLEMENTED ++ return true; ++ } ++ ++ /** ++ * Sets extensions for this impl. ++ * ++ * @param crlEntryExts ++ * CRLExtensions ++ */ ++ public void setExtensions(CRLExtensions crlEntryExts) { ++ this.extensions = crlEntryExts; ++ } ++ ++ /** ++ * Unmarshals a revoked certificate from its encoded form. ++ * ++ * @param revokedCert ++ * the encoded bytes. ++ * @exception CRLException ++ * on parsing errors. ++ * @exception X509ExtensionException ++ * on extension handling errors. ++ */ ++ public RevokedCertImpl(byte[] revokedCert) throws CRLException, ++ X509ExtensionException { ++ try { ++ DerValue derValue = new DerValue(revokedCert); ++ parse(derValue); ++ } catch (IOException e) { ++ throw new CRLException("Parsing error: " + e.toString()); ++ } ++ } ++ ++ /** ++ * Unmarshals a revoked certificate from its encoded form. ++ * ++ * @param derValue ++ * the DER value containing the revoked certificate. ++ * @exception CRLException ++ * on parsing errors. ++ * @exception X509ExtensionException ++ * on extension handling errors. ++ */ ++ public RevokedCertImpl(DerValue derValue) throws CRLException, ++ X509ExtensionException { ++ parse(derValue); ++ } ++ ++ /** ++ * Returns true if this revoked certificate entry has extensions, otherwise ++ * false. ++ * ++ * @return true if this CRL entry has extensions, otherwise false. ++ */ ++ public boolean hasExtensions() { ++ if (extensions == null) ++ return false; ++ else ++ return true; ++ } ++ ++ /** ++ * Decode a revoked certificate from an input stream. ++ * ++ * @param inStrm ++ * an input stream holding at least one revoked certificate ++ * @exception CRLException ++ * on parsing errors. ++ * @exception X509ExtensionException ++ * on extension handling errors. ++ */ ++ public void decode(InputStream inStrm) throws CRLException, ++ X509ExtensionException { ++ try { ++ DerValue derValue = new DerValue(inStrm); ++ parse(derValue); ++ } catch (IOException e) { ++ throw new CRLException("Parsing error: " + e.toString()); ++ } ++ } ++ ++ /** ++ * Encodes the revoked certificate to an output stream. ++ * ++ * @param outStrm ++ * an output stream to which the encoded revoked certificate is ++ * written. ++ * @exception CRLException ++ * on encoding errors. ++ * @exception X509ExtensionException ++ * on extension handling errors. ++ */ ++ public void encode(DerOutputStream outStrm) throws CRLException, ++ X509ExtensionException { ++ try (DerOutputStream seq = new DerOutputStream()) { ++ if (revokedCert == null) { ++ DerOutputStream tmp = new DerOutputStream(); ++ // sequence { serialNumber, revocationDate, extensions } ++ serialNumber.encode(tmp); ++ ++ // from 2050 should encode GeneralizedTime ++ tmp.putUTCTime(revocationDate); ++ ++ if (extensions != null) ++ extensions.encode(tmp, isExplicit); ++ ++ seq.write(DerValue.tag_Sequence, tmp); ++ ++ revokedCert = seq.toByteArray(); ++ } ++ outStrm.write(revokedCert); ++ } catch (IOException e) { ++ throw new CRLException("Encoding error: " + e.toString()); ++ } ++ } ++ ++ /** ++ * Gets the serial number for this RevokedCertificate, the userCertificate. ++ * ++ * @return the serial number. ++ */ ++ public BigInteger getSerialNumber() { ++ return serialNumber.getNumber().toBigInteger(); ++ } ++ ++ /** ++ * Gets the revocation date for this RevokedCertificate, the revocationDate. ++ * ++ * @return the revocation date. ++ */ ++ public Date getRevocationDate() { ++ return (new Date(revocationDate.getTime())); ++ } ++ ++ /** ++ * Returns extensions for this impl. ++ * ++ * @return the CRLExtensions ++ */ ++ public CRLExtensions getExtensions() { ++ return extensions; ++ } ++ ++ /** ++ * Returns a printable string of this revoked certificate. ++ * ++ * @return value of this revoked certificate in a printable form. ++ */ ++ public String toString() { ++ StringBuffer sb = new StringBuffer(serialNumber.toString() + " On: " + revocationDate.toString()); ++ ++ if (extensions != null) { ++ sb.append("\n"); ++ for (int i = 0; i < extensions.size(); i++) ++ sb.append("Entry Extension[" + i + "]: " ++ + (extensions.elementAt(i)).toString()); ++ } ++ sb.append("\n"); ++ return (sb.toString()); ++ } ++ ++ /** ++ * Gets a Set of the extension(s) marked CRITICAL in the ++ * RevokedCertificate by OID strings. ++ * ++ * @return a set of the extension oid strings in the ++ * Object that are marked critical. ++ */ ++ public Set getCriticalExtensionOIDs() { ++ if (extensions == null) ++ return null; ++ Set extSet = new LinkedHashSet(); ++ Extension ex; ++ for (Enumeration e = extensions.getElements(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ if (ex.isCritical()) ++ extSet.add(ex.getExtensionId().toString()); ++ } ++ return extSet; ++ } ++ ++ /** ++ * Gets a Set of the extension(s) marked NON-CRITICAL in the ++ * RevokedCertificate by OID strings. ++ * ++ * @return a set of the extension oid strings in the ++ * Object that are marked critical. ++ */ ++ public Set getNonCriticalExtensionOIDs() { ++ if (extensions == null) ++ return null; ++ Set extSet = new LinkedHashSet(); ++ Extension ex; ++ for (Enumeration e = extensions.getElements(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ if (!ex.isCritical()) ++ extSet.add(ex.getExtensionId().toString()); ++ } ++ return extSet; ++ } ++ ++ /** ++ * Gets the DER encoded OCTET string for the extension value ++ * (extnValue) identified by the passed in oid String. ++ * The oid string is ++ * represented by a set of positive whole number separated ++ * by ".", that means,
++ * <positive whole number>.<positive whole number>.<positive ++ * whole number>.<...> ++ * ++ * @param oid the Object Identifier value for the extension. ++ * @return the DER encoded octet string of the extension value. ++ */ ++ public byte[] getExtensionValue(String oid) { ++ if (extensions == null) ++ return null; ++ try (DerOutputStream out = new DerOutputStream()) { ++ String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); ++ Extension crlExt = null; ++ ++ if (extAlias == null) { // may be unknown ++ ObjectIdentifier findOID = new ObjectIdentifier(oid); ++ Extension ex = null; ++ ObjectIdentifier inCertOID; ++ for (Enumeration e = extensions.getElements(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ inCertOID = ex.getExtensionId(); ++ if (inCertOID.equals(findOID)) { ++ crlExt = ex; ++ break; ++ } ++ } ++ } else ++ crlExt = extensions.get(extAlias); ++ if (crlExt == null) ++ return null; ++ byte[] extData = crlExt.getExtensionValue(); ++ if (extData == null) ++ return null; ++ ++ out.putOctetString(extData); ++ return out.toByteArray(); ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ private void parse(DerValue derVal) ++ throws CRLException, X509ExtensionException { ++ ++ if (derVal.tag != DerValue.tag_Sequence) { ++ throw new CRLException("Invalid encoded RevokedCertificate, " + ++ "starting sequence tag missing."); ++ } ++ if (derVal.data.available() == 0) ++ throw new CRLException("No data encoded for RevokedCertificates"); ++ ++ // serial number ++ try { ++ DerInputStream in = derVal.toDerInputStream(); ++ DerValue val = in.getDerValue(); ++ this.serialNumber = new SerialNumber(val); ++ } catch (IOException e) { ++ throw new CRLException("Parsing Serial Number error: " ++ + e.toString()); ++ } ++ ++ // revocationDate ++ try { ++ int nextByte = derVal.data.peekByte(); ++ if ((byte) nextByte == DerValue.tag_UtcTime) { ++ this.revocationDate = derVal.data.getUTCTime(); ++ } else if ((byte) nextByte == DerValue.tag_GeneralizedTime) { ++ this.revocationDate = derVal.data.getGeneralizedTime(); ++ } else { ++ throw new CRLException("Invalid encoding for RevokedCertificates"); ++ } ++ } catch (IOException e) { ++ throw new CRLException("Parsing Revocation Date error: " ++ + e.toString()); ++ } ++ ++ if (derVal.data.available() == 0) ++ return; // no extensions ++ ++ // crlEntryExtensions ++ try { ++ this.extensions = new CRLExtensions(derVal.toDerInputStream()); ++ } catch (IOException e) { ++ throw new CRLException("Parsing CRL Entry Extensions error: " ++ + e.toString()); ++ } ++ } ++ ++ /** ++ * Serialization write ... X.509 certificates serialize as themselves, and ++ * they're parsed when they get read back. (Actually they serialize as some ++ * type data from the serialization subsystem, then the cert data.) ++ */ ++ private void writeObject(ObjectOutputStream stream) throws CRLException, X509ExtensionException, IOException { ++ DerOutputStream dos = new DerOutputStream(); ++ encode(dos); ++ dos.derEncode(stream); ++ } ++ ++ /** ++ * Serialization read ... X.509 certificates serialize as themselves, and ++ * they're parsed when they get read back. ++ */ ++ private void readObject(ObjectInputStream stream) throws CRLException, X509ExtensionException, IOException { ++ decode(stream); ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/RevokedCertificate.java b/org/mozilla/jss/netscape/security/x509/RevokedCertificate.java +new file mode 100644 +index 00000000..a08ee3c6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/RevokedCertificate.java +@@ -0,0 +1,95 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.math.BigInteger; ++import java.security.cert.X509CRL; ++import java.security.cert.X509CRLEntry; ++import java.util.Date; ++ ++/** ++ *

++ * Abstract class for a revoked certificate in a CRL (Certificate Revocation List). ++ * ++ * The ASN.1 definition for revokedCertificates is: ++ * ++ *

++ *  revokedCertificates    SEQUENCE OF SEQUENCE  {
++ *      userCertificate    CertificateSerialNumber,
++ *      revocationDate     ChoiceOfTime,
++ *      crlEntryExtensions Extensions OPTIONAL
++ *                         -- if present, must be v2
++ *  }  OPTIONAL
++ * 

++ * CertificateSerialNumber ::= INTEGER ++ *

++ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension ++ *

++ * Extension ::= SEQUENCE { ++ * extnId OBJECT IDENTIFIER, ++ * critical BOOLEAN DEFAULT FALSE, ++ * extnValue OCTET STRING ++ * -- contains a DER encoding of a value ++ * -- of the type registered for use with ++ * -- the extnId object identifier value ++ * } ++ *

++ * ++ * @see X509CRL ++ * ++ * @author Hemma Prafullchandra ++ * @version 1.4 97/12/10 ++ */ ++ ++public abstract class RevokedCertificate extends X509CRLEntry { ++ /* implements X509Extension { */ ++ ++ /** ++ * Gets the serial number for this RevokedCertificate, ++ * the userCertificate. ++ * ++ * @return the serial number. ++ */ ++ public abstract BigInteger getSerialNumber(); ++ ++ /** ++ * Gets the revocation date for this RevokedCertificate, ++ * the revocationDate. ++ * ++ * @return the revocation date. ++ */ ++ public abstract Date getRevocationDate(); ++ ++ /** ++ * Returns true if this revoked certificate entry has ++ * extensions. ++ * ++ * @return true if this entry has extensions, false otherwise. ++ */ ++ public abstract boolean hasExtensions(); ++ ++ /** ++ * Returns a string representation of this revoked certificate. ++ * ++ * @return a string representation of this revoked certificate. ++ */ ++ public abstract String toString(); ++ ++ public abstract CRLExtensions getExtensions(); ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/SerialNumber.java b/org/mozilla/jss/netscape/security/x509/SerialNumber.java +new file mode 100644 +index 00000000..368b510a +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/SerialNumber.java +@@ -0,0 +1,127 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.Serializable; ++import java.math.BigInteger; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the SerialNumber class used by certificates. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.5 ++ */ ++public class SerialNumber implements Serializable { ++ ++ private static final long serialVersionUID = 1600956411497203535L; ++ private BigInt serialNum; ++ ++ // Construct the class from the DerValue ++ private void construct(DerValue derVal) throws IOException { ++ serialNum = derVal.getInteger(); ++ if (derVal.data.available() != 0) { ++ throw new IOException("Excess SerialNumber data"); ++ } ++ } ++ ++ /** ++ * The default constructor for this class using BigInteger. ++ * ++ * @param num the BigInteger number used to create the serial number. ++ */ ++ public SerialNumber(BigInteger num) { ++ serialNum = new BigInt(num); ++ } ++ ++ public SerialNumber(BigInt num) { ++ serialNum = num; ++ } ++ ++ /** ++ * The default constructor for this class using int. ++ * ++ * @param num the BigInteger number used to create the serial number. ++ */ ++ public SerialNumber(int num) { ++ serialNum = new BigInt(num); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the SerialNumber from. ++ * @exception IOException on decoding errors. ++ */ ++ public SerialNumber(DerInputStream in) throws IOException { ++ DerValue derVal = in.getDerValue(); ++ construct(derVal); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DerValue. ++ * ++ * @param val the DerValue to read the SerialNumber from. ++ * @exception IOException on decoding errors. ++ */ ++ public SerialNumber(DerValue val) throws IOException { ++ construct(val); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed stream. ++ * ++ * @param in the InputStream to read the SerialNumber from. ++ * @exception IOException on decoding errors. ++ */ ++ public SerialNumber(InputStream in) throws IOException { ++ DerValue derVal = new DerValue(in); ++ construct(derVal); ++ } ++ ++ /** ++ * Return the SerialNumber as user readable string. ++ */ ++ public String toString() { ++ return ("SerialNumber: [" + serialNum.toString() + "]"); ++ } ++ ++ /** ++ * Encode the SerialNumber in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putInteger(serialNum); ++ } ++ ++ /** ++ * Return the serial number. ++ */ ++ public BigInt getNumber() { ++ return (serialNum); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/SubjectAlternativeNameExtension.java b/org/mozilla/jss/netscape/security/x509/SubjectAlternativeNameExtension.java +new file mode 100644 +index 00000000..37ee3492 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/SubjectAlternativeNameExtension.java +@@ -0,0 +1,257 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This represents the Subject Alternative Name Extension. ++ * ++ * This extension, if present, allows the subject to specify multiple ++ * alternative names. ++ * ++ *

++ * Extensions are represented as a sequence of the extension identifier (Object Identifier), a boolean flag stating ++ * whether the extension is to be treated as being critical and the extension value itself (this is again a DER encoding ++ * of the extension value). ++ *

++ * The ASN.1 syntax for this is: ++ * ++ *

++ * SubjectAltName ::= GeneralNames
++ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
++ * 
++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.9 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class SubjectAlternativeNameExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -4022446008355607196L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = ++ "x509.info.extensions.SubjectAlternativeName"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "SubjectAlternativeName"; ++ public static final String SUBJECT_NAME = "subject_name"; ++ ++ // private data members ++ GeneralNames names; ++ ++ // Encode this extension ++ private void encodeThis() throws IOException { ++ DerOutputStream os = new DerOutputStream(); ++ try { ++ names.encode(os); ++ } catch (GeneralNamesException e) { ++ throw new IOException("SubjectAlternativeName: " + e); ++ } ++ extensionValue = os.toByteArray(); ++ } ++ ++ /** ++ * Create a SubjectAlternativeNameExtension with the passed GeneralNames. ++ * ++ * @param names the GeneralNames for the subject. ++ * @exception IOException on error. ++ */ ++ public SubjectAlternativeNameExtension(boolean critical, GeneralNames names) ++ throws IOException { ++ this.names = names; ++ this.extensionId = PKIXExtensions.SubjectAlternativeName_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ public SubjectAlternativeNameExtension(GeneralNames names) ++ throws IOException { ++ this.names = names; ++ this.extensionId = PKIXExtensions.SubjectAlternativeName_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create a default SubjectAlternativeNameExtension. ++ */ ++ public SubjectAlternativeNameExtension() { ++ extensionId = PKIXExtensions.SubjectAlternativeName_Id; ++ critical = false; ++ names = new GeneralNames(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public SubjectAlternativeNameExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.SubjectAlternativeName_Id; ++ this.critical = critical.booleanValue(); ++ ++ if (!(value instanceof byte[])) ++ throw new IOException("SubjectAlternativeName: " ++ + "Illegal argument type"); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ System.arraycopy(value, 0, extValue, 0, len); ++ ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ try { ++ names = new GeneralNames(val); ++ } catch (GeneralNamesException e) { ++ throw new IOException("SubjectAlternativeName: " + e, e); ++ } ++ } ++ ++ /** ++ * Returns a printable representation of the SubjectAlternativeName. ++ */ ++ public String toString() { ++ if (names == null) ++ return ""; ++ String s = super.toString() + "SubjectAlternativeName [\n" ++ + names.toString() + "]\n"; ++ return (s); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = PKIXExtensions.SubjectAlternativeName_Id; ++ //critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(SUBJECT_NAME)) { ++ if (!(obj instanceof GeneralNames)) { ++ throw new IOException("Attribute value should be of " + ++ "type GeneralNames."); ++ } ++ names = (GeneralNames) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectAlternativeName."); ++ } ++ } ++ ++ /** ++ * Set the GeneralNames of this extension. ++ */ ++ public void setGeneralNames(GeneralNames names) { ++ clearValue(); ++ this.names = names; ++ } ++ ++ /** ++ * Get the GeneralNames of this extension. ++ */ ++ public GeneralNames getGeneralNames() { ++ return names; ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(SUBJECT_NAME)) { ++ return (names); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectAlternativeName."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(SUBJECT_NAME)) { ++ names = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectAlternativeName."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(SUBJECT_NAME); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/SubjectDirAttributesExtension.java b/org/mozilla/jss/netscape/security/x509/SubjectDirAttributesExtension.java +new file mode 100644 +index 00000000..fda86f87 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/SubjectDirAttributesExtension.java +@@ -0,0 +1,289 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class represents the Subject Directory Attributes Extension. ++ * ++ *

++ * The subject directory attributes extension is not recommended as an essential part of this profile, but it may be ++ * used in local environments. This extension MUST be non-critical. ++ * ++ *

++ * The ASN.1 syntax for this extension is:
++ *
++ *    SubjectDirectoryAttributes ::= SEQUENCE (1..MAX) OF Attribute
++ *
++ *    Attribute	::= SEQUENCE {
++ * type		AttributeType,
++ * 	value		SET OF AttributeValue
++ *              	-- at least one value is required --}
++ *
++ *    AttributeType	::= OBJECT IDENTIFIER
++ *
++ *    AttributeValue	::= ANY
++ *
++ * 
++ * ++ * @author Christine Ho ++ * @version 1.7 ++ * ++ * @see CertAttrSet ++ * @see Extension ++ */ ++public class SubjectDirAttributesExtension extends Extension ++ implements CertAttrSet { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -1215458115428197688L; ++ ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ //public static final String IDENT = "x509.info.extensions.SubjectDirectoryAttributes"; ++ public static final String IDENT = "Subject Directory Attributes"; ++ ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "SubjectDirectoryAttributes"; ++ ++ // Private data members ++ private Vector attrList = new Vector(); ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ try (DerOutputStream out = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ //encoding the attributes ++ Enumeration attrs = attrList.elements(); ++ while (attrs.hasMoreElements()) { ++ Attribute attr = attrs.nextElement(); ++ attr.encode(tmp); ++ } ++ ++ out.write(DerValue.tag_SequenceOf, tmp); ++ this.extensionValue = out.toByteArray(); ++ } ++ } ++ ++ // Decode this extension value ++ private void decodeThis(DerValue derVal) throws IOException { ++ ++ if (derVal.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for " + ++ "Subject Directory Attribute extension."); ++ } ++ ++ if (derVal.data.available() == 0) { ++ throw new IOException(NAME + " No data available in " ++ + "passed DER encoded value."); ++ } ++ ++ // Decode all the Attributes ++ while (derVal.data.available() != 0) { ++ DerValue encAttr = derVal.data.getDerValue(); ++ Attribute attr = new Attribute(encAttr); ++ attrList.addElement(attr); ++ } ++ } ++ ++ /** ++ * Default constructor for this object. ++ * ++ * @param derVal Der encoded value of this extension ++ */ ++ public SubjectDirAttributesExtension(DerValue derVal) throws IOException { ++ ++ this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; ++ this.critical = false; ++ decodeThis(derVal); ++ } ++ ++ /** ++ * Default constructor for this object. ++ * ++ * @param list Attribute object list ++ */ ++ public SubjectDirAttributesExtension(Attribute[] list) throws IOException { ++ ++ this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; ++ this.critical = false; ++ ++ if ((list == null) || (list.length == 0)) { ++ throw new IOException("No data available in " ++ + "passed Attribute List."); ++ } ++ ++ // add the Attributes ++ for (int i = 0; i < list.length; i++) { ++ attrList.addElement(list[i]); ++ } ++ } ++ ++ /** ++ * Constructor from parsing extension ++ * ++ * @param list Attribute object list ++ */ ++ public SubjectDirAttributesExtension(Boolean crit, Object value) ++ throws IOException { ++ ++ this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; ++ this.critical = crit.booleanValue(); ++ ++ if (!(value instanceof byte[])) ++ throw new IOException(NAME + "Illegal argument type"); ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ System.arraycopy(value, 0, extValue, 0, len); ++ ++ this.extensionValue = extValue; ++ decodeThis(new DerValue(extValue)); ++ } ++ ++ /** ++ * Constructor for this object. ++ * ++ * @param list Attribute object list ++ * @param critical The criticality ++ */ ++ public SubjectDirAttributesExtension(Attribute[] list, boolean critical) ++ throws IOException { ++ ++ this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; ++ this.critical = critical; ++ ++ if ((list == null) || (list.length == 0)) { ++ throw new IOException("No data available in " ++ + "passed Attribute List."); ++ } ++ ++ // add the Attributes ++ for (int i = 0; i < list.length; i++) { ++ attrList.addElement(list[i]); ++ } ++ } ++ ++ /** ++ * Return user readable form of extension. ++ */ ++ public String toString() { ++ ++ String s = super.toString() + "SubjectDirectoryAttributes:[\n"; ++ ++ Enumeration attrs = attrList.elements(); ++ StringBuffer tempBuffer = new StringBuffer(); ++ while (attrs.hasMoreElements()) { ++ Attribute attr = attrs.nextElement(); ++ tempBuffer.append(attr.toString()); ++ } ++ s += tempBuffer.toString(); ++ return (s + "]\n"); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ decodeThis(val); ++ } ++ ++ /** ++ * Encode this extension value to the output stream. ++ * ++ * @param out the DerOutputStream to encode the extension to. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectDirectoryAttributes."); ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectDirectoryAttributes."); ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectDirectoryAttributes."); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ /** ++ * Returns an enumeration of attributes in the extension. ++ */ ++ public Enumeration getAttributesList() { ++ if (attrList == null) ++ return null; ++ return attrList.elements(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/SubjectKeyIdentifierExtension.java b/org/mozilla/jss/netscape/security/x509/SubjectKeyIdentifierExtension.java +new file mode 100644 +index 00000000..6c367dbe +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/SubjectKeyIdentifierExtension.java +@@ -0,0 +1,222 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.reflect.Array; ++import java.util.Enumeration; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the Subject Key Identifier Extension. ++ * ++ * This extension, if present, provides a means of identifying the particular ++ * public key used in an application. This extension by default is marked ++ * non-critical. ++ * ++ *

++ * Extensions are addiitonal attributes which can be inserted in a X509 v3 certificate. For example a ++ * "Driving License Certificate" could have the driving license number as a extension. ++ * ++ *

++ * Extensions are represented as a sequence of the extension identifier (Object Identifier), a boolean flag stating ++ * whether the extension is to be treated as being critical and the extension value itself (this is again a DER encoding ++ * of the extension value). ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.7 ++ * @see Extension ++ * @see CertAttrSet ++ */ ++public class SubjectKeyIdentifierExtension extends Extension ++ implements CertAttrSet { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 2457721262590880939L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = ++ "x509.info.extensions.SubjectKeyIdentifier"; ++ /** ++ * Attribute names. ++ */ ++ public static final String NAME = "SubjectKeyIdentifier"; ++ public static final String KEY_ID = "key_id"; ++ ++ // Private data member ++ private KeyIdentifier id; ++ ++ // Encode this extension value ++ private void encodeThis() throws IOException { ++ DerOutputStream os = new DerOutputStream(); ++ id.encode(os); ++ extensionValue = os.toByteArray(); ++ } ++ ++ /** ++ * Create a SubjectKeyIdentifierExtension with the passed octet string. ++ * The criticality is set to False. ++ * ++ * @param octetString the octet string identifying the key identifier. ++ */ ++ public SubjectKeyIdentifierExtension(boolean critical, byte[] octetString) ++ throws IOException { ++ id = new KeyIdentifier(octetString); ++ ++ this.extensionId = PKIXExtensions.SubjectKey_Id; ++ this.critical = critical; ++ encodeThis(); ++ } ++ ++ public SubjectKeyIdentifierExtension(byte[] octetString) ++ throws IOException { ++ id = new KeyIdentifier(octetString); ++ ++ this.extensionId = PKIXExtensions.SubjectKey_Id; ++ this.critical = false; ++ encodeThis(); ++ } ++ ++ /** ++ * Create the extension from the passed DER encoded value. ++ * ++ * @param critical true if the extension is to be treated as critical. ++ * @param value Array of DER encoded bytes of the actual value. ++ * @exception IOException on error. ++ */ ++ public SubjectKeyIdentifierExtension(Boolean critical, Object value) ++ throws IOException { ++ this.extensionId = PKIXExtensions.SubjectKey_Id; ++ this.critical = critical.booleanValue(); ++ ++ int len = Array.getLength(value); ++ byte[] extValue = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ extValue[i] = Array.getByte(value, i); ++ } ++ this.extensionValue = extValue; ++ DerValue val = new DerValue(extValue); ++ this.id = new KeyIdentifier(val); ++ } ++ ++ /** ++ * Returns a printable representation. ++ */ ++ public String toString() { ++ if (id == null) ++ return ""; ++ String s = super.toString() + "SubjectKeyIdentifier [\n" ++ + id.toString() + "]\n"; ++ return (s); ++ } ++ ++ /** ++ * Write the extension to the OutputStream. ++ * ++ * @param out the OutputStream to write the extension to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ if (extensionValue == null) { ++ extensionId = PKIXExtensions.SubjectKey_Id; ++ critical = false; ++ encodeThis(); ++ } ++ super.encode(tmp); ++ out.write(tmp.toByteArray()); ++ } ++ ++ /** ++ * Decode the extension from the InputStream. ++ * ++ * @param in the InputStream to unmarshal the contents from. ++ * @exception IOException on decoding or validity errors. ++ */ ++ public void decode(InputStream in) throws IOException { ++ throw new IOException("Method not to be called directly."); ++ } ++ ++ /** ++ * Set the attribute value. ++ */ ++ public void set(String name, Object obj) throws IOException { ++ clearValue(); ++ if (name.equalsIgnoreCase(KEY_ID)) { ++ if (!(obj instanceof KeyIdentifier)) { ++ throw new IOException("Attribute value should be of" + ++ " type KeyIdentifier."); ++ } ++ id = (KeyIdentifier) obj; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectKeyIdentifierExtension."); ++ } ++ } ++ ++ /** ++ * Get the attribute value. ++ */ ++ public Object get(String name) throws IOException { ++ if (name.equalsIgnoreCase(KEY_ID)) { ++ return (id); ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectKeyIdentifierExtension."); ++ } ++ } ++ ++ /** ++ * Delete the attribute value. ++ */ ++ public void delete(String name) throws IOException { ++ if (name.equalsIgnoreCase(KEY_ID)) { ++ id = null; ++ } else { ++ throw new IOException("Attribute name not recognized by " + ++ "CertAttrSet:SubjectKeyIdentifierExtension."); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(KEY_ID); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/URIName.java b/org/mozilla/jss/netscape/security/x509/URIName.java +new file mode 100644 +index 00000000..5143c587 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/URIName.java +@@ -0,0 +1,85 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class implements the URIName as required by the GeneralNames ++ * ASN.1 object. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.3 ++ * @see GeneralName ++ * @see GeneralNames ++ * @see GeneralNameInterface ++ */ ++public class URIName implements GeneralNameInterface { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 8340049830612859508L; ++ private String name; ++ ++ /** ++ * Create the URIName object from the passed encoded Der value. ++ * ++ * @param derValue the encoded DER URIName. ++ * @exception IOException on error. ++ */ ++ public URIName(DerValue derValue) throws IOException { ++ name = derValue.getIA5String(); ++ } ++ ++ /** ++ * Create the URIName object with the specified name. ++ * ++ * @param name the URIName. ++ */ ++ public URIName(String name) { ++ this.name = name; ++ } ++ ++ /** ++ * Return the type of the GeneralName. ++ */ ++ public int getType() { ++ return (GeneralNameInterface.NAME_URI); ++ } ++ ++ /** ++ * Encode the URI name into the DerOutputStream. ++ * ++ * @param out the DER stream to encode the URIName to. ++ * @exception IOException on encoding errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ out.putIA5String(name); ++ } ++ ++ /** ++ * Convert the name into user readable string. ++ */ ++ public String toString() { ++ return ("URIName: " + name); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/UniqueIdentity.java b/org/mozilla/jss/netscape/security/x509/UniqueIdentity.java +new file mode 100644 +index 00000000..24a8b11e +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/UniqueIdentity.java +@@ -0,0 +1,112 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.BitArray; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * This class defines the UniqueIdentity class used by certificates. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.6 ++ */ ++public class UniqueIdentity { ++ // Private data members ++ private BitArray id; ++ ++ /** ++ * The default constructor for this class. ++ * ++ * @param id the byte array containing the unique identifier. ++ */ ++ public UniqueIdentity(BitArray id) { ++ this.id = id; ++ } ++ ++ /** ++ * The default constructor for this class. ++ * ++ * @param id the byte array containing the unique identifier. ++ */ ++ public UniqueIdentity(byte[] id) { ++ this.id = new BitArray(id.length * 8, id); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param in the DerInputStream to read the UniqueIdentity from. ++ * @exception IOException on decoding errors. ++ */ ++ public UniqueIdentity(DerInputStream in) throws IOException { ++ DerValue derVal = in.getDerValue(); ++ id = derVal.getUnalignedBitString(true); ++ } ++ ++ /** ++ * Create the object, decoding the values from the passed DER stream. ++ * ++ * @param derVal the DerValue decoded from the stream. ++ * @param tag the tag the value is encoded under. ++ * @exception IOException on decoding errors. ++ */ ++ public UniqueIdentity(DerValue derVal) throws IOException { ++ id = derVal.getUnalignedBitString(true); ++ } ++ ++ /** ++ * Return the UniqueIdentity as a printable string. ++ */ ++ public String toString() { ++ return ("UniqueIdentity:" + id.toString() + "\n"); ++ } ++ ++ /** ++ * Encode the UniqueIdentity in DER form to the stream. ++ * ++ * @param out the DerOutputStream to marshal the contents to. ++ * @param tag enocode it under the following tag. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out, byte tag) throws IOException { ++ byte[] bytes = id.toByteArray(); ++ int excessBits = bytes.length * 8 - id.length(); ++ ++ out.write(tag); ++ out.putLength(bytes.length + 1); ++ ++ out.write(excessBits); ++ out.write(bytes); ++ } ++ ++ /** ++ * Return the unique id. ++ */ ++ public boolean[] getId() { ++ if (id == null) ++ return null; ++ ++ return id.toBooleanArray(); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/UserNotice.java b/org/mozilla/jss/netscape/security/x509/UserNotice.java +new file mode 100644 +index 00000000..4b19b6dd +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/UserNotice.java +@@ -0,0 +1,96 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Represent the UserNotice Qualifier. ++ * ++ * UserNotice ::= SEQUENCE { ++ * noticeRef NoticeReference OPTIONAL, ++ * explicitText DisplayText OPTIONAL ++ * } ++ * ++ * @author Thomas Kwan ++ */ ++public class UserNotice extends Qualifier { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 5770869942793748051L; ++ private NoticeReference mNoticeReference = null; ++ private DisplayText mDisplayText = null; ++ ++ public UserNotice(NoticeReference ref, DisplayText text) { ++ mNoticeReference = ref; ++ mDisplayText = text; ++ } ++ ++ public UserNotice(DerValue val) throws IOException { ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new IOException("Invalid encoding for UserNotice"); ++ } ++ // case 0: no element ++ if (val.data.available() == 0) ++ return; ++ // case 1: 1 element ++ DerValue inSeq = val.data.getDerValue(); ++ if (inSeq.tag == DerValue.tag_Sequence) { ++ mNoticeReference = new NoticeReference(inSeq); ++ } else { ++ mDisplayText = new DisplayText(inSeq); ++ } ++ if (val.data.available() == 0) ++ return; ++ // case 2: 2 elements ++ mDisplayText = new DisplayText(val.data.getDerValue()); ++ } ++ ++ public NoticeReference getNoticeReference() { ++ return mNoticeReference; ++ } ++ ++ public DisplayText getDisplayText() { ++ return mDisplayText; ++ } ++ ++ /** ++ * Write the UserNotice to the DerOutputStream. ++ * ++ * @param out the DerOutputStream to write the object to. ++ * @exception IOException on errors. ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ // OPTIONAL ++ if (mNoticeReference != null) { ++ mNoticeReference.encode(tmp); ++ } ++ // OPTIONAL ++ if (mDisplayText != null) { ++ mDisplayText.encode(tmp); ++ } ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X500Name.java b/org/mozilla/jss/netscape/security/x509/X500Name.java +new file mode 100644 +index 00000000..b19f8f37 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X500Name.java +@@ -0,0 +1,723 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.security.Principal; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.Enumeration; ++import java.util.List; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * X.500 names are used to identify entities, such as those which are ++ * identified by X.509 certificates. They are world-wide, hierarchical, ++ * and descriptive. Entities can be identified by attributes, and in ++ * some systems can be searched for according to those attributes. ++ * ++ *

++ * This class exposes only partial X.500 name functionality. Most ++ * notably, it works best if Relative Distinguished Names only have one ++ * (unique) attribute each, and if only the most common attributes need ++ * to be visible to applications. This limitation, and others, will ++ * be lifted over time. ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.35 ++ * @see GeneralName ++ * @see GeneralNames ++ * @see GeneralNameInterface ++ * @see RDN ++ * @see AVA ++ * @see LdapDNStrConverter ++ */ ++ ++public class X500Name implements Principal, GeneralNameInterface { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -730790062013191108L; ++ ++ /** ++ * Constructs a name from a Ldap DN string, such ++ * as &lb;CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US&rb;. The ++ * older "/C=US/O=Sun Microsystems, Inc/OU=JavaSoft/CN=Dave" syntax ++ * is not currently supported. (The former is RFC 1779 style.) ++ * ++ * @param ldapDNString a Ldap DN String e.g. as defined in RFC1779 ++ */ ++ public X500Name(String ldapDNString) ++ throws IOException { ++ X500Name x500name; ++ ++ if (ldapDNString == null || ldapDNString.equals("")) { ++ clear(); ++ return; ++ } ++ x500name = LdapDNStrConverter.getDefault().parseDN(ldapDNString); ++ names = x500name.getNames(); ++ } ++ ++ /** ++ * Constructs a X500Name from a Ldap DN String using the specified ++ * LdapDNStrConverter. Also use the input tags. ++ * ++ * @see LdapDNStrConverter ++ * ++ * @param ldapDNString a Ldap DN String e.g. as defined in RFC1779. ++ * @param ldapDNStrConverter A LdapDNStrConverter ++ */ ++ public X500Name(String ldapDNString, LdapDNStrConverter ldapDNStrConverter, byte[] tags) ++ throws IOException { ++ ++ if (ldapDNString == null || ldapDNString.equals("")) { ++ clear(); ++ return; ++ } ++ X500Name x500name; ++ x500name = ldapDNStrConverter.parseDN(ldapDNString, tags); ++ names = x500name.getNames(); ++ ++ } ++ ++ public X500Name(String ldapDNString, byte[] tags) ++ throws IOException { ++ if (ldapDNString == null || ldapDNString.equals("")) { ++ clear(); ++ return; ++ } ++ X500Name x500name; ++ x500name = LdapDNStrConverter.getDefault().parseDN(ldapDNString, tags); ++ names = x500name.getNames(); ++ } ++ ++ /** ++ * Constructs a X500Name from a Ldap DN String using the specified ++ * LdapDNStrConverter. ++ * ++ * @see LdapDNStrConverter ++ * ++ * @param ldapDNString a Ldap DN String e.g. as defined in RFC1779. ++ * @param ldapDNStrConverter A LdapDNStrConverter ++ */ ++ public X500Name(String ldapDNString, ++ LdapDNStrConverter ldapDNStrConverter) ++ throws IOException { ++ if (ldapDNString == null || ldapDNString.equals("")) { ++ clear(); ++ return; ++ } ++ X500Name x500name; ++ x500name = ldapDNStrConverter.parseDN(ldapDNString); ++ names = x500name.getNames(); ++ } ++ ++ /** ++ * Constructs a X500Name from fields common in enterprise application ++ * environments. ++ * ++ * @param commonName common name of a person, e.g. "Vivette Davis" ++ * @param organizationUnit small organization name, e.g. "Purchasing" ++ * @param organizationName large organization name, e.g. "Onizuka, Inc." ++ * @param country two letter country code, e.g. "CH" ++ */ ++ public X500Name( ++ String commonName, ++ String organizationUnit, ++ String organizationName, ++ String country) throws IOException { ++ DirStrConverter dirStrConverter = new DirStrConverter(); ++ PrintableConverter printableConverter = new PrintableConverter(); ++ AVA[] assertion = new AVA[1]; // array is cloned in constructors. ++ int i = 4; ++ ++ names = new RDN[i]; ++ /* ++ * NOTE: it's only on output that little-endian ++ * ordering is used. ++ */ ++ assertion[0] = new AVA(commonName_oid, ++ dirStrConverter.getValue(commonName)); ++ names[--i] = new RDN(assertion); ++ ++ assertion[0] = new AVA(orgUnitName_oid, ++ dirStrConverter.getValue(organizationUnit)); ++ names[--i] = new RDN(assertion); ++ ++ assertion[0] = new AVA(orgName_oid, ++ dirStrConverter.getValue(organizationName)); ++ names[--i] = new RDN(assertion); ++ ++ assertion[0] = new AVA(countryName_oid, ++ printableConverter.getValue(country)); ++ names[--i] = new RDN(assertion); ++ } ++ ++ /** ++ * Constructs a X500Name from fields common in Internet application ++ * environments. ++ * ++ * @param commonName common name of a person, e.g. "Vivette Davis" ++ * @param organizationUnit small organization name, e.g. "Purchasing" ++ * @param organizationName large organization name, e.g. "Onizuka, Inc." ++ * @param localityName locality (city) name, e.g. "Palo Alto" ++ * @param stateName state name, e.g. "California" ++ * @param country two letter country code, e.g. "CH" ++ */ ++ public X500Name( ++ String commonName, ++ String organizationUnit, ++ String organizationName, ++ String localityName, ++ String stateName, ++ String country) throws IOException { ++ DirStrConverter dirStrConverter = new DirStrConverter(); ++ PrintableConverter printableConverter = new PrintableConverter(); ++ AVA[] assertion = new AVA[1]; // array is cloned in constructors. ++ int i = 6; ++ ++ names = new RDN[i]; ++ /* ++ * NOTE: it's only on output that little-endian ++ * ordering is used. ++ */ ++ assertion[0] = new AVA(commonName_oid, ++ dirStrConverter.getValue(commonName)); ++ names[--i] = new RDN(assertion); ++ ++ assertion[0] = new AVA(orgUnitName_oid, ++ dirStrConverter.getValue(organizationUnit)); ++ names[--i] = new RDN(assertion); ++ ++ assertion[0] = new AVA(orgName_oid, ++ dirStrConverter.getValue(organizationName)); ++ names[--i] = new RDN(assertion); ++ ++ assertion[0] = new AVA(localityName_oid, ++ dirStrConverter.getValue(localityName)); ++ names[--i] = new RDN(assertion); ++ ++ assertion[0] = new AVA(stateName_oid, ++ dirStrConverter.getValue(stateName)); ++ names[--i] = new RDN(assertion); ++ ++ assertion[0] = new AVA(countryName_oid, ++ printableConverter.getValue(country)); ++ names[--i] = new RDN(assertion); ++ } ++ ++ /** ++ * Constructs a name from an ASN.1 encoded value. The encoding ++ * of the name in the stream uses DER (a BER/1 subset). ++ * ++ * @param value a DER-encoded value holding an X.500 name. ++ */ ++ public X500Name(DerValue value) throws IOException { ++ ++ this(value.toDerInputStream()); ++ } ++ ++ /** ++ * Constructs a name from an ASN.1 encoded input stream. The encoding ++ * of the name in the stream uses DER (a BER/1 subset). ++ * ++ * @param in DER-encoded data holding an X.500 name. ++ */ ++ public X500Name(DerInputStream in) ++ throws IOException { ++ parseDER(in); ++ } ++ ++ /** ++ * Constructs a name from an ASN.1 encoded byte array. ++ * ++ * @param name DER-encoded byte array holding an X.500 name. ++ */ ++ public X500Name(byte[] name) ++ throws IOException { ++ DerInputStream in = new DerInputStream(name); ++ parseDER(in); ++ ++ } ++ ++ /** ++ * Constructs a X500Name from array of RDN. The RDNs are expected to ++ * be in big endian order i.e. most significant first. ++ * ++ * @param rdns an array of RDN. ++ */ ++ public X500Name(RDN[] rdns) ++ throws IOException { ++ names = rdns.clone(); ++ } ++ ++ /** ++ * convenience method. ++ * ++ * @param rdns a vector of rdns. ++ */ ++ public X500Name(Vector rdnVector) ++ throws IOException { ++ int size = rdnVector.size(); ++ names = new RDN[size]; ++ for (int i = 0; i < size; i++) { ++ names[i] = rdnVector.elementAt(i); ++ } ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + Arrays.hashCode(names); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) ++ return true; ++ if (obj == null) ++ return false; ++ if (getClass() != obj.getClass()) ++ return false; ++ X500Name other = (X500Name) obj; ++ if (!Arrays.equals(names, other.names)) ++ return false; ++ return true; ++ } ++ ++ /** ++ * Sets private data to a null state ++ */ ++ ++ private void clear() { ++ dn = ""; ++ names = null; ++ ++ } ++ ++ /** ++ * Returns the name component as a Java string, regardless of its ++ * encoding restrictions. ++ */ ++ private String getString(DerValue attribute) throws IOException { ++ String value = attribute.getAsString(); ++ ++ if (value == null) ++ throw new IOException("not a DER string encoding, " ++ + attribute.tag); ++ else ++ return value; ++ } ++ ++ /** ++ * Return type of GeneralName. ++ */ ++ public int getType() { ++ return (GeneralNameInterface.NAME_DIRECTORY); ++ } ++ ++ /** ++ * Returns a "Country" name component. If more than one ++ * such attribute exists, the topmost one is returned. ++ * ++ * @return "C=" component of the name, if any. ++ */ ++ public String getCountry() throws IOException { ++ DerValue attr = findAttribute(countryName_oid); ++ ++ return getString(attr); ++ } ++ ++ /** ++ * Returns an "Organization" name component. If more than ++ * one such attribute exists, the topmost one is returned. ++ * ++ * @return "O=" component of the name, if any. ++ */ ++ public String getOrganization() throws IOException { ++ DerValue attr = findAttribute(orgName_oid); ++ ++ return getString(attr); ++ } ++ ++ /** ++ * Returns an "Organizational Unit" name component. If more ++ * than one such attribute exists, the topmost one is returned. ++ * ++ * @return "OU=" component of the name, if any. ++ */ ++ public String getOrganizationalUnit() throws IOException { ++ DerValue attr = findAttribute(orgUnitName_oid); ++ ++ return getString(attr); ++ } ++ ++ /** ++ * Returns a "Common Name" component. If more than one such ++ * attribute exists, the topmost one is returned. ++ * ++ * @return "CN=" component of the name, if any. ++ */ ++ public String getCommonName() throws IOException { ++ DerValue attr = findAttribute(commonName_oid); ++ ++ return getString(attr); ++ } ++ ++ /** ++ * Returns a "UID" component. If more than one such ++ * attribute exists, the topmost one is returned. ++ * ++ * @return "UID=" component of the name, if any. ++ */ ++ public String getUserID() throws IOException { ++ DerValue attr = findAttribute(uidName_oid); ++ ++ return getString(attr); ++ } ++ ++ /** ++ * Returns a "Locality" name component. If more than one ++ * such component exists, the topmost one is returned. ++ * ++ * @return "L=" component of the name, if any. ++ */ ++ public String getLocality() throws IOException { ++ DerValue attr = findAttribute(localityName_oid); ++ ++ return getString(attr); ++ } ++ ++ /** ++ * Returns a "State" name component. If more than one ++ * such component exists, the topmost one is returned. ++ * ++ * @return "S=" component of the name, if any. ++ */ ++ public String getState() throws IOException { ++ DerValue attr = findAttribute(stateName_oid); ++ ++ return getString(attr); ++ } ++ ++ /** ++ * Returns a "Email" name component. If more than one ++ * such component exists, the topmost one is returned. ++ * ++ * @return "E=" component of the name, if any. ++ */ ++ public String getEmail() throws IOException { ++ DerValue attr = findAttribute(email_oid); ++ if (attr == null) ++ return null; ++ return getString(attr); ++ } ++ ++ /** ++ * Returns a Ldap DN String from the X500Name using the global default ++ * LdapDNStrConverter ++ * ++ * @see LdapDNStrConverter ++ * @return Ldap DN string of this X500Name using the default converter. ++ */ ++ public String toLdapDNString() ++ throws IOException { ++ if (dn == null) ++ generateDN(LdapDNStrConverter.getDefault()); ++ return dn; ++ } ++ ++ /** ++ * Return a list of attributes of the given type. ++ * ++ * The "most specific" value comes last. ++ * ++ * If there are no name attributes of the given type, an empty ++ * list is returned. ++ */ ++ public List getAttributesForOid(ObjectIdentifier oid) ++ throws IOException { ++ List xs = new ArrayList<>(); ++ for (int i = 0; i < names.length; i++) { ++ DerValue v = names[i].findAttribute(oid); ++ if (v != null) ++ xs.add(getString(v)); ++ } ++ return xs; ++ } ++ ++ /** ++ * Returns a Ldap DN String from the X500Name ++ * using the specified LdapDNStrconverter. ++ * For example, RFC1779String converter can be passed to convert the ++ * DN to RFC1779 string syntax. ++ * ++ * @see LdapDNStrConverter ++ * @param ldapDNStrConverter a LdapDNStrConverter ++ * @return Ldap DN string of the X500Name ++ */ ++ public String toLdapDNString(LdapDNStrConverter ldapDNStrConverter) ++ throws IOException { ++ ++ if (dn == null) ++ generateDN(ldapDNStrConverter); ++ return dn; ++ } ++ ++ /** ++ * Returns a Ldap DN string, using the global default LdapDNStrConverter ++ * or null if an error occurs in the conversion. ++ */ ++ public String toString() { ++ String s; ++ if (names == null) { ++ s = ""; ++ return s; ++ } ++ try { ++ s = toLdapDNString(); ++ } catch (IOException e) { ++ return null; ++ } ++ return s; ++ } ++ ++ /** ++ * Returns the value of toString(). This call is needed to ++ * implement the java.security.Principal interface. ++ */ ++ public String getName() { ++ return toString(); ++ } ++ ++ private String dn; // RFC 1779 style DN, or null ++ private RDN names[]; // RDNs ++ ++ /** ++ * Find the first instance of this attribute in a "top down" ++ * search of all the attributes in the name. ++ */ ++ private DerValue findAttribute(ObjectIdentifier attribute) { ++ int i; ++ DerValue retval = null; ++ ++ for (i = 0; i < names.length; i++) { ++ retval = names[i].findAttribute(attribute); ++ if (retval != null) ++ break; ++ } ++ return retval; ++ } ++ ++ /** ++ * Returns an enumerator of RDNs in the X500Name. ++ * ++ * @return enumeration of rdns in this X500Name. ++ */ ++ public Enumeration getRDNs() { ++ return new RDNEnumerator(); ++ } ++ ++ /** ++ * Returns an array of RDN in the X500Name. ++ * ++ * @return array of RDN in this X500name. ++ */ ++ public RDN[] getNames() { ++ return names.clone(); ++ } ++ ++ /** ++ * Returns the number of RDNs in the X500Name. ++ * ++ * @return number of RDNs in this X500Name. ++ */ ++ public int getNamesLength() { ++ return names.length; ++ } ++ ++ /****************************************************************/ ++ ++ private void parseDER(DerInputStream in) throws IOException { ++ // ++ // X.500 names are a "SEQUENCE OF" RDNs, which means one or ++ // more and order matters. We scan them in order, which ++ // conventionally is big-endian. ++ // ++ DerValue nameseq[] = in.getSequence(5); ++ int i; ++ ++ if (nameseq.length != 0) { ++ names = new RDN[nameseq.length]; ++ } else { ++ clear(); ++ } ++ ++ for (i = 0; i < nameseq.length; i++) ++ names[i] = new RDN(nameseq[i]); ++ } ++ ++ /** ++ * Encodes the name in DER-encoded form. ++ * ++ * @param out where to put the DER-encoded X.500 name ++ */ ++ public void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ int i; ++ ++ int len = 0; ++ if (names == null) { ++ len = 0; ++ } else { ++ len = names.length; ++ ++ } ++ ++ for (i = 0; i < len; i++) ++ names[i].encode(tmp); ++ ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++ ++ /** ++ * Gets the name in DER-encoded form. ++ * ++ * @return the DER encoded byte array of this name, ++ * null if no names are present. ++ */ ++ public byte[] getEncoded() throws IOException { ++ try (DerOutputStream out = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ int len = 0; ++ ++ if (names == null) { ++ len = 0; ++ } else { ++ len = names.length; ++ } ++ ++ for (int i = 0; i < len; i++) ++ names[i].encode(tmp); ++ ++ out.write(DerValue.tag_Sequence, tmp); ++ return out.toByteArray(); ++ } ++ } ++ ++ /* ++ * Dump the printable form of a distinguished name. Each relative ++ * name is separated from the next by a ",", and assertions in the ++ * relative names have "label=value" syntax. ++ * ++ * Uses RFC 1779 syntax (i.e. little-endian, comma separators) ++ * ++ */ ++ private void generateDN(LdapDNStrConverter ldapDNStrConverter) ++ throws IOException { ++ if (names == null) ++ return; ++ ++ dn = ldapDNStrConverter.encodeDN(this); ++ } ++ ++ private class RDNEnumerator implements Enumeration { ++ private int index; ++ ++ public RDNEnumerator() { ++ index = 0; ++ } ++ ++ public boolean hasMoreElements() { ++ return (index < names.length); ++ } ++ ++ public RDN nextElement() { ++ if (index >= names.length) ++ return null; ++ return names[index++]; ++ } ++ } ++ ++ /****************************************************************/ ++ ++ /* ++ * Maybe return a preallocated OID, to reduce storage costs ++ * and speed recognition of common X.500 attributes. ++ */ ++ static ObjectIdentifier intern(ObjectIdentifier oid) ++ throws IOException { ++ return X500NameAttrMap.getDefault().getOid(oid); ++ } ++ ++ /* ++ * Selected OIDs from X.520 ++ */ ++ ++ /** OID for the "CN=" attribute, denoting a person's common name. */ ++ public static final ObjectIdentifier commonName_oid = X500NameAttrMap.getDefault().getOid("CN"); ++ ++ /** OID for the "UID=" attribute, denoting a person's ID. */ ++ public static final ObjectIdentifier uidName_oid = X500NameAttrMap.getDefault().getOid("UID"); ++ ++ /** OID for the "C=" attribute, denoting a country. */ ++ public static final ObjectIdentifier countryName_oid = X500NameAttrMap.getDefault().getOid("C"); ++ ++ /** OID for the "L=" attribute, denoting a locality (such as a city) */ ++ public static final ObjectIdentifier localityName_oid = X500NameAttrMap.getDefault().getOid("L"); ++ ++ /** OID for the "O=" attribute, denoting an organization name */ ++ public static final ObjectIdentifier orgName_oid = X500NameAttrMap.getDefault().getOid("O"); ++ ++ /** OID for the "OU=" attribute, denoting an organizational unit name */ ++ public static final ObjectIdentifier orgUnitName_oid = X500NameAttrMap.getDefault().getOid("OU"); ++ ++ /** OID for the "S=" attribute, denoting a state (such as Delaware) */ ++ public static final ObjectIdentifier stateName_oid = X500NameAttrMap.getDefault().getOid("ST"); ++ ++ /** OID for the "STREET=" attribute, denoting a street address. */ ++ public static final ObjectIdentifier streetAddress_oid = X500NameAttrMap.getDefault().getOid("STREET"); ++ ++ /** OID for the "T=" attribute, denoting a person's title. */ ++ public static final ObjectIdentifier title_oid = X500NameAttrMap.getDefault().getOid("TITLE"); ++ ++ /** OID for the "E=" attribute, denoting a person's email address. */ ++ public static final ObjectIdentifier email_oid = X500NameAttrMap.getDefault().getOid("E"); ++ ++ /* ++ * OIDs from other sources which show up in X.500 names we ++ * expect to deal with often ++ */ ++ ++ private static final int ipAddress_data[] = // SKIP ++ { 1, 3, 6, 1, 4, 1, 42, 2, 11, 2, 1 }; ++ ++ /** OID for "IP=" IP address attributes, used with SKIP. */ ++ public static final ObjectIdentifier ipAddress_oid = new ObjectIdentifier(ipAddress_data); ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X500NameAttrMap.java b/org/mozilla/jss/netscape/security/x509/X500NameAttrMap.java +new file mode 100644 +index 00000000..28673bf4 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X500NameAttrMap.java +@@ -0,0 +1,376 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.util.Enumeration; ++import java.util.Hashtable; ++ ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * Maps an attribute name in an X500 AVA to its OID and a ++ * converter for the attribute type. The converter converts from a string to ++ * its DER encoded attribute value. * For example, "CN" maps to its OID of ++ * 2.5.4.3 and the Directory String Converter. The Directory String ++ * Converter converts from a string to a DerValue with tag Printable, T.61 or ++ * UniversalString. ++ * ++ * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. ++ * ++ */ ++ ++public class X500NameAttrMap { ++ // ++ // public constructors. ++ // ++ ++ /** ++ * Construct a X500NameAttrMap. ++ */ ++ public X500NameAttrMap() { ++ } ++ ++ // ++ // public get methods. ++ // ++ ++ /** ++ * Get the attribute name (keyword) of the specified OID. ++ * ++ * @param oid An ObjectIdentifier ++ * ++ * @return An attribute name (keyword string) for the OID. ++ */ ++ public String getName(ObjectIdentifier oid) { ++ // XXX assert oid != null ++ return oid2Name.get(oid); ++ } ++ ++ /** ++ * Get the ObjectIdentifier of the attribute name. ++ * ++ * @param name An attribute name (string of ascii characters) ++ * ++ * @return An ObjectIdentifier for the attribute. ++ */ ++ public ObjectIdentifier getOid(String name) { ++ // XXX assert name != null ++ return name2OID.get(name.toUpperCase()); ++ } ++ ++ /** ++ * Get the Attribute Value Converter for the specified attribute name. ++ * ++ * @param name An attribute name ++ * ++ * @return An attribute value converter for the attribute name ++ */ ++ public AVAValueConverter getValueConverter(String name) { ++ ObjectIdentifier oid = ++ name2OID.get(name.toUpperCase()); ++ if (oid == null) ++ return null; ++ return oid2ValueConverter.get(oid); ++ } ++ ++ /** ++ * Get the Attribute Value Converter for the specified ObjectIdentifier. ++ * ++ * @param oid An ObjectIdentifier ++ * ++ * @return An AVAValueConverter for the OID. ++ */ ++ public AVAValueConverter getValueConverter(ObjectIdentifier oid) { ++ return oid2ValueConverter.get(oid); ++ } ++ ++ /** ++ * Get an Enumeration of all attribute names in this map. ++ * ++ * @return An Enumeration of all attribute names. ++ */ ++ public Enumeration getAllNames() { ++ return name2OID.keys(); ++ } ++ ++ /** ++ * Get an Enumeration of all ObjectIdentifiers in this map. ++ * ++ * @return An Enumeration of all OIDs in this map. ++ */ ++ public Enumeration getAllOIDs() { ++ return oid2Name.keys(); ++ } ++ ++ /** ++ * Get the ObjectIdentifier object in the map for the specified OID. ++ * ++ * @param oid An ObjectIdentifier. ++ * @return The ObjectIdentifier object in this map for the OID. ++ */ ++ public ObjectIdentifier getOid(ObjectIdentifier oid) { ++ String name = oid2Name.get(oid); ++ if (name == null) ++ return null; ++ return name2OID.get(name); ++ } ++ ++ // ++ // public add methods. ++ // ++ ++ /** ++ * Adds a attribute name, ObjectIdentifier, AVAValueConverter entry ++ * to the map. ++ * ++ * @param name An attribute name (string of ascii chars) ++ * @param oid The ObjectIdentifier for the attribute. ++ * @param valueConverter An AVAValueConverter object for converting ++ * an value for this attribute from a string to ++ * a DerValue and vice versa. ++ */ ++ public void addNameOID(String name, ObjectIdentifier oid, ++ AVAValueConverter valueConverter) { ++ // normalize name for case insensitive compare. ++ ObjectIdentifier theOid; ++ Class expValueConverter; ++ ++ theOid = name2OID.get(name); ++ if (theOid != null) { ++ expValueConverter = oid2ValueConverter.get(theOid).getClass(); ++ if (!theOid.equals(oid) || ++ expValueConverter != valueConverter.getClass()) { ++ throw new IllegalArgumentException( ++ "Another keyword-oid-valueConverter triple already " + ++ "exists in the X500NameAttrMap "); ++ } ++ return; ++ } ++ name2OID.put(name.toUpperCase(), oid); ++ oid2Name.put(oid, name.toUpperCase()); ++ oid2ValueConverter.put(oid, valueConverter); ++ } ++ ++ // ++ // public static methods. ++ // ++ ++ /** ++ * Get the global default X500NameAttrMap. ++ * ++ * @return The global default X500NameAttrMap. ++ */ ++ public static X500NameAttrMap getDefault() { ++ return defMap; ++ } ++ ++ /** ++ * Get the global default X500NamAttrMap using the DirStrConverter. ++ * ++ * @return The global default X500NameAttrMap using the DirStrConverter. ++ */ ++ ++ public static X500NameAttrMap getDirDefault() { ++ return defDirMap; ++ ++ } ++ ++ /** ++ * Set the global default X500NameAttrMap. ++ * ++ * @param newDefault The new default X500NameAttrMap. ++ */ ++ public static void setDefault(X500NameAttrMap newDefault) { ++ // XXX assert newDef != null ++ defMap = newDefault; ++ } ++ ++ // ++ // private variables ++ // ++ ++ Hashtable name2OID = new Hashtable(); ++ Hashtable oid2Name = new Hashtable(); ++ Hashtable oid2ValueConverter = ++ new Hashtable(); ++ ++ // ++ // global defaults. ++ // ++ ++ private static X500NameAttrMap defMap; ++ ++ private static X500NameAttrMap defDirMap; ++ ++ /* ++ * Create the default maps on initialization. ++ */ ++ static { ++ defMap = new X500NameAttrMap(); ++ AVAValueConverter directoryStr = new DirStrConverter(), ia5Str = new IA5StringConverter(); ++ defMap.addNameOID("CN", ++ new ObjectIdentifier("2.5.4.3"), ++ directoryStr); ++ defMap.addNameOID("OU", ++ new ObjectIdentifier("2.5.4.11"), ++ directoryStr); ++ defMap.addNameOID("O", ++ new ObjectIdentifier("2.5.4.10"), ++ directoryStr); ++ // serialNumber added for CEP support ++ defMap.addNameOID("SERIALNUMBER", ++ new ObjectIdentifier("2.5.4.5"), ++ new PrintableConverter()); ++ defMap.addNameOID("C", ++ new ObjectIdentifier("2.5.4.6"), ++ new PrintableConverter()); ++ defMap.addNameOID("L", ++ new ObjectIdentifier("2.5.4.7"), ++ directoryStr); ++ defMap.addNameOID("ST", ++ new ObjectIdentifier("2.5.4.8"), ++ directoryStr); ++ defMap.addNameOID("STREET", ++ new ObjectIdentifier("2.5.4.9"), ++ directoryStr); ++ defMap.addNameOID("TITLE", ++ new ObjectIdentifier("2.5.4.12"), ++ directoryStr); ++ // RFC 1274 UserId, rfc822MailBox ++ defMap.addNameOID("UID", ++ new ObjectIdentifier("0.9.2342.19200300.100.1.1"), ++ directoryStr); ++ defMap.addNameOID("MAIL", ++ new ObjectIdentifier("0.9.2342.19200300.100.1.3"), ++ ia5Str); ++ // PKCS9 e-mail address ++ defMap.addNameOID("E", ++ new ObjectIdentifier("1.2.840.113549.1.9.1"), ++ ia5Str); ++ ++ // DC definition from draft-ietf-asid-ldap-domains-02.txt ++ defMap.addNameOID("DC", ++ new ObjectIdentifier("0.9.2342.19200300.100.1.25"), ++ ia5Str); ++ ++ // more defined in RFC2459 used in Subject Directory Attr extension ++ defMap.addNameOID("SN", // surname ++ new ObjectIdentifier("2.5.4.4"), ++ directoryStr); ++ defMap.addNameOID("GIVENNAME", ++ new ObjectIdentifier("2.5.4.42"), ++ directoryStr); ++ defMap.addNameOID("INITIALS", ++ new ObjectIdentifier("2.5.4.43"), ++ directoryStr); ++ defMap.addNameOID("GENERATIONQUALIFIER", ++ new ObjectIdentifier("2.5.4.44"), ++ directoryStr); ++ defMap.addNameOID("DNQUALIFIER", ++ new ObjectIdentifier("2.5.4.46"), ++ directoryStr); ++ ++ // these two added mainly for CEP support ++ // PKCS9 unstructured name ++ defMap.addNameOID("UNSTRUCTUREDNAME", ++ new ObjectIdentifier("1.2.840.113549.1.9.2"), ++ ia5Str); ++ // PKCS9 unstructured address ++ defMap.addNameOID("UNSTRUCTUREDADDRESS", ++ new ObjectIdentifier("1.2.840.113549.1.9.8"), ++ new PrintableConverter()); ++ }; ++ ++ static { ++ defDirMap = new X500NameAttrMap(); ++ AVAValueConverter directoryStr = new DirStrConverter(); ++ ++ defDirMap.addNameOID("CN", ++ new ObjectIdentifier("2.5.4.3"), ++ directoryStr); ++ defDirMap.addNameOID("OU", ++ new ObjectIdentifier("2.5.4.11"), ++ directoryStr); ++ defDirMap.addNameOID("O", ++ new ObjectIdentifier("2.5.4.10"), ++ directoryStr); ++ // serialNumber added for CEP support ++ defDirMap.addNameOID("SERIALNUMBER", ++ new ObjectIdentifier("2.5.4.5"), ++ directoryStr); ++ defDirMap.addNameOID("C", ++ new ObjectIdentifier("2.5.4.6"), ++ directoryStr); ++ defDirMap.addNameOID("L", ++ new ObjectIdentifier("2.5.4.7"), ++ directoryStr); ++ defDirMap.addNameOID("ST", ++ new ObjectIdentifier("2.5.4.8"), ++ directoryStr); ++ defDirMap.addNameOID("STREET", ++ new ObjectIdentifier("2.5.4.9"), ++ directoryStr); ++ defDirMap.addNameOID("TITLE", ++ new ObjectIdentifier("2.5.4.12"), ++ directoryStr); ++ // RFC 1274 UserId, rfc822MailBox ++ defDirMap.addNameOID("UID", ++ new ObjectIdentifier("0.9.2342.19200300.100.1.1"), ++ directoryStr); ++ defDirMap.addNameOID("MAIL", ++ new ObjectIdentifier("0.9.2342.19200300.100.1.3"), ++ directoryStr); ++ // PKCS9 e-mail address ++ defDirMap.addNameOID("E", ++ new ObjectIdentifier("1.2.840.113549.1.9.1"), ++ directoryStr); ++ ++ // DC definition from draft-ietf-asid-ldap-domains-02.txt ++ defDirMap.addNameOID("DC", ++ new ObjectIdentifier("0.9.2342.19200300.100.1.25"), ++ directoryStr); ++ ++ // more defined in RFC2459 used in Subject Directory Attr extension ++ defDirMap.addNameOID("SN", // surname ++ new ObjectIdentifier("2.5.4.4"), ++ directoryStr); ++ defDirMap.addNameOID("GIVENNAME", ++ new ObjectIdentifier("2.5.4.42"), ++ directoryStr); ++ defDirMap.addNameOID("INITIALS", ++ new ObjectIdentifier("2.5.4.43"), ++ directoryStr); ++ defDirMap.addNameOID("GENERATIONQUALIFIER", ++ new ObjectIdentifier("2.5.4.44"), ++ directoryStr); ++ defDirMap.addNameOID("DNQUALIFIER", ++ new ObjectIdentifier("2.5.4.46"), ++ directoryStr); ++ ++ // these two added mainly for CEP support ++ // PKCS9 unstructured name ++ defDirMap.addNameOID("UNSTRUCTUREDNAME", ++ new ObjectIdentifier("1.2.840.113549.1.9.2"), ++ directoryStr); ++ // PKCS9 unstructured address ++ defDirMap.addNameOID("UNSTRUCTUREDADDRESS", ++ new ObjectIdentifier("1.2.840.113549.1.9.8"), ++ directoryStr); ++ }; ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X500Signer.java b/org/mozilla/jss/netscape/security/x509/X500Signer.java +new file mode 100644 +index 00000000..41959443 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X500Signer.java +@@ -0,0 +1,110 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.security.NoSuchAlgorithmException; ++import java.security.Signature; ++import java.security.SignatureException; ++ ++/** ++ * This class provides a binding between a Signature object and an ++ * authenticated X.500 name (from an X.509 certificate chain), which ++ * is needed in many public key signing applications. ++ * ++ *

++ * The name of the signer is important, both because knowing it is the whole point of the signature, and because the ++ * associated X.509 certificate is always used to verify the signature. ++ * ++ *

++ * The X.509 certificate chain is temporarily not associated with ++ * the signer, but this omission will be resolved. ++ * ++ * @version 1.18 ++ * ++ * @author David Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ */ ++public final class X500Signer { ++ ++ private Signature sig; ++ private X500Name agent; // XXX should be X509CertChain ++ private AlgorithmId algid; ++ ++ /** ++ * Called for each chunk of the data being signed. That ++ * is, you can present the data in many chunks, so that ++ * it doesn't need to be in a single sequential buffer. ++ * ++ * @param buf buffer holding the next chunk of the data to be signed ++ * @param offset starting point of to-be-signed data ++ * @param len how many bytes of data are to be signed ++ * @exception SignatureException on errors. ++ */ ++ public void update(byte buf[], int offset, int len) ++ throws SignatureException { ++ sig.update(buf, offset, len); ++ } ++ ++ /** ++ * Produces the signature for the data processed by update(). ++ * ++ * @exception SignatureException on errors. ++ */ ++ public byte[] sign() throws SignatureException { ++ return sig.sign(); ++ } ++ ++ /** ++ * Returns the algorithm used to sign. ++ */ ++ public AlgorithmId getAlgorithmId() { ++ return algid; ++ } ++ ++ /** ++ * Returns the name of the signing agent. ++ */ ++ public X500Name getSigner() { ++ return agent; ++ } ++ ++ /* ++ * Constructs a binding between a signature and an X500 name ++ * from an X.509 certificate. ++ */ ++ // package private ----hmmmmm ????? ++ public X500Signer(Signature sig, X500Name agent) { ++ if (sig == null || agent == null) ++ throw new IllegalArgumentException("null parameter"); ++ ++ this.sig = sig; ++ this.agent = agent; ++ ++ try { ++ String alg = sig.getAlgorithm(); ++ if (alg.equals("DSA")) { ++ alg = "SHA1withDSA"; ++ } ++ this.algid = AlgorithmId.get(alg); ++ ++ } catch (NoSuchAlgorithmException e) { ++ throw new RuntimeException("internal error! " + e.getMessage()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X509AttributeName.java b/org/mozilla/jss/netscape/security/x509/X509AttributeName.java +new file mode 100644 +index 00000000..e0591ba6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X509AttributeName.java +@@ -0,0 +1,64 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++/** ++ * This class is used to parse attribute names like "x509.info.extensions". ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.4 ++ */ ++public class X509AttributeName { ++ // Public members ++ private static final char SEPARATOR = '.'; ++ ++ // Private data members ++ private String prefix = null; ++ private String suffix = null; ++ ++ /** ++ * Default constructor for the class. Name is of the form ++ * "x509.info.extensions". ++ * ++ * @param name the attribute name. ++ */ ++ public X509AttributeName(String name) { ++ int i = name.indexOf(SEPARATOR); ++ if (i == (-1)) { ++ prefix = name; ++ } else { ++ prefix = name.substring(0, i); ++ suffix = name.substring(i + 1); ++ } ++ } ++ ++ /** ++ * Return the prefix of the name. ++ */ ++ public String getPrefix() { ++ return (prefix); ++ } ++ ++ /** ++ * Return the suffix of the name. ++ */ ++ public String getSuffix() { ++ return (suffix); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X509CRLImpl.java b/org/mozilla/jss/netscape/security/x509/X509CRLImpl.java +new file mode 100755 +index 00000000..b88033d6 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X509CRLImpl.java +@@ -0,0 +1,1078 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.math.BigInteger; ++import java.security.InvalidKeyException; ++import java.security.NoSuchAlgorithmException; ++import java.security.NoSuchProviderException; ++import java.security.Principal; ++import java.security.PrivateKey; ++import java.security.PublicKey; ++import java.security.Signature; ++import java.security.SignatureException; ++import java.security.cert.CRLException; ++import java.security.cert.Certificate; ++import java.security.cert.X509CRL; ++import java.security.cert.X509CRLEntry; ++import java.security.cert.X509Certificate; ++import java.util.Date; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.LinkedHashSet; ++import java.util.Set; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ *

++ * An implmentation for X509 CRL (Certificate Revocation List). ++ *

++ * The X.509 v2 CRL format is described below in ASN.1: ++ * ++ *

++ * 
++ *

++ * CertificateList ::= SEQUENCE { tbsCertList TBSCertList, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING ++ * } ++ *

++ * A good description and profiling is provided in the IETF PKIX WG draft, Part I: X.509 Certificate and CRL Profile, ++ * <draft-ietf-pkix-ipki-part1-06.txt>. ++ *

++ * The ASN.1 definition of tbsCertList is: ++ * ++ *

++ * TBSCertList  ::=  SEQUENCE  {
++ *     version                 Version OPTIONAL,
++ *                             -- if present, must be v2
++ *     signature               AlgorithmIdentifier,
++ *     issuer                  Name,
++ *     thisUpdate              ChoiceOfTime,
++ *     nextUpdate              ChoiceOfTime OPTIONAL,
++ *     revokedCertificates     SEQUENCE OF SEQUENCE  {
++ *         userCertificate         CertificateSerialNumber,
++ *         revocationDate          ChoiceOfTime,
++ *         crlEntryExtensions      Extensions OPTIONAL
++ *                                 -- if present, must be v2
++ *         }  OPTIONAL,
++ *     crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
++ *                                  -- if present, must be v2
++ *     }
++ * 
++ * ++ * @author Hemma Prafullchandra ++ * @version 1.8 ++ * @see X509CRL ++ */ ++public class X509CRLImpl extends X509CRL { ++ ++ // CRL data, and its envelope ++ private byte[] signedCRL = null; // DER encoded crl ++ private byte[] signature = null; // raw signature bits ++ private byte[] tbsCertList = null; // DER encoded "to-be-signed" CRL ++ private AlgorithmId sigAlgId; // sig alg in CRL ++ ++ // crl information ++ private int version; ++ private AlgorithmId infoSigAlgId; // sig alg in "to-be-signed" crl ++ private X500Name issuer; ++ private Date thisUpdate = null; ++ private Date nextUpdate = null; ++ // private static final Hashtable revokedCerts = new Hashtable(); ++ private Hashtable revokedCerts = new Hashtable(); ++ // private static CRLExtensions extensions = null; ++ private CRLExtensions extensions = null; ++ private boolean entriesIncluded = true; ++ private final static boolean isExplicit = true; ++ ++ private boolean readOnly = false; ++ ++ /** ++ * Unmarshals an X.509 CRL from its encoded form, parsing the encoded ++ * bytes. This form of constructor is used by agents which ++ * need to examine and use CRL contents. Note that the buffer ++ * must include only one CRL, and no "garbage" may be left at ++ * the end. ++ * ++ * @param crlData the encoded bytes, with no trailing padding. ++ * @exception CRLException on parsing errors. ++ * @exception X509ExtensionException on extension handling errors. ++ */ ++ public X509CRLImpl(byte[] crlData) ++ throws CRLException, X509ExtensionException { ++ try { ++ DerValue in = new DerValue(crlData); ++ ++ parse(in); ++ signedCRL = crlData; ++ } catch (IOException e) { ++ throw new CRLException("Parsing error: " + e.getMessage()); ++ } ++ } ++ ++ public X509CRLImpl(byte[] crlData, boolean includeEntries) ++ throws CRLException, X509ExtensionException { ++ try { ++ entriesIncluded = includeEntries; ++ DerValue in = new DerValue(crlData); ++ ++ parse(in, includeEntries); ++ signedCRL = crlData; ++ } catch (IOException e) { ++ throw new CRLException("Parsing error: " + e.getMessage()); ++ } ++ } ++ ++ /** ++ * Unmarshals an X.509 CRL from an input stream. Only one CRL ++ * is expected at the end of the input stream. ++ * ++ * @param inStrm an input stream holding at least one CRL ++ * @exception CRLException on parsing errors. ++ * @exception X509ExtensionException on extension handling errors. ++ */ ++ public X509CRLImpl(InputStream inStrm) ++ throws CRLException, X509ExtensionException { ++ try { ++ DerValue val = new DerValue(inStrm); ++ ++ parse(val); ++ signedCRL = val.toByteArray(); ++ } catch (IOException e) { ++ throw new CRLException("Parsing error: " + e.getMessage()); ++ } ++ } ++ ++ /** ++ * Initial CRL constructor, no revoked certs, and no extensions. ++ * ++ * @param issuer the name of the CA issuing this CRL. ++ * @param thisUpdate the Date of this issue. ++ * @param nextUpdate the Date of the next CRL. ++ */ ++ public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate) { ++ this.issuer = issuer; ++ this.thisUpdate = thisDate; ++ this.nextUpdate = nextDate; ++ } ++ ++ /** ++ * CRL constructor, revoked certs, no extensions. ++ * ++ * @param issuer the name of the CA issuing this CRL. ++ * @param thisUpdate the Date of this issue. ++ * @param nextUpdate the Date of the next CRL. ++ * @param badCerts the array of revoked certificates. ++ * ++ * @exception CRLException on parsing/construction errors. ++ * @exception X509ExtensionException on extension handling errors. ++ */ ++ public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, ++ RevokedCertificate[] badCerts) ++ throws CRLException, X509ExtensionException { ++ this.issuer = issuer; ++ this.thisUpdate = thisDate; ++ this.nextUpdate = nextDate; ++ if (badCerts != null) { ++ for (int i = 0; i < badCerts.length; i++) ++ this.revokedCerts.put(badCerts[i].getSerialNumber(), ++ badCerts[i]); ++ } ++ } ++ ++ /** ++ * CRL constructor, revoked certs and extensions. ++ * ++ * @param issuer the name of the CA issuing this CRL. ++ * @param thisUpdate the Date of this issue. ++ * @param nextUpdate the Date of the next CRL. ++ * @param badCerts the array of revoked certificates. ++ * @param crlExts the CRL extensions. ++ * ++ * @exception CRLException on parsing/construction errors. ++ * @exception X509ExtensionException on extension handling errors. ++ */ ++ public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, ++ RevokedCertificate[] badCerts, CRLExtensions crlExts) ++ throws CRLException, X509ExtensionException { ++ this.issuer = issuer; ++ this.thisUpdate = thisDate; ++ this.nextUpdate = nextDate; ++ if (badCerts != null) { ++ for (int i = 0; i < badCerts.length; i++) { ++ if (badCerts[i] != null) { ++ this.revokedCerts.put(badCerts[i].getSerialNumber(), ++ badCerts[i]); ++ if (badCerts[i].hasExtensions()) ++ this.version = 1; ++ } ++ } ++ } ++ if (crlExts != null) { ++ this.extensions = crlExts; ++ this.version = 1; ++ } ++ } ++ ++ /** ++ * CRL constructor, revoked certs and extensions. ++ * This will be used by code that constructs CRL and uses ++ * encodeInfo() in order to sign it using external means ++ * (other than sign() method) ++ * ++ * @param issuer the name of the CA issuing this CRL. ++ * @param sigAlg signing algorithm id ++ * @param thisUpdate the Date of this issue. ++ * @param nextUpdate the Date of the next CRL. ++ * @param badCerts the array of revoked certificates. ++ * @param crlExts the CRL extensions. ++ */ ++ public X509CRLImpl(X500Name issuer, AlgorithmId algId, Date thisDate, Date nextDate, ++ RevokedCertificate[] badCerts, CRLExtensions crlExts) ++ throws CRLException, X509ExtensionException { ++ this(issuer, thisDate, nextDate, badCerts, crlExts); ++ infoSigAlgId = algId; ++ } ++ ++ /** ++ * CRL constructor, revoked certs and extensions. ++ * ++ * @param issuer the name of the CA issuing this CRL. ++ * @param sigAlg signing algorithm id ++ * @param thisUpdate the Date of this issue. ++ * @param nextUpdate the Date of the next CRL. ++ * @param badCerts the hashtable of revoked certificates. ++ * @param crlExts the CRL extensions. ++ * ++ * @exception CRLException on parsing/construction errors. ++ * @exception X509ExtensionException on extension handling errors. ++ */ ++ public X509CRLImpl(X500Name issuer, AlgorithmId algId, ++ Date thisDate, Date nextDate, ++ Hashtable badCerts, CRLExtensions crlExts) ++ throws CRLException, X509ExtensionException { ++ this.issuer = issuer; ++ this.thisUpdate = thisDate; ++ this.nextUpdate = nextDate; ++ this.revokedCerts = badCerts; ++ if (crlExts != null) { ++ this.extensions = crlExts; ++ this.version = 1; ++ } ++ infoSigAlgId = algId; ++ } ++ ++ /** ++ * Returns the ASN.1 DER encoded form of this CRL. ++ * ++ * @exception CRLException if an encoding error occurs. ++ */ ++ public byte[] getEncoded() throws CRLException { ++ if (signedCRL == null) ++ throw new CRLException("Null CRL to encode"); ++ byte[] dup = new byte[signedCRL.length]; ++ System.arraycopy(signedCRL, 0, dup, 0, dup.length); ++ return dup; ++ } ++ ++ /** ++ * Returns true if signedCRL was set. ++ * ++ * @param byte array of containing signed CRL. ++ */ ++ public boolean setSignedCRL(byte[] crl) { ++ boolean done = false; ++ if (tbsCertList != null && signedCRL == null) { ++ signedCRL = new byte[crl.length]; ++ System.arraycopy(crl, 0, signedCRL, 0, signedCRL.length); ++ done = true; ++ } ++ return done; ++ } ++ ++ public boolean hasUnsupportedCriticalExtension() { ++ // XXX NOT IMPLEMENTED ++ return true; ++ } ++ ++ /** ++ * Encodes the "to-be-signed" CRL to the OutputStream. ++ * ++ * @param out the OutputStream to write to. ++ * @exception CRLException on encoding errors. ++ * @exception X509ExtensionException on extension encoding errors. ++ */ ++ public void encodeInfo(OutputStream out) ++ throws CRLException, X509ExtensionException { ++ try (DerOutputStream seq = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ DerOutputStream rCerts = new DerOutputStream(); ++ ++ if (version != 0) // v2 crl encode version ++ tmp.putInteger(new BigInt(version)); ++ infoSigAlgId.encode(tmp); ++ issuer.encode(tmp); ++ ++ // from 2050 should encode GeneralizedTime ++ tmp.putUTCTime(thisUpdate); ++ ++ if (nextUpdate != null) ++ tmp.putUTCTime(nextUpdate); ++ ++ if (!revokedCerts.isEmpty()) { ++ for (Enumeration e = revokedCerts.elements(); e.hasMoreElements();) ++ ((RevokedCertImpl) e.nextElement()).encode(rCerts); ++ tmp.write(DerValue.tag_Sequence, rCerts); ++ } ++ ++ if (extensions != null) ++ extensions.encode(tmp, isExplicit); ++ ++ seq.write(DerValue.tag_Sequence, tmp); ++ ++ tbsCertList = seq.toByteArray(); ++ out.write(tbsCertList); ++ } catch (IOException e) { ++ throw new CRLException("Encoding error: " + e.getMessage()); ++ } ++ } ++ ++ /** ++ * Verifies that this CRL was signed using the ++ * private key that corresponds to the specified public key. ++ * ++ * @param key the PublicKey used to carry out the verification. ++ * ++ * @exception NoSuchAlgorithmException on unsupported signature ++ * algorithms. ++ * @exception InvalidKeyException on incorrect key. ++ * @exception NoSuchProviderException if there's no default provider. ++ * @exception SignatureException on signature errors. ++ * @exception CRLException on encoding errors. ++ */ ++ public void verify(PublicKey key) ++ throws CRLException, NoSuchAlgorithmException, InvalidKeyException, ++ NoSuchProviderException, SignatureException { ++ String sigProvider = null; ++ verify(key, sigProvider); ++ } ++ ++ /** ++ * Verifies that this CRL was signed using the ++ * private key that corresponds to the specified public key, ++ * and that the signature verification was computed by ++ * the given provider. ++ * ++ * @param key the PublicKey used to carry out the verification. ++ * @param sigProvider the name of the signature provider. ++ * ++ * @exception NoSuchAlgorithmException on unsupported signature ++ * algorithms. ++ * @exception InvalidKeyException on incorrect key. ++ * @exception NoSuchProviderException on incorrect provider. ++ * @exception SignatureException on signature errors. ++ * @exception CRLException on encoding errors. ++ */ ++ public void verify(PublicKey key, String sigProvider) ++ throws CRLException, NoSuchAlgorithmException, InvalidKeyException, ++ NoSuchProviderException, SignatureException { ++ if (signedCRL == null) { ++ throw new CRLException("Uninitialized CRL"); ++ } ++ Signature sigVerf = null; ++ ++ String sigAlg = sigAlgId.getName(); ++ if (sigProvider != null && sigProvider.equals("Mozilla-JSS")) { ++ if (sigAlg.equals("MD5withRSA")) { ++ sigAlg = "MD5/RSA"; ++ } else if (sigAlg.equals("MD2withRSA")) { ++ sigAlg = "MD2/RSA"; ++ } else if (sigAlg.equals("SHA1withRSA")) { ++ sigAlg = "SHA1/RSA"; ++ } else if (sigAlg.equals("SHA1withDSA")) { ++ sigAlg = "SHA1/DSA"; ++ } else if (sigAlg.equals("SHA1withEC")) { ++ sigAlg = "SHA1/EC"; ++ } else if (sigAlg.equals("SHA256withRSA")) { ++ sigAlg = "SHA256/RSA"; ++ } else if (sigAlg.equals("SHA384withRSA")) { ++ sigAlg = "SHA384/RSA"; ++ } else if (sigAlg.equals("SHA512withRSA")) { ++ sigAlg = "SHA512/RSA"; ++ } else if (sigAlg.equals("SHA256withEC")) { ++ sigAlg = "SHA256/EC"; ++ } else if (sigAlg.equals("SHA384withEC")) { ++ sigAlg = "SHA384/EC"; ++ } else if (sigAlg.equals("SHA512withEC")) { ++ sigAlg = "SHA512/EC"; ++ } ++ } ++ sigVerf = Signature.getInstance(sigAlg, sigProvider); ++ sigVerf.initVerify(key); ++ ++ if (tbsCertList == null) ++ throw new CRLException("Uninitialized CRL"); ++ ++ sigVerf.update(tbsCertList, 0, tbsCertList.length); ++ ++ if (!sigVerf.verify(signature)) { ++ throw new CRLException("Signature does not match."); ++ } ++ } ++ ++ /** ++ * Encodes an X.509 CRL, and signs it using the key ++ * passed. ++ * ++ * @param key the private key used for signing. ++ * @param algorithm the name of the signature algorithm used. ++ * ++ * @exception NoSuchAlgorithmException on unsupported signature ++ * algorithms. ++ * @exception InvalidKeyException on incorrect key. ++ * @exception NoSuchProviderException on incorrect provider. ++ * @exception SignatureException on signature errors. ++ * @exception CRLException if any mandatory data was omitted. ++ * @exception X509ExtensionException on any extension errors. ++ */ ++ public void sign(PrivateKey key, String algorithm) ++ throws CRLException, NoSuchAlgorithmException, InvalidKeyException, ++ NoSuchProviderException, SignatureException, X509ExtensionException { ++ sign(key, algorithm, null); ++ } ++ ++ /** ++ * Encodes an X.509 CRL, and signs it using the key ++ * passed. ++ * ++ * @param key the private key used for signing. ++ * @param algorithm the name of the signature algorithm used. ++ * @param provider the name of the provider. ++ * ++ * @exception NoSuchAlgorithmException on unsupported signature ++ * algorithms. ++ * @exception InvalidKeyException on incorrect key. ++ * @exception NoSuchProviderException on incorrect provider. ++ * @exception SignatureException on signature errors. ++ * @exception CRLException if any mandatory data was omitted. ++ * @exception X509ExtensionException on any extension errors. ++ */ ++ public void sign(PrivateKey key, String algorithm, String provider) ++ throws CRLException, NoSuchAlgorithmException, InvalidKeyException, ++ NoSuchProviderException, SignatureException, X509ExtensionException { ++ try (DerOutputStream out = new DerOutputStream()){ ++ if (readOnly) ++ throw new CRLException("cannot over-write existing CRL"); ++ Signature sigEngine = null; ++ if (provider == null) ++ sigEngine = Signature.getInstance(algorithm); ++ else ++ sigEngine = Signature.getInstance(algorithm, provider); ++ ++ sigEngine.initSign(key); ++ ++ // in case the name is reset ++ sigAlgId = AlgorithmId.get(sigEngine.getAlgorithm()); ++ infoSigAlgId = sigAlgId; ++ ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ // encode crl info ++ encodeInfo(tmp); ++ ++ // encode algorithm identifier ++ sigAlgId.encode(tmp); ++ ++ // Create and encode the signature itself. ++ sigEngine.update(tbsCertList, 0, tbsCertList.length); ++ signature = sigEngine.sign(); ++ tmp.putBitString(signature); ++ ++ // Wrap the signed data in a SEQUENCE { data, algorithm, sig } ++ out.write(DerValue.tag_Sequence, tmp); ++ signedCRL = out.toByteArray(); ++ readOnly = true; ++ ++ } catch (IOException e) { ++ throw new CRLException("Error while encoding data: " + ++ e.getMessage()); ++ } ++ } ++ ++ /** ++ * Returns a printable string of this CRL. ++ * ++ * @return value of this CRL in a printable form. ++ */ ++ public String toString() { ++ StringBuffer sb = new StringBuffer("X.509 CRL v" + (version + 1) + "\n" + "Signature Algorithm: " + sigAlgId + ++ ", OID=" + sigAlgId.getOID() + "\n" + "Issuer: " + issuer + "\n" + "\nThis Update: " + thisUpdate ++ + "\n"); ++ if (nextUpdate != null) ++ sb.append("Next Update: " + nextUpdate + "\n"); ++ if (revokedCerts.isEmpty()) ++ sb.append("\nNO certificates have been revoked\n"); ++ else { ++ sb.append("\nRevoked Certificates:\n"); ++ for (Enumeration e = revokedCerts.elements(); e.hasMoreElements();) ++ sb.append(e.nextElement()); ++ } ++ if (extensions != null) { ++ for (int i = 0; i < extensions.size(); i++) { ++ sb.append("\nCRL Extension[" + i + "]: " + extensions.elementAt(i)); ++ } ++ } ++ org.mozilla.jss.netscape.security.util.PrettyPrintFormat pp = ++ new org.mozilla.jss.netscape.security.util.PrettyPrintFormat(" ", 20); ++ String signaturebits = pp.toHexString(signature); ++ sb.append("\nSignature:\n" + signaturebits); ++ ++ return sb.toString(); ++ } ++ ++ /** ++ * Checks whether the given serial number is on this CRL. ++ * ++ * @param serialNumber the number to check for. ++ * @return true if the given serial number is on this CRL, ++ * false otherwise. ++ */ ++ public boolean isRevoked(BigInteger serialNumber) { ++ if (revokedCerts == null || revokedCerts.isEmpty()) ++ return false; ++ return revokedCerts.containsKey(serialNumber); ++ } ++ ++ public boolean isRevoked(Certificate cert) { ++ if (cert == null) ++ return false; ++ if (cert instanceof X509Certificate) { ++ return isRevoked(((X509Certificate) cert).getSerialNumber()); ++ } else { ++ return false; ++ } ++ } ++ ++ /** ++ * Gets the version number from the CRL. ++ * The ASN.1 definition for this is: ++ * ++ *
++     * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
++     *             -- v3 does not apply to CRLs but appears for consistency
++     *             -- with definition of Version for certs
++     * 
++ * ++ * @return the version number. ++ */ ++ public int getVersion() { ++ return version; ++ } ++ ++ /** ++ * Gets the issuer distinguished name from this CRL. ++ * The issuer name identifies the entity who has signed (and ++ * issued the CRL). The issuer name field contains an ++ * X.500 distinguished name (DN). ++ * The ASN.1 definition for this is: ++ * ++ *
++     * issuer    Name
++     *
++     * Name ::= CHOICE { RDNSequence }
++     * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
++     * RelativeDistinguishedName ::=
++     *     SET OF AttributeValueAssertion
++     *
++     * AttributeValueAssertion ::= SEQUENCE {
++     *                               AttributeType,
++     *                               AttributeValue }
++     * AttributeType ::= OBJECT IDENTIFIER
++     * AttributeValue ::= ANY
++     * 
++ * ++ * The Name describes a hierarchical name composed of attributes, ++ * such as country name, and corresponding values, such as US. ++ * The type of the component AttributeValue is determined by the ++ * AttributeType; in general it will be a directoryString. ++ * A directoryString is usually one of PrintableString, ++ * TeletexString or UniversalString. ++ * ++ * @return the issuer name. ++ */ ++ public Principal getIssuerDN() { ++ return issuer; ++ } ++ ++ /** ++ * Gets the thisUpdate date from the CRL. ++ * The ASN.1 definition for this is: ++ * ++ * @return the thisUpdate date from the CRL. ++ */ ++ public Date getThisUpdate() { ++ return (new Date(thisUpdate.getTime())); ++ } ++ ++ /** ++ * Gets the nextUpdate date from the CRL. ++ * ++ * @return the nextUpdate date from the CRL, or null if ++ * not present. ++ */ ++ public Date getNextUpdate() { ++ if (nextUpdate == null) ++ return null; ++ return (new Date(nextUpdate.getTime())); ++ } ++ ++ /** ++ * Get the revoked certificate from the CRL by the serial ++ * number provided. ++ * ++ * @return the revoked certificate or null if there is ++ * no entry in the CRL marked with the provided serial number. ++ * @see RevokedCertificate ++ */ ++ public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { ++ if (revokedCerts == null || revokedCerts.isEmpty()) ++ return null; ++ return revokedCerts.get(serialNumber); ++ } ++ ++ /** ++ * Gets all the revoked certificates from the CRL. ++ * A Set of RevokedCertificate. ++ * ++ * @return all the revoked certificates or null if there are ++ * none. ++ * @see RevokedCertificate ++ */ ++ public Set getRevokedCertificates() { ++ if (revokedCerts == null || revokedCerts.isEmpty()) ++ return null; ++ else { ++ Set certSet = new LinkedHashSet(revokedCerts.values()); ++ return certSet; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ public Hashtable getListOfRevokedCertificates() { ++ if (revokedCerts == null) { ++ return null; ++ } else { ++ return (Hashtable) revokedCerts.clone(); ++ } ++ } ++ ++ public int getNumberOfRevokedCertificates() { ++ if (revokedCerts == null) ++ return -1; ++ else ++ return revokedCerts.size(); ++ } ++ ++ /** ++ * Gets the DER encoded CRL information, the tbsCertList from this CRL. ++ * This can be used to verify the signature independently. ++ * ++ * @return the DER encoded CRL information. ++ * @exception CRLException on parsing errors. ++ * @exception X509ExtensionException on extension parsing errors. ++ */ ++ public byte[] getTBSCertList() ++ throws CRLException { ++ if (tbsCertList == null) ++ throw new CRLException("Uninitialized CRL"); ++ byte[] dup = new byte[tbsCertList.length]; ++ System.arraycopy(tbsCertList, 0, dup, 0, dup.length); ++ return dup; ++ } ++ ++ /** ++ * Gets the raw Signature bits from the CRL. ++ * ++ * @return the signature. ++ */ ++ public byte[] getSignature() { ++ if (signature == null) ++ return null; ++ byte[] dup = new byte[signature.length]; ++ System.arraycopy(signature, 0, dup, 0, dup.length); ++ return dup; ++ } ++ ++ /** ++ * Returns true if signature was set. ++ * ++ * @param byte array of containing CRL signature. ++ */ ++ public boolean setSignature(byte[] crlSignature) { ++ boolean done = false; ++ if (tbsCertList != null && signature == null) { ++ signature = new byte[crlSignature.length]; ++ System.arraycopy(crlSignature, 0, signature, 0, signature.length); ++ done = true; ++ } ++ return done; ++ } ++ ++ /** ++ * Gets the signature algorithm name for the CRL ++ * signature algorithm. For example, the string "SHA1withDSA". ++ * The ASN.1 definition for this is: ++ * ++ *
++     * AlgorithmIdentifier  ::=  SEQUENCE  {
++     *     algorithm               OBJECT IDENTIFIER,
++     *     parameters              ANY DEFINED BY algorithm OPTIONAL  }
++     *                             -- contains a value of the type
++     *                             -- registered for use with the
++     *                             -- algorithm object identifier value
++     * 
++ * ++ * @return the signature algorithm name. ++ */ ++ public String getSigAlgName() { ++ if (sigAlgId == null) ++ return null; ++ return sigAlgId.getName(); ++ } ++ ++ /** ++ * Gets the signature algorithm OID string from the CRL. ++ * An OID is represented by a set of positive whole number separated ++ * by ".", that means,
++ * <positive whole number>.<positive whole number>.<...> ++ * For example, the string "1.2.840.10040.4.3" identifies the SHA-1 ++ * with DSA signature algorithm, as per the PKIX part I. ++ * ++ * @return the signature algorithm oid string. ++ */ ++ public String getSigAlgOID() { ++ if (sigAlgId == null) ++ return null; ++ ObjectIdentifier oid = sigAlgId.getOID(); ++ return oid.toString(); ++ } ++ ++ /** ++ * Gets the DER encoded signature algorithm parameters from this ++ * CRL's signature algorithm. In most cases, the signature ++ * algorithm parameters are null, the parameters are usually ++ * supplied with the Public Key. ++ * ++ * @return the DER encoded signature algorithm parameters, or ++ * null if no parameters are present. ++ */ ++ public byte[] getSigAlgParams() { ++ if (sigAlgId == null) ++ return null; ++ try { ++ return sigAlgId.getEncodedParams(); ++ } catch (IOException e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets a Set of the extension(s) marked CRITICAL in the ++ * CRL by OID strings. ++ * ++ * @return a set of the extension oid strings in the ++ * CRL that are marked critical. ++ */ ++ public Set getCriticalExtensionOIDs() { ++ if (extensions == null) ++ return null; ++ Set extSet = new LinkedHashSet(); ++ Extension ex; ++ for (Enumeration e = extensions.getElements(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ if (ex.isCritical()) { ++ extSet.add(ex.getExtensionId().toString()); ++ } ++ } ++ return extSet; ++ } ++ ++ /** ++ * Gets a Set of the extension(s) marked NON-CRITICAL in the ++ * CRL by OID strings. ++ * ++ * @return a set of the extension oid strings in the ++ * CRL that are NOT marked critical. ++ */ ++ public Set getNonCriticalExtensionOIDs() { ++ if (extensions == null) ++ return null; ++ Set extSet = new LinkedHashSet(); ++ Extension ex; ++ for (Enumeration e = extensions.getElements(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ if (!ex.isCritical()) ++ extSet.add(ex.getExtensionId().toString()); ++ } ++ return extSet; ++ } ++ ++ /** ++ * Gets the DER encoded OCTET string for the extension value ++ * (extnValue) identified by the passed in oid String. ++ * The oid string is ++ * represented by a set of positive whole number separated ++ * by ".", that means,
++ * <positive whole number>.<positive whole number>.<...> ++ * ++ * @param oid the Object Identifier value for the extension. ++ * @return the der encoded octet string of the extension value. ++ */ ++ public byte[] getExtensionValue(String oid) { ++ if (extensions == null) ++ return null; ++ try (DerOutputStream out = new DerOutputStream()) { ++ String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); ++ Extension crlExt = null; ++ ++ if (extAlias == null) { // may be unknown ++ ObjectIdentifier findOID = new ObjectIdentifier(oid); ++ Extension ex = null; ++ ObjectIdentifier inCertOID; ++ for (Enumeration e = extensions.getElements(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ inCertOID = ex.getExtensionId(); ++ if (inCertOID.equals(findOID)) { ++ crlExt = ex; ++ break; ++ } ++ } ++ } else ++ crlExt = extensions.get(extAlias); ++ if (crlExt == null) ++ return null; ++ byte[] extData = crlExt.getExtensionValue(); ++ if (extData == null) ++ return null; ++ ++ out.putOctetString(extData); ++ return out.toByteArray(); ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ public BigInteger getCRLNumber() { ++ try { ++ CRLExtensions exts = getExtensions(); ++ if (exts == null) ++ return null; ++ Enumeration e = exts.getElements(); ++ while (e.hasMoreElements()) { ++ Extension ext = e.nextElement(); ++ if (ext instanceof CRLNumberExtension) { ++ CRLNumberExtension numExt = (CRLNumberExtension) ext; ++ return (BigInteger) numExt.get(CRLNumberExtension.NUMBER); ++ } ++ } ++ } catch (Exception e) { ++ } ++ return null; ++ } ++ ++ public BigInteger getDeltaBaseCRLNumber() { ++ try { ++ CRLExtensions exts = getExtensions(); ++ if (exts == null) ++ return null; ++ Enumeration e = exts.getElements(); ++ while (e.hasMoreElements()) { ++ Extension ext = e.nextElement(); ++ if (ext instanceof DeltaCRLIndicatorExtension) { ++ DeltaCRLIndicatorExtension numExt = (DeltaCRLIndicatorExtension) ext; ++ return (BigInteger) numExt.get(DeltaCRLIndicatorExtension.NUMBER); ++ } ++ } ++ } catch (Exception e) { ++ } ++ return null; ++ } ++ ++ public boolean isDeltaCRL() { ++ try { ++ CRLExtensions exts = getExtensions(); ++ if (exts == null) ++ return false; ++ Enumeration e = exts.getElements(); ++ while (e.hasMoreElements()) { ++ Extension ext = e.nextElement(); ++ if (ext instanceof DeltaCRLIndicatorExtension) { ++ return true; ++ } ++ } ++ } catch (Exception e) { ++ } ++ return false; ++ } ++ ++ /** ++ * Returns extensions for this impl. ++ * ++ * @param extn CRLExtensions ++ */ ++ public CRLExtensions getExtensions() { ++ return extensions; ++ } ++ ++ public boolean areEntriesIncluded() { ++ return entriesIncluded; ++ } ++ ++ /*********************************************************************/ ++ /* ++ * Parses an X.509 CRL, should be used only by constructors. ++ */ ++ private void parse(DerValue val) ++ throws CRLException, IOException, X509ExtensionException { ++ parse(val, true); ++ } ++ ++ private void parse(DerValue val, boolean includeEntries) ++ throws CRLException, IOException, X509ExtensionException { ++ // check if can over write the certificate ++ if (readOnly) ++ throw new CRLException("cannot over-write existing CRL"); ++ ++ readOnly = true; ++ DerValue seq[] = new DerValue[3]; ++ ++ seq[0] = val.data.getDerValue(); ++ seq[1] = val.data.getDerValue(); ++ seq[2] = val.data.getDerValue(); ++ ++ if (val.data.available() != 0) ++ throw new CRLException("signed overrun, bytes = " ++ + val.data.available()); ++ ++ if (seq[0].tag != DerValue.tag_Sequence) ++ throw new CRLException("signed CRL fields invalid"); ++ ++ sigAlgId = AlgorithmId.parse(seq[1]); ++ signature = seq[2].getBitString(); ++ ++ if (seq[1].data.available() != 0) ++ throw new CRLException("AlgorithmId field overrun"); ++ ++ if (seq[2].data.available() != 0) ++ throw new CRLException("Signature field overrun"); ++ ++ // the tbsCertsList ++ tbsCertList = seq[0].toByteArray(); ++ ++ // parse the information ++ DerInputStream derStrm = seq[0].data; ++ DerValue tmp; ++ byte nextByte; ++ ++ // version (optional if v1) ++ version = 0; // by default, version = v1 == 0 ++ nextByte = (byte) derStrm.peekByte(); ++ if (nextByte == DerValue.tag_Integer) { ++ version = derStrm.getInteger().toInt(); ++ if (version != 1) // i.e. v2 ++ throw new CRLException("Invalid version"); ++ } ++ tmp = derStrm.getDerValue(); ++ // signature ++ { ++ AlgorithmId tmpId = AlgorithmId.parse(tmp); ++ if (!tmpId.equals(sigAlgId)) ++ throw new CRLException("Signature algorithm mismatch"); ++ ++ infoSigAlgId = tmpId; ++ } ++ // issuer ++ issuer = new X500Name(derStrm); ++ ++ // thisUpdate ++ // check if UTCTime encoded or GeneralizedTime ++ ++ nextByte = (byte) derStrm.peekByte(); ++ if (nextByte == DerValue.tag_UtcTime) { ++ thisUpdate = derStrm.getUTCTime(); ++ } else if (nextByte == DerValue.tag_GeneralizedTime) { ++ thisUpdate = derStrm.getGeneralizedTime(); ++ } else { ++ throw new CRLException("Invalid encoding for thisUpdate" ++ + " (tag=" + nextByte + ")"); ++ } ++ ++ if (derStrm.available() == 0) ++ return; // done parsing no more optional fields present ++ ++ // nextUpdate (optional) ++ nextByte = (byte) derStrm.peekByte(); ++ if (nextByte == DerValue.tag_UtcTime) { ++ nextUpdate = derStrm.getUTCTime(); ++ } else if (nextByte == DerValue.tag_GeneralizedTime) { ++ nextUpdate = derStrm.getGeneralizedTime(); ++ } // else it is not present ++ ++ if (derStrm.available() == 0) ++ return; // done parsing no more optional fields present ++ ++ // revokedCertificates (optional) ++ nextByte = (byte) derStrm.peekByte(); ++ if ((nextByte == DerValue.tag_SequenceOf) ++ && (!((nextByte & 0x0c0) == 0x080))) { ++ if (includeEntries) { ++ DerValue[] badCerts = derStrm.getSequence(4); ++ for (int i = 0; i < badCerts.length; i++) { ++ RevokedCertImpl entry = new RevokedCertImpl(badCerts[i]); ++ if (entry.hasExtensions() && (version == 0)) ++ throw new CRLException("Invalid encoding, extensions" + ++ " not supported in CRL v1 entries."); ++ ++ revokedCerts.put(entry.getSerialNumber(), ++ entry); ++ } ++ } else { ++ derStrm.skipSequence(4); ++ } ++ } ++ ++ if (derStrm.available() == 0) ++ return; // done parsing no extensions ++ ++ // crlExtensions (optional) ++ tmp = derStrm.getDerValue(); ++ if (tmp.isConstructed() && tmp.isContextSpecific((byte) 0)) { ++ if (version == 0) ++ throw new CRLException("Invalid encoding, extensions not" + ++ " supported in CRL v1."); ++ extensions = new CRLExtensions(tmp.data); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X509Cert.java b/org/mozilla/jss/netscape/security/x509/X509Cert.java +new file mode 100644 +index 00000000..c3f51492 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X509Cert.java +@@ -0,0 +1,846 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.security.Certificate; ++import java.security.InvalidKeyException; ++import java.security.Key; ++import java.security.NoSuchAlgorithmException; ++import java.security.Principal; ++import java.security.PrivateKey; ++import java.security.PublicKey; ++import java.security.Signature; ++import java.security.SignatureException; ++import java.util.Date; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * @author David Brownell ++ * @version 1.5 ++ * ++ * @see CertAndKeyGen ++ * @deprecated Use the new X509Certificate class. ++ * This class is only restored for backwards compatibility. ++ */ ++public class X509Cert implements Certificate, Serializable { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -6968141532738786900L; ++ /* The algorithm id */ ++ protected AlgorithmId algid; ++ ++ /** ++ * Construct a uninitialized X509 Cert on which ++ * decode must later be called (or which may be deserialized). ++ */ ++ // XXX deprecated, delete this ++ public X509Cert() { ++ } ++ ++ /** ++ * Unmarshals a certificate from its encoded form, parsing the ++ * encoded bytes. This form of constructor is used by agents which ++ * need to examine and use certificate contents. That is, this is ++ * one of the more commonly used constructors. Note that the buffer ++ * must include only a certificate, and no "garbage" may be left at ++ * the end. If you need to ignore data at the end of a certificate, ++ * use another constructor. ++ * ++ * @param cert the encoded bytes, with no terminatu (CONSUMED) ++ * @exception IOException when the certificate is improperly encoded. ++ */ ++ public X509Cert( ++ byte cert[]) throws IOException { ++ DerValue in = new DerValue(cert); ++ ++ parse(in); ++ if (in.data.available() != 0) ++ throw new CertParseError("garbage at end"); ++ signedCert = cert; ++ } ++ ++ /** ++ * Unmarshals a certificate from its encoded form, parsing the ++ * encoded bytes. This form of constructor is used by agents which ++ * need to examine and use certificate contents. That is, this is ++ * one of the most commonly used constructors. ++ * ++ * @param buf the buffer holding the encoded bytes ++ * @param offset the offset in the buffer where the bytes begin ++ * @param len how many bytes of certificate exist ++ * ++ * @exception IOException when the certificate is improperly encoded. ++ */ ++ public X509Cert( ++ byte buf[], ++ int offset, ++ int len) throws IOException { ++ DerValue in = new DerValue(buf, offset, len); ++ ++ parse(in); ++ if (in.data.available() != 0) ++ throw new CertParseError("garbage at end"); ++ signedCert = new byte[len]; ++ System.arraycopy(buf, offset, signedCert, 0, len); ++ } ++ ++ /** ++ * Unmarshal a certificate from its encoded form, parsing a DER value. ++ * This form of constructor is used by agents which need to examine ++ * and use certificate contents. ++ * ++ * @param derVal the der value containing the encoded cert. ++ * @exception IOException when the certificate is improperly encoded. ++ */ ++ public X509Cert(DerValue derVal) throws IOException { ++ parse(derVal); ++ if (derVal.data.available() != 0) ++ throw new CertParseError("garbage at end"); ++ signedCert = derVal.toByteArray(); ++ } ++ ++ /** ++ * Partially constructs a certificate from descriptive parameters. ++ * This constructor may be used by Certificate Authority (CA) code, ++ * which later signs and encodes the ++ * certificate. Also, self-signed certificates serve as CA certificates, ++ * and are sometimes used as certificate requests. ++ * ++ *

++ * Until the certificate has been signed and encoded, some of the mandatory fields in the certificate will not be ++ * available via accessor functions: the serial number, issuer name and signing algorithm, and of course the signed ++ * certificate. The fields passed to this constructor are available, and must be non-null. ++ * ++ *

++ * Note that the public key being signed is generally independent of the signature algorithm being used. So for ++ * example Diffie-Hellman keys (which do not support signatures) can be placed in X.509 certificates when some other ++ * signature algorithm (e.g. DSS/DSA, or one of the RSA based algorithms) is used. ++ * ++ * @see CertAndKeyGen ++ * ++ * @param subjectName the X.500 distinguished name being certified ++ * @param subjectPublicKey the public key being certified. This ++ * must be an "X509Key" implementing the "PublicKey" interface. ++ * @param notBefore the first time the certificate is valid ++ * @param notAfter the last time the certificate is valid ++ * ++ * @exception CertException if the public key is inappropriate ++ */ ++ public X509Cert( ++ X500Name subjectName, ++ X509Key subjectPublicKey, ++ Date notBefore, ++ Date notAfter) throws CertException { ++ subject = subjectName; ++ ++ if (subjectPublicKey == null) ++ throw new CertException(CertException.err_INVALID_PUBLIC_KEY, ++ "Public Key is NULL"); ++ ++ /* ++ * The X509 cert API requires X509 keys, else things break. ++ */ ++ pubkey = subjectPublicKey; ++ notbefore = notBefore; ++ notafter = notAfter; ++ version = 0; ++ } ++ ++ /** ++ * Decode an X.509 certificate from an input stream. ++ * ++ * @param in an input stream holding at least one certificate ++ * @exception IOException when the certificate is improperly encoded. ++ */ ++ public void decode(InputStream in) throws IOException { ++ DerValue val = new DerValue(in); ++ ++ parse(val); ++ if (val.data.available() != 0) ++ throw new CertParseError("garbage at end"); ++ signedCert = val.toByteArray(); ++ } ++ ++ /** ++ * Appends the certificate to an output stream. ++ * ++ * @param out an input stream to which the certificate is appended. ++ * @exception IOException when appending fails. ++ */ ++ public void encode(OutputStream out) throws IOException { ++ out.write(getSignedCert()); ++ } ++ ++ /** ++ * Compares two certificates. This is false if the ++ * certificates are not both X.509 certs, otherwise it ++ * compares them as binary data. ++ * ++ * @param other the object being compared with this one ++ * @return true iff the certificates are equivalent ++ */ ++ public boolean equals(Object other) { ++ if (other instanceof X509Cert) ++ return equals((X509Cert) other); ++ else ++ return false; ++ } ++ ++ /** ++ * Compares two certificates, returning false if any data ++ * differs between the two. ++ * ++ * @param other the object being compared with this one ++ * @return true iff the certificates are equivalent ++ */ ++ public boolean equals(X509Cert src) { ++ if (this == src) ++ return true; ++ if (signedCert == null || src.signedCert == null) ++ return false; ++ if (signedCert.length != src.signedCert.length) ++ return false; ++ for (int i = 0; i < signedCert.length; i++) ++ if (signedCert[i] != src.signedCert[i]) ++ return false; ++ return true; ++ } ++ ++ /** Returns the "X.509" format identifier. */ ++ public String getFormat() // for Certificate ++ { ++ return "X.509"; ++ } ++ ++ /** Returns getIssuerName */ ++ public Principal getGuarantor() // for Certificate ++ { ++ return getIssuerName(); ++ } ++ ++ /** Returns getSubjectName */ ++ public Principal getPrincipal() { ++ return getSubjectName(); ++ } ++ ++ /** ++ * Throws an exception if the certificate is invalid because it is ++ * now outside of the certificate's validity period, or because it ++ * was not signed using the verification key provided. Successfully ++ * verifying a certificate does not indicate that one should ++ * trust the entity which it represents. ++ * ++ *

++ * Note that since this class represents only a single X.509 ++ * certificate, it cannot know anything about the certificate chain ++ * which is used to provide the verification key and to establish trust. ++ * Other code must manage and use those cert chains. ++ * ++ *

For now, you must walk the cert chain being used to verify any ++ * given cert. Start at the root, which is a self-signed certificate; ++ * verify it using the key inside the certificate. Then use that to ++ * verify the next certificate in the chain, issued by that CA. In ++ * this manner, verify each certificate until you reach the particular ++ * certificate you wish to verify. You should not use a certificate ++ * if any of the verification operations for its certificate chain ++ * were unsuccessful. ++ * ++ * ++ * @param issuerPublicKey the public key of the issuing CA ++ * @exception CertException when the certificate is not valid. ++ */ ++ public void verify(PublicKey issuerPublicKey) ++ throws CertException { ++ Date now = new Date(); ++ ++ if (now.before(notbefore)) ++ throw new CertException(CertException.verf_INVALID_NOTBEFORE); ++ if (now.after(notafter)) ++ throw new CertException(CertException.verf_INVALID_EXPIRED); ++ if (signedCert == null) ++ throw new CertException(CertException.verf_INVALID_SIG, ++ "?? certificate is not signed yet ??"); ++ ++ // ++ // Verify the signature ... ++ // ++ String algName = null; ++ ++ try { ++ Signature sigVerf = null; ++ ++ algName = issuerSigAlg.getName(); ++ sigVerf = Signature.getInstance(algName); ++ sigVerf.initVerify(issuerPublicKey); ++ sigVerf.update(rawCert, 0, rawCert.length); ++ ++ if (!sigVerf.verify(signature)) { ++ throw new CertException(CertException.verf_INVALID_SIG, ++ "Signature ... by <" + issuer + "> for <" + subject + ">"); ++ } ++ ++ // Gag -- too many catch clauses, let most through. ++ ++ } catch (NoSuchAlgorithmException e) { ++ throw new CertException(CertException.verf_INVALID_SIG, ++ "Unsupported signature algorithm (" + algName + ")"); ++ ++ } catch (InvalidKeyException e) { ++ // e.printStackTrace(); ++ throw new CertException(CertException.err_INVALID_PUBLIC_KEY, ++ "Algorithm (" + algName + ") rejected public key"); ++ ++ } catch (SignatureException e) { ++ throw new CertException(CertException.verf_INVALID_SIG, ++ "Signature by <" + issuer + "> for <" + subject + ">"); ++ } ++ } ++ ++ /** ++ * Creates an X.509 certificate, and signs it using the issuer ++ * passed (associating a signature algorithm and an X.500 name). ++ * This operation is used to implement the certificate generation ++ * functionality of a certificate authority. ++ * ++ * @see #getSignedCert ++ * @see #getSigner ++ * @see CertAndKeyGen ++ * ++ * @param serial the serial number of the certificate (non-null) ++ * @param issuer the certificate issuer (CA) (non-null) ++ * @return the signed certificate, as returned by getSignedCert ++ * ++ * @exception IOException if any of the data could not be encoded, ++ * or when any mandatory data was omitted ++ * @exception SignatureException on signing failures ++ */ ++ public byte[] ++ encodeAndSign( ++ BigInt serial, ++ X500Signer issuer ++ ) throws IOException, SignatureException { ++ rawCert = null; ++ ++ /* ++ * Get the remaining cert parameters, and make sure we have enough. ++ * ++ * We deduce version based on what attribute data are available ++ * For now, we have no attributes, so we always deduce X.509v1 ! ++ */ ++ version = 0; ++ serialnum = serial; ++ this.issuer = issuer.getSigner(); ++ issuerSigAlg = issuer.getAlgorithmId(); ++ ++ if (subject == null || pubkey == null ++ || notbefore == null || notafter == null) ++ throw new IOException("not enough cert parameters"); ++ ++ /* ++ * Encode the raw cert, create its signature and put it ++ * into the envelope. ++ */ ++ rawCert = DERencode(); ++ signedCert = sign(issuer, rawCert); ++ return signedCert; ++ } ++ ++ /** ++ * Returns an X500Signer that may be used to create signatures. Those ++ * signature may in turn be verified using this certificate (or a ++ * copy of it). ++ * ++ *

++ * NOTE: If the private key is by itself capable of ++ * creating signatures, this fact may not be recognized at this time. ++ * Specifically, the case of DSS/DSA keys which get their algorithm ++ * parameters from higher in the certificate chain is not supportable ++ * without using an X509CertChain API, and there is no current support ++ * for other sources of algorithm parameters. ++ * ++ * @param algorithm the signature algorithm to be used. Note that a ++ * given public/private key pair may support several such algorithms. ++ * @param privateKey the private key used to create the signature, ++ * which must correspond to the public key in this certificate ++ * @return the Signer object ++ * ++ * @exception NoSuchAlgorithmException if the signature ++ * algorithm is not supported ++ * @exception InvalidKeyException if either the key in the certificate, ++ * or the private key parameter, does not support the requested ++ * signature algorithm ++ */ ++ public X500Signer getSigner(AlgorithmId algorithmId, ++ PrivateKey privateKey) ++ throws NoSuchAlgorithmException, InvalidKeyException { ++ String algorithm; ++ Signature sig; ++ ++ if (privateKey != null) { ++ Key key = privateKey; ++ algorithm = key.getAlgorithm(); ++ } else { ++ throw new InvalidKeyException("Private Key is NULL"); ++ } ++ ++ sig = Signature.getInstance(algorithmId.getName()); ++ ++ if (!pubkey.getAlgorithm().equals(algorithm)) { ++ ++ throw new InvalidKeyException("Private key algorithm " + ++ algorithm + ++ " incompatible with certificate " + ++ pubkey.getAlgorithm()); ++ } ++ sig.initSign(privateKey); ++ return new X500Signer(sig, subject); ++ } ++ ++ /** ++ * Returns a signature object that may be used to verify signatures ++ * created using a specified signature algorithm and the public key ++ * contained in this certificate. ++ * ++ *

++ * NOTE: If the public key in this certificate is not by ++ * itself capable of verifying signatures, this may not be recognized ++ * at this time. Specifically, the case of DSS/DSA keys which get ++ * their algorithm parameters from higher in the certificate chain ++ * is not supportable without using an X509CertChain API, and there ++ * is no current support for other sources of algorithm parameters. ++ * ++ * @param algorithm the algorithm of the signature to be verified ++ * @return the Signature object ++ * @exception NoSuchAlgorithmException if the signature ++ * algorithm is not supported ++ * @exception InvalidKeyException if the key in the certificate ++ * does not support the requested signature algorithm ++ */ ++ public Signature getVerifier(String algorithm) ++ throws NoSuchAlgorithmException, InvalidKeyException { ++ Signature sig; ++ ++ sig = Signature.getInstance(algorithm); ++ sig.initVerify(pubkey); ++ return sig; ++ } ++ ++ /** ++ * Return the signed X.509 certificate as a byte array. ++ * The bytes are in standard DER marshaled form. ++ * Null is returned in the case of a partially constructed cert. ++ */ ++ public byte[] getSignedCert() { ++ return signedCert; ++ } ++ ++ /** ++ * Returns the certificate's serial number. ++ * Null is returned in the case of a partially constructed cert. ++ */ ++ public BigInt getSerialNumber() { ++ return serialnum; ++ } ++ ++ /** ++ * Returns the subject's X.500 distinguished name. ++ */ ++ public X500Name getSubjectName() { ++ return subject; ++ } ++ ++ /** ++ * Returns the certificate issuer's X.500 distinguished name. ++ * Null is returned in the case of a partially constructed cert. ++ */ ++ public X500Name getIssuerName() { ++ return issuer; ++ } ++ ++ /** ++ * Returns the algorithm used by the issuer to sign the certificate. ++ * Null is returned in the case of a partially constructed cert. ++ */ ++ public AlgorithmId getIssuerAlgorithmId() { ++ return issuerSigAlg; ++ } ++ ++ /** ++ * Returns the first time the certificate is valid. ++ */ ++ public Date getNotBefore() { ++ return notbefore; ++ } ++ ++ /** ++ * Returns the last time the certificate is valid. ++ */ ++ public Date getNotAfter() { ++ return notafter; ++ } ++ ++ /** ++ * Returns the subject's public key. Note that some public key ++ * algorithms support an optional certificate generation policy ++ * where the keys in the certificates are not in themselves sufficient ++ * to perform a public key operation. Those keys need to be augmented ++ * by algorithm parameters, which the certificate generation policy ++ * chose not to place in the certificate. ++ * ++ *

++ * Two such public key algorithms are: DSS/DSA, where algorithm parameters could be acquired from a CA certificate ++ * in the chain of issuers; and Diffie-Hellman, with a similar solution although the CA then needs both a ++ * Diffie-Hellman certificate and a signature capable certificate. ++ */ ++ public PublicKey getPublicKey() { ++ return pubkey; ++ } ++ ++ /** ++ * Returns the X.509 version number of this certificate, zero based. ++ * That is, "2" indicates an X.509 version 3 (1993) certificate, ++ * and "0" indicates X.509v1 (1988). ++ * Zero is returned in the case of a partially constructed cert. ++ */ ++ public int getVersion() { ++ return version; ++ } ++ ++ /** ++ * Calculates a hash code value for the object. Objects ++ * which are equal will also have the same hashcode. ++ */ ++ public int hashCode() { ++ int retval = 0; ++ ++ for (int i = 0; i < signedCert.length; i++) ++ retval += signedCert[i] * i; ++ return retval; ++ } ++ ++ /** ++ * Returns a printable representation of the certificate. This does not ++ * contain all the information available to distinguish this from any ++ * other certificate. The certificate must be fully constructed ++ * before this function may be called; in particular, if you are ++ * creating certificates you must call encodeAndSign() before calling ++ * this function. ++ */ ++ public String toString() { ++ String s; ++ ++ if (subject == null || pubkey == null ++ || notbefore == null || notafter == null ++ || issuer == null || issuerSigAlg == null ++ || serialnum == null) ++ throw new NullPointerException("X.509 cert is incomplete"); ++ ++ s = " X.509v" + (version + 1) + " certificate,\n"; ++ s += " Subject is " + subject + "\n"; ++ s += " Key: " + pubkey; ++ s += " Validity <" + notbefore + "> until <" + notafter + ">\n"; ++ s += " Issuer is " + issuer + "\n"; ++ s += " Issuer signature used " + issuerSigAlg.toString() + "\n"; ++ s += " Serial number = " + serialnum + "\n"; ++ ++ // optional v2, v3 extras ++ ++ return "[\n" + s + "]"; ++ } ++ ++ /** ++ * Returns a printable representation of the certificate. ++ * ++ * @param detailed true iff lots of detail is requested ++ */ ++ public String toString(boolean detailed) { ++ return toString(); ++ } ++ ++ /* ++ * Certificate data, and its envelope ++ */ ++ private byte rawCert[]; ++ private byte signature[]; ++ private byte signedCert[]; ++ ++ /* ++ * X509.v1 data (parsed) ++ */ ++ private X500Name subject; // from subject ++ private X509Key pubkey; ++ ++ private Date notafter; // from CA (constructor) ++ private Date notbefore; ++ ++ private int version; // from CA (signAndEncode) ++ private BigInt serialnum; ++ private X500Name issuer; ++ private AlgorithmId issuerSigAlg; ++ ++ /* ++ * X509.v2 extensions ++ */ ++ ++ /* ++ * X509.v3 extensions ++ */ ++ ++ /* ++ * Other extensions ... Netscape, Verisign, SET, etc ++ */ ++ ++ /************************************************************/ ++ ++ /* ++ * Cert is a SIGNED ASN.1 macro, a three elment sequence: ++ * ++ * - Data to be signed (ToBeSigned) -- the "raw" cert ++ * - Signature algorithm (SigAlgId) ++ * - The signature bits ++ * ++ * This routine unmarshals the certificate, saving the signature ++ * parts away for later verification. ++ */ ++ private void parse(DerValue val) ++ throws IOException { ++ DerValue seq[] = new DerValue[3]; ++ ++ seq[0] = val.data.getDerValue(); ++ seq[1] = val.data.getDerValue(); ++ seq[2] = val.data.getDerValue(); ++ ++ if (val.data.available() != 0) ++ throw new CertParseError("signed overrun, bytes = " ++ + val.data.available()); ++ if (seq[0].tag != DerValue.tag_Sequence) ++ throw new CertParseError("signed fields invalid"); ++ ++ rawCert = seq[0].toByteArray(); // XXX slow; fixme! ++ ++ issuerSigAlg = AlgorithmId.parse(seq[1]); ++ signature = seq[2].getBitString(); ++ ++ if (seq[1].data.available() != 0) { ++ // XXX why was this error check commented out? ++ // It was originally part of the next check. ++ throw new CertParseError("algid field overrun"); ++ } ++ ++ if (seq[2].data.available() != 0) ++ throw new CertParseError("signed fields overrun"); ++ ++ /* ++ * Let's have fun parsing the cert itself. ++ */ ++ DerInputStream in; ++ DerValue tmp; ++ ++ in = seq[0].data; ++ ++ /* ++ * Version -- this is optional (default zero). If it's there it's ++ * the first field and is specially tagged. ++ * ++ * Both branches leave "tmp" holding a value for the serial ++ * number that comes next. ++ */ ++ version = 0; ++ tmp = in.getDerValue(); ++ if (tmp.isConstructed() && tmp.isContextSpecific()) { ++ version = tmp.data.getInteger().toInt(); ++ if (tmp.data.available() != 0) ++ throw new IOException("X.509 version, bad format"); ++ tmp = in.getDerValue(); ++ } ++ ++ /* ++ * serial number ... an integer ++ */ ++ serialnum = tmp.getInteger(); ++ ++ /* ++ * algorithm type for CA's signature ... needs to match the ++ * one on the envelope, and that's about it! different IDs ++ * may represent a signature attack. In general we want to ++ * inherit parameters. ++ */ ++ tmp = in.getDerValue(); ++ { ++ AlgorithmId algid; ++ ++ algid = AlgorithmId.parse(tmp); ++ ++ if (!algid.equals(issuerSigAlg)) ++ throw new CertParseError("CA Algorithm mismatch!"); ++ ++ this.algid = algid; ++ } ++ ++ /* ++ * issuer name ++ */ ++ issuer = new X500Name(in); ++ ++ /* ++ * validity: SEQUENCE { start date, end date } ++ */ ++ tmp = in.getDerValue(); ++ if (tmp.tag != DerValue.tag_Sequence) ++ throw new CertParseError("corrupt validity field"); ++ ++ notbefore = tmp.data.getUTCTime(); ++ notafter = tmp.data.getUTCTime(); ++ if (tmp.data.available() != 0) ++ throw new CertParseError("excess validity data"); ++ ++ /* ++ * subject name and public key ++ */ ++ subject = new X500Name(in); ++ ++ tmp = in.getDerValue(); ++ pubkey = X509Key.parse(tmp); ++ ++ /* ++ * XXX for v2 and later, a bunch of tagged options follow ++ */ ++ ++ if (in.available() != 0) { ++ /* ++ * Until we parse V2/V3 data ... ignore it. ++ * ++ // throw new CertParseError ("excess cert data"); ++ System.out.println ( ++ "@end'o'cert, optional V2/V3 data unparsed: " ++ + in.available () ++ + " bytes" ++ ); ++ */ ++ } ++ } ++ ++ /* ++ * Encode only the parts that will later be signed. ++ */ ++ private byte[] DERencode() throws IOException { ++ DerOutputStream raw = new DerOutputStream(); ++ ++ encode(raw); ++ return raw.toByteArray(); ++ } ++ ++ /* ++ * Marshal the contents of a "raw" certificate into a DER sequence. ++ */ ++ private void encode(DerOutputStream out) throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ /* ++ * encode serial number, issuer signing algorithm, ++ * and issuer name into the data we'll return ++ */ ++ tmp.putInteger(serialnum); ++ issuerSigAlg.encode(tmp); ++ issuer.encode(tmp); ++ ++ /* ++ * Validity is a two element sequence ... encode the ++ * elements, then wrap them into the data we'll return ++ */ ++ { ++ DerOutputStream seq = new DerOutputStream(); ++ ++ seq.putUTCTime(notbefore); ++ seq.putUTCTime(notafter); ++ tmp.write(DerValue.tag_Sequence, seq); ++ } ++ ++ /* ++ * Encode subject (principal) and associated key ++ */ ++ subject.encode(tmp); ++ pubkey.encode(tmp); ++ ++ /* ++ * Wrap the data; encoding of the "raw" cert is now complete. ++ */ ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++ ++ /* ++ * Calculate the signature of the "raw" certificate, ++ * and marshal the cert with the signature and a ++ * description of the signing algorithm. ++ */ ++ private byte[] sign(X500Signer issuer, byte data[]) ++ throws IOException, SignatureException { ++ /* ++ * Encode the to-be-signed data, then the algorithm used ++ * to create the signature. ++ */ ++ try (DerOutputStream out = new DerOutputStream()) { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ tmp.write(data); ++ issuer.getAlgorithmId().encode(tmp); ++ ++ /* ++ * Create and encode the signature itself. ++ */ ++ issuer.update(data, 0, data.length); ++ signature = issuer.sign(); ++ tmp.putBitString(signature); ++ ++ /* ++ * Wrap the signed data in a SEQUENCE { data, algorithm, sig } ++ */ ++ out.write(DerValue.tag_Sequence, tmp); ++ return out.toByteArray(); ++ } ++ } ++ ++ /** ++ * Serialization write ... X.509 certificates serialize as ++ * themselves, and they're parsed when they get read back. ++ * (Actually they serialize as some type data from the ++ * serialization subsystem, then the cert data.) ++ */ ++ private void writeObject(java.io.ObjectOutputStream stream) throws IOException { ++ encode(stream); ++ } ++ ++ /** ++ * Serialization read ... X.509 certificates serialize as ++ * themselves, and they're parsed when they get read back. ++ */ ++ private void readObject(ObjectInputStream stream) throws IOException { ++ decode(stream); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X509CertImpl.java b/org/mozilla/jss/netscape/security/x509/X509CertImpl.java +new file mode 100755 +index 00000000..62fe2113 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X509CertImpl.java +@@ -0,0 +1,1239 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.math.BigInteger; ++import java.security.InvalidKeyException; ++import java.security.NoSuchAlgorithmException; ++import java.security.NoSuchProviderException; ++import java.security.Principal; ++import java.security.PrivateKey; ++import java.security.PublicKey; ++import java.security.Signature; ++import java.security.SignatureException; ++import java.security.cert.CertificateEncodingException; ++import java.security.cert.CertificateException; ++import java.security.cert.CertificateExpiredException; ++import java.security.cert.CertificateFactory; ++import java.security.cert.CertificateNotYetValidException; ++import java.security.cert.CertificateParsingException; ++import java.security.cert.X509Certificate; ++import java.util.Date; ++import java.util.Enumeration; ++import java.util.LinkedHashSet; ++import java.util.Set; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerEncoder; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++import org.mozilla.jss.netscape.security.util.ObjectIdentifier; ++ ++/** ++ * The X509CertImpl class represents an X.509 certificate. These certificates ++ * are widely used to support authentication and other functionality in ++ * Internet security systems. Common applications include Privacy Enhanced ++ * Mail (PEM), Transport Layer Security (SSL), code signing for trusted ++ * software distribution, and Secure Electronic Transactions (SET). There ++ * is a commercial infrastructure ready to manage large scale deployments ++ * of X.509 identity certificates. ++ * ++ *

++ * These certificates are managed and vouched for by Certificate ++ * Authorities (CAs). CAs are services which create certificates by placing data in the X.509 standard format and ++ * then digitally signing that data. Such signatures are quite difficult to forge. CAs act as trusted third parties, ++ * making introductions between agents who have no direct knowledge of each other. CA certificates are either signed by ++ * themselves, or by some other CA such as a "root" CA. ++ * ++ *

++ * RFC 1422 is very informative, though it does not describe much of the recent work being done with X.509 certificates. ++ * That includes a 1996 version (X.509v3) and a variety of enhancements being made to facilitate an explosion of ++ * personal certificates used as "Internet Drivers' Licences", or with SET for credit card transactions. ++ * ++ *

++ * More recent work includes the IETF PKIX Working Group efforts, especially part 1. ++ * ++ * @author Dave Brownell ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.94 97/12/10 ++ * @see X509CertInfo ++ */ ++public class X509CertImpl extends X509Certificate ++ implements Serializable, DerEncoder { ++ // Serialization compatibility with the X509CertImpl in x509v1.jar ++ // supporting the subset of X509Certificate on JDK1.1.x platforms. ++ static final long serialVersionUID = -2048442350420423405L; ++ ++ private static final String DOT = "."; ++ /** ++ * Public attribute names. ++ */ ++ public static final String NAME = "x509"; ++ public static final String INFO = X509CertInfo.NAME; ++ public static final String ALG_ID = "algorithm"; ++ public static final String SIGNATURE = "signature"; ++ public static final String SIGNED_CERT = "signed_cert"; ++ ++ /** ++ * The following are defined for ease-of-use. These ++ * are the most frequently retrieved attributes. ++ */ ++ // x509.info.subject.dname ++ public static final String SUBJECT_DN = NAME + DOT + INFO + DOT + ++ X509CertInfo.SUBJECT + DOT + ++ CertificateSubjectName.DN_NAME; ++ // x509.info.issuer.dname ++ public static final String ISSUER_DN = NAME + DOT + INFO + DOT + ++ X509CertInfo.ISSUER + DOT + ++ CertificateIssuerName.DN_NAME; ++ // x509.info.serialNumber.number ++ public static final String SERIAL_ID = NAME + DOT + INFO + DOT + ++ X509CertInfo.SERIAL_NUMBER + DOT + ++ CertificateSerialNumber.NUMBER; ++ // x509.info.key.value ++ public static final String PUBLIC_KEY = NAME + DOT + INFO + DOT + ++ X509CertInfo.KEY + DOT + ++ CertificateX509Key.KEY; ++ ++ // x509.algorithm ++ public static final String SIG_ALG = NAME + DOT + ALG_ID; ++ ++ // x509.signature ++ public static final String SIG = NAME + DOT + SIGNATURE; ++ ++ // when we sign and decode we set this to true ++ // this is our means to make certificates immutable ++ private boolean readOnly = false; ++ ++ // Certificate data, and its envelope ++ private byte[] signedCert; ++ protected X509CertInfo info = null; ++ protected AlgorithmId algId; ++ protected byte[] signature; ++ ++ // recognized extension OIDS ++ private static final String KEY_USAGE_OID = "2.5.29.15"; ++ private static final String BASIC_CONSTRAINT_OID = "2.5.29.19"; ++ ++ /** ++ * Default constructor. ++ */ ++ public X509CertImpl() { ++ } ++ ++ /** ++ * Unmarshals a certificate from its encoded form, parsing the ++ * encoded bytes. This form of constructor is used by agents which ++ * need to examine and use certificate contents. That is, this is ++ * one of the more commonly used constructors. Note that the buffer ++ * must include only a certificate, and no "garbage" may be left at ++ * the end. If you need to ignore data at the end of a certificate, ++ * use another constructor. ++ * ++ * @param certData the encoded bytes, with no trailing padding. ++ * @exception CertificateException on parsing and initialization errors. ++ */ ++ public X509CertImpl(byte[] certData) ++ throws CertificateException { ++ this(certData, null); ++ } ++ ++ /** ++ * As a special optimization, this constructor acts as X509CertImpl(byte[]) ++ * except that it takes an X509CertInfo which it uses as a 'hint' for ++ * how to construct one field. ++ * ++ * @param certData the encode bytes, with no traiing padding ++ * @param certInfo the certInfo which has already been constructed ++ * from the certData ++ */ ++ ++ public X509CertImpl(byte[] certData, X509CertInfo certInfo) ++ throws CertificateException { ++ ++ // setting info here causes it to skip decoding in the parse() ++ // method ++ info = certInfo; ++ ++ try { ++ DerValue in = new DerValue(certData); ++ ++ parse(in); ++ signedCert = certData; ++ } catch (IOException e) { ++ throw new CertificateException("Unable to initialize, " + e); ++ } ++ } ++ ++ /** ++ * unmarshals an X.509 certificate from an input stream. ++ * ++ * @param in an input stream holding at least one certificate ++ * @exception CertificateException on parsing and initialization errors. ++ */ ++ public X509CertImpl(InputStream in) ++ throws CertificateException { ++ try { ++ DerValue val = new DerValue(in); ++ ++ parse(val); ++ signedCert = val.toByteArray(); ++ } catch (IOException e) { ++ throw new CertificateException("Unable to initialize, " + e); ++ } ++ } ++ ++ /** ++ * Construct an initialized X509 Certificate. The certificate is stored ++ * in raw form and has to be signed to be useful. ++ * ++ * @param certInfo the X509CertificateInfo which the Certificate is to be ++ * created from. ++ */ ++ public X509CertImpl(X509CertInfo certInfo) { ++ this.info = certInfo; ++ } ++ ++ /** ++ * Unmarshal a certificate from its encoded form, parsing a DER value. ++ * This form of constructor is used by agents which need to examine ++ * and use certificate contents. ++ * ++ * @param derVal the der value containing the encoded cert. ++ * @exception CertificateException on parsing and initialization errors. ++ */ ++ public X509CertImpl(DerValue derVal) ++ throws CertificateException { ++ try { ++ parse(derVal); ++ signedCert = derVal.toByteArray(); ++ } catch (IOException e) { ++ throw new CertificateException("Unable to initialize, " + e); ++ } ++ } ++ ++ public boolean hasUnsupportedCriticalExtension() { ++ // XXX NOT IMPLEMENTED ++ return true; ++ } ++ ++ /** ++ * Decode an X.509 certificate from an input stream. ++ * ++ * @param in an input stream holding at least one certificate ++ * @exception CertificateException on parsing errors. ++ * @exception IOException on other errors. ++ */ ++ public void decode(InputStream in) ++ throws CertificateException, IOException { ++ DerValue val = new DerValue(in); ++ ++ parse(val); ++ signedCert = val.toByteArray(); ++ } ++ ++ /** ++ * Appends the certificate to an output stream. ++ * ++ * @param out an input stream to which the certificate is appended. ++ * @exception CertificateEncodingException on encoding errors. ++ */ ++ public void encode(OutputStream out) ++ throws CertificateEncodingException { ++ if (signedCert == null) ++ throw new CertificateEncodingException( ++ "Null certificate to encode"); ++ try { ++ out.write(signedCert); ++ } catch (IOException e) { ++ throw new CertificateEncodingException(e.toString()); ++ } ++ } ++ ++ /** ++ * DER encode this object onto an output stream. ++ * Implements the DerEncoder interface. ++ * ++ * @param out ++ * the output stream on which to write the DER encoding. ++ * ++ * @exception IOException on encoding error. ++ */ ++ public void derEncode(OutputStream out) throws IOException { ++ if (signedCert == null) ++ throw new IOException("Null certificate to encode"); ++ ++ out.write(signedCert); ++ } ++ ++ /** ++ * Returns the encoded form of this certificate. It is ++ * assumed that each certificate type would have only a single ++ * form of encoding; for example, X.509 certificates would ++ * be encoded as ASN.1 DER. ++ * ++ * @exception CertificateEncodingException if an encoding error occurs. ++ */ ++ public byte[] getEncoded() throws CertificateEncodingException { ++ if (signedCert == null) ++ throw new CertificateEncodingException( ++ "Null certificate to encode"); ++ byte[] dup = new byte[signedCert.length]; ++ System.arraycopy(signedCert, 0, dup, 0, dup.length); ++ return dup; ++ } ++ ++ /** ++ * Throws an exception if the certificate was not signed using the ++ * verification key provided. Successfully verifying a certificate ++ * does not indicate that one should trust the entity which ++ * it represents. ++ * ++ * @param key the public key used for verification. ++ * ++ * @exception InvalidKeyException on incorrect key. ++ * @exception NoSuchAlgorithmException on unsupported signature ++ * algorithms. ++ * @exception NoSuchProviderException if there's no default provider. ++ * @exception SignatureException on signature errors. ++ * @exception CertificateException on encoding errors. ++ */ ++ public void verify(PublicKey key) ++ throws CertificateException, NoSuchAlgorithmException, ++ InvalidKeyException, NoSuchProviderException, SignatureException { ++ String sigProvider = null; ++ verify(key, sigProvider); ++ } ++ ++ /** ++ * Throws an exception if the certificate was not signed using the ++ * verification key provided. Successfully verifying a certificate ++ * does not indicate that one should trust the entity which ++ * it represents. ++ * ++ * @param key the public key used for verification. ++ * @param sigProvider the name of the provider. ++ * ++ * @exception NoSuchAlgorithmException on unsupported signature ++ * algorithms. ++ * @exception InvalidKeyException on incorrect key. ++ * @exception NoSuchProviderException on incorrect provider. ++ * @exception SignatureException on signature errors. ++ * @exception CertificateException on encoding errors. ++ */ ++ public void verify(PublicKey key, String sigProvider) ++ throws CertificateException, NoSuchAlgorithmException, ++ InvalidKeyException, NoSuchProviderException, SignatureException { ++ if (signedCert == null) { ++ throw new CertificateEncodingException("Uninitialized certificate"); ++ } ++ // Verify the signature ... ++ Signature sigVerf = null; ++ ++ sigVerf = Signature.getInstance(algId.getName(), sigProvider); ++ sigVerf.initVerify(key); ++ ++ byte[] rawCert = info.getEncodedInfo(); ++ sigVerf.update(rawCert, 0, rawCert.length); ++ ++ if (!sigVerf.verify(signature)) { ++ throw new SignatureException("Signature does not match."); ++ } ++ } ++ ++ /** ++ * Creates an X.509 certificate, and signs it using the key ++ * passed (associating a signature algorithm and an X.500 name). ++ * This operation is used to implement the certificate generation ++ * functionality of a certificate authority. ++ * ++ * @param key the private key used for signing. ++ * @param algorithm the name of the signature algorithm used. ++ * ++ * @exception InvalidKeyException on incorrect key. ++ * @exception NoSuchAlgorithmException on unsupported signature ++ * algorithms. ++ * @exception NoSuchProviderException if there's no default provider. ++ * @exception SignatureException on signature errors. ++ * @exception CertificateException on encoding errors. ++ */ ++ public void sign(PrivateKey key, String algorithm) ++ throws CertificateException, NoSuchAlgorithmException, ++ InvalidKeyException, NoSuchProviderException, SignatureException { ++ sign(key, algorithm, null); ++ } ++ ++ /** ++ * Creates an X.509 certificate, and signs it using the key ++ * passed (associating a signature algorithm and an X.500 name). ++ * This operation is used to implement the certificate generation ++ * functionality of a certificate authority. ++ * ++ * @param key the private key used for signing. ++ * @param algorithm the name of the signature algorithm used. ++ * @param provider the name of the provider. ++ * ++ * @exception NoSuchAlgorithmException on unsupported signature ++ * algorithms. ++ * @exception InvalidKeyException on incorrect key. ++ * @exception NoSuchProviderException on incorrect provider. ++ * @exception SignatureException on signature errors. ++ * @exception CertificateException on encoding errors. ++ */ ++ public void sign(PrivateKey key, String algorithm, String provider) ++ throws CertificateException, NoSuchAlgorithmException, ++ InvalidKeyException, NoSuchProviderException, SignatureException { ++ try (DerOutputStream out = new DerOutputStream()){ ++ if (readOnly) ++ throw new CertificateEncodingException( ++ "cannot over-write existing certificate"); ++ Signature sigEngine = null; ++ if (provider == null) ++ sigEngine = Signature.getInstance(algorithm); ++ else ++ sigEngine = Signature.getInstance(algorithm, provider); ++ ++ sigEngine.initSign(key); ++ ++ // in case the name is reset ++ algId = AlgorithmId.get(sigEngine.getAlgorithm()); ++ ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ // encode certificate info ++ info.encode(tmp); ++ byte[] rawCert = tmp.toByteArray(); ++ ++ // encode algorithm identifier ++ algId.encode(tmp); ++ ++ // Create and encode the signature itself. ++ sigEngine.update(rawCert, 0, rawCert.length); ++ signature = sigEngine.sign(); ++ tmp.putBitString(signature); ++ ++ // Wrap the signed data in a SEQUENCE { data, algorithm, sig } ++ out.write(DerValue.tag_Sequence, tmp); ++ signedCert = out.toByteArray(); ++ readOnly = true; ++ ++ } catch (IOException e) { ++ throw new CertificateEncodingException(e.toString()); ++ } ++ } ++ ++ /** ++ * Checks that the certificate is currently valid, i.e. the current ++ * time is within the specified validity period. ++ * ++ * @exception CertificateExpiredException if the certificate has expired. ++ * @exception CertificateNotYetValidException if the certificate is not ++ * yet valid. ++ */ ++ public void checkValidity() ++ throws CertificateExpiredException, CertificateNotYetValidException { ++ Date date = new Date(); ++ checkValidity(date); ++ } ++ ++ /** ++ * Checks that the specified date is within the certificate's ++ * validity period, or basically if the certificate would be ++ * valid at the specified date/time. ++ * ++ * @param date the Date to check against to see if this certificate ++ * is valid at that date/time. ++ * ++ * @exception CertificateExpiredException if the certificate has expired ++ * with respect to the date supplied. ++ * @exception CertificateNotYetValidException if the certificate is not ++ * yet valid with respect to the date supplied. ++ */ ++ public void checkValidity(Date date) ++ throws CertificateExpiredException, CertificateNotYetValidException { ++ ++ CertificateValidity interval = null; ++ try { ++ interval = (CertificateValidity) info.get(CertificateValidity.NAME); ++ } catch (Exception e) { ++ throw new CertificateNotYetValidException("Incorrect validity period"); ++ } ++ if (interval == null) ++ throw new CertificateNotYetValidException("Null validity period"); ++ interval.valid(date); ++ } ++ ++ /** ++ * Return the requested attribute from the certificate. ++ * ++ * @param name the name of the attribute. ++ * @exception CertificateParsingException on invalid attribute identifier. ++ */ ++ public Object get(String name) ++ throws CertificateParsingException { ++ X509AttributeName attr = new X509AttributeName(name); ++ String id = attr.getPrefix(); ++ if (!(id.equalsIgnoreCase(NAME))) { ++ throw new CertificateParsingException("Invalid root of " ++ + "attribute name, expected [" + NAME + ++ "], received " + "[" + id + "]"); ++ } ++ attr = new X509AttributeName(attr.getSuffix()); ++ id = attr.getPrefix(); ++ ++ if (id.equalsIgnoreCase(INFO)) { ++ if (attr.getSuffix() != null) { ++ try { ++ return info.get(attr.getSuffix()); ++ } catch (IOException e) { ++ throw new CertificateParsingException(e.toString()); ++ } catch (CertificateException e) { ++ throw new CertificateParsingException(e.toString()); ++ } ++ } else { ++ return (info); ++ } ++ } else if (id.equalsIgnoreCase(ALG_ID)) { ++ return (algId); ++ } else if (id.equalsIgnoreCase(SIGNATURE)) { ++ return (signature); ++ } else if (id.equalsIgnoreCase(SIGNED_CERT)) { ++ return (signedCert); ++ } else { ++ throw new CertificateParsingException("Attribute name not " ++ + "recognized or get() not allowed for the same: " + id); ++ } ++ } ++ ++ /** ++ * Set the requested attribute in the certificate. ++ * ++ * @param name the name of the attribute. ++ * @param obj the value of the attribute. ++ * @exception CertificateException on invalid attribute identifier. ++ * @exception IOException on encoding error of attribute. ++ */ ++ public void set(String name, Object obj) ++ throws CertificateException, IOException { ++ // check if immutable ++ if (readOnly) ++ throw new CertificateException("cannot over-write existing" ++ + " certificate"); ++ ++ X509AttributeName attr = new X509AttributeName(name); ++ String id = attr.getPrefix(); ++ if (!(id.equalsIgnoreCase(NAME))) { ++ throw new CertificateException("Invalid root of attribute name," ++ + " expected [" + NAME + "], received " + id); ++ } ++ attr = new X509AttributeName(attr.getSuffix()); ++ id = attr.getPrefix(); ++ ++ if (id.equalsIgnoreCase(INFO)) { ++ if (attr.getSuffix() == null) { ++ if (!(obj instanceof X509CertInfo)) { ++ throw new CertificateException("Attribute value should" ++ + " be of type X509CertInfo."); ++ } ++ info = (X509CertInfo) obj; ++ signedCert = null; //reset this as certificate data has changed ++ } else { ++ info.set(attr.getSuffix(), obj); ++ signedCert = null; //reset this as certificate data has changed ++ } ++ } else { ++ throw new CertificateException("Attribute name not recognized or " + ++ "set() not allowed for the same: " + id); ++ } ++ } ++ ++ /** ++ * Delete the requested attribute from the certificate. ++ * ++ * @param name the name of the attribute. ++ * @exception CertificateException on invalid attribute identifier. ++ * @exception IOException on other errors. ++ */ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ // check if immutable ++ if (readOnly) ++ throw new CertificateException("cannot over-write existing" ++ + " certificate"); ++ ++ X509AttributeName attr = new X509AttributeName(name); ++ String id = attr.getPrefix(); ++ if (!(id.equalsIgnoreCase(NAME))) { ++ throw new CertificateException("Invalid root of attribute name," ++ + " expected [" ++ + NAME + "], received " + id); ++ } ++ attr = new X509AttributeName(attr.getSuffix()); ++ id = attr.getPrefix(); ++ ++ if (id.equalsIgnoreCase(INFO)) { ++ if (attr.getSuffix() != null) { ++ info = null; ++ } else { ++ info.delete(attr.getSuffix()); ++ } ++ } else if (id.equalsIgnoreCase(ALG_ID)) { ++ algId = null; ++ } else if (id.equalsIgnoreCase(SIGNATURE)) { ++ signature = null; ++ } else if (id.equalsIgnoreCase(SIGNED_CERT)) { ++ signedCert = null; ++ } else { ++ throw new CertificateException("Attribute name not recognized or " + ++ "delete() not allowed for the same: " + id); ++ } ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getElements() { ++ Vector elements = new Vector(); ++ elements.addElement(NAME + DOT + INFO); ++ elements.addElement(NAME + DOT + ALG_ID); ++ elements.addElement(NAME + DOT + SIGNATURE); ++ elements.addElement(NAME + DOT + SIGNED_CERT); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ /** ++ * Returns a printable representation of the certificate. This does not ++ * contain all the information available to distinguish this from any ++ * other certificate. The certificate must be fully constructed ++ * before this function may be called. ++ */ ++ public String toString() { ++ if (info == null || algId == null || signature == null) ++ return ""; ++ ++ StringBuffer sb = new StringBuffer("[\n"+info.toString() + "\n" + " Algorithm: [" + algId.toString() + "]\n"); ++ ++ org.mozilla.jss.netscape.security.util.PrettyPrintFormat pp = ++ new org.mozilla.jss.netscape.security.util.PrettyPrintFormat(" ", 20); ++ String signaturebits = pp.toHexString(signature); ++ sb.append(" Signature:\n" + signaturebits); ++ sb.append("]"); ++ ++ return sb.toString(); ++ } ++ ++ // the strongly typed gets, as per java.security.cert.X509Certificate ++ ++ /** ++ * Gets the publickey from this certificate. ++ * ++ * @return the publickey. ++ */ ++ public PublicKey getPublicKey() { ++ if (info == null) ++ return null; ++ try { ++ PublicKey key = (PublicKey) info.get(CertificateX509Key.NAME ++ + DOT + CertificateX509Key.KEY); ++ return key; ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets the version number from the certificate. ++ * ++ * @return the version number. ++ */ ++ public int getVersion() { ++ if (info == null) ++ return -1; ++ try { ++ int vers = ((Integer) info.get(CertificateVersion.NAME ++ + DOT + CertificateVersion.VERSION)).intValue(); ++ return vers; ++ } catch (Exception e) { ++ return -1; ++ } ++ } ++ ++ /** ++ * Gets the serial number from the certificate. ++ * ++ * @return the serial number. ++ */ ++ public BigInteger getSerialNumber() { ++ if (info == null) ++ return null; ++ try { ++ SerialNumber ser = (SerialNumber) info.get( ++ CertificateSerialNumber.NAME + DOT + ++ CertificateSerialNumber.NUMBER); ++ return ser.getNumber().toBigInteger(); ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets the subject distinguished name from the certificate. ++ * ++ * @return the subject name. ++ */ ++ public Principal getSubjectDN() { ++ if (info == null) ++ return null; ++ try { ++ Principal subject = (Principal) info.get( ++ CertificateSubjectName.NAME + DOT + ++ CertificateSubjectName.DN_NAME); ++ return subject; ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ public CertificateSubjectName getSubjectObj() { ++ return info.getSubjectObj(); ++ } ++ ++ public X509CertInfo getInfo() { ++ return info; ++ } ++ ++ /** ++ * Gets the issuer distinguished name from the certificate. ++ * ++ * @return the issuer name. ++ */ ++ public Principal getIssuerDN() { ++ if (info == null) ++ return null; ++ try { ++ Principal issuer = (Principal) info.get( ++ CertificateIssuerName.NAME + DOT + ++ CertificateIssuerName.DN_NAME); ++ return issuer; ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ public CertificateIssuerName getIssuerObj() { ++ return info.getIssuerObj(); ++ } ++ ++ /** ++ * Gets the notBefore date from the validity period of the certificate. ++ * ++ * @return the start date of the validity period. ++ */ ++ public Date getNotBefore() { ++ if (info == null) ++ return null; ++ try { ++ Date d = (Date) info.get(CertificateValidity.NAME + DOT + ++ CertificateValidity.NOT_BEFORE); ++ return d; ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets the notAfter date from the validity period of the certificate. ++ * ++ * @return the end date of the validity period. ++ */ ++ public Date getNotAfter() { ++ if (info == null) ++ return null; ++ try { ++ Date d = (Date) info.get(CertificateValidity.NAME + DOT + ++ CertificateValidity.NOT_AFTER); ++ return d; ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets the DER encoded certificate informations, the tbsCertificate from this certificate. ++ * This can be used to verify the signature independently. ++ * ++ * @return the DER encoded certificate information. ++ * @exception CertificateEncodingException if an encoding error occurs. ++ */ ++ public byte[] getTBSCertificate() throws CertificateEncodingException { ++ if (info != null) { ++ return info.getEncodedInfo(); ++ } else ++ throw new CertificateEncodingException("Uninitialized certificate"); ++ } ++ ++ /** ++ * Gets the raw Signature bits from the certificate. ++ * ++ * @return the signature. ++ */ ++ public byte[] getSignature() { ++ if (signature == null) ++ return null; ++ byte[] dup = new byte[signature.length]; ++ System.arraycopy(signature, 0, dup, 0, dup.length); ++ return dup; ++ } ++ ++ /** ++ * Gets the signature algorithm name for the certificate ++ * signature algorithm. ++ * For example, the string "SHA-1/DSA" or "DSS". ++ * ++ * @return the signature algorithm name. ++ */ ++ public String getSigAlgName() { ++ if (algId == null) ++ return null; ++ return (algId.getName()); ++ } ++ ++ /** ++ * Gets the signature algorithm OID string from the certificate. ++ * For example, the string "1.2.840.10040.4.3" ++ * ++ * @return the signature algorithm oid string. ++ */ ++ public String getSigAlgOID() { ++ if (algId == null) ++ return null; ++ ObjectIdentifier oid = algId.getOID(); ++ return (oid.toString()); ++ } ++ ++ /** ++ * Gets the DER encoded signature algorithm parameters from this ++ * certificate's signature algorithm. ++ * ++ * @return the DER encoded signature algorithm parameters, or ++ * null if no parameters are present. ++ */ ++ public byte[] getSigAlgParams() { ++ if (algId == null) ++ return null; ++ try { ++ return algId.getEncodedParams(); ++ } catch (IOException e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets the Issuer Unique Identity from the certificate. ++ * ++ * @return the Issuer Unique Identity. ++ */ ++ public boolean[] getIssuerUniqueID() { ++ if (info == null) ++ return null; ++ try { ++ UniqueIdentity id = (UniqueIdentity) info.get( ++ CertificateIssuerUniqueIdentity.NAME ++ + DOT + CertificateIssuerUniqueIdentity.ID); ++ if (id == null) ++ return null; ++ else ++ return (id.getId()); ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets the Subject Unique Identity from the certificate. ++ * ++ * @return the Subject Unique Identity. ++ */ ++ public boolean[] getSubjectUniqueID() { ++ if (info == null) ++ return null; ++ try { ++ UniqueIdentity id = (UniqueIdentity) info.get( ++ CertificateSubjectUniqueIdentity.NAME ++ + DOT + CertificateSubjectUniqueIdentity.ID); ++ if (id == null) ++ return null; ++ else ++ return (id.getId()); ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets a Set of the extension(s) marked CRITICAL in the ++ * certificate by OID strings. ++ * ++ * @return a set of the extension oid strings in the ++ * certificate that are marked critical. ++ */ ++ public Set getCriticalExtensionOIDs() { ++ if (info == null) ++ return null; ++ try { ++ CertificateExtensions exts = (CertificateExtensions) info.get( ++ CertificateExtensions.NAME); ++ if (exts == null) ++ return null; ++ Set extSet = new LinkedHashSet(); ++ Extension ex; ++ for (Enumeration e = exts.getAttributes(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ if (ex.isCritical()) ++ extSet.add(ex.getExtensionId().toString()); ++ } ++ return extSet; ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Gets a Set of the extension(s) marked NON-CRITICAL in the ++ * certificate by OID strings. ++ * ++ * @return a set of the extension oid strings in the ++ * certificate that are NOT marked critical. ++ */ ++ public Set getNonCriticalExtensionOIDs() { ++ if (info == null) ++ return null; ++ try { ++ CertificateExtensions exts = (CertificateExtensions) info.get( ++ CertificateExtensions.NAME); ++ if (exts == null) ++ return null; ++ ++ Set extSet = new LinkedHashSet(); ++ Extension ex; ++ for (Enumeration e = exts.getAttributes(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ if (!ex.isCritical()) ++ extSet.add(ex.getExtensionId().toString()); ++ } ++ return extSet; ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ public Extension getExtension(String oid) { ++ try { ++ CertificateExtensions exts = (CertificateExtensions) info.get( ++ CertificateExtensions.NAME); ++ if (exts == null) ++ return null; ++ ObjectIdentifier findOID = new ObjectIdentifier(oid); ++ Extension ex = null; ++ ; ++ ObjectIdentifier inCertOID; ++ for (Enumeration e = exts.getAttributes(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ inCertOID = ex.getExtensionId(); ++ if (inCertOID.equals(findOID)) { ++ return ex; ++ } ++ } ++ } catch (Exception e) { ++ } ++ return null; ++ } ++ ++ /** ++ * Gets the DER encoded extension identified by the passed ++ * in oid String. ++ * ++ * @param oid the Object Identifier value for the extension. ++ */ ++ public byte[] getExtensionValue(String oid) { ++ DerOutputStream out = null; ++ try { ++ String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); ++ Extension certExt = null; ++ ++ if (extAlias == null) { // may be unknown ++ // get the extensions, search thru' for this oid ++ CertificateExtensions exts = (CertificateExtensions) info.get( ++ CertificateExtensions.NAME); ++ if (exts == null) ++ return null; ++ ++ ObjectIdentifier findOID = new ObjectIdentifier(oid); ++ Extension ex = null; ++ ; ++ ObjectIdentifier inCertOID; ++ for (Enumeration e = exts.getAttributes(); e.hasMoreElements();) { ++ ex = e.nextElement(); ++ inCertOID = ex.getExtensionId(); ++ if (inCertOID.equals(findOID)) { ++ certExt = ex; ++ break; ++ } ++ } ++ } else { // there's sub-class that can handle this extension ++ certExt = (Extension) this.get(extAlias); ++ } ++ if (certExt == null) ++ return null; ++ byte[] extData = certExt.getExtensionValue(); ++ if (extData == null) ++ return null; ++ ++ out = new DerOutputStream(); ++ out.putOctetString(extData); ++ return out.toByteArray(); ++ } catch (Exception e) { ++ return null; ++ } finally { ++ if (out != null) { ++ try { ++ out.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++ ++ /** ++ * Get a boolean array representing the bits of the KeyUsage extension, ++ * (oid = 2.5.29.15). ++ * ++ * @return the bit values of this extension as an array of booleans. ++ */ ++ public boolean[] getKeyUsage() { ++ try { ++ String extAlias = OIDMap.getName(new ObjectIdentifier( ++ KEY_USAGE_OID)); ++ if (extAlias == null) ++ return null; ++ ++ KeyUsageExtension certExt = (KeyUsageExtension) this.get(extAlias); ++ if (certExt == null) ++ return null; ++ ++ return certExt.getBits(); ++ } catch (Exception e) { ++ return null; ++ } ++ } ++ ++ /** ++ * Get the certificate constraints path length from the ++ * the critical BasicConstraints extension, (oid = 2.5.29.19). ++ * ++ * @return the length of the constraint. ++ */ ++ public int getBasicConstraints() { ++ try { ++ String extAlias = OIDMap.getName(new ObjectIdentifier( ++ BASIC_CONSTRAINT_OID)); ++ if (extAlias == null) ++ return -1; ++ BasicConstraintsExtension certExt = ++ (BasicConstraintsExtension) this.get(extAlias); ++ if (certExt == null) ++ return -1; ++ ++ if (((Boolean) certExt.get(BasicConstraintsExtension.IS_CA)).booleanValue() == true) ++ return ((Integer) certExt.get( ++ BasicConstraintsExtension.PATH_LEN)).intValue(); ++ else ++ return -1; ++ } catch (Exception e) { ++ return -1; ++ } ++ } ++ ++ public boolean getBasicConstraintsIsCA() { ++ boolean isCA = false; ++ try { ++ String extAlias = OIDMap.getName(new ObjectIdentifier( ++ BASIC_CONSTRAINT_OID)); ++ if (extAlias == null) ++ return false; ++ ++ BasicConstraintsExtension certExt = ++ (BasicConstraintsExtension) this.get(extAlias); ++ if (certExt == null) ++ return false; ++ ++ isCA = ((Boolean) certExt.get(BasicConstraintsExtension.IS_CA)).booleanValue(); ++ } catch (Exception e) { ++ return false; ++ } ++ return isCA; ++ } ++ ++ /************************************************************/ ++ ++ /* ++ * Cert is a SIGNED ASN.1 macro, a three elment sequence: ++ * ++ * - Data to be signed (ToBeSigned) -- the "raw" cert ++ * - Signature algorithm (SigAlgId) ++ * - The signature bits ++ * ++ * This routine unmarshals the certificate, saving the signature ++ * parts away for later verification. ++ */ ++ private void parse(DerValue val) throws CertificateException, IOException { ++ // check if can over write the certificate ++ if (readOnly) ++ throw new CertificateParsingException( ++ "cannot over-write existing certificate"); ++ ++ readOnly = true; ++ DerValue seq[] = new DerValue[3]; ++ ++ seq[0] = val.data.getDerValue(); ++ seq[1] = val.data.getDerValue(); ++ seq[2] = val.data.getDerValue(); ++ ++ if (val.data.available() != 0) { ++ throw new CertificateParsingException("signed overrun, bytes = " ++ + val.data.available()); ++ } ++ if (seq[0].tag != DerValue.tag_Sequence) { ++ throw new CertificateParsingException("signed fields invalid"); ++ } ++ ++ algId = AlgorithmId.parse(seq[1]); ++ signature = seq[2].getBitString(); ++ ++ if (seq[1].data.available() != 0) { ++ throw new CertificateParsingException("algid field overrun"); ++ } ++ if (seq[2].data.available() != 0) ++ throw new CertificateParsingException("signed fields overrun"); ++ ++ // The CertificateInfo ++ if (info == null) { ++ info = new X509CertInfo(seq[0]); ++ } ++ } ++ ++ /** ++ * Serialization write ... X.509 certificates serialize as ++ * themselves, and they're parsed when they get read back. ++ * (Actually they serialize as some type data from the ++ * serialization subsystem, then the cert data.) ++ */ ++ private void writeObject(ObjectOutputStream stream) throws CertificateException, IOException { ++ encode(stream); ++ } ++ ++ /** ++ * Serialization read ... X.509 certificates serialize as ++ * themselves, and they're parsed when they get read back. ++ */ ++ private void readObject(ObjectInputStream stream) throws CertificateException, IOException { ++ decode(stream); ++ } ++ ++ protected static class CertificateRep1 implements java.io.Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -5207881613631592409L; ++ private String type1; ++ private byte[] data1; ++ ++ /** ++ * Construct the alternate Certificate class with the Certificate ++ * type and Certificate encoding bytes. ++ * ++ *

++ * ++ * @param type the standard name of the Certificate type. ++ *

++ * ++ * @param data the Certificate data. ++ */ ++ protected CertificateRep1(String type, byte[] data) { ++ this.type1 = type; ++ this.data1 = data; ++ } ++ ++ /** ++ * Resolve the Certificate Object. ++ * ++ *

++ * ++ * @return the resolved Certificate Object. ++ * ++ * @throws java.io.ObjectStreamException if the Certificate could not ++ * be resolved. ++ */ ++ protected Object readResolve() throws java.io.ObjectStreamException { ++ try { ++ @SuppressWarnings("unused") ++ CertificateFactory cf = CertificateFactory.getInstance(type1); // check for errors ++ return new X509CertImpl(data1); ++ ++ /* ++ return cf.generateCertificate ++ (new java.io.ByteArrayInputStream(data1)); ++ */ ++ } catch (CertificateException e) { ++ throw new java.io.NotSerializableException("java.security.cert.Certificate: " + ++ type1 + ++ ": " + ++ e.getMessage()); ++ } ++ } ++ ++ } ++ ++ protected Object writeReplace() throws java.io.ObjectStreamException { ++ try { ++ return new CertificateRep1("X.509", getEncoded()); ++ } catch (CertificateException e) { ++ throw new java.io.NotSerializableException("java.security.cert.Certificate: " + ++ "X.509" + ++ ": " + ++ e.getMessage()); ++ } ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X509CertInfo.java b/org/mozilla/jss/netscape/security/x509/X509CertInfo.java +new file mode 100644 +index 00000000..ea565d9d +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X509CertInfo.java +@@ -0,0 +1,976 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.io.ObjectOutputStream; ++import java.io.OutputStream; ++import java.io.Serializable; ++import java.security.cert.CertificateEncodingException; ++import java.security.cert.CertificateException; ++import java.security.cert.CertificateParsingException; ++import java.util.Enumeration; ++import java.util.Hashtable; ++import java.util.Vector; ++ ++import org.mozilla.jss.netscape.security.util.DerInputStream; ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * The X509CertInfo class represents X.509 certificate information. ++ * ++ *

++ * X.509 certificates have several base data elements, including: ++ *

    ++ * ++ *
  • The Subject Name, an X.500 Distinguished Name for the entity (subject) for which the certificate was ++ * issued. ++ * ++ *
  • The Subject Public Key, the public key of the subject. This is one of the most important parts of the ++ * certificate. ++ * ++ *
  • The Validity Period, a time period (e.g. six months) within which the certificate is valid (unless ++ * revoked). ++ * ++ *
  • The Issuer Name, an X.500 Distinguished Name for the Certificate Authority (CA) which issued the ++ * certificate. ++ * ++ *
  • A Serial Number assigned by the CA, for use in certificate revocation and other applications. ++ * ++ * @author Amit Kapoor ++ * @author Hemma Prafullchandra ++ * @version 1.16 ++ * @see CertAttrSet ++ * @see Serializable ++ * @see X509CertImpl ++ */ ++public class X509CertInfo implements CertAttrSet, Serializable { ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -5094073467876311577L; ++ /** ++ * Identifier for this attribute, to be used with the ++ * get, set, delete methods of Certificate, x509 type. ++ */ ++ public static final String IDENT = "x509.info"; ++ // Certificate attribute names ++ public static final String NAME = "info"; ++ public static final String VERSION = CertificateVersion.NAME; ++ public static final String SERIAL_NUMBER = CertificateSerialNumber.NAME; ++ public static final String ALGORITHM_ID = CertificateAlgorithmId.NAME; ++ public static final String ISSUER = CertificateIssuerName.NAME; ++ public static final String VALIDITY = CertificateValidity.NAME; ++ public static final String SUBJECT = CertificateSubjectName.NAME; ++ public static final String KEY = CertificateX509Key.NAME; ++ public static final String ISSUER_ID = CertificateIssuerUniqueIdentity.NAME; ++ public static final String SUBJECT_ID = CertificateSubjectUniqueIdentity.NAME; ++ public static final String EXTENSIONS = CertificateExtensions.NAME; ++ ++ // X509.v1 data ++ protected CertificateVersion version = new CertificateVersion(); ++ protected CertificateSerialNumber serialNum = null; ++ protected CertificateAlgorithmId algId = null; ++ protected CertificateIssuerName issuer = null; ++ protected CertificateValidity interval = null; ++ protected CertificateSubjectName subject = null; ++ protected CertificateX509Key pubKey = null; ++ ++ // X509.v2 & v3 extensions ++ protected CertificateIssuerUniqueIdentity issuerUniqueId = null; ++ protected CertificateSubjectUniqueIdentity subjectUniqueId = null; ++ ++ // X509.v3 extensions ++ protected CertificateExtensions extensions = null; ++ ++ // Attribute numbers for internal manipulation ++ private static final int ATTR_VERSION = 1; ++ private static final int ATTR_SERIAL = 2; ++ private static final int ATTR_ALGORITHM = 3; ++ private static final int ATTR_ISSUER = 4; ++ private static final int ATTR_VALIDITY = 5; ++ private static final int ATTR_SUBJECT = 6; ++ private static final int ATTR_KEY = 7; ++ private static final int ATTR_ISSUER_ID = 8; ++ private static final int ATTR_SUBJECT_ID = 9; ++ private static final int ATTR_EXTENSIONS = 10; ++ ++ // DER encoded CertificateInfo data ++ private byte[] rawCertInfo = null; ++ ++ // The certificate attribute name to integer mapping stored here ++ private static final Hashtable map = new Hashtable(); ++ static { ++ map.put(VERSION, Integer.valueOf(ATTR_VERSION)); ++ map.put(SERIAL_NUMBER, Integer.valueOf(ATTR_SERIAL)); ++ map.put(ALGORITHM_ID, Integer.valueOf(ATTR_ALGORITHM)); ++ map.put(ISSUER, Integer.valueOf(ATTR_ISSUER)); ++ map.put(VALIDITY, Integer.valueOf(ATTR_VALIDITY)); ++ map.put(SUBJECT, Integer.valueOf(ATTR_SUBJECT)); ++ map.put(KEY, Integer.valueOf(ATTR_KEY)); ++ map.put(ISSUER_ID, Integer.valueOf(ATTR_ISSUER_ID)); ++ map.put(SUBJECT_ID, Integer.valueOf(ATTR_SUBJECT_ID)); ++ map.put(EXTENSIONS, Integer.valueOf(ATTR_EXTENSIONS)); ++ } ++ ++ /** ++ * Construct an uninitialized X509CertInfo on which ++ * decode must later be called (or which may be deserialized). ++ */ ++ public X509CertInfo() { ++ } ++ ++ /** ++ * Unmarshals a certificate from its encoded form, parsing the ++ * encoded bytes. This form of constructor is used by agents which ++ * need to examine and use certificate contents. That is, this is ++ * one of the more commonly used constructors. Note that the buffer ++ * must include only a certificate, and no "garbage" may be left at ++ * the end. If you need to ignore data at the end of a certificate, ++ * use another constructor. ++ * ++ * @param cert the encoded bytes, with no trailing data. ++ * @exception CertificateParsingException on parsing errors. ++ */ ++ public X509CertInfo(byte[] cert) throws CertificateParsingException { ++ try { ++ DerValue in = new DerValue(cert); ++ ++ parse(in); ++ } catch (IOException e) { ++ throw new CertificateParsingException(e); ++ } ++ } ++ ++ /** ++ * Unmarshal a certificate from its encoded form, parsing a DER value. ++ * This form of constructor is used by agents which need to examine ++ * and use certificate contents. ++ * ++ * @param derVal the der value containing the encoded cert. ++ * @exception CertificateParsingException on parsing errors. ++ */ ++ public X509CertInfo(DerValue derVal) throws CertificateParsingException { ++ try { ++ parse(derVal); ++ } catch (IOException e) { ++ throw new CertificateParsingException(e); ++ } ++ } ++ ++ /** ++ * Decode an X.509 certificate from an input stream. ++ * ++ * @param in an input stream holding at least one certificate ++ * @exception CertificateParsingException on decoding errors. ++ * @exception IOException on other errors. ++ */ ++ public void decode(InputStream in) ++ throws CertificateParsingException, IOException { ++ DerValue val = new DerValue(in); ++ ++ parse(val); ++ } ++ ++ /** ++ * Appends the certificate to an output stream. ++ * ++ * @param out an output stream to which the certificate is appended. ++ * @exception CertificateException on encoding errors. ++ * @exception IOException on other errors. ++ */ ++ public void encode(OutputStream out) ++ throws CertificateException, IOException { ++ encode(out, false); ++ } ++ ++ /** ++ * Appends the certificate to an output stream. ++ * ++ * @param out An output stream to which the certificate is appended. ++ * @param ignoreCache Whether to ignore the internal cache when encoding. ++ * (the cache can easily become out of date). ++ */ ++ public void encode(OutputStream out, boolean ignoreCache) ++ throws IOException, CertificateException { ++ if (ignoreCache || (rawCertInfo == null)) { ++ DerOutputStream tmp = new DerOutputStream(); ++ emit(tmp); ++ rawCertInfo = tmp.toByteArray(); ++ } ++ out.write(rawCertInfo); ++ } ++ ++ /** ++ * Return an enumeration of names of attributes existing within this ++ * attribute. ++ */ ++ public Enumeration getAttributeNames() { ++ Vector elements = new Vector(); ++ elements.addElement(VERSION); ++ elements.addElement(SERIAL_NUMBER); ++ elements.addElement(ALGORITHM_ID); ++ elements.addElement(ISSUER); ++ elements.addElement(VALIDITY); ++ elements.addElement(SUBJECT); ++ elements.addElement(KEY); ++ elements.addElement(ISSUER_ID); ++ elements.addElement(SUBJECT_ID); ++ elements.addElement(EXTENSIONS); ++ ++ return (elements.elements()); ++ } ++ ++ /** ++ * Return the name of this attribute. ++ */ ++ public String getName() { ++ return (NAME); ++ } ++ ++ /** ++ * Returns the encoded certificate info. ++ * ++ * @exception CertificateEncodingException on encoding information errors. ++ */ ++ public byte[] getEncodedInfo() throws CertificateEncodingException { ++ return getEncodedInfo(false); ++ } ++ ++ public byte[] getEncodedInfo(boolean ignoreCache) throws CertificateEncodingException { ++ try { ++ if (ignoreCache || (rawCertInfo == null)) { ++ DerOutputStream tmp = new DerOutputStream(); ++ emit(tmp); ++ rawCertInfo = tmp.toByteArray(); ++ } ++ byte[] dup = new byte[rawCertInfo.length]; ++ System.arraycopy(rawCertInfo, 0, dup, 0, dup.length); ++ return dup; ++ } catch (IOException e) { ++ throw new CertificateEncodingException(e); ++ } catch (CertificateException e) { ++ throw new CertificateEncodingException(e); ++ } ++ } ++ ++ /** ++ * Compares two X509CertInfo objects. This is false if the ++ * certificates are not both X.509 certs, otherwise it ++ * compares them as binary data. ++ * ++ * @param other the object being compared with this one ++ * @return true iff the certificates are equivalent ++ */ ++ public boolean equals(Object other) { ++ if (other instanceof X509CertInfo) { ++ return equals((X509CertInfo) other); ++ } else { ++ return false; ++ } ++ } ++ ++ /** ++ * Compares two certificates, returning false if any data ++ * differs between the two. ++ * ++ * @param other the object being compared with this one ++ * @return true iff the certificates are equivalent ++ */ ++ public boolean equals(X509CertInfo other) { ++ if (this == other) { ++ return (true); ++ } else if (rawCertInfo == null || other.rawCertInfo == null) { ++ return (false); ++ } else if (rawCertInfo.length != other.rawCertInfo.length) { ++ return (false); ++ } ++ for (int i = 0; i < rawCertInfo.length; i++) { ++ if (rawCertInfo[i] != other.rawCertInfo[i]) { ++ return (false); ++ } ++ } ++ return (true); ++ } ++ ++ /** ++ * Calculates a hash code value for the object. Objects ++ * which are equal will also have the same hashcode. ++ */ ++ public int hashCode() { ++ int retval = 0; ++ ++ for (int i = 1; i < rawCertInfo.length; i++) { ++ retval += rawCertInfo[i] * i; ++ } ++ return (retval); ++ } ++ ++ /** ++ * Returns a printable representation of the certificate. ++ */ ++ public String toString() { ++ ++ if (subject == null || pubKey == null || interval == null ++ || issuer == null || algId == null || serialNum == null) { ++ throw new NullPointerException("X.509 cert is incomplete"); ++ } ++ StringBuffer sb = new StringBuffer("[\n" + " " + version.toString() + "\n" + " Subject: " ++ + subject.toString() + "\n" ++ + " Signature Algorithm: " + algId.toString() + "\n" + " Key: " + pubKey.toString() + "\n"); ++ ++ sb.append(" " + interval.toString() + "\n" + " Issuer: " + issuer.toString() + "\n" ++ + " " + serialNum.toString() + "\n"); ++ // optional v2, v3 extras ++ if (issuerUniqueId != null) { ++ sb.append(" Issuer Id:\n" + issuerUniqueId.toString() + "\n"); ++ } ++ if (subjectUniqueId != null) { ++ sb.append(" Subject Id:\n" + subjectUniqueId.toString() + "\n"); ++ } ++ if (extensions != null) { ++ org.mozilla.jss.netscape.security.util.PrettyPrintFormat pp = ++ new org.mozilla.jss.netscape.security.util.PrettyPrintFormat(" ", 20); ++ for (int i = 0; i < extensions.size(); i++) { ++ sb.append(" Extension[" + i + "] = "); ++ Extension ext = extensions.elementAt(i); ++ DerOutputStream out = null; ++ try { ++ if (OIDMap.getClass(ext.getExtensionId()) == null) { ++ sb.append(ext.toString()); ++ byte[] extValue = ext.getExtensionValue(); ++ if (extValue != null) { ++ out = new DerOutputStream(); ++ out.putOctetString(extValue); ++ extValue = out.toByteArray(); ++ String extValuebits = pp.toHexString(extValue); ++ sb.append("Extension unknown: " ++ + "DER encoded OCTET string =\n" ++ + extValuebits); ++ } ++ } else ++ sb.append(ext.toString()); //sub-class exists ++ } catch (CertificateException e) { ++ sb.append(", Error parsing this extension"); ++ } catch (IOException e) { ++ sb.append(", Error parsing this extension"); ++ } finally { ++ if (out != null) { ++ try { ++ out.close(); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ } ++ } ++ sb.append("\n]"); ++ return sb.toString(); ++ } ++ ++ /** ++ * Set the certificate attribute. ++ * ++ * @param name the name of the Certificate attribute. ++ * @param val the value of the Certificate attribute. ++ * @exception CertificateException on invalid attributes. ++ * @exception IOException on other errors. ++ */ ++ public void set(String name, Object val) ++ throws CertificateException, IOException { ++ X509AttributeName attrName = new X509AttributeName(name); ++ ++ int attr = attributeMap(attrName.getPrefix()); ++ if (attr == 0) { ++ throw new CertificateException("Attribute name not recognized: " ++ + name); ++ } ++ // set rawCertInfo to null, so that we are forced to re-encode ++ rawCertInfo = null; ++ ++ switch (attr) { ++ case ATTR_VERSION: ++ if (attrName.getSuffix() == null) { ++ setVersion(val); ++ } else { ++ version.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_SERIAL: ++ if (attrName.getSuffix() == null) { ++ setSerialNumber(val); ++ } else { ++ serialNum.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_ALGORITHM: ++ if (attrName.getSuffix() == null) { ++ setAlgorithmId(val); ++ } else { ++ algId.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_ISSUER: ++ if (attrName.getSuffix() == null) { ++ setIssuer(val); ++ } else { ++ issuer.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_VALIDITY: ++ if (attrName.getSuffix() == null) { ++ setValidity(val); ++ } else { ++ interval.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_SUBJECT: ++ if (attrName.getSuffix() == null) { ++ setSubject(val); ++ } else { ++ subject.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_KEY: ++ if (attrName.getSuffix() == null) { ++ setKey(val); ++ } else { ++ pubKey.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_ISSUER_ID: ++ if (attrName.getSuffix() == null) { ++ setIssuerUniqueId(val); ++ } else { ++ issuerUniqueId.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_SUBJECT_ID: ++ if (attrName.getSuffix() == null) { ++ setSubjectUniqueId(val); ++ } else { ++ subjectUniqueId.set(attrName.getSuffix(), val); ++ } ++ break; ++ ++ case ATTR_EXTENSIONS: ++ if (attrName.getSuffix() == null) { ++ setExtensions(val); ++ } else { ++ extensions.set(attrName.getSuffix(), val); ++ } ++ break; ++ } ++ } ++ ++ /** ++ * Delete the certificate attribute. ++ * ++ * @param name the name of the Certificate attribute. ++ * @exception CertificateException on invalid attributes. ++ * @exception IOException on other errors. ++ */ ++ public void delete(String name) ++ throws CertificateException, IOException { ++ X509AttributeName attrName = new X509AttributeName(name); ++ ++ int attr = attributeMap(attrName.getPrefix()); ++ if (attr == 0) { ++ throw new CertificateException("Attribute name not recognized: " ++ + name); ++ } ++ // set rawCertInfo to null, so that we are forced to re-encode ++ rawCertInfo = null; ++ ++ switch (attr) { ++ case ATTR_VERSION: ++ if (attrName.getSuffix() == null) { ++ version = null; ++ } else { ++ version.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_SERIAL): ++ if (attrName.getSuffix() == null) { ++ serialNum = null; ++ } else { ++ serialNum.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_ALGORITHM): ++ if (attrName.getSuffix() == null) { ++ algId = null; ++ } else { ++ algId.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_ISSUER): ++ if (attrName.getSuffix() == null) { ++ issuer = null; ++ } else { ++ issuer.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_VALIDITY): ++ if (attrName.getSuffix() == null) { ++ interval = null; ++ } else { ++ interval.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_SUBJECT): ++ if (attrName.getSuffix() == null) { ++ subject = null; ++ } else { ++ subject.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_KEY): ++ if (attrName.getSuffix() == null) { ++ pubKey = null; ++ } else { ++ pubKey.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_ISSUER_ID): ++ if (attrName.getSuffix() == null) { ++ issuerUniqueId = null; ++ } else { ++ issuerUniqueId.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_SUBJECT_ID): ++ if (attrName.getSuffix() == null) { ++ subjectUniqueId = null; ++ } else { ++ subjectUniqueId.delete(attrName.getSuffix()); ++ } ++ break; ++ case (ATTR_EXTENSIONS): ++ if (attrName.getSuffix() == null) { ++ extensions = null; ++ } else { ++ extensions.delete(attrName.getSuffix()); ++ } ++ break; ++ } ++ } ++ ++ /** ++ * Get the certificate attribute. ++ * ++ * @param name the name of the Certificate attribute. ++ * ++ * @exception CertificateException on invalid attributes. ++ * @exception IOException on other errors. ++ */ ++ public Object get(String name) ++ throws CertificateException, IOException { ++ X509AttributeName attrName = new X509AttributeName(name); ++ ++ int attr = attributeMap(attrName.getPrefix()); ++ if (attr == 0) { ++ throw new CertificateParsingException( ++ "Attribute name not recognized: " + name); ++ } ++ ++ switch (attr) { ++ case (ATTR_VERSION): ++ if (attrName.getSuffix() == null) { ++ return (version); ++ } else { ++ return (version.get(attrName.getSuffix())); ++ } ++ case (ATTR_SERIAL): ++ if (attrName.getSuffix() == null) { ++ return (serialNum); ++ } else { ++ return (serialNum.get(attrName.getSuffix())); ++ } ++ case (ATTR_ALGORITHM): ++ if (attrName.getSuffix() == null) { ++ return (algId); ++ } else { ++ return (algId.get(attrName.getSuffix())); ++ } ++ case (ATTR_ISSUER): ++ if (attrName.getSuffix() == null) { ++ return (issuer); ++ } else { ++ return (issuer.get(attrName.getSuffix())); ++ } ++ case (ATTR_VALIDITY): ++ if (attrName.getSuffix() == null) { ++ return (interval); ++ } else { ++ return (interval.get(attrName.getSuffix())); ++ } ++ case (ATTR_SUBJECT): ++ if (attrName.getSuffix() == null) { ++ return (subject); ++ } else { ++ return (subject.get(attrName.getSuffix())); ++ } ++ case (ATTR_KEY): ++ if (attrName.getSuffix() == null) { ++ return (pubKey); ++ } else { ++ return (pubKey.get(attrName.getSuffix())); ++ } ++ case (ATTR_ISSUER_ID): ++ if (attrName.getSuffix() == null) { ++ return (issuerUniqueId); ++ } else { ++ if (issuerUniqueId == null) ++ return null; ++ else ++ return (issuerUniqueId.get(attrName.getSuffix())); ++ } ++ case (ATTR_SUBJECT_ID): ++ if (attrName.getSuffix() == null) { ++ return (subjectUniqueId); ++ } else { ++ if (subjectUniqueId == null) ++ return null; ++ else ++ return (subjectUniqueId.get(attrName.getSuffix())); ++ } ++ case (ATTR_EXTENSIONS): ++ if (attrName.getSuffix() == null) { ++ return (extensions); ++ } else { ++ if (extensions == null) ++ return null; ++ else ++ return (extensions.get(attrName.getSuffix())); ++ } ++ } ++ return null; ++ } ++ ++ /* ++ * This routine unmarshals the certificate information. ++ */ ++ private void parse(DerValue val) ++ throws CertificateParsingException, IOException { ++ DerInputStream in; ++ DerValue tmp; ++ ++ if (val.tag != DerValue.tag_Sequence) { ++ throw new CertificateParsingException("signed fields invalid"); ++ } ++ rawCertInfo = val.toByteArray(); ++ ++ in = val.data; ++ ++ // Version ++ tmp = in.getDerValue(); ++ if (tmp.isContextSpecific((byte) 0)) { ++ version = new CertificateVersion(tmp); ++ tmp = in.getDerValue(); ++ } ++ ++ // Serial number ... an integer ++ serialNum = new CertificateSerialNumber(tmp); ++ ++ // Algorithm Identifier ++ algId = new CertificateAlgorithmId(in); ++ ++ // Issuer name ++ issuer = new CertificateIssuerName(in); ++ ++ // validity: SEQUENCE { start date, end date } ++ interval = new CertificateValidity(in); ++ ++ // subject name ++ subject = new CertificateSubjectName(in); ++ ++ // public key ++ pubKey = new CertificateX509Key(in); ++ ++ // If more data available, make sure version is not v1. ++ if (in.available() != 0) { ++ if (version.compare(CertificateVersion.V1) == 0) { ++ throw new CertificateParsingException("excess cert data"); ++ } ++ } else { ++ return; ++ } ++ ++ // Get the issuerUniqueId if present ++ tmp = in.getDerValue(); ++ if (tmp.isContextSpecific((byte) 1)) { ++ issuerUniqueId = new CertificateIssuerUniqueIdentity(tmp); ++ if (in.available() == 0) { ++ return; ++ } ++ tmp = in.getDerValue(); ++ } ++ ++ // Get the subjectUniqueId if present. ++ if (tmp.isContextSpecific((byte) 2)) { ++ subjectUniqueId = new CertificateSubjectUniqueIdentity(tmp); ++ if (in.available() == 0) { ++ return; ++ } ++ tmp = in.getDerValue(); ++ } ++ ++ // Get the extensions. ++ if (version.compare(CertificateVersion.V3) != 0) { ++ throw new CertificateParsingException("excess cert data"); ++ } ++ if (tmp.isConstructed() && tmp.isContextSpecific((byte) 3)) { ++ extensions = new CertificateExtensions(tmp.data); ++ } ++ } ++ ++ /* ++ * Marshal the contents of a "raw" certificate into a DER sequence. ++ */ ++ private void emit(DerOutputStream out) ++ throws CertificateException, IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ ++ // version number, iff not V1 ++ version.encode(tmp); ++ ++ // Encode serial number, issuer signing algorithm, issuer name ++ // and validity ++ serialNum.encode(tmp); ++ algId.encode(tmp); ++ issuer.encode(tmp); ++ interval.encode(tmp); ++ ++ // Encode subject (principal) and associated key ++ subject.encode(tmp); ++ pubKey.encode(tmp); ++ ++ // Encode issuerUniqueId & subjectUniqueId. ++ if (issuerUniqueId != null) { ++ issuerUniqueId.encode(tmp); ++ } ++ if (subjectUniqueId != null) { ++ subjectUniqueId.encode(tmp); ++ } ++ ++ // Write all the extensions. ++ if (extensions != null) { ++ extensions.encode(tmp); ++ } ++ ++ // Wrap the data; encoding of the "raw" cert is now complete. ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++ ++ /** ++ * Serialization write ... X.509 certificates serialize as ++ * themselves, and they're parsed when they get read back. ++ * (Actually they serialize as some type data from the ++ * serialization subsystem, then the cert data.) ++ */ ++ private void writeObject(ObjectOutputStream stream) throws CertificateException, IOException { ++ encode(stream); ++ } ++ ++ /** ++ * Serialization read ... X.509 certificates serialize as ++ * themselves, and they're parsed when they get read back. ++ */ ++ private void readObject(ObjectInputStream stream) throws CertificateException, IOException { ++ decode(stream); ++ } ++ ++ /** ++ * Returns the integer attribute number for the passed attribute name. ++ */ ++ private int attributeMap(String name) { ++ Integer num = map.get(name); ++ if (num == null) { ++ return (0); ++ } ++ return (num.intValue()); ++ } ++ ++ /** ++ * Set the version number of the certificate. ++ * ++ * @param val the Object class value for the Extensions ++ * @exception CertificateException on invalid data. ++ */ ++ private void setVersion(Object val) throws CertificateException { ++ if (!(val instanceof CertificateVersion)) { ++ throw new CertificateException("Version class type invalid."); ++ } ++ version = (CertificateVersion) val; ++ } ++ ++ /** ++ * Set the serial number of the certificate. ++ * ++ * @param val the Object class value for the CertificateSerialNumber ++ * @exception CertificateException on invalid data. ++ */ ++ private void setSerialNumber(Object val) throws CertificateException { ++ if (!(val instanceof CertificateSerialNumber)) { ++ throw new CertificateException("SerialNumber class type invalid."); ++ } ++ serialNum = (CertificateSerialNumber) val; ++ } ++ ++ /** ++ * Set the algorithm id of the certificate. ++ * ++ * @param val the Object class value for the AlgorithmId ++ * @exception CertificateException on invalid data. ++ */ ++ private void setAlgorithmId(Object val) throws CertificateException { ++ if (!(val instanceof CertificateAlgorithmId)) { ++ throw new CertificateException( ++ "AlgorithmId class type invalid."); ++ } ++ algId = (CertificateAlgorithmId) val; ++ } ++ ++ /** ++ * Set the issuer name of the certificate. ++ * ++ * @param val the Object class value for the issuer ++ * @exception CertificateException on invalid data. ++ */ ++ private void setIssuer(Object val) throws CertificateException { ++ if (!(val instanceof CertificateIssuerName)) { ++ throw new CertificateException( ++ "Issuer class type invalid."); ++ } ++ issuer = (CertificateIssuerName) val; ++ } ++ ++ public CertificateIssuerName getIssuerObj() { ++ return issuer; ++ } ++ ++ /** ++ * Set the validity interval of the certificate. ++ * ++ * @param val the Object class value for the CertificateValidity ++ * @exception CertificateException on invalid data. ++ */ ++ private void setValidity(Object val) throws CertificateException { ++ if (!(val instanceof CertificateValidity)) { ++ throw new CertificateException( ++ "CertificateValidity class type invalid."); ++ } ++ interval = (CertificateValidity) val; ++ } ++ ++ /** ++ * Set the subject name of the certificate. ++ * ++ * @param val the Object class value for the Subject ++ * @exception CertificateException on invalid data. ++ */ ++ private void setSubject(Object val) throws CertificateException { ++ if (!(val instanceof CertificateSubjectName)) { ++ throw new CertificateException( ++ "Subject class type invalid."); ++ } ++ subject = (CertificateSubjectName) val; ++ } ++ ++ public CertificateSubjectName getSubjectObj() { ++ return subject; ++ } ++ ++ /** ++ * Set the public key in the certificate. ++ * ++ * @param val the Object class value for the PublicKey ++ * @exception CertificateException on invalid data. ++ */ ++ private void setKey(Object val) throws CertificateException { ++ if (!(val instanceof CertificateX509Key)) { ++ throw new CertificateException( ++ "Key class type invalid."); ++ } ++ pubKey = (CertificateX509Key) val; ++ } ++ ++ /** ++ * Set the Issuer Unique Identity in the certificate. ++ * ++ * @param val the Object class value for the IssuerUniqueId ++ * @exception CertificateException ++ */ ++ private void setIssuerUniqueId(Object val) throws CertificateException { ++ if (version.compare(CertificateVersion.V2) < 0) { ++ throw new CertificateException("Invalid version"); ++ } ++ if (!(val instanceof CertificateIssuerUniqueIdentity)) { ++ throw new CertificateException( ++ "IssuerUniqueId class type invalid."); ++ } ++ issuerUniqueId = (CertificateIssuerUniqueIdentity) val; ++ } ++ ++ /** ++ * Set the Subject Unique Identity in the certificate. ++ * ++ * @param val the Object class value for the SubjectUniqueId ++ * @exception CertificateException ++ */ ++ private void setSubjectUniqueId(Object val) throws CertificateException { ++ if (version.compare(CertificateVersion.V2) < 0) { ++ throw new CertificateException("Invalid version"); ++ } ++ if (!(val instanceof CertificateSubjectUniqueIdentity)) { ++ throw new CertificateException( ++ "SubjectUniqueId class type invalid."); ++ } ++ subjectUniqueId = (CertificateSubjectUniqueIdentity) val; ++ } ++ ++ /** ++ * Set the extensions in the certificate. ++ * ++ * @param val the Object class value for the Extensions ++ * @exception CertificateException ++ */ ++ private void setExtensions(Object val) throws CertificateException { ++ if (version.compare(CertificateVersion.V3) < 0) { ++ throw new CertificateException("Invalid version"); ++ } ++ if (!(val instanceof CertificateExtensions)) { ++ throw new CertificateException( ++ "Extensions class type invalid."); ++ } ++ extensions = (CertificateExtensions) val; ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X509ExtensionException.java b/org/mozilla/jss/netscape/security/x509/X509ExtensionException.java +new file mode 100644 +index 00000000..ac6c90c9 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X509ExtensionException.java +@@ -0,0 +1,54 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.security.GeneralSecurityException; ++ ++/** ++ * X.509 Extension Exception. ++ * ++ * @author Hemma Prafullchandra ++ * 1.2 ++ */ ++public class X509ExtensionException extends GeneralSecurityException { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = 8152491877676477910L; ++ ++ /** ++ * Constructs an X509ExtensionException with no detail message. A ++ * detail message is a String that describes this particular ++ * exception. ++ */ ++ public X509ExtensionException() { ++ super(); ++ } ++ ++ /** ++ * Constructs the exception with the specified detail ++ * message. A detail message is a String that describes this ++ * particular exception. ++ * ++ * @param message the detail message. ++ */ ++ public X509ExtensionException(String message) { ++ super(message); ++ } ++} +diff --git a/org/mozilla/jss/netscape/security/x509/X509Key.java b/org/mozilla/jss/netscape/security/x509/X509Key.java +new file mode 100644 +index 00000000..bd630ce2 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/X509Key.java +@@ -0,0 +1,501 @@ ++// --- 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 org.mozilla.jss.netscape.security.x509; ++ ++import java.io.ByteArrayInputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.ObjectInputStream; ++import java.security.InvalidKeyException; ++import java.security.Key; ++import java.security.KeyFactory; ++import java.security.NoSuchAlgorithmException; ++import java.security.Provider; ++import java.security.PublicKey; ++import java.security.Security; ++import java.security.spec.InvalidKeySpecException; ++import java.security.spec.X509EncodedKeySpec; ++ ++import org.mozilla.jss.netscape.security.util.DerOutputStream; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++/** ++ * Holds an X.509 key, for example a public key found in an X.509 ++ * certificate. Includes a description of the algorithm to be used ++ * with the key; these keys normally are used as ++ * "SubjectPublicKeyInfo". ++ * ++ *

    ++ * While this class can represent any kind of X.509 key, it may be desirable to provide subclasses which understand how ++ * to parse keying data. For example, RSA public keys have two members, one for the public modulus and one for the prime ++ * exponent. If such a class is provided, it is used when parsing X.509 keys. If one is not provided, the key still ++ * parses correctly. ++ * ++ * @version 1.74, 97/12/10 ++ * @author David Brownell ++ */ ++public class X509Key implements PublicKey { ++ ++ /** use serialVersionUID from JDK 1.1. for interoperability */ ++ private static final long serialVersionUID = -5359250853002055002L; ++ ++ /* The algorithm information (name, parameters, etc). */ ++ protected AlgorithmId algid; ++ ++ /* The key bytes, without the algorithm information */ ++ protected byte[] key; ++ ++ /* The encoding for the key. */ ++ protected byte[] encodedKey; ++ ++ /** ++ * Default constructor. The key constructed must have its key ++ * and algorithm initialized before it may be used, for example ++ * by using decode. ++ */ ++ public X509Key() { ++ } ++ ++ /* ++ * Build and initialize as a "default" key. All X.509 key ++ * data is stored and transmitted losslessly, but no knowledge ++ * about this particular algorithm is available. ++ */ ++ public X509Key(AlgorithmId algid, byte[] key) ++ throws InvalidKeyException { ++ this.algid = algid; ++ this.key = key; ++ encode(); ++ } ++ ++ /** ++ * Construct X.509 subject public key from a DER value. If ++ * the runtime environment is configured with a specific class for ++ * this kind of key, a subclass is returned. Otherwise, a generic ++ * X509Key object is returned. ++ * ++ *

    ++ * This mechanism gurantees that keys (and algorithms) may be freely manipulated and transferred, without risk of ++ * losing information. Also, when a key (or algorithm) needs some special handling, that specific need can be ++ * accomodated. ++ * ++ * @param in the DER-encoded SubjectPublicKeyInfo value ++ * @exception IOException on data format errors ++ */ ++ public static X509Key parse(DerValue in) throws IOException { ++ AlgorithmId algorithm; ++ X509Key subjectKey; ++ ++ if (in.tag != DerValue.tag_Sequence) ++ throw new IOException("corrupt subject key"); ++ ++ algorithm = AlgorithmId.parse(in.data.getDerValue()); ++ try { ++ subjectKey = buildX509Key(algorithm, in.data.getBitString()); ++ ++ } catch (InvalidKeyException e) { ++ throw new IOException("subject key, " + e.getMessage()); ++ } ++ ++ if (in.data.available() != 0) ++ throw new IOException("excess subject key"); ++ return subjectKey; ++ } ++ ++ /** ++ * Parse the key bits. This may be redefined by subclasses to take ++ * advantage of structure within the key. For example, RSA public ++ * keys encapsulate two unsigned integers (modulus and exponent) as ++ * DER values within the key bits; Diffie-Hellman and ++ * DSS/DSA keys encapsulate a single unsigned integer. ++ * ++ *

    ++ * This function is called when creating X.509 SubjectPublicKeyInfo values using the X509Key member functions, such ++ * as parse and decode. ++ * ++ * @exception IOException on parsing errors. ++ * @exception InvalidKeyException on invalid key encodings. ++ */ ++ protected void parseKeyBits() throws IOException, InvalidKeyException { ++ encode(); ++ } ++ ++ /* ++ * Factory interface, building the kind of key associated with this ++ * specific algorithm ID or else returning this generic base class. ++ * See the description above. ++ */ ++ static X509Key buildX509Key(AlgorithmId algid, byte[] key) ++ throws IOException, InvalidKeyException { ++ /* ++ * Use the algid and key parameters to produce the ASN.1 encoding ++ * of the key, which will then be used as the input to the ++ * key factory. ++ */ ++ DerOutputStream x509EncodedKeyStream = new DerOutputStream(); ++ encode(x509EncodedKeyStream, algid, key); ++ X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(x509EncodedKeyStream.toByteArray()); ++ ++ try { ++ // Instantiate the key factory of the appropriate algorithm ++ KeyFactory keyFac = null; ++ if (Security.getProvider("Mozilla-JSS") == null) { ++ keyFac = KeyFactory.getInstance(algid.getName()); ++ } else { ++ keyFac = KeyFactory.getInstance(algid.getName(), ++ "Mozilla-JSS"); ++ } ++ ++ // Generate the public key ++ PublicKey pubKey = keyFac.generatePublic(x509KeySpec); ++ ++ if (pubKey instanceof X509Key) { ++ /* ++ * Return specialized X509Key, where the structure within the ++ * key has been parsed ++ */ ++ return (X509Key) pubKey; ++ } ++ } catch (NoSuchAlgorithmException e) { ++ // Return generic X509Key with opaque key data (see below) ++ } catch (InvalidKeySpecException e) { ++ throw new InvalidKeyException(e.toString()); ++ } catch (Exception e) { ++ throw new InvalidKeyException(e.toString()); ++ } ++ ++ /* ++ * Try again using JDK1.1-style for backwards compatibility. ++ */ ++ String classname = ""; ++ try { ++ Provider sunProvider; ++ ++ sunProvider = Security.getProvider("SUN"); ++ if (sunProvider == null) ++ throw new InstantiationException(); ++ classname = sunProvider.getProperty("PublicKey.X.509." + ++ algid.getName()); ++ if (classname == null) { ++ throw new InstantiationException(); ++ } ++ ++ Class keyClass = Class.forName(classname); ++ Object inst; ++ X509Key result; ++ ++ inst = keyClass.newInstance(); ++ if (inst instanceof X509Key) { ++ result = (X509Key) inst; ++ result.algid = algid; ++ result.key = key; ++ result.parseKeyBits(); ++ return result; ++ } ++ } catch (ClassNotFoundException e) { ++ } catch (InstantiationException e) { ++ } catch (IllegalAccessException e) { ++ // this should not happen. ++ throw new IOException(classname + " [internal error]"); ++ } ++ ++ X509Key result = new X509Key(); ++ result.algid = algid; ++ result.key = key; ++ return result; ++ } ++ ++ /** ++ * Returns the algorithm to be used with this key. ++ */ ++ public String getAlgorithm() { ++ return algid.getName(); ++ } ++ ++ /** ++ * Returns the algorithm ID to be used with this key. ++ */ ++ public AlgorithmId getAlgorithmId() { ++ return algid; ++ } ++ ++ /** ++ * Encode SubjectPublicKeyInfo sequence on the DER output stream. ++ * ++ * @exception IOException on encoding errors. ++ */ ++ public final void encode(DerOutputStream out) throws IOException { ++ encode(out, this.algid, this.key); ++ } ++ ++ /** ++ * Returns the DER-encoded form of the key as a byte array. ++ */ ++ public synchronized byte[] getEncoded() { ++ byte[] result = null; ++ try { ++ result = encode(); ++ } catch (InvalidKeyException e) { ++ } ++ return result; ++ } ++ ++ /** ++ * Returns the format for this key: "X.509" ++ */ ++ public String getFormat() { ++ return "X.509"; ++ } ++ ++ /** ++ * Returns the raw key as a byte array ++ */ ++ public byte[] getKey() { ++ return key; ++ } ++ ++ /** ++ * Returns the DER-encoded form of the key as a byte array. ++ * ++ * @exception InvalidKeyException on encoding errors. ++ */ ++ public byte[] encode() throws InvalidKeyException { ++ if (encodedKey == null) { ++ try { ++ DerOutputStream out; ++ ++ out = new DerOutputStream(); ++ encode(out); ++ encodedKey = out.toByteArray(); ++ ++ } catch (IOException e) { ++ throw new InvalidKeyException("IOException : " + ++ e.getMessage()); ++ } ++ } ++ return copyEncodedKey(encodedKey); ++ } ++ ++ /* ++ * Returns a printable representation of the key ++ */ ++ public String toString() { ++ org.mozilla.jss.netscape.security.util.PrettyPrintFormat pp = ++ new org.mozilla.jss.netscape.security.util.PrettyPrintFormat(" ", 20); ++ String keybits = pp.toHexString(key); ++ ++ return "algorithm = " + algid.toString() ++ + ", unparsed keybits = \n" + keybits; ++ } ++ ++ /** ++ * Initialize an X509Key object from an input stream. The data on that ++ * input stream must be encoded using DER, obeying the X.509 SubjectPublicKeyInfo format. That is, the ++ * data is a ++ * sequence consisting of an algorithm ID and a bit string which holds ++ * the key. (That bit string is often used to encapsulate another DER ++ * encoded sequence.) ++ * ++ *

    ++ * Subclasses should not normally redefine this method; they should instead provide a parseKeyBits ++ * method to parse any fields inside the key member. ++ * ++ *

    ++ * The exception to this rule is that since private keys need not be encoded using the X.509 ++ * SubjectPublicKeyInfo format, private keys may override this method, encode, and of ++ * course getFormat. ++ * ++ * @param in an input stream with a DER-encoded X.509 ++ * SubjectPublicKeyInfo value ++ * @exception InvalidKeyException on parsing errors. ++ */ ++ public void decode(InputStream in) ++ throws InvalidKeyException { ++ DerValue val; ++ ++ try { ++ val = new DerValue(in); ++ if (val.tag != DerValue.tag_Sequence) ++ throw new InvalidKeyException("invalid key format"); ++ ++ algid = AlgorithmId.parse(val.data.getDerValue()); ++ key = val.data.getBitString(); ++ parseKeyBits(); ++ if (val.data.available() != 0) ++ throw new InvalidKeyException("excess key data"); ++ ++ } catch (IOException e) { ++ // e.printStackTrace (); ++ throw new InvalidKeyException("IOException : " + ++ e.getMessage()); ++ } ++ } ++ ++ public void decode(byte[] encodedKey) throws InvalidKeyException { ++ decode(new ByteArrayInputStream(encodedKey)); ++ } ++ ++ /** ++ * Serialization write ... X.509 keys serialize as ++ * themselves, and they're parsed when they get read back. ++ */ ++ private void writeObject(java.io.ObjectOutputStream stream) throws IOException { ++ stream.write(getEncoded()); ++ } ++ ++ /** ++ * Serialization read ... X.509 keys serialize as ++ * themselves, and they're parsed when they get read back. ++ */ ++ private void readObject(ObjectInputStream stream) throws IOException { ++ try { ++ decode(stream); ++ } catch (InvalidKeyException e) { ++ e.printStackTrace(); ++ throw new IOException("deserialized key is invalid: " + e.getMessage()); ++ } ++ } ++ ++ public boolean equals(Object object) { ++ if (this == object) { ++ return true; ++ } ++ ++ if (object instanceof Key) { ++ Key key = (Key) object; ++ ++ byte[] b1; ++ if (encodedKey != null) { ++ b1 = encodedKey; ++ } else { ++ b1 = getEncoded(); ++ } ++ byte[] b2 = key.getEncoded(); ++ ++ return java.security.MessageDigest.isEqual(b1, b2); ++ } ++ ++ return false; ++ } ++ ++ /** ++ * Calculates a hash code value for the object. Objects ++ * which are equal will also have the same hashcode. ++ */ ++ public int hashCode() { ++ int retval = 0; ++ byte[] b1 = getEncoded(); ++ ++ for (int i = 1; i < b1.length; i++) { ++ retval += b1[i] * i; ++ } ++ return (retval); ++ } ++ ++ /* ++ * Make a copy of the encoded key. ++ */ ++ private byte[] copyEncodedKey(byte[] encodedKey) { ++ int len = encodedKey.length; ++ byte[] copy = new byte[len]; ++ System.arraycopy(encodedKey, 0, copy, 0, len); ++ return copy; ++ } ++ ++ /* ++ * Produce SubjectPublicKey encoding from algorithm id and key material. ++ */ ++ static void encode(DerOutputStream out, AlgorithmId algid, byte[] key) ++ throws IOException { ++ DerOutputStream tmp = new DerOutputStream(); ++ algid.encode(tmp); ++ tmp.putBitString(key); ++ out.write(DerValue.tag_Sequence, tmp); ++ } ++ ++ /* ++ * parsePublicKey returns a PublicKey for use with package JSS from within netscape.security.*. ++ * This function provide an interim solution for migrating from using the netscape.security.* package ++ * to using the JSS package. ++ */ ++ ++ public static PublicKey parsePublicKey(DerValue in) throws IOException { ++ AlgorithmId algorithm; ++ PublicKey subjectKey; ++ ++ if (in.tag != DerValue.tag_Sequence) ++ throw new IOException("corrupt subject key"); ++ ++ algorithm = AlgorithmId.parse(in.data.getDerValue()); ++ try { ++ subjectKey = buildPublicKey(algorithm, in.data.getBitString()); ++ ++ } catch (InvalidKeyException e) { ++ throw new IOException("subject key, " + e.getMessage()); ++ } ++ ++ if (in.data.available() != 0) ++ throw new IOException("excess subject key"); ++ return subjectKey; ++ } ++ ++ /* buildPublicKey returns a PublicKey for use with the JSS package from within netscape.security.*. ++ * This function provide an interim solution for migrating from using the netscape.security.* package ++ * to using the JSS package. ++ */ ++ static PublicKey buildPublicKey(AlgorithmId algid, byte[] key) ++ throws IOException, InvalidKeyException { ++ /* ++ * Use the algid and key parameters to produce the ASN.1 encoding ++ * of the key, which will then be used as the input to the ++ * key factory. ++ */ ++ DerOutputStream x509EncodedKeyStream = new DerOutputStream(); ++ encode(x509EncodedKeyStream, algid, key); ++ X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(x509EncodedKeyStream.toByteArray()); ++ ++ try { ++ // Instantiate the key factory of the appropriate algorithm ++ KeyFactory keyFac = null; ++ if (Security.getProvider("Mozilla-JSS") == null) { ++ keyFac = KeyFactory.getInstance(algid.getName()); ++ } else { ++ keyFac = KeyFactory.getInstance(algid.getName(), ++ "Mozilla-JSS"); ++ } ++ ++ // Generate the public key ++ PublicKey pubKey = keyFac.generatePublic(x509KeySpec); ++ ++ /* ++ * Return specialized X509Key, where the structure within the ++ * key has been parsed ++ */ ++ return pubKey; ++ } catch (NoSuchAlgorithmException e) { ++ // Return generic X509Key with opaque key data (see below) ++ throw new InvalidKeyException(e.toString()); ++ } catch (InvalidKeySpecException e) { ++ throw new InvalidKeyException(e.toString()); ++ } catch (Exception e) { ++ throw new InvalidKeyException(e.toString()); ++ } ++ ++ } ++ ++} +diff --git a/org/mozilla/jss/netscape/security/x509/manifest.mn b/org/mozilla/jss/netscape/security/x509/manifest.mn +new file mode 100644 +index 00000000..fb17fcef +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/manifest.mn +@@ -0,0 +1,10 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++CORE_DEPTH = ../../../../../.. ++ ++PACKAGE = org/mozilla/jss/netscape/security/x509 ++ ++MODULE = jss +diff --git a/org/mozilla/jss/netscape/security/x509/rules.mk b/org/mozilla/jss/netscape/security/x509/rules.mk +new file mode 100644 +index 00000000..edc59392 +--- /dev/null ++++ b/org/mozilla/jss/netscape/security/x509/rules.mk +@@ -0,0 +1,18 @@ ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, You can obtain one at http://mozilla.org/MPL/2.0/. ++ ++javadoc: ++ @echo Steve's Javadoc rule ------------------------- ++ /usr/java/jdk1.1.5/bin/javadoc -sourcepath $(JAVA_HOME)/lib/classes.zip:$(CORE_DEPTH)/ninja -d /u/stevep/javadoc $(JSRCS) ++ @echo End of Steve's Javadoc rule ------------------ ++ ++runserver: ++ $(DEBUG_CMD) $(SOURCE_BIN_DIR)/jssjava -classpath $(JAVA_HOME)/lib/classes.zip:$(SOURCE_CLASSES_DIR)_DBG org.mozilla.jss.ssl.SSLServer ++ ++runclient: ++ $(DEBUG_CMD) $(SOURCE_BIN_DIR)/jssjava -classpath $(JAVA_HOME)/lib/classes.zip:$(SOURCE_CLASSES_DIR)_DBG org.mozilla.jss.ssl.SSLClient ++ ++debugcore: ++ dbx $(SOURCE_BIN_DIR)/jssjava core +diff --git a/org/mozilla/jss/tests/X509CertTest.java b/org/mozilla/jss/tests/X509CertTest.java +new file mode 100644 +index 00000000..c5f96132 +--- /dev/null ++++ b/org/mozilla/jss/tests/X509CertTest.java +@@ -0,0 +1,155 @@ ++/* This Source Code Form is subject to the terms of the Mozilla Public ++ * License, v. 2.0. If a copy of the MPL was not distributed with this ++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ ++package org.mozilla.jss.tests; ++ ++import java.io.*; ++import org.mozilla.jss.CryptoManager; ++import org.mozilla.jss.util.Debug; ++import java.security.MessageDigest; ++import java.security.Provider; ++import java.security.Security; ++import java.security.PublicKey; ++import java.security.cert.CertificateException; ++import java.security.InvalidKeyException; ++import java.security.NoSuchAlgorithmException; ++import java.security.KeyPair; ++import java.security.interfaces.RSAPublicKey; ++ ++ ++import org.mozilla.jss.crypto.CryptoToken; ++import org.mozilla.jss.crypto.KeyPairGenerator; ++import org.mozilla.jss.crypto.KeyPairAlgorithm; ++ ++import org.mozilla.jss.pkcs11.PK11ECPublicKey; ++ ++import org.mozilla.jss.netscape.security.x509.X509CertImpl; ++import org.mozilla.jss.netscape.security.x509.X509CertInfo; ++import org.mozilla.jss.netscape.security.x509.X509Key; ++import org.mozilla.jss.netscape.security.x509.CertificateIssuerName; ++import org.mozilla.jss.netscape.security.x509.CertificateVersion; ++import org.mozilla.jss.netscape.security.x509.CertificateSerialNumber; ++import org.mozilla.jss.netscape.security.x509.CertificateValidity; ++import org.mozilla.jss.netscape.security.x509.CertificateSubjectName; ++import org.mozilla.jss.netscape.security.x509.CertificateAlgorithmId; ++import org.mozilla.jss.netscape.security.x509.AlgorithmId; ++import org.mozilla.jss.netscape.security.x509.CertificateX509Key; ++import org.mozilla.jss.netscape.security.x509.X500Name; ++import org.mozilla.jss.netscape.security.x509.CertificateExtensions; ++ ++import org.mozilla.jss.netscape.security.util.BigInt; ++import org.mozilla.jss.netscape.security.util.DerValue; ++ ++import java.util.Date; ++import java.util.Calendar; ++import java.math.BigInteger; ++ ++public class X509CertTest { ++ ++ public static String subjectDN = "CN = 8a99f98342b97d130142ba2cc30f07d3"; ++ public static String issuerDN= "C = US, ST = North Carolina, O = Red Hat Inc., OU = Red Hat Network, CN = Red Hat Candlepin Authority, E = ca-support@redhat.com "; ++ ++ /* Just some sample code to exercise the new classes */ ++ public static void main(String []args) { ++ ++ X509CertImpl certImpl = null; ++ X509CertInfo certInfo = null; ++ ++ if( args.length != 1 ) { ++ System.out.println("Usage: X509CertTest "); ++ return; ++ } ++ String dbdir = args[0]; ++ ++ try { ++ ++ Date notBefore = new Date(); ++ Calendar cal=Calendar.getInstance(); ++ cal.setTime(notBefore); ++ cal.set(Calendar.YEAR,2037); ++ ++ Date notAfter = cal.getTime(); ++ ++ //Generate ca keyPair ++ ++ CryptoManager.initialize(dbdir); ++ CryptoManager cryptoManager = CryptoManager.getInstance(); ++ CryptoToken token = cryptoManager.getInternalKeyStorageToken(); ++ KeyPairGenerator gen = token.getKeyPairGenerator(KeyPairAlgorithm.RSA); ++ gen.initialize(2048); ++ KeyPair keypairCA = gen.genKeyPair(); ++ PublicKey pubCA = keypairCA.getPublic(); ++ ++ ++ gen.initialize(2048); ++ KeyPair keypairUser = gen.genKeyPair(); ++ PublicKey pubUser = keypairUser.getPublic(); ++ ++ CertificateIssuerName issuernameObj = ++ new CertificateIssuerName(new X500Name(issuerDN)); ++ ++ certInfo = createX509CertInfo(convertPublicKeyToX509Key(pubUser), ++ BigInteger.valueOf((long) 1),issuernameObj,subjectDN, ++ notBefore, notAfter, "SHA256withRSA"); ++ ++ certImpl = new X509CertImpl(certInfo); ++ certImpl.sign(keypairCA.getPrivate(),"SHA256withRSA"); ++ ++ String certOutput = certImpl.toString(); ++ ++ System.out.println("Test certificate output: \n" + certOutput); ++ ++ ++ } catch( Exception e ) { ++ e.printStackTrace(); ++ System.exit(1); ++ } ++ System.exit(0); ++ } ++ ++ public static X509CertInfo createX509CertInfo(X509Key x509key, ++ BigInteger serialno, CertificateIssuerName issuernameObj, String subjname, ++ Date notBefore, Date notAfter, String alg) ++ throws IOException, ++ CertificateException, ++ InvalidKeyException, ++ NoSuchAlgorithmException { ++ X509CertInfo info = new X509CertInfo(); ++ ++ info.set(X509CertInfo.VERSION, new ++ CertificateVersion(CertificateVersion.V3)); ++ info.set(X509CertInfo.SERIAL_NUMBER, new ++ CertificateSerialNumber(serialno)); ++ if (issuernameObj != null) { ++ info.set(X509CertInfo.ISSUER, ++ issuernameObj); ++ } ++ info.set(X509CertInfo.SUBJECT, new ++ CertificateSubjectName(new X500Name(subjname))); ++ info.set(X509CertInfo.VALIDITY, new ++ CertificateValidity(notBefore, notAfter)); ++ info.set(X509CertInfo.ALGORITHM_ID, new ++ CertificateAlgorithmId(AlgorithmId.get(alg))); ++ info.set(X509CertInfo.KEY, new CertificateX509Key(x509key)); ++ info.set(X509CertInfo.EXTENSIONS, new CertificateExtensions()); ++ return info; ++ } ++ ++ public static X509Key convertPublicKeyToX509Key(PublicKey pubk) ++ throws InvalidKeyException , IOException { ++ X509Key xKey = null; ++ ++ if (pubk instanceof RSAPublicKey) { ++ RSAPublicKey rsaKey = (RSAPublicKey) pubk; ++ ++ xKey = new org.mozilla.jss.netscape.security.provider.RSAPublicKey( ++ new BigInt(rsaKey.getModulus()), ++ new BigInt(rsaKey.getPublicExponent())); ++ } else if (pubk instanceof PK11ECPublicKey) { ++ byte encoded[] = pubk.getEncoded(); ++ ++ xKey = X509Key.parse(new DerValue(encoded)); ++ } ++ return xKey; ++ } ++} +-- +2.14.4 + diff --git a/jss-fix-README.patch b/jss-fix-README.patch new file mode 100644 index 0000000..e2c8020 --- /dev/null +++ b/jss-fix-README.patch @@ -0,0 +1,67 @@ +commit 76f7c710435d28c9dc8cad8540615ba73f3c00c4 +Author: Alexander Scheel +Date: Thu May 31 08:17:15 2018 -0400 + + Updated README instructions for git + + Signed-off-by: Alexander Scheel + +diff --git a/README b/README +index 5a1bc49c..9f1929ac 100644 +--- a/jss/README ++++ b/jss/README +@@ -17,7 +17,7 @@ + (There is no need to clone every time. For additional builds, + simply use: + cd nspr; hg pull -u -v; cd ..; cd nss; hg pull -u -v; cd ..; +- cd jss; hg pull -u -v; cd .. ++ cd jss; git pull -u -v; cd .. + ) + + (b) Alternatively, for upstream builds which use +@@ -39,7 +39,7 @@ + + (There is no need to clone every time. For additional builds, + simply use: +- cd jss; hg pull -u -v; cd .. ++ cd jss; git pull -u -v; cd .. + ) + + +@@ -164,7 +164,7 @@ + good practice to create a "regular tag" to the source code at these + various points in time using the following format: + +- # hg tag -m "message" JSS___YYYYMMDD ++ # git tag -m "message" JSS___YYYYMMDD + + where: = JSS Major Version Number + = JSS Minor Version Number +@@ -174,10 +174,10 @@ + + For example: + +- # hg id +- b3e864205ff0+ tip ++ # git rev-parse HEAD ++ b3e864205ff0... + +- # hg tag -m "Added tag JSS_4_4_20170328 for changeset b3e864205ff0" JSS_4_4_20170328 ++ # git tag -m "Added tag JSS_4_4_20170328 for changeset b3e864205ff0" JSS_4_4_20170328 + + At the appropriate time, a new major.minor version may be created. At this + time, it is important to create a maintenance branch for any future changes +@@ -185,10 +185,10 @@ + + For example: + +- # hg id +- f00f00f00f00+ tip ++ # git rev-parse HEAD ++ f00f00f00f00... + +- # hg branch -m "Created branch JSS_4_4_BRANCH for changeset f00f00f00f00" JSS_4_4_BRANCH ++ # git checkout -b JSS_4_4_BRANCH + + + (8) Known Issues diff --git a/jss-fix-algorithm-identifier-encode-decode.patch b/jss-fix-algorithm-identifier-encode-decode.patch new file mode 100644 index 0000000..415c87e --- /dev/null +++ b/jss-fix-algorithm-identifier-encode-decode.patch @@ -0,0 +1,44 @@ +From 7c7a97f60c1b3400b921981a3cd9e9aae4f28987 Mon Sep 17 00:00:00 2001 +From: Christina Fu +Date: Tue, 26 Jun 2018 17:59:28 -0700 +Subject: [PATCH] Ticket 12 AlgorithmIdentifier decode/encode process alters + original data + +This patch provides fix to ensure that the encoding and decoding of an AlgorithmIdentifier +structure would not alter the data. + +credit: original fix suggestion provided by david.k.stutzman2.ctr@mail.mil + +fixes https://pagure.io/jss/issue/12 +--- + org/mozilla/jss/pkix/primitive/AlgorithmIdentifier.java | 13 +------------ + 1 file changed, 1 insertion(+), 12 deletions(-) + +diff --git a/org/mozilla/jss/pkix/primitive/AlgorithmIdentifier.java b/org/mozilla/jss/pkix/primitive/AlgorithmIdentifier.java +index 0662f76..3487707 100644 +--- a/jss/org/mozilla/jss/pkix/primitive/AlgorithmIdentifier.java ++++ b/jss/org/mozilla/jss/pkix/primitive/AlgorithmIdentifier.java +@@ -103,19 +103,8 @@ public static class Template implements ASN1Template { + Assert._assert( seq.size() == 2 ); + + OBJECT_IDENTIFIER algOID = (OBJECT_IDENTIFIER)seq.elementAt(0); +- boolean allowParams = true; +- try { +- if (algOID.equals(SignatureAlgorithm.ECSignatureWithSHA256Digest.toOID()) || +- algOID.equals(SignatureAlgorithm.ECSignatureWithSHA384Digest.toOID()) || +- algOID.equals(SignatureAlgorithm.ECSignatureWithSHA512Digest.toOID())) { +- allowParams = false; +- } +- } catch (NoSuchAlgorithmException e) { +- // System.out.println("JSS: AlgorithmIdentifier:decode: " + e.toString()); +- // unlikely to happen; swallow it. treat it as allowParams; +- } + +- if (!allowParams) { ++ if (seq.elementAt(1) == null) { + return new AlgorithmIdentifier( + algOID // OID + ); +-- +1.8.3.1 + diff --git a/jss-fix-classpath.patch b/jss-fix-classpath.patch new file mode 100644 index 0000000..fb350ab --- /dev/null +++ b/jss-fix-classpath.patch @@ -0,0 +1,15 @@ +diff --git a/build_java.pl b/build_java.pl +index 78cc5fd0..b74a9338 100644 +--- a/jss/build_java.pl ++++ b/jss/build_java.pl +@@ -162,8 +162,9 @@ sub setup_vars { + } + $jni_header_dir = "$dist_dir/private/jss/_jni"; + ++ $classpath = "-classpath /usr/share/java/apache-commons-codec.jar:/usr/share/java/commons-lang.jar:/usr/share/java/ldapjdk.jar:"; + if( $jce_jar ) { +- $classpath = "-classpath $jce_jar"; ++ $classpath .= ":$jce_jar"; + } + + # retrieve present working directory diff --git a/jss-update-jss-spec-in-template.patch b/jss-update-jss-spec-in-template.patch new file mode 100644 index 0000000..970f4ea --- /dev/null +++ b/jss-update-jss-spec-in-template.patch @@ -0,0 +1,63 @@ +From 56979bf6b22a81355fe948d3c28ec78d925db1d8 Mon Sep 17 00:00:00 2001 +From: Jack Magne +Date: Thu, 21 Jun 2018 22:18:38 -0600 +Subject: [PATCH] Update dependencies in 'jss.spec.in' template + +--- + jss.spec.in | 27 ++++++++++++++++++++++----- + 1 file changed, 22 insertions(+), 5 deletions(-) + +diff --git a/jss.spec.in b/jss.spec.in +index f1e629f2..4a240138 100644 +--- a/jss/jss.spec.in ++++ b/jss/jss.spec.in +@@ -10,13 +10,22 @@ Version: 4.4.4 + Release: 1%{?_timestamp}%{?_commit_id}%{?dist} + + # To generate the source tarball: +-# +-# git clone https://github.com/dogtagpki/jss.git +-# cd jss +-# git archive --format=tar.gz --prefix jss-VERSION/jss/ -o jss-VERSION.tar.gz -v HEAD +-# ++# $ git clone https://github.com/dogtagpki/jss.git ++# $ cd jss ++# $ git archive \ ++# --format=tar.gz \ ++# --prefix jss-VERSION/jss/ \ ++# -o jss-VERSION.tar.gz \ ++# + Source: https://github.com/dogtagpki/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz + ++# To create a patch for all changes since a version tag: ++# $ git format-patch \ ++# --stdout \ ++# \ ++# > jss-VERSION-RELEASE.patch ++# Patch: jss-VERSION-RELEASE.patch ++ + Conflicts: idm-console-framework < 1.1.17-4 + Conflicts: pki-base < 10.4.0 + Conflicts: tomcatjss < 7.2.1 +@@ -31,10 +40,18 @@ BuildRequires: jpackage-utils + %if 0%{?fedora} >= 25 || 0%{?rhel} > 7 + BuildRequires: perl-interpreter + %endif ++BuildRequires: apache-commons-lang ++BuildRequires: apache-commons-codec ++BuildRequires: ldapjdk ++ + + Requires: nss >= 3.28.4-6 + Requires: java-headless + Requires: jpackage-utils ++Requires: apache-commons-lang ++Requires: apache-commons-codec ++Requires: ldapjdk ++ + + %description + Java Security Services (JSS) is a java native interface which provides a bridge +-- +2.14.4 + diff --git a/jss.spec b/jss.spec index b94f68c..0b435c8 100644 --- a/jss.spec +++ b/jss.spec @@ -1,43 +1,65 @@ +################################################################################ Name: jss -Version: 4.4.2 -Release: 4%{?dist} -Summary: Java Security Services (JSS) +################################################################################ -Group: System Environment/Libraries +Summary: Java Security Services (JSS) +URL: http://www.dogtagpki.org/wiki/JSS License: MPLv1.1 or GPLv2+ or LGPLv2+ -URL: http://www.mozilla.org/projects/security/pki/jss/ -# The source for this package was pulled from upstream's hg. Use the -# following commands to generate the tarball: + +Version: 4.4.4 +Release: 3%{?_timestamp}%{?_commit_id}%{?dist} + +# To generate the source tarball: # -# hg clone https://hg.mozilla.org/projects/jss +# git clone https://github.com/dogtagpki/jss.git # cd jss -# hg archive --prefix jss-4.4.2/jss/ ../jss-4.4.2.tar.gz +# git archive --format=tar.gz --prefix jss-VERSION/jss/ -o jss-VERSION.tar.gz -v HEAD # -Source0: http://pki.fedoraproject.org/pki/sources/%{name}/%{version}/%{name}-%{version}.tar.gz -Source1: http://pki.fedoraproject.org/pki/sources/%{name}/%{version}/MPL-1.1.txt -Source2: http://pki.fedoraproject.org/pki/sources/%{name}/%{version}/gpl.txt -Source3: http://pki.fedoraproject.org/pki/sources/%{name}/%{version}/lgpl.txt -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Source: https://github.com/dogtagpki/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz + +Patch0: jss-fix-classpath.patch +Patch1: jss-fix-README.patch +Patch2: jss-update-jss-spec-in-template.patch +Patch3: jss-add-build-sh.patch +Patch4: jss-add-travis-yml.patch +Patch5: jss-add-x509-cert-and-crl-migration.patch +Patch6: jss-fix-algorithm-identifier-encode-decode.patch +Patch7: jss-add-TLS-SHA384-ciphers.patch Conflicts: idm-console-framework < 1.1.17-4 Conflicts: pki-base < 10.4.0 Conflicts: tomcatjss < 7.2.1 +# autosetup +BuildRequires: git + BuildRequires: nss-devel >= 3.28.4-6 BuildRequires: nspr-devel >= 4.13.1 BuildRequires: java-devel -%if 0%{?fedora} >= 25 +BuildRequires: jpackage-utils +%if 0%{?fedora} >= 25 || 0%{?rhel} > 7 BuildRequires: perl-interpreter %endif -Requires: java-headless +BuildRequires: apache-commons-lang +BuildRequires: apache-commons-codec +BuildRequires: ldapjdk + Requires: nss >= 3.28.4-6 +Requires: java-headless +Requires: jpackage-utils +Requires: apache-commons-lang +Requires: apache-commons-codec +Requires: ldapjdk %description Java Security Services (JSS) is a java native interface which provides a bridge for java-based applications to use native Network Security Services (NSS). This only works with gcj. Other JREs require that JCE providers be signed. +################################################################################ %package javadoc +################################################################################ + Summary: Java Security Services (JSS) Javadocs Group: Documentation Requires: jss = %{version}-%{release} @@ -45,10 +67,29 @@ Requires: jss = %{version}-%{release} %description javadoc This package contains the API documentation for JSS. +################################################################################ %prep -%setup -q -n %{name}-%{version} +#%autosetup -n %{name}-%{version} -p 1 -S git +%setup -q -n %{name}-%{version} +pushd jss +# Force use of 'patch' rather than 'git apply' by injecting "/jss/" prefix +# on patches of existing files located in top-level directory . . . +%patch0 -p2 +%patch1 -p2 +%patch2 -p2 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p2 +%patch7 -p2 +popd + +################################################################################ %build + +%set_build_flags + [ -z "$JAVA_HOME" ] && export JAVA_HOME=%{_jvmdir}/java [ -z "$USE_INSTALLED_NSPR" ] && export USE_INSTALLED_NSPR=1 [ -z "$USE_INSTALLED_NSS" ] && export USE_INSTALLED_NSS=1 @@ -90,15 +131,13 @@ make -C jss/coreconf make -C jss make -C jss javadoc -%check - +################################################################################ %install -rm -rf $RPM_BUILD_ROOT docdir # Copy the license files here so we can include them in %%doc -cp -p %{SOURCE1} . -cp -p %{SOURCE2} . -cp -p %{SOURCE3} . +cp -p jss/MPL-1.1.txt . +cp -p jss/gpl.txt . +cp -p jss/lgpl.txt . # There is no install target so we'll do it by hand @@ -119,338 +158,40 @@ popd install -d -m 0755 $RPM_BUILD_ROOT%{_javadocdir}/%{name}-%{version} cp -rp dist/jssdoc/* $RPM_BUILD_ROOT%{_javadocdir}/%{name}-%{version} cp -p jss/jss.html $RPM_BUILD_ROOT%{_javadocdir}/%{name}-%{version} -cp -p *.txt $RPM_BUILD_ROOT%{_javadocdir}/%{name}-%{version} - -%clean -rm -rf $RPM_BUILD_ROOT +cp -p jss/*.txt $RPM_BUILD_ROOT%{_javadocdir}/%{name}-%{version} # No ldconfig is required since this library is loaded by Java itself. +################################################################################ %files + %defattr(-,root,root,-) -%doc jss/jss.html MPL-1.1.txt gpl.txt lgpl.txt +%doc jss/jss.html jss/MPL-1.1.txt jss/gpl.txt jss/lgpl.txt %{_libdir}/jss/* %{_jnidir}/* %{_libdir}/jss/lib*.so +################################################################################ %files javadoc + %defattr(-,root,root,-) %dir %{_javadocdir}/%{name}-%{version} %{_javadocdir}/%{name}-%{version}/* +################################################################################ %changelog -* Thu Aug 03 2017 Fedora Release Engineering - 4.4.2-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild -* Wed Jul 26 2017 Fedora Release Engineering - 4.4.2-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild +* Thu Jul 5 2018 Dogtag PKI Team 4.4.4-3 +- JSS Pagure Issue #12 - org.mozilla.jss.pkix.primitive.AlgorithmIdentifier + decode/encode process alters original data (cfu) +- JSS Pagure Issue #4 - JSS: Add support for TLS_*_SHA384 ciphers (cfu) -* Tue May 9 2017 Matthew Harmsen - 4.4.2-2 -- Bump NSS dependencies from 4.28.3 to 4.28.4-6 to pick-up fix in - Mozilla Bugzilla #1360207 - Fix incorrect if (ss->...) in SSL_ReconfigFD +* Thu Jun 21 2018 Dogtag PKI Team 4.4.4-2 +- JSS Pagure Issue #11 - (RFE) Migrate Dogtag x509 cert and crl + functionality to JSS (jmagne) -* Mon May 1 2017 Matthew Harmsen - 4.4.2-1 -- Mozilla Bugzilla #1352476 - RFE: Document on the README how to create a - release tag (mharmsen) -- Mozilla Bugzilla #1355358 - CryptoStore: add methods for importing and - exporting EncryptedPrivateKeyInfo (ftweedal) -- Mozilla Bugzilla #1359731 - CryptoStore.importPrivateKey enhancements - (ftweedal) +* Tue May 29 2018 Dogtag PKI Team 4.4.4-1 +- Rebased to JSS 4.4.4 -* Mon Apr 17 2017 Matthew Harmsen - 4.4.1-2 -- Mozilla Bugzilla #1355268 - JSS 4.4 is incompatible with versions of - idm-console-framework < 1.1.17-4 -- Red Hat Bugzilla #1435076 - Remove unused legacy lines from JSS spec files - -* Mon Mar 27 2017 Matthew Harmsen - 4.4.1-1 -- Bugzilla Bug #1431937 - Rebase jss to 4.4.x in Fedora 25+ -- Updated build requirements for NSPR -- Updated build and runtime requirements for NSS -- ## 'jss-post-rebase.patch' resolves the following issues ported from - ## upstream: -- Mozilla Bugzilla #1337092 - CMC conformance update: Implement required ASN.1 - code for RFC5272+ (cfu) -- Mozilla Bugzilla #1347394 - Eclipse project files for JSS (edewata) -- Mozilla Bugzilla #1347429 - Deprecated SSL 3.0 cipher names in SSLSocket - class. (edewata) -- Mozilla Bugzilla #1348856 - SSL alert callback (edewata) -- Mozilla Bugzilla #1349278 - SSL cipher enumeration (edewata) -- Mozilla Bugzilla #1349349 - Problem with Password.readPasswordFromConsole(). - (edewata) -- Mozilla Bugzilla #1349831 - Revise top-level README file (mharmsen) -- Mozilla Bugzilla #1349836 - Changes to JSS Version Block (mharmsen) -- Mozilla Bugzilla #1350130 - Missing - CryptoManager.verifyCertificateNowCUNative() implementation. (emaldona) - -* Tue Mar 21 2017 Matthew Harmsen - 4.4.0-3 -- Bugzilla Bug #1434535 - JSS 4.4.x is incompatible with versions of - pki-base < 10.4.0 - -* Wed Mar 15 2017 Matthew Harmsen - 4.4.0-2 -- Bugzilla Bug #1432568 - JSS 4.4.x is incompatible with versions of - tomcatjss < 7.2.1 - -* Mon Mar 13 2017 Elio Maldonado - 4.4.0-1 -- Bugzilla Bug #1431937 - Rebase jss to 4.4.x in Fedora 25+ -- ## JSS 4.4.0 includes the following patches ported from downstream: -- Mozilla Bugzilla #507536 - Add IPv6 functionality to JSS -- Mozilla Bugzilla #1307872 - Expose NSS calls for OCSP settings -- Mozilla Bugzilla #1307882 - RFE ecc - add ecc curve name support in JSS and - CS interface -- Mozilla Bugzilla #1307993 - Expose updated certificate verification function - in JSS -- Mozilla Bugzilla #1308000 - Incorrect socket accept error message due to bad - pointer arithmetic -- Mozilla Bugzilla #1308001 - Verification should fail when a revoked - certificate is added -- Mozilla Bugzilla #1308004 - Warnings should be cleaned up in JSS build -- Mozilla Bugzilla #1308006 - DRM failed to recovery keys when in FIPS mode - (HSM + NSS) -- Mozilla Bugzilla #1308008 - Defects revealed by Coverity scan -- Mozilla Bugzilla #1308009 - Add support for PKCS5v2; support for secure PKCS12 -- Mozilla Bugzilla #1308012 - DRM: during archiving and recovering, wrapping - unwrapping keys should be done in the token -- Mozilla Bugzilla #1308013 - JSS - HSM token name was mistaken for - manufacturer identifier -- Mozilla Bugzilla #1308017 - Un-deprecate previously deprecated methods in - JSS 4.2.6 -- Mozilla Bugzilla #1308019 - Provide Tomcat support for TLS v1.1 and - TLS v1.2 via NSS through JSS -- Mozilla Bugzilla #1308026 - JSS certificate validation does not pass up exact - error from NSS -- Mozilla Bugzilla #1308027 - Merge pki-symkey into jss -- Mozilla Bugzilla #1308029 - Resolve Javadoc build issues -- Mozilla Bugzilla #1308047 - support ECC encryption key archival and recovery -- Mozilla Bugzilla #1313122 - Remove bypass tests as latest NSS has removed - PKCS#11 bypass support -- Mozilla Bugzilla #1328675 - Simple problem unwrapping AES sym keys on token -- Mozilla Bugzilla #1345174 - Cannot create system certs when using LunaSA HSM - in FIPS Mode and ECC algorithms -- Mozilla Bugzilla #1345613 - expose AES KeyWrap and add some useful OID - functions -- Mozilla Bugzilla #1346410 - Load JSS libraries appropriately -- ## JSS 4.4.0 includes the following changes for building and testing: -- Mozilla Bugzilla #1331765 - Simplify JSS Makefile build and test -- Mozilla Bugzilla #1346420 - Document steps required to use the proper - libjss4.so when running certain HMAC Algorithms tests - -* Wed Feb 22 2017 Jack Magne - 4.2.6-44 -- Bugzilla Bug #1425971 - Simple problem unwrapping AES sym keys on token - -* Fri Feb 10 2017 Fedora Release Engineering - 4.2.6-43 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild - -* Tue Aug 9 2016 Christina Fu - 4.2.6-42 -- Sync up patches from both Fedora and RHEL; adding two patches - (cfu, edewata, mharmsen) from RHEL: -- Bugzilla Bug #1238450 - UnsatisfiedLinkError on Windows (cfu) -- make it compile on Windows platforms (cfu for nhosoi) - -* Fri Jun 24 2016 Christina Fu - 4.2.6-41 -- Bugzilla 1221295 jss fails to decode EncryptedKey >> EnvelopedData - (cfu for roysjosh@gmail.com) - -* Thu May 19 2016 Christina Fu - 4.2.6-40 -- Bugzilla 1074208 - pass up exact JSS certificate validation errors from NSS - (edewata) -- Bugzilla 1331596 - Key archival fails when KRA is configured with lunasa. - (cfu) -- PKI ticket 801 - Merge pki-symkey into jss (phase 1) - (jmagne) - -* Wed Dec 09 2015 Endi Dewata - 4.2.6-38 -- Bugzilla Bug #1289799 - JSS build failure on F23 and Rawhide (edewata) - -* Thu Apr 09 2015 Marcin Juszkiewicz - 4.2.6-37 -- Fix use of __isa_bits macro so it does not fail during srpm generation on koji - -* Thu Apr 09 2015 Marcin Juszkiewicz - 4.2.6-36 -- Use __isa_bits macro to check for 64-bit arch. Unblocks aarch64 and ppc64le. - -* Tue Sep 30 2014 Christina Fu - 4.2.6-35 -- Bugzilla Bug #1040640 - Incorrect OIDs for SHA2 algorithms - (cfu for jnimeh@gmail.com) -- Bugzilla Bug #1133718 - Key strength validation is not performed for RC4 - algorithm (nkinder) -- Bugzilla Bug #816396 - Provide Tomcat support for TLS v1.1 and - TLS v1.2 via NSS through JSS (cfu) - -* Sat Aug 16 2014 Fedora Release Engineering - 4.2.6-34 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild - -* Sun Jun 08 2014 Fedora Release Engineering - 4.2.6-33 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild - -* Fri Mar 28 2014 Michael Simacek - 4.2.6-32 -- Use Requires: java-headless rebuild (#1067528) - -* Sat Aug 03 2013 Fedora Release Engineering - 4.2.6-31 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild - -* Wed Jul 17 2013 Nathan Kinder - 4.2.6-30 -- Bugzilla Bug #847120 - Unable to build JSS on F17 or newer - -* Thu Feb 14 2013 Fedora Release Engineering - 4.2.6-29 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild - -* Wed Dec 19 2012 Stanislav Ochotnicky - 4.2.6-28 -- revbump after jnidir change - -* Wed Dec 12 2012 Stanislav Ochotnicky - 4.2.6-27 -- Simple rebuild - -* Mon Nov 19 2012 Christina Fu - 4.2.6-26 -- added source URLs in spec file to pass Package Wrangler - -* Thu Jul 19 2012 Fedora Release Engineering - 4.2.6-25 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild - -* Fri Mar 30 2012 Matthew Harmsen - 4.2.6-24 -- Bugzilla Bug #783007 - Un-deprecate previously deprecated methods in - JSS 4.2.6 . . . BadPaddingException (mharmsen) - -* Tue Mar 20 2012 Christina Fu - 4.2.6-23 -- Bugzilla Bug #797351 - JSS - HSM token name was mistaken for manufacturer - identifier (cfu) -- Bugzilla Bug #804840 - [RFE] ECC encryption keys cannot be archived - ECC phase2 work - support for ECC encryption key archival and recovery (cfu) -- Bugzilla Bug #783007 - Un-deprecate previously deprecated methods in - JSS 4.2.6 . . . (mharmsen) -- Dogtag TRAC Task #109 (https://fedorahosted.org/pki/ticket/109) - add - benign JNI jar file symbolic link from JNI libdir to JNI jar file (mharmsen) - -* Fri Jan 13 2012 Fedora Release Engineering - 4.2.6-22 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild - -* Wed Oct 19 2011 Christina Fu - 4.2.6-21 -- Bugzilla Bug #737122 - DRM: during archiving and recovering, wrapping - unwrapping keys should be done in the token -- support for PKCS5v2; support for secure PKCS12 -- Bugzilla Bug #744797 - KRA key recovery (retrieve pkcs#12) fails after the - in-place upgrade( CS 8.0->8.1) - -* Mon Sep 19 2011 Matthew Harmsen - 4.2.6-20 -- Bugzilla Bug #715621 - Defects revealed by Coverity scan - -* Wed Aug 31 2011 Matthew Harmsen - 4.2.6-19.1 -- Bugzilla Bug #734590 - Refactor JNI libraries for Fedora 16+ . . . - -* Mon Aug 15 2011 Christina Fu - 4.2.6-19 -- Bugzilla Bug 733550 - DRM failed to recovery keys when in FIPS mode - (HSM + NSS) - -* Fri Aug 12 2011 Matthew Harmsen - 4.2.6-18 -- Bugzilla Bug #660436 - Warnings should be cleaned up in JSS build - (jdennis, mharmsen) - -* Wed May 18 2011 Christina Fu - 4.2.6-17 -- Bug 670980 - Cannot create system certs when using LunaSA HSM in FIPS Mode - and ECC algorithms (support tokens that don't do ECDH) - -* Fri Apr 08 2011 Jack Magne - 4.2.6-15.99 -- bug 694661 - TKS instance crash during token enrollment. - Back out of previous patch for #676083. - -* Thu Feb 24 2011 Andrew Wnuk - 4.2.6-15 -- bug 676083 - JSS: slots not freed - -* Wed Feb 09 2011 Fedora Release Engineering - 4.2.6-14 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild - -* Mon Jan 31 2011 John Dennis - 4.2.6-13 -- remove misleading comment in spec file concerning jar signing - -* Tue Jan 11 2011 Kevin Wright - 4.2.6-12 -- added missing patch line - -* Tue Dec 21 2010 Christina Fu - 4.2.6-11 -- bug 654657 - - Incorrect socket accept error message due to bad pointer arithmetic -- bug 661142 - - Verification should fail when a revoked certificate is added - -* Thu Dec 16 2010 John Dennis - 4.2.6-10 -- Resolves: bug 656094 - - Rebase jss to at least jss-4.2.6-9 -- - merge in updates from Fedora - move jar location to %%{_libdir}/jss and provide symlinks, on 32bit looks like this: - /usr/lib/java/jss4.jar -> /usr/lib/jss/jss4.jar - /usr/lib/jss/jss4-.jar - /usr/lib/jss/jss4.jar -> jss4-.jar - /usr/lib/jss/libjss4.so -- bug 654657 - - Incorrect socket accept error message due to bad pointer arithmetic -- bug 647364 - - Expose updated certificate verification function in JSS -- bug 529945 - - expose NSS calls for OCSP settings -- bug 638833 - - rfe ecc - add ec curve name support in JSS and CS -- - Need to explicitly catch UnsatisfiedLinkError exception for System.load() -- bug 533304 - - Move location of libjss4.so to subdirectory and use System.load() to - load it instead of System.loadLibrary() for Fedora packaging compliance - -* Mon Nov 30 2009 Dennis Gregorovic - 4.2.6-4.1 -- Rebuilt for RHEL 6 - -* Fri Jul 31 2009 Rob Crittenden 4.2.6-4 -- Resolves: bug 224688 - - Support ECC POP on the server -- Resolves: bug 469456 - - Server Sockets are hard coded to IPV4 -- Resolves: bug 509183 - - Set NSS dependency >= 3.12.3.99 - -* Fri Jul 24 2009 Fedora Release Engineering - 4.2.6-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild - -* Fri Jun 5 2009 Rob Crittenden 4.2.6-2 -- Include patch to fix missing @param so javadocs will build - -* Fri Jun 5 2009 Rob Crittenden 4.2.6-1 -- Resolves: bug 455305 - - CA ECC Signing Key Failure -- Resolves: bug 502111 - - Need JSS interface for NSS's PK11_GenerateKeyPairWithOpFlags() function -- Resolves: bug 503809 - - Update JSS version to 4.2.6 -- Resolves: bug 503817 - - Create JSS Javadocs as their own RPM - -* Wed Feb 25 2009 Fedora Release Engineering - 4.2.5-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild - -* Tue Aug 5 2008 Tom "spot" Callaway - 4.2.5-3 -- fix license tag - -* Tue Feb 19 2008 Fedora Release Engineering - 4.2.5-2 -- Autorebuild for GCC 4.3 - -* Fri Aug 3 2007 Rob Crittenden 4.2.5-1 -- update to 4.2.5 - -* Thu May 24 2007 Rob Crittenden 4.2.4-6 -- Use _jnidir macro instead of _javadir for the jar files. This will break - multilib installs but adheres to the jpackage spec. - -* Wed May 16 2007 Rob Crittenden 4.2.4-5 -- Include the 3 license files -- Remove Requires for nss and nspr. These libraries have versioned symbols - so BuildRequires is enough to set the minimum. -- Add sparc64 for the 64-bit list - -* Mon May 14 2007 Rob Crittenden 4.2.4-4 -- Included additional comments on jar signing and why ldconfig is not - required. - -* Thu May 10 2007 Rob Crittenden 4.2.4-3 -- Added information on how to pull the source into a tar.gz - -* Thu Mar 15 2007 Rob Crittenden 4.2.4-2 -- Added RPM_OPT_FLAGS to XCFLAGS -- Added link to Sun JCE information - -* Tue Feb 27 2007 Rob Crittenden 4.2.4-1 -- Initial build +* Thu Apr 05 2018 Dogtag PKI Team 4.4.3-1 +- Rebased to JSS 4.4.3 +- Red Hat Bugzilla #1548548 - Partial Fedora build flags injection diff --git a/lgpl.txt b/lgpl.txt deleted file mode 100644 index 5ab7695..0000000 --- a/lgpl.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 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. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -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 and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, 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 library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete 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 distribute a copy of this License along with the -Library. - - 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 Library or any portion -of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -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 Library, 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 Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you 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. - - If distribution of 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 satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be 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. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library 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. - - 9. 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 Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -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 with -this License. - - 11. 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 Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library 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 Library. - -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. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library 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. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser 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 Library -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 Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -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 - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "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 -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. 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 LIBRARY 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 -LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/sources b/sources index 88399b9..2638641 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (jss-4.4.2.tar.gz) = 71f76aef2905c374a0d87b25a73db2498562fe16cef1489c5859271d2ae264695dd2b00880539c35b21f4e0b19856abc5028b813fe05b33352304b538182b251 +SHA512 (jss-4.4.4.tar.gz) = 206a2f26239a647b36f19277e2eebb73bfbd3bc436dabe708423498fb509956618a99c6f0532e47c7b7ddda71652612a450f7490b809866339a1d3827f7e2f51