diff options
author | Endi Sukma Dewata <edewata@redhat.com> | 2012-03-24 02:27:47 -0500 |
---|---|---|
committer | Endi Sukma Dewata <edewata@redhat.com> | 2012-03-26 11:43:54 -0500 |
commit | 621d9e5c413e561293d7484b93882d985b3fe15f (patch) | |
tree | 638f3d75761c121d9a8fb50b52a12a6686c5ac5c /base/util/src | |
parent | 40d3643b8d91886bf210aa27f711731c81a11e49 (diff) | |
download | pki-621d9e5c413e561293d7484b93882d985b3fe15f.tar.gz pki-621d9e5c413e561293d7484b93882d985b3fe15f.tar.xz pki-621d9e5c413e561293d7484b93882d985b3fe15f.zip |
Removed unnecessary pki folder.
Previously the source code was located inside a pki folder.
This folder was created during svn migration and is no longer
needed. This folder has now been removed and the contents have
been moved up one level.
Ticket #131
Diffstat (limited to 'base/util/src')
283 files changed, 54394 insertions, 0 deletions
diff --git a/base/util/src/CMakeLists.txt b/base/util/src/CMakeLists.txt new file mode 100644 index 000000000..592e3b59b --- /dev/null +++ b/base/util/src/CMakeLists.txt @@ -0,0 +1,369 @@ +project(pki-cmsutil_java Java) + +find_file(JSS_JAR + NAMES + jss4.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +find_file(LDAPJDK_JAR + NAMES + ldapjdk.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +find_file(COMMONS_CODEC_JAR + NAMES + commons-codec.jar + PATHS + /usr/share/java +) + +find_file(XALAN_JAR + NAMES + xalan-j2.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +find_file(XERCES_JAR + NAMES + xerces-j2.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +set(pki-nsutil_java_SRCS + netscape/net/TransferProtocolClient.java + netscape/net/smtp/SmtpProtocolException.java + netscape/net/smtp/SmtpClient.java + netscape/net/NetworkClient.java + netscape/security/pkcs/ParsingException.java + netscape/security/pkcs/PKCS9Attribute.java + netscape/security/pkcs/EncodingException.java + netscape/security/pkcs/SignerInfo.java + netscape/security/pkcs/PKCS10Attribute.java + netscape/security/pkcs/PKCS9Attributes.java + netscape/security/pkcs/ContentInfo.java + netscape/security/pkcs/PKCS10.java + netscape/security/pkcs/PKCS10Attributes.java + netscape/security/pkcs/PKCS7.java + netscape/security/pkcs/PKCS8Key.java + netscape/security/x509/OIDName.java + netscape/security/x509/CertificateChain.java + netscape/security/x509/CertificateVersion.java + netscape/security/x509/LdapV3DNStrConverter.java + netscape/security/x509/IssuerAlternativeNameExtension.java + netscape/security/x509/PolicyMappingsExtension.java + netscape/security/x509/CRLExtensions.java + netscape/security/x509/X500NameAttrMap.java + netscape/security/x509/CertificatePolicySet.java + netscape/security/x509/X509Cert.java + netscape/security/x509/NSCCommentExtension.java + netscape/security/x509/Attribute.java + netscape/security/x509/PrivateKeyUsageExtension.java + netscape/security/x509/X509CRLImpl.java + netscape/security/x509/X500Signer.java + netscape/security/x509/CPSuri.java + netscape/security/x509/AlgorithmId.java + netscape/security/x509/SerialNumber.java + netscape/security/x509/X509CertImpl.java + netscape/security/x509/CertificatePolicyInfo.java + netscape/security/x509/CertException.java + netscape/security/x509/CRLNumberExtension.java + netscape/security/x509/GeneralNameInterface.java + netscape/security/x509/PolicyQualifiers.java + netscape/security/x509/AVA.java + netscape/security/x509/CertificateX509Key.java + netscape/security/x509/RFC822Name.java + netscape/security/x509/Extensions.java + netscape/security/x509/FreshestCRLExtension.java + netscape/security/x509/UserNotice.java + netscape/security/x509/ReasonFlags.java + netscape/security/x509/InvalidIPAddressException.java + netscape/security/x509/IPAddressName.java + netscape/security/x509/SubjectKeyIdentifierExtension.java + netscape/security/x509/GenericValueConverter.java + netscape/security/x509/CRLReasonExtension.java + netscape/security/x509/CertificateAlgorithmId.java + netscape/security/x509/DisplayText.java + netscape/security/x509/CertificateValidity.java + netscape/security/x509/PKIXExtensions.java + netscape/security/x509/PrintableConverter.java + netscape/security/x509/SubjectDirAttributesExtension.java + netscape/security/x509/CRLDistributionPoint.java + netscape/security/x509/NameConstraintsExtension.java + netscape/security/x509/X509AttributeName.java + netscape/security/x509/RFC1779StrConverter.java + netscape/security/x509/X500Name.java + netscape/security/x509/DNSName.java + netscape/security/x509/URIName.java + netscape/security/x509/CertAndKeyGen.java + netscape/security/x509/RevocationReason.java + netscape/security/x509/AVAValueConverter.java + netscape/security/x509/CRLDistributionPointsExtension.java + netscape/security/x509/GeneralSubtree.java + netscape/security/x509/X509Key.java + netscape/security/x509/PolicyConstraintsExtension.java + netscape/security/x509/X509CertInfo.java + netscape/security/x509/LdapDNStrConverter.java + netscape/security/x509/X509ExtensionException.java + netscape/security/x509/AuthorityKeyIdentifierExtension.java + netscape/security/x509/NoticeReference.java + netscape/security/x509/IA5StringConverter.java + netscape/security/x509/UniqueIdentity.java + netscape/security/x509/RevokedCertificate.java + netscape/security/x509/CertificateExtensions.java + netscape/security/x509/HoldInstructionExtension.java + netscape/security/x509/IssuingDistributionPoint.java + netscape/security/x509/GeneralName.java + netscape/security/x509/CertificateIssuerName.java + netscape/security/x509/IssuingDistributionPointExtension.java + netscape/security/x509/AlgIdDSA.java + netscape/security/x509/DeltaCRLIndicatorExtension.java + netscape/security/x509/GeneralSubtrees.java + netscape/security/x509/BasicConstraintsExtension.java + netscape/security/x509/CertAttrSet.java + netscape/security/x509/GeneralNamesException.java + netscape/security/x509/ACertAttrSet.java + netscape/security/x509/Qualifier.java + netscape/security/x509/KeyIdentifier.java + netscape/security/x509/CertificateSerialNumber.java + netscape/security/x509/KeyUsageExtension.java + netscape/security/x509/SubjectAlternativeNameExtension.java + netscape/security/x509/CertificateSubjectUniqueIdentity.java + netscape/security/x509/CertificateSubjectName.java + netscape/security/x509/GeneralNames.java + netscape/security/x509/Extension.java + netscape/security/x509/CertificatePoliciesExtension.java + netscape/security/x509/CertificateIssuerExtension.java + netscape/security/x509/RDN.java + netscape/security/x509/CertificatePolicyMap.java + netscape/security/x509/DirStrConverter.java + netscape/security/x509/CertificateIssuerUniqueIdentity.java + netscape/security/x509/PolicyQualifierInfo.java + netscape/security/x509/EDIPartyName.java + netscape/security/x509/InvalidityDateExtension.java + netscape/security/x509/CertificatePolicyId.java + netscape/security/x509/CertParseError.java + netscape/security/x509/OIDMap.java + netscape/security/x509/PolicyConstraint.java + netscape/security/x509/RevokedCertImpl.java + netscape/security/x509/OtherName.java + netscape/security/util/ASN1CharsetProvider.java + netscape/security/util/ASN1CharStrConvMap.java + netscape/security/util/BigInt.java + netscape/security/util/BitArray.java + netscape/security/util/ByteArrayLexOrder.java + netscape/security/util/ByteArrayTagOrder.java + netscape/security/util/CertPrettyPrint.java + netscape/security/util/CrlPrettyPrint.java + netscape/security/util/DerEncoder.java + netscape/security/util/DerInputBuffer.java + netscape/security/util/DerInputStream.java + netscape/security/util/DerOutputStream.java + netscape/security/util/DerValue.java + netscape/security/util/ExtPrettyPrint.java + netscape/security/util/IA5Charset.java + netscape/security/util/IA5CharsetDecoder.java + netscape/security/util/IA5CharsetEncoder.java + netscape/security/util/ObjectIdentifier.java + netscape/security/util/PrettyPrintFormat.java + netscape/security/util/PrettyPrintResources.java + netscape/security/util/PrintableCharset.java + netscape/security/util/PrintableCharsetDecoder.java + netscape/security/util/PrintableCharsetEncoder.java + netscape/security/util/PubKeyPrettyPrint.java + netscape/security/util/UniversalCharset.java + netscape/security/util/UniversalCharsetDecoder.java + netscape/security/util/UniversalCharsetEncoder.java + netscape/security/provider/DSA.java + netscape/security/provider/DSAPrivateKey.java + netscape/security/provider/DSAParameters.java + netscape/security/provider/DSAPublicKey.java + netscape/security/provider/RSAPublicKey.java + netscape/security/provider/SHA.java + netscape/security/provider/MD5.java + netscape/security/provider/DSAParameterGenerator.java + netscape/security/provider/CMS.java + netscape/security/provider/DSAKeyPairGenerator.java + netscape/security/provider/DSAKeyFactory.java + netscape/security/provider/Sun.java + netscape/security/provider/X509CertificateFactory.java + netscape/security/extensions/SubjectInfoAccessExtension.java + netscape/security/extensions/GenericASN1Extension.java + netscape/security/extensions/NSCertTypeExtension.java + netscape/security/extensions/CertificateScopeEntry.java + netscape/security/extensions/KerberosName.java + netscape/security/extensions/CertificateRenewalWindowExtension.java + netscape/security/extensions/PresenceServerExtension.java + netscape/security/extensions/OCSPNoCheckExtension.java + netscape/security/extensions/CertInfo.java + netscape/security/extensions/AccessDescription.java + netscape/security/extensions/InhibitAnyPolicyExtension.java + netscape/security/extensions/CertificateScopeOfUseExtension.java + netscape/security/extensions/AuthInfoAccessExtension.java + netscape/security/extensions/ExtendedKeyUsageExtension.java + netscape/security/acl/AclImpl.java + netscape/security/acl/AllPermissionsImpl.java + netscape/security/acl/PrincipalImpl.java + netscape/security/acl/AclEntryImpl.java + netscape/security/acl/OwnerImpl.java + netscape/security/acl/WorldGroupImpl.java + netscape/security/acl/GroupImpl.java + netscape/security/acl/PermissionImpl.java +) + +set(pki-cmsutil_java_SRCS + com/netscape/cmsutil/ldap/LDAPUtil.java + com/netscape/cmsutil/xml/XMLObject.java + com/netscape/cmsutil/scep/CRSPKIMessage.java + com/netscape/cmsutil/util/Cert.java + com/netscape/cmsutil/util/Fmt.java + com/netscape/cmsutil/util/HMACDigest.java + com/netscape/cmsutil/util/Utils.java + com/netscape/cmsutil/net/ISocketFactory.java + com/netscape/cmsutil/crypto/Token.java + com/netscape/cmsutil/crypto/Module.java + com/netscape/cmsutil/crypto/CryptoUtil.java + com/netscape/cmsutil/password/PlainPasswordReader.java + com/netscape/cmsutil/password/IPasswordWriter.java + com/netscape/cmsutil/password/IPasswordStore.java + com/netscape/cmsutil/password/IPasswordReader.java + com/netscape/cmsutil/password/PlainPasswordWriter.java + com/netscape/cmsutil/password/PlainPasswordFile.java + com/netscape/cmsutil/radius/FramedProtocolAttribute.java + com/netscape/cmsutil/radius/TerminationActionAttribute.java + com/netscape/cmsutil/radius/NASPortTypeAttribute.java + com/netscape/cmsutil/radius/LoginLATServiceAttribute.java + com/netscape/cmsutil/radius/VendorSpecificAttribute.java + com/netscape/cmsutil/radius/ServiceTypeAttribute.java + com/netscape/cmsutil/radius/FramedAppleTalkLinkAttribute.java + com/netscape/cmsutil/radius/RadiusConn.java + com/netscape/cmsutil/radius/StateAttribute.java + com/netscape/cmsutil/radius/Attribute.java + com/netscape/cmsutil/radius/ReplyMessageAttribute.java + com/netscape/cmsutil/radius/ResponseAuthenticator.java + com/netscape/cmsutil/radius/GenericAttribute.java + com/netscape/cmsutil/radius/AccessChallenge.java + com/netscape/cmsutil/radius/FramedMTUAttribute.java + com/netscape/cmsutil/radius/NASPortAttribute.java + com/netscape/cmsutil/radius/CallingStationIdAttribute.java + com/netscape/cmsutil/radius/UserNameAttribute.java + com/netscape/cmsutil/radius/PacketFactory.java + com/netscape/cmsutil/radius/UserPasswordAttribute.java + com/netscape/cmsutil/radius/NASPacket.java + com/netscape/cmsutil/radius/FramedRoutingAttribute.java + com/netscape/cmsutil/radius/LoginLATNodeAttribute.java + com/netscape/cmsutil/radius/AttributeSet.java + com/netscape/cmsutil/radius/RequestAuthenticator.java + com/netscape/cmsutil/radius/CHAPPasswordAttribute.java + com/netscape/cmsutil/radius/FramedIPNetmaskAttribute.java + com/netscape/cmsutil/radius/LoginLATPortAttribute.java + com/netscape/cmsutil/radius/CallbackNumberAttribute.java + com/netscape/cmsutil/radius/RejectException.java + com/netscape/cmsutil/radius/ChallengeException.java + com/netscape/cmsutil/radius/NASClassAttribute.java + com/netscape/cmsutil/radius/LoginTCPPortAttribute.java + com/netscape/cmsutil/radius/IdleTimeoutAttribute.java + com/netscape/cmsutil/radius/AccessReject.java + com/netscape/cmsutil/radius/SessionTimeoutAttribute.java + com/netscape/cmsutil/radius/FramedIPAddressAttribute.java + com/netscape/cmsutil/radius/FramedRouteAttribute.java + com/netscape/cmsutil/radius/AccessAccept.java + com/netscape/cmsutil/radius/FramedAppleTalkNetworkAttribute.java + com/netscape/cmsutil/radius/ProxyStateAttribute.java + com/netscape/cmsutil/radius/FilterIdAttribute.java + com/netscape/cmsutil/radius/NASIdentifierAttribute.java + com/netscape/cmsutil/radius/LoginIPHostAttribute.java + com/netscape/cmsutil/radius/LoginServiceAttribute.java + com/netscape/cmsutil/radius/PortLimitAttribute.java + com/netscape/cmsutil/radius/AttributeFactory.java + com/netscape/cmsutil/radius/ServerPacket.java + com/netscape/cmsutil/radius/FramedAppleTalkZoneAttribute.java + com/netscape/cmsutil/radius/CHAPChallengeAttribute.java + com/netscape/cmsutil/radius/AccessRequest.java + com/netscape/cmsutil/radius/CallerStationIdAttribute.java + com/netscape/cmsutil/radius/Packet.java + com/netscape/cmsutil/radius/CallbackIdAttribute.java + com/netscape/cmsutil/radius/FramedIPXNetworkAttribute.java + com/netscape/cmsutil/radius/LoginLATGroupAttribute.java + com/netscape/cmsutil/radius/Authenticator.java + com/netscape/cmsutil/radius/NASIPAddressAttribute.java + com/netscape/cmsutil/radius/FramedCompressionAttribute.java + com/netscape/cmsutil/http/ConnectAsync.java + com/netscape/cmsutil/http/JssSSLSocketFactory.java + com/netscape/cmsutil/http/HttpClient.java + com/netscape/cmsutil/http/HttpMessage.java + com/netscape/cmsutil/http/HttpRequest.java + com/netscape/cmsutil/http/HttpResponse.java + com/netscape/cmsutil/http/Http.java + com/netscape/cmsutil/http/HttpEofException.java + com/netscape/cmsutil/http/HttpProtocolException.java + com/netscape/cmsutil/ocsp/Signature.java + com/netscape/cmsutil/ocsp/RevokedInfo.java + com/netscape/cmsutil/ocsp/TBSRequest.java + com/netscape/cmsutil/ocsp/CertID.java + com/netscape/cmsutil/ocsp/NameID.java + com/netscape/cmsutil/ocsp/KeyHashID.java + com/netscape/cmsutil/ocsp/OCSPRequest.java + com/netscape/cmsutil/ocsp/Response.java + com/netscape/cmsutil/ocsp/GoodInfo.java + com/netscape/cmsutil/ocsp/SingleResponse.java + com/netscape/cmsutil/ocsp/BasicOCSPResponse.java + com/netscape/cmsutil/ocsp/ResponseBytes.java + com/netscape/cmsutil/ocsp/OCSPResponse.java + com/netscape/cmsutil/ocsp/UnknownInfo.java + com/netscape/cmsutil/ocsp/OCSPResponseStatus.java + com/netscape/cmsutil/ocsp/CertStatus.java + com/netscape/cmsutil/ocsp/ResponseData.java + com/netscape/cmsutil/ocsp/Request.java + com/netscape/cmsutil/ocsp/ResponderID.java +) + +set(CMAKE_JAVA_INCLUDE_PATH + ${LDAPJDK_JAR} ${XALAN_JAR} ${XERCES_JAR} + ${JSS_JAR} ${COMMONS_CODEC_JAR}) + +set(CMAKE_JAVA_TARGET_VERSION ${APPLICATION_VERSION}) + +# build pki-nsutil +set(CMAKE_JAR_CLASSES_PREFIX netscape) +add_jar(pki-nsutil ${pki-nsutil_java_SRCS}) +install_jar(pki-nsutil ${JAVA_JAR_INSTALL_DIR}/pki) +set(PKI_NSUTIL_JAR ${pki-nsutil_JAR_FILE} CACHE INTERNAL "pki-nsutil jar file") + +# build pki-cmsutil +set(CMAKE_JAR_CLASSES_PREFIX com/netscape/cmsutil) +add_jar(pki-cmsutil ${pki-cmsutil_java_SRCS}) +add_dependencies(pki-cmsutil pki-nsutil) +install_jar(pki-cmsutil ${JAVA_JAR_INSTALL_DIR}/pki) +set(PKI_CMSUTIL_JAR ${pki-cmsutil_JAR_FILE} CACHE INTERNAL "pki-cmsutil jar file") + +create_javadoc(pki-util-${APPLICATION_VERSION} + PACKAGES com.netscape.cmsutil.crypto + com.netscape.cmsutil.http + com.netscape.cmsutil.ldap + com.netscape.cmsutil.net + com.netscape.cmsutil.ocsp + com.netscape.cmsutil.password + com.netscape.cmsutil.radius + com.netscape.cmsutil.scep + com.netscape.cmsutil.util + com.netscape.cmsutil.xml + SOURCEPATH ${CMAKE_CURRENT_SOURCE_DIR} + CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH} + WINDOWTITLE "pki-util" + WINDOWTITLE "pki-util" + DOCTITLE "<h1>dogtag</h1>" + AUTHOR TRUE + USE TRUE + VERSION TRUE +) +add_dependencies(pki-util-${APPLICATION_VERSION}_javadoc pki-cmsutil) diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java new file mode 100644 index 000000000..bf8a9cfc1 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java @@ -0,0 +1,1292 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.crypto; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharConversionException; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.security.pkcs.PKCS10; +import netscape.security.pkcs.PKCS7; +import netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.AlgorithmId; +import netscape.security.x509.CertificateAlgorithmId; +import netscape.security.x509.CertificateChain; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.CertificateIssuerName; +import netscape.security.x509.CertificateSerialNumber; +import netscape.security.x509.CertificateSubjectName; +import netscape.security.x509.CertificateValidity; +import netscape.security.x509.CertificateVersion; +import netscape.security.x509.CertificateX509Key; +import netscape.security.x509.X500Name; +import netscape.security.x509.X500Signer; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509CertInfo; +import netscape.security.x509.X509Key; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.CryptoManager.NotInitializedException; +import org.mozilla.jss.NoSuchTokenException; +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.BIT_STRING; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.crypto.Algorithm; +import org.mozilla.jss.crypto.BadPaddingException; +import org.mozilla.jss.crypto.Cipher; +import org.mozilla.jss.crypto.CryptoStore; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.DigestAlgorithm; +import org.mozilla.jss.crypto.EncryptionAlgorithm; +import org.mozilla.jss.crypto.IVParameterSpec; +import org.mozilla.jss.crypto.IllegalBlockSizeException; +import org.mozilla.jss.crypto.InternalCertificate; +import org.mozilla.jss.crypto.InvalidKeyFormatException; +import org.mozilla.jss.crypto.KeyGenAlgorithm; +import org.mozilla.jss.crypto.KeyGenerator; +import org.mozilla.jss.crypto.KeyPairAlgorithm; +import org.mozilla.jss.crypto.KeyPairGenerator; +import org.mozilla.jss.crypto.KeyWrapAlgorithm; +import org.mozilla.jss.crypto.KeyWrapper; +import org.mozilla.jss.crypto.NoSuchItemOnTokenException; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.PBEAlgorithm; +import org.mozilla.jss.crypto.PrivateKey; +import org.mozilla.jss.crypto.Signature; +import org.mozilla.jss.crypto.SignatureAlgorithm; +import org.mozilla.jss.crypto.SymmetricKey; +import org.mozilla.jss.crypto.TokenException; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkcs11.PK11ECPublicKey; +import org.mozilla.jss.pkcs12.PasswordConverter; +import org.mozilla.jss.pkcs7.EncryptedContentInfo; +import org.mozilla.jss.pkix.crmf.CertReqMsg; +import org.mozilla.jss.pkix.crmf.CertRequest; +import org.mozilla.jss.pkix.crmf.CertTemplate; +import org.mozilla.jss.pkix.crmf.EncryptedKey; +import org.mozilla.jss.pkix.crmf.EncryptedValue; +import org.mozilla.jss.pkix.crmf.PKIArchiveOptions; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; +import org.mozilla.jss.pkix.primitive.Name; +import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; +import org.mozilla.jss.util.Base64OutputStream; +import org.mozilla.jss.util.Password; + +import com.netscape.cmsutil.util.Cert; +import com.netscape.cmsutil.util.Utils; + +public class CryptoUtil { + + public static final String CERTREQ_BEGIN_HEADING = "-----BEGIN CERTIFICATE REQUEST-----"; + public static final String CERTREQ_END_HEADING = "-----END CERTIFICATE REQUEST-----"; + public static final int LINE_COUNT = 76; + public static final String CERT_BEGIN_HEADING = "-----BEGIN CERTIFICATE-----"; + public static final String CERT_END_HEADING = "-----END CERTIFICATE-----"; + + /* + * encodes cert + */ + // private static BASE64Encoder mEncoder = new BASE64Encoder(); + public static String toMIME64(X509CertImpl cert) { + try { + return "-----BEGIN CERTIFICATE-----\n" + // + mEncoder.encodeBuffer(cert.getEncoded()) + + Utils.base64encode(cert.getEncoded()) + + "-----END CERTIFICATE-----\n"; + } catch (Exception e) { + } + return null; + } + + public static boolean arraysEqual(byte[] bytes, byte[] ints) { + if (bytes == null || ints == null) { + return false; + } + + if (bytes.length != ints.length) { + return false; + } + + for (int i = 0; i < bytes.length; i++) { + if (bytes[i] != ints[i]) { + return false; + } + } + return true; + } + + /** + * Retrieves handle to a JSS token. + */ + public static CryptoToken getTokenByName(String token) + throws CryptoManager.NotInitializedException, + NoSuchTokenException { + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken t = null; + + if (token.equals("internal")) { + t = cm.getInternalKeyStorageToken(); + } else { + t = cm.getTokenByName(token); + } + return t; + } + + /** + * Generates a RSA key pair. + */ + public static KeyPair generateRSAKeyPair(String token, int keysize) + throws CryptoManager.NotInitializedException, + NoSuchTokenException, + NoSuchAlgorithmException, + TokenException { + CryptoToken t = getTokenByName(token); + KeyPairGenerator g = t.getKeyPairGenerator(KeyPairAlgorithm.RSA); + + g.initialize(keysize); + KeyPair pair = g.genKeyPair(); + + return pair; + } + + public static boolean isECCKey(X509Key key) { + String keyAlgo = key.getAlgorithm(); + if (keyAlgo.equals("EC") || + keyAlgo.equals("OID.1.2.840.10045.44")) { // ECC + return true; + } + return false; + } + + /** + * Generates an ecc key pair. + */ + public static KeyPair generateECCKeyPair(String token, int keysize) + throws CryptoManager.NotInitializedException, + NoSuchTokenException, + NoSuchAlgorithmException, + TokenException { + return generateECCKeyPair(token, keysize, null, null); + } + + public static KeyPair generateECCKeyPair(String token, int keysize, + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage[] usage_ops, + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage[] usage_mask) + throws CryptoManager.NotInitializedException, + NoSuchTokenException, + NoSuchAlgorithmException, + TokenException { + CryptoToken t = getTokenByName(token); + + KeyPairAlgorithm alg = KeyPairAlgorithm.EC; + KeyPairGenerator g = t.getKeyPairGenerator(alg); + + g.setKeyPairUsages(usage_ops, usage_mask); + g.initialize(keysize); + + KeyPair pair = g.genKeyPair(); + + return pair; + } + + /** + * Generates an ecc key pair by curve name + */ + public static KeyPair generateECCKeyPair(String token, String curveName) + throws CryptoManager.NotInitializedException, + NoSuchTokenException, + NoSuchAlgorithmException, + TokenException { + return generateECCKeyPair(token, curveName, null, null); + } + + public static KeyPair generateECCKeyPair(CryptoToken token, String curveName) + throws CryptoManager.NotInitializedException, + NoSuchTokenException, + NoSuchAlgorithmException, + TokenException { + return generateECCKeyPair(token, curveName, null, null); + } + + public static KeyPair generateECCKeyPair(String token, String curveName, + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage[] usage_ops, + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage[] usage_mask) + throws CryptoManager.NotInitializedException, + NoSuchTokenException, + NoSuchAlgorithmException, + TokenException { + CryptoToken t = getTokenByName(token); + return generateECCKeyPair(t, curveName, usage_ops, usage_mask); + } + + public static KeyPair generateECCKeyPair(CryptoToken token, String curveName, + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage[] usage_ops, + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage[] usage_mask) + throws CryptoManager.NotInitializedException, + NoSuchTokenException, + NoSuchAlgorithmException, + TokenException { + KeyPairAlgorithm alg = KeyPairAlgorithm.EC; + KeyPairGenerator g = token.getKeyPairGenerator(alg); + + g.setKeyPairUsages(usage_ops, usage_mask); + + System.out.println("CryptoUtil: generateECCKeyPair: curve = " + curveName); + int curveCode = 0; + try { + curveCode = g.getCurveCodeByName(curveName); + } catch (Exception e) { + System.out.println("CryptoUtil: generateECCKeyPair: " + e.toString()); + throw new NoSuchAlgorithmException(); + } + g.initialize(curveCode); + + System.out.println("CryptoUtil: generateECCKeyPair: after KeyPairGenerator initialize with:" + curveName); + KeyPair pair = g.genKeyPair(); + + return pair; + } + + public static byte[] getModulus(PublicKey pubk) { + RSAPublicKey rsaKey = (RSAPublicKey) pubk; + + return rsaKey.getModulus().toByteArray(); + } + + public static byte[] getPublicExponent(PublicKey pubk) { + RSAPublicKey rsaKey = (RSAPublicKey) pubk; + + return rsaKey.getPublicExponent().toByteArray(); + } + + public static String base64Encode(byte[] bytes) throws IOException { + // All this streaming is lame, but Base64OutputStream needs a + // PrintStream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Base64OutputStream b64 = new Base64OutputStream(new + PrintStream(new + FilterOutputStream(output))); + + b64.write(bytes); + b64.flush(); + + // This is internationally safe because Base64 chars are + // contained within 8859_1 + return output.toString("8859_1"); + } + + public static byte[] base64Decode(String s) throws IOException { + // BASE64Decoder base64 = new BASE64Decoder(); + // byte[] d = base64.decodeBuffer(s); + byte[] d = Utils.base64decode(s); + + return d; + } + + /* + * formats a cert request + */ + public static String reqFormat(String content) { + String result = CERTREQ_BEGIN_HEADING + "\n"; + + while (content.length() >= LINE_COUNT) { + result = result + content.substring(0, LINE_COUNT) + "\n"; + content = content.substring(LINE_COUNT); + } + if (content.length() > 0) { + result = result + content + "\n" + CERTREQ_END_HEADING; + } else { + result = result + CERTREQ_END_HEADING; + } + + return result; + } + + public static String getPKCS10FromKey(String dn, + byte modulus[], byte exponent[], byte prikdata[]) + throws IOException, + InvalidKeyException, + TokenException, + NoSuchProviderException, + CertificateException, + SignatureException, + CryptoManager.NotInitializedException, + NoSuchAlgorithmException { + X509Key x509key = getPublicX509Key(modulus, exponent); + PrivateKey prik = findPrivateKeyFromID(prikdata); + PKCS10 pkcs10 = createCertificationRequest(dn, x509key, prik); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bs); + pkcs10.print(ps); + return bs.toString(); + } + + public static String getPKCS10FromKey(String dn, + byte modulus[], byte exponent[], byte prikdata[], String alg) + throws IOException, + InvalidKeyException, + TokenException, + NoSuchProviderException, + CertificateException, + SignatureException, + CryptoManager.NotInitializedException, + NoSuchAlgorithmException { + X509Key x509key = getPublicX509Key(modulus, exponent); + PrivateKey prik = findPrivateKeyFromID(prikdata); + PKCS10 pkcs10 = createCertificationRequest(dn, x509key, prik, alg); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bs); + pkcs10.print(ps); + return bs.toString(); + } + + /* + * formats a cert + */ + public static String certFormat(String content) { + if (content == null || content.length() == 0) { + return ""; + } + String result = CERT_BEGIN_HEADING + "\n"; + + while (content.length() >= LINE_COUNT) { + result = result + content.substring(0, LINE_COUNT) + "\n"; + content = content.substring(LINE_COUNT); + } + if (content.length() > 0) { + result = result + content + "\n" + CERT_END_HEADING; + } else { + result = result + CERT_END_HEADING; + } + + return result; + } + + /** + * strips out the begin and end certificate brackets + * + * @param s the string potentially bracketed with + * "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" + * @return string without the brackets + */ + public static String stripCertBrackets(String s) { + if (s == null) { + return s; + } + + if (s.startsWith(CERT_BEGIN_HEADING) && s.endsWith(CERT_END_HEADING)) { + 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 normalizeCertAndReq(String s) { + if (s == null) { + return s; + } + s = s.replaceAll("-----BEGIN CERTIFICATE REQUEST-----", ""); + s = s.replaceAll("-----BEGIN NEW CERTIFICATE REQUEST-----", ""); + s = s.replaceAll("-----END CERTIFICATE REQUEST-----", ""); + s = s.replaceAll("-----END NEW CERTIFICATE REQUEST-----", ""); + s = s.replaceAll("-----BEGIN CERTIFICATE-----", ""); + s = s.replaceAll("-----END CERTIFICATE-----", ""); + + StringBuffer sb = new StringBuffer(); + StringTokenizer st = new StringTokenizer(s, "\r\n "); + + while (st.hasMoreTokens()) { + String nextLine = st.nextToken(); + + nextLine = nextLine.trim(); + if (nextLine.equals("-----BEGIN CERTIFICATE REQUEST-----")) { + continue; + } + if (nextLine.equals("-----BEGIN NEW CERTIFICATE REQUEST-----")) { + continue; + } + if (nextLine.equals("-----END CERTIFICATE REQUEST-----")) { + continue; + } + if (nextLine.equals("-----END NEW CERTIFICATE REQUEST-----")) { + continue; + } + if (nextLine.equals("-----BEGIN CERTIFICATE-----")) { + continue; + } + if (nextLine.equals("-----END CERTIFICATE-----")) { + continue; + } + sb.append(nextLine); + } + return sb.toString(); + } + + public static String normalizeCertStr(String s) { + String val = ""; + + 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 += s.charAt(i); + } + return val; + } + + public static void importCertificateChain(String certchain) + throws IOException, + CryptoManager.NotInitializedException, + TokenException, + CertificateEncodingException, + CertificateException { + byte[] blah = base64Decode(certchain); + CryptoManager manager = CryptoManager.getInstance(); + PKCS7 pkcs7 = null; + try { + // try PKCS7 first + pkcs7 = new PKCS7(blah); + } catch (Exception e) { + } + X509Certificate cert = null; + if (pkcs7 == null) { + cert = manager.importCACertPackage(blah); + } else { + java.security.cert.X509Certificate certsInP7[] = + pkcs7.getCertificates(); + if (certsInP7 == null) { + cert = manager.importCACertPackage(blah); + } else { + for (int i = 0; i < certsInP7.length; i++) { + // import P7 one by one + cert = manager.importCACertPackage(certsInP7[i].getEncoded()); + } + } + } + X509Certificate[] certchains = + CryptoManager.getInstance().buildCertificateChain(cert); + + if (certchains != null) { + cert = certchains[certchains.length - 1]; + } + InternalCertificate icert = (InternalCertificate) cert; + icert.setSSLTrust(InternalCertificate.TRUSTED_CA + | InternalCertificate.TRUSTED_CLIENT_CA + | InternalCertificate.VALID_CA); + } + + public static SEQUENCE parseCRMFMsgs(byte cert_request[]) + throws IOException, InvalidBERException { + ByteArrayInputStream crmfBlobIn = + new ByteArrayInputStream(cert_request); + SEQUENCE crmfMsgs = (SEQUENCE) + new SEQUENCE.OF_Template(new CertReqMsg.Template()).decode( + crmfBlobIn); + return crmfMsgs; + } + + public static X509Key getX509KeyFromCRMFMsgs(SEQUENCE crmfMsgs) + throws IOException, NoSuchAlgorithmException, + InvalidKeyException, InvalidKeyFormatException { + int nummsgs = crmfMsgs.size(); + if (nummsgs <= 0) { + throw new IOException("invalid certificate requests"); + } + CertReqMsg msg = (CertReqMsg) crmfMsgs.elementAt(0); + CertRequest certreq = msg.getCertReq(); + CertTemplate certTemplate = certreq.getCertTemplate(); + SubjectPublicKeyInfo spkinfo = certTemplate.getPublicKey(); + PublicKey pkey = spkinfo.toPublicKey(); + X509Key x509key = convertPublicKeyToX509Key(pkey); + return x509key; + } + + public static X509Key getPublicX509Key(byte modulus[], byte exponent[]) + throws InvalidKeyException { + return new netscape.security.provider.RSAPublicKey(new BigInt(modulus), + new BigInt(exponent)); + } + + public static X509Key getPublicX509ECCKey(byte encoded[]) + throws InvalidKeyException { + try { + return X509Key.parse(new DerValue(encoded)); + } catch (IOException e) { + throw new InvalidKeyException(); + } + } + + public static X509Key convertPublicKeyToX509Key(PublicKey pubk) + throws InvalidKeyException { + X509Key xKey; + + if (pubk instanceof RSAPublicKey) { + RSAPublicKey rsaKey = (RSAPublicKey) pubk; + + xKey = new netscape.security.provider.RSAPublicKey( + new BigInt(rsaKey.getModulus()), + new BigInt(rsaKey.getPublicExponent())); + } else if (pubk instanceof PK11ECPublicKey) { + byte encoded[] = pubk.getEncoded(); + xKey = CryptoUtil.getPublicX509ECCKey(encoded); + } else { + // Assert.assert(pubk instanceof DSAPublicKey); + DSAPublicKey dsaKey = (DSAPublicKey) pubk; + DSAParams params = dsaKey.getParams(); + + xKey = new netscape.security.provider.DSAPublicKey(dsaKey.getY(), + params.getP(), params.getQ(), params.getG()); + } + return xKey; + } + + public static String getSubjectName(SEQUENCE crmfMsgs) + throws IOException { + int nummsgs = crmfMsgs.size(); + if (nummsgs <= 0) { + throw new IOException("invalid certificate requests"); + } + CertReqMsg msg = (CertReqMsg) crmfMsgs.elementAt(0); + CertRequest certreq = msg.getCertReq(); + CertTemplate certTemplate = certreq.getCertTemplate(); + Name n = certTemplate.getSubject(); + ByteArrayOutputStream subjectEncStream = new ByteArrayOutputStream(); + n.encode(subjectEncStream); + + byte[] b = subjectEncStream.toByteArray(); + X500Name subject = new X500Name(b); + return subject.toString(); + } + + /** + * Creates a Certificate template. + */ + public static X509CertInfo createX509CertInfo(KeyPair pair, + int serialno, String issuername, String subjname, + Date notBefore, Date notAfter) + throws IOException, + CertificateException, + InvalidKeyException { + return createX509CertInfo(convertPublicKeyToX509Key(pair.getPublic()), + serialno, issuername, subjname, notBefore, notAfter); + } + + public static X509CertInfo createX509CertInfo(PublicKey publickey, + int serialno, String issuername, String subjname, + Date notBefore, Date notAfter) + throws IOException, + CertificateException, + InvalidKeyException { + return createX509CertInfo(convertPublicKeyToX509Key(publickey), serialno, + issuername, subjname, notBefore, notAfter); + } + + public static X509CertInfo createX509CertInfo(X509Key x509key, + int serialno, String issuername, String subjname, + Date notBefore, Date notAfter) + throws IOException, + CertificateException, + InvalidKeyException { + // set default; use the other call with "alg" to set algorithm + String alg = "SHA256withRSA"; + try { + return createX509CertInfo(x509key, serialno, issuername, subjname, notBefore, notAfter, alg); + } catch (NoSuchAlgorithmException ex) { + // for those that calls the old call without alg + throw new CertificateException("createX509CertInfo old call should not be here"); + } + } + + public static X509CertInfo createX509CertInfo(X509Key x509key, + int serialno, String issuername, 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)); + info.set(X509CertInfo.ISSUER, new + CertificateIssuerName(new X500Name(issuername))); + 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 X509CertImpl signECCCert(PrivateKey privateKey, + X509CertInfo certInfo) + throws NoSuchTokenException, + CryptoManager.NotInitializedException, + NoSuchAlgorithmException, + NoSuchTokenException, + TokenException, + InvalidKeyException, + SignatureException, + IOException, + CertificateException { + // set default; use the other call with "alg" to specify algorithm + String alg = "SHA256withEC"; + return signECCCert(privateKey, certInfo, alg); + } + + public static X509CertImpl signECCCert(PrivateKey privateKey, + X509CertInfo certInfo, String alg) + throws NoSuchTokenException, + CryptoManager.NotInitializedException, + NoSuchAlgorithmException, + NoSuchTokenException, + TokenException, + InvalidKeyException, + SignatureException, + IOException, + CertificateException { + return signCert(privateKey, certInfo, + Cert.mapAlgorithmToJss(alg)); + } + + /** + * Signs certificate. + */ + public static X509CertImpl signCert(PrivateKey privateKey, + X509CertInfo certInfo, String alg) + throws NoSuchTokenException, + CryptoManager.NotInitializedException, + NoSuchAlgorithmException, + NoSuchTokenException, + TokenException, + InvalidKeyException, + SignatureException, + IOException, + CertificateException { + return signCert(privateKey, certInfo, + Cert.mapAlgorithmToJss(alg)); + } + + public static X509CertImpl signCert(PrivateKey privateKey, + X509CertInfo certInfo, SignatureAlgorithm sigAlg) + throws NoSuchTokenException, + CryptoManager.NotInitializedException, + NoSuchAlgorithmException, + NoSuchTokenException, + TokenException, + InvalidKeyException, + SignatureException, + IOException, + CertificateException { + + DerInputStream ds = new DerInputStream(ASN1Util.encode(sigAlg.toOID())); + ObjectIdentifier sigAlgOID = new ObjectIdentifier(ds); + AlgorithmId aid = new AlgorithmId(sigAlgOID); + certInfo.set(X509CertInfo.ALGORITHM_ID, + new CertificateAlgorithmId(aid)); + + org.mozilla.jss.crypto.PrivateKey priKey = + (org.mozilla.jss.crypto.PrivateKey) privateKey; + CryptoToken token = priKey.getOwningToken(); + + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream out = new DerOutputStream(); + + certInfo.encode(tmp); + Signature signer = token.getSignatureContext(sigAlg); + + signer.initSign(priKey); + signer.update(tmp.toByteArray()); + byte signed[] = signer.sign(); + + aid.encode(tmp); + tmp.putBitString(signed); + out.write(DerValue.tag_Sequence, tmp); + X509CertImpl signedCert = new X509CertImpl(out.toByteArray()); + + return signedCert; + } + + /** + * Creates a PKCS#10 request. + */ + public static PKCS10 createCertificationRequest(String subjectName, + X509Key pubk, PrivateKey prik) + throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, IOException, CertificateException, + SignatureException { + // give default + String alg = "SHA256withRSA"; + if (isECCKey(pubk)) { + alg = "SHA256withEC"; + } + return createCertificationRequest(subjectName, pubk, prik, alg); + } + + public static PKCS10 createCertificationRequest(String subjectName, + X509Key pubk, PrivateKey prik, String alg) + throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, IOException, CertificateException, + SignatureException { + X509Key key = pubk; + java.security.Signature sig = java.security.Signature.getInstance(alg, + "Mozilla-JSS"); + + sig.initSign(prik); + PKCS10 pkcs10 = new PKCS10(key); + X500Name name = new X500Name(subjectName); + X500Signer signer = new X500Signer(sig, name); + + pkcs10.encodeAndSign(signer); + return pkcs10; + } + + /** + * Creates a PKCS#10 request. + */ + public static PKCS10 createCertificationRequest(String subjectName, + KeyPair keyPair) + throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, IOException, CertificateException, + SignatureException { + String alg; + PublicKey pubk = keyPair.getPublic(); + X509Key key = convertPublicKeyToX509Key(pubk); + if (pubk instanceof RSAPublicKey) { + alg = "SHA256withRSA"; + } else if (isECCKey(key)) { + alg = "SHA256withEC"; + } else { + // Assert.assert(pubk instanceof DSAPublicKey); + alg = "DSA"; + } + return createCertificationRequest(subjectName, keyPair, alg); + } + + public static PKCS10 createCertificationRequest(String subjectName, + KeyPair keyPair, String alg) + throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, IOException, CertificateException, + SignatureException { + PublicKey pubk = keyPair.getPublic(); + X509Key key = convertPublicKeyToX509Key(pubk); + + java.security.Signature sig = java.security.Signature.getInstance(alg, + "Mozilla-JSS"); + + sig.initSign(keyPair.getPrivate()); + + PKCS10 pkcs10 = new PKCS10(key); + + X500Name name = new X500Name(subjectName); + X500Signer signer = new X500Signer(sig, name); + + pkcs10.encodeAndSign(signer); + + return pkcs10; + } + + public static void unTrustCert(InternalCertificate cert) { + // remove TRUSTED_CA + int flag = cert.getSSLTrust(); + + flag ^= InternalCertificate.VALID_CA; + cert.setSSLTrust(flag); + } + + /** + * Trusts a certificate by nickname. + */ + public static void trustCertByNickname(String nickname) + throws CryptoManager.NotInitializedException, + TokenException { + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate certs[] = cm.findCertsByNickname(nickname); + + if (certs == null) { + return; + } + for (int i = 0; i < certs.length; i++) { + trustCert((InternalCertificate) certs[i]); + } + } + + /** + * Trusts a certificate. + */ + public static void trustCert(InternalCertificate cert) { + int flag = InternalCertificate.VALID_CA | InternalCertificate.TRUSTED_CA + | InternalCertificate.USER + | InternalCertificate.TRUSTED_CLIENT_CA; + + cert.setSSLTrust(flag); + cert.setObjectSigningTrust(flag); + cert.setEmailTrust(flag); + } + + /** + * To certificate server point of view, SSL trust is + * what we referring. + */ + public static boolean isCertTrusted(InternalCertificate cert) { + if (isTrust(cert.getSSLTrust()) && isTrust(cert.getObjectSigningTrust()) + && isTrust(cert.getEmailTrust())) { + return true; + } else { + return false; + } + } + + public static boolean isTrust(int flag) { + if (((flag & InternalCertificate.VALID_CA) > 0) + && ((flag & InternalCertificate.TRUSTED_CA) > 0) + && ((flag & InternalCertificate.USER) > 0) + && ((flag & InternalCertificate.TRUSTED_CLIENT_CA) > 0)) { + return true; + } else { + return false; + } + } + + /** + * Generates a symmetric key. + */ + public static SymmetricKey generateKey(CryptoToken token, + KeyGenAlgorithm alg) + throws TokenException, NoSuchAlgorithmException, + IllegalStateException { + try { + KeyGenerator kg = token.getKeyGenerator(alg); + + return kg.generate(); + } catch (CharConversionException e) { + throw new RuntimeException( + "CharConversionException while generating symmetric key"); + } + } + + /** + * Compares 2 byte arrays to see if they are the same. + */ + public static boolean compare(byte src[], byte dest[]) { + if (src != null && dest != null) { + if (src.length == dest.length) { + boolean matched = true; + + for (int i = 0; i < src.length; i++) { + if (src[i] != dest[i]) { + matched = false; + } + } + if (matched) { + return true; + } + } + } + return false; + } + + public static String byte2string(byte id[]) { + return new BigInteger(id).toString(16); + } + + public static byte[] string2byte(String id) { + return (new BigInteger(id, 16)).toByteArray(); + } + + /** + * Retrieves a private key from a unique key ID. + */ + public static PrivateKey findPrivateKeyFromID(byte id[]) + throws CryptoManager.NotInitializedException, + TokenException { + CryptoManager cm = CryptoManager.getInstance(); + @SuppressWarnings("unchecked") + Enumeration<CryptoToken> enums = cm.getAllTokens(); + + while (enums.hasMoreElements()) { + CryptoToken token = enums.nextElement(); + CryptoStore store = token.getCryptoStore(); + PrivateKey keys[] = store.getPrivateKeys(); + + if (keys != null) { + for (int i = 0; i < keys.length; i++) { + if (compare(keys[i].getUniqueID(), id)) { + return keys[i]; + } + } + } + } + return null; + } + + /** + * Retrieves all user certificates from all tokens. + */ + public static X509CertImpl[] getAllUserCerts() + throws CryptoManager.NotInitializedException, + TokenException { + Vector<X509CertImpl> certs = new Vector<X509CertImpl>(); + CryptoManager cm = CryptoManager.getInstance(); + @SuppressWarnings("unchecked") + Enumeration<CryptoToken> enums = cm.getAllTokens(); + + while (enums.hasMoreElements()) { + CryptoToken token = (CryptoToken) enums.nextElement(); + + CryptoStore store = token.getCryptoStore(); + org.mozilla.jss.crypto.X509Certificate list[] = store.getCertificates(); + + for (int i = 0; i < list.length; i++) { + try { + @SuppressWarnings("unused") + PrivateKey key = cm.findPrivKeyByCert(list[i]); // check for errors + X509CertImpl impl = null; + + try { + impl = new X509CertImpl(list[i].getEncoded()); + } catch (CertificateException e) { + continue; + } + certs.addElement(impl); + } catch (TokenException e) { + continue; + } catch (ObjectNotFoundException e) { + continue; + } + } + } + if (certs.size() == 0) { + return null; + } else { + X509CertImpl c[] = new X509CertImpl[certs.size()]; + + certs.copyInto(c); + return c; + } + } + + /** + * Deletes a private key. + */ + public static void deletePrivateKey(PrivateKey prikey) + throws CryptoManager.NotInitializedException, TokenException { + + try { + CryptoToken token = prikey.getOwningToken(); + CryptoStore store = token.getCryptoStore(); + + store.deletePrivateKey(prikey); + } catch (NoSuchItemOnTokenException e) { + } + } + + /** + * Retrieves a private key by nickname. + */ + public static PrivateKey getPrivateKey(String nickname) + throws CryptoManager.NotInitializedException, TokenException { + try { + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate cert = cm.findCertByNickname(nickname); + org.mozilla.jss.crypto.PrivateKey prikey = cm.findPrivKeyByCert(cert); + + return prikey; + } catch (ObjectNotFoundException e) { + } + return null; + } + + /** + * Deletes all certificates by a nickname. + */ + public static void deleteAllCertificates(String nickname) + throws CryptoManager.NotInitializedException, TokenException { + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate certs[] = cm.findCertsByNickname(nickname); + + if (certs == null) { + return; + } + for (int i = 0; i < certs.length; i++) { + try { + X509Certificate cert = certs[i]; + org.mozilla.jss.crypto.PrivateKey prikey = cm.findPrivKeyByCert( + cert); + CryptoToken token = prikey.getOwningToken(); + CryptoStore store = token.getCryptoStore(); + + store.deleteCert(cert); + } catch (NoSuchItemOnTokenException e) { + } catch (ObjectNotFoundException e) { + } + } + } + + /** + * Imports a PKCS#7 certificate chain that includes the user + * certificate, and trusts the certificate. + */ + public static X509Certificate importUserCertificateChain(String c, + String nickname) + throws CryptoManager.NotInitializedException, + CryptoManager.NicknameConflictException, + CryptoManager.UserCertConflictException, + NoSuchItemOnTokenException, + TokenException, + CertificateEncodingException { + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate cert = cm.importCertPackage(c.getBytes(), nickname); + + trustCertByNickname(nickname); + return cert; + } + + /** + * Imports a user certificate, and trusts the certificate. + */ + public static void importUserCertificate(X509CertImpl cert, String nickname) + throws CryptoManager.NotInitializedException, + CertificateEncodingException, + NoSuchItemOnTokenException, + TokenException, + CryptoManager.NicknameConflictException, + CryptoManager.UserCertConflictException { + CryptoManager cm = CryptoManager.getInstance(); + + cm.importUserCACertPackage(cert.getEncoded(), nickname); + trustCertByNickname(nickname); + } + + public static void importUserCertificate(X509CertImpl cert, String nickname, + boolean trust) + throws CryptoManager.NotInitializedException, + CertificateEncodingException, + NoSuchItemOnTokenException, + TokenException, + CryptoManager.NicknameConflictException, + CryptoManager.UserCertConflictException { + CryptoManager cm = CryptoManager.getInstance(); + + cm.importUserCACertPackage(cert.getEncoded(), nickname); + if (trust) + trustCertByNickname(nickname); + } + + public static java.security.cert.X509Certificate[] getX509CertificateFromPKCS7(byte[] b) throws IOException { + ByteArrayInputStream bis = new ByteArrayInputStream(b); + CertificateChain certchain = new CertificateChain(); + + certchain.decode(bis); + java.security.cert.X509Certificate[] certs = certchain.getChain(); + + return certs; + } + + @SuppressWarnings("deprecation") + public static String unwrapUsingPassphrase(String wrappedRecoveredKey, String recoveryPassphrase) + throws IOException, InvalidBERException, InvalidKeyException, IllegalStateException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException, NotInitializedException, TokenException, + IllegalBlockSizeException, BadPaddingException { + EncryptedContentInfo cInfo = null; + String unwrappedData = null; + + //We have to do this to get the decoding to work. + @SuppressWarnings("unused") + PBEAlgorithm pbeAlg = PBEAlgorithm.PBE_SHA1_DES3_CBC; + + Password pass = new Password(recoveryPassphrase.toCharArray()); + PasswordConverter passConverter = new + PasswordConverter(); + + byte[] encoded = Utils.base64decode(wrappedRecoveredKey); + + ByteArrayInputStream inStream = new ByteArrayInputStream(encoded); + cInfo = (EncryptedContentInfo) + new EncryptedContentInfo.Template().decode(inStream); + + byte[] decodedData = cInfo.decrypt(pass, passConverter); + + unwrappedData = Utils.base64encode(decodedData); + + return unwrappedData; + } + + @SuppressWarnings("deprecation") + public static String unwrapUsingSymmetricKey(CryptoToken token, IVParameterSpec IV, byte[] wrappedRecoveredKey, + SymmetricKey recoveryKey, EncryptionAlgorithm alg) throws NoSuchAlgorithmException, TokenException, + BadPaddingException, + IllegalBlockSizeException, InvalidKeyException, InvalidAlgorithmParameterException { + + Cipher decryptor = token.getCipherContext(alg); + decryptor.initDecrypt(recoveryKey, IV); + byte[] unwrappedData = decryptor.doFinal(wrappedRecoveredKey); + String unwrappedS = Utils.base64encode(unwrappedData); + + return unwrappedS; + } + + @SuppressWarnings("deprecation") + public static byte[] wrapPassphrase(CryptoToken token, String passphrase, IVParameterSpec IV, SymmetricKey sk, + EncryptionAlgorithm alg) + throws NoSuchAlgorithmException, TokenException, InvalidKeyException, + InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException { + byte[] wrappedPassphrase = null; + Cipher encryptor = null; + + encryptor = token.getCipherContext(alg); + + if (encryptor != null) { + encryptor.initEncrypt(sk, IV); + wrappedPassphrase = encryptor.doFinal(passphrase.getBytes("UTF-8")); + } else { + throw new IOException("Failed to create cipher"); + } + + return wrappedPassphrase; + } + + @SuppressWarnings("deprecation") + public static byte[] wrapSymmetricKey(CryptoManager manager, CryptoToken token, String transportCert, + SymmetricKey sk) throws CertificateEncodingException, TokenException, NoSuchAlgorithmException, + InvalidKeyException, InvalidAlgorithmParameterException { + byte transport[] = Utils.base64decode(transportCert); + X509Certificate tcert = manager.importCACertPackage(transport); + KeyWrapper rsaWrap = token.getKeyWrapper(KeyWrapAlgorithm.RSA); + rsaWrap.initWrap(tcert.getPublicKey(), null); + byte session_data[] = rsaWrap.wrap(sk); + return session_data; + } + + @SuppressWarnings("deprecation") + public static byte[] createPKIArchiveOptions(CryptoManager manager, CryptoToken token, String transportCert, + SymmetricKey vek, String passphrase, KeyGenAlgorithm keyGenAlg, IVParameterSpec IV) throws TokenException, + CharConversionException, + NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException, + CertificateEncodingException, IOException, IllegalStateException, IllegalBlockSizeException, + BadPaddingException, InvalidBERException { + byte[] key_data = null; + + //generate session key + SymmetricKey sk = CryptoUtil.generateKey(token, keyGenAlg); + + if (passphrase != null) { + key_data = wrapPassphrase(token, passphrase, IV, sk, EncryptionAlgorithm.DES3_CBC_PAD); + } else { + // wrap payload using session key + KeyWrapper wrapper1 = token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + wrapper1.initWrap(sk, IV); + key_data = wrapper1.wrap(vek); + } + + // wrap session key using transport key + byte[] session_data = wrapSymmetricKey(manager, token, transportCert, sk); + + // create PKIArchiveOptions structure + AlgorithmIdentifier algS = new AlgorithmIdentifier(new OBJECT_IDENTIFIER("1.2.840.113549.3.7"), + new OCTET_STRING(IV.getIV())); + EncryptedValue encValue = new EncryptedValue(null, algS, new BIT_STRING(session_data, 0), null, null, + new BIT_STRING(key_data, 0)); + EncryptedKey key = new EncryptedKey(encValue); + PKIArchiveOptions opt = new PKIArchiveOptions(key); + + byte[] encoded = null; + + //Let's make sure we can decode the encoded PKIArchiveOptions.. + ByteArrayOutputStream oStream = new ByteArrayOutputStream(); + + opt.encode(oStream); + + encoded = oStream.toByteArray(); + ByteArrayInputStream inStream = new ByteArrayInputStream(encoded); + + @SuppressWarnings("unused") + PKIArchiveOptions options = (PKIArchiveOptions) + (new PKIArchiveOptions.Template()).decode(inStream); + + return encoded; + } +} + +// START ENABLE_ECC +// This following can be removed when JSS with ECC capability +// is integrated. +class CryptoAlgorithm extends Algorithm { + protected CryptoAlgorithm(int oidIndex, String name) { + super(oidIndex, name); + } +} + +class CryptoKeyPairAlgorithm extends KeyPairAlgorithm { + protected CryptoKeyPairAlgorithm(int oidIndex, String name, Algorithm algFamily) { + super(oidIndex, name, algFamily); + } +} + +class CryptoSignatureAlgorithm extends SignatureAlgorithm { + protected CryptoSignatureAlgorithm(int oidIndex, String name, + SignatureAlgorithm signingAlg, DigestAlgorithm digestAlg, + OBJECT_IDENTIFIER oid) { + super(oidIndex, name, signingAlg, digestAlg, oid); + } +} +// END ENABLE_ECC diff --git a/base/util/src/com/netscape/cmsutil/crypto/Module.java b/base/util/src/com/netscape/cmsutil/crypto/Module.java new file mode 100644 index 000000000..bf4a7fe73 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/crypto/Module.java @@ -0,0 +1,75 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.crypto; + +import java.util.Vector; + +import org.mozilla.jss.crypto.CryptoToken; + +public class Module { + // Common Name is the name given when module is added + private String mCommonName = ""; + // User friendly name is the name to be displayed on panel + private String mUserFriendlyName = ""; + private String mImagePath = ""; + // a Vector of Tokens + private Vector<Token> mTokens = null; + private boolean mFound = false; + + public Module(String name, String printName) { + mCommonName = name; + mUserFriendlyName = printName; + mTokens = new Vector<Token>(); + } + + public Module(String name, String printName, String image) { + mCommonName = name; + mUserFriendlyName = printName; + mImagePath = image; + mTokens = new Vector<Token>(); + } + + public void addToken(CryptoToken t) { + Token token = new Token(t); + mTokens.addElement(token); + } + + public String getCommonName() { + return mCommonName; + } + + public String getUserFriendlyName() { + return mUserFriendlyName; + } + + public String getImagePath() { + return mImagePath; + } + + public boolean isFound() { + return mFound; + } + + public void setFound(boolean isFound) { + mFound = isFound; + } + + public Vector<Token> getTokens() { + return mTokens; + } +} diff --git a/base/util/src/com/netscape/cmsutil/crypto/Token.java b/base/util/src/com/netscape/cmsutil/crypto/Token.java new file mode 100644 index 000000000..c6f5a5e3c --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/crypto/Token.java @@ -0,0 +1,57 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.crypto; + +import org.mozilla.jss.crypto.CryptoToken; + +public class Token { + CryptoToken mToken; + + public Token(CryptoToken token) { + mToken = token; + } + + public String getNickName() { + String nickName = ""; + try { + nickName = mToken.getName(); + } catch (Exception e) { + } + return nickName; + } + + public boolean isLoggedIn() { + boolean isLoggedIn = false; + try { + isLoggedIn = mToken.isLoggedIn(); + } catch (Exception e) { + } + + return isLoggedIn; + } + + public boolean isPresent() { + boolean isPresent = false; + try { + isPresent = mToken.isPresent(); + } catch (Exception e) { + } + + return isPresent; + } +} diff --git a/base/util/src/com/netscape/cmsutil/http/ConnectAsync.java b/base/util/src/com/netscape/cmsutil/http/ConnectAsync.java new file mode 100644 index 000000000..ca230ca21 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/ConnectAsync.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 com.netscape.cmsutil.http; + +import java.net.SocketException; + +import com.netscape.cmsutil.net.ISocketFactory; + +public class ConnectAsync extends Thread { + String host = null; + int port = 0; + ISocketFactory obj = null; + + public ConnectAsync(ISocketFactory sock, String host, int port) { + super(); + this.host = host; + this.port = port; + this.obj = sock; + setName("ConnectAsync"); + } + + public void run() { + try { + obj.makeSocket(host, port); + } catch (SocketException e) { + // Stop throwing exception + } catch (Exception e) { + // Stop throwing exception + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/http/Http.java b/base/util/src/com/netscape/cmsutil/http/Http.java new file mode 100644 index 000000000..2cda7fd12 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/Http.java @@ -0,0 +1,31 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.http; + +public class Http { + public static final String HttpVers = "HTTP/1.0"; + + public static final String Vers1_0 = "HTTP/1.0"; + public static final String Vers1_1 = "HTTP/1.1"; + public static final String CRLF = "\r\n"; + + public static final char CR = '\r'; + public static final char LF = '\n'; + public static final char SP = ' '; + +} diff --git a/base/util/src/com/netscape/cmsutil/http/HttpClient.java b/base/util/src/com/netscape/cmsutil/http/HttpClient.java new file mode 100644 index 000000000..438c70c23 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/HttpClient.java @@ -0,0 +1,217 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.Socket; + +import org.mozilla.jss.ssl.SSLCertificateApprovalCallback; + +import com.netscape.cmsutil.net.ISocketFactory; + +/** + * basic http client. + * not optimized for performance. + * handles only string content. + */ +public class HttpClient { + protected ISocketFactory mFactory = null; + + protected Socket mSocket = null; + protected InputStream mInputStream = null; + protected OutputStream mOutputStream = null; + + protected InputStreamReader mInputStreamReader = null; + protected OutputStreamWriter mOutputStreamWriter = null; + protected BufferedReader mBufferedReader = null; + protected SSLCertificateApprovalCallback mCertApprovalCallback = null; + protected boolean mConnected = false; + + public HttpClient() { + } + + public HttpClient(ISocketFactory factory) { + mFactory = factory; + } + + public HttpClient(ISocketFactory factory, SSLCertificateApprovalCallback certApprovalCallback) { + mFactory = factory; + mCertApprovalCallback = certApprovalCallback; + } + + public void connect(String host, int port) + throws IOException { + if (mFactory != null) { + if (mCertApprovalCallback == null) { + mSocket = mFactory.makeSocket(host, port); + } else { + mSocket = mFactory.makeSocket(host, port, mCertApprovalCallback, null); + } + } else { + mSocket = new Socket(host, port); + } + + if (mSocket == null) { + IOException e = new IOException("Couldn't make connection"); + + throw e; + } + + mInputStream = mSocket.getInputStream(); + mOutputStream = mSocket.getOutputStream(); + mInputStreamReader = new InputStreamReader(mInputStream, "UTF8"); + mBufferedReader = new BufferedReader(mInputStreamReader); + mOutputStreamWriter = new OutputStreamWriter(mOutputStream, "UTF8"); + mConnected = true; + } + + // Inserted by beomsuk + public void connect(String host, int port, int timeout) + throws IOException { + if (mFactory != null) { + mSocket = mFactory.makeSocket(host, port, timeout); + } else { + mSocket = new Socket(host, port); + } + + if (mSocket == null) { + IOException e = new IOException("Couldn't make connection"); + + throw e; + } + + mInputStream = mSocket.getInputStream(); + mOutputStream = mSocket.getOutputStream(); + mInputStreamReader = new InputStreamReader(mInputStream, "UTF8"); + mBufferedReader = new BufferedReader(mInputStreamReader); + mOutputStreamWriter = new OutputStreamWriter(mOutputStream, "UTF8"); + mConnected = true; + } + + // Insert end + public boolean connected() { + return mConnected; + } + + /** + * Sends a request to http server. + * Returns a http response. + */ + public HttpResponse send(HttpRequest request) + throws IOException { + HttpResponse resp = new HttpResponse(); + + if (mOutputStream == null) + throw new IOException("Output stream not initialized"); + request.write(mOutputStreamWriter); + try { + resp.parse(mBufferedReader); + } catch (IOException e) { + // XXX should we disconnect in all cases ? + disconnect(); + throw e; + } + disconnect(); + return resp; + } + + public void disconnect() + throws IOException { + mSocket.close(); + mInputStream = null; + mOutputStream = null; + mConnected = false; + } + + public InputStream getInputStream() { + return mInputStream; + } + + public OutputStream getOutputStream() { + return mOutputStream; + } + + public BufferedReader getBufferedReader() { + return mBufferedReader; + } + + public InputStreamReader getInputStreamReader() { + return mInputStreamReader; + } + + public OutputStreamWriter getOutputStreamWriter() { + return mOutputStreamWriter; + } + + public Socket getSocket() { + return mSocket; + } + + /** + * unit test + */ + public static void main(String args[]) + throws Exception { + HttpClient c = new HttpClient(); + HttpRequest req = new HttpRequest(); + HttpResponse resp = null; + + System.out.println("connecting to " + args[0] + " " + args[1]); + c.connect(args[0], Integer.parseInt(args[1])); + + req.setMethod("GET"); + req.setURI(args[2]); + if (args.length >= 4) + req.setHeader("Connection", args[3]); + resp = c.send(req); + + System.out.println("version " + resp.getHttpVers()); + System.out.println("status code " + resp.getStatusCode()); + System.out.println("reason " + resp.getReasonPhrase()); + System.out.println("content " + resp.getContent()); + + //String lenstr = resp.getHeader("Content-Length"); + //System.out.println("content len is "+lenstr); + //int length = Integer.parseInt(lenstr); + //char[] content = new char[length]; + //c.mBufferedReader.read(content, 0, content.length); + //System.out.println(content); + + if (args.length >= 4 && args[3].equalsIgnoreCase("keep-alive")) { + for (int i = 0; i < 2; i++) { + if (i == 1) + req.setHeader("Connection", "Close"); + resp = c.send(req); + System.out.println("version " + resp.getHttpVers()); + System.out.println("status code " + resp.getStatusCode()); + System.out.println("reason " + resp.getReasonPhrase()); + System.out.println("content " + resp.getContent()); + //len = Integer.parseInt(resp.getHeader("Content-Length")); + //System.out.println("content len is "+len); + //msgbody = new char[len]; + //c.mBufferedReader.read(msgbody, 0, len); + //System.out.println(content); + } + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/http/HttpEofException.java b/base/util/src/com/netscape/cmsutil/http/HttpEofException.java new file mode 100644 index 000000000..824b9ea2a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/HttpEofException.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 com.netscape.cmsutil.http; + +import java.io.IOException; + +public class HttpEofException extends IOException { + /** + * + */ + private static final long serialVersionUID = 433303354049669059L; + + public HttpEofException() { + super(); + } + + public HttpEofException(String msg) { + super(msg); + } +} diff --git a/base/util/src/com/netscape/cmsutil/http/HttpMessage.java b/base/util/src/com/netscape/cmsutil/http/HttpMessage.java new file mode 100644 index 000000000..badec5930 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/HttpMessage.java @@ -0,0 +1,163 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * Basic HTTP Message, excluding message body. + * Not optimized for performance. + * Set fields or parse from input. + */ +public class HttpMessage { + protected String mLine = null; // request or response line. + protected Hashtable<String, String> mHeaders = null; + protected String mContent = null; // arbitrary content chars assumed. + + /** + * Instantiate a HttpResponse for write to http client. + */ + public HttpMessage() { + mHeaders = new Hashtable<String, String>(); + } + + /** + * Set a header field. <br> + * Content-length is automatically set on write.<br> + * If value spans multiple lines must be in proper http format for + * multiple lines. + */ + public void setHeader(String name, String value) { + if (mHeaders == null) + mHeaders = new Hashtable<String, String>(); + mHeaders.put(name.toLowerCase(), value); + } + + /** + * get a header + */ + public String getHeader(String name) { + return (String) mHeaders.get(name.toLowerCase()); + } + + /** + * write http headers + * does not support values of more than one line + */ + public void writeHeaders(OutputStreamWriter writer) + throws IOException { + if (mHeaders != null) { + Enumeration<String> keys = mHeaders.keys(); + String header, value; + + while (keys.hasMoreElements()) { + header = keys.nextElement(); + value = mHeaders.get(header); + writer.write(header + ":" + value + Http.CRLF); + } + } + writer.write(Http.CRLF); // end with CRLF line. + } + + /** + * read http headers. + * does not support values of more than one line or multivalue headers. + */ + public void readHeaders(BufferedReader reader) + throws IOException { + mHeaders = new Hashtable<String, String>(); + + int colon; + String line, key, value; + + while (true) { + line = reader.readLine(); + if (line == null || line.equals("")) + break; + colon = line.indexOf(':'); + if (colon == -1) { + mHeaders = null; + throw new HttpProtocolException("Bad Http header format"); + } + key = line.substring(0, colon); + value = line.substring(colon + 1); + mHeaders.put(key.toLowerCase(), value.trim()); + } + } + + public void write(OutputStreamWriter writer) + throws IOException { + writer.write(mLine + Http.CRLF); + writeHeaders(writer); + writer.flush(); + if (mContent != null) { + writer.write(mContent); + } + writer.flush(); + } + + public void parse(BufferedReader reader) + throws IOException { + String line = reader.readLine(); + + // if (line == null) { + // throw new HttpEofException("End of stream reached"); + // } + if (line.equals("")) { + throw new HttpProtocolException("Bad Http req/resp line " + line); + } + mLine = line; + readHeaders(reader); + + // won't work if content length is not set. + String lenstr = mHeaders.get("content-length"); + + if (lenstr != null) { + int len = Integer.parseInt(lenstr); + char[] cbuf = new char[len]; + int done = reader.read(cbuf, 0, cbuf.length); + int total = done; + + while (done >= 0 && total < len) { + done = reader.read(cbuf, total, len - total); + total += done; + } + + mContent = new String(cbuf); + } + } + + public void reset() { + mLine = null; + mHeaders = null; + mContent = null; + } + + public void setContent(String content) { + mContent = content; + } + + public String getContent() { + return mContent; + } + +} diff --git a/base/util/src/com/netscape/cmsutil/http/HttpProtocolException.java b/base/util/src/com/netscape/cmsutil/http/HttpProtocolException.java new file mode 100644 index 000000000..b5ceb1d7f --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/HttpProtocolException.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 com.netscape.cmsutil.http; + +import java.io.IOException; + +public class HttpProtocolException extends IOException { + /** + * + */ + private static final long serialVersionUID = -953002842302351684L; + + public HttpProtocolException() { + super(); + } + + public HttpProtocolException(String msg) { + super(msg); + } +} diff --git a/base/util/src/com/netscape/cmsutil/http/HttpRequest.java b/base/util/src/com/netscape/cmsutil/http/HttpRequest.java new file mode 100644 index 000000000..9024dabf0 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/HttpRequest.java @@ -0,0 +1,137 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.OutputStreamWriter; + +/** + * Basic HTTP Request. not optimized for performance. + * Set fields or parse from input. + * Handles text content. + */ +public class HttpRequest extends HttpMessage { + public static final String GET = "GET"; + public static final String POST = "POST"; + public static final String HEAD = "HEAD"; + + protected String mMethod = null; + protected String mURI = null; + protected String mHttpVers = null; + + /** + * Instantiate a HttpResponse for write to http client. + */ + public HttpRequest() { + super(); + } + + /** + * set set request method. + */ + public void setMethod(String method) + throws HttpProtocolException { + if (!method.equals(GET) && !method.equals(HEAD) && + !method.equals(POST)) + throw new HttpProtocolException("No such method " + method); + mMethod = method; + } + + /** + * set reason phrase. + */ + public void setURI(String uri) { + mURI = uri; + } + + /** + * write request to the http client + */ + public void write(OutputStreamWriter writer) + throws IOException { + if (mMethod == null || mURI == null) { + HttpProtocolException e = new HttpProtocolException( + "Http request method or uri not initialized"); + + //e.printStackTrace(); + throw e; + } + + mLine = mMethod + " " + mURI + " " + Http.HttpVers; + super.write(writer); + } + + /** + * parse a http request from a http client + */ + public void parse(BufferedReader reader) + throws IOException { + super.parse(reader); + + int method = mLine.indexOf(Http.SP); + + mMethod = mLine.substring(0, method); + if (!mMethod.equals(GET) && !mMethod.equals(POST) && + !mMethod.equals(HEAD)) { + reset(); + throw new HttpProtocolException("Bad Http request method"); + } + + int uri = mLine.lastIndexOf(Http.SP); + + mURI = mLine.substring(method + 1, uri); + + mHttpVers = mLine.substring(uri + 1); + if (!mHttpVers.equals("")) { + if (!mHttpVers.equals(Http.Vers1_0) && + !mHttpVers.equals(Http.Vers1_1)) { + reset(); + throw new HttpProtocolException("Bad Http version in request"); + } + } + } + + public void reset() { + mMethod = null; + mURI = null; + mHttpVers = null; + super.reset(); + } + + /** + * get method + */ + public String getMethod() { + return mMethod; + } + + /** + * get reason phrase + */ + public String getURI() { + return mURI; + } + + /** + * get http version + */ + public String getHttpVers() { + return mHttpVers; + } +} diff --git a/base/util/src/com/netscape/cmsutil/http/HttpResponse.java b/base/util/src/com/netscape/cmsutil/http/HttpResponse.java new file mode 100644 index 000000000..7ac7e2f69 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/HttpResponse.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 com.netscape.cmsutil.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.OutputStreamWriter; + +/** + * Basic HTTP Response. + * Set fields or parse from input. + * Handles only text content. + */ +public class HttpResponse extends HttpMessage { + protected String mStatusCode = null; + protected String mReasonPhrase = null; + protected String mHttpVers = null; + + /** + * Instantiate a HttpResponse for write to http client. + */ + public HttpResponse() { + super(); + } + + /** + * set status code of response + */ + public void setStatusCode(int code) { + mStatusCode = String.valueOf(code); + } + + /** + * set reason phrase. + */ + public void setReasonPhrase(String phrase) { + mReasonPhrase = phrase; + } + + /** + * get status code + */ + public String getStatusCode() { + return mStatusCode; + } + + /** + * get reason phrase + */ + public String getReasonPhrase() { + return mReasonPhrase; + } + + /** + * write the response out to the http client + */ + public void write(OutputStreamWriter writer) + throws IOException { + if (mStatusCode == null) { + throw new HttpProtocolException("status code not set in response"); + } + // write status-line + mLine = Http.HttpVers + " " + mStatusCode + " "; + if (mReasonPhrase != null) + mLine += mReasonPhrase; + mLine += Http.CRLF; + super.write(writer); + } + + /** + * parse a http response from a http server + */ + public void parse(BufferedReader reader) + throws IOException { + mHttpVers = null; + mStatusCode = null; + mReasonPhrase = null; + + super.parse(reader); + + int httpvers = mLine.indexOf(' '); + + if (httpvers == -1) { + reset(); + throw new HttpProtocolException("no Http version in response"); + } + mHttpVers = mLine.substring(0, httpvers); + if (!mHttpVers.equals(Http.Vers1_0) && + !mHttpVers.equals(Http.Vers1_1)) { + reset(); + throw new HttpProtocolException("Bad Http version in response"); + } + + int code = mLine.indexOf(' ', httpvers + 1); + + if (code == -1) { + reset(); + throw new HttpProtocolException("no status code in response"); + } + mStatusCode = mLine.substring(httpvers + 1, code); + try { + Integer.parseInt(mStatusCode); + } catch (NumberFormatException e) { + reset(); + throw new HttpProtocolException("Bad status code in response"); + } + + mReasonPhrase = mLine.substring(code + 1); + } + + public void reset() { + mStatusCode = null; + mHttpVers = null; + mReasonPhrase = null; + super.reset(); + } + + /** + * get http version + */ + public String getHttpVers() { + return mHttpVers; + } +} diff --git a/base/util/src/com/netscape/cmsutil/http/JssSSLSocketFactory.java b/base/util/src/com/netscape/cmsutil/http/JssSSLSocketFactory.java new file mode 100644 index 000000000..c2013a5d2 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/http/JssSSLSocketFactory.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 com.netscape.cmsutil.http; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.ssl.SSLCertificateApprovalCallback; +import org.mozilla.jss.ssl.SSLClientCertificateSelectionCallback; +import org.mozilla.jss.ssl.SSLHandshakeCompletedEvent; +import org.mozilla.jss.ssl.SSLHandshakeCompletedListener; +import org.mozilla.jss.ssl.SSLSocket; + +import com.netscape.cmsutil.net.ISocketFactory; + +/** + * Uses NSS ssl socket. + * + * @version $Revision$ $Date$ + */ +public class JssSSLSocketFactory implements ISocketFactory { + private String mClientAuthCertNickname = null; + private SSLSocket s = null; + + public JssSSLSocketFactory() { + } + + public JssSSLSocketFactory(String certNickname) { + mClientAuthCertNickname = certNickname; + } + + // XXX remove these static SSL cipher suite initializations later on. + static final int cipherSuites[] = { + SSLSocket.SSL3_RSA_WITH_RC4_128_MD5, + SSLSocket.SSL3_RSA_WITH_3DES_EDE_CBC_SHA, + SSLSocket.SSL3_RSA_WITH_DES_CBC_SHA, + SSLSocket.SSL3_RSA_EXPORT_WITH_RC4_40_MD5, + SSLSocket.SSL3_RSA_EXPORT_WITH_RC2_CBC_40_MD5, + SSLSocket.SSL3_RSA_WITH_NULL_MD5, + SSLSocket.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSLSocket.TLS_RSA_WITH_AES_128_CBC_SHA, + SSLSocket.TLS_RSA_WITH_AES_256_CBC_SHA, + SSLSocket.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSLSocket.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + //SSLSocket.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + //SSLSocket.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + //SSLSocket.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + SSLSocket.TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + SSLSocket.TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + SSLSocket.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + SSLSocket.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + 0 + }; + + static { + int i; + + for (i = SSLSocket.SSL2_RC4_128_WITH_MD5; i <= SSLSocket.SSL2_RC2_128_CBC_EXPORT40_WITH_MD5; ++i) { + try { + SSLSocket.setCipherPreferenceDefault(i, false); + } catch (SocketException e) { + } + } + + //skip SSL_EN_IDEA_128_EDE3_CBC_WITH_MD5 + for (i = SSLSocket.SSL2_DES_64_CBC_WITH_MD5; i <= SSLSocket.SSL2_DES_192_EDE3_CBC_WITH_MD5; ++i) { + try { + SSLSocket.setCipherPreferenceDefault(i, false); + } catch (SocketException e) { + } + } + for (i = 0; cipherSuites[i] != 0; ++i) { + try { + SSLSocket.setCipherPreferenceDefault(cipherSuites[i], true); + } catch (SocketException e) { + } + } + } + + public Socket makeSocket(String host, int port) + throws IOException, UnknownHostException { + return makeSocket(host, port, null, null); + } + + public Socket makeSocket(String host, int port, + SSLCertificateApprovalCallback certApprovalCallback, + SSLClientCertificateSelectionCallback clientCertCallback) + throws IOException, UnknownHostException { + + try { + s = new SSLSocket(host, port, null, 0, certApprovalCallback, + clientCertCallback); + for (int i = 0; cipherSuites[i] != 0; ++i) { + try { + SSLSocket.setCipherPreferenceDefault(cipherSuites[i], true); + } catch (SocketException e) { + } + } + + s.setUseClientMode(true); + s.enableSSL2(false); + //TODO Do we rally want to set the default each time? + SSLSocket.enableSSL2Default(false); + s.enableV2CompatibleHello(false); + + SSLHandshakeCompletedListener listener = null; + + listener = new ClientHandshakeCB(this); + s.addHandshakeCompletedListener(listener); + + if (mClientAuthCertNickname != null) { + // 052799 setClientCertNickname does not + // report error if the nickName is invalid. + // So we check this ourself using + // findCertByNickname + CryptoManager.getInstance().findCertByNickname(mClientAuthCertNickname); + + s.setClientCertNickname(mClientAuthCertNickname); + } + s.forceHandshake(); + } catch (org.mozilla.jss.crypto.ObjectNotFoundException e) { + throw new IOException(e.toString()); + } catch (org.mozilla.jss.crypto.TokenException e) { + throw new IOException(e.toString()); + } catch (UnknownHostException e) { + throw e; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e.toString()); + } + return s; + } + + public Socket makeSocket(String host, int port, int timeout) + throws IOException, UnknownHostException { + Thread t = new ConnectAsync(this, host, port); + + t.start(); + try { + t.join(1000 * timeout); + } catch (InterruptedException e) { + } + + if (t.isAlive()) { + } + + return s; + } + + public void log(int level, String msg) { + } + + class ClientHandshakeCB implements SSLHandshakeCompletedListener { + Object sc; + + public ClientHandshakeCB(Object sc) { + this.sc = sc; + } + + public void handshakeCompleted(SSLHandshakeCompletedEvent event) { + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ldap/LDAPUtil.java b/base/util/src/com/netscape/cmsutil/ldap/LDAPUtil.java new file mode 100644 index 000000000..e821db67a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ldap/LDAPUtil.java @@ -0,0 +1,101 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ldap; + +import java.io.IOException; +import java.util.ArrayList; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPAttributeSet; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.util.LDIF; +import netscape.ldap.util.LDIFAttributeContent; +import netscape.ldap.util.LDIFContent; +import netscape.ldap.util.LDIFModifyContent; +import netscape.ldap.util.LDIFRecord; + +public class LDAPUtil { + + // special chars are *, (, ), \, null + public static String SPECIAL_CHARS = "*()\\\000"; + + /** + * This method escapes special characters for LDAP filter (RFC 4515). + * Each special character will be replaced by a backslash followed by + * 2-digit hex of the ASCII code. + * + * @param string string to escape + * @return escaped string + */ + public static String escape(String string) { + StringBuilder sb = new StringBuilder(); + for (char c : string.toCharArray()) { + if (SPECIAL_CHARS.indexOf(c) >= 0) { + sb.append('\\'); + if (c < 0x10) sb.append('0'); // make sure it's 2-digit + sb.append(Integer.toHexString(c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + public static void importLDIF(LDAPConnection conn, String filename, ArrayList<String> errors) throws IOException { + LDIF ldif = new LDIF(filename); + while (true) { + try { + LDIFRecord record = ldif.nextRecord(); + if (record == null) + break; + + String dn = record.getDN(); + LDIFContent content = record.getContent(); + int type = content.getType(); + if (type == LDIFContent.ATTRIBUTE_CONTENT) { + LDIFAttributeContent c = (LDIFAttributeContent) content; + LDAPAttribute[] attrs = c.getAttributes(); + LDAPAttributeSet myAttrs = new LDAPAttributeSet(); + for (int i = 0; i < attrs.length; i++) + myAttrs.add(attrs[i]); + LDAPEntry entry = new LDAPEntry(dn, myAttrs); + try { + conn.add(entry); + } catch (LDAPException ee) { + errors.add("LDAPUtil:importLDIF: exception in adding entry " + dn + + ":" + ee.toString() + "\n"); + } + } else if (type == LDIFContent.MODIFICATION_CONTENT) { + LDIFModifyContent c = (LDIFModifyContent) content; + LDAPModification[] mods = c.getModifications(); + try { + conn.modify(dn, mods); + } catch (LDAPException ee) { + errors.add("LDAPUtil:importLDIF: exception in modifying entry " + dn + + ":" + ee.toString()); + } + } + } catch (Exception e) { + throw new IOException(e.toString()); + } + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/net/ISocketFactory.java b/base/util/src/com/netscape/cmsutil/net/ISocketFactory.java new file mode 100644 index 000000000..18f6cac88 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/net/ISocketFactory.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) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.net; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +import org.mozilla.jss.ssl.SSLCertificateApprovalCallback; +import org.mozilla.jss.ssl.SSLClientCertificateSelectionCallback; + +public interface ISocketFactory { + Socket makeSocket(String host, int port) + throws IOException, UnknownHostException; + + Socket makeSocket(String host, int port, int timeout) + throws IOException, UnknownHostException; + + Socket makeSocket(String host, int port, + SSLCertificateApprovalCallback certApprovalCallback, + SSLClientCertificateSelectionCallback clientCertCallback) + throws IOException, UnknownHostException; +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/BasicOCSPResponse.java b/base/util/src/com/netscape/cmsutil/ocsp/BasicOCSPResponse.java new file mode 100644 index 000000000..11ae7f152 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/BasicOCSPResponse.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 com.netscape.cmsutil.ocsp; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +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.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; +import org.mozilla.jss.pkix.cert.Certificate; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; + +/** + * RFC 2560: + * + * <pre> + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class BasicOCSPResponse implements Response { + private byte mData[] = null; + private ResponseData _rd = null; + private AlgorithmIdentifier _signAlg = null; + private BIT_STRING _signature = null; + private Certificate _certs[] = null; + + public BasicOCSPResponse(ResponseData rd, AlgorithmIdentifier signAlg, + BIT_STRING signature, Certificate certs[]) { + _rd = rd; + _signAlg = signAlg; + _signature = signature; + _certs = certs; + } + + public BasicOCSPResponse(OCTET_STRING os) { + this(os.toByteArray()); + } + + public BasicOCSPResponse(byte data[]) { + mData = data; + + // extract _rd, _signAlg, _signature and _certs + try { + BasicOCSPResponse resp = (BasicOCSPResponse) getTemplate().decode(new ByteArrayInputStream(data)); + _rd = resp.getResponseData(); + _signAlg = resp.getSignatureAlgorithm(); + _signature = resp.getSignature(); + _certs = resp.getCerts(); + } catch (Exception e) { + // exception in decoding byte data + } + } + + private static final Tag TAG = SEQUENCE.TAG; + + public Tag getTag() { + return TAG; + } + + public void encode(Tag t, OutputStream os) throws IOException { + if (mData != null) { + os.write(mData); + } else { + SEQUENCE seq = new SEQUENCE(); + seq.addElement(_rd); + seq.addElement(_signAlg); + seq.addElement(_signature); + if (_certs != null) { + SEQUENCE certsSeq = new SEQUENCE(); + for (Certificate c : _certs) { + certsSeq.addElement(c); + } + EXPLICIT certsExplicit = new EXPLICIT(new Tag(0), certsSeq); + seq.addElement(certsExplicit); + } + seq.encode(t, os); + } + } + + public void encode(OutputStream os) throws IOException { + encode(TAG, os); + } + + public OCTET_STRING getBytes() { + return null; + } + + public ResponseData getResponseData() { + return _rd; + } + + public AlgorithmIdentifier getSignatureAlgorithm() { + return _signAlg; + } + + public BIT_STRING getSignature() { + return _signature; + } + + public int getCertsCount() { + return (_certs != null) ? _certs.length : 0; + } + + public Certificate[] getCerts() { + return _certs; + } + + public Certificate getCertificateAt(int pos) { + return (_certs != null) ? _certs[pos] : null; + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(ResponseData.getTemplate()); + seqt.addElement(AlgorithmIdentifier.getTemplate()); + seqt.addElement(BIT_STRING.getTemplate()); + seqt.addOptionalElement(new EXPLICIT.Template( + new Tag(0), new SEQUENCE.OF_Template( + Certificate.getTemplate()))); + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, istream); + + ResponseData rd = (ResponseData) seq.elementAt(0); + AlgorithmIdentifier alg = (AlgorithmIdentifier) seq.elementAt(1); + BIT_STRING bs = (BIT_STRING) seq.elementAt(2); + Certificate[] certs = null; + if (seq.size() == 4) { + // optional certificates are present + EXPLICIT certSeqExplicit = (EXPLICIT) seq.elementAt(3); + SEQUENCE certSeq = (SEQUENCE) certSeqExplicit.getContent(); + if (certSeq != null) { + certs = new Certificate[certSeq.size()]; + for (int x = 0; x < certSeq.size(); x++) { + certs[x] = (Certificate) certSeq.elementAt(x); + } + } + } + + return new BasicOCSPResponse(rd, alg, bs, certs); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/CertID.java b/base/util/src/com/netscape/cmsutil/ocsp/CertID.java new file mode 100644 index 000000000..b6979c784 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/CertID.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 com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; + +/** + * RFC 2560: + * + * <pre> + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key + * serialNumber CertificateSerialNumber } + * </pre> + * + * @version $Revision$ $Date$ + */ + +public class CertID implements ASN1Value { + /////////////////////////////////////////////////////////////////////// + // Members and member access + /////////////////////////////////////////////////////////////////////// + private AlgorithmIdentifier hashAlgorithm; + private OCTET_STRING issuerNameHash; + private OCTET_STRING issuerKeyHash; + private INTEGER serialNumber; + private SEQUENCE sequence; + + public AlgorithmIdentifier getHashAlgorithm() { + return hashAlgorithm; + } + + public OCTET_STRING getIssuerNameHash() { + return issuerNameHash; + } + + public OCTET_STRING getIssuerKeyHash() { + return issuerKeyHash; + } + + public INTEGER getSerialNumber() { + return serialNumber; + } + + /////////////////////////////////////////////////////////////////////// + // Constructors + /////////////////////////////////////////////////////////////////////// + + public CertID(AlgorithmIdentifier hashAlgorithm, + OCTET_STRING issuerNameHash, OCTET_STRING issuerKeyHash, + INTEGER serialNumber) { + sequence = new SEQUENCE(); + + this.hashAlgorithm = hashAlgorithm; + sequence.addElement(hashAlgorithm); + + this.issuerNameHash = issuerNameHash; + sequence.addElement(issuerNameHash); + + this.issuerKeyHash = issuerKeyHash; + sequence.addElement(issuerKeyHash); + + this.serialNumber = serialNumber; + sequence.addElement(serialNumber); + } + + /////////////////////////////////////////////////////////////////////// + // encoding/decoding + /////////////////////////////////////////////////////////////////////// + + 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.encode(implicitTag, ostream); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding a <code>CertID</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(AlgorithmIdentifier.getTemplate()); + seqt.addElement(OCTET_STRING.getTemplate()); + seqt.addElement(OCTET_STRING.getTemplate()); + seqt.addElement(INTEGER.getTemplate()); + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, istream); + + return new CertID( + (AlgorithmIdentifier) seq.elementAt(0), + (OCTET_STRING) seq.elementAt(1), + (OCTET_STRING) seq.elementAt(2), + (INTEGER) seq.elementAt(3)); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/CertStatus.java b/base/util/src/com/netscape/cmsutil/ocsp/CertStatus.java new file mode 100644 index 000000000..a90eb215f --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/CertStatus.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 com.netscape.cmsutil.ocsp; + +import org.mozilla.jss.asn1.ASN1Value; + +/** + * RFC 2560: + * + * <pre> + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + * </pre> + * + * @version $Revision$ $Date$ + */ +public interface CertStatus extends ASN1Value { +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/GoodInfo.java b/base/util/src/com/netscape/cmsutil/ocsp/GoodInfo.java new file mode 100644 index 000000000..fa7387260 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/GoodInfo.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 com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.NULL; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; + +/** + * RFC 2560: + * + * <pre> + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class GoodInfo implements CertStatus { + private static final Tag TAG = SEQUENCE.TAG; + + public GoodInfo() { + } + + public Tag getTag() { + return Tag.get(0); + } + + public void encode(Tag t, OutputStream os) throws IOException { + NULL.getInstance().encode(getTag(), os); + } + + public void encode(OutputStream os) throws IOException { + encode(getTag(), os); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(new NULL.Template()); + + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + // SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, + // istream); + + return new GoodInfo(); + + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/KeyHashID.java b/base/util/src/com/netscape/cmsutil/ocsp/KeyHashID.java new file mode 100644 index 000000000..358fb0ebd --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/KeyHashID.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 com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; + +/** + * RFC 2560: + * + * <pre> + * ResponderID ::= CHOICE { + * byName [1] EXPLICIT Name, + * byKey [2] EXPLICIT KeyHash } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class KeyHashID implements ResponderID { + private OCTET_STRING _hash = null; + private static final Tag TAG = SEQUENCE.TAG; + + public KeyHashID(OCTET_STRING hash) { + _hash = hash; + } + + public Tag getTag() { + return Tag.get(2); + } + + public void encode(Tag tag, OutputStream os) throws IOException { + _hash.encode(os); + } + + public void encode(OutputStream os) throws IOException { + _hash.encode(os); + } + + public OCTET_STRING getHash() { + return _hash; + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + // seqt.addElement(new EXPLICIT.Template( + // new Tag (2), new OCTET_STRING.Template()) ); + seqt.addElement(new OCTET_STRING.Template()); + + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, + istream); + + OCTET_STRING o = (OCTET_STRING) seq.elementAt(0); + return new KeyHashID(o); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/NameID.java b/base/util/src/com/netscape/cmsutil/ocsp/NameID.java new file mode 100644 index 000000000..529ededbb --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/NameID.java @@ -0,0 +1,106 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; +import org.mozilla.jss.pkix.primitive.Name; + +/** + * RFC 2560: + * + * <pre> + * ResponderID ::= CHOICE { + * byName [1] EXPLICIT Name, + * byKey [2] EXPLICIT KeyHash } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class NameID implements ResponderID { + private Name _name = null; + private static final Tag TAG = SEQUENCE.TAG; + + public NameID(Name n) { + _name = n; + } + + public Tag getTag() { + return Tag.get(1); + } + + public void encode(Tag tag, OutputStream os) throws IOException { + _name.encode(os); + } + + public void encode(OutputStream os) throws IOException { + _name.encode(os); + } + + public Name getName() { + return _name; + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + // seqt.addElement(new EXPLICIT.Template( + // new Tag (1), new Name.Template()) ); + seqt.addElement(new Name.Template()); + + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, + istream); + + // EXPLICIT e_name = (EXPLICIT) seq.elementAt(0); + Name name = (Name) seq.elementAt(0); + return new NameID(name); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/OCSPRequest.java b/base/util/src/com/netscape/cmsutil/ocsp/OCSPRequest.java new file mode 100644 index 000000000..963bdc832 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/OCSPRequest.java @@ -0,0 +1,140 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.EXPLICIT; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; + +/** + * RFC 2560: + * + * <pre> + * OCSPRequest ::= SEQUENCE { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ + +public class OCSPRequest implements ASN1Value { + + /////////////////////////////////////////////////////////////////////// + // Members and member access + /////////////////////////////////////////////////////////////////////// + private TBSRequest tbsRequest; + private Signature optionalSignature; + private SEQUENCE sequence; + + /** + * Returns the <code>TBSRequest</code> field. + */ + public TBSRequest getTBSRequest() { + return tbsRequest; + } + + /** + * Returns the <code>Signature</code> field. + */ + public Signature getSignature() { + return optionalSignature; + } + + /* THIS code is probably broken. It does not properly encode the explicit element */ + + public OCSPRequest(TBSRequest tbsRequest, Signature optionalSignature) { + sequence = new SEQUENCE(); + + this.tbsRequest = tbsRequest; + sequence.addElement(tbsRequest); + + this.optionalSignature = optionalSignature; + if (optionalSignature != null) { + sequence.addElement(optionalSignature); + } + } + + /////////////////////////////////////////////////////////////////////// + // encoding/decoding + /////////////////////////////////////////////////////////////////////// + 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.encode(implicitTag, ostream); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding OCSPRequest. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(TBSRequest.getTemplate()); + seqt.addOptionalElement(new EXPLICIT.Template(new Tag(0), + new Signature.Template())); + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(istream); + Signature signature = null; + if (seq.elementAt(1) != null) { + signature = (Signature) ((EXPLICIT) seq.elementAt(1)).getContent(); + } + + return new OCSPRequest( + (TBSRequest) seq.elementAt(0), + signature); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/OCSPResponse.java b/base/util/src/com/netscape/cmsutil/ocsp/OCSPResponse.java new file mode 100644 index 000000000..6696cd9dc --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/OCSPResponse.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 com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.EXPLICIT; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; + +/** + * RFC 2560: + * + * <pre> + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class OCSPResponse implements ASN1Value { + /////////////////////////////////////////////////////////////////////// + // Members and member access + /////////////////////////////////////////////////////////////////////// + private OCSPResponseStatus responseStatus = null; + private ResponseBytes responseBytes = null; + private SEQUENCE sequence; + + public OCSPResponseStatus getResponseStatus() { + return responseStatus; + } + + public ResponseBytes getResponseBytes() { + return responseBytes; + } + + public OCSPResponse(OCSPResponseStatus responseStatus, + ResponseBytes responseBytes) { + sequence = new SEQUENCE(); + + this.responseStatus = responseStatus; + sequence.addElement(responseStatus); + + this.responseBytes = responseBytes; + sequence.addElement(new EXPLICIT(Tag.get(0), responseBytes)); + } + + /////////////////////////////////////////////////////////////////////// + // encoding/decoding + /////////////////////////////////////////////////////////////////////// + + 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.encode(implicitTag, ostream); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding an <code>OCSPResponse</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(OCSPResponseStatus.getTemplate()); + seqt.addOptionalElement( + new EXPLICIT.Template( + new Tag(0), new ResponseBytes.Template())); + + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, istream); + + OCSPResponseStatus rs = (OCSPResponseStatus) seq.elementAt(0); + ResponseBytes rb = null; + ASN1Value val = seq.elementAt(1); + if (val instanceof EXPLICIT) { + EXPLICIT exp = (EXPLICIT) val; + rb = (ResponseBytes) exp.getContent(); + } else { + rb = (ResponseBytes) val; + } + return new OCSPResponse(rs, rb); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/OCSPResponseStatus.java b/base/util/src/com/netscape/cmsutil/ocsp/OCSPResponseStatus.java new file mode 100644 index 000000000..38ca881c2 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/OCSPResponseStatus.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 com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.ENUMERATED; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.Tag; + +/** + * RFC 2560: + * + * <pre> + * OCSPResponseStatus ::= ENUMERATED { + * successful (0), --Response has valid confirmations + * malformedRequest (1), --Illegal confirmation request + * internalError (2), --Internal error in issuer + * tryLater (3), --Try again later + * --(4) is not used + * sigRequired (5), --Must sign the request + * unauthorized (6) --Request unauthorized + * } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class OCSPResponseStatus implements ASN1Value { + /////////////////////////////////////////////////////////////////////// + // Members and member access + /////////////////////////////////////////////////////////////////////// + public final static OCSPResponseStatus SUCCESSFUL = + new OCSPResponseStatus(0); + public final static OCSPResponseStatus MALFORMED_REQUEST = + new OCSPResponseStatus(1); + public final static OCSPResponseStatus INTERNAL_ERROR = + new OCSPResponseStatus(2); + public final static OCSPResponseStatus TRY_LATER = + new OCSPResponseStatus(3); + public final static OCSPResponseStatus SIG_REQUIRED = + new OCSPResponseStatus(5); + public final static OCSPResponseStatus UNAUTHORIZED = + new OCSPResponseStatus(6); + + private ENUMERATED responseStatus; + + public long getValue() { + return responseStatus.getValue(); + } + + public OCSPResponseStatus(long val) { + responseStatus = new ENUMERATED(val); + } + + /////////////////////////////////////////////////////////////////////// + // encoding/decoding + /////////////////////////////////////////////////////////////////////// + + private static final Tag TAG = ENUMERATED.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 { + responseStatus.encode(implicitTag, ostream); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding an <code>OCSPResponseStatus</code>. + */ + public static class Template implements ASN1Template { + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + ENUMERATED.Template enumt = new ENUMERATED.Template(); + ENUMERATED enum1 = (ENUMERATED) enumt.decode(implicitTag, istream); + + return new OCSPResponseStatus(enum1.getValue()); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/Request.java b/base/util/src/com/netscape/cmsutil/ocsp/Request.java new file mode 100644 index 000000000..85c97de22 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/Request.java @@ -0,0 +1,147 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.EXPLICIT; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; +import org.mozilla.jss.pkix.cert.Extension; + +/** + * RFC 2560: + * + * <pre> + * Request ::= SEQUENCE { + * reqCert CertID, + * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ + +public class Request implements ASN1Value { + /////////////////////////////////////////////////////////////////////// + // members and member access + /////////////////////////////////////////////////////////////////////// + private CertID reqCert = null; + private SEQUENCE singleRequestExtensions = null; + private SEQUENCE sequence = null; + + public CertID getCertID() { + return reqCert; + } + + public int getExtensionsCount() { + if (singleRequestExtensions == null) { + return 0; + } else { + return singleRequestExtensions.size(); + } + } + + public Extension getRequestExtensionAt(int index) { + if (singleRequestExtensions == null) { + throw new ArrayIndexOutOfBoundsException(); + } + return (Extension) singleRequestExtensions.elementAt(index); + } + + public Request(CertID reqCert, SEQUENCE singleRequestExtensions) { + sequence = new SEQUENCE(); + + this.reqCert = reqCert; + sequence.addElement(reqCert); + + if (singleRequestExtensions != null) { + this.singleRequestExtensions = singleRequestExtensions; + sequence.addElement(singleRequestExtensions); + } + } + + /////////////////////////////////////////////////////////////////////// + // encode / decode + /////////////////////////////////////////////////////////////////////// + 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.encode(implicitTag, ostream); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding Request. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(CertID.getTemplate()); + seqt.addOptionalElement(new EXPLICIT.Template(new Tag(0), + new SEQUENCE.OF_Template(new Extension.Template()))); + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, istream); + + EXPLICIT tag = (EXPLICIT) seq.elementAt(1); + + if (tag == null) { + return new Request( + (CertID) seq.elementAt(0), + (SEQUENCE) null); + } else { + return new Request( + (CertID) seq.elementAt(0), + (SEQUENCE) tag.getContent()); + } + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/ResponderID.java b/base/util/src/com/netscape/cmsutil/ocsp/ResponderID.java new file mode 100644 index 000000000..02e30de05 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/ResponderID.java @@ -0,0 +1,34 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import org.mozilla.jss.asn1.ASN1Value; + +/** + * RFC 2560: + * + * <pre> + * ResponderID ::= CHOICE { + * byName [1] EXPLICIT Name, + * byKey [2] EXPLICIT KeyHash } + * </pre> + * + * @version $Revision$ $Date$ + */ +public interface ResponderID extends ASN1Value { +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/Response.java b/base/util/src/com/netscape/cmsutil/ocsp/Response.java new file mode 100644 index 000000000..0d363e811 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/Response.java @@ -0,0 +1,34 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.OCTET_STRING; + +/** + * RFC 2560: + * + * <pre> + * response OCTET STRING + * </pre> + * + * @version $Revision$ $Date$ + */ +public interface Response extends ASN1Value { + public OCTET_STRING getBytes(); +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/ResponseBytes.java b/base/util/src/com/netscape/cmsutil/ocsp/ResponseBytes.java new file mode 100644 index 000000000..c5d461148 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/ResponseBytes.java @@ -0,0 +1,130 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; + +/** + * RFC 2560: + * + * <pre> + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class ResponseBytes implements ASN1Value { + /////////////////////////////////////////////////////////////////////// + // Members and member access + /////////////////////////////////////////////////////////////////////// + public final static OBJECT_IDENTIFIER OCSP = + new OBJECT_IDENTIFIER("1.3.6.1.5.5.7.48.1"); + public final static OBJECT_IDENTIFIER OCSP_BASIC = + new OBJECT_IDENTIFIER("1.3.6.1.5.5.7.48.1.1"); + + private OBJECT_IDENTIFIER responseType = null; + private OCTET_STRING response = null; + private SEQUENCE sequence; + + public OBJECT_IDENTIFIER getObjectIdentifier() { + return responseType; + } + + public OCTET_STRING getResponse() { + return response; + } + + public ResponseBytes(OBJECT_IDENTIFIER responseType, OCTET_STRING response) { + sequence = new SEQUENCE(); + + this.responseType = responseType; + sequence.addElement(responseType); + + this.response = response; + sequence.addElement(response); + } + + /////////////////////////////////////////////////////////////////////// + // encoding/decoding + /////////////////////////////////////////////////////////////////////// + + 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.encode(implicitTag, ostream); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(OBJECT_IDENTIFIER.getTemplate()); + seqt.addElement(OCTET_STRING.getTemplate()); + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, istream); + + return new ResponseBytes( + (OBJECT_IDENTIFIER) seq.elementAt(0), + (OCTET_STRING) seq.elementAt(1)); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/ResponseData.java b/base/util/src/com/netscape/cmsutil/ocsp/ResponseData.java new file mode 100644 index 000000000..1b28cf134 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/ResponseData.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 com.netscape.cmsutil.ocsp; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ANY; +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.EXPLICIT; +import org.mozilla.jss.asn1.GeneralizedTime; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; +import org.mozilla.jss.pkix.cert.Extension; + +/** + * RFC 2560: + * + * <pre> + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class ResponseData implements ASN1Value { + private static final INTEGER v1 = new INTEGER(0); + private INTEGER mVer; + private ResponderID mRID = null; + private GeneralizedTime mProduced = null; + private SingleResponse mSR[] = null; + private Extension mExts[] = null; + + private static final Tag TAG = SEQUENCE.TAG; + + public ResponseData(INTEGER ver, ResponderID rid, GeneralizedTime produced, + SingleResponse sr[], Extension exts[]) { + mVer = (ver != null) ? ver : v1; + mRID = rid; + mProduced = produced; + mSR = sr; + mExts = exts; + } + + public ResponseData(ResponderID rid, GeneralizedTime produced, + SingleResponse sr[]) { + this(v1, rid, produced, sr, null); + } + + public ResponseData(ResponderID rid, GeneralizedTime produced, + SingleResponse sr[], Extension exts[]) { + this(v1, rid, produced, sr, exts); + } + + public Tag getTag() { + return TAG; + } + + public void encode(OutputStream os) throws IOException { + encode(null, os); + } + + public void encode(Tag t, OutputStream os) throws IOException { + SEQUENCE seq = new SEQUENCE(); + + if (mVer != v1) { + seq.addElement(new EXPLICIT(Tag.get(0), new INTEGER(mVer))); + } + + seq.addElement(new EXPLICIT(mRID.getTag(), mRID)); + seq.addElement(mProduced); + SEQUENCE responses = new SEQUENCE(); + for (int i = 0; i < mSR.length; i++) { + responses.addElement(mSR[i]); + } + seq.addElement(responses); + if (mExts != null) { + SEQUENCE exts = new SEQUENCE(); + for (int i = 0; i < mExts.length; i++) { + exts.addElement(mExts[i]); + } + seq.addElement(new EXPLICIT(Tag.get(1), exts)); + } + if (t == null) { + seq.encode(os); + } else { + seq.encode(t, os); + } + } + + public ResponderID getResponderID() { + return mRID; + } + + public GeneralizedTime getProducedAt() { + return mProduced; + } + + public int getResponseCount() { + return (mSR != null) ? mSR.length : 0; + } + + public SingleResponse getResponseAt(int pos) { + return (mSR != null) ? mSR[pos] : null; + } + + public int getResponseExtensionCount() { + return (mExts != null) ? mExts.length : 0; + } + + public Extension getResponseExtensionAt(int pos) { + return (mExts != null) ? mExts[pos] : null; + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addOptionalElement(new EXPLICIT.Template( + new Tag(0), new INTEGER.Template())); + seqt.addElement(new ANY.Template()); + seqt.addElement(new GeneralizedTime.Template()); + seqt.addElement(new SEQUENCE.OF_Template( + SingleResponse.getTemplate())); + seqt.addOptionalElement(new EXPLICIT.Template( + new Tag(1), new SEQUENCE.OF_Template( + Extension.getTemplate()))); + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, + istream); + + INTEGER ver = v1; + EXPLICIT e_ver = (EXPLICIT) seq.elementAt(0); + if (e_ver != null && e_ver.getTag().getNum() == 0) { + ver = (INTEGER) e_ver.getContent(); + } + ResponderID rid = null; + ANY e_rid = (ANY) seq.elementAt(1); + if (e_rid.getTag().getNum() == 1) { + // name id + rid = (NameID) + NameID.getTemplate().decode(e_rid.getTag(), + new ByteArrayInputStream(e_rid.getEncoded())); + } else if (e_rid.getTag().getNum() == 2) { + // key hash id + rid = (KeyHashID) + KeyHashID.getTemplate().decode(e_rid.getTag(), + new ByteArrayInputStream(e_rid.getEncoded())); + } + GeneralizedTime producedAt = (GeneralizedTime) seq.elementAt(2); + SEQUENCE responses = (SEQUENCE) seq.elementAt(3); + SingleResponse sr[] = null; + if ((responses != null) && (responses.size() > 0)) { + sr = new SingleResponse[responses.size()]; + for (int i = 0; i < responses.size(); i++) { + sr[i] = (SingleResponse) responses.elementAt(i); + } + } + + //decode response extension sequence + EXPLICIT extns_exp = (EXPLICIT) seq.elementAt(4); + SEQUENCE extns_seq; + Extension[] extns_array = null; + if (extns_exp != null) { + extns_seq = (SEQUENCE) extns_exp.getContent(); + extns_array = new Extension[extns_seq.size()]; + for (int x = 0; x < extns_array.length; x++) { + extns_array[x] = (Extension) extns_seq.elementAt(x); + } + } + + return new ResponseData(ver, rid, producedAt, sr, extns_array); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/RevokedInfo.java b/base/util/src/com/netscape/cmsutil/ocsp/RevokedInfo.java new file mode 100644 index 000000000..9b0b2d186 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/RevokedInfo.java @@ -0,0 +1,113 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.EXPLICIT; +import org.mozilla.jss.asn1.GeneralizedTime; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; + +/** + * RFC 2560: + * + * <pre> + * RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class RevokedInfo implements CertStatus { + private static final Tag TAG = SEQUENCE.TAG; + + private GeneralizedTime mRevokedAt; + + public RevokedInfo(GeneralizedTime revokedAt) { + mRevokedAt = revokedAt; + } + + public Tag getTag() { + return Tag.get(1); + } + + public void encode(Tag t, OutputStream os) throws IOException { + SEQUENCE seq = new SEQUENCE(); + seq.addElement(mRevokedAt); + seq.encode(t, os); + } + + public void encode(OutputStream os) throws IOException { + encode(getTag(), os); + } + + public GeneralizedTime getRevocationTime() { + return mRevokedAt; + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(new GeneralizedTime.Template()); + seqt.addOptionalElement( + new EXPLICIT.Template(new Tag(0), + new INTEGER.Template())); + + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, + istream); + + GeneralizedTime revokedAt = (GeneralizedTime) + seq.elementAt(0); + return new RevokedInfo(revokedAt); + + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/Signature.java b/base/util/src/com/netscape/cmsutil/ocsp/Signature.java new file mode 100644 index 000000000..b9b192aee --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/Signature.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 com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +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; +import org.mozilla.jss.pkix.cert.Certificate; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; + +/** + * RFC 2560: + * + * <pre> + * Signature ::= SEQUENCE { + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ + +public class Signature implements ASN1Value { + /////////////////////////////////////////////////////////////////////// + // Members and member access + /////////////////////////////////////////////////////////////////////// + private AlgorithmIdentifier signatureAlgorithm; + private BIT_STRING signature; + private SEQUENCE certs; + private SEQUENCE sequence; + + public AlgorithmIdentifier getSignatureAlgorithm() { + return signatureAlgorithm; + } + + public BIT_STRING getSignature() { + return signature; + } + + public int getCertificateCount() { + if (certs == null) { + return 0; + } else { + return certs.size(); + } + } + + public Certificate getCertificateAt(int index) { + if (certs == null) { + throw new ArrayIndexOutOfBoundsException(); + } + return (Certificate) certs.elementAt(index); + } + + public Signature(AlgorithmIdentifier signatureAlgorithm, + BIT_STRING signature, SEQUENCE certs) { + sequence = new SEQUENCE(); + + this.signatureAlgorithm = signatureAlgorithm; + sequence.addElement(signatureAlgorithm); + + this.signature = signature; + sequence.addElement(signature); + + this.certs = certs; + sequence.addElement(certs); + } + + /////////////////////////////////////////////////////////////////////// + // encode / decode + /////////////////////////////////////////////////////////////////////// + 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.encode(implicitTag, ostream); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding Request. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(AlgorithmIdentifier.getTemplate()); + seqt.addElement(BIT_STRING.getTemplate()); + seqt.addOptionalElement( + new EXPLICIT.Template( + new Tag(0), + new SEQUENCE.OF_Template(new Certificate.Template()) + ) + ); + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, istream); + SEQUENCE certs = null; + if (seq.elementAt(2) != null) { + certs = (SEQUENCE) ((EXPLICIT) seq.elementAt(2)).getContent(); + } + + return new Signature( + (AlgorithmIdentifier) seq.elementAt(0), + (BIT_STRING) seq.elementAt(1), + certs); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/SingleResponse.java b/base/util/src/com/netscape/cmsutil/ocsp/SingleResponse.java new file mode 100644 index 000000000..ab54e5019 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/SingleResponse.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 com.netscape.cmsutil.ocsp; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ANY; +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.EXPLICIT; +import org.mozilla.jss.asn1.GeneralizedTime; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; +import org.mozilla.jss.pkix.cert.Extension; + +/** + * RFC 2560: + * + * <pre> + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ +public class SingleResponse implements ASN1Value { + private CertID mCID = null; + private CertStatus mStatus = null; + private GeneralizedTime mThisUpdate = null; + private GeneralizedTime mNextUpdate = null; + + private static final Tag TAG = SEQUENCE.TAG; + + public SingleResponse(CertID cid, CertStatus s, + GeneralizedTime thisUpdate, GeneralizedTime nextUpdate) { + mCID = cid; + mStatus = s; + mThisUpdate = thisUpdate; + mNextUpdate = nextUpdate; + } + + public CertID getCertID() { + return mCID; + } + + public Tag getTag() { + return null; + } + + public void encode(Tag t, OutputStream os) throws IOException { + SEQUENCE seq = new SEQUENCE(); + seq.addElement(mCID); + seq.addElement(mStatus); + seq.addElement(mThisUpdate); + if (mNextUpdate != null) { + seq.addElement(new EXPLICIT(Tag.get(0), mNextUpdate)); + } + if (t == null) { + seq.encode(os); + } else { + seq.encode(t, os); + } + } + + public void encode(OutputStream os) throws IOException { + encode(null, os); + } + + public CertStatus getCertStatus() { + return mStatus; + } + + public GeneralizedTime getThisUpdate() { + return mThisUpdate; + } + + public GeneralizedTime getNextUpdate() { + return mNextUpdate; + } + + public int getExtensionCount() { + return 0; + } + + public Extension getExtensionAt(int pos) { + return null; + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement(new CertID.Template()); + seqt.addElement(new ANY.Template()); + seqt.addElement(new GeneralizedTime.Template()); + seqt.addOptionalElement(new EXPLICIT.Template( + new Tag(0), new GeneralizedTime.Template())); + seqt.addOptionalElement(new EXPLICIT.Template(new Tag(1), + new SEQUENCE.OF_Template(new Extension.Template()))); + + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, + istream); + + CertID cid = (CertID) seq.elementAt(0); + CertStatus status = null; + ANY e_status = (ANY) seq.elementAt(1); + if (e_status.getTag().getNum() == 0) { + status = (GoodInfo) + GoodInfo.getTemplate().decode( + e_status.getTag(), + new ByteArrayInputStream(e_status.getEncoded())); + // good + } else if (e_status.getTag().getNum() == 1) { + // revoked + status = (RevokedInfo) + RevokedInfo.getTemplate().decode( + e_status.getTag(), + new ByteArrayInputStream(e_status.getEncoded())); + } else if (e_status.getTag().getNum() == 2) { + // unknown + status = (UnknownInfo) + UnknownInfo.getTemplate().decode( + e_status.getTag(), + new ByteArrayInputStream(e_status.getEncoded())); + } + GeneralizedTime thisUpdate = (GeneralizedTime) + seq.elementAt(2); + GeneralizedTime nextUpdate = null; + + return new SingleResponse(cid, status, thisUpdate, + nextUpdate); + + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/TBSRequest.java b/base/util/src/com/netscape/cmsutil/ocsp/TBSRequest.java new file mode 100644 index 000000000..b7f706edb --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/TBSRequest.java @@ -0,0 +1,210 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ANY; +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.EXPLICIT; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; +import org.mozilla.jss.pkix.cert.Extension; + +/** + * RFC 2560: + * + * <pre> + * TBSRequest ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * requestorName [1] EXPLICIT GeneralName OPTIONAL, + * requestList SEQUENCE OF Request, + * requestExtensions [2] EXPLICIT Extensions OPTIONAL } + * </pre> + * + * @version $Revision$ $Date$ + */ + +public class TBSRequest implements ASN1Value { + /////////////////////////////////////////////////////////////////////// + // members and member access + /////////////////////////////////////////////////////////////////////// + private static final INTEGER v1 = new INTEGER(0); + private INTEGER version; + private ANY requestorName; + private SEQUENCE requestList; + private SEQUENCE requestExtensions; + + public INTEGER getVersion() { + return version; + } + + public ANY getRequestorName() { + return requestorName; + } + + public int getRequestCount() { + if (requestList == null) { + return 0; + } else { + return requestList.size(); + } + } + + public Request getRequestAt(int index) { + return (Request) requestList.elementAt(index); + } + + public int getExtensionsCount() { + if (requestExtensions == null) { + return 0; + } else { + return requestExtensions.size(); + } + } + + public Extension getRequestExtensionAt(int index) { + return (Extension) requestExtensions.elementAt(index); + } + + /////////////////////////////////////////////////////////////////////// + // constructors + /////////////////////////////////////////////////////////////////////// + + public TBSRequest(INTEGER version, ANY requestorName, + SEQUENCE requestList, SEQUENCE requestExtensions) { + this.version = (version != null) ? version : v1; + this.requestorName = requestorName; + this.requestList = requestList; + this.requestExtensions = requestExtensions; + } + + /////////////////////////////////////////////////////////////////////// + // encode / decode + /////////////////////////////////////////////////////////////////////// + public 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(); + + if (version != v1) { + seq.addElement(new EXPLICIT(Tag.get(0), version)); + } + + if (requestorName != null) { + seq.addElement(new EXPLICIT(Tag.get(1), requestorName)); + } + + seq.addElement(requestList); + + if (requestExtensions != null) { + seq.addElement(new EXPLICIT(Tag.get(2), requestExtensions)); + } + if (implicitTag == null) { + seq.encode(ostream); + } else { + seq.encode(implicitTag, ostream); + } + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding TBSRequest. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + seqt = new SEQUENCE.Template(); + seqt.addElement( + new EXPLICIT.Template( + new Tag(0), new INTEGER.Template()), + new EXPLICIT(new Tag(0), new INTEGER(0)) + ); + seqt.addOptionalElement( + new EXPLICIT.Template( + new Tag(1), new ANY.Template())); + seqt.addElement(new SEQUENCE.OF_Template(new Request.Template())); + seqt.addOptionalElement(new EXPLICIT.Template(new Tag(2), + new SEQUENCE.OF_Template(new Extension.Template()))); + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, istream); + + INTEGER v = v1; //assume default version + EXPLICIT e_ver = (EXPLICIT) seq.elementAt(0); + if (e_ver != null) { + v = (INTEGER) e_ver.getContent(); + } + + ANY requestorname = null; + EXPLICIT e_requestorName = (EXPLICIT) seq.elementAt(1); + if (e_requestorName != null) { + requestorname = (ANY) e_requestorName.getContent(); + } + + //request sequence (element 2) done below + + EXPLICIT exts = (EXPLICIT) seq.elementAt(3); + SEQUENCE exts_seq; + if (exts != null) { + exts_seq = (SEQUENCE) exts.getContent(); + } else { + exts_seq = null; + } + + return new TBSRequest( + v, + requestorname, + (SEQUENCE) seq.elementAt(2), + exts_seq); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/ocsp/UnknownInfo.java b/base/util/src/com/netscape/cmsutil/ocsp/UnknownInfo.java new file mode 100644 index 000000000..1fe4ea743 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/UnknownInfo.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 com.netscape.cmsutil.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.mozilla.jss.asn1.ASN1Template; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.NULL; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.Tag; + +/** + * RFC 2560: + * + * <pre> + * UnknownInfo ::= NULL -- this can be replaced with an enumeration + * </pre> + * + * @version $Revision$ $Date$ + */ +public class UnknownInfo implements CertStatus { + private static final Tag TAG = SEQUENCE.TAG; + + public UnknownInfo() { + } + + public Tag getTag() { + return Tag.get(2); + } + + public void encode(Tag t, OutputStream os) throws IOException { + NULL.getInstance().encode(getTag(), os); + } + + public void encode(OutputStream os) throws IOException { + encode(getTag(), os); + } + + private static final Template templateInstance = new Template(); + + public static Template getTemplate() { + return templateInstance; + } + + /** + * A Template for decoding <code>ResponseBytes</code>. + */ + public static class Template implements ASN1Template { + + private SEQUENCE.Template seqt; + + public Template() { + // seqt = new SEQUENCE.Template(); + // seqt.addElement(new NULL.Template() ); + + } + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws InvalidBERException, IOException { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws InvalidBERException, IOException { + // SEQUENCE seq = (SEQUENCE) seqt.decode(implicitTag, + // istream); + + return new UnknownInfo(); + + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/password/IPasswordReader.java b/base/util/src/com/netscape/cmsutil/password/IPasswordReader.java new file mode 100644 index 000000000..759e9e777 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/password/IPasswordReader.java @@ -0,0 +1,29 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.password; + +import java.io.IOException; +import java.util.Enumeration; + +public interface IPasswordReader { + public void init(String pwdPath) throws IOException; + + public String getPassword(String tag); + + public Enumeration<String> getTags(); +} diff --git a/base/util/src/com/netscape/cmsutil/password/IPasswordStore.java b/base/util/src/com/netscape/cmsutil/password/IPasswordStore.java new file mode 100644 index 000000000..49b2610fa --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/password/IPasswordStore.java @@ -0,0 +1,34 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.password; + +import java.io.IOException; +import java.util.Enumeration; + +public interface IPasswordStore { + public void init(String pwdPath) throws IOException; + + public String getPassword(String tag); + + public Enumeration<String> getTags(); + + public Object putPassword(String tag, String password); + + public void commit() + throws IOException, ClassCastException, NullPointerException; +} diff --git a/base/util/src/com/netscape/cmsutil/password/IPasswordWriter.java b/base/util/src/com/netscape/cmsutil/password/IPasswordWriter.java new file mode 100644 index 000000000..c9f9691e7 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/password/IPasswordWriter.java @@ -0,0 +1,30 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.password; + +import java.io.IOException; + +public interface IPasswordWriter { + public void init(String pwdPath) + throws IOException;; + + public Object putPassword(String tag, String password); + + public void commit() + throws IOException, ClassCastException, NullPointerException; +} diff --git a/base/util/src/com/netscape/cmsutil/password/PlainPasswordFile.java b/base/util/src/com/netscape/cmsutil/password/PlainPasswordFile.java new file mode 100644 index 000000000..eb43607f0 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/password/PlainPasswordFile.java @@ -0,0 +1,70 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.password; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; + +public class PlainPasswordFile implements IPasswordStore { + private String mPwdPath = ""; + private Properties mPwdStore; + private static final String PASSWORD_WRITER_HEADER = ""; + + public PlainPasswordFile() { + } + + public void init(String pwdPath) + throws IOException { + mPwdStore = new Properties(); + // initialize mPwdStore + mPwdPath = pwdPath; + + FileInputStream file = new FileInputStream(mPwdPath); + mPwdStore.load(file); + file.close(); + } + + public String getPassword(String tag) { + return (String) mPwdStore.getProperty(tag); + } + + // return an array of String-based tag + public Enumeration<String> getTags() { + Enumeration<?> e = mPwdStore.propertyNames(); + Vector<String> v = new Vector<String>(); + while (e.hasMoreElements()) { + v.add((String) e.nextElement()); + } + return v.elements(); + } + + public Object putPassword(String tag, String password) { + return mPwdStore.setProperty(tag, password); + } + + public void commit() + throws IOException, ClassCastException, NullPointerException { + FileOutputStream file = new FileOutputStream(mPwdPath); + mPwdStore.store(file, PASSWORD_WRITER_HEADER); + file.close(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/password/PlainPasswordReader.java b/base/util/src/com/netscape/cmsutil/password/PlainPasswordReader.java new file mode 100644 index 000000000..68724a9f6 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/password/PlainPasswordReader.java @@ -0,0 +1,58 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.password; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; + +public class PlainPasswordReader implements IPasswordReader { + private String mPwdPath = ""; + private Properties mPwdStore; + + public PlainPasswordReader() { + } + + public void init(String pwdPath) + throws IOException { + mPwdStore = new Properties(); + // initialize mPwdStore + mPwdPath = pwdPath; + mPwdStore = new Properties(); + + FileInputStream file = new FileInputStream(mPwdPath); + mPwdStore.load(file); + file.close(); + } + + public String getPassword(String tag) { + return (String) mPwdStore.getProperty(tag); + } + + // return an array of String-based tag + public Enumeration<String> getTags() { + Enumeration<?> e = mPwdStore.propertyNames(); + Vector<String> v = new Vector<String>(); + while (e.hasMoreElements()) { + v.add((String) e.nextElement()); + } + return v.elements(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/password/PlainPasswordWriter.java b/base/util/src/com/netscape/cmsutil/password/PlainPasswordWriter.java new file mode 100644 index 000000000..3ceac4bd6 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/password/PlainPasswordWriter.java @@ -0,0 +1,56 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.password; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +public class PlainPasswordWriter implements IPasswordWriter { + private static final String PASSWORD_WRITER_HEADER = ""; + private String mPwdPath = ""; + private Properties mPwdStore; + + public PlainPasswordWriter() { + } + + public void init(String pwdPath) + throws IOException { + mPwdStore = new Properties(); + // initialize mPwdStore + mPwdPath = pwdPath; + mPwdStore = new Properties(); + + FileInputStream file = new FileInputStream(mPwdPath); + mPwdStore.load(file); + file.close(); + } + + public Object putPassword(String tag, String password) { + return mPwdStore.setProperty(tag, password); + } + + public void commit() + throws IOException, ClassCastException, NullPointerException { + FileOutputStream file = new FileOutputStream(mPwdPath); + mPwdStore.store(file, PASSWORD_WRITER_HEADER); + file.close(); + } + +} diff --git a/base/util/src/com/netscape/cmsutil/radius/AccessAccept.java b/base/util/src/com/netscape/cmsutil/radius/AccessAccept.java new file mode 100644 index 000000000..4824c885f --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/AccessAccept.java @@ -0,0 +1,27 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class AccessAccept extends ServerPacket { + public AccessAccept(byte data[]) throws IOException { + super(data); + } + +} diff --git a/base/util/src/com/netscape/cmsutil/radius/AccessChallenge.java b/base/util/src/com/netscape/cmsutil/radius/AccessChallenge.java new file mode 100644 index 000000000..c06f809b1 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/AccessChallenge.java @@ -0,0 +1,27 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class AccessChallenge extends ServerPacket { + public AccessChallenge(byte data[]) throws IOException { + super(data); + } + +} diff --git a/base/util/src/com/netscape/cmsutil/radius/AccessReject.java b/base/util/src/com/netscape/cmsutil/radius/AccessReject.java new file mode 100644 index 000000000..5f32ef349 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/AccessReject.java @@ -0,0 +1,27 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class AccessReject extends ServerPacket { + public AccessReject(byte data[]) throws IOException { + super(data); + } + +} diff --git a/base/util/src/com/netscape/cmsutil/radius/AccessRequest.java b/base/util/src/com/netscape/cmsutil/radius/AccessRequest.java new file mode 100644 index 000000000..7856b0cc8 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/AccessRequest.java @@ -0,0 +1,25 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +public class AccessRequest extends NASPacket { + public AccessRequest(short id, Authenticator auth) { + super(ACCESS_REQUEST, id, auth); + } + +} diff --git a/base/util/src/com/netscape/cmsutil/radius/Attribute.java b/base/util/src/com/netscape/cmsutil/radius/Attribute.java new file mode 100644 index 000000000..5e79816e4 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/Attribute.java @@ -0,0 +1,97 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public abstract class Attribute { + public static final int USER_NAME = 1; + public static final int USER_PASSWORD = 2; + public static final int CHAP_PASSWORD = 3; + public static final int NAS_IP_ADDRESS = 4; + public static final int NAS_PORT = 5; + public static final int SERVICE_TYPE = 6; + public static final int FRAMED_PROTOCOL = 7; + public static final int FRAMED_IP_ADDRESS = 8; + public static final int FRAMED_IP_NETMASK = 9; + public static final int FRAMED_ROUTING = 10; + public static final int FILTER_ID = 11; + public static final int FRAMED_MTU = 12; + public static final int FRAMED_COMPRESSION = 13; + public static final int LOGIN_IP_HOST = 14; + public static final int LOGIN_SERVICE = 15; + public static final int LOGIN_TCP_PORT = 16; + // 17 HAS NOT BEEN ASSIGNED + public static final int REPLY_MESSAGE = 18; + public static final int CALLBACK_NUMBER = 19; + public static final int CALLBACK_ID = 20; + // 21 HAS NOT BEEN ASSIGNED + public static final int FRAMED_ROUTE = 22; + public static final int FRAMED_IPX_NETWORK = 23; + public static final int STATE = 24; + public static final int NAS_CLASS = 25; + public static final int VENDOR_SPECIFIC = 26; + public static final int SESSION_TIMEOUT = 27; + public static final int IDLE_TIMEOUT = 28; + public static final int TERMINATION_ACTION = 29; + public static final int CALLER_STATION_ID = 30; + public static final int CALLING_STATION_ID = 31; + public static final int NAS_IDENTIFIER = 32; + public static final int PROXY_STATE = 33; + public static final int LOGIN_LAT_SERVICE = 34; + public static final int LOGIN_LAT_NODE = 35; + public static final int LOGIN_LAT_GROUP = 36; + public static final int FRAMED_APPLETALK_LINK = 37; + public static final int FRAMED_APPLETALK_NETWORK = 38; + public static final int FRAMED_APPLETALK_ZONE = 39; + // 40-59 HAS NOT BEEN ASSIGNED + public static final int CHAP_CHALLENGE = 60; + public static final int NAS_PORT_TYPE = 61; + public static final int PORT_LIMIT = 62; + public static final int LOGIN_LAT_PORT = 63; + + protected int _t = 0; + + public Attribute() { + } + + public Attribute(int t) { + _t = t; + } + + public int getType() { + return _t; + } + + public abstract byte[] getValue() + throws IOException; + + public byte[] getData() + throws IOException { + ByteArrayOutputStream attrOS = new ByteArrayOutputStream(); + + attrOS.write(_t); // type + byte value[] = getValue(); + + attrOS.write(value.length + 2); // length + attrOS.write(value); + + return attrOS.toByteArray(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/AttributeFactory.java b/base/util/src/com/netscape/cmsutil/radius/AttributeFactory.java new file mode 100644 index 000000000..021c06720 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/AttributeFactory.java @@ -0,0 +1,154 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class AttributeFactory { + public static Attribute createAttribute(byte data[]) + throws IOException { + switch (data[0] & 0xFF) { + case Attribute.USER_NAME: // 1 + return new UserNameAttribute(data); + + case Attribute.USER_PASSWORD: // 2 + return new UserPasswordAttribute(data); + + case Attribute.NAS_IP_ADDRESS: // 4 + return new NASIPAddressAttribute(data); + + case Attribute.NAS_PORT: // 5 + return new NASPortAttribute(data); + + case Attribute.CHAP_PASSWORD: // 3 + return new CHAPPasswordAttribute(data); + + case Attribute.SERVICE_TYPE: // 6 + return new ServiceTypeAttribute(data); + + case Attribute.FRAMED_PROTOCOL: // 7 + return new FramedProtocolAttribute(data); + + case Attribute.FRAMED_IP_ADDRESS: // 8 + return new FramedIPAddressAttribute(data); + + case Attribute.FRAMED_IP_NETMASK: // 9 + return new FramedIPNetmaskAttribute(data); + + case Attribute.FRAMED_ROUTING: // 10 + return new FramedRoutingAttribute(data); + + case Attribute.FILTER_ID: // 11 + return new FilterIdAttribute(data); + + case Attribute.FRAMED_MTU: // 12 + return new FramedMTUAttribute(data); + + case Attribute.FRAMED_COMPRESSION: // 13 + return new FramedCompressionAttribute(data); + + case Attribute.LOGIN_IP_HOST: // 14 + return new LoginIPHostAttribute(data); + + case Attribute.LOGIN_SERVICE: // 15 + return new LoginServiceAttribute(data); + + case Attribute.LOGIN_TCP_PORT: // 16 + return new LoginTCPPortAttribute(data); + + case Attribute.REPLY_MESSAGE: // 18 + return new ReplyMessageAttribute(data); + + case Attribute.CALLBACK_NUMBER: // 19 + return new CallbackNumberAttribute(data); + + case Attribute.CALLBACK_ID: // 20 + return new CallbackIdAttribute(data); + + case Attribute.FRAMED_ROUTE: // 22 + return new FramedRouteAttribute(data); + + case Attribute.FRAMED_IPX_NETWORK: // 23 + return new FramedIPXNetworkAttribute(data); + + case Attribute.STATE: // 24 + return new StateAttribute(data); + + case Attribute.NAS_CLASS: // 25 + return new NASClassAttribute(data); + + case Attribute.VENDOR_SPECIFIC: // 26 + return new VendorSpecificAttribute(data); + + case Attribute.SESSION_TIMEOUT: // 27 + return new SessionTimeoutAttribute(data); + + case Attribute.IDLE_TIMEOUT: // 28 + return new IdleTimeoutAttribute(data); + + case Attribute.TERMINATION_ACTION: // 29 + return new TerminationActionAttribute(data); + + case Attribute.CALLER_STATION_ID: // 30 + return new CallerStationIdAttribute(data); + + case Attribute.CALLING_STATION_ID: // 31 + return new CallingStationIdAttribute(data); + + case Attribute.NAS_IDENTIFIER: // 32 + return new NASIdentifierAttribute(data); + + case Attribute.PROXY_STATE: // 33 + return new ProxyStateAttribute(data); + + case Attribute.LOGIN_LAT_SERVICE: // 34 + return new LoginLATServiceAttribute(data); + + case Attribute.LOGIN_LAT_NODE: // 35 + return new LoginLATNodeAttribute(data); + + case Attribute.LOGIN_LAT_GROUP: // 36 + return new LoginLATGroupAttribute(data); + + case Attribute.FRAMED_APPLETALK_LINK: // 37 + return new FramedAppleTalkLinkAttribute(data); + + case Attribute.FRAMED_APPLETALK_NETWORK: // 38 + return new FramedAppleTalkNetworkAttribute(data); + + case Attribute.FRAMED_APPLETALK_ZONE: // 39 + return new FramedAppleTalkZoneAttribute(data); + + case Attribute.CHAP_CHALLENGE: // 60 + return new CHAPChallengeAttribute(data); + + case Attribute.NAS_PORT_TYPE: // 61 + return new NASPortTypeAttribute(data); + + case Attribute.PORT_LIMIT: // 62 + return new PortLimitAttribute(data); + + case Attribute.LOGIN_LAT_PORT: // 63 + return new LoginLATPortAttribute(data); + + default: + return new GenericAttribute(data); + // throw new IOException("Unknown attribute " + (data[0] & 0xFF)); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/AttributeSet.java b/base/util/src/com/netscape/cmsutil/radius/AttributeSet.java new file mode 100644 index 000000000..d6974d371 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/AttributeSet.java @@ -0,0 +1,56 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.util.Enumeration; +import java.util.Vector; + +public class AttributeSet { + private Vector<Attribute> _attrs = new Vector<Attribute>(); + + public AttributeSet() { + } + + public void addAttribute(Attribute attr) { + _attrs.addElement(attr); + } + + public int size() { + return _attrs.size(); + } + + public Enumeration<Attribute> getAttributes() { + return _attrs.elements(); + } + + public Attribute getAttributeByType(int type) { + int l = _attrs.size(); + + for (int i = 0; i < l; i++) { + Attribute attr = getAttributeAt(i); + + if (attr.getType() == type) + return attr; + } + return null; + } + + public Attribute getAttributeAt(int pos) { + return _attrs.elementAt(pos); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/Authenticator.java b/base/util/src/com/netscape/cmsutil/radius/Authenticator.java new file mode 100644 index 000000000..008af489a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/Authenticator.java @@ -0,0 +1,24 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public abstract class Authenticator { + public abstract byte[] getData() throws IOException; +} diff --git a/base/util/src/com/netscape/cmsutil/radius/CHAPChallengeAttribute.java b/base/util/src/com/netscape/cmsutil/radius/CHAPChallengeAttribute.java new file mode 100644 index 000000000..cd715a031 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/CHAPChallengeAttribute.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) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class CHAPChallengeAttribute extends Attribute { + private String _str = null; + + public CHAPChallengeAttribute(byte value[]) { + super(); + _t = CHAP_CHALLENGE; + _str = new String(value, 2, value.length - 2); + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/CHAPPasswordAttribute.java b/base/util/src/com/netscape/cmsutil/radius/CHAPPasswordAttribute.java new file mode 100644 index 000000000..3f0ef1793 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/CHAPPasswordAttribute.java @@ -0,0 +1,55 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class CHAPPasswordAttribute extends Attribute { + private byte _value[] = null; + private int _ident = 0; + private String _str = null; + + public CHAPPasswordAttribute(String s) { + _str = s; + } + + public CHAPPasswordAttribute(byte value[]) { + super(); + _t = CHAP_PASSWORD; + _ident = value[2]; + _str = new String(value, 2, 16); + _value = value; + } + + public int getIdent() { + return _ident; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + byte val[] = new byte[1 + _str.length()]; + byte s[] = _str.getBytes(); + + val[0] = (byte) _ident; + System.arraycopy(s, 0, val, 1, s.length); + return val; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/CallbackIdAttribute.java b/base/util/src/com/netscape/cmsutil/radius/CallbackIdAttribute.java new file mode 100644 index 000000000..5fd806003 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/CallbackIdAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class CallbackIdAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public CallbackIdAttribute(byte value[]) { + super(); + _t = CALLBACK_ID; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/CallbackNumberAttribute.java b/base/util/src/com/netscape/cmsutil/radius/CallbackNumberAttribute.java new file mode 100644 index 000000000..d6e45cecd --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/CallbackNumberAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class CallbackNumberAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public CallbackNumberAttribute(byte value[]) { + super(); + _t = CALLBACK_NUMBER; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/CallerStationIdAttribute.java b/base/util/src/com/netscape/cmsutil/radius/CallerStationIdAttribute.java new file mode 100644 index 000000000..3b5eec804 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/CallerStationIdAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class CallerStationIdAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public CallerStationIdAttribute(byte value[]) { + super(); + _t = CALLER_STATION_ID; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/CallingStationIdAttribute.java b/base/util/src/com/netscape/cmsutil/radius/CallingStationIdAttribute.java new file mode 100644 index 000000000..9a57f8089 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/CallingStationIdAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class CallingStationIdAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public CallingStationIdAttribute(byte value[]) { + super(); + _t = CALLING_STATION_ID; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/ChallengeException.java b/base/util/src/com/netscape/cmsutil/radius/ChallengeException.java new file mode 100644 index 000000000..972f7f084 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/ChallengeException.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 com.netscape.cmsutil.radius; + +public class ChallengeException extends Exception { + /** + * + */ + private static final long serialVersionUID = -3496050461777520369L; + private AccessChallenge _res = null; + + public ChallengeException(AccessChallenge res) { + _res = res; + } + + public AttributeSet getAttributeSet() { + return _res.getAttributeSet(); + } + + public String getState() { + return ((StateAttribute) (_res.getAttributeSet().getAttributeByType(Attribute.STATE))).getString(); + } + + public String getReplyMessage() { + return ((ReplyMessageAttribute) (_res.getAttributeSet().getAttributeByType(Attribute.REPLY_MESSAGE))) + .getString(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FilterIdAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FilterIdAttribute.java new file mode 100644 index 000000000..879d7d5c7 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FilterIdAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FilterIdAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public FilterIdAttribute(byte value[]) { + super(); + _t = CHAP_PASSWORD; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkLinkAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkLinkAttribute.java new file mode 100644 index 000000000..05273780f --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkLinkAttribute.java @@ -0,0 +1,51 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedAppleTalkLinkAttribute extends Attribute { + public static int UN_NUMBERED = 0; + + private byte _value[] = null; + private int _type = 0; + + public FramedAppleTalkLinkAttribute(byte value[]) { + super(); + _t = FRAMED_APPLETALK_LINK; + _value = value; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkNetworkAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkNetworkAttribute.java new file mode 100644 index 000000000..cea0d936a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkNetworkAttribute.java @@ -0,0 +1,49 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedAppleTalkNetworkAttribute extends Attribute { + private byte _value[] = null; + private int _type = 0; + + public FramedAppleTalkNetworkAttribute(byte value[]) { + super(); + _t = FRAMED_APPLETALK_NETWORK; + _value = value; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkZoneAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkZoneAttribute.java new file mode 100644 index 000000000..54ee47c45 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedAppleTalkZoneAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedAppleTalkZoneAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public FramedAppleTalkZoneAttribute(byte value[]) { + super(); + _t = FRAMED_APPLETALK_ZONE; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedCompressionAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedCompressionAttribute.java new file mode 100644 index 000000000..b57c030b5 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedCompressionAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedCompressionAttribute extends Attribute { + public static final int NONE = 1; + public static final int VJ_TCP_IP_HEADER = 2; + public static final int IPX_HEADER = 2; + public static final int STAC_LZS = 3; + + private byte _value[] = null; + private int _type = 0; + + public FramedCompressionAttribute(byte value[]) { + super(); + _t = FRAMED_COMPRESSION; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + _value = value; + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedIPAddressAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedIPAddressAttribute.java new file mode 100644 index 000000000..2f66ee8c2 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedIPAddressAttribute.java @@ -0,0 +1,39 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedIPAddressAttribute extends Attribute { + private byte _value[] = null; + private byte _addr[] = new byte[4]; + + public FramedIPAddressAttribute(byte value[]) { + super(); + _t = FRAMED_IP_ADDRESS; + _addr[0] = value[2]; + _addr[1] = value[3]; + _addr[2] = value[4]; + _addr[3] = value[5]; + _value = value; + } + + public byte[] getValue() throws IOException { + return _addr; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedIPNetmaskAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedIPNetmaskAttribute.java new file mode 100644 index 000000000..f8e1980c7 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedIPNetmaskAttribute.java @@ -0,0 +1,39 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedIPNetmaskAttribute extends Attribute { + private byte _value[] = null; + private byte _mask[] = new byte[4]; + + public FramedIPNetmaskAttribute(byte value[]) { + super(); + _t = FRAMED_IP_NETMASK; + _mask[0] = value[2]; + _mask[1] = value[3]; + _mask[2] = value[4]; + _mask[3] = value[5]; + _value = value; + } + + public byte[] getValue() throws IOException { + return _mask; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedIPXNetworkAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedIPXNetworkAttribute.java new file mode 100644 index 000000000..92f47eec1 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedIPXNetworkAttribute.java @@ -0,0 +1,39 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedIPXNetworkAttribute extends Attribute { + private byte _value[] = null; + private byte _net[] = new byte[4]; + + public FramedIPXNetworkAttribute(byte value[]) { + super(); + _t = FRAMED_IPX_NETWORK; + _net[0] = value[2]; + _net[1] = value[3]; + _net[2] = value[4]; + _net[3] = value[5]; + _value = value; + } + + public byte[] getValue() throws IOException { + return _net; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedMTUAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedMTUAttribute.java new file mode 100644 index 000000000..5cd9551a2 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedMTUAttribute.java @@ -0,0 +1,49 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedMTUAttribute extends Attribute { + private byte _value[] = null; + private int _type = 0; + + public FramedMTUAttribute(byte value[]) { + super(); + _t = FRAMED_IP_ADDRESS; + _value = value; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedProtocolAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedProtocolAttribute.java new file mode 100644 index 000000000..5af219b9a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedProtocolAttribute.java @@ -0,0 +1,56 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedProtocolAttribute extends Attribute { + public static final int PPP = 1; + public static final int SLIP = 2; + public static final int ARAP = 3; + public static final int GANDALF = 4; + public static final int XYLOGICS = 5; + public static final int X_75 = 6; + + private byte _value[] = null; + private int _type = 0; + + public FramedProtocolAttribute(byte value[]) { + super(); + _t = SERVICE_TYPE; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + _value = value; + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedRouteAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedRouteAttribute.java new file mode 100644 index 000000000..9b123fe2a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedRouteAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedRouteAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public FramedRouteAttribute(byte value[]) { + super(); + _t = FRAMED_ROUTE; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/FramedRoutingAttribute.java b/base/util/src/com/netscape/cmsutil/radius/FramedRoutingAttribute.java new file mode 100644 index 000000000..14d2b0cc8 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/FramedRoutingAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class FramedRoutingAttribute extends Attribute { + public static final int NONE = 0; + public static final int SEND_ROUTING_PACKETS = 1; + public static final int LISTEN_FOR_ROUTING_PACKETS = 2; + public static final int SEND_AND_LISTEN = 3; + + private byte _value[] = null; + private int _type = 0; + + public FramedRoutingAttribute(byte value[]) { + super(); + _t = FRAMED_ROUTING; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + _value = value; + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/GenericAttribute.java b/base/util/src/com/netscape/cmsutil/radius/GenericAttribute.java new file mode 100644 index 000000000..ac1798ae3 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/GenericAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class GenericAttribute extends Attribute { + private byte _value[] = null; + + public GenericAttribute(byte value[]) { + super(); + _t = value[0]; + _value = new byte[value.length - 2]; + System.arraycopy(value, 2, _value, 0, _value.length); + } + + public byte[] getValue() throws IOException { + return _value; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/IdleTimeoutAttribute.java b/base/util/src/com/netscape/cmsutil/radius/IdleTimeoutAttribute.java new file mode 100644 index 000000000..44b0c5087 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/IdleTimeoutAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class IdleTimeoutAttribute extends Attribute { + private int _timeout = 0; + + public IdleTimeoutAttribute(byte value[]) { + super(); + _t = IDLE_TIMEOUT; + _timeout = value[5] & 0xFF; + _timeout |= ((value[4] << 8) & 0xFF00); + _timeout |= ((value[3] << 16) & 0xFF0000); + _timeout |= ((value[2] << 24) & 0xFF000000); + } + + public IdleTimeoutAttribute(int timeout) { + super(IDLE_TIMEOUT); + _timeout = timeout; + } + + public int getTimeout() { + return _timeout; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_timeout >>> 24) & 0xFF); + p[1] = (byte) ((_timeout >>> 16) & 0xFF); + p[2] = (byte) ((_timeout >>> 8) & 0xFF); + p[3] = (byte) (_timeout & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/LoginIPHostAttribute.java b/base/util/src/com/netscape/cmsutil/radius/LoginIPHostAttribute.java new file mode 100644 index 000000000..0d1c0565a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/LoginIPHostAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class LoginIPHostAttribute extends Attribute { + public static final int NAS_ALLOW_SELECT = 0xFFFFFFFF; + public static final int NAS_SELECT = 0; + + private byte _value[] = null; + private int _type = 0; + + public LoginIPHostAttribute(byte value[]) { + super(); + _t = LOGIN_IP_HOST; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + _value = value; + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/LoginLATGroupAttribute.java b/base/util/src/com/netscape/cmsutil/radius/LoginLATGroupAttribute.java new file mode 100644 index 000000000..4cee6bc3a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/LoginLATGroupAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class LoginLATGroupAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public LoginLATGroupAttribute(byte value[]) { + super(); + _t = LOGIN_LAT_GROUP; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/LoginLATNodeAttribute.java b/base/util/src/com/netscape/cmsutil/radius/LoginLATNodeAttribute.java new file mode 100644 index 000000000..2c2d3411e --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/LoginLATNodeAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class LoginLATNodeAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public LoginLATNodeAttribute(byte value[]) { + super(); + _t = LOGIN_LAT_NODE; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/LoginLATPortAttribute.java b/base/util/src/com/netscape/cmsutil/radius/LoginLATPortAttribute.java new file mode 100644 index 000000000..330161ec8 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/LoginLATPortAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class LoginLATPortAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public LoginLATPortAttribute(byte value[]) { + super(); + _t = PROXY_STATE; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/LoginLATServiceAttribute.java b/base/util/src/com/netscape/cmsutil/radius/LoginLATServiceAttribute.java new file mode 100644 index 000000000..158630d27 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/LoginLATServiceAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class LoginLATServiceAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public LoginLATServiceAttribute(byte value[]) { + super(); + _t = LOGIN_LAT_SERVICE; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/LoginServiceAttribute.java b/base/util/src/com/netscape/cmsutil/radius/LoginServiceAttribute.java new file mode 100644 index 000000000..73f49d39f --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/LoginServiceAttribute.java @@ -0,0 +1,58 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class LoginServiceAttribute extends Attribute { + public static final int TELNET = 0; + public static final int RLOGIN = 1; + public static final int TCP_CLEAR = 2; + public static final int PORTMASTER = 3; + public static final int LAT = 4; + public static final int X25_PAD = 5; + public static final int X25_T3POS = 6; + public static final int TCP_CLEAR_QUIET = 8; + + private byte _value[] = null; + private int _type = 0; + + public LoginServiceAttribute(byte value[]) { + super(); + _t = LOGIN_SERVICE; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + _value = value; + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/LoginTCPPortAttribute.java b/base/util/src/com/netscape/cmsutil/radius/LoginTCPPortAttribute.java new file mode 100644 index 000000000..6b44f50c6 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/LoginTCPPortAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class LoginTCPPortAttribute extends Attribute { + private int _port = 0; + + public LoginTCPPortAttribute(byte value[]) { + super(); + _t = LOGIN_TCP_PORT; + _port = value[5] & 0xFF; + _port |= ((value[4] << 8) & 0xFF00); + _port |= ((value[3] << 16) & 0xFF0000); + _port |= ((value[2] << 24) & 0xFF000000); + } + + public LoginTCPPortAttribute(int port) { + super(LOGIN_TCP_PORT); + _port = port; + } + + public int getPort() { + return _port; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_port >>> 24) & 0xFF); + p[1] = (byte) ((_port >>> 16) & 0xFF); + p[2] = (byte) ((_port >>> 8) & 0xFF); + p[3] = (byte) (_port & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/NASClassAttribute.java b/base/util/src/com/netscape/cmsutil/radius/NASClassAttribute.java new file mode 100644 index 000000000..57b983028 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/NASClassAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class NASClassAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public NASClassAttribute(byte value[]) { + super(); + _t = NAS_CLASS; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/NASIPAddressAttribute.java b/base/util/src/com/netscape/cmsutil/radius/NASIPAddressAttribute.java new file mode 100644 index 000000000..d4022b3dd --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/NASIPAddressAttribute.java @@ -0,0 +1,41 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; +import java.net.InetAddress; + +public class NASIPAddressAttribute extends Attribute { + private InetAddress _ip = null; + private byte _value[] = null; + + public NASIPAddressAttribute(byte value[]) { + super(); + _t = NAS_IP_ADDRESS; + _value = value; + } + + public NASIPAddressAttribute(InetAddress ip) { + super(NAS_IP_ADDRESS); + _ip = ip; + } + + public byte[] getValue() throws IOException { + return _ip.getAddress(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/NASIdentifierAttribute.java b/base/util/src/com/netscape/cmsutil/radius/NASIdentifierAttribute.java new file mode 100644 index 000000000..0a3a62cd8 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/NASIdentifierAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class NASIdentifierAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public NASIdentifierAttribute(byte value[]) { + super(); + _t = NAS_IDENTIFIER; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/NASPacket.java b/base/util/src/com/netscape/cmsutil/radius/NASPacket.java new file mode 100644 index 000000000..70d143989 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/NASPacket.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 com.netscape.cmsutil.radius; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public abstract class NASPacket extends Packet { + public NASPacket(int c, short id, Authenticator auth) { + super(c, id, auth); + } + + public byte[] getData() throws IOException { + // prepare the attributes first + ByteArrayOutputStream attrsOS = new ByteArrayOutputStream(); + + for (int i = 0; i < _attrs.size(); i++) { + Attribute attr = (Attribute) getAttributeAt(i); + + attrsOS.write(attr.getData()); + } + byte attrsData[] = attrsOS.toByteArray(); + + ByteArrayOutputStream dataOS = new ByteArrayOutputStream(); + + dataOS.write(_c); // code + dataOS.write(_id); // identifier + int len = attrsData.length + 20; + + dataOS.write((len >>> 8) & 0xFF); + dataOS.write(len & 0xFF); + dataOS.write(_auth.getData()); + dataOS.write(attrsData); + + return dataOS.toByteArray(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/NASPortAttribute.java b/base/util/src/com/netscape/cmsutil/radius/NASPortAttribute.java new file mode 100644 index 000000000..0f7b31e75 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/NASPortAttribute.java @@ -0,0 +1,48 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class NASPortAttribute extends Attribute { + private int _port = 0; + + public NASPortAttribute(byte value[]) { + super(); + _t = NAS_PORT; + _port = value[5] & 0xFF; + _port |= ((value[4] << 8) & 0xFF00); + _port |= ((value[3] << 16) & 0xFF0000); + _port |= ((value[2] << 24) & 0xFF000000); + } + + public NASPortAttribute(int port) { + super(NAS_PORT); + _port = port; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_port >>> 24) & 0xFF); + p[1] = (byte) ((_port >>> 16) & 0xFF); + p[2] = (byte) ((_port >>> 8) & 0xFF); + p[3] = (byte) (_port & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/NASPortTypeAttribute.java b/base/util/src/com/netscape/cmsutil/radius/NASPortTypeAttribute.java new file mode 100644 index 000000000..84ccc3ae1 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/NASPortTypeAttribute.java @@ -0,0 +1,53 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class NASPortTypeAttribute extends Attribute { + public static final int ASYNC = 0; + public static final int SYNC = 1; + public static final int ISDN_SYNC = 2; + public static final int ISDN_ASYNC_V120 = 3; + public static final int ISDN_ASYNC_V110 = 4; + public static final int VIRTUAL = 5; + public static final int PIAFS = 6; + public static final int HDLC = 7; + public static final int X_25 = 8; + public static final int X_75 = 9; + public static final int G3_FAX = 10; + public static final int SDSL = 11; + public static final int ADSL_CAP = 12; + public static final int ADSL_DMT = 13; + public static final int IDSL = 14; + public static final int ETHERNET = 15; + public static final int XDSL = 16; + public static final int CABLE = 17; + + private byte _value[] = null; + + public NASPortTypeAttribute(byte value[]) { + super(); + _t = NAS_PORT_TYPE; + _value = value; + } + + public byte[] getValue() throws IOException { + return _value; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/Packet.java b/base/util/src/com/netscape/cmsutil/radius/Packet.java new file mode 100644 index 000000000..4fad0ba79 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/Packet.java @@ -0,0 +1,70 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +public abstract class Packet { + public static final int ACCESS_REQUEST = 1; + public static final int ACCESS_ACCEPT = 2; + public static final int ACCESS_REJECT = 3; + // public static final int ACCOUNTING_REQUEST = 4; + // public static final int ACCOUNTING_RESPONSE = 5; + public static final int ACCESS_CHALLENGE = 11; + public static final int RESERVED = 255; + + protected int _c = 0; + protected short _id = 0; + protected Authenticator _auth = null; + protected AttributeSet _attrs = new AttributeSet(); + + public Packet() { + } + + public Packet(int c, short id, Authenticator auth) { + _c = c; + _id = id; + _auth = auth; + } + + public int getCode() { + return _c; + } + + public short getIdentifier() { + return _id; + } + + public Authenticator getAuthenticator() { + return _auth; + } + + public void addAttribute(Attribute attr) { + _attrs.addAttribute(attr); + } + + public AttributeSet getAttributeSet() { + return _attrs; + } + + public Attribute getAttributeAt(int pos) { + return _attrs.getAttributeAt(pos); + } + + public String toString() { + return "Packet [code=" + _c + ",id=" + (_id & 0xFF) + "]"; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/PacketFactory.java b/base/util/src/com/netscape/cmsutil/radius/PacketFactory.java new file mode 100644 index 000000000..8d2e20e74 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/PacketFactory.java @@ -0,0 +1,39 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class PacketFactory { + public static ServerPacket createServerPacket(byte data[]) + throws IOException { + switch (data[0] & 0xFF) { + case Packet.ACCESS_ACCEPT: + return new AccessAccept(data); + + case Packet.ACCESS_REJECT: + return new AccessReject(data); + + case Packet.ACCESS_CHALLENGE: + return new AccessChallenge(data); + + default: + throw new IOException("Unknown server packet " + (data[0] & 0xFF)); + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/PortLimitAttribute.java b/base/util/src/com/netscape/cmsutil/radius/PortLimitAttribute.java new file mode 100644 index 000000000..7903bb1fd --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/PortLimitAttribute.java @@ -0,0 +1,51 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class PortLimitAttribute extends Attribute { + private int _port = 0; + + private byte _value[] = null; + + public PortLimitAttribute(byte value[]) { + super(); + _t = FRAMED_IP_ADDRESS; + _value = value; + _port = value[5] & 0xFF; + _port |= ((value[4] << 8) & 0xFF00); + _port |= ((value[3] << 16) & 0xFF0000); + _port |= ((value[2] << 24) & 0xFF000000); + + } + + public int getPort() { + return _port; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_port >>> 24) & 0xFF); + p[1] = (byte) ((_port >>> 16) & 0xFF); + p[2] = (byte) ((_port >>> 8) & 0xFF); + p[3] = (byte) (_port & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/ProxyStateAttribute.java b/base/util/src/com/netscape/cmsutil/radius/ProxyStateAttribute.java new file mode 100644 index 000000000..83831b652 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/ProxyStateAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class ProxyStateAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public ProxyStateAttribute(byte value[]) { + super(); + _t = PROXY_STATE; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/RadiusConn.java b/base/util/src/com/netscape/cmsutil/radius/RadiusConn.java new file mode 100644 index 000000000..b22807a5d --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/RadiusConn.java @@ -0,0 +1,230 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Properties; + +/** + * This class implements RFC2865 - Remote Authentication Dial In + * User Service (RADIUS), June 2000. + */ +public class RadiusConn { + public static int MAX_RETRIES = 10; + public static int OFFICAL_PORT = 1812; + public static int DEFAULT_PORT = 1645; + public static int DEFAULT_TIMEOUT = 5; + + public static String OPTION_DEBUG = "OPTION_DEBUG"; + + private Properties _options = null; + private boolean _traceOn = true; + private String _host[] = new String[2]; + private int _port[] = new int[2]; + private int _selected = 0; + private String _secret = null; + private DatagramSocket _socket = null; + private short _id = (short) System.currentTimeMillis(); + private int _maxRetries = MAX_RETRIES; + private SecureRandom _rand = null; + + public RadiusConn(String host1, String host2, int port, String secret, + int timeout) throws SocketException { + this(host1, port, host2, port, secret, timeout, null, null); + } + + public RadiusConn(String host, int port, String secret, byte seed[], + Properties options) + throws SocketException { + this(host, port, host, port, secret, DEFAULT_TIMEOUT, seed, options); + } + + public RadiusConn(String host1, int port1, String host2, int port2, + String secret, int timeout, byte seed[], Properties options) + throws SocketException { + _host[0] = host1; + _port[0] = port1; + _host[1] = host2; + _port[1] = port2; + _selected = 0; + _secret = secret; + _options = options; + _socket = new DatagramSocket(); + _socket.setSoTimeout(timeout * 1000); + if (seed == null) { + _rand = new SecureRandom(); + } else { + _rand = new SecureRandom(seed); + } + } + + public void disconnect() throws IOException { + _socket.disconnect(); + } + + public void authenticate(String name, String password) + throws IOException, NoSuchAlgorithmException, + RejectException, ChallengeException { + int retries = 0; + Packet res = null; + + do { + AccessRequest req = createAccessRequest(); + + req.addAttribute(new UserNameAttribute(name)); + req.addAttribute(new UserPasswordAttribute(req.getAuthenticator(), + _secret, password)); + req.addAttribute(new NASIPAddressAttribute(InetAddress.getLocalHost())); + req.addAttribute(new NASPortAttribute(_socket.getLocalPort())); + + send(req, _host[_selected], _port[_selected]); + try { + retries++; + res = receive(); + if (res instanceof AccessReject) { + throw new RejectException((AccessReject) res); + } else if (res instanceof AccessChallenge) { + throw new ChallengeException((AccessChallenge) res); + } + } catch (InterruptedIOException e) { + if (retries >= _maxRetries) { + // switch server if maxRetries reaches limit + retries = 0; + if (_selected == 0) { + _selected = 1; + } else { + _selected = 0; + } + // throw e; + } + + } + } while (res == null); + } + + public void replyChallenge(String password, ChallengeException ce) + throws IOException, NoSuchAlgorithmException, + RejectException, ChallengeException { + replyChallenge(null, password, ce); + } + + public void replyChallenge(String name, String password, + ChallengeException ce) + throws IOException, NoSuchAlgorithmException, + RejectException, ChallengeException { + StateAttribute state = (StateAttribute) + ce.getAttributeSet().getAttributeByType(Attribute.STATE); + + if (state == null) + throw new IOException("State not found in challenge"); + AccessRequest req = createAccessRequest(); + + req.addAttribute(state); // needed in challenge + if (name != null) { + req.addAttribute(new UserNameAttribute(name)); + } + req.addAttribute(new UserPasswordAttribute(req.getAuthenticator(), + _secret, password)); + req.addAttribute(new NASIPAddressAttribute(InetAddress.getLocalHost())); + req.addAttribute(new NASPortAttribute(_socket.getLocalPort())); + + send(req, _host[_selected], _port[_selected]); + Packet res = receive(); + + if (res instanceof AccessReject) { + throw new RejectException((AccessReject) res); + } else if (res instanceof AccessChallenge) { + throw new ChallengeException((AccessChallenge) res); + } + } + + public void replyChallenge(String name, String password, String state) + throws IOException, NoSuchAlgorithmException, + RejectException, ChallengeException { + if (state == null) + throw new IOException("State not found in challenge"); + AccessRequest req = createAccessRequest(); + + req.addAttribute(new StateAttribute(state)); // needed in challenge + req.addAttribute(new UserNameAttribute(name)); + req.addAttribute(new UserPasswordAttribute(req.getAuthenticator(), + _secret, password)); + req.addAttribute(new NASIPAddressAttribute(InetAddress.getLocalHost())); + req.addAttribute(new NASPortAttribute(_socket.getLocalPort())); + + send(req, _host[_selected], _port[_selected]); + Packet res = receive(); + + if (res instanceof AccessReject) { + throw new RejectException((AccessReject) res); + } else if (res instanceof AccessChallenge) { + throw new ChallengeException((AccessChallenge) res); + } + } + + private short getIdentifier() { + return _id++; + } + + private void send(NASPacket packet, String host, int port) + throws IOException { + DatagramPacket dp = new DatagramPacket(new byte[4096], 4096); + + dp.setPort(port); + dp.setAddress(InetAddress.getByName(host)); + byte data[] = packet.getData(); + + dp.setLength(data.length); + dp.setData(data); + _socket.send(dp); + if (_traceOn) + trace("Sent " + packet); + } + + private ServerPacket receive() + throws IOException { + DatagramPacket dp = new DatagramPacket(new byte[4096], 4096); + + _socket.receive(dp); + byte data[] = dp.getData(); + ServerPacket p = PacketFactory.createServerPacket(data); + + if (_traceOn) + trace("Received " + p + " size=" + p.getAttributeSet().size()); + return p; + } + + private AccessRequest createAccessRequest() throws NoSuchAlgorithmException { + RequestAuthenticator ra = new RequestAuthenticator(_rand, _secret); + AccessRequest req = new AccessRequest(getIdentifier(), ra); + + return req; + } + + private void trace(String msg) { + System.out.println("TRACE: " + msg); + System.out.flush(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/RejectException.java b/base/util/src/com/netscape/cmsutil/radius/RejectException.java new file mode 100644 index 000000000..f312ef2a4 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/RejectException.java @@ -0,0 +1,39 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +public class RejectException extends Exception { + /** + * + */ + private static final long serialVersionUID = -6410697638175895003L; + private AccessReject _res = null; + + public RejectException(AccessReject res) { + _res = res; + } + + public AttributeSet getAttributeSet() { + return _res.getAttributeSet(); + } + + public String getReplyMessage() { + return ((ReplyMessageAttribute) (_res.getAttributeSet().getAttributeByType(Attribute.REPLY_MESSAGE))) + .getString(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/ReplyMessageAttribute.java b/base/util/src/com/netscape/cmsutil/radius/ReplyMessageAttribute.java new file mode 100644 index 000000000..5ec4ea052 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/ReplyMessageAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class ReplyMessageAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public ReplyMessageAttribute(byte value[]) { + super(); + _t = REPLY_MESSAGE; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/RequestAuthenticator.java b/base/util/src/com/netscape/cmsutil/radius/RequestAuthenticator.java new file mode 100644 index 000000000..5d82752dd --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/RequestAuthenticator.java @@ -0,0 +1,44 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class RequestAuthenticator extends Authenticator { + private byte _ra[] = null; + + public RequestAuthenticator(SecureRandom rand, String secret) + throws NoSuchAlgorithmException { + byte[] authenticator = new byte[16]; + + rand.nextBytes(authenticator); + + MessageDigest md5 = MessageDigest.getInstance("MD5"); + + md5.update(authenticator); + md5.update(secret.getBytes()); + _ra = md5.digest(); + } + + public byte[] getData() throws IOException { + return _ra; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/ResponseAuthenticator.java b/base/util/src/com/netscape/cmsutil/radius/ResponseAuthenticator.java new file mode 100644 index 000000000..3c3de33c4 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/ResponseAuthenticator.java @@ -0,0 +1,32 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class ResponseAuthenticator extends Authenticator { + private byte _data[] = null; + + public ResponseAuthenticator(byte data[]) { + _data = data; + } + + public byte[] getData() throws IOException { + return _data; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/ServerPacket.java b/base/util/src/com/netscape/cmsutil/radius/ServerPacket.java new file mode 100644 index 000000000..f7d7fa302 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/ServerPacket.java @@ -0,0 +1,47 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public abstract class ServerPacket extends Packet { + public ServerPacket(byte data[]) throws IOException { + super(); + _c = data[0]; + _id = data[1]; + int datalen = data[3] & 0xFF; + + datalen |= ((data[2] << 8) & 0xFF00); + byte authData[] = new byte[16]; + + System.arraycopy(data, 4, authData, 0, 16); + _auth = new ResponseAuthenticator(authData); + + // building attributes + int startp = 20; + + while (startp != datalen) { + int attrLen = (data[startp + 1] & 0xFF); + byte attrData[] = new byte[attrLen]; + + System.arraycopy(data, startp, attrData, 0, attrData.length); + addAttribute(AttributeFactory.createAttribute(attrData)); + startp += attrData.length; + } + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/ServiceTypeAttribute.java b/base/util/src/com/netscape/cmsutil/radius/ServiceTypeAttribute.java new file mode 100644 index 000000000..f31c74f9a --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/ServiceTypeAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class ServiceTypeAttribute extends Attribute { + public static final int LOGIN = 1; + public static final int FRAMED = 2; + public static final int CALLBACK_LOGIN = 3; + public static final int CALLBACK_FRAMED = 4; + public static final int OUTBOUND = 5; + public static final int ADMINSITRATIVE = 6; + public static final int NAS_PROMPT = 7; + public static final int AUTHENTICATE_ONLY = 8; + public static final int CALLBACK_NAS_PROMPT = 9; + public static final int CALL_CHECK = 10; + public static final int CALLBACK_ADMINISTRATIVE = 11; + + private byte _value[] = null; + private int _type = 0; + + public ServiceTypeAttribute(byte value[]) { + super(); + _t = SERVICE_TYPE; + _type = value[5] & 0xFF; + _type |= ((value[4] << 8) & 0xFF00); + _type |= ((value[3] << 16) & 0xFF0000); + _type |= ((value[2] << 24) & 0xFF000000); + _value = value; + } + + public int getType() { + return _type; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_type >>> 24) & 0xFF); + p[1] = (byte) ((_type >>> 16) & 0xFF); + p[2] = (byte) ((_type >>> 8) & 0xFF); + p[3] = (byte) (_type & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/SessionTimeoutAttribute.java b/base/util/src/com/netscape/cmsutil/radius/SessionTimeoutAttribute.java new file mode 100644 index 000000000..2809aee4b --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/SessionTimeoutAttribute.java @@ -0,0 +1,48 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class SessionTimeoutAttribute extends Attribute { + private int _timeout = 0; + + public SessionTimeoutAttribute(byte value[]) { + super(); + _t = SESSION_TIMEOUT; + _timeout = value[5] & 0xFF; + _timeout |= ((value[4] << 8) & 0xFF00); + _timeout |= ((value[3] << 16) & 0xFF0000); + _timeout |= ((value[2] << 24) & 0xFF000000); + } + + public SessionTimeoutAttribute(int timeout) { + super(SESSION_TIMEOUT); + _timeout = timeout; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_timeout >>> 24) & 0xFF); + p[1] = (byte) ((_timeout >>> 16) & 0xFF); + p[2] = (byte) ((_timeout >>> 8) & 0xFF); + p[3] = (byte) (_timeout & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/StateAttribute.java b/base/util/src/com/netscape/cmsutil/radius/StateAttribute.java new file mode 100644 index 000000000..027f95620 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/StateAttribute.java @@ -0,0 +1,45 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class StateAttribute extends Attribute { + private byte _value[] = null; + private String _str = null; + + public StateAttribute(String str) { + _t = STATE; + _str = str; + } + + public StateAttribute(byte value[]) { + super(); + _t = STATE; + _str = new String(value, 2, value.length - 2); + _value = value; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + return _str.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/TerminationActionAttribute.java b/base/util/src/com/netscape/cmsutil/radius/TerminationActionAttribute.java new file mode 100644 index 000000000..b47a70d8c --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/TerminationActionAttribute.java @@ -0,0 +1,55 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class TerminationActionAttribute extends Attribute { + public static final int DEFAULT = 0; + public static final int RADIUS_REQUEST = 1; + + private int _action = 0; + + public TerminationActionAttribute(byte value[]) { + super(); + _t = TERMINATION_ACTION; + _action = value[5] & 0xFF; + _action |= ((value[4] << 8) & 0xFF00); + _action |= ((value[3] << 16) & 0xFF0000); + _action |= ((value[2] << 24) & 0xFF000000); + } + + public TerminationActionAttribute(int action) { + super(TERMINATION_ACTION); + _action = action; + } + + public int getAction() { + return _action; + } + + public byte[] getValue() throws IOException { + byte[] p = new byte[4]; + + p[0] = (byte) ((_action >>> 24) & 0xFF); + p[1] = (byte) ((_action >>> 16) & 0xFF); + p[2] = (byte) ((_action >>> 8) & 0xFF); + p[3] = (byte) (_action & 0xFF); + return p; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/UserNameAttribute.java b/base/util/src/com/netscape/cmsutil/radius/UserNameAttribute.java new file mode 100644 index 000000000..af7ce6bbe --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/UserNameAttribute.java @@ -0,0 +1,39 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class UserNameAttribute extends Attribute { + private String _name = null; + + public UserNameAttribute(byte value[]) { + super(); + _t = USER_NAME; + _name = new String(value, 2, value.length - 2); + } + + public UserNameAttribute(String name) { + super(USER_NAME); + _name = name; + } + + public byte[] getValue() throws IOException { + return _name.getBytes(); + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/UserPasswordAttribute.java b/base/util/src/com/netscape/cmsutil/radius/UserPasswordAttribute.java new file mode 100644 index 000000000..31c27cdfd --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/UserPasswordAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class UserPasswordAttribute extends Attribute { + private Authenticator _ra = null; + private String _secret = null; + private String _password = null; + + public UserPasswordAttribute(byte value[]) { + // + } + + public UserPasswordAttribute(Authenticator ra, String secret, String password) { + super(USER_PASSWORD); + _ra = ra; + _secret = secret; + _password = password; + } + + public byte[] getValue() throws IOException { + MessageDigest md5 = null; + + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new IOException(e.getMessage()); + } + md5.update(_secret.getBytes()); + md5.update(_ra.getData()); + byte sum[] = md5.digest(); + + byte up[] = _password.getBytes(); + int oglen = (up.length / 16) + 1; + byte ret[] = new byte[oglen * 16]; + + for (int i = 0; i < ret.length; i++) { + if ((i % 16) == 0) { + md5.reset(); + md5.update(_secret.getBytes()); + } + if (i < up.length) { + ret[i] = (byte) (sum[i % 16] ^ up[i]); + } else { + ret[i] = (byte) (sum[i % 16] ^ 0); + } + md5.update(ret[i]); + if ((i % 16) == 15) { + sum = md5.digest(); + } + } + return ret; + } +} diff --git a/base/util/src/com/netscape/cmsutil/radius/VendorSpecificAttribute.java b/base/util/src/com/netscape/cmsutil/radius/VendorSpecificAttribute.java new file mode 100644 index 000000000..5f3d9f170 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/radius/VendorSpecificAttribute.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 com.netscape.cmsutil.radius; + +import java.io.IOException; + +public class VendorSpecificAttribute extends Attribute { + private byte _value[] = null; + private String _id = null; + private String _str = null; + + public VendorSpecificAttribute(byte value[]) { + super(); + _t = VENDOR_SPECIFIC; + _id = new String(value, 2, 4); + _str = new String(value, 6, value.length - 6); + _value = value; + } + + public String getId() { + return _id; + } + + public String getString() { + return _str; + } + + public byte[] getValue() throws IOException { + byte v[] = new byte[_id.length() + _str.length()]; + byte idData[] = _id.getBytes(); + byte strData[] = _str.getBytes(); + + System.arraycopy(idData, 0, v, 0, _id.length()); + System.arraycopy(strData, 0, v, _id.length(), _str.length()); + return v; + } +} diff --git a/base/util/src/com/netscape/cmsutil/scep/CRSPKIMessage.java b/base/util/src/com/netscape/cmsutil/scep/CRSPKIMessage.java new file mode 100644 index 000000000..03bc68723 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/scep/CRSPKIMessage.java @@ -0,0 +1,905 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.scep; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharConversionException; +import java.io.IOException; +import java.security.PublicKey; +import java.util.Arrays; +import java.util.Hashtable; + +import netscape.security.pkcs.PKCS10; + +import org.mozilla.jss.asn1.ANY; +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.NULL; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.PrintableString; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.crypto.SignatureAlgorithm; +import org.mozilla.jss.crypto.TokenException; +import org.mozilla.jss.pkcs7.Attribute; +import org.mozilla.jss.pkcs7.ContentInfo; +import org.mozilla.jss.pkcs7.EncryptedContentInfo; +import org.mozilla.jss.pkcs7.EnvelopedData; +import org.mozilla.jss.pkcs7.IssuerAndSerialNumber; +import org.mozilla.jss.pkcs7.RecipientInfo; +import org.mozilla.jss.pkcs7.SignedData; +import org.mozilla.jss.pkcs7.SignerInfo; +import org.mozilla.jss.pkix.cert.Certificate; +import org.mozilla.jss.pkix.cert.CertificateInfo; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; + +public class CRSPKIMessage { + + // OIDs for authenticated attributes + public static OBJECT_IDENTIFIER CRS_MESSAGETYPE = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 113733, 1, 9, 2 } + ); + public static OBJECT_IDENTIFIER CRS_PKISTATUS = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 113733, 1, 9, 3 } + ); + public static OBJECT_IDENTIFIER CRS_FAILINFO = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 113733, 1, 9, 4 } + ); + public static OBJECT_IDENTIFIER CRS_SENDERNONCE = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 113733, 1, 9, 5 } + ); + public static OBJECT_IDENTIFIER CRS_RECIPIENTNONCE = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 113733, 1, 9, 6 } + ); + public static OBJECT_IDENTIFIER CRS_TRANSID = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 113733, 1, 9, 7 } + ); + public static OBJECT_IDENTIFIER CRS_EXTENSIONREQ = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 113733, 1, 9, 8 } + ); + + // PKCS9 defined OIDs + + public static OBJECT_IDENTIFIER PKCS9_CONTENT_TYPE = + new OBJECT_IDENTIFIER(new long[] { 1, 2, 840, 113549, 1, 9, 3 } + ); + + public static OBJECT_IDENTIFIER PKCS9_MESSAGE_DIGEST = + new OBJECT_IDENTIFIER(new long[] { 1, 2, 840, 113549, 1, 9, 4 } + ); + + /* PKCS 1 - rsaEncryption */ + public static OBJECT_IDENTIFIER RSA_ENCRYPTION = + new OBJECT_IDENTIFIER(new long[] { 1, 2, 840, 113549, 1, 1, 1 } + ); + + public static OBJECT_IDENTIFIER DES_CBC_ENCRYPTION = + new OBJECT_IDENTIFIER(new long[] { 1, 3, 14, 3, 2, 7 } + ); + + public static OBJECT_IDENTIFIER DES_EDE3_CBC_ENCRYPTION = + new OBJECT_IDENTIFIER(new long[] { 1, 2, 840, 113549, 3, 7 } + ); + + public static OBJECT_IDENTIFIER MD5_DIGEST = + new OBJECT_IDENTIFIER(new long[] { 1, 2, 840, 113549, 2, 5 } + ); + + public static OBJECT_IDENTIFIER SHA1_DIGEST = + new OBJECT_IDENTIFIER(new long[] { 1, 3, 14, 3, 2, 26 } + ); + + public static OBJECT_IDENTIFIER SHA256_DIGEST = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 101, 3, 4, 2, 1 } + ); + + public static OBJECT_IDENTIFIER SHA512_DIGEST = + new OBJECT_IDENTIFIER(new long[] { 2, 16, 840, 1, 101, 3, 4, 2, 3 } + ); + + // Strings given in 'messageType' authenticated attribute + public final static String mType_PKCSReq = "19"; + public final static String mType_CertRep = "3"; + public final static String mType_GetCertInitial = "20"; + public final static String mType_GetCert = "21"; + public final static String mType_GetCRL = "22"; + + // Strings given in 'PKIStatus' authenticated attribute + public final static String mStatus_SUCCESS = "0"; + public final static String mStatus_FAILURE = "2"; + public final static String mStatus_PENDING = "3"; + + // Strings given in 'failInfo' authenticated attribute + public final static String mFailInfo_badAlg = "0"; + public final static String mFailInfo_badMessageCheck = "1"; + public final static String mFailInfo_badRequest = "2"; + public final static String mFailInfo_badTime = "3"; + public final static String mFailInfo_badCertId = "4"; + public final static String mFailInfo_unsupportedExt = "5"; + public final static String mFailInfo_mustArchiveKeys = "6"; + public final static String mFailInfo_badIdentity = "7"; + public final static String mFailInfo_popRequired = "8"; + public final static String mFailInfo_popFailed = "9"; + public final static String mFailInfo_noKeyReuse = "10"; + public final static String mFailInfo_internalCAError = "11"; + public final static String mFailInfo_tryLater = "12"; + + // ************************************************************************ + // These private members represent the flattened structure of the PKIMessage + // ************************************************************************ + + // top level is just a ContentInfo + private ContentInfo crsci; + // it's content is a signedData + private SignedData sd; + + // In the signed data, we have: + private int sdv; // Version + private ContentInfo data; // The data to be digested + private EnvelopedData sded; // Enveloped data inside of signed data + private byte[] signerCertBytes; + org.mozilla.jss.pkix.cert.Certificate signerCert; + + private SET sis; // set of SignerInfos + private SignerInfo si; // First SignerInfo + private AlgorithmIdentifier digestAlgorithmId = null; + private int siv; // Version + private SET aa; // Authenticated Attributes + private SET aa_old; // Authenticated Attributes + private IssuerAndSerialNumber sgnIASN; // Signer's Issuer Name and Serialnum + private OCTET_STRING aa_digest; // digest of the authenticated attrs + + private String messageType; // these are all authenticated attributes + private String failInfo; + private String pkiStatus; + private String transactionID; + private byte[] senderNonce; + private byte[] recipientNonce; + private OCTET_STRING msg_digest; // digest of the message + + // Inside the sded Enveloped data + private RecipientInfo ri; // First RecipientInfo + private int riv; // Version + private AlgorithmIdentifier riAlgid; // alg that the bulk key is wrapped with + private byte[] riKey; // bulk key, wrapped with above algorithm + private byte[] cKey; // * 'clear', unwrapped key (not in ASN.1) * + private IssuerAndSerialNumber rcpIASN; // Recipient's Issuer Name and Serial Number + + private EncryptedContentInfo eci; + private byte[] iv; // initialization vector for above key + private byte[] ec; // encrypted content (P10, in case of request) + private byte[] cc; // * 'clear' content (not in ASN.1) * + private String encryptionAlgorithm = null; + + // For the CertRep, the enveloped content is another signed Data: + private SignedData crsd; + private int rsdVersion; + private byte[] rsdCert; // certificate to send in response + + private PKCS10 myP10; + + private Hashtable<String, Object> attrs; // miscellanous + + // *** END *** // + + public void debug() { + } + + public void put(String a, Object b) { + attrs.put(a, b); + } + + public Object get(Object a) { + return attrs.get(a); + } + + private SignatureAlgorithm getSignatureAlgorithm(String hashAlgorithm) { + SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSASignatureWithMD5Digest; + if (hashAlgorithm != null) { + if (hashAlgorithm.equals("SHA1")) { + signatureAlgorithm = SignatureAlgorithm.RSASignatureWithSHA1Digest; + } else if (hashAlgorithm.equals("SHA256")) { + signatureAlgorithm = SignatureAlgorithm.RSASignatureWithSHA256Digest; + } else if (hashAlgorithm.equals("SHA512")) { + signatureAlgorithm = SignatureAlgorithm.RSASignatureWithSHA512Digest; + } + } + return signatureAlgorithm; + } + + private OBJECT_IDENTIFIER getAlgorithmOID(String hashAlgorithm) { + OBJECT_IDENTIFIER oid = MD5_DIGEST; + if (hashAlgorithm != null) { + if (hashAlgorithm.equals("SHA1")) { + oid = SHA1_DIGEST; + } else if (hashAlgorithm.equals("SHA256")) { + oid = SHA256_DIGEST; + } else if (hashAlgorithm.equals("SHA512")) { + oid = SHA512_DIGEST; + } + } + return oid; + } + + // getHashAlgorithm is added to work around issue 636217 + private String getHashAlgorithm(OBJECT_IDENTIFIER algorithmOID) { + String hashAlgorithm = null; + if (algorithmOID != null) { + if (algorithmOID.equals(MD5_DIGEST)) { + hashAlgorithm = "MD5"; + } else if (algorithmOID.equals(SHA1_DIGEST)) { + hashAlgorithm = "SHA1"; + } else if (algorithmOID.equals(SHA256_DIGEST)) { + hashAlgorithm = "SHA256"; + } else if (algorithmOID.equals(SHA512_DIGEST)) { + hashAlgorithm = "SHA512"; + } + } + return hashAlgorithm; + } + + // These functions are used to initialize the various blobs + + public void makeSignedData(int version, + byte[] certificate, String hashAlgorithm) { + + try { + SET digest_algs = new SET(); + + digest_algs.addElement(new AlgorithmIdentifier(getAlgorithmOID(hashAlgorithm), new NULL())); + + // SET certs = new SET(); + // certs.addElement(new ANY(certificate)); + + SET sis = new SET(); + + sis.addElement(si); + + ContentInfo data = this.data; + + this.sd = new SignedData( + digest_algs, + data, + null, // don't send the certs, he already has them + null, // crl's + sis); + + } catch (Exception e) { + } + } + + public byte[] getResponse() throws IOException, InvalidBERException { + + crsci = new ContentInfo(ContentInfo.SIGNED_DATA, + sd); + + return ASN1Util.encode(crsci); + + // ANY a = crsci.getContent(); + // return a.getEncoded(); + } + + /* + public void makeSignerInfo_old(int version, + // issuer and serialnumber + byte[] digest) { + + si = new SignerInfo(new INTEGER(version), + sgnIASN, // issuer and serialnum + new AlgorithmIdentifier(MD5_DIGEST, new NULL()), // digest algorithm + this.aa, // Authenticated Attributes + new AlgorithmIdentifier(RSA_ENCRYPTION,new NULL()), // digest encryption algorithm + new OCTET_STRING(digest), // digest + null); // unauthenticated attributes + + } + */ + + public void makeSignerInfo(int version, + // issuer and serialnumber + org.mozilla.jss.crypto.PrivateKey pk, String hashAlgorithm) + throws java.security.NoSuchAlgorithmException, + TokenException, + java.security.InvalidKeyException, + java.security.SignatureException, + org.mozilla.jss.CryptoManager.NotInitializedException { + + si = new SignerInfo(sgnIASN, // issuer and serialnum + this.aa, // Authenticated Attributes + null, // Unauthenticated Attrs + ContentInfo.ENVELOPED_DATA, // content type + msg_digest.toByteArray(), // digest + getSignatureAlgorithm(hashAlgorithm), + pk); + } + + public void makeAuthenticatedAttributes() { + + aa = new SET(); + + try { + if (transactionID != null) { + SET tidset = new SET(); + + tidset.addElement((new PrintableString(transactionID))); + aa.addElement(new Attribute(CRS_TRANSID, tidset)); + } + + if (pkiStatus != null) { + SET pkistatusset = new SET(); + + pkistatusset.addElement(new PrintableString(pkiStatus)); + aa.addElement(new Attribute(CRS_PKISTATUS, pkistatusset)); + } + + if (messageType != null) { + SET aaset = new SET(); + + aaset.addElement(new PrintableString(messageType)); + aa.addElement(new Attribute(CRS_MESSAGETYPE, aaset)); + } + + if (failInfo != null) { + SET fiset = new SET(); + + fiset.addElement(new PrintableString(failInfo)); + aa.addElement(new Attribute(CRS_FAILINFO, fiset)); + } + + if (senderNonce != null) { + SET snset = new SET(); + + snset.addElement(new OCTET_STRING(senderNonce)); + aa.addElement(new Attribute(CRS_SENDERNONCE, snset)); + } + + if (recipientNonce != null) { + SET rnset = new SET(); + + rnset.addElement(new OCTET_STRING(recipientNonce)); + aa.addElement(new Attribute(CRS_RECIPIENTNONCE, rnset)); + } + + // XXX sender nonce + + } catch (CharConversionException e) { + } + } + + public byte[] makeEnvelopedData(int version) { + + byte[] r; + + try { + + if (this.ri != null) { + ContentInfo ci; + + SET ris = new SET(); + + ris.addElement(this.ri); + + this.sded = new EnvelopedData( + new INTEGER(version), + ris, + eci); + + ci = new ContentInfo(ContentInfo.ENVELOPED_DATA, + sded); + ByteArrayOutputStream ba = new ByteArrayOutputStream(); + + ci.encode(ba); + r = ba.toByteArray(); + } else { + r = new byte[0]; + } + + this.data = new ContentInfo(ContentInfo.DATA, + new OCTET_STRING(r)); + + return r; + + // return this.sded.getEncodedContents(); + } catch (Exception e) { + return null; + } + + } + + public void makeRecipientInfo(int version, byte[] riKey) { + this.riv = version; + + this.riAlgid = new AlgorithmIdentifier(RSA_ENCRYPTION, new NULL()); + this.riKey = riKey; + + this.ri = new RecipientInfo( + new INTEGER(this.riv), + rcpIASN, + this.riAlgid, + new OCTET_STRING(this.riKey) + ); + } + + public void makeEncryptedContentInfo(byte[] iv, byte[] ec, String algorithm) { + this.iv = iv; + this.ec = ec; + + try { + OBJECT_IDENTIFIER oid = DES_CBC_ENCRYPTION; + if (algorithm != null && algorithm.equals("DES3")) + oid = DES_EDE3_CBC_ENCRYPTION; + + AlgorithmIdentifier aid = new AlgorithmIdentifier(oid, new OCTET_STRING(iv)); + + //eci = EncryptedContentInfo.createCRSCompatibleEncryptedContentInfo( + eci = new EncryptedContentInfo(ContentInfo.DATA, + aid, + new OCTET_STRING(ec) + ); + + } catch (Exception e) { + } + } + + public byte[] makeSignedRep(int v, byte[] certificate) { + rsdVersion = v; + rsdCert = certificate; + try { + SET certs = new SET(); + ANY cert = new ANY(certificate); + + certs.addElement(cert); + + crsd = new SignedData( + new SET(), // empty set of digestAlgorithmID's + new ContentInfo( + new OBJECT_IDENTIFIER(new long[] { 1, 2, 840, 113549, 1, 7, 1 } + ), + null), //empty content + certs, + null, // no CRL's + new SET() // empty SignerInfos + ); + ContentInfo wrap = new ContentInfo(ContentInfo.SIGNED_DATA, + crsd); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + wrap.encode(baos); + + return baos.toByteArray(); + // return crsd.getEncodedContents(); + } catch (Exception e) { + return null; + } + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("{ messageType="); + sb.append(getMessageType()); + sb.append(", failInfo="); + sb.append(getFailInfo()); + sb.append(", pkiStatus="); + sb.append(getPKIStatus()); + sb.append(", transactionID="); + sb.append(getTransactionID()); + sb.append(", senderNonce="); + sb.append(Arrays.toString(getSenderNonce())); + sb.append(", recipientNonce="); + sb.append(Arrays.toString(getRecipientNonce())); + sb.append(" }"); + + String s = sb.toString(); + return s; + } + + public String getMessageType() { + return messageType; + } + + public String getFailInfo() { + return failInfo; + } + + public String getPKIStatus() { + return pkiStatus; + } + + public String getTransactionID() { + return transactionID; + } + + public byte[] getSenderNonce() { + return senderNonce; + } + + public byte[] getRecipientNonce() { + return recipientNonce; + } + + public byte[] getWrappedKey() { + return riKey; + } + + public byte[] getEncryptedPkcs10() { + return ec; + } + + public byte[] getIV() { + return iv; + } + + public String getEncryptionAlgorithm() { + return encryptionAlgorithm; + } + + public String getDigestAlgorithmName() { + String name = null; + if (digestAlgorithmId != null) { + name = getHashAlgorithm(digestAlgorithmId.getOID()); + } + return name; + } + + public PublicKey getSignerPublicKey() { + try { + + org.mozilla.jss.pkix.cert.Certificate.Template ct = new + org.mozilla.jss.pkix.cert.Certificate.Template(); + + ByteArrayInputStream bais = new ByteArrayInputStream(this.signerCertBytes); + + signerCert = (org.mozilla.jss.pkix.cert.Certificate) ct.decode(bais); + return signerCert.getInfo().getSubjectPublicKeyInfo().toPublicKey(); + } catch (Exception e) { + return null; + } + } + + public byte[] getAA() { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + aa.encode(baos); + return baos.toByteArray(); + } catch (Exception e) { + return null; + } + + } + + public void setAA_old(SET auth_attrs) { + aa_old = auth_attrs; + } + + // SWP + public byte[] getAA_old() { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + aa_old.encode(baos); + return baos.toByteArray(); + } catch (Exception e) { + return null; + } + + } + + public byte[] getAADigest() { + return aa_digest.toByteArray(); + } + + public PKCS10 getP10() { + return myP10; + } + + public void setP10(PKCS10 p10) { + myP10 = p10; + } + + public void setSgnIssuerAndSerialNumber(IssuerAndSerialNumber iasn) { + this.sgnIASN = iasn; + } + + public void setRcpIssuerAndSerialNumber(IssuerAndSerialNumber iasn) { + this.rcpIASN = iasn; + } + + public IssuerAndSerialNumber getSgnIssuerAndSerialNumber() { + return this.sgnIASN; + } + + public IssuerAndSerialNumber getRcpIssuerAndSerialNumber() { + return this.rcpIASN; + } + + public void setMessageType(String messageType) { + this.messageType = messageType; + } + + public void setPKIStatus(String pkiStatus) { + this.pkiStatus = pkiStatus; + } + + public void setFailInfo(String failInfo) { + this.failInfo = failInfo; + } + + public void setTransactionID(String tid) { + this.transactionID = tid; + } + + public void setRecipientNonce(byte[] rn) { + this.recipientNonce = rn; + } + + public void setSenderNonce(byte[] sn) { + this.senderNonce = sn; + } + + // public void setCertificate(byte [] cert) { this.certificate = cert; } + + public void setMsgDigest(byte[] digest) { + this.msg_digest = new OCTET_STRING(digest); + } + + public void setAADigest(byte[] digest) { + this.aa_digest = new OCTET_STRING(digest); + } + + public void setPending() { + // setIssuerAndSerialNumber(); + + setMessageType(mType_CertRep); + setPKIStatus(mStatus_PENDING); + }; + + public void setFailure(String failInfo) { + setMessageType(mType_CertRep); + setPKIStatus(mStatus_FAILURE); + setFailInfo(failInfo); + } + + // Should add a Certificate to this call + public void setSuccess() { + setMessageType(mType_CertRep); + setPKIStatus(mStatus_SUCCESS); + } + + /** + * Gets a byte array which is the der-encoded blob + * which gets sent back to the router. + */ + + public byte[] getEncoded() { + //Assert.assert(messageType != null); + //Assert.assert(pkiStatus != null); + + return new byte[1]; // blagh + } + + private void decodeCRSPKIMessage(ByteArrayInputStream bais) throws InvalidBERException, Exception { + + org.mozilla.jss.pkcs7.ContentInfo.Template crscit; + + crscit = new ContentInfo.Template(); + crsci = (ContentInfo) crscit.decode(bais); + + if (!ContentInfo.SIGNED_DATA.equals(crsci.getContentType())) { + throw new Exception("ContentType wasn't signed data, it was" + crsci.getContentType()); + } + + // Now that we know that the contentInfo is a SignedData, we can decode it + SignedData.Template sdt = new SignedData.Template(); + + sd = (SignedData) sdt.decode( + new ByteArrayInputStream( + ((ANY) crsci.getContent()).getEncoded() + )); + this.decodeSD(); + } + + public CRSPKIMessage() { + attrs = new Hashtable<String, Object>(); + } + + public CRSPKIMessage(ByteArrayInputStream bais) throws InvalidBERException, Exception { + attrs = new Hashtable<String, Object>(); + decodeCRSPKIMessage(bais); + } + + private void decodeSD() throws Exception { + ContentInfo sdci; + + sis = sd.getSignerInfos(); + + decodeSI(); + + sdci = sd.getContentInfo(); + + // HACK to work with CRS + ANY a = (ANY) sdci.getContent(); + ByteArrayInputStream s = new ByteArrayInputStream(a.getEncoded()); + OCTET_STRING os = (OCTET_STRING) (new OCTET_STRING.Template()).decode(s); + + ByteArrayInputStream s2 = new ByteArrayInputStream(os.toByteArray()); + ContentInfo ci = (ContentInfo) (new ContentInfo.Template()).decode(s2); + ByteArrayInputStream s3 = new ByteArrayInputStream(((ANY) ci.getContent()).getEncoded()); + + EnvelopedData.Template edt = new EnvelopedData.Template(); + + sded = (EnvelopedData) edt.decode(s3); + + SET signerCerts = (SET) sd.getCertificates(); + Certificate firstCert = (Certificate) signerCerts.elementAt(0); + + signerCertBytes = ASN1Util.encode(firstCert); + + CertificateInfo firstCertInfo = firstCert.getInfo(); + + sgnIASN = new IssuerAndSerialNumber(firstCertInfo.getIssuer(), + firstCertInfo.getSerialNumber()); + + decodeED(); + } + + private void decodeSI() throws Exception { + if (sis.size() == 0) { + throw new Exception("SignerInfos is empty"); + } + si = (SignerInfo) sis.elementAt(0); + + digestAlgorithmId = si.getDigestAlgorithmIdentifer(); + + decodeAA(); + + aa_digest = new OCTET_STRING(si.getEncryptedDigest()); + } + + private void decodeED() throws Exception { + SET ris; + + ris = (SET) sded.getRecipientInfos(); + + if (ris.size() == 0) { + throw new Exception("RecipientInfos is empty"); + } + ri = (RecipientInfo) ris.elementAt(0); + eci = sded.getEncryptedContentInfo(); + + if (eci.getContentEncryptionAlgorithm().getOID().equals(DES_EDE3_CBC_ENCRYPTION)) { + encryptionAlgorithm = "DES3"; + } else if (eci.getContentEncryptionAlgorithm().getOID().equals(DES_CBC_ENCRYPTION)) { + encryptionAlgorithm = "DES"; + } else { + throw new Exception("P10 encrypted alg is not supported (not DES): " + + eci.getContentEncryptionAlgorithm().getOID()); + } + + ec = eci.getEncryptedContent().toByteArray(); + + OCTET_STRING.Template ost = new OCTET_STRING.Template(); + + OCTET_STRING os = (OCTET_STRING) + ost.decode(new ByteArrayInputStream( + ((ANY) eci.getContentEncryptionAlgorithm().getParameters()).getEncoded() + ) + ); + + iv = os.toByteArray(); + + decodeRI(); + } + + /** + * The PKCS10 request is encrypt with a symmetric key. + * This key in turn is encrypted with the RSA key in the + * CA certificate. + * + * riAlgid is the algorithm the symm key is encrypted with. It had + * better be RSA + * riKey is the encrypted symmetric key + */ + + private void decodeRI() throws Exception { + + // really should get issuer and serial number of our RI, as this + // indicates the key we should use to decrypt with. However, we're just + // going to assume that the key is the Signing cert for the server. + + riAlgid = ri.getKeyEncryptionAlgorithmID(); + + if (!riAlgid.getOID().equals(RSA_ENCRYPTION)) { + throw new Exception("Request is protected by a key which we can't decrypt"); + } + + riKey = ri.getEncryptedKey().toByteArray(); + + } + + private void decodeAA() throws InvalidBERException, IOException { + aa = si.getAuthenticatedAttributes(); + + int count; + + for (count = 0; count < aa.size(); count++) { + Attribute a = (Attribute) aa.elementAt(count); + SET s = (SET) a.getValues(); + ANY f = (ANY) s.elementAt(0); + PrintableString ps; + PrintableString.Template pst = new PrintableString.Template(); + OCTET_STRING.Template ost = new OCTET_STRING.Template(); + + OBJECT_IDENTIFIER oid = a.getType(); + + if (oid.equals(CRS_MESSAGETYPE)) { + ps = (PrintableString) pst.decode(new ByteArrayInputStream(f.getEncoded())); + // We make a new string here + messageType = ps.toString(); + + } else if (oid.equals(CRS_PKISTATUS)) { + ps = (PrintableString) pst.decode(new ByteArrayInputStream(f.getEncoded())); + pkiStatus = new String(ps.toString()); + } else if (oid.equals(CRS_FAILINFO)) { + ps = (PrintableString) pst.decode(new ByteArrayInputStream(f.getEncoded())); + failInfo = new String(ps.toString()); + } else if (oid.equals(CRS_SENDERNONCE)) { + OCTET_STRING oss = (OCTET_STRING) ost.decode(new ByteArrayInputStream(f.getEncoded())); + + senderNonce = oss.toByteArray(); + } else if (oid.equals(CRS_RECIPIENTNONCE)) { + OCTET_STRING osr = (OCTET_STRING) ost.decode(new ByteArrayInputStream(f.getEncoded())); + + recipientNonce = osr.toByteArray(); + } else if (oid.equals(CRS_TRANSID)) { + ps = (PrintableString) pst.decode(new ByteArrayInputStream(f.getEncoded())); + transactionID = new String(ps.toString()); + } + + } + + } // end of decodeAA(); + + public String getMessageTypeString() { + if (messageType == null) { + return null; + } + + if (messageType.equals(mType_PKCSReq)) { + return "PKCSReq"; + } + if (messageType.equals(mType_CertRep)) { + return "CertRep"; + } + if (messageType.equals(mType_GetCertInitial)) { + return "GetCertInitial"; + } + if (messageType.equals(mType_GetCert)) { + return "GetCert"; + } + if (messageType.equals(mType_GetCRL)) { + return "GetCRL"; + } + // messageType should match one of the above + //Assert.assert(false); + return null; + } +} diff --git a/base/util/src/com/netscape/cmsutil/util/Cert.java b/base/util/src/com/netscape/cmsutil/util/Cert.java new file mode 100644 index 000000000..3563f70c7 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/util/Cert.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 com.netscape.cmsutil.util; + +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; + +import netscape.security.pkcs.PKCS7; +import netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509CertImpl; + +import org.mozilla.jss.crypto.SignatureAlgorithm; + +public class Cert { + + 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("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("-----BEGIN CERTIFICATE-----")) && + (s.endsWith("-----END CERTIFICATE-----"))) { + 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) { + String val = ""; + + 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 += s.charAt(i); + } + return val; + } + + public static String normalizeCertStrAndReq(String s) { + String val = ""; + + 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 += s.charAt(i); + } + return val; + } +} diff --git a/base/util/src/com/netscape/cmsutil/util/Fmt.java b/base/util/src/com/netscape/cmsutil/util/Fmt.java new file mode 100644 index 000000000..a24b8d090 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/util/Fmt.java @@ -0,0 +1,605 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.util; + +// Fmt - some simple single-arg sprintf-like routines +// +// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// +// Visit the ACME Labs Java page for up-to-date versions of this and other +// fine Java utilities: http://www.acme.com/java/ + +/// Some simple single-arg sprintf-like routines. +// <P> +// It is apparently impossible to declare a Java method that accepts +// variable numbers of any type of argument. You can declare it to take +// Objects, but numeric variables and constants are not in fact Objects. +// <P> +// However, using the built-in string concatenation, it's almost as +// convenient to make a series of single-argument formatting routines. +// <P> +// Fmt can format the following types: +// <BLOCKQUOTE><CODE> +// byte short int long float double char String Object +// </CODE></BLOCKQUOTE> +// For each type there is a set of overloaded methods, each returning +// a formatted String. There's the plain formatting version: +// <BLOCKQUOTE><PRE> +// Fmt.fmt( x ) +// </PRE></BLOCKQUOTE> +// There's a version specifying a minimum field width: +// <BLOCKQUOTE><PRE> +// Fmt.fmt( x, minWidth ) +// </PRE></BLOCKQUOTE> +// And there's a version that takes flags: +// <BLOCKQUOTE><PRE> +// Fmt.fmt( x, minWidth, flags ) +// </PRE></BLOCKQUOTE> +// Currently available flags are: +// <BLOCKQUOTE><PRE> +// Fmt.ZF - zero-fill +// Fmt.LJ - left justify +// Fmt.HX - hexadecimal +// Fmt.OC - octal +// </PRE></BLOCKQUOTE> +// The HX and OC flags imply unsigned output. +// <P> +// For doubles and floats, there's a significant-figures parameter before +// the flags: +// <BLOCKQUOTE><PRE> +// Fmt.fmt( d ) +// Fmt.fmt( d, minWidth ) +// Fmt.fmt( d, minWidth, sigFigs ) +// Fmt.fmt( d, minWidth, sigFigs, flags ) +// </PRE></BLOCKQUOTE> +// <P> +// <A HREF="/resources/classes/Acme/Fmt.java">Fetch the software.</A><BR> +// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A> +// <HR> +// Similar classes: +// <UL> +// <LI> Andrew Scherpbier's <A HREF="http://www.sdsu.edu/doc/java-SDSU/sdsu.FormatString.html">FormatString</A> +// Tries to allow variable numbers of arguments by +// supplying overloaded routines with different combinations of parameters, +// but doesn't actually supply that many. The floating point conversion +// is described as "very incomplete". +// <LI> Core Java's <A HREF="http://www.apl.jhu.edu/~hall/java/CoreJava-Format.html">Format</A>. +// The design seems a little weird. They want you to create an instance, +// passing the format string to the constructor, and then call an instance +// method with your data to do the actual formatting. The extra steps are +// pointless; better to just use static methods. +// </UL> + +public class Fmt { + + // Flags. + /// Zero-fill. + public static final int ZF = 1; + /// Left justify. + public static final int LJ = 2; + /// Hexadecimal. + public static final int HX = 4; + /// Octal. + public static final int OC = 8; + // Was a number - internal use. + private static final int WN = 16; + + // byte + public static String fmt(byte b) { + return fmt(b, 0, 0); + } + + public static String fmt(byte b, int minWidth) { + return fmt(b, minWidth, 0); + } + + public static String fmt(byte b, int minWidth, int flags) { + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + + if (hexadecimal) + return fmt(Integer.toString(b & 0xff, 16), minWidth, flags | WN); + else if (octal) + return fmt(Integer.toString(b & 0xff, 8), minWidth, flags | WN); + else + return fmt(Integer.toString(b & 0xff), minWidth, flags | WN); + } + + // short + public static String fmt(short s) { + return fmt(s, 0, 0); + } + + public static String fmt(short s, int minWidth) { + return fmt(s, minWidth, 0); + } + + public static String fmt(short s, int minWidth, int flags) { + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + + if (hexadecimal) + return fmt( + Integer.toString(s & 0xffff, 16), minWidth, flags | WN); + else if (octal) + return fmt( + Integer.toString(s & 0xffff, 8), minWidth, flags | WN); + else + return fmt(Integer.toString(s), minWidth, flags | WN); + } + + // int + public static String fmt(int i) { + return fmt(i, 0, 0); + } + + public static String fmt(int i, int minWidth) { + return fmt(i, minWidth, 0); + } + + public static String fmt(int i, int minWidth, int flags) { + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + + if (hexadecimal) + return fmt( + Long.toString(i & 0xffffffffL, 16), minWidth, flags | WN); + else if (octal) + return fmt( + Long.toString(i & 0xffffffffL, 8), minWidth, flags | WN); + else + return fmt(Integer.toString(i), minWidth, flags | WN); + } + + // long + public static String fmt(long l) { + return fmt(l, 0, 0); + } + + public static String fmt(long l, int minWidth) { + return fmt(l, minWidth, 0); + } + + public static String fmt(long l, int minWidth, int flags) { + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + + if (hexadecimal) { + if ((l & 0xf000000000000000L) != 0) + return fmt( + Long.toString(l >>> 60, 16) + + fmt(l & 0x0fffffffffffffffL, 15, HX | ZF), + minWidth, flags | WN); + else + return fmt(Long.toString(l, 16), minWidth, flags | WN); + } else if (octal) { + if ((l & 0x8000000000000000L) != 0) + return fmt( + Long.toString(l >>> 63, 8) + + fmt(l & 0x7fffffffffffffffL, 21, OC | ZF), + minWidth, flags | WN); + else + return fmt(Long.toString(l, 8), minWidth, flags | WN); + } else + return fmt(Long.toString(l), minWidth, flags | WN); + } + + // float + public static String fmt(float f) { + return fmt(f, 0, 0, 0); + } + + public static String fmt(float f, int minWidth) { + return fmt(f, minWidth, 0, 0); + } + + public static String fmt(float f, int minWidth, int sigFigs) { + return fmt(f, minWidth, sigFigs, 0); + } + + public static String fmt(float f, int minWidth, int sigFigs, int flags) { + if (sigFigs != 0) + return fmt( + sigFigFix(Float.toString(f), sigFigs), minWidth, + flags | WN); + else + return fmt(Float.toString(f), minWidth, flags | WN); + } + + // double + public static String fmt(double d) { + return fmt(d, 0, 0, 0); + } + + public static String fmt(double d, int minWidth) { + return fmt(d, minWidth, 0, 0); + } + + public static String fmt(double d, int minWidth, int sigFigs) { + return fmt(d, minWidth, sigFigs, 0); + } + + public static String fmt(double d, int minWidth, int sigFigs, int flags) { + if (sigFigs != 0) + return fmt( + sigFigFix(doubleToString(d), sigFigs), minWidth, + flags | WN); + else + return fmt(doubleToString(d), minWidth, flags | WN); + } + + // char + public static String fmt(char c) { + return fmt(c, 0, 0); + } + + public static String fmt(char c, int minWidth) { + return fmt(c, minWidth, 0); + } + + public static String fmt(char c, int minWidth, int flags) { + // return fmt( Character.toString( c ), minWidth, flags ); + // Character currently lacks a static toString method. Workaround + // is to make a temporary instance and use the instance toString. + return fmt(Character.valueOf(c).toString(), minWidth, flags); + } + + // Object + public static String fmt(Object o) { + return fmt(o, 0, 0); + } + + public static String fmt(Object o, int minWidth) { + return fmt(o, minWidth, 0); + } + + public static String fmt(Object o, int minWidth, int flags) { + return fmt(o.toString(), minWidth, flags); + } + + // String + public static String fmt(String s) { + return fmt(s, 0, 0); + } + + public static String fmt(String s, int minWidth) { + return fmt(s, minWidth, 0); + } + + public static String fmt(String s, int minWidth, int flags) { + int len = s.length(); + boolean zeroFill = ((flags & ZF) != 0); + boolean leftJustify = ((flags & LJ) != 0); + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + boolean wasNumber = ((flags & WN) != 0); + + if ((hexadecimal || octal || zeroFill) && !wasNumber) + throw new InternalError("Acme.Fmt: number flag on a non-number"); + if (zeroFill && leftJustify) + throw new InternalError("Acme.Fmt: zero-fill left-justify is silly"); + if (hexadecimal && octal) + throw new InternalError("Acme.Fmt: can't do both hex and octal"); + if (len >= minWidth) + return s; + int fillWidth = minWidth - len; + StringBuffer fill = new StringBuffer(fillWidth); + + for (int i = 0; i < fillWidth; ++i) + if (zeroFill) + fill.append('0'); + else + fill.append(' '); + if (leftJustify) + return s + fill; + else if (zeroFill && s.startsWith("-")) + return "-" + fill + s.substring(1); + else + return fill + s; + } + + // Internal routines. + + private static String sigFigFix(String s, int sigFigs) { + // First dissect the floating-point number string into sign, + // integer part, fraction part, and exponent. + String sign; + String unsigned; + + if (s.startsWith("-") || s.startsWith("+")) { + sign = s.substring(0, 1); + unsigned = s.substring(1); + } else { + sign = ""; + unsigned = s; + } + String mantissa; + String exponent; + int eInd = unsigned.indexOf('e'); + + if (eInd == -1) { + mantissa = unsigned; + exponent = ""; + } else { + mantissa = unsigned.substring(0, eInd); + exponent = unsigned.substring(eInd); + } + StringBuffer number, fraction; + int dotInd = mantissa.indexOf('.'); + + if (dotInd == -1) { + number = new StringBuffer(mantissa); + fraction = new StringBuffer(""); + } else { + number = new StringBuffer(mantissa.substring(0, dotInd)); + fraction = new StringBuffer(mantissa.substring(dotInd + 1)); + } + + int numFigs = number.length(); + int fracFigs = fraction.length(); + + if ((numFigs == 0 || number.toString().equals("0")) && + fracFigs > 0) { + // Don't count leading zeros in the fraction. + numFigs = 0; + for (int i = 0; i < fraction.length(); ++i) { + if (fraction.charAt(i) != '0') + break; + --fracFigs; + } + } + int mantFigs = numFigs + fracFigs; + + if (sigFigs > mantFigs) { + // We want more figures; just append zeros to the fraction. + for (int i = mantFigs; i < sigFigs; ++i) + fraction.append('0'); + } else if (sigFigs < mantFigs && sigFigs >= numFigs) { + // Want fewer figures in the fraction; chop. + fraction.setLength( + fraction.length() - (fracFigs - (sigFigs - numFigs))); + // Round? + } else if (sigFigs < numFigs) { + // Want fewer figures in the number; turn them to zeros. + fraction.setLength(0); // should already be zero, but make sure + for (int i = sigFigs; i < numFigs; ++i) + number.setCharAt(i, '0'); + // Round? + } + // Else sigFigs == mantFigs, which is fine. + + if (fraction.length() == 0) + return sign + number + exponent; + else + return sign + number + "." + fraction + exponent; + } + + /// Improved version of Double.toString(), returns more decimal places. + // <P> + // The JDK 1.0.2 version of Double.toString() returns only six decimal + // places on some systems. In JDK 1.1 full precision is returned on + // all platforms. + // @deprecated + // @see java.lang.Double.toString + public static String doubleToString(double d) { + // Handle special numbers first, to avoid complications. + if (Double.isNaN(d)) + return "NaN"; + if (d == Double.NEGATIVE_INFINITY) + return "-Inf"; + if (d == Double.POSITIVE_INFINITY) + return "Inf"; + + // Grab the sign, and then make the number positive for simplicity. + boolean negative = false; + + if (d < 0.0D) { + negative = true; + d = -d; + } + + // Get the native version of the unsigned value, as a template. + String unsStr = Double.toString(d); + + // Dissect out the exponent. + String mantStr, expStr; + int exp; + int eInd = unsStr.indexOf('e'); + + if (eInd == -1) { + mantStr = unsStr; + expStr = ""; + exp = 0; + } else { + mantStr = unsStr.substring(0, eInd); + expStr = unsStr.substring(eInd + 1); + if (expStr.startsWith("+")) + exp = Integer.parseInt(expStr.substring(1)); + else + exp = Integer.parseInt(expStr); + } + + // Dissect out the number part. + String numStr; + int dotInd = mantStr.indexOf('.'); + + if (dotInd == -1) + numStr = mantStr; + else + numStr = mantStr.substring(0, dotInd); + long num; + + if (numStr.length() == 0) + num = 0; + else + num = Integer.parseInt(numStr); + + // Build the new mantissa. + StringBuffer newMantBuf = new StringBuffer(numStr + "."); + double p = Math.pow(10, exp); + double frac = d - num * p; + String digits = "0123456789"; + int nDigits = 16 - numStr.length(); // about 16 digits in a double + + for (int i = 0; i < nDigits; ++i) { + p /= 10.0D; + int dig = (int) (frac / p); + + if (dig < 0) + dig = 0; + if (dig > 9) + dig = 9; + newMantBuf.append(digits.charAt(dig)); + frac -= dig * p; + } + + if ((int) (frac / p + 0.5D) == 1) { + // Round up. + boolean roundMore = true; + + for (int i = newMantBuf.length() - 1; i >= 0; --i) { + int dig = digits.indexOf(newMantBuf.charAt(i)); + + if (dig == -1) + continue; + ++dig; + if (dig == 10) { + newMantBuf.setCharAt(i, '0'); + continue; + } + newMantBuf.setCharAt(i, digits.charAt(dig)); + roundMore = false; + break; + } + if (roundMore) { + // If this happens, we need to prepend a 1. But I haven't + // found a test case yet, so I'm leaving it out for now. + // But if you get this message, please let me know! + newMantBuf.append("ROUNDMORE"); + } + } + + // Chop any trailing zeros. + int len = newMantBuf.length(); + + while (newMantBuf.charAt(len - 1) == '0') + newMantBuf.setLength(--len); + // And chop a trailing dot, if any. + if (newMantBuf.charAt(len - 1) == '.') + newMantBuf.setLength(--len); + + // Done. + return (negative ? "-" : "") + + newMantBuf + + (expStr.length() != 0 ? ("e" + expStr) : ""); + } + + /****************************************************************************** + * /// Test program. + * public static void main( String[] args ) + * { + * System.out.println( "Starting tests." ); + * show( Fmt.fmt( "Hello there." ) ); + * show( Fmt.fmt( 123 ) ); + * show( Fmt.fmt( 123, 10 ) ); + * show( Fmt.fmt( 123, 10, Fmt.ZF ) ); + * show( Fmt.fmt( 123, 10, Fmt.LJ ) ); + * show( Fmt.fmt( -123 ) ); + * show( Fmt.fmt( -123, 10 ) ); + * show( Fmt.fmt( -123, 10, Fmt.ZF ) ); + * show( Fmt.fmt( -123, 10, Fmt.LJ ) ); + * show( Fmt.fmt( (byte) 0xbe, 22, Fmt.OC ) ); + * show( Fmt.fmt( (short) 0xbabe, 22, Fmt.OC ) ); + * show( Fmt.fmt( 0xcafebabe, 22, Fmt.OC ) ); + * show( Fmt.fmt( 0xdeadbeefcafebabeL, 22, Fmt.OC ) ); + * show( Fmt.fmt( 0x8000000000000000L, 22, Fmt.OC ) ); + * show( Fmt.fmt( (byte) 0xbe, 16, Fmt.HX ) ); + * show( Fmt.fmt( (short) 0xbabe, 16, Fmt.HX ) ); + * show( Fmt.fmt( 0xcafebabe, 16, Fmt.HX ) ); + * show( Fmt.fmt( 0xdeadbeefcafebabeL, 16, Fmt.HX ) ); + * show( Fmt.fmt( 0x8000000000000000L, 16, Fmt.HX ) ); + * show( Fmt.fmt( 'c' ) ); + * show( Fmt.fmt( new java.util.Date() ) ); + * show( Fmt.fmt( 123.456F ) ); + * show( Fmt.fmt( 123456000000000000.0F ) ); + * show( Fmt.fmt( 123.456F, 0, 8 ) ); + * show( Fmt.fmt( 123.456F, 0, 7 ) ); + * show( Fmt.fmt( 123.456F, 0, 6 ) ); + * show( Fmt.fmt( 123.456F, 0, 5 ) ); + * show( Fmt.fmt( 123.456F, 0, 4 ) ); + * show( Fmt.fmt( 123.456F, 0, 3 ) ); + * show( Fmt.fmt( 123.456F, 0, 2 ) ); + * show( Fmt.fmt( 123.456F, 0, 1 ) ); + * show( Fmt.fmt( 123456000000000000.0F, 0, 4 ) ); + * show( Fmt.fmt( -123.456F, 0, 4 ) ); + * show( Fmt.fmt( -123456000000000000.0F, 0, 4 ) ); + * show( Fmt.fmt( 123.0F ) ); + * show( Fmt.fmt( 123.0D ) ); + * show( Fmt.fmt( 1.234567890123456789F ) ); + * show( Fmt.fmt( 1.234567890123456789D ) ); + * show( Fmt.fmt( 1234567890123456789F ) ); + * show( Fmt.fmt( 1234567890123456789D ) ); + * show( Fmt.fmt( 0.000000000000000000001234567890123456789F ) ); + * show( Fmt.fmt( 0.000000000000000000001234567890123456789D ) ); + * show( Fmt.fmt( 12300.0F ) ); + * show( Fmt.fmt( 12300.0D ) ); + * show( Fmt.fmt( 123000.0F ) ); + * show( Fmt.fmt( 123000.0D ) ); + * show( Fmt.fmt( 1230000.0F ) ); + * show( Fmt.fmt( 1230000.0D ) ); + * show( Fmt.fmt( 12300000.0F ) ); + * show( Fmt.fmt( 12300000.0D ) ); + * show( Fmt.fmt( Float.NaN ) ); + * show( Fmt.fmt( Float.POSITIVE_INFINITY ) ); + * show( Fmt.fmt( Float.NEGATIVE_INFINITY ) ); + * show( Fmt.fmt( Double.NaN ) ); + * show( Fmt.fmt( Double.POSITIVE_INFINITY ) ); + * show( Fmt.fmt( Double.NEGATIVE_INFINITY ) ); + * show( Fmt.fmt( 1.0F / 8.0F ) ); + * show( Fmt.fmt( 1.0D / 8.0D ) ); + * System.out.println( "Done with tests." ); + * } + * + * private static void show( String str ) + * { + * System.out.println( "#" + str + "#" ); + * } + ******************************************************************************/ + +} diff --git a/base/util/src/com/netscape/cmsutil/util/HMACDigest.java b/base/util/src/com/netscape/cmsutil/util/HMACDigest.java new file mode 100644 index 000000000..09bf53bbf --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/util/HMACDigest.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 com.netscape.cmsutil.util; + +import java.security.MessageDigest; + +/** + * This class implements the HMAC algorithm specified in RFC 2104 using + * any MessageDigest. + * + * @author mikep + * @version $Revision$, $Date$ + * @see java.security.MessageDigest + */ +public class HMACDigest implements Cloneable { + public static final int PAD_BYTES = 64; + public static final int IPAD = 0x36; + public static final int OPAD = 0x5C; + + /** + * inner padding - key XORd with ipad + */ + private byte[] mKeyIpad = new byte[PAD_BYTES]; + + /** + * outer padding - key XORd with opad + */ + private byte[] mKeyOpad = new byte[PAD_BYTES]; + + /** + * The real MessageDigest + */ + private MessageDigest mMD = null; + + /** + * Creates an HMACDigest + * + * @param md The MessageDigest to be used for the HMAC calculation. It + * must be clonable. + */ + public HMACDigest(MessageDigest md) { + mMD = md; + } + + /** + * Creates an HMACDigest and initializes the HMAC function + * with the given key. + * + * @param md The MessageDigest to be used for the HMAC calculation. It + * must be clonable. + * @param key The key value to be used in the HMAC calculation + */ + public HMACDigest(MessageDigest md, byte[] key) { + this(md); + init(key); + } + + /** + * Return the MessageDigest used for this HMAC + */ + public MessageDigest getMessageDigest() { + return mMD; + } + + /** + * Initialize the HMAC function + * + * The HMAC transform looks like: + * + * hash(key XOR opad, hash(key XOR ipad, text)) + * + * where key is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + * + * This routine must be called after every reset. + * + * @param key The password used to protect the hash value + */ + public void init(byte[] key) { + int i; + + reset(); + + // If the key is longer than 64 bytes, just hash it down + if (key.length > 64) { + key = mMD.digest(key); + mMD.reset(); // Redundant? + } + + // Copy the key. Truncate if key is too long + for (i = 0; i < key.length && i < PAD_BYTES; i++) { + mKeyIpad[i] = key[i]; + mKeyOpad[i] = key[i]; + } + + // XOR in the pads + for (i = 0; i < PAD_BYTES; i++) { + mKeyIpad[i] ^= IPAD; + mKeyOpad[i] ^= OPAD; + } + + mMD.update(mKeyIpad); + + // Hmmm, we really shouldn't key Opad around in memory for so + // long, but it would just force the user to key their key around + // until digest() time. Oh well, at least clear the key and Ipad + for (i = 0; i < PAD_BYTES; i++) { + mKeyIpad[i] = 0; + } + for (i = 0; i < key.length; i++) { + key[0] = 0; + } + } + + /** + * Updates the digest using the specified array of bytes. + * + * @param input the array of bytes. + */ + public void update(byte[] input) { + mMD.update(input); + } + + /** + * Completes the HMAC computation with the outer pad + * The digest is reset after this call is made. + * + * @return the array of bytes for the resulting hash value. + */ + public byte[] digest() { + byte[] finalDigest; + byte[] innerDigest = mMD.digest(); + + mMD.reset(); // Redundant? + mMD.update(mKeyOpad); + mMD.update(innerDigest); + finalDigest = mMD.digest(); + reset(); // Clear pad arrays + return finalDigest; + } + + /** + * Resets the digest for further use. + */ + public void reset() { + int i; + + mMD.reset(); + + // Clear out the pads + for (i = 0; i < PAD_BYTES; i++) { + mKeyIpad[i] = 0; + mKeyOpad[i] = 0; + } + } + + /** + * Clone the HMACDigest + * + * @return a clone if the implementation is cloneable. + * @exception CloneNotSupportedException if this is called on a + * MessageDigest implementation that does not support <code>Cloneable</code>. + */ + public Object clone() throws CloneNotSupportedException { + int i; + + HMACDigest hd = (HMACDigest) super.clone(); + + hd.mKeyOpad = new byte[PAD_BYTES]; + hd.mKeyIpad = new byte[PAD_BYTES]; + + for (i = 0; i < PAD_BYTES; i++) { + hd.mKeyOpad[i] = mKeyOpad[i]; + hd.mKeyIpad[i] = mKeyIpad[i]; + } + + hd.mMD = (MessageDigest) mMD.clone(); + return hd; + } + +} diff --git a/base/util/src/com/netscape/cmsutil/util/Utils.java b/base/util/src/com/netscape/cmsutil/util/Utils.java new file mode 100644 index 000000000..303566416 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/util/Utils.java @@ -0,0 +1,276 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.util; + +import org.apache.commons.codec.binary.Base64; + +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.Vector; + +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 (Exception e) { + 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 = (int) 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 = (int) 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) { + try { + BufferedReader in = new BufferedReader(new FileReader(orig)); + PrintWriter out = new PrintWriter( + new BufferedWriter(new FileWriter(dest))); + String line = ""; + while (in.ready()) { + line = in.readLine(); + if (line != null) + out.println(line); + } + in.close(); + out.close(); + } catch (Exception ee) { + } + } + + 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<String> v) { + String s[] = new String[v.size()]; + + v.copyInto(s); + return s; + } + + public static String base64encode(byte[] bytes) { + String string = new Base64(64).encodeToString(bytes); + return string; + } + + public static byte[] base64decode(String string) { + byte[] bytes = Base64.decodeBase64(string); + return bytes; + } +} diff --git a/base/util/src/com/netscape/cmsutil/xml/XMLObject.java b/base/util/src/com/netscape/cmsutil/xml/XMLObject.java new file mode 100644 index 000000000..ed2fb67ee --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/xml/XMLObject.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 com.netscape.cmsutil.xml; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.util.Vector; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; +import org.xml.sax.SAXException; + +public class XMLObject { + private Document mDoc = null; + + public XMLObject() throws ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + mDoc = docBuilder.newDocument(); + } + + public XMLObject(InputStream s) + throws SAXException, IOException, ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + mDoc = docBuilder.parse(s); + } + + public XMLObject(File f) + throws SAXException, IOException, ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + mDoc = docBuilder.parse(f); + } + + public Document getDocument() { + return mDoc; + } + + /** + * Each document should have 1 root only. This method should be called once. + */ + public Node createRoot(String name) { + Element root = mDoc.createElement(name); + mDoc.appendChild(root); + return (Node) root; + } + + public Node getRoot() { + return mDoc.getFirstChild(); + } + + /** + * If you have duplicate containers, then this method will return the + * first container in the list. + */ + public Node getContainer(String tagname) { + NodeList list = mDoc.getElementsByTagName(tagname); + if (list.getLength() > 0) + return list.item(0); + return null; + } + + public Node createContainer(Node containerParent, String containerName) { + Element node = mDoc.createElement(containerName); + containerParent.appendChild(node); + return (Node) node; + } + + public void addItemToContainer(Node container, String tagname, String value) { + Element node = mDoc.createElement(tagname); + Text text = mDoc.createTextNode(value); + node.appendChild(text); + container.appendChild(node); + } + + public String getValue(String tagname) { + Node n = getContainer(tagname); + + if (n != null) { + NodeList c = n.getChildNodes(); + if (c.getLength() == 0) + return null; + Node item = c.item(0); + return item.getNodeValue(); + } + + return null; + } + + public Vector<String> getAllValues(String tagname) { + Vector<String> v = new Vector<String>(); + NodeList nodes = mDoc.getElementsByTagName(tagname); + for (int i = 0; i < nodes.getLength(); i++) { + Node n = nodes.item(i); + NodeList c = n.getChildNodes(); + if (c.getLength() > 0) { + Node nn = c.item(0); + if (nn.getNodeType() == Node.TEXT_NODE) + v.addElement(nn.getNodeValue()); + } + } + return v; + } + + public Vector<String> getValuesFromContainer(Node container, String tagname) { + Vector<String> v = new Vector<String>(); + NodeList c = container.getChildNodes(); + int len = c.getLength(); + for (int i = 0; i < len; i++) { + Node subchild = c.item(i); + if (subchild.getNodeName().equals(tagname)) { + NodeList grandchildren = subchild.getChildNodes(); + if (grandchildren.getLength() > 0) { + Node grandchild = grandchildren.item(0); + if (grandchild.getNodeType() == Node.TEXT_NODE) + v.addElement(grandchild.getNodeValue()); + } + } + } + + return v; + } + + public byte[] toByteArray() throws TransformerConfigurationException, TransformerException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + TransformerFactory tranFactory = TransformerFactory.newInstance(); + Transformer aTransformer = tranFactory.newTransformer(); + Source src = new DOMSource(mDoc); + Result dest = new StreamResult(bos); + aTransformer.transform(src, dest); + return bos.toByteArray(); + } + + public void output(OutputStream os) + throws TransformerConfigurationException, TransformerException { + TransformerFactory tranFactory = TransformerFactory.newInstance(); + Transformer aTransformer = tranFactory.newTransformer(); + Source src = new DOMSource(mDoc); + Result dest = new StreamResult(os); + aTransformer.transform(src, dest); + } + + public String toXMLString() throws TransformerConfigurationException, TransformerException { + TransformerFactory tranFactory = TransformerFactory.newInstance(); + Transformer transformer = tranFactory.newTransformer(); + Source src = new DOMSource(mDoc); + StreamResult dest = new StreamResult(new StringWriter()); + transformer.transform(src, dest); + String xmlString = dest.getWriter().toString(); + return xmlString; + } +} diff --git a/base/util/src/netscape/net/NetworkClient.java b/base/util/src/netscape/net/NetworkClient.java new file mode 100644 index 000000000..ae8cdfcf4 --- /dev/null +++ b/base/util/src/netscape/net/NetworkClient.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 netscape.net; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * This is the base class for network clients. + * + * @version 1.21, 08/07/97 + * @author Jonathan Payne + */ +public class NetworkClient { + /** Socket for communicating with server. */ + protected Socket serverSocket = null; + + /** Stream for printing to the server. */ + public PrintStream serverOutput; + + /** Buffered stream for reading replies from server. */ + public InputStream serverInput; + + /** Open a connection to the server. */ + public void openServer(String server, int port) + throws IOException, UnknownHostException { + if (serverSocket != null) + closeServer(); + serverSocket = doConnect(server, port); + serverOutput = new PrintStream(new BufferedOutputStream(serverSocket.getOutputStream()), + true); + serverInput = new BufferedInputStream(serverSocket.getInputStream()); + } + + /** + * Return a socket connected to the server, with any + * appropriate options pre-established + */ + protected Socket doConnect(String server, int port) + throws IOException, UnknownHostException { + return new Socket(server, port); + } + + /** Close an open connection to the server. */ + public void closeServer() throws IOException { + if (!serverIsOpen()) { + return; + } + serverSocket.close(); + serverSocket = null; + serverInput = null; + serverOutput = null; + } + + /** Return server connection status */ + public boolean serverIsOpen() { + return serverSocket != null; + } + + /** Create connection with host <i>host</i> on port <i>port</i> */ + public NetworkClient(String host, int port) throws IOException { + openServer(host, port); + } + + public NetworkClient() { + } +} diff --git a/base/util/src/netscape/net/TransferProtocolClient.java b/base/util/src/netscape/net/TransferProtocolClient.java new file mode 100644 index 000000000..76e3a9c21 --- /dev/null +++ b/base/util/src/netscape/net/TransferProtocolClient.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 netscape.net; + +import java.io.IOException; +import java.util.Vector; + +/** + * This class implements that basic intefaces of transfer protocols. + * It is used by subclasses implementing specific protocols. + * + * @version 1.25, 08/07/97 + * @author Jonathan Payne + */ + +public class TransferProtocolClient extends NetworkClient { + static final boolean debug = false; + + /** + * Array of strings (usually 1 entry) for the last reply + * from the server. + */ + protected Vector<String> serverResponse = new Vector<String>(1); + + /** code for last reply */ + protected int lastReplyCode; + + /** + * Pulls the response from the server and returns the code as a + * number. Returns -1 on failure. + */ + public int readServerResponse() throws IOException { + StringBuffer replyBuf = new StringBuffer(32); + int c; + int continuingCode = -1; + int code; + String response; + + serverResponse.setSize(0); + while (true) { + while ((c = serverInput.read()) != -1) { + if (c == '\r') { + if ((c = serverInput.read()) != '\n') + replyBuf.append('\r'); + } + replyBuf.append((char) c); + if (c == '\n') + break; + } + response = replyBuf.toString(); + replyBuf.setLength(0); + if (debug) { + System.out.print(response); + } + try { + code = Integer.parseInt(response.substring(0, 3)); + } catch (NumberFormatException e) { + code = -1; + } catch (StringIndexOutOfBoundsException e) { + /* this line doesn't contain a response code, so + we just completely ignore it */ + continue; + } + serverResponse.addElement(response); + if (continuingCode != -1) { + /* we've seen a XXX- sequence */ + if (code != continuingCode || + (response.length() >= 4 && response.charAt(3) == '-')) { + continue; + } else { + /* seen the end of code sequence */ + continuingCode = -1; + break; + } + } else if (response.length() >= 4 && response.charAt(3) == '-') { + continuingCode = code; + continue; + } else { + break; + } + } + + return lastReplyCode = code; + } + + /** Sends command <i>cmd</i> to the server. */ + public void sendServer(String cmd) { + serverOutput.print(cmd); + if (debug) { + System.out.print("Sending: " + cmd); + } + } + + /** converts the server response into a string. */ + public String getResponseString() { + return (String) serverResponse.elementAt(0); + } + + /** Returns all server response strings. */ + public Vector<String> getResponseStrings() { + return serverResponse; + } + + /** standard constructor to host <i>host</i>, port <i>port</i>. */ + public TransferProtocolClient(String host, int port) throws IOException { + super(host, port); + } + + /** creates an uninitialized instance of this class. */ + public TransferProtocolClient() { + } +} diff --git a/base/util/src/netscape/net/smtp/SmtpClient.java b/base/util/src/netscape/net/smtp/SmtpClient.java new file mode 100644 index 000000000..40b927b8f --- /dev/null +++ b/base/util/src/netscape/net/smtp/SmtpClient.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 netscape.net.smtp; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.InetAddress; + +import netscape.net.TransferProtocolClient; + +/** + * This class implements the SMTP client. + * You can send a piece of mail by creating a new SmtpClient, calling + * the "to" method to add destinations, calling "from" to name the + * sender, calling startMessage to return a stream to which you write + * the message (with RFC733 headers) and then you finally close the Smtp + * Client. + * + * @version 1.17, 12 Dec 1994 + * @author James Gosling + */ + +public class SmtpClient extends TransferProtocolClient { + SmtpPrintStream message; + + /** + * issue the QUIT command to the SMTP server and close the connection. + */ + public void closeServer() throws IOException { + if (serverIsOpen()) { + closeMessage(); + issueCommand("QUIT\r\n", 221); + super.closeServer(); + } + } + + void issueCommand(String cmd, int expect) throws IOException { + sendServer(cmd); + int reply; + while ((reply = readServerResponse()) != expect) + if (reply != 220) { + throw new SmtpProtocolException(getResponseString()); + } + } + + private void toCanonical(String s) throws IOException { + issueCommand("rcpt to: " + s + "\r\n", 250); + } + + public void to(String s) throws IOException { + int st = 0; + int limit = s.length(); + int pos = 0; + int lastnonsp = 0; + int parendepth = 0; + boolean ignore = false; + while (pos < limit) { + int c = s.charAt(pos); + if (parendepth > 0) { + if (c == '(') + parendepth++; + else if (c == ')') + parendepth--; + if (parendepth == 0) + if (lastnonsp > st) + ignore = true; + else + st = pos + 1; + } else if (c == '(') + parendepth++; + else if (c == '<') + st = lastnonsp = pos + 1; + else if (c == '>') + ignore = true; + else if (c == ',') { + if (lastnonsp > st) + toCanonical(s.substring(st, lastnonsp)); + st = pos + 1; + ignore = false; + } else { + if (c > ' ' && !ignore) + lastnonsp = pos + 1; + else if (st == pos) + st++; + } + pos++; + } + if (lastnonsp > st) + toCanonical(s.substring(st, lastnonsp)); + } + + public void from(String s) throws IOException { + issueCommand("mail from: " + s + "\r\n", 250); + } + + /** open a SMTP connection to host <i>host</i>. */ + private void openServer(String host) throws IOException { + openServer(host, 25); + issueCommand("helo " + InetAddress.getLocalHost().getHostName() + "\r\n", 250); + } + + public PrintStream startMessage() throws IOException { + issueCommand("data\r\n", 354); + return message = new SmtpPrintStream(serverOutput, this); + } + + void closeMessage() throws IOException { + if (message != null) + message.close(); + } + + /** New SMTP client connected to host <i>host</i>. */ + public SmtpClient(String host) throws IOException { + super(); + if (host != null) { + try { + openServer(host); + return; + } catch (Exception e) { + } + } + try { + String s; + try { + // java.security.AccessController.beginPrivileged(); + s = System.getProperty("mail.host"); + } finally { + // java.security.AccessController.endPrivileged(); + } + if (s != null) { + openServer(s); + return; + } + } catch (Exception e) { + } + try { + openServer("localhost"); + } catch (Exception e) { + openServer("mailhost"); + } + } + + /** Create an uninitialized SMTP client. */ + public SmtpClient() throws IOException { + this(null); + } +} + +class SmtpPrintStream extends java.io.PrintStream { + private SmtpClient target; + private int lastc = '\n'; + + SmtpPrintStream(OutputStream fos, SmtpClient cl) { + super(fos); + target = cl; + } + + public void close() { + if (target == null) + return; + if (lastc != '\n') { + write('\r'); + write('\n'); + } + try { + target.issueCommand(".\r\n", 250); + target.message = null; + out = null; + target = null; + } catch (IOException e) { + } + } + + public void write(int b) { + try { + // quote a dot at the beginning of a line + if (lastc == '\n' && b == '.') { + out.write('.'); + } + + // translate NL to CRLF + if (b == '\n') { + out.write('\r'); + } + out.write(b); + lastc = b; + } catch (IOException e) { + } + } + + public void write(byte b[], int off, int len) { + try { + int lc = lastc; + while (--len >= 0) { + int c = b[off++]; + + // quote a dot at the beginning of a line + if (lc == '\n' && c == '.') + out.write('.'); + + // translate NL to CRLF + if (c == '\n') { + out.write('\r'); + } + out.write(c); + lc = c; + } + lastc = lc; + } catch (IOException e) { + } + } + + public void print(String s) { + int len = s.length(); + for (int i = 0; i < len; i++) { + write(s.charAt(i)); + } + } +} diff --git a/base/util/src/netscape/net/smtp/SmtpProtocolException.java b/base/util/src/netscape/net/smtp/SmtpProtocolException.java new file mode 100644 index 000000000..9ffe5d95b --- /dev/null +++ b/base/util/src/netscape/net/smtp/SmtpProtocolException.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 netscape.net.smtp; + +import java.io.IOException; + +/** + * This exeception is thrown when unexpected results are returned during + * an SMTP session. + */ +public class SmtpProtocolException extends IOException { + /** + * + */ + private static final long serialVersionUID = -5586603317525864401L; + + SmtpProtocolException(String s) { + super(s); + } +} diff --git a/base/util/src/netscape/security/acl/AclEntryImpl.java b/base/util/src/netscape/security/acl/AclEntryImpl.java new file mode 100644 index 000000000..46365f5d8 --- /dev/null +++ b/base/util/src/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<Permission> permissionSet = new Vector<Permission>(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<Permission> 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<Permission> e = permissions(); + while (e.hasMoreElements()) { + Permission p = (Permission) 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<Permission>(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/base/util/src/netscape/security/acl/AclImpl.java b/base/util/src/netscape/security/acl/AclImpl.java new file mode 100644 index 000000000..76750b7b9 --- /dev/null +++ b/base/util/src/netscape/security/acl/AclImpl.java @@ -0,0 +1,391 @@ +// --- 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<Principal, AclEntry> allowedUsersTable = new Hashtable<Principal, AclEntry>(23); + private Hashtable<Principal, AclEntry> allowedGroupsTable = new Hashtable<Principal, AclEntry>(23); + private Hashtable<Principal, AclEntry> deniedUsersTable = new Hashtable<Principal, AclEntry>(23); + private Hashtable<Principal, AclEntry> deniedGroupsTable = new Hashtable<Principal, AclEntry>(23); + private String aclName = null; + private Vector<Permission> zeroSet = new Vector<Permission>(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<Principal, AclEntry> 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<Principal, AclEntry> 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<Permission> getPermissions(Principal user) { + + Enumeration<Permission> individualPositive; + Enumeration<Permission> individualNegative; + Enumeration<Permission> groupPositive; + Enumeration<Permission> 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<Permission> temp1 = subtract(groupPositive, individualNegative); + Enumeration<Permission> 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<Permission> 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<Permission> permSet = getPermissions(principal); + while (permSet.hasMoreElements()) { + Permission p = (Permission) permSet.nextElement(); + if (p.equals(permission)) + return true; + } + return false; + } + + /** + * returns an enumeration of the entries in this ACL. + */ + public synchronized Enumeration<AclEntry> entries() { + return new AclEnumerator(this, + allowedUsersTable, allowedGroupsTable, + deniedUsersTable, deniedGroupsTable); + } + + /** + * return a stringified version of the + * ACL. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + Enumeration<AclEntry> entries = entries(); + while (entries.hasMoreElements()) { + AclEntry entry = (AclEntry) 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<Principal, AclEntry> findTable(AclEntry entry) { + Hashtable<Principal, AclEntry> 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 <T> Enumeration<T> union(Enumeration<T> e1, Enumeration<T> e2) { + Vector<T> v = new Vector<T>(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 <T> Enumeration<T> subtract(Enumeration<T> e1, Enumeration<T> e2) { + Vector<T> v = new Vector<T>(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<Permission> getGroupPositive(Principal user) { + Enumeration<Permission> groupPositive = zeroSet.elements(); + Enumeration<Principal> e = allowedGroupsTable.keys(); + while (e.hasMoreElements()) { + Group g = (Group) e.nextElement(); + if (g.isMember(user)) { + AclEntry ae = (AclEntry) allowedGroupsTable.get(g); + groupPositive = union(ae.permissions(), groupPositive); + } + } + return groupPositive; + } + + private Enumeration<Permission> getGroupNegative(Principal user) { + Enumeration<Permission> groupNegative = zeroSet.elements(); + Enumeration<Principal> e = deniedGroupsTable.keys(); + while (e.hasMoreElements()) { + Group g = (Group) e.nextElement(); + if (g.isMember(user)) { + AclEntry ae = (AclEntry) deniedGroupsTable.get(g); + groupNegative = union(ae.permissions(), groupNegative); + } + } + return groupNegative; + } + + private Enumeration<Permission> getIndividualPositive(Principal user) { + Enumeration<Permission> individualPositive = zeroSet.elements(); + AclEntry ae = (AclEntry) allowedUsersTable.get(user); + if (ae != null) + individualPositive = ae.permissions(); + return individualPositive; + } + + private Enumeration<Permission> getIndividualNegative(Principal user) { + Enumeration<Permission> individualNegative = zeroSet.elements(); + AclEntry ae = (AclEntry) deniedUsersTable.get(user); + if (ae != null) + individualNegative = ae.permissions(); + return individualNegative; + } +} + +final class AclEnumerator implements Enumeration<AclEntry> { + Acl acl; + Enumeration<AclEntry> u1, u2, g1, g2; + + AclEnumerator(Acl acl, Hashtable<Principal, AclEntry> u1, Hashtable<Principal, AclEntry> g1, + Hashtable<Principal, AclEntry> u2, Hashtable<Principal, AclEntry> g2) { + this.acl = acl; + this.u1 = u1.elements(); + this.u2 = u2.elements(); + this.g1 = g1.elements(); + this.g2 = g2.elements(); + } + + public boolean hasMoreElements() { + 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/base/util/src/netscape/security/acl/AllPermissionsImpl.java b/base/util/src/netscape/security/acl/AllPermissionsImpl.java new file mode 100644 index 000000000..f2b57742f --- /dev/null +++ b/base/util/src/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/base/util/src/netscape/security/acl/GroupImpl.java b/base/util/src/netscape/security/acl/GroupImpl.java new file mode 100644 index 000000000..ed087a544 --- /dev/null +++ b/base/util/src/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<Principal> groupMembers = new Vector<Principal>(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<Principal> 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<Group> alreadySeen = new Vector<Group>(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<Group> alreadySeen) { + Enumeration<Principal> 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/base/util/src/netscape/security/acl/OwnerImpl.java b/base/util/src/netscape/security/acl/OwnerImpl.java new file mode 100644 index 000000000..3f47a1dd8 --- /dev/null +++ b/base/util/src/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<? extends Principal> 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/base/util/src/netscape/security/acl/PermissionImpl.java b/base/util/src/netscape/security/acl/PermissionImpl.java new file mode 100644 index 000000000..2e73d3c7e --- /dev/null +++ b/base/util/src/netscape/security/acl/PermissionImpl.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) 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; + } + } + + /** + * Prints a stringified version of the permission. + * + * @return the string representation of the Permission. + */ + public String toString() { + return permission; + } +} diff --git a/base/util/src/netscape/security/acl/PrincipalImpl.java b/base/util/src/netscape/security/acl/PrincipalImpl.java new file mode 100644 index 000000000..25fa11095 --- /dev/null +++ b/base/util/src/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/base/util/src/netscape/security/acl/WorldGroupImpl.java b/base/util/src/netscape/security/acl/WorldGroupImpl.java new file mode 100644 index 000000000..2f885cbec --- /dev/null +++ b/base/util/src/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/base/util/src/netscape/security/extensions/AccessDescription.java b/base/util/src/netscape/security/extensions/AccessDescription.java new file mode 100644 index 000000000..f13c937e7 --- /dev/null +++ b/base/util/src/netscape/security/extensions/AccessDescription.java @@ -0,0 +1,76 @@ +// --- 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.extensions; + +import java.io.IOException; +import java.io.Serializable; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import 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 { + 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/base/util/src/netscape/security/extensions/AuthInfoAccessExtension.java b/base/util/src/netscape/security/extensions/AuthInfoAccessExtension.java new file mode 100644 index 000000000..b8e2933dd --- /dev/null +++ b/base/util/src/netscape/security/extensions/AuthInfoAccessExtension.java @@ -0,0 +1,272 @@ +// --- 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.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 com.netscape.cmsutil.util.Utils; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; +import netscape.security.x509.GeneralName; +import netscape.security.x509.URIName; + +/** + * 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<AccessDescription> mDesc = new Vector<AccessDescription>(); + + /** + * 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[]) ((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<String> 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 { + 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() { + String s = super.toString() + "AuthInfoAccess [\n"; + + for (int i = 0; i < mDesc.size(); i++) { + AccessDescription ad = mDesc.elementAt(i); + + s += "(" + i + ")"; + s += " "; + s += ad.getMethod().toString() + " " + ad.getLocation().toString(); + } + return (s + "]\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())); + } 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/base/util/src/netscape/security/extensions/CertInfo.java b/base/util/src/netscape/security/extensions/CertInfo.java new file mode 100644 index 000000000..ab88ec8ab --- /dev/null +++ b/base/util/src/netscape/security/extensions/CertInfo.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 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 netscape.security.x509.AlgorithmId; +import netscape.security.x509.CertificateAlgorithmId; +import netscape.security.x509.CertificateIssuerName; +import netscape.security.x509.CertificateSerialNumber; +import netscape.security.x509.CertificateSubjectName; +import netscape.security.x509.CertificateValidity; +import netscape.security.x509.CertificateVersion; +import netscape.security.x509.X500Name; +import 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.getAlgorithmId("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/base/util/src/netscape/security/extensions/CertificateRenewalWindowExtension.java b/base/util/src/netscape/security/extensions/CertificateRenewalWindowExtension.java new file mode 100644 index 000000000..018fd71c8 --- /dev/null +++ b/base/util/src/netscape/security/extensions/CertificateRenewalWindowExtension.java @@ -0,0 +1,190 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import 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[]) ((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<String> 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 { + 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/base/util/src/netscape/security/extensions/CertificateScopeEntry.java b/base/util/src/netscape/security/extensions/CertificateScopeEntry.java new file mode 100644 index 000000000..527093ccf --- /dev/null +++ b/base/util/src/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 netscape.security.extensions; + +import java.io.IOException; + +import netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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/base/util/src/netscape/security/extensions/CertificateScopeOfUseExtension.java b/base/util/src/netscape/security/extensions/CertificateScopeOfUseExtension.java new file mode 100644 index 000000000..16641f36b --- /dev/null +++ b/base/util/src/netscape/security/extensions/CertificateScopeOfUseExtension.java @@ -0,0 +1,199 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; +import 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<CertificateScopeEntry> mEntries = null; + + static { + try { + OIDMap.addAttribute(CertificateScopeOfUseExtension.class.getName(), + ID.toString(), NAME); + } catch (CertificateException e) { + } + } + + public CertificateScopeOfUseExtension(boolean critical, Vector<CertificateScopeEntry> 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[]) ((byte[]) value).clone(); + decodeThis(); + } + + public String getName() { + return NAME; + } + + public Vector<CertificateScopeEntry> 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<String> 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<CertificateScopeEntry>(); + while (val.data.available() != 0) { + mEntries.addElement(new CertificateScopeEntry( + val.data.getDerValue())); + } + } + + private void encodeThis() throws IOException { + 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 = (CertificateScopeEntry) + 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() { + String s = super.toString() + "CertificateUseOfScope [\n"; + + if (mEntries != null) { + for (int i = 0; i < mEntries.size(); i++) { + CertificateScopeEntry se = (CertificateScopeEntry) + mEntries.elementAt(i); + + s += se.toString(); + } + } + return (s + "]\n"); + } +} diff --git a/base/util/src/netscape/security/extensions/ExtendedKeyUsageExtension.java b/base/util/src/netscape/security/extensions/ExtendedKeyUsageExtension.java new file mode 100644 index 000000000..939da036f --- /dev/null +++ b/base/util/src/netscape/security/extensions/ExtendedKeyUsageExtension.java @@ -0,0 +1,226 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; +import 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<ObjectIdentifier> oidSet = null; + private byte mCached[] = null; + + static { + try { + OIDMap.addAttribute(ExtendedKeyUsageExtension.class.getName(), + OID, ExtendedKeyUsageExtension.NAME); + } catch (CertificateException e) { + } + } + + public ExtendedKeyUsageExtension() { + this(false, null); + } + + public ExtendedKeyUsageExtension(boolean crit, Vector<ObjectIdentifier> oids) { + try { + extensionId = ObjectIdentifier.getObjectIdentifier(OID); + } catch (IOException e) { + // never here + } + critical = crit; + if (oids != null) { + oidSet = new Vector<ObjectIdentifier>(oids); + } else { + oidSet = new Vector<ObjectIdentifier>(); + } + encodeExtValue(); + } + + public ExtendedKeyUsageExtension(Boolean crit, Object byteVal) + throws IOException { + extensionId = ObjectIdentifier.getObjectIdentifier(OID); + critical = crit.booleanValue(); + extensionValue = (byte[]) ((byte[]) byteVal).clone(); + decodeThis(); + } + + public void setCritical(boolean newValue) { + if (critical != newValue) { + critical = newValue; + mCached = null; + } + } + + public Enumeration<ObjectIdentifier> 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<ObjectIdentifier>(); + } + + 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(); + } + } + + public String toString() { + String presentation = "oid=" + ExtendedKeyUsageExtension.OID + " "; + + if (critical) { + presentation += "critical=true"; + } + if (extensionValue != null) { + String extByteValue = new String(" val="); + + for (int i = 0; i < extensionValue.length; i++) { + extByteValue += (extensionValue[i] + " "); + } + presentation += extByteValue; + } + 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<String> 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<ObjectIdentifier>(); + while (val.data.available() != 0) { + DerValue oidVal = val.data.getDerValue(); + + oidSet.addElement(oidVal.getOID()); + } + } + + private void encodeExtValue() { + DerOutputStream out = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + + if (!oidSet.isEmpty()) { + Enumeration<ObjectIdentifier> oidList = oidSet.elements(); + + try { + while (oidList.hasMoreElements()) { + temp.putOID((ObjectIdentifier) oidList.nextElement()); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + try { + out.write(DerValue.tag_Sequence, temp); + } catch (IOException ex) { + } + + extensionValue = out.toByteArray(); + } +} diff --git a/base/util/src/netscape/security/extensions/GenericASN1Extension.java b/base/util/src/netscape/security/extensions/GenericASN1Extension.java new file mode 100644 index 000000000..ff1ea7173 --- /dev/null +++ b/base/util/src/netscape/security/extensions/GenericASN1Extension.java @@ -0,0 +1,448 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; +import 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<String, String> 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 os = new DerOutputStream(); + 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 '}': + os.write(DerValue.tag_Sequence, tmp); + return os.toByteArray(); + default: + type = (String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_TYPE); + if (type.equalsIgnoreCase("integer")) { + int num = Integer.parseInt((String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE)); + PutInteger(tmp, num); + } else if (type.equalsIgnoreCase("ia5string")) { + source = (String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); + value = (String) 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 = (String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); + value = (String) 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 = (String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); + value = (String) 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 = (String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); + value = (String) 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 = (String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_SOURCE); + value = (String) 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 = (String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); + PutUTCtime(tmp, value); + } else if (type.equalsIgnoreCase("oid")) { + value = (String) mConfig.get(PROP_ATTRIBUTE + "." + ch + "." + PROP_VALUE); + PutOID(tmp, value); + } else if (type.equalsIgnoreCase("boolean")) { + boolean bool = false; + String b = (String) 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<String, String> 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("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<String, String> config) + throws IOException, ParseException { + mConfig = config; + ObjectIdentifier tmpid = new ObjectIdentifier((String) mConfig.get(PROP_OID)); + name = (String) mConfig.get(PROP_NAME); + OID = (String) mConfig.get(PROP_OID); + pattern = (String) 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 = (String) 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. + */ + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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 = 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); + } + + for (i = 0, j = 0; j < s.length(); j++) { + int ch = (int) 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 = (int) s.charAt(j); + if (ch == 10 || ch == 13 || ch == 9) + continue; + buff[i++] = (byte) ch; + } + + s = new String(buff); + + return s; + } +} diff --git a/base/util/src/netscape/security/extensions/InhibitAnyPolicyExtension.java b/base/util/src/netscape/security/extensions/InhibitAnyPolicyExtension.java new file mode 100644 index 000000000..81b8cf5b5 --- /dev/null +++ b/base/util/src/netscape/security/extensions/InhibitAnyPolicyExtension.java @@ -0,0 +1,179 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; +import 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() { + this(false, null); + } + + public InhibitAnyPolicyExtension(boolean crit, BigInt skipCerts) { + 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; + } + + public String toString() { + String presentation = "ObjectId: " + OID + " "; + + if (critical) { + presentation += "Criticality=true"; + } else { + presentation += "Criticality=false"; + } + if (extensionValue != null) { + String extByteValue = new String(" 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<String> 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 { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + try { + extensionId = ObjectIdentifier.getObjectIdentifier(OID); + } catch (IOException e) { + // never here + } + DerOutputStream os = new DerOutputStream(); + os.putInteger(mSkipCerts); + this.extensionValue = os.toByteArray(); + } + + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + private void encodeExtValue() { + DerOutputStream out = new DerOutputStream(); + try { + out.putInteger(mSkipCerts); + } catch (IOException e) { + } + extensionValue = out.toByteArray(); + } +} diff --git a/base/util/src/netscape/security/extensions/KerberosName.java b/base/util/src/netscape/security/extensions/KerberosName.java new file mode 100644 index 000000000..0a6a6e213 --- /dev/null +++ b/base/util/src/netscape/security/extensions/KerberosName.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 netscape.security.extensions; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Vector; + +import netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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<String> m_name_strings = null; + + public KerberosName(String realm, int name_type, Vector<String> 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 { + + 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((String) 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 = (String) m_name_strings.elementAt(i); + } else { + strings += ","; + strings += (String) 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<String> strings = new Vector<String>(); + 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/base/util/src/netscape/security/extensions/NSCertTypeExtension.java b/base/util/src/netscape/security/extensions/NSCertTypeExtension.java new file mode 100644 index 000000000..04b3038e5 --- /dev/null +++ b/base/util/src/netscape/security/extensions/NSCertTypeExtension.java @@ -0,0 +1,377 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; + +/** + * NSCertTypeExtension + * Represents Netscape Certificate Type Extension + * + * <p> + * 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<String> mAttributeNames = new Vector<String>(); + + 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 { + 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[]) ((byte[]) value).clone(); + + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + + this.mBitString = val.getUnalignedBitString().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 new Boolean(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<String> getAttributeNames() { + return mAttributeNames.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + + public static void main(String[] argv) { + } +} diff --git a/base/util/src/netscape/security/extensions/OCSPNoCheckExtension.java b/base/util/src/netscape/security/extensions/OCSPNoCheckExtension.java new file mode 100644 index 000000000..28da8085f --- /dev/null +++ b/base/util/src/netscape/security/extensions/OCSPNoCheckExtension.java @@ -0,0 +1,153 @@ +// --- 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.extensions; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Enumeration; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; +import 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() { + this(Boolean.FALSE); + } + + public OCSPNoCheckExtension(Boolean crit) { + try { + extensionId = ObjectIdentifier.getObjectIdentifier(OCSPNoCheckExtension.OID); + } catch (IOException e) { + // never here + } + critical = crit.booleanValue(); + DerOutputStream tmpD = new DerOutputStream(); + + try { + tmpD.putNull(); + } catch (IOException ex) { + } + 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[]) ((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(); + } + } + + public String toString() { + String presentation = "oid=" + OID + " "; + + if (critical) { + presentation += "critical=true"; + } + if (extensionValue != null) { + String extByteValue = new String(" val="); + + for (int i = 0; i < extensionValue.length; i++) { + extByteValue += (extensionValue[i] + " "); + } + presentation += extByteValue; + } + 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<String> getAttributeNames() { + // NOT USED + return null; + } + + public String getName() { + return NAME; + } + + public void delete(String name) + throws CertificateException, IOException { + // NOT USED + } +} diff --git a/base/util/src/netscape/security/extensions/PresenceServerExtension.java b/base/util/src/netscape/security/extensions/PresenceServerExtension.java new file mode 100644 index 000000000..c67fe9965 --- /dev/null +++ b/base/util/src/netscape/security/extensions/PresenceServerExtension.java @@ -0,0 +1,321 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; + +public class PresenceServerExtension extends Extension implements CertAttrSet { + /** + * + */ + private static final long serialVersionUID = -6333109673043357921L; + private boolean mCritical; + private String mOID = null; + 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[]) ((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 { + 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<String> 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[]) throws Exception { + /* + 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 + : } + : } + : } + */ + 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 + + ByteArrayOutputStream dos = new ByteArrayOutputStream(); + ext.encode(dos); + FileOutputStream fos = new FileOutputStream("pse.der"); + fos.write(dos.toByteArray()); + fos.close(); + + Extension ext1 = new Extension(new DerValue(dos.toByteArray())); + + @SuppressWarnings("unused") + PresenceServerExtension ext2 = new PresenceServerExtension( + new Boolean(false), ext1.getExtensionValue()); + + } +} diff --git a/base/util/src/netscape/security/extensions/SubjectInfoAccessExtension.java b/base/util/src/netscape/security/extensions/SubjectInfoAccessExtension.java new file mode 100644 index 000000000..5c373289f --- /dev/null +++ b/base/util/src/netscape/security/extensions/SubjectInfoAccessExtension.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 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 com.netscape.cmsutil.util.Utils; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; +import netscape.security.x509.GeneralName; +import netscape.security.x509.URIName; + +/** + * 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<AccessDescription> mDesc = new Vector<AccessDescription>(); + + /** + * 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[]) ((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<String> 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 { + 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() { + String s = super.toString() + "AuthInfoAccess [\n"; + + for (int i = 0; i < mDesc.size(); i++) { + AccessDescription ad = mDesc.elementAt(i); + + s += "(" + i + ")"; + s += " "; + s += ad.getMethod().toString() + " " + ad.getLocation().toString(); + } + return (s + "]\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())); + } 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/base/util/src/netscape/security/pkcs/ContentInfo.java b/base/util/src/netscape/security/pkcs/ContentInfo.java new file mode 100644 index 000000000..9825494c2 --- /dev/null +++ b/base/util/src/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 netscape.security.pkcs; + +import java.io.IOException; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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/base/util/src/netscape/security/pkcs/EncodingException.java b/base/util/src/netscape/security/pkcs/EncodingException.java new file mode 100644 index 000000000..cb495e990 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/pkcs/PKCS10.java b/base/util/src/netscape/security/pkcs/PKCS10.java new file mode 100644 index 000000000..a6ddd203f --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS10.java @@ -0,0 +1,343 @@ +// --- 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.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 com.netscape.cmsutil.util.Utils; + +import netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.x509.AlgorithmId; +import netscape.security.x509.X500Name; +import netscape.security.x509.X500Signer; +import netscape.security.x509.X509Key; + +/** + * 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: + * + * <pre> + * 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 + * </pre> + * + * @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; + + certificateRequest = data; + + // + // Outer sequence: request, signature algorithm, signature. + // Parse, and prepare to verify later. + // + in = new DerInputStream(data); + seq = in.getSequence(3); + + if (seq.length != 3) + throw new IllegalArgumentException("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); + + byte val1[] = seq[0].data.getDerValue().toByteArray(); + subjectPublicKeyInfo = X509Key.parse(new DerValue(val1)); + PublicKey publicKey = X509Key.parsePublicKey(new DerValue(val1)); + + // 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("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)) + throw new SignatureException("Invalid PKCS #10 signature"); + } + } catch (InvalidKeyException e) { + throw new SignatureException("invalid key"); + } + } + + 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)); + 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/base/util/src/netscape/security/pkcs/PKCS10Attribute.java b/base/util/src/netscape/security/pkcs/PKCS10Attribute.java new file mode 100644 index 000000000..a649c395a --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS10Attribute.java @@ -0,0 +1,238 @@ +// --- 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.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 netscape.security.util.DerEncoder; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.ACertAttrSet; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extensions; +import netscape.security.x509.OIDMap; + +/** + * Represent a PKCS Attribute. + * + * <p> + * 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. + * + * <p> + * 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: + * + * <pre> + * Attribute :: SEQUENCE { + * type AttributeValue, + * values SET OF AttributeValue + * } + * AttributeValue ::= ANY + * </pre> + * + * @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 = (CertAttrSet) 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<CertAttrSet> extClass = (Class<CertAttrSet>) OIDMap.getClass(attributeId); + if (extClass != null) { + Constructor<CertAttrSet> cons = (Constructor<CertAttrSet>) 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()); + } 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 { + // 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 + DerOutputStream tmp = new DerOutputStream(); + 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 <code>DerEncoder</code> 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/base/util/src/netscape/security/pkcs/PKCS10Attributes.java b/base/util/src/netscape/security/pkcs/PKCS10Attributes.java new file mode 100644 index 000000000..8beb1e64c --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS10Attributes.java @@ -0,0 +1,147 @@ +// --- 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.pkcs; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import netscape.security.util.DerEncoder; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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<PKCS10Attribute> implements DerEncoder { + + /** + * + */ + private static final long serialVersionUID = 1362260612357629542L; + private Hashtable<String, PKCS10Attribute> map; + + /** + * Default constructor for the certificate attribute. + */ + public PKCS10Attributes() { + map = new Hashtable<String, PKCS10Attribute>(); + } + + /** + * 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<String, PKCS10Attribute>(); + 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); + 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 <code>DerEncoder</code> interface. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on encoding errors. + */ + public void derEncode(OutputStream out) + throws IOException { + + // first copy the elements into an array + PKCS10Attribute[] attribs = new PKCS10Attribute[size()]; + copyInto(attribs); + + DerOutputStream attrOut = new DerOutputStream(); + attrOut.putOrderedSetOf(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), + attribs); + + out.write(attrOut.toByteArray()); + } + + /** + * 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<PKCS10Attribute> getElements() { + return map.elements(); + } +} diff --git a/base/util/src/netscape/security/pkcs/PKCS7.java b/base/util/src/netscape/security/pkcs/PKCS7.java new file mode 100644 index 000000000..c8fb69582 --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS7.java @@ -0,0 +1,446 @@ +// --- 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.pkcs; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +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 netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.AlgorithmId; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; + +/** + * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile + * Supports only <tt>SignedData</tt> ContentInfo + * type, where to the type of data signed is plain Data. + * For signedData, <tt>crls</tt>, <tt>attributes</tt> and + * PKCS#6 Extended Certificates are not supported. + * + * @version 1.33 97/12/10 + * @author Benjamin Renaud + */ +public class 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); + } + + 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 = (X509Certificate) 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 " + + "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<SignerInfo> intResult = new Vector<SignerInfo>(); + 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 = (BigInteger) cert.getSerialNumber(); + BigInt thisSerial = new BigInt(tmpSerial); + if (serial.equals(thisSerial) && name.equals(thisName)) { + return cert; + } + } + return null; + } + + /** + * Returns the PKCS7 block in a printable string form. + */ + public String toString() { + String out = ""; + + out += "PKCS7 :: version: " + version + "\n"; + out += "PKCS7 :: digest AlgorithmIds: \n"; + for (int i = 0; i < digestAlgorithmIds.length; i++) { + out += "\t" + digestAlgorithmIds[i] + "\n"; + } + out += contentInfo + "\n"; + out += "PKCS7 :: certificates: \n"; + for (int i = 0; i < certificates.length; i++) { + out += "\t" + i + ". " + certificates[i] + "\n"; + } + out += "PKCS7 :: signer infos: \n"; + for (int i = 0; i < signerInfos.length; i++) { + out += ("\t" + i + ". " + signerInfos[i] + "\n"); + } + return out; + } +} diff --git a/base/util/src/netscape/security/pkcs/PKCS8Key.java b/base/util/src/netscape/security/pkcs/PKCS8Key.java new file mode 100644 index 000000000..7c5f64582 --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS8Key.java @@ -0,0 +1,435 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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 <code>decode</code>. + */ + 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. + * + * <P> + * 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 <code>key</code> bits; Diffie-Hellman and + * DSS/DSA keys encapsulate a single unsigned integer. + * + * <P> + * This function is called when creating PKCS#8 SubjectPublicKeyInfo values using the PKCS8Key member functions, + * such as <code>parse</code> and <code>decode</code>. + * + * @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() { + netscape.security.util.PrettyPrintFormat pp = + new 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.) + * + * <P> + * Subclasses should not normally redefine this method; they should instead provide a <code>parseKeyBits</code> + * method to parse any fields inside the <code>key</code> 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 synchronized 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 synchronized 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 <code>Key</code>. + * 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 <code>true</code> if this key has the same encoding as the + * object argument; <code>false</code> 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/base/util/src/netscape/security/pkcs/PKCS9Attribute.java b/base/util/src/netscape/security/pkcs/PKCS9Attribute.java new file mode 100644 index 000000000..6a6fd7dc9 --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS9Attribute.java @@ -0,0 +1,1123 @@ +// --- 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.pkcs; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Date; +import java.util.Hashtable; + +import netscape.security.util.DerEncoder; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import 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<String, ObjectIdentifier> NAME_OID_TABLE = new Hashtable<String, ObjectIdentifier>( + 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<ObjectIdentifier, String> OID_NAME_TABLE = new Hashtable<ObjectIdentifier, String>( + 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 <code>PKCS9_OIDS</code>. + * 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. + * + * <P> + * The following table shows the correspondence between attribute types and value component classes. + * + * <P> + * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER> + * + * <TR> + * <TH>OID</TH> + * <TH>Attribute Type Name</TH> + * <TH>Kind</TH> + * <TH>Value Class</TH> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.1</TD> + * <TD>EmailAddress</TD> + * <TD>Multiple-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.2</TD> + * <TD>UnstructuredName</TD> + * <TD>Multiple-valued</TD> + * <TD><code>String</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.3</TD> + * <TD>ContentType</TD> + * <TD>Single-valued</TD> + * <TD><code>ObjectIdentifier</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.4</TD> + * <TD>MessageDigest</TD> + * <TD>Single-valued</TD> + * <TD><code>byte[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.5</TD> + * <TD>SigningTime</TD> + * <TD>Single-valued</TD> + * <TD><code>Date</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.6</TD> + * <TD>Countersignature</TD> + * <TD>Multiple-valued</TD> + * <TD><code>SignerInfo</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.7</TD> + * <TD>ChallengePassword</TD> + * <TD>Single-valued</TD> + * <TD><code>String</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.8</TD> + * <TD>UnstructuredAddress</TD> + * <TD>Single-valued</TD> + * <TD><code>String</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.9</TD> + * <TD>ExtendedCertificateAttributes</TD> + * <TD>Multiple-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.10</TD> + * <TD>IssuerAndSerialNumber</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.11</TD> + * <TD>PasswordCheck</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.12</TD> + * <TD>PublicKey</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.13</TD> + * <TD>SigningDescription</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.14</TD> + * <TD>ExtensionRequest</TD> + * <TD>Single-valued</TD> + * <TD><code>Sequence</code></TD> + * </TR> + * + * </TABLE> + */ + 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 <code>PKCS9_OIDS</code>. + */ + 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 <code>PKCS9_OIDS[index]</code>. + */ + private int index; + + /** + * Value set of this attribute. Its class is given by <code>VALUE_CLASSES[index]</code>. + */ + 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. + * + * <P> + * The following table gives the class that <code>value</code> must have for a given attribute. + * + * <P> + * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER> + * + * <TR> + * <TH>OID</TH> + * <TH>Attribute Type Name</TH> + * <TH>Kind</TH> + * <TH>Value Class</TH> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.1</TD> + * <TD>EmailAddress</TD> + * <TD>Multiple-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.2</TD> + * <TD>UnstructuredName</TD> + * <TD>Multiple-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.3</TD> + * <TD>ContentType</TD> + * <TD>Single-valued</TD> + * <TD><code>ObjectIdentifier</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.4</TD> + * <TD>MessageDigest</TD> + * <TD>Single-valued</TD> + * <TD><code>byte[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.5</TD> + * <TD>SigningTime</TD> + * <TD>Single-valued</TD> + * <TD><code>Date</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.6</TD> + * <TD>Countersignature</TD> + * <TD>Multiple-valued</TD> + * <TD><code>SignerInfo[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.7</TD> + * <TD>ChallengePassword</TD> + * <TD>Single-valued</TD> + * <TD><code>String</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.8</TD> + * <TD>UnstructuredAddress</TD> + * <TD>Single-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.9</TD> + * <TD>ExtendedCertificateAttributes</TD> + * <TD>Multiple-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.10</TD> + * <TD>IssuerAndSerialNumber</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.11</TD> + * <TD>PasswordCheck</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.12</TD> + * <TD>PublicKey</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.13</TD> + * <TD>SigningDescription</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.14</TD> + * <TD>ExtensionRequest</TD> + * <TD>Single-valued</TD> + * <TD><code>Sequence</code></TD> + * </TR> + * + * </TABLE> + */ + 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. + * + * <P> + * The following table gives the class that <code>value</code> must have for a given attribute. Reasonable variants + * of these attributes are accepted; in particular, case does not matter. + * + * <P> + * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER> + * + * <TR> + * <TH>OID</TH> + * <TH>Attribute Type Name</TH> + * <TH>Kind</TH> + * <TH>Value Class</TH> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.1</TD> + * <TD>EmailAddress</TD> + * <TD>Multiple-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.2</TD> + * <TD>UnstructuredName</TD> + * <TD>Multiple-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.3</TD> + * <TD>ContentType</TD> + * <TD>Single-valued</TD> + * <TD><code>ObjectIdentifier</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.4</TD> + * <TD>MessageDigest</TD> + * <TD>Single-valued</TD> + * <TD><code>byte[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.5</TD> + * <TD>SigningTime</TD> + * <TD>Single-valued</TD> + * <TD><code>Date</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.6</TD> + * <TD>Countersignature</TD> + * <TD>Multiple-valued</TD> + * <TD><code>SignerInfo[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.7</TD> + * <TD>ChallengePassword</TD> + * <TD>Single-valued</TD> + * <TD><code>String</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.8</TD> + * <TD>UnstructuredAddress</TD> + * <TD>Single-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.9</TD> + * <TD>ExtendedCertificateAttributes</TD> + * <TD>Multiple-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.10</TD> + * <TD>IssuerAndSerialNumber</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.11</TD> + * <TD>PasswordCheck</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.12</TD> + * <TD>PublicKey</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.13</TD> + * <TD>SigningDescription</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.14</TD> + * <TD>ExtensionRequest</TD> + * <TD>Single-valued</TD> + * <TD><code>Sequence</code></TD> + * </TR> + * + * </TABLE> + * + * @exception IllegalArgumentException + * if the <code>name</code> is not recognized of the <code>value</code> 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. + * + * <P> + * N.B.: This method always encodes values of ChallengePassword and UnstructuredAddress attributes as ASN.1 + * <code>PrintableString</code>s, without checking whether they should be encoded as <code>T61String</code>s. + */ + public void derEncode(OutputStream out) throws IOException { + DerOutputStream temp = 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 + { + DerOutputStream temp2 = new DerOutputStream(); + temp2.putOID((ObjectIdentifier) value); + temp.write(DerValue.tag_Set, temp2.toByteArray()); + } + break; + + case 4: // message digest + { + DerOutputStream temp2 = new DerOutputStream(); + temp2.putOctetString((byte[]) value); + temp.write(DerValue.tag_Set, temp2.toByteArray()); + } + break; + + case 5: // signing time + { + DerOutputStream temp2 = new DerOutputStream(); + 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 + { + DerOutputStream temp2 = new DerOutputStream(); + 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 { + DerOutputStream temp2 = new DerOutputStream(); + //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 + } + + DerOutputStream derOut = new DerOutputStream(); + 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. + * + * <P> + * The following table gives the class of the value returned, depending on the type of this attribute. + * + * <P> + * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER> + * + * <TR> + * <TH>OID</TH> + * <TH>Attribute Type Name</TH> + * <TH>Kind</TH> + * <TH>Value Class</TH> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.1</TD> + * <TD>EmailAddress</TD> + * <TD>Multiple-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.2</TD> + * <TD>UnstructuredName</TD> + * <TD>Multiple-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.3</TD> + * <TD>ContentType</TD> + * <TD>Single-valued</TD> + * <TD><code>ObjectIdentifier</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.4</TD> + * <TD>MessageDigest</TD> + * <TD>Single-valued</TD> + * <TD><code>byte[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.5</TD> + * <TD>SigningTime</TD> + * <TD>Single-valued</TD> + * <TD><code>Date</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.6</TD> + * <TD>Countersignature</TD> + * <TD>Multiple-valued</TD> + * <TD><code>SignerInfo[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.7</TD> + * <TD>ChallengePassword</TD> + * <TD>Single-valued</TD> + * <TD><code>String</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.8</TD> + * <TD>UnstructuredAddress</TD> + * <TD>Single-valued</TD> + * <TD><code>String[]</code></TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.9</TD> + * <TD>ExtendedCertificateAttributes</TD> + * <TD>Multiple-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.10</TD> + * <TD>IssuerAndSerialNumber</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.11</TD> + * <TD>PasswordCheck</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.12</TD> + * <TD>PublicKey</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.13</TD> + * <TD>SigningDescription</TD> + * <TD>Single-valued</TD> + * <TD>(not supported)</TD> + * </TR> + * + * <TR> + * <TD>1.2.840.113549.1.9.14</TD> + * <TD>ExtensionRequest</TD> + * <TD>Single-valued</TD> + * <TD><code>Sequence</code></TD> + * </TR> + * + * </TABLE> + * + */ + 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 (String) 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 (ObjectIdentifier) 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 (String) 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 + netscape.security.util.PrettyPrintFormat pp = + new 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 <code>start</code>, find the first + * index <code>i</code> such that <code>a[i] = obj</code>. + * + * @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/base/util/src/netscape/security/pkcs/PKCS9Attributes.java b/base/util/src/netscape/security/pkcs/PKCS9Attributes.java new file mode 100644 index 000000000..485cdded1 --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS9Attributes.java @@ -0,0 +1,312 @@ +// --- 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.pkcs; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Hashtable; + +import netscape.security.util.DerEncoder; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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<ObjectIdentifier, PKCS9Attribute> attributes = new Hashtable<ObjectIdentifier, PKCS9Attribute>(3); + + /** + * The keys of this hashtable are the OIDs of permitted attributes. + */ + private final Hashtable<ObjectIdentifier, ObjectIdentifier> 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<ObjectIdentifier, ObjectIdentifier>(permittedAttributes.length); + + for (int i = 0; i < permittedAttributes.length; i++) + this.permittedAttributes.put(permittedAttributes[i], + permittedAttributes[i]); + } else { + this.permittedAttributes = null; + } + + // derEncoding initialized in <code>decode()</code> + 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 <code>decode()</code> + 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 <code>attribs</code> 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 { + 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 (byte[]) 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 <code>DerEncoder</code>s to <code>DerEncoder[]</code>. + */ + 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/base/util/src/netscape/security/pkcs/ParsingException.java b/base/util/src/netscape/security/pkcs/ParsingException.java new file mode 100644 index 000000000..88e91a8db --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/pkcs/SignerInfo.java b/base/util/src/netscape/security/pkcs/SignerInfo.java new file mode 100644 index 000000000..adb0115cc --- /dev/null +++ b/base/util/src/netscape/security/pkcs/SignerInfo.java @@ -0,0 +1,347 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerEncoder; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.AlgorithmId; +import 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 <code>DerEncoder</code> 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 { + 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); + + DerOutputStream tmp = new DerOutputStream(); + 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() { + netscape.security.util.PrettyPrintFormat pp = + new 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/base/util/src/netscape/security/provider/CMS.java b/base/util/src/netscape/security/provider/CMS.java new file mode 100644 index 000000000..4a4f150ab --- /dev/null +++ b/base/util/src/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 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<Object>() { + public Object run() { + /* + * Certificates + */ + put("CertificateFactory.X.509", "netscape.security.provider.X509CertificateFactory"); + put("Alg.Alias.CertificateFactory.X.509", "X.509"); + return null; + } + }); + } +} diff --git a/base/util/src/netscape/security/provider/DSA.java b/base/util/src/netscape/security/provider/DSA.java new file mode 100644 index 000000000..262095576 --- /dev/null +++ b/base/util/src/netscape/security/provider/DSA.java @@ -0,0 +1,660 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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 */ + 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 netscape.security.provider.DSA#engineUpdate + * @see 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 netscape.security.provider.DSA#engineUpdate + * @see 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 && seed1 == null) || + (seed1 == null && seed2 != null) || + (seed1 != null && seed2 == null) || + 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: + * <dl> + * + * <dt><tt>Kseed</tt> + * + * <dd>a byte array. + * + * </dl> + * + * @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: + * + * <dl> + * + * <dt><tt>Kseed</tt> + * + * <dd>a byte array. + * + * </dl> + * + * @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/base/util/src/netscape/security/provider/DSAKeyFactory.java b/base/util/src/netscape/security/provider/DSAKeyFactory.java new file mode 100755 index 000000000..41f0081f2 --- /dev/null +++ b/base/util/src/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 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 extends KeySpec> T engineGetKeySpec(Key key, Class<T> 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 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 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/base/util/src/netscape/security/provider/DSAKeyPairGenerator.java b/base/util/src/netscape/security/provider/DSAKeyPairGenerator.java new file mode 100644 index 000000000..3e04792bf --- /dev/null +++ b/base/util/src/netscape/security/provider/DSAKeyPairGenerator.java @@ -0,0 +1,398 @@ +// --- 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.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 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<Integer, AlgIdDSA> 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<Integer, AlgIdDSA>(); + + /* + * 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 <code>genParams</code> is false, a set of pre-computed parameters is + * used. In this case, <code>modelen</code> 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 = (DSAParameterSpec) + 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()); + } + } + + /* Test vectors from the DSA specs. */ + + private static int[] testXSeed = { 0xbd029bbe, 0x7f51960b, 0xcf9edb2b, + 0x61f06f0f, 0xeb5a38b6 }; + + private int[] x_t = { 0x67452301, 0xefcdab89, 0x98badcfe, + 0x10325476, 0xc3d2e1f0 }; + + /** + * 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/base/util/src/netscape/security/provider/DSAParameterGenerator.java b/base/util/src/netscape/security/provider/DSAParameterGenerator.java new file mode 100755 index 000000000..cd7b8de33 --- /dev/null +++ b/base/util/src/netscape/security/provider/DSAParameterGenerator.java @@ -0,0 +1,299 @@ +// --- 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.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 ZERO = BigInteger.valueOf(0); + 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 <code>p</code>, in bits. + * + * @return an array of BigInteger, with <code>p</code> at index 0 and + * <code>q</code> 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. + * + * <p>The seed parameter corresponds to the <code>SEED</code> parameter + * referenced in the FIPS specification of the DSA algorithm, + * and L is the size of <code>p</code>, in bits. + * + * @param seed the seed to generate the parameters + * @param L the size of <code>p</code>, in bits. + * + * @return an array of BigInteger, with <code>p</code> at index 0, + * <code>q</code> 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 <code>g</code> parameter for DSA. + * + * @param p the prime, <code>p</code>. + * @param q the subprime, <code>q</code>. + * + * @param the <code>g</code> + */ + 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/base/util/src/netscape/security/provider/DSAParameters.java b/base/util/src/netscape/security/provider/DSAParameters.java new file mode 100755 index 000000000..e2a5dd128 --- /dev/null +++ b/base/util/src/netscape/security/provider/DSAParameters.java @@ -0,0 +1,130 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import 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 extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> 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 { + 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/base/util/src/netscape/security/provider/DSAPrivateKey.java b/base/util/src/netscape/security/provider/DSAPrivateKey.java new file mode 100644 index 000000000..0cfc5e5ea --- /dev/null +++ b/base/util/src/netscape/security/provider/DSAPrivateKey.java @@ -0,0 +1,147 @@ +// --- 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.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 netscape.security.pkcs.PKCS8Key; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerValue; +import 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 { + DSAParameterSpec paramSpec; + AlgorithmParameters algParams = algid.getParameters(); + if (algParams == null) { + return null; + } + paramSpec = (DSAParameterSpec) algParams.getParameterSpec + (DSAParameterSpec.class); + return (DSAParams) paramSpec; + } + } 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()); + } + } +} diff --git a/base/util/src/netscape/security/provider/DSAPublicKey.java b/base/util/src/netscape/security/provider/DSAPublicKey.java new file mode 100644 index 000000000..89262809b --- /dev/null +++ b/base/util/src/netscape/security/provider/DSAPublicKey.java @@ -0,0 +1,133 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerValue; +import netscape.security.x509.AlgIdDSA; +import 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 = (DSAParameterSpec) algParams.getParameterSpec + (DSAParameterSpec.class); + return (DSAParams) 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()); + } + } +} diff --git a/base/util/src/netscape/security/provider/MD5.java b/base/util/src/netscape/security/provider/MD5.java new file mode 100644 index 000000000..d7aeacaee --- /dev/null +++ b/base/util/src/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 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 String algorithm; + + 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] = (int) buf[i * 4 + offset] & 0xff; + for (int j = 1; j < 4; j++) { + x[i] += ((int) 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]; + 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 = (int[]) this.state.clone(); + that.transformBuffer = (int[]) this.transformBuffer.clone(); + that.buffer = (byte[]) this.buffer.clone(); + that.digestBits = (byte[]) this.digestBits.clone(); + that.count = this.count; + return that; + } catch (CloneNotSupportedException e) { + } + return that; + } +} diff --git a/base/util/src/netscape/security/provider/RSAPublicKey.java b/base/util/src/netscape/security/provider/RSAPublicKey.java new file mode 100644 index 000000000..4c65b4fa0 --- /dev/null +++ b/base/util/src/netscape/security/provider/RSAPublicKey.java @@ -0,0 +1,152 @@ +// --- 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.provider; + +import java.io.IOException; +import java.io.Serializable; +import java.security.InvalidKeyException; + +import netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.AlgorithmId; +import 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()); + } + } + +} diff --git a/base/util/src/netscape/security/provider/SHA.java b/base/util/src/netscape/security/provider/SHA.java new file mode 100644 index 000000000..42a6b8b90 --- /dev/null +++ b/base/util/src/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 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. + * + * <p> + * 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/base/util/src/netscape/security/provider/Sun.java b/base/util/src/netscape/security/provider/Sun.java new file mode 100644 index 000000000..df384aeab --- /dev/null +++ b/base/util/src/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 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", "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", + "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", "netscape.security.provider.MD5"); + put("MessageDigest.SHA-1", "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", + "netscape.security.provider.DSAParameterGenerator"); + + /* + * Algorithm Parameter engines + */ + put("AlgorithmParameters.DSA", + "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", "netscape.security.provider.DSAKeyFactory"); + + } finally { + // AccessController.endPrivileged(); + } + } +} diff --git a/base/util/src/netscape/security/provider/X509CertificateFactory.java b/base/util/src/netscape/security/provider/X509CertificateFactory.java new file mode 100644 index 000000000..9780983a5 --- /dev/null +++ b/base/util/src/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 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 netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509ExtensionException; + +public class X509CertificateFactory extends CertificateFactorySpi { + + public Certificate engineGenerateCertificate(InputStream inStream) + throws CertificateException { + return new X509CertImpl(inStream); + } + + public Collection<Certificate> 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<CRL> engineGenerateCRLs(InputStream inStream) + throws CRLException { + return null; + } + +} diff --git a/base/util/src/netscape/security/util/ASN1CharStrConvMap.java b/base/util/src/netscape/security/util/ASN1CharStrConvMap.java new file mode 100644 index 000000000..c9c364f4f --- /dev/null +++ b/base/util/src/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 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. + * + * <p> + * 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<Byte> 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<Byte, Charset> charsets = new HashMap<Byte, Charset>(); + + 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/base/util/src/netscape/security/util/ASN1CharsetProvider.java b/base/util/src/netscape/security/util/ASN1CharsetProvider.java new file mode 100644 index 000000000..1de1c3c48 --- /dev/null +++ b/base/util/src/netscape/security/util/ASN1CharsetProvider.java @@ -0,0 +1,30 @@ +package 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<String, Charset> charsets = new HashMap<String, Charset>(); + + public ASN1CharsetProvider() { + addCharset(new PrintableCharset()); + addCharset(new IA5Charset()); + addCharset(new UniversalCharset()); + } + + public Iterator<Charset> 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/base/util/src/netscape/security/util/BigInt.java b/base/util/src/netscape/security/util/BigInt.java new file mode 100644 index 000000000..9210648f1 --- /dev/null +++ b/base/util/src/netscape/security/util/BigInt.java @@ -0,0 +1,210 @@ +// --- 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.util; + +import java.math.BigInteger; + +/** + * A low-overhead arbitrary-precision <em>unsigned</em> 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. + * + * <P> + * <em><b>NOTE:</b> 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 { + + // 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) + ((int) 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 <code>java.math.BigInteger</code>). + */ + 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; + } + + /** + * 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/base/util/src/netscape/security/util/BitArray.java b/base/util/src/netscape/security/util/BitArray.java new file mode 100644 index 000000000..ab77c226e --- /dev/null +++ b/base/util/src/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 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 = (byte[]) 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 (byte[]) 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/base/util/src/netscape/security/util/ByteArrayLexOrder.java b/base/util/src/netscape/security/util/ByteArrayLexOrder.java new file mode 100644 index 000000000..2ee2f740e --- /dev/null +++ b/base/util/src/netscape/security/util/ByteArrayLexOrder.java @@ -0,0 +1,58 @@ +// --- 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.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<byte[]> { + + /** + * 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 <code>ClassCastException</code> 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/base/util/src/netscape/security/util/ByteArrayTagOrder.java b/base/util/src/netscape/security/util/ByteArrayTagOrder.java new file mode 100644 index 000000000..e57a3b5f1 --- /dev/null +++ b/base/util/src/netscape/security/util/ByteArrayTagOrder.java @@ -0,0 +1,44 @@ +// --- 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.util; + +import java.util.Comparator; + +public class ByteArrayTagOrder implements Comparator<byte[]> { + + /** + * 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 <code>ClassCastException</code> 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/base/util/src/netscape/security/util/CertPrettyPrint.java b/base/util/src/netscape/security/util/CertPrettyPrint.java new file mode 100644 index 000000000..3a8c65fd0 --- /dev/null +++ b/base/util/src/netscape/security/util/CertPrettyPrint.java @@ -0,0 +1,345 @@ +// --- 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.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 netscape.security.x509.CertificateExtensions; +import netscape.security.x509.CertificateX509Key; +import netscape.security.x509.Extension; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509CertInfo; +import netscape.security.x509.X509Key; + +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.pkcs7.ContentInfo; +import org.mozilla.jss.pkcs7.SignedData; + +/** + * 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 Certificate mCert = 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) { + String content = ""; + + 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 += print.toString(Locale.getDefault()); + content += "\n"; + } + + return content; + } + } + + return content; + } + + public String stripCertBrackets(String s) { + if (s == null) { + return s; + } + + if ((s.startsWith("-----BEGIN CERTIFICATE-----")) && + (s.endsWith("-----END CERTIFICATE-----"))) { + 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) { + String val = ""; + + 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 += s.charAt(i); + } + return val; + } + + 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 = (Extension) 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", "SHA1", "SHA256", "SHA512" }; + String certFingerprints = ""; + + 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 += pp.indent(12) + hashes[i] + ":\n" + + pp.toHexString(md.digest(), 16, 16); + } + + sb.append(certFingerprints); + } catch (Exception e) { + } + + return sb.toString(); + } + +} diff --git a/base/util/src/netscape/security/util/CrlPrettyPrint.java b/base/util/src/netscape/security/util/CrlPrettyPrint.java new file mode 100644 index 000000000..edf1217ea --- /dev/null +++ b/base/util/src/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 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 netscape.security.x509.CRLExtensions; +import netscape.security.x509.Extension; +import netscape.security.x509.RevokedCertificate; +import 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<RevokedCertificate> revokedCerts = mCRL.getRevokedCertificates(); + + if (revokedCerts != null) { + Iterator<RevokedCertificate> 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 = (Extension) 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 = (Extension) 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/base/util/src/netscape/security/util/DerEncoder.java b/base/util/src/netscape/security/util/DerEncoder.java new file mode 100644 index 000000000..c2eb64fc0 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/util/DerInputBuffer.java b/base/util/src/netscape/security/util/DerInputBuffer.java new file mode 100644 index 000000000..7534f3d06 --- /dev/null +++ b/base/util/src/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 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() { + int len = available(); + if (len <= 0) + return null; + 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/base/util/src/netscape/security/util/DerInputStream.java b/base/util/src/netscape/security/util/DerInputStream.java new file mode 100644 index 000000000..20ced6757 --- /dev/null +++ b/base/util/src/netscape/security/util/DerInputStream.java @@ -0,0 +1,662 @@ +// --- 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.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). + * + * <P> + * 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. + * + * <P> + * 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 <em>data</em> 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. + */ + public byte[] toByteArray() { + 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<DerValue> vec = new Vector<DerValue>(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] = (DerValue) 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(); + } + + /** + * 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 * 1000; + } + 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 * 1000; + } + 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 * 1000; + } + 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 * 1000; + } + 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 <code>reset</code> will return here. + */ + public void mark(int value) { + buffer.mark(value); + } + + /** + * Return to the position of the last <code>mark</code> 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/base/util/src/netscape/security/util/DerOutputStream.java b/base/util/src/netscape/security/util/DerOutputStream.java new file mode 100644 index 000000000..62290d604 --- /dev/null +++ b/base/util/src/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 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. + * + * <P> + * 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 <em>DerValue.tag_Sequence</em> + * @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 <em>DerValue.tag_Sequence</em> + * @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 + * + * <pre> + * <em> <field> [N] IMPLICIT <type></em> + * </pre> + * + * For example, <em>FooLength [1] IMPLICIT INTEGER</em>, 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<byte[]> 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. + * + * <P> + * 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. + * + * <P> + * 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 <code>DerOutputStream</code> to an <code>OutputStream</code>. + * + * @exception IOException on output error. + */ + public void derEncode(OutputStream out) throws IOException { + out.write(toByteArray()); + } +} diff --git a/base/util/src/netscape/security/util/DerValue.java b/base/util/src/netscape/security/util/DerValue.java new file mode 100644 index 000000000..71b6f7f2c --- /dev/null +++ b/base/util/src/netscape/security/util/DerValue.java @@ -0,0 +1,715 @@ +// --- 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.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 netscape.security.x509.AVAValueConverter; +import 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. + * + * <P> + * All DER-encoded data are triples <em>{type, length, data}</em>. This class represents such tagged values as they have + * been read (or constructed), and provides structured access to the encoded data. + * + * <P> + * 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; + + // 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(); + } + + /* + * @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; + } + + /** + * 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/base/util/src/netscape/security/util/ExtPrettyPrint.java b/base/util/src/netscape/security/util/ExtPrettyPrint.java new file mode 100644 index 000000000..90d0d094f --- /dev/null +++ b/base/util/src/netscape/security/util/ExtPrettyPrint.java @@ -0,0 +1,1653 @@ +// --- 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.util; + +import java.math.BigInteger; +import java.text.DateFormat; +import java.util.Enumeration; +import java.util.ResourceBundle; +import java.util.Vector; + +import netscape.security.extensions.AccessDescription; +import netscape.security.extensions.AuthInfoAccessExtension; +import netscape.security.extensions.CertificateScopeEntry; +import netscape.security.extensions.CertificateScopeOfUseExtension; +import netscape.security.extensions.ExtendedKeyUsageExtension; +import netscape.security.extensions.InhibitAnyPolicyExtension; +import netscape.security.extensions.NSCertTypeExtension; +import netscape.security.extensions.OCSPNoCheckExtension; +import netscape.security.extensions.PresenceServerExtension; +import netscape.security.extensions.SubjectInfoAccessExtension; +import netscape.security.x509.Attribute; +import netscape.security.x509.AuthorityKeyIdentifierExtension; +import netscape.security.x509.BasicConstraintsExtension; +import netscape.security.x509.CPSuri; +import netscape.security.x509.CRLDistributionPoint; +import netscape.security.x509.CRLDistributionPointsExtension; +import netscape.security.x509.CRLDistributionPointsExtension.Reason; +import netscape.security.x509.CRLNumberExtension; +import netscape.security.x509.CRLReasonExtension; +import netscape.security.x509.CertificateIssuerExtension; +import netscape.security.x509.CertificatePoliciesExtension; +import netscape.security.x509.CertificatePolicyInfo; +import netscape.security.x509.CertificatePolicyMap; +import netscape.security.x509.DeltaCRLIndicatorExtension; +import netscape.security.x509.DisplayText; +import netscape.security.x509.Extension; +import netscape.security.x509.FreshestCRLExtension; +import netscape.security.x509.GeneralName; +import netscape.security.x509.GeneralNameInterface; +import netscape.security.x509.GeneralNames; +import netscape.security.x509.HoldInstructionExtension; +import netscape.security.x509.InvalidityDateExtension; +import netscape.security.x509.IssuerAlternativeNameExtension; +import netscape.security.x509.IssuingDistributionPoint; +import netscape.security.x509.IssuingDistributionPointExtension; +import netscape.security.x509.KeyIdentifier; +import netscape.security.x509.KeyUsageExtension; +import netscape.security.x509.NSCCommentExtension; +import netscape.security.x509.NameConstraintsExtension; +import netscape.security.x509.NoticeReference; +import netscape.security.x509.OIDMap; +import netscape.security.x509.PolicyConstraintsExtension; +import netscape.security.x509.PolicyMappingsExtension; +import netscape.security.x509.PolicyQualifierInfo; +import netscape.security.x509.PolicyQualifiers; +import netscape.security.x509.PrivateKeyUsageExtension; +import netscape.security.x509.Qualifier; +import netscape.security.x509.RDN; +import netscape.security.x509.SerialNumber; +import netscape.security.x509.SubjectAlternativeNameExtension; +import netscape.security.x509.SubjectDirAttributesExtension; +import netscape.security.x509.SubjectKeyIdentifierExtension; +import 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 ResourceBundle resource = 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 + try { + 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(); + } catch (Exception e) { + return ""; + } + + } + + /*========================================================== + * Private methods + *==========================================================*/ + + private String getNSCCommentExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getNameConstraintsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getOCSPNoCheckExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getSubjectInfoAccessExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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 = (AccessDescription) + 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(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getAuthInfoAccessExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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 = (AccessDescription) + 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(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getPresenceServerExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getPrivateKeyUsageExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getExtendedKeyUsageExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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<ObjectIdentifier> e = usage.getOIDs(); + + if (e != null) { + while (e.hasMoreElements()) { + ObjectIdentifier oid = (ObjectIdentifier) 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(); + } catch (Exception e) { + 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 (Exception e) { + 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 (Exception e) { + 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 (Exception e) { + 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 (Exception e) { + 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 (((Extension) 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(); + try { + 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 (((Extension) 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(); + } catch (Exception e) { + return ""; + } + } + + /** + * 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 (((Extension) 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 (Exception e) { + 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 (((Extension) 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 (Exception e) { + 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 (((Extension) 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 (Exception e) { + 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 (((Extension) 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 (Exception 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 (((Extension) 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 (Exception e) { + return ""; + } + } + + /** + * String Representation of CertificateScopeOfUse Extension + */ + private String getCertificateScopeOfUseExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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<CertificateScopeEntry> 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 = (CertificateScopeEntry) 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(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of FreshestCRLExtension + */ + private String getFreshestCRLExtension() { + StringBuffer sb = new StringBuffer(); + + try { + + // + // 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 (((Extension) 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) + "<i>empty</i>\n"); + } + + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of CRLDistributionPointsExtension + */ + private String getCRLDistributionPointsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + + // + // 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 (((Extension) 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) + "<i>empty</i>\n"); + } + + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + 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(); + + try { + 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 (((Extension) 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(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of InvalidityDateExtension + */ + private String getInvalidityDateExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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 (((Extension) 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(); + } catch (Exception e) { + return ""; + } + } + + /** + * 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 (((Extension) 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 (Exception e) { + return ""; + } + } + + /** + * String Representation of HoldInstructionExtension + */ + private String getHoldInstructionExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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 (((Extension) 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(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of PolicyConstraintsExtension + */ + private String getPolicyConstraintsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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 (((Extension) 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(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of PolicyMappingsExtension + */ + private String getPolicyMappingsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + 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 (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + PolicyMappingsExtension ext = (PolicyMappingsExtension) mExt; + Enumeration<CertificatePolicyMap> 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 = + (CertificatePolicyMap) 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(); + } catch (Throwable e) { + return ""; + } + } + + /** + * 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 (((Extension) 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<Attribute> 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 = (Attribute) 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<String> 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 = (String) 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<CertificatePolicyInfo> cpv = (Vector<CertificatePolicyInfo>) cp.get("infos"); + Enumeration<CertificatePolicyInfo> 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 (Exception e) { + return sb.toString(); + } + } + +} diff --git a/base/util/src/netscape/security/util/IA5Charset.java b/base/util/src/netscape/security/util/IA5Charset.java new file mode 100644 index 000000000..db6da2331 --- /dev/null +++ b/base/util/src/netscape/security/util/IA5Charset.java @@ -0,0 +1,24 @@ +package 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/base/util/src/netscape/security/util/IA5CharsetDecoder.java b/base/util/src/netscape/security/util/IA5CharsetDecoder.java new file mode 100644 index 000000000..620d65aca --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/util/IA5CharsetEncoder.java b/base/util/src/netscape/security/util/IA5CharsetEncoder.java new file mode 100644 index 000000000..dad0c9a2d --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/util/ObjectIdentifier.java b/base/util/src/netscape/security/util/ObjectIdentifier.java new file mode 100644 index 000000000..6ff02d1b0 --- /dev/null +++ b/base/util/src/netscape/security/util/ObjectIdentifier.java @@ -0,0 +1,426 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.util; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Hashtable; +import java.util.StringTokenizer; + +/** + * Represent an ISO Object Identifier. + * + * <P> + * 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. + * + * <P> + * 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 int[componentLen]; + + start = 0; + int i = 0; + String comp = null; + while ((end = oid.indexOf(ch, start)) != -1) { + comp = oid.substring(start, end); + components[i++] = Integer.valueOf(comp).intValue(); + start = end + 1; + } + comp = oid.substring(start); + components[i] = Integer.valueOf(comp).intValue(); + } + + /** + * Constructs an object ID from an array of integers. This + * is used to construct constant object IDs. + */ + public ObjectIdentifier(int values[]) { + try { + components = (int[]) values.clone(); + componentLen = values.length; + } 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 }. + * + * <P> + * <STRONG>NOTE:</STRONG> 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. + */ + int component; + boolean first_subid = true; + + for (components = new int[allocationQuantum], componentLen = 0; in.available() > bufferEnd;) { + component = getComponent(in); + + if (first_subid) { + int 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 < 40) + X = 0; + else if (component < 80) + X = 1; + else + X = 2; + Y = component - (X * 40); + + components[0] = X; + components[1] = 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) { + int tmp_components[]; + + tmp_components = new int[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; + + bytes.write((components[0] * 40) + components[1]); + for (i = 2; i < componentLen; i++) + putComponent(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 int getComponent(DerInputStream in) + throws IOException { + int retval, i, tmp; + + for (i = 0, retval = 0; i < 4; i++) { + retval <<= 7; + tmp = in.getByte(); + retval |= (tmp & 0x07f); + if ((tmp & 0x080) == 0) + return retval; + } + + throw new IOException("X509.OID, component value too big"); + } + + /* + * 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 putComponent(DerOutputStream out, int val) + throws IOException { + int i; + byte buf[] = new byte[4]; + + for (i = 0; i < 4; i++) { + buf[i] = (byte) (val & 0x07f); + val >>>= 7; + if (val == 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 <em>other</em> 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] < components[i]) + 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] != other.components[i]) + 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]; + 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() { + String retval; + int i; + + for (i = 0, retval = ""; i < componentLen; i++) { + if (i != 0) + retval += "."; + retval += components[i]; + } + return retval; + } + + /* + * To simplify, we assume no individual component of an object ID is + * larger than 32 bits. Then we represent the path from the root as + * an array that's (usually) only filled at the beginning. + */ + private int 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<String, ObjectIdentifier> mOIDs = new Hashtable<String, ObjectIdentifier>(); + + 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 { + String retval; + int i; + + for (i = 0, retval = ""; i < values.length; i++) { + if (i != 0) + retval += "."; + retval += values[i]; + } + return getObjectIdentifier(retval); + } +} diff --git a/base/util/src/netscape/security/util/PrettyPrintFormat.java b/base/util/src/netscape/security/util/PrettyPrintFormat.java new file mode 100644 index 000000000..25bc23d26 --- /dev/null +++ b/base/util/src/netscape/security/util/PrettyPrintFormat.java @@ -0,0 +1,160 @@ +// --- 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.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) { + 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/base/util/src/netscape/security/util/PrettyPrintResources.java b/base/util/src/netscape/security/util/PrettyPrintResources.java new file mode 100644 index 000000000..a3f068f64 --- /dev/null +++ b/base/util/src/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 netscape.security.util; + +import java.util.ListResourceBundle; + +import netscape.security.extensions.NSCertTypeExtension; +import 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/base/util/src/netscape/security/util/PrintableCharset.java b/base/util/src/netscape/security/util/PrintableCharset.java new file mode 100644 index 000000000..a9cf15c04 --- /dev/null +++ b/base/util/src/netscape/security/util/PrintableCharset.java @@ -0,0 +1,46 @@ +package 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/base/util/src/netscape/security/util/PrintableCharsetDecoder.java b/base/util/src/netscape/security/util/PrintableCharsetDecoder.java new file mode 100644 index 000000000..014095494 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/util/PrintableCharsetEncoder.java b/base/util/src/netscape/security/util/PrintableCharsetEncoder.java new file mode 100644 index 000000000..bc658096a --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/util/PubKeyPrettyPrint.java b/base/util/src/netscape/security/util/PubKeyPrettyPrint.java new file mode 100644 index 000000000..46c007cd9 --- /dev/null +++ b/base/util/src/netscape/security/util/PubKeyPrettyPrint.java @@ -0,0 +1,121 @@ +// --- 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.util; + +import java.security.PublicKey; +import java.util.Locale; +import java.util.ResourceBundle; + +import netscape.security.provider.RSAPublicKey; +import 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 (Exception e) { + } + + return sb.toString(); + } +} diff --git a/base/util/src/netscape/security/util/UniversalCharset.java b/base/util/src/netscape/security/util/UniversalCharset.java new file mode 100644 index 000000000..59cc3c6f4 --- /dev/null +++ b/base/util/src/netscape/security/util/UniversalCharset.java @@ -0,0 +1,24 @@ +package 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/base/util/src/netscape/security/util/UniversalCharsetDecoder.java b/base/util/src/netscape/security/util/UniversalCharsetDecoder.java new file mode 100644 index 000000000..a41c5ad50 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/util/UniversalCharsetEncoder.java b/base/util/src/netscape/security/util/UniversalCharsetEncoder.java new file mode 100644 index 000000000..cd2a51296 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/x509/ACertAttrSet.java b/base/util/src/netscape/security/x509/ACertAttrSet.java new file mode 100755 index 000000000..8a757d7f5 --- /dev/null +++ b/base/util/src/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 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 netscape.security.util.DerOutputStream; +import 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 <code>decode</code> 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<String> 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/base/util/src/netscape/security/x509/AVA.java b/base/util/src/netscape/security/x509/AVA.java new file mode 100644 index 000000000..ad94c2c61 --- /dev/null +++ b/base/util/src/netscape/security/x509/AVA.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 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 netscape.security.util.DerEncoder; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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. + * + * <P> + * 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. <br> + * 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; + } + + /** + * 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 <code>DerEncoder</code> 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 { + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream tmp2 = 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/base/util/src/netscape/security/x509/AVAValueConverter.java b/base/util/src/netscape/security/x509/AVAValueConverter.java new file mode 100644 index 000000000..cd3ce7616 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import 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. + * + * <P> + * 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/base/util/src/netscape/security/x509/AlgIdDSA.java b/base/util/src/netscape/security/x509/AlgIdDSA.java new file mode 100644 index 000000000..0a64ad37b --- /dev/null +++ b/base/util/src/netscape/security/x509/AlgIdDSA.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 netscape.security.x509; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.ProviderException; +import java.security.interfaces.DSAParams; + +import netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class identifies DSS/DSA Algorithm variants, which are distinguished + * by using different algorithm parameters <em>P, Q, G</em>. It uses the + * NIST/IETF standard DER encoding. These are used to implement the Digital + * Signature Standard (DSS), FIPS 186. + * + * <P> + * <em><b>NOTE:</b> 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. </em> + * + * @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; + } + + /** + * Default constructor. The OID and parameters must be + * deserialized before this algorithm ID is used. + */ + // XXX deprecated for general use + public AlgIdDSA() { + } + + 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 { + 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"; + } +} diff --git a/base/util/src/netscape/security/x509/AlgorithmId.java b/base/util/src/netscape/security/x509/AlgorithmId.java new file mode 100644 index 000000000..b0113af41 --- /dev/null +++ b/base/util/src/netscape/security/x509/AlgorithmId.java @@ -0,0 +1,767 @@ +// --- 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.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; +import java.security.AlgorithmParameters; +import java.security.NoSuchAlgorithmException; + +import netscape.security.util.DerEncoder; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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. + * + * <P> + * 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). + * + * <P> + * 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; + + /* Are we debugging? */ + private static boolean debug = false; + + /** + * 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; + + /** + * Returns one of the algorithm IDs most commonly associated + * with this algorithm name. + * + * @param algname the name being used + * @deprecated use the short get form of this method. + * @exception NoSuchAlgorithmException on error. + */ + public static AlgorithmId getAlgorithmId(String algname) + throws NoSuchAlgorithmException { + return get(algname); + } + + 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 <code>DerEncoder</code> 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 { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + bytes.putOID(algid); + 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 { + DerOutputStream out = new DerOutputStream(); + DerOutputStream bytes = new DerOutputStream(); + + bytes.putOID(algid); + 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("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("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.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.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 <code>getName</code> 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; + } + + /** + * 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 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 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 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 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 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 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", "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", "SHA512withRSA", "SHA1withEC", + "SHA256withEC", "SHA384withEC", "SHA512withEC" }; + +} diff --git a/base/util/src/netscape/security/x509/Attribute.java b/base/util/src/netscape/security/x509/Attribute.java new file mode 100644 index 000000000..11e22db10 --- /dev/null +++ b/base/util/src/netscape/security/x509/Attribute.java @@ -0,0 +1,325 @@ +// --- 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.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerEncoder; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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: + * + * <pre> + * + * Attribute ::= SEQUENCE { + * type AttributeType, + * value SET OF AttributeValue + * -- at least one value is required --} + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY + * + * </pre> + * + * 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<String> valueSet = new Vector<String>(); + 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<String> 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<String> 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<String> 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<String> 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<String> 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 <code>DerEncoder</code> 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"; + String values = "Values: "; + Enumeration<String> n = valueSet.elements(); + if (n.hasMoreElements()) { + values += n.nextElement(); + while (n.hasMoreElements()) + values += "," + n.nextElement(); + } + return theoid + values + "\n"; + } + + //========== PRIVATE METHODS ================================== + + //encode the attribute object + private void encodeThis(OutputStream out) + throws IOException { + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream tmp2 = 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 { + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream tmp2 = 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<String> 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/base/util/src/netscape/security/x509/AuthorityKeyIdentifierExtension.java b/base/util/src/netscape/security/x509/AuthorityKeyIdentifierExtension.java new file mode 100644 index 000000000..91b6c2598 --- /dev/null +++ b/base/util/src/netscape/security/x509/AuthorityKeyIdentifierExtension.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 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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class represents the Authority Key Identifier Extension. + * + * <p> + * 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). + * <p> + * The ASN.1 syntax for this is: + * + * <pre> + * AuthorityKeyIdentifier ::= SEQUENCE { + * keyIdentifier [0] KeyIdentifier OPTIONAL, + * authorityCertIssuer [1] GeneralNames OPTIONAL, + * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL + * } + * KeyIdentifier ::= OCTET STRING + * </pre> + * + * @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 { + DerOutputStream seq = new DerOutputStream(); + DerOutputStream tmp = 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.toString()); + } + 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.toString()); + } + + } 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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/base/util/src/netscape/security/x509/BasicConstraintsExtension.java b/base/util/src/netscape/security/x509/BasicConstraintsExtension.java new file mode 100644 index 000000000..2688e961d --- /dev/null +++ b/base/util/src/netscape/security/x509/BasicConstraintsExtension.java @@ -0,0 +1,295 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class represents the Basic Constraints Extension. + * + * <p> + * 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. + * + * <pre> + * The ASN.1 syntax for this extension is: + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL + * } + * </pre> + * + * @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 { + 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 (new Boolean(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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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/base/util/src/netscape/security/x509/CPSuri.java b/base/util/src/netscape/security/x509/CPSuri.java new file mode 100644 index 000000000..d0a2e0762 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/CRLDistributionPoint.java b/base/util/src/netscape/security/x509/CRLDistributionPoint.java new file mode 100644 index 000000000..c7ad84389 --- /dev/null +++ b/base/util/src/netscape/security/x509/CRLDistributionPoint.java @@ -0,0 +1,467 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import netscape.security.util.BitArray; +import netscape.security.util.DerOutputStream; +import 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; + +/** + * <pre> + * 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) } + * </pre> + */ +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 <code>fullName</code> of the <code>DistributionPointName</code>, which may be <code>null</code>. + */ + public GeneralNames getFullName() { + return fullName; + } + + /** + * Returns the <code>relativeName</code> of the <code>DistributionPointName</code>, which may be <code>null</code>. + */ + public RDN getRelativeName() { + return relativeName; + } + + /** + * Sets the <code>fullName</code> of the <code>DistributionPointName</code>. It may be set to <code>null</code>. + * If it is set to a non-null value, <code>relativeName</code> will be + * set to <code>null</code>, 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 <code>relativeName</code> of the <code>DistributionPointName</code>. It may be set to <code>null</code>. + * If it is set to a non-null value, <code>fullName</code> will be + * set to <code>null</code>, 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 <code>null</code>. + */ + public BitArray getReasons() { + return reasons; + } + + /** + * Sets the reason flags for this distribution point. May be set to <code>null</code>. + */ + public void setReasons(BitArray reasons) { + this.reasons = reasons; + } + + /** + * Returns the CRLIssuer for the CRL at this distribution point. + * May be <code>null</code>. + */ + public GeneralNames getCRLIssuer() { + return CRLIssuer; + } + + /** + * Sets the CRLIssuer for the CRL at this distribution point. + * May be set to <code>null</code>. + * + * @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[]) { + try { + if (args.length != 1) { + System.out.println("Usage: CRLDistributionPoint <outfile>"); + System.exit(-1); + } + + ByteArrayOutputStream 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(); + (new FileOutputStream(args[0])).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"); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 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/base/util/src/netscape/security/x509/CRLDistributionPointsExtension.java b/base/util/src/netscape/security/x509/CRLDistributionPointsExtension.java new file mode 100644 index 000000000..c939a7431 --- /dev/null +++ b/base/util/src/netscape/security/x509/CRLDistributionPointsExtension.java @@ -0,0 +1,391 @@ +// --- 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.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 netscape.security.util.BitArray; +import netscape.security.util.DerOutputStream; + +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; + +/** + * An extension that tells applications where to find the CRL for + * this certificate. + * + * <pre> + * 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) } + * </pre> + */ +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[]) ((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.toString()); + } + } 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"; + + static { + try { + OIDMap.addAttribute(CRLDistributionPointsExtension.class.getName(), + OID, NAME); + } catch (CertificateException e) { + } + } + + 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<String> getAttributeNames() { + return (new Vector<String>()).elements(); + } + + public String getName() { + return NAME; + } + + /** + * Test driver. + */ + public static void main(String args[]) { + + try { + + if (args.length != 1) { + System.out.println("Usage: CRLDistributionPointsExtentions " + + "<outfile>"); + System.exit(-1); + } + + BufferedOutputStream 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); + + bos.close(); + + } catch (Exception 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<String, Reason> map = new Hashtable<String, Reason>(); + private static Vector<Reason> list = new Vector<Reason>(); + + 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<Reason> result = new Vector<Reason>(); + 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/base/util/src/netscape/security/x509/CRLExtensions.java b/base/util/src/netscape/security/x509/CRLExtensions.java new file mode 100755 index 000000000..bdadcc12e --- /dev/null +++ b/base/util/src/netscape/security/x509/CRLExtensions.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 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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class defines the CRL Extensions. + * + * @author Hemma Prafullchandra + * @version 1.4 + */ +public class CRLExtensions extends Vector<Extension> { + + /** + * + */ + private static final long serialVersionUID = 365767738692986418L; + private Hashtable<String, Extension> 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[] { new Boolean(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<String, Extension>(); + } + + /** + * 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<String, Extension>(); + 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<String, Extension>(); + 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 { + // #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); + + DerOutputStream tmp = new DerOutputStream(); + if (isExplicit) + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0), seq); + else + tmp = seq; + + out.write(tmp.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 = (Extension) 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<Extension> getElements() { + return (map.elements()); + } +} diff --git a/base/util/src/netscape/security/x509/CRLNumberExtension.java b/base/util/src/netscape/security/x509/CRLNumberExtension.java new file mode 100755 index 000000000..7c89b179f --- /dev/null +++ b/base/util/src/netscape/security/x509/CRLNumberExtension.java @@ -0,0 +1,226 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * Represent the CRL Number Extension. + * + * <p> + * 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"); + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(NUMBER); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CRLReasonExtension.java b/base/util/src/netscape/security/x509/CRLReasonExtension.java new file mode 100644 index 000000000..3c11fc70b --- /dev/null +++ b/base/util/src/netscape/security/x509/CRLReasonExtension.java @@ -0,0 +1,234 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * Represent the CRLReason Extension of CRL entry. + * + * <p> + * 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[]) ((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"); + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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; + } + +} diff --git a/base/util/src/netscape/security/x509/CertAndKeyGen.java b/base/util/src/netscape/security/x509/CertAndKeyGen.java new file mode 100644 index 000000000..1579d46bf --- /dev/null +++ b/base/util/src/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 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 netscape.security.pkcs.PKCS10; + +/** + * Generate a pair of keys, and provide access to them. This class is + * provided primarily for ease of use. + * + * <P> + * 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. + * + * <P> + * 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. + * <em>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.</em> + * + * <P> + * 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. + * + * <P> + * 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. + * + * <P> + * <STRONG><em>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!</em></STRONG> + */ + public PrivateKey getPrivateKey() { + return privateKey; + } + + /** + * Returns a self-signed X.509v1 certificate for the public key. + * The certificate is immediately valid. + * + * <P> + * 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 <a href = + * "#getSelfCertificate(netscape.security.x509.X500Name, long)"> + * + * @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. + * + * <P> + * 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 (X509Certificate) cert; + + } catch (IOException e) { + throw new CertificateEncodingException("getSelfCert: " + + e.getMessage()); + } + } + + /** + * Returns a PKCS #10 certificate request. The caller uses either <code>PKCS10.print</code> or + * <code>PKCS10.toByteArray</code> operations on the result, to get the request in an appropriate + * transmission format. + * + * <P> + * 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/base/util/src/netscape/security/x509/CertAttrSet.java b/base/util/src/netscape/security/x509/CertAttrSet.java new file mode 100755 index 000000000..7e8d6db82 --- /dev/null +++ b/base/util/src/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 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. + * <p> + * 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 <code>decode</code> 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<String> getAttributeNames(); + + /** + * Returns the name (identifier) of this CertAttrSet. + * + * @return the name of this CertAttrSet. + */ + String getName(); +} diff --git a/base/util/src/netscape/security/x509/CertException.java b/base/util/src/netscape/security/x509/CertException.java new file mode 100644 index 000000000..31d9e686e --- /dev/null +++ b/base/util/src/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 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 + * (<code>verf_*</code>) 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/base/util/src/netscape/security/x509/CertParseError.java b/base/util/src/netscape/security/x509/CertParseError.java new file mode 100644 index 000000000..7328c7207 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/x509/CertificateAlgorithmId.java b/base/util/src/netscape/security/x509/CertificateAlgorithmId.java new file mode 100644 index 000000000..41610844e --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificateAlgorithmId.java @@ -0,0 +1,189 @@ +// --- 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.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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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 synchronized void writeObject(ObjectOutputStream stream) + throws IOException { + encode(stream); + } + + private synchronized 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(ALGORITHM); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CertificateChain.java b/base/util/src/netscape/security/x509/CertificateChain.java new file mode 100644 index 000000000..b60325378 --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificateChain.java @@ -0,0 +1,137 @@ +// --- 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.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.security.cert.X509Certificate; + +import netscape.security.pkcs.ContentInfo; +import netscape.security.pkcs.PKCS7; +import 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 = (X509Certificate[]) 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 (X509Certificate[]) 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]"; + for (int i = 0; i < mChain.length; i++) { + s += mChain[i].toString(); + } + s += "]\n"; + return s; + } + + private X509Certificate[] mChain = null; +} diff --git a/base/util/src/netscape/security/x509/CertificateExtensions.java b/base/util/src/netscape/security/x509/CertificateExtensions.java new file mode 100644 index 000000000..b9667d8f6 --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificateExtensions.java @@ -0,0 +1,276 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package 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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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<Extension> + 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<String, Extension> map; + + // Parse the encoded extension + public void parseExtension(Extension ext) throws IOException { + try { + @SuppressWarnings("unchecked") + Class<CertAttrSet> extClass = (Class<CertAttrSet>) 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<CertAttrSet> 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[] { new Boolean(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 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 CertificateExtensions() { + map = new Hashtable<String, Extension>(); + } + + /** + * 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<String, Extension>(); + 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<String, Extension>(); + 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<String, Extension>(); + DerValue[] exts = str.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } + + private synchronized void writeObject(ObjectOutputStream stream) + throws CertificateException, IOException { + encode(stream); + } + + private synchronized 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 { + 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); + + DerOutputStream tmp = new DerOutputStream(); + 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<String> getNames() { + return map.keys(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration<Extension> getAttributes() { + return (map.elements()); + } + + public Enumeration<String> getAttributeNames() { + return (map.keys()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CertificateIssuerExtension.java b/base/util/src/netscape/security/x509/CertificateIssuerExtension.java new file mode 100644 index 000000000..774116bcc --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificateIssuerExtension.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 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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * Represent the CRL Certificate Issuer Extension. + * + * <p> + * 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.toString()); + } + 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.toString()); + } + } + + /** + * 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(CERTIFICATE_ISSUER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CertificateIssuerName.java b/base/util/src/netscape/security/x509/CertificateIssuerName.java new file mode 100644 index 000000000..a2f9026c1 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(DN_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CertificateIssuerUniqueIdentity.java b/base/util/src/netscape/security/x509/CertificateIssuerUniqueIdentity.java new file mode 100644 index 000000000..351116ffb --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(ID); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CertificatePoliciesExtension.java b/base/util/src/netscape/security/x509/CertificatePoliciesExtension.java new file mode 100644 index 000000000..fedc15917 --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificatePoliciesExtension.java @@ -0,0 +1,338 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; + +import com.netscape.cmsutil.util.Utils; + +/** + * This class defines the Certificate Policies Extension. + * + * <p> + * 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. + * + * <pre> + * CertificatePolicies ::= SEQUENECE OF PolicyInformation + * </pre> + * + * @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<CertificatePolicyInfo> mInfos; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + for (int i = 0; i < mInfos.size(); i++) { + ((CertificatePolicyInfo) mInfos.elementAt(i)).encode(tmp); + } + os.write(DerValue.tag_Sequence, tmp); + extensionValue = os.toByteArray(); + } + + public CertificatePoliciesExtension(boolean critical, Vector<CertificatePolicyInfo> 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<CertificatePolicyInfo> 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<CertificatePolicyInfo>(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<CertificatePolicyInfo>(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<CertificatePolicyInfo>) 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<Vector<CertificatePolicyInfo>> getAttributes() { + Vector<Vector<CertificatePolicyInfo>> elements = new Vector<Vector<CertificatePolicyInfo>>(); + elements.addElement(mInfos); + return (elements.elements()); + } + + private static final String[] NAMES = { INFOS }; + + @Override + public Enumeration<String> 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<CertificatePolicyInfo> infos = new Vector<CertificatePolicyInfo>(); + 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())); + } catch (IOException e) { + System.out.println(e.toString()); + } + } + +} diff --git a/base/util/src/netscape/security/x509/CertificatePolicyId.java b/base/util/src/netscape/security/x509/CertificatePolicyId.java new file mode 100644 index 000000000..bfc93b0bb --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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/base/util/src/netscape/security/x509/CertificatePolicyInfo.java b/base/util/src/netscape/security/x509/CertificatePolicyInfo.java new file mode 100644 index 000000000..33e541c65 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/CertificatePolicyMap.java b/base/util/src/netscape/security/x509/CertificatePolicyMap.java new file mode 100644 index 000000000..75ddf3314 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/CertificatePolicySet.java b/base/util/src/netscape/security/x509/CertificatePolicySet.java new file mode 100644 index 000000000..86d9c107f --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; +import java.util.Vector; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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<CertificatePolicyId> ids; + + /** + * The default constructor for this class. + * + * @param ids the sequence of CertificatePolicyId's. + */ + public CertificatePolicySet(Vector<CertificatePolicyId> 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<CertificatePolicyId>(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++) { + ((CertificatePolicyId) ids.elementAt(i)).encode(tmp); + } + out.write(DerValue.tag_Sequence, tmp); + } +} diff --git a/base/util/src/netscape/security/x509/CertificateSerialNumber.java b/base/util/src/netscape/security/x509/CertificateSerialNumber.java new file mode 100644 index 000000000..e9655178f --- /dev/null +++ b/base/util/src/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 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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(NUMBER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CertificateSubjectName.java b/base/util/src/netscape/security/x509/CertificateSubjectName.java new file mode 100644 index 000000000..6159638b9 --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificateSubjectName.java @@ -0,0 +1,203 @@ +// --- 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.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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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 synchronized void writeObject(ObjectOutputStream stream) + throws IOException { + encode(stream); + } + + private synchronized 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."); + } + } + + /** + * 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(DN_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CertificateSubjectUniqueIdentity.java b/base/util/src/netscape/security/x509/CertificateSubjectUniqueIdentity.java new file mode 100644 index 000000000..51687e86d --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(ID); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/CertificateValidity.java b/base/util/src/netscape/security/x509/CertificateValidity.java new file mode 100644 index 000000000..0c2c841b0 --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificateValidity.java @@ -0,0 +1,306 @@ +// --- 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.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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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 synchronized void writeObject(ObjectOutputStream stream) + throws IOException { + encode(stream); + } + + private synchronized 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"); + } + DerOutputStream pair = 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); + } + DerOutputStream seq = new DerOutputStream(); + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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 <code>Date</code> supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the <code>Date</code> 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/base/util/src/netscape/security/x509/CertificateVersion.java b/base/util/src/netscape/security/x509/CertificateVersion.java new file mode 100644 index 000000000..d3659779f --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificateVersion.java @@ -0,0 +1,247 @@ +// --- 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.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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; + } + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(new BigInt(version)); + + DerOutputStream seq = new DerOutputStream(); + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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/base/util/src/netscape/security/x509/CertificateX509Key.java b/base/util/src/netscape/security/x509/CertificateX509Key.java new file mode 100644 index 000000000..c7003bb8e --- /dev/null +++ b/base/util/src/netscape/security/x509/CertificateX509Key.java @@ -0,0 +1,190 @@ +// --- 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.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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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 synchronized void writeObject(ObjectOutputStream stream) + throws IOException { + encode(stream); + } + + private synchronized 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(KEY); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/DNSName.java b/base/util/src/netscape/security/x509/DNSName.java new file mode 100644 index 000000000..831f51cc1 --- /dev/null +++ b/base/util/src/netscape/security/x509/DNSName.java @@ -0,0 +1,82 @@ +// --- 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.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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); + } +} diff --git a/base/util/src/netscape/security/x509/DeltaCRLIndicatorExtension.java b/base/util/src/netscape/security/x509/DeltaCRLIndicatorExtension.java new file mode 100755 index 000000000..da870f4fd --- /dev/null +++ b/base/util/src/netscape/security/x509/DeltaCRLIndicatorExtension.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 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 netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * Represent the Delta CRL Indicator Extension. + * + * <p> + * 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"); + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(NUMBER); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/DirStrConverter.java b/base/util/src/netscape/security/x509/DirStrConverter.java new file mode 100644 index 000000000..776344c0a --- /dev/null +++ b/base/util/src/netscape/security/x509/DirStrConverter.java @@ -0,0 +1,171 @@ +// --- 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.x509; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetEncoder; + +import netscape.security.util.ASN1CharStrConvMap; +import 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. + * + * <p> + * 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_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/base/util/src/netscape/security/x509/DisplayText.java b/base/util/src/netscape/security/x509/DisplayText.java new file mode 100644 index 000000000..a379617a7 --- /dev/null +++ b/base/util/src/netscape/security/x509/DisplayText.java @@ -0,0 +1,82 @@ +// --- 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.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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 { + + /** 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/base/util/src/netscape/security/x509/EDIPartyName.java b/base/util/src/netscape/security/x509/EDIPartyName.java new file mode 100644 index 000000000..0c69242cc --- /dev/null +++ b/base/util/src/netscape/security/x509/EDIPartyName.java @@ -0,0 +1,154 @@ +// --- 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.x509; + +import java.io.IOException; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class defines the EDIPartyName of the GeneralName choice. + * The ASN.1 syntax for this is: + * + * <pre> + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + * </pre> + * + * @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((byte) 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((byte) 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) + 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/base/util/src/netscape/security/x509/Extension.java b/base/util/src/netscape/security/x509/Extension.java new file mode 100644 index 000000000..7dc9e2890 --- /dev/null +++ b/base/util/src/netscape/security/x509/Extension.java @@ -0,0 +1,199 @@ +// --- 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.x509; + +import java.io.IOException; +import java.io.Serializable; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; + +/** + * Represent a X509 Extension Attribute. + * + * <p> + * 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. + * + * <p> + * 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). + * + * <pre> + * ASN.1 definition of Extension: + * Extension ::= SEQUENCE { + * ExtensionId OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extensionValue OCTET STRING + * } + * </pre> + * + * @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 { + DerOutputStream bytes = new DerOutputStream(); + + if (extensionId == null) + throw new IOException("Null OID to encode for the extension!"); + + 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/base/util/src/netscape/security/x509/Extensions.java b/base/util/src/netscape/security/x509/Extensions.java new file mode 100644 index 000000000..622367ab6 --- /dev/null +++ b/base/util/src/netscape/security/x509/Extensions.java @@ -0,0 +1,226 @@ +// --- 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.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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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<Extension> + 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<String, Extension> map; + + // Parse the encoded extension + public void parseExtension(Extension ext) throws IOException { + try { + @SuppressWarnings("unchecked") + Class<CertAttrSet> extClass = (Class<CertAttrSet>) 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<CertAttrSet> 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[] { new Boolean(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<String, Extension>(); + } + + /** + * 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<String, Extension>(); + 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<String, Extension>(); + 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"); + } + + 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<String> getAttributeNames() { + return map.keys(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/FreshestCRLExtension.java b/base/util/src/netscape/security/x509/FreshestCRLExtension.java new file mode 100644 index 000000000..c749ae04e --- /dev/null +++ b/base/util/src/netscape/security/x509/FreshestCRLExtension.java @@ -0,0 +1,396 @@ +// --- 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.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 netscape.security.util.BitArray; +import netscape.security.util.DerOutputStream; + +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; + +/** + * An extension that tells applications where to find + * the latest (freshest) delta CRL for this certificate + * or full CRL. + * + * <pre> + * 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) } + * </pre> + */ +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[]) ((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.toString()); + } + } 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. + ///////////////////////////////////////////////////////////// + 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<String> getAttributeNames() { + return (new Vector<String>()).elements(); + } + + public String getName() { + return NAME; + } + + /** + * Test driver. + */ + public static void main(String args[]) { + + try { + + if (args.length != 1) { + System.out.println("Usage: FreshestCRLExtentions " + + "<outfile>"); + System.exit(-1); + } + + BufferedOutputStream 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); + + bos.close(); + + } catch (Exception 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<String, Reason> map = new Hashtable<String, Reason>(); + private static Vector<Reason> list = new Vector<Reason>(); + + 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<Reason> result = new Vector<Reason>(); + for (int i = 0; i < size; i++) { + Reason r = (Reason) 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/base/util/src/netscape/security/x509/GeneralName.java b/base/util/src/netscape/security/x509/GeneralName.java new file mode 100644 index 000000000..5ed98d830 --- /dev/null +++ b/base/util/src/netscape/security/x509/GeneralName.java @@ -0,0 +1,199 @@ +// --- 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.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class implements the ASN.1 GeneralName object class. + * <p> + * The ASN.1 syntax for this is: + * + * <pre> + * 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 + * } + * </pre> + * + * @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); + } + } +} diff --git a/base/util/src/netscape/security/x509/GeneralNameInterface.java b/base/util/src/netscape/security/x509/GeneralNameInterface.java new file mode 100644 index 000000000..4a9673663 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import 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/base/util/src/netscape/security/x509/GeneralNames.java b/base/util/src/netscape/security/x509/GeneralNames.java new file mode 100644 index 000000000..9e06db5ac --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This object class represents the GeneralNames type required in + * X509 certificates. + * <p> + * The ASN.1 syntax for this is: + * + * <pre> + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * </pre> + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.7 + */ +public class GeneralNames extends Vector<GeneralNameInterface> { + /** + * + */ + private static final long serialVersionUID = 3204492869396713312L; + + /** + * Create the GeneralNames, decoding from the passed DerValue. + * + * <b>Caution when using this constructor. It may be broken! + * Better to call addElement(gni) directly where gni is + * a GeneralNameInterface object </b> + * + * @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<GeneralNameInterface> 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/base/util/src/netscape/security/x509/GeneralNamesException.java b/base/util/src/netscape/security/x509/GeneralNamesException.java new file mode 100644 index 000000000..6309ed114 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/x509/GeneralSubtree.java b/base/util/src/netscape/security/x509/GeneralSubtree.java new file mode 100644 index 000000000..635427e0b --- /dev/null +++ b/base/util/src/netscape/security/x509/GeneralSubtree.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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.PrettyPrintFormat; + +/** + * Represent the GeneralSubtree ASN.1 object, whose syntax is: + * + * <pre> + * GeneralSubtree ::= SEQUENCE { + * base GeneralName, + * minimum [0] BaseDistance DEFAULT 0, + * maximum [1] BaseDistance OPTIONAL + * } + * BaseDistance ::= INTEGER (0..MAX) + * </pre> + * + * @version 1.5 + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class GeneralSubtree { + 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 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/base/util/src/netscape/security/x509/GeneralSubtrees.java b/base/util/src/netscape/security/x509/GeneralSubtrees.java new file mode 100644 index 000000000..37097ca71 --- /dev/null +++ b/base/util/src/netscape/security/x509/GeneralSubtrees.java @@ -0,0 +1,106 @@ +// --- 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.x509; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.PrettyPrintFormat; + +/** + * Represent the GeneralSubtrees ASN.1 object. + * + * @version 1.4 + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class GeneralSubtrees { + private Vector<GeneralSubtree> trees; + private PrettyPrintFormat pp = new PrettyPrintFormat(":"); + + /** + * The default constructor for the class. + * + * @param trees the sequence of GeneralSubtree. + */ + public GeneralSubtrees(Vector<GeneralSubtree> 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<GeneralSubtree>(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) { + + String s = ""; + GeneralSubtree element; + + for (Enumeration<GeneralSubtree> e = trees.elements(); e.hasMoreElements();) { + element = (GeneralSubtree) e.nextElement(); + s = s + pp.indent(indent + 4) + element.toPrint(indent) + "\n"; + } + + return (s); + } + + /** + * 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++) { + ((GeneralSubtree) trees.elementAt(i)).encode(seq); + } + out.write(DerValue.tag_Sequence, seq); + } + + public Vector<GeneralSubtree> getSubtrees() { + return trees; + } +} diff --git a/base/util/src/netscape/security/x509/GenericValueConverter.java b/base/util/src/netscape/security/x509/GenericValueConverter.java new file mode 100644 index 000000000..73bc1979c --- /dev/null +++ b/base/util/src/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 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 netscape.security.util.ASN1CharStrConvMap; +import 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. + * + * <p> + * 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. + * + * <p> + * 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/base/util/src/netscape/security/x509/HoldInstructionExtension.java b/base/util/src/netscape/security/x509/HoldInstructionExtension.java new file mode 100644 index 000000000..b42bb6ac9 --- /dev/null +++ b/base/util/src/netscape/security/x509/HoldInstructionExtension.java @@ -0,0 +1,354 @@ +// --- 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.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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; + +/** + * Represent the CRL Hold Instruction Code Extension. + * + * <p> + * 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"); + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(HOLD_INSTRUCTION); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/IA5StringConverter.java b/base/util/src/netscape/security/x509/IA5StringConverter.java new file mode 100644 index 000000000..eced75a58 --- /dev/null +++ b/base/util/src/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 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 netscape.security.util.ASN1CharStrConvMap; +import 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/base/util/src/netscape/security/x509/IPAddressName.java b/base/util/src/netscape/security/x509/IPAddressName.java new file mode 100644 index 000000000..75b5bc564 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; +import java.util.StringTokenizer; + +import netscape.security.util.DerOutputStream; +import 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 { + String r = "IPAddress: " + Integer.toHexString(address[0] & 0xff); + String hexString = Integer.toHexString(address[1] & 0xff); + if (hexString.length() == 1) { + r = r + "0" + hexString; + } else { + r += hexString; + } + for (int i = 2; i < address.length;) { + r += ":" + Integer.toHexString(address[i] & 0xff); + hexString = Integer.toHexString(address[i + 1] & 0xff); + if (hexString.length() == 1) { + r = r + "0" + hexString; + } else { + r += hexString; + } + i += 2; + } + return r; + } + } +} + +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:<br> + * <ul> + * <li>x:x:x:x:x:x:x:x + * <li>...::xxx (using :: shorthand) + * <li>...:n.n.n.n (with n.n.n.n at the end) + * </ul> + */ + 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/base/util/src/netscape/security/x509/InvalidIPAddressException.java b/base/util/src/netscape/security/x509/InvalidIPAddressException.java new file mode 100644 index 000000000..f544df200 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/x509/InvalidityDateExtension.java b/base/util/src/netscape/security/x509/InvalidityDateExtension.java new file mode 100755 index 000000000..44c76275f --- /dev/null +++ b/base/util/src/netscape/security/x509/InvalidityDateExtension.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 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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * Represent the CRL Invalidity Date Extension. + * + * <p> + * 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"); + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(INVALIDITY_DATE); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/IssuerAlternativeNameExtension.java b/base/util/src/netscape/security/x509/IssuerAlternativeNameExtension.java new file mode 100644 index 000000000..df0289f9e --- /dev/null +++ b/base/util/src/netscape/security/x509/IssuerAlternativeNameExtension.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 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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This represents the Issuer Alternative Name Extension. + * + * This extension, if present, allows the issuer to specify multiple + * alternative names. + * + * <p> + * 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.toString()); + } + 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.toString()); + } + } + + /** + * 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(ISSUER_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/IssuingDistributionPoint.java b/base/util/src/netscape/security/x509/IssuingDistributionPoint.java new file mode 100644 index 000000000..0f0747f84 --- /dev/null +++ b/base/util/src/netscape/security/x509/IssuingDistributionPoint.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 netscape.security.x509; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import netscape.security.util.BitArray; +import 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; + +/** + * <pre> + * 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 + * </pre> + * + * See the documentation in <code>CRLDistributionPoint</code> for + * the <code>DistributionPointName</code> and <code>ReasonFlags</code> 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 <code>fullName</code> of the <code>DistributionPointName</code>, which may be <code>null</code>. + */ + public GeneralNames getFullName() { + return fullName; + } + + /** + * Returns the <code>relativeName</code> of the <code>DistributionPointName</code>, which may be <code>null</code>. + */ + public RDN getRelativeName() { + return relativeName; + } + + /** + * Sets the <code>fullName</code> of the <code>DistributionPointName</code>. It may be set to <code>null</code>. + * If it is set to a non-null value, <code>relativeName</code> will be + * set to <code>null</code>, 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 <code>relativeName</code> of the <code>DistributionPointName</code>. It may be set to <code>null</code>. + * If it is set to a non-null value, <code>fullName</code> will be + * set to <code>null</code>, 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 <code>null</code>. + */ + public BitArray getOnlySomeReasons() { + return onlySomeReasons; + } + + /** + * Sets the reason flags for this distribution point. May be set to <code>null</code>. + */ + 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[]) { + + try { + if (args.length != 1) { + System.out.println("Usage: IssuingDistributionPoint <outfile>"); + System.exit(-1); + } + + BufferedOutputStream 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); + bos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/base/util/src/netscape/security/x509/IssuingDistributionPointExtension.java b/base/util/src/netscape/security/x509/IssuingDistributionPointExtension.java new file mode 100644 index 000000000..d5f5100bf --- /dev/null +++ b/base/util/src/netscape/security/x509/IssuingDistributionPointExtension.java @@ -0,0 +1,416 @@ +// --- 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.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 netscape.security.util.BitArray; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +import org.mozilla.jss.asn1.ASN1Util; + +/** + * A critical CRL extension that identifies the CRL distribution point + * for a particular CRL + * + * <pre> + * 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 + * </pre> + */ +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[]) ((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); + } catch (IOException e) { + throw new IOException("Invalid encoding of IssuingDistributionPoint " + + 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); + } + } else { + throw new IOException("Invalid encoding of IssuingDistributionPoint"); + } + } + } + + } else if (i == 3) { + opt.resetTag(DerValue.tag_BitString); + try { + BitArray reasons = opt.getUnalignedBitString(); + issuingDistributionPoint.setOnlySomeReasons(reasons); + + @SuppressWarnings("unused") + byte[] a = reasons.toByteArray(); // check for errors + } catch (IOException e) { + throw new IOException("Invalid encoding of IssuingDistributionPoint " + 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); + } + } + } 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 + */ + + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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[]) { + + try { + + if (args.length != 1) { + System.out.println("Usage: IssuingDistributionPointExtension " + + "<outfile>"); + System.exit(-1); + } + + BufferedOutputStream 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); + + bos.close(); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/base/util/src/netscape/security/x509/KeyIdentifier.java b/base/util/src/netscape/security/x509/KeyIdentifier.java new file mode 100644 index 000000000..631f6fd65 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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 ((byte[]) octetString.clone()); + } + + /** + * Returns a printable representation of the KeyUsage. + */ + public String toString() { + netscape.security.util.PrettyPrintFormat pp = + new 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/base/util/src/netscape/security/x509/KeyUsageExtension.java b/base/util/src/netscape/security/x509/KeyUsageExtension.java new file mode 100644 index 000000000..56084dbcf --- /dev/null +++ b/base/util/src/netscape/security/x509/KeyUsageExtension.java @@ -0,0 +1,414 @@ +// --- 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.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 netscape.security.util.BitArray; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * Represent the Key Usage Extension. + * + * <p> + * 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 { + 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); + this.bitString = val.getUnalignedBitString().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 new Boolean(isSet(0)); + } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { + return new Boolean(isSet(1)); + } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { + return new Boolean(isSet(2)); + } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { + return new Boolean(isSet(3)); + } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { + return new Boolean(isSet(4)); + } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { + return new Boolean(isSet(5)); + } else if (name.equalsIgnoreCase(CRL_SIGN)) { + return new Boolean(isSet(6)); + } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { + return new Boolean(isSet(7)); + } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { + return new Boolean(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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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 (boolean[]) bitString.clone(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/LdapDNStrConverter.java b/base/util/src/netscape/security/x509/LdapDNStrConverter.java new file mode 100644 index 000000000..a8cb87813 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/x509/LdapV3DNStrConverter.java b/base/util/src/netscape/security/x509/LdapV3DNStrConverter.java new file mode 100644 index 000000000..1245cc6bd --- /dev/null +++ b/base/util/src/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 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 netscape.security.util.DerValue; +import 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<RDN> rdnVector = new Vector<RDN>(); + 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] = (RDN) 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<AVA> avaVector = new Vector<AVA>(); + 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] = (AVA) 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((int) '\\'); + 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. + * <p> + * <strong><i>NOTE:</i></strong> 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/base/util/src/netscape/security/x509/NSCCommentExtension.java b/base/util/src/netscape/security/x509/NSCCommentExtension.java new file mode 100644 index 000000000..0912d5b0c --- /dev/null +++ b/base/util/src/netscape/security/x509/NSCCommentExtension.java @@ -0,0 +1,230 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import 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<Object> mInfos; + + private PrettyPrintFormat pp = new PrettyPrintFormat(":"); + + // Encode this extension value + private void encodeThis() throws IOException { + 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<Object>(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<Object>) 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(INFOS); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + +} diff --git a/base/util/src/netscape/security/x509/NameConstraintsExtension.java b/base/util/src/netscape/security/x509/NameConstraintsExtension.java new file mode 100644 index 000000000..948d0d8c9 --- /dev/null +++ b/base/util/src/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 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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.PrettyPrintFormat; + +/** + * This class defines the Name Constraints Extension. + * <p> + * 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. + * <p> + * The ASN.1 syntax for this is: + * + * <pre> + * 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) + * </pre> + * + * @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 PrettyPrintFormat pp = new PrettyPrintFormat(":"); + + // Encode this extension value. + private void encodeThis() throws IOException { + 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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/base/util/src/netscape/security/x509/NoticeReference.java b/base/util/src/netscape/security/x509/NoticeReference.java new file mode 100644 index 000000000..150b34f40 --- /dev/null +++ b/base/util/src/netscape/security/x509/NoticeReference.java @@ -0,0 +1,94 @@ +// --- 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.x509; + +import java.io.IOException; +import java.util.Vector; + +import netscape.security.util.BigInt; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * Represent the NoticeReference. + * + * NoticeReference ::= SEQUENCE { + * organization DisplayText, + * noticeNumbers SEQUENCE OF INTEGER + * } + * + * @author Thomas Kwan + */ +public class NoticeReference { + + 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<BigInt> num = new Vector<BigInt>(); + 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/base/util/src/netscape/security/x509/OIDMap.java b/base/util/src/netscape/security/x509/OIDMap.java new file mode 100644 index 000000000..9c732d938 --- /dev/null +++ b/base/util/src/netscape/security/x509/OIDMap.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 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 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<ObjectIdentifier, String> oid2Name = new Hashtable<ObjectIdentifier, String>(); + private static final Hashtable<String, ObjectIdentifier> name2OID = new Hashtable<String, ObjectIdentifier>(); + private static final Hashtable<String, String> name2Class = new Hashtable<String, String>(); + + // Initialize recognized extensions from EXTENSIONS_{OIDS/CLASSES} files + static { + loadNames(); + loadClasses(); + } + + // 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, + "netscape.security.x509.AuthorityKeyIdentifierExtension"); + props.put(SUB_KEY_IDENTIFIER, + "netscape.security.x509.SubjectKeyIdentifierExtension"); + props.put(KEY_USAGE, + "netscape.security.x509.KeyUsageExtension"); + props.put(PRIVATE_KEY_USAGE, + "netscape.security.x509.PrivateKeyUsageExtension"); + props.put(POLICY_MAPPINGS, + "netscape.security.x509.PolicyMappingsExtension"); + props.put(SUB_ALT_NAME, + "netscape.security.x509.SubjectAlternativeNameExtension"); + props.put(ISSUER_ALT_NAME, + "netscape.security.x509.IssuerAlternativeNameExtension"); + props.put(BASIC_CONSTRAINTS, + "netscape.security.x509.BasicConstraintsExtension"); + props.put(NAME_CONSTRAINTS, + "netscape.security.x509.NameConstraintsExtension"); + props.put(POLICY_CONSTRAINTS, + "netscape.security.x509.PolicyConstraintsExtension"); + props.put(CERT_POLICIES, + "netscape.security.x509.CertificatePoliciesExtension"); + props.put(SUBJ_DIR_ATTR, + "netscape.security.x509.SubjectDirAttributesExtension"); + props.put(EXT_KEY_USAGE, + "netscape.security.extensions.ExtendedKeyUsageExtension"); + props.put(CRL_NUMBER, "netscape.security.x509.CRLNumberExtension"); + props.put(CRL_REASON, "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 { + try { + FileInputStream fis = new FileInputStream(namesMap); + props.load(fis); + fis.close(); + } catch (IOException e) { + loadNamesDefault(props); + } + } + + Iterator<String> 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 { + try { + FileInputStream fis = new FileInputStream(classMap); + props.load(fis); + } catch (IOException e) { + loadClassDefault(props); + } + } + + Iterator<String> names = props.stringPropertyNames().iterator(); + while (names.hasNext()) { + String name = names.next(); + String className = props.getProperty(name); + + name2Class.put(name, className); + } + } + + /** + * 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 (String) 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 (ObjectIdentifier) 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 = (String) 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 = (String) 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/base/util/src/netscape/security/x509/OIDName.java b/base/util/src/netscape/security/x509/OIDName.java new file mode 100644 index 000000000..e5c1b7ac3 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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/base/util/src/netscape/security/x509/OtherName.java b/base/util/src/netscape/security/x509/OtherName.java new file mode 100644 index 000000000..38d3a0af3 --- /dev/null +++ b/base/util/src/netscape/security/x509/OtherName.java @@ -0,0 +1,208 @@ +// --- 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.x509; + +import java.io.IOException; +import java.io.InputStream; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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[]) { + mOID = oid; + DerOutputStream dos = new DerOutputStream(); + try { + dos.putDerValue(new DerValue(data)); + } catch (IOException e) { + } + mData = dos.toByteArray(); + } + + /** + * Constructs a string-based other name. + */ + public OtherName(ObjectIdentifier oid, byte tag, String value) { + 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) { + } + mData = dos.toByteArray(); + } + + public OtherName(ObjectIdentifier oid, String value) { + mOID = oid; + DerOutputStream dos = new DerOutputStream(); + try { + dos.putPrintableString(value); + } catch (IOException e) { + } + 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((int) (data[i] & 0xff), 0x10)); + } + return b.toString(); + } +} diff --git a/base/util/src/netscape/security/x509/PKIXExtensions.java b/base/util/src/netscape/security/x509/PKIXExtensions.java new file mode 100644 index 000000000..9946a5c57 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import netscape.security.util.ObjectIdentifier; + +/** + * Lists all the object identifiers of the X509 extensions of the PKIX profile. + * + * <p> + * 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. + * + * <p> + * 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/base/util/src/netscape/security/x509/PolicyConstraint.java b/base/util/src/netscape/security/x509/PolicyConstraint.java new file mode 100644 index 000000000..22f9cebed --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/PolicyConstraintsExtension.java b/base/util/src/netscape/security/x509/PolicyConstraintsExtension.java new file mode 100644 index 000000000..7d98b21ba --- /dev/null +++ b/base/util/src/netscape/security/x509/PolicyConstraintsExtension.java @@ -0,0 +1,306 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class defines the certificate extension which specifies the + * Policy constraints. + * <p> + * 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. + * <p> + * The ASN.1 syntax for this is (IMPLICIT tagging is defined in the module definition): + * + * <pre> + * PolicyConstraints ::= SEQUENCE { + * requireExplicitPolicy [0] SkipCerts OPTIONAL, + * inhibitPolicyMapping [1] SkipCerts OPTIONAL + * } + * SkipCerts ::= INTEGER (0..MAX) + * </pre> + * + * @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 { + DerOutputStream tagged = new DerOutputStream(); + DerOutputStream seq = 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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/base/util/src/netscape/security/x509/PolicyMappingsExtension.java b/base/util/src/netscape/security/x509/PolicyMappingsExtension.java new file mode 100644 index 000000000..9bdfb611b --- /dev/null +++ b/base/util/src/netscape/security/x509/PolicyMappingsExtension.java @@ -0,0 +1,258 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import 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. + * <p> + * 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. + * + * <p> + * 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<CertificatePolicyMap> maps = null; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + for (int i = 0; i < maps.size(); i++) { + ((CertificatePolicyMap) 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<CertificatePolicyMap> 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<CertificatePolicyMap> map) + throws IOException { + init(critical, map); + } + + /** + * init policy with criticality and map. + */ + private void init(boolean critical, Vector<CertificatePolicyMap> 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<CertificatePolicyMap>(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<CertificatePolicyMap>(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<CertificatePolicyMap>) 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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<CertificatePolicyMap> getMappings() { + if (maps == null) + return null; + return maps.elements(); + } +} diff --git a/base/util/src/netscape/security/x509/PolicyQualifierInfo.java b/base/util/src/netscape/security/x509/PolicyQualifierInfo.java new file mode 100644 index 000000000..56d3e32c0 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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/base/util/src/netscape/security/x509/PolicyQualifiers.java b/base/util/src/netscape/security/x509/PolicyQualifiers.java new file mode 100644 index 000000000..ee756f50a --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; +import java.util.Vector; + +import netscape.security.util.DerOutputStream; +import 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<PolicyQualifierInfo> mInfo = new Vector<PolicyQualifierInfo>(); + + /** + * 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() { + String s = "PolicyQualifiers: ["; + for (int i = 0; i < mInfo.size(); i++) { + PolicyQualifierInfo pq = mInfo.elementAt(i); + s = s + pq.toString(); + } + s = s + "]\n"; + + return (s); + } + + /** + * 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/base/util/src/netscape/security/x509/PrintableConverter.java b/base/util/src/netscape/security/x509/PrintableConverter.java new file mode 100644 index 000000000..d63696d8f --- /dev/null +++ b/base/util/src/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 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 netscape.security.util.ASN1CharStrConvMap; +import 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/base/util/src/netscape/security/x509/PrivateKeyUsageExtension.java b/base/util/src/netscape/security/x509/PrivateKeyUsageExtension.java new file mode 100644 index 000000000..e3ecdb33d --- /dev/null +++ b/base/util/src/netscape/security/x509/PrivateKeyUsageExtension.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 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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class defines the Private Key Usage Extension. + * + * <p> + * 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. + * + * <pre> + * PrivateKeyUsagePeriod ::= SEQUENCE { + * notBefore [0] GeneralizedTime OPTIONAL, + * notAfter [1] GeneralizedTime OPTIONAL } + * </pre> + * + * @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 { + 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((byte) 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((byte) 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 <code>Date</code> supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the <code>Date</code> 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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/base/util/src/netscape/security/x509/Qualifier.java b/base/util/src/netscape/security/x509/Qualifier.java new file mode 100644 index 000000000..7c0c7edfe --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/RDN.java b/base/util/src/netscape/security/x509/RDN.java new file mode 100644 index 000000000..c5e8765a0 --- /dev/null +++ b/base/util/src/netscape/security/x509/RDN.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 netscape.security.x509; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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 = (AVA[]) avas.clone(); + } + + /** + * convenience method. + */ + public RDN(Vector<AVA> 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 (AVA[]) 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<AVA> { + 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. + + /** + * Checks if this RDN is the same as another by comparing the AVAs + * in the RDNs. + * + * @param other the other RDN. + * @return true iff the other RDN is the same. + */ + public boolean equals(RDN other) { + int i; + + if (other == this) + return true; + if (assertion.length != other.assertion.length) + return false; + + for (i = 0; i < assertion.length; i++) + if (!assertion[i].equals(other.assertion[i])) + 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<AVA> 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/base/util/src/netscape/security/x509/RFC1779StrConverter.java b/base/util/src/netscape/security/x509/RFC1779StrConverter.java new file mode 100644 index 000000000..6527d0fff --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerValue; +import 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/base/util/src/netscape/security/x509/RFC822Name.java b/base/util/src/netscape/security/x509/RFC822Name.java new file mode 100644 index 000000000..257b5c51d --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/ReasonFlags.java b/base/util/src/netscape/security/x509/ReasonFlags.java new file mode 100755 index 000000000..e43c7022c --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.BitArray; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * Represent the CRL Reason Flags. + * + * <p> + * 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 new Boolean(isSet(0)); + } else if (name.equalsIgnoreCase(KEY_COMPROMISE)) { + return new Boolean(isSet(1)); + } else if (name.equalsIgnoreCase(CA_COMPROMISE)) { + return new Boolean(isSet(2)); + } else if (name.equalsIgnoreCase(AFFLIATION_CHANGED)) { + return new Boolean(isSet(3)); + } else if (name.equalsIgnoreCase(SUPERSEDED)) { + return new Boolean(isSet(4)); + } else if (name.equalsIgnoreCase(CESSATION_OF_OPERATION)) { + return new Boolean(isSet(5)); + } else if (name.equalsIgnoreCase(CERTIFICATION_HOLD)) { + return new Boolean(isSet(6)); + } else if (name.equalsIgnoreCase(PRIVILEGE_WITHDRAWN)) { + return new Boolean(isSet(7)); + } else if (name.equalsIgnoreCase(AA_COMPROMISE)) { + return new Boolean(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<String> getElements() { + Vector<String> elements = new Vector<String>(); + 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/base/util/src/netscape/security/x509/RevocationReason.java b/base/util/src/netscape/security/x509/RevocationReason.java new file mode 100644 index 000000000..419eb1772 --- /dev/null +++ b/base/util/src/netscape/security/x509/RevocationReason.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 netscape.security.x509; + +/** + * Represent the enumerated type used in CRLReason Extension of CRL entry. + * + * + * @author galperin + * @version $Revision$, $Date$ + */ + +public final class RevocationReason { + /** + * Reasons + */ + public static final RevocationReason UNSPECIFIED = new RevocationReason(0); + public static final RevocationReason KEY_COMPROMISE = new RevocationReason(1); + public static final RevocationReason CA_COMPROMISE = new RevocationReason(2); + public static final RevocationReason AFFILIATION_CHANGED = new RevocationReason(3); + public static final RevocationReason SUPERSEDED = new RevocationReason(4); + public static final RevocationReason CESSATION_OF_OPERATION = new RevocationReason(5); + public static final RevocationReason CERTIFICATE_HOLD = new RevocationReason(6); + public static final RevocationReason REMOVE_FROM_CRL = new RevocationReason(8); + public static final RevocationReason PRIVILEGE_WITHDRAWN = new RevocationReason(9); + public static final RevocationReason AA_COMPROMISE = new RevocationReason(10); + + // Private data members + private int mReason; + + /** + * Create a RevocationReason with the passed integer value. + * + * @param reason integer value of the enumeration alternative. + */ + private RevocationReason(int reason) { + this.mReason = reason; + } + + public int toInt() { + return mReason; + } + + public static RevocationReason fromInt(int reason) { + if (reason == UNSPECIFIED.mReason) + return UNSPECIFIED; + if (reason == KEY_COMPROMISE.mReason) + return KEY_COMPROMISE; + if (reason == CA_COMPROMISE.mReason) + return CA_COMPROMISE; + if (reason == AFFILIATION_CHANGED.mReason) + return AFFILIATION_CHANGED; + if (reason == SUPERSEDED.mReason) + return SUPERSEDED; + if (reason == CESSATION_OF_OPERATION.mReason) + return CESSATION_OF_OPERATION; + if (reason == CERTIFICATE_HOLD.mReason) + return CERTIFICATE_HOLD; + if (reason == REMOVE_FROM_CRL.mReason) + return REMOVE_FROM_CRL; + if (reason == PRIVILEGE_WITHDRAWN.mReason) + return PRIVILEGE_WITHDRAWN; + if (reason == AA_COMPROMISE.mReason) + return AA_COMPROMISE; + return null; + } + + public boolean equals(Object other) { + if (this == other) + return true; + else if (other instanceof RevocationReason) + return ((RevocationReason) other).mReason == mReason; + else + return false; + } + + public int hashCode() { + return mReason; + } + + public String toString() { + if (equals(UNSPECIFIED)) + return "Unspecified"; + if (equals(KEY_COMPROMISE)) + return "Key_Compromise"; + if (equals(CA_COMPROMISE)) + return "CA_Compromise"; + if (equals(AFFILIATION_CHANGED)) + return "Affiliation_Changed"; + if (equals(SUPERSEDED)) + return "Superseded"; + if (equals(CESSATION_OF_OPERATION)) + return "Cessation_of_Operation"; + if (equals(CERTIFICATE_HOLD)) + return "Certificate_Hold"; + if (equals(REMOVE_FROM_CRL)) + return "Remove_from_CRL"; + if (equals(PRIVILEGE_WITHDRAWN)) + return "Privilege_Withdrawn"; + if (equals(AA_COMPROMISE)) + return "AA_Compromise"; + return "[UNDEFINED]"; + } +} diff --git a/base/util/src/netscape/security/x509/RevokedCertImpl.java b/base/util/src/netscape/security/x509/RevokedCertImpl.java new file mode 100755 index 000000000..345694fb1 --- /dev/null +++ b/base/util/src/netscape/security/x509/RevokedCertImpl.java @@ -0,0 +1,454 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; + +/** + * <p> + * Abstract class for a revoked certificate in a CRL. This class is for each entry in the + * <code>revokedCertificates</code>, so it deals with the inner <em>SEQUENCE</em>. The ASN.1 definition for this is: + * + * <pre> + * 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 + * } + * </pre> + * + * @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 { + 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); + + DerOutputStream seq = new DerOutputStream(); + 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 <em>userCertificate</em>. + * + * @return the serial number. + */ + public BigInteger getSerialNumber() { + return ((BigInt) serialNumber.getNumber()).toBigInteger(); + } + + /** + * Gets the revocation date for this RevokedCertificate, the <em>revocationDate</em>. + * + * @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(); + + sb.append(serialNumber.toString()); + sb.append(" On: " + revocationDate.toString()); + if (extensions != null) { + sb.append("\n"); + for (int i = 0; i < extensions.size(); i++) + sb.append("Entry Extension[" + i + "]: " + + ((Extension) (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<String> getCriticalExtensionOIDs() { + if (extensions == null) + return null; + Set<String> extSet = new LinkedHashSet<String>(); + Extension ex; + for (Enumeration<Extension> 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<String> getNonCriticalExtensionOIDs() { + if (extensions == null) + return null; + Set<String> extSet = new LinkedHashSet<String>(); + Extension ex; + for (Enumeration<Extension> 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 + * (<em>extnValue</em>) identified by the passed in oid String. + * The <code>oid</code> string is + * represented by a set of positive whole number separated + * by ".", that means,<br> + * <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 { + 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<Extension> 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; + + DerOutputStream out = new DerOutputStream(); + 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 synchronized 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 synchronized void readObject(ObjectInputStream stream) + throws CRLException, X509ExtensionException, IOException { + decode(stream); + } + +} diff --git a/base/util/src/netscape/security/x509/RevokedCertificate.java b/base/util/src/netscape/security/x509/RevokedCertificate.java new file mode 100644 index 000000000..2087d064a --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.math.BigInteger; +import java.security.cert.X509CRL; +import java.security.cert.X509CRLEntry; +import java.util.Date; + +/** + * <p> + * Abstract class for a revoked certificate in a CRL (Certificate Revocation List). + * + * The ASN.1 definition for <em>revokedCertificates</em> is: + * + * <pre> + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate ChoiceOfTime, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, must be v2 + * } OPTIONAL + * <p> + * CertificateSerialNumber ::= INTEGER + * <p> + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * <p> + * 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 + * } + * </pre> + * + * @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 <em>userCertificate</em>. + * + * @return the serial number. + */ + public abstract BigInteger getSerialNumber(); + + /** + * Gets the revocation date for this RevokedCertificate, + * the <em>revocationDate</em>. + * + * @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/base/util/src/netscape/security/x509/SerialNumber.java b/base/util/src/netscape/security/x509/SerialNumber.java new file mode 100644 index 000000000..a2d7109c0 --- /dev/null +++ b/base/util/src/netscape/security/x509/SerialNumber.java @@ -0,0 +1,124 @@ +// --- 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.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; + +import netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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 { + 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/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java b/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java new file mode 100644 index 000000000..c30ae1576 --- /dev/null +++ b/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.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 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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This represents the Subject Alternative Name Extension. + * + * This extension, if present, allows the subject to specify multiple + * alternative names. + * + * <p> + * 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). + * <p> + * The ASN.1 syntax for this is: + * + * <pre> + * SubjectAltName ::= GeneralNames + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * </pre> + * + * @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); + } + } + + /** + * 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."); + } + } + + /** + * 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(SUBJECT_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/SubjectDirAttributesExtension.java b/base/util/src/netscape/security/x509/SubjectDirAttributesExtension.java new file mode 100644 index 000000000..b249ef600 --- /dev/null +++ b/base/util/src/netscape/security/x509/SubjectDirAttributesExtension.java @@ -0,0 +1,286 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * This class represents the Subject Directory Attributes Extension. + * + * <p> + * 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. + * + * <pre> + * 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 + * + * </pre> + * + * @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<Attribute> attrList = new Vector<Attribute>(); + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + //encoding the attributes + Enumeration<Attribute> 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<Attribute> attrs = attrList.elements(); + while (attrs.hasMoreElements()) { + Attribute attr = attrs.nextElement(); + s += attr.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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + + /** + * Returns an enumeration of attributes in the extension. + */ + public Enumeration<Attribute> getAttributesList() { + if (attrList == null) + return null; + return attrList.elements(); + } +} diff --git a/base/util/src/netscape/security/x509/SubjectKeyIdentifierExtension.java b/base/util/src/netscape/security/x509/SubjectKeyIdentifierExtension.java new file mode 100644 index 000000000..ea0ebae82 --- /dev/null +++ b/base/util/src/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 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 netscape.security.util.DerOutputStream; +import 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. + * + * <p> + * 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. + * + * <p> + * 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + elements.addElement(KEY_ID); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/base/util/src/netscape/security/x509/URIName.java b/base/util/src/netscape/security/x509/URIName.java new file mode 100644 index 000000000..cc321a3b3 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/UniqueIdentity.java b/base/util/src/netscape/security/x509/UniqueIdentity.java new file mode 100644 index 000000000..5113efeaf --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.BitArray; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/UserNotice.java b/base/util/src/netscape/security/x509/UserNotice.java new file mode 100644 index 000000000..dc2e1d535 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.DerOutputStream; +import 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/base/util/src/netscape/security/x509/X500Name.java b/base/util/src/netscape/security/x509/X500Name.java new file mode 100644 index 000000000..d44003baa --- /dev/null +++ b/base/util/src/netscape/security/x509/X500Name.java @@ -0,0 +1,699 @@ +// --- 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.x509; + +import java.io.IOException; +import java.security.Principal; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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. + * + * <P> + * <em>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.</em> + * + * @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 = (RDN[]) rdns.clone(); + } + + /** + * convenience method. + * + * @param rdns a vector of rdns. + */ + public X500Name(Vector<RDN> rdnVector) + throws IOException { + int size = rdnVector.size(); + names = new RDN[size]; + for (int i = 0; i < size; i++) { + names[i] = (RDN) rdnVector.elementAt(i); + } + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are identical. + */ + synchronized public boolean equals(X500Name other) { + int i; + + if (this == other) + return true; + + if (names.length != other.names.length) + return false; + for (i = 0; i < names.length; i++) { + if (!names[i].equals(other.names[i])) + 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; + } + + /** + * 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<RDN> getRDNs() { + return new RDNEnumerator(); + } + + /** + * Returns an array of RDN in the X500Name. + * + * @return array of RDN in this X500name. + */ + public RDN[] getNames() { + return (RDN[]) 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 { + + 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<RDN> { + 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/base/util/src/netscape/security/x509/X500NameAttrMap.java b/base/util/src/netscape/security/x509/X500NameAttrMap.java new file mode 100644 index 000000000..1c87c79b8 --- /dev/null +++ b/base/util/src/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 netscape.security.x509; + +import java.util.Enumeration; +import java.util.Hashtable; + +import 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 (AVAValueConverter) 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 (AVAValueConverter) oid2ValueConverter.get(oid); + } + + /** + * Get an Enumeration of all attribute names in this map. + * + * @return An Enumeration of all attribute names. + */ + public Enumeration<String> getAllNames() { + return name2OID.keys(); + } + + /** + * Get an Enumeration of all ObjectIdentifiers in this map. + * + * @return An Enumeration of all OIDs in this map. + */ + public Enumeration<ObjectIdentifier> 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<? extends AVAValueConverter> 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<String, ObjectIdentifier> name2OID = new Hashtable<String, ObjectIdentifier>(); + Hashtable<ObjectIdentifier, String> oid2Name = new Hashtable<ObjectIdentifier, String>(); + Hashtable<ObjectIdentifier, AVAValueConverter> oid2ValueConverter = + new Hashtable<ObjectIdentifier, AVAValueConverter>(); + + // + // 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/base/util/src/netscape/security/x509/X500Signer.java b/base/util/src/netscape/security/x509/X500Signer.java new file mode 100644 index 000000000..0b8cf87a4 --- /dev/null +++ b/base/util/src/netscape/security/x509/X500Signer.java @@ -0,0 +1,116 @@ +// --- 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.x509; + +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.Signer; + +/** + * 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. + * + * <P> + * 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. + * + * <P> + * <em>The X.509 certificate chain is temporarily not associated with + * the signer, but this omission will be resolved.</em> + * + * @version 1.18 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public final class X500Signer extends Signer { + /** + * + */ + private static final long serialVersionUID = -3148659822293810158L; + + /** + * 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 { + this.algid = AlgorithmId.getAlgorithmId(sig.getAlgorithm()); + String alg = sig.getAlgorithm(); + if (alg.equals("DSA")) { + alg = "SHA1withDSA"; + } + this.algid = AlgorithmId.getAlgorithmId(alg); + + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("internal error! " + e.getMessage()); + } + } + + private Signature sig; + private X500Name agent; // XXX should be X509CertChain + private AlgorithmId algid; +} diff --git a/base/util/src/netscape/security/x509/X509AttributeName.java b/base/util/src/netscape/security/x509/X509AttributeName.java new file mode 100644 index 000000000..2f6c46cb1 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/x509/X509CRLImpl.java b/base/util/src/netscape/security/x509/X509CRLImpl.java new file mode 100755 index 000000000..8c69b6aa0 --- /dev/null +++ b/base/util/src/netscape/security/x509/X509CRLImpl.java @@ -0,0 +1,1071 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; + +/** + * <p> + * An implmentation for X509 CRL (Certificate Revocation List). + * <p> + * The X.509 v2 CRL format is described below in ASN.1: + * + * <pre> + * </pre> + * <p> + * CertificateList ::= SEQUENCE { tbsCertList TBSCertList, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING + * } + * <p> + * 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>. + * <p> + * The ASN.1 definition of <code>tbsCertList</code> is: + * + * <pre> + * 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 + * } + * </pre> + * + * @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<BigInteger, RevokedCertificate> revokedCerts = new Hashtable<BigInteger, RevokedCertificate>(); + // 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<BigInteger, RevokedCertificate> 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 tmp = new DerOutputStream(); + DerOutputStream rCerts = new DerOutputStream(); + DerOutputStream seq = 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<RevokedCertificate> 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 { + verify(key, null); + } + + /** + * 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.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"; + } + } + 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 { + 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 out = new DerOutputStream(); + 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(); + sb.append("X.509 CRL v" + (version + 1) + "\n"); + sb.append("Signature Algorithm: " + sigAlgId.toString() + + ", OID=" + (sigAlgId.getOID()).toString() + "\n"); + sb.append("Issuer: " + issuer.toString() + "\n"); + sb.append("\nThis Update: " + thisUpdate.toString() + "\n"); + if (nextUpdate != null) + sb.append("Next Update: " + nextUpdate.toString() + "\n"); + if (revokedCerts.isEmpty()) + sb.append("\nNO certificates have been revoked\n"); + else { + sb.append("\nRevoked Certificates:\n"); + for (Enumeration<RevokedCertificate> e = revokedCerts.elements(); e.hasMoreElements();) + sb.append(((RevokedCertificate) e.nextElement()).toString()); + } + if (extensions != null) { + for (int i = 0; i < extensions.size(); i++) { + sb.append("\nCRL Extension[" + i + "]: " + + ((Extension) (extensions.elementAt(i))).toString()); + } + } + netscape.security.util.PrettyPrintFormat pp = + new 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: + * + * <pre> + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * -- v3 does not apply to CRLs but appears for consistency + * -- with definition of Version for certs + * </pre> + * + * @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: + * + * <pre> + * issuer Name + * + * Name ::= CHOICE { RDNSequence } + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * RelativeDistinguishedName ::= + * SET OF AttributeValueAssertion + * + * AttributeValueAssertion ::= SEQUENCE { + * AttributeType, + * AttributeValue } + * AttributeType ::= OBJECT IDENTIFIER + * AttributeValue ::= ANY + * </pre> + * + * 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 (Principal) 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; + RevokedCertificate badCert = + (RevokedCertificate) revokedCerts.get(serialNumber); + return badCert; + } + + /** + * 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<RevokedCertificate> getRevokedCertificates() { + if (revokedCerts == null || revokedCerts.isEmpty()) + return null; + else { + Set<RevokedCertificate> certSet = new LinkedHashSet<RevokedCertificate>(revokedCerts.values()); + return certSet; + } + } + + @SuppressWarnings("unchecked") + public Hashtable<BigInteger, RevokedCertificate> getListOfRevokedCertificates() { + if (revokedCerts == null) { + return null; + } else { + return (Hashtable<BigInteger, RevokedCertificate>) revokedCerts.clone(); + } + } + + public int getNumberOfRevokedCertificates() { + if (revokedCerts == null) + return -1; + else + return revokedCerts.size(); + } + + /** + * Gets the DER encoded CRL information, the <code>tbsCertList</code> 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: + * + * <pre> + * 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 + * </pre> + * + * @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,<br> + * <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<String> getCriticalExtensionOIDs() { + if (extensions == null) + return null; + Set<String> extSet = new LinkedHashSet<String>(); + Extension ex; + for (Enumeration<Extension> e = extensions.getElements(); e.hasMoreElements();) { + ex = e.nextElement(); + if (ex.isCritical()) { + extSet.add(((ObjectIdentifier) 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<String> getNonCriticalExtensionOIDs() { + if (extensions == null) + return null; + Set<String> extSet = new LinkedHashSet<String>(); + Extension ex; + for (Enumeration<Extension> e = extensions.getElements(); e.hasMoreElements();) { + ex = e.nextElement(); + if (!ex.isCritical()) + extSet.add(((ObjectIdentifier) ex.getExtensionId()).toString()); + } + return extSet; + } + + /** + * Gets the DER encoded OCTET string for the extension value + * (<code>extnValue</code>) identified by the passed in oid String. + * The <code>oid</code> string is + * represented by a set of positive whole number separated + * by ".", that means,<br> + * <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 { + 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<Extension> 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; + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extData); + return out.toByteArray(); + } catch (Exception e) { + return null; + } + } + + public BigInteger getCRLNumber() { + try { + CRLExtensions exts = getExtensions(); + if (exts == null) + return null; + Enumeration<Extension> e = exts.getElements(); + while (e.hasMoreElements()) { + Extension ext = (Extension) 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<Extension> e = exts.getElements(); + while (e.hasMoreElements()) { + Extension ext = (Extension) 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<Extension> e = exts.getElements(); + while (e.hasMoreElements()) { + Extension ext = (Extension) 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(), + (RevokedCertificate) 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/base/util/src/netscape/security/x509/X509Cert.java b/base/util/src/netscape/security/x509/X509Cert.java new file mode 100644 index 000000000..9ab7ba754 --- /dev/null +++ b/base/util/src/netscape/security/x509/X509Cert.java @@ -0,0 +1,849 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import 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 <a href="#decode"> + * decode</a> 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 <a href="#signAndEncode">signs and encodes</a> the + * certificate. Also, self-signed certificates serve as CA certificates, + * and are sometimes used as certificate requests. + * + * <P> + * 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. + * + * <P> + * 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 instanceof PublicKey)) + throw new CertException(CertException.err_INVALID_PUBLIC_KEY, + "Doesn't implement PublicKey interface"); + + /* + * 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 <a href="#getIssuerName">getIssuerName</a> */ + public Principal getGuarantor() // for Certificate + { + return getIssuerName(); + } + + /** Returns <a href="#getSubjectName">getSubjectName</a> */ + 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 <em>not</em> indicate that one should + * trust the entity which it represents. + * + * <P> + * <em>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. + * + * <P>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. + * </em> + * + * @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). + * + * <P> + * <em><b>NOTE:</b> 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.</em> + * + * @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 instanceof Key) { + Key key = (Key) privateKey; + algorithm = key.getAlgorithm(); + } else { + throw new InvalidKeyException("private key not a key!"); + } + + 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. + * + * <P> + * <em><b>NOTE:</b> 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.</em> + * + * @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. + * + * <P> + * 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. + */ + 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 synchronized 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 synchronized void + readObject(ObjectInputStream stream) + throws IOException { + decode(stream); + } +} diff --git a/base/util/src/netscape/security/x509/X509CertImpl.java b/base/util/src/netscape/security/x509/X509CertImpl.java new file mode 100755 index 000000000..9c62b36a6 --- /dev/null +++ b/base/util/src/netscape/security/x509/X509CertImpl.java @@ -0,0 +1,1226 @@ +// --- 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.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 netscape.security.util.BigInt; +import netscape.security.util.DerEncoder; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; +import 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. + * + * <P> + * These certificates are managed and vouched for by <em>Certificate + * Authorities</em> (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. + * + * <P> + * 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. + * + * <P> + * 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 <code>DerEncoder</code> 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 <em>not</em> 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 { + + verify(key, null); + } + + /** + * Throws an exception if the certificate was not signed using the + * verification key provided. Successfully verifying a certificate + * does <em>not</em> 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 { + 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 out = new DerOutputStream(); + 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 <code>date</code> supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the <code>date</code> 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<String> getElements() { + Vector<String> elements = new Vector<String>(); + 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(); + + sb.append("[\n"); + sb.append(info.toString() + "\n"); + sb.append(" Algorithm: [" + algId.toString() + "]\n"); + + netscape.security.util.PrettyPrintFormat pp = + new 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 ((BigInt) 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; + } + } + + /** + * 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; + } + } + + /** + * 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 <code>tbsCertificate</code> 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<String> getCriticalExtensionOIDs() { + if (info == null) + return null; + try { + CertificateExtensions exts = (CertificateExtensions) info.get( + CertificateExtensions.NAME); + if (exts == null) + return null; + Set<String> extSet = new LinkedHashSet<String>(); + Extension ex; + for (Enumeration<Extension> e = exts.getAttributes(); e.hasMoreElements();) { + ex = e.nextElement(); + if (ex.isCritical()) + extSet.add(((ObjectIdentifier) 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<String> getNonCriticalExtensionOIDs() { + if (info == null) + return null; + try { + CertificateExtensions exts = (CertificateExtensions) info.get( + CertificateExtensions.NAME); + if (exts == null) + return null; + + Set<String> extSet = new LinkedHashSet<String>(); + Extension ex; + for (Enumeration<Extension> e = exts.getAttributes(); e.hasMoreElements();) { + ex = e.nextElement(); + if (!ex.isCritical()) + extSet.add(((ObjectIdentifier) 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<Extension> 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) { + 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<Extension> 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; + + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extData); + return out.toByteArray(); + } catch (Exception e) { + return null; + } + } + + /** + * 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 synchronized 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 synchronized 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. + * + * <p> + * + * @param type the standard name of the Certificate type. + * <p> + * + * @param data the Certificate data. + */ + protected CertificateRep1(String type, byte[] data) { + this.type1 = type; + this.data1 = data; + } + + /** + * Resolve the Certificate Object. + * + * <p> + * + * @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/base/util/src/netscape/security/x509/X509CertInfo.java b/base/util/src/netscape/security/x509/X509CertInfo.java new file mode 100644 index 000000000..4777cd958 --- /dev/null +++ b/base/util/src/netscape/security/x509/X509CertInfo.java @@ -0,0 +1,964 @@ +// --- 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.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 netscape.security.util.DerInputStream; +import netscape.security.util.DerOutputStream; +import netscape.security.util.DerValue; + +/** + * The X509CertInfo class represents X.509 certificate information. + * + * <P> + * X.509 certificates have several base data elements, including: + * <UL> + * + * <LI>The <em>Subject Name</em>, an X.500 Distinguished Name for the entity (subject) for which the certificate was + * issued. + * + * <LI>The <em>Subject Public Key</em>, the public key of the subject. This is one of the most important parts of the + * certificate. + * + * <LI>The <em>Validity Period</em>, a time period (e.g. six months) within which the certificate is valid (unless + * revoked). + * + * <LI>The <em>Issuer Name</em>, an X.500 Distinguished Name for the Certificate Authority (CA) which issued the + * certificate. + * + * <LI>A <em>Serial Number</em> 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<String, Integer> map = new Hashtable<String, Integer>(); + 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 <a href="#decode"> + * decode</a> 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.toString()); + } + } + + /** + * 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.toString()); + } + } + + /** + * 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<String> getAttributeNames() { + Vector<String> elements = new Vector<String>(); + 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.toString()); + } catch (CertificateException e) { + throw new CertificateEncodingException(e.toString()); + } + } + + /** + * 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(); + + sb.append("[\n"); + sb.append(" " + version.toString() + "\n"); + sb.append(" Subject: " + subject.toString() + "\n"); + sb.append(" Signature Algorithm: " + algId.toString() + "\n"); + sb.append(" Key: " + pubKey.toString() + "\n"); + sb.append(" " + interval.toString() + "\n"); + sb.append(" Issuer: " + issuer.toString() + "\n"); + sb.append(" " + 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) { + netscape.security.util.PrettyPrintFormat pp = + new netscape.security.util.PrettyPrintFormat(" ", 20); + for (int i = 0; i < extensions.size(); i++) { + sb.append(" Extension[" + i + "] = "); + Extension ext = (Extension) extensions.elementAt(i); + try { + if (OIDMap.getClass(ext.getExtensionId()) == null) { + sb.append(ext.toString()); + byte[] extValue = ext.getExtensionValue(); + if (extValue != null) { + DerOutputStream 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 (Exception e) { + sb.append(", Error parsing this extension"); + } + } + } + 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 synchronized 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 synchronized 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; + } + + /** + * 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; + } + + /** + * 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/base/util/src/netscape/security/x509/X509ExtensionException.java b/base/util/src/netscape/security/x509/X509ExtensionException.java new file mode 100644 index 000000000..c7174aed8 --- /dev/null +++ b/base/util/src/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 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/base/util/src/netscape/security/x509/X509Key.java b/base/util/src/netscape/security/x509/X509Key.java new file mode 100644 index 000000000..a8253c0d8 --- /dev/null +++ b/base/util/src/netscape/security/x509/X509Key.java @@ -0,0 +1,508 @@ +// --- 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.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 netscape.security.util.DerOutputStream; +import 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". + * + * <P> + * 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 <code>decode</code>. + */ + 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. + * + * <P> + * 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 <code>key</code> bits; Diffie-Hellman and + * DSS/DSA keys encapsulate a single unsigned integer. + * + * <P> + * This function is called when creating X.509 SubjectPublicKeyInfo values using the X509Key member functions, such + * as <code>parse</code> and <code>decode</code>. + * + * @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() { + netscape.security.util.PrettyPrintFormat pp = + new 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 <code>SubjectPublicKeyInfo</code> 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.) + * + * <P> + * Subclasses should not normally redefine this method; they should instead provide a <code>parseKeyBits</code> + * method to parse any fields inside the <code>key</code> member. + * + * <P> + * The exception to this rule is that since private keys need not be encoded using the X.509 + * <code>SubjectPublicKeyInfo</code> format, private keys may override this method, <code>encode</code>, and of + * course <code>getFormat</code>. + * + * @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 synchronized 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 synchronized 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()); + } + + } + +} |