From: adam Date: Tue, 16 Mar 2004 05:00:05 +0000 (+0000) Subject: import X-Git-Url: http://git.megacz.com/?p=org.ibex.wildebeest.git;a=commitdiff_plain;h=85e24abe600bebadc68bce7cf47d087177b4eb83 import darcs-hash:20040316050005-5007d-d3b0594e35ba6c6edf7b86abf5a5dcb5b471fa9c.gz --- 85e24abe600bebadc68bce7cf47d087177b4eb83 diff --git a/lib/netscape.jar b/lib/netscape.jar new file mode 100644 index 0000000..b1a72b2 Binary files /dev/null and b/lib/netscape.jar differ diff --git a/src/org/xwt/plat/Win32-dll.cc b/src/org/xwt/plat/Win32-dll.cc new file mode 100644 index 0000000..ff823e5 --- /dev/null +++ b/src/org/xwt/plat/Win32-dll.cc @@ -0,0 +1,307 @@ +// Copyright 2002 Adam Megacz, ALL RIGHTS RESERVED + +// +// A simple DLL to invoke xwt.exe and pass it any arguments found in the tag +// + +#include +#include +#include +#include +#include +#include +#include + + +// Globals //////////////////////////////////////////////////////////////////////// + +using namespace std; + +#define CLSID_STRING_SIZE 39 + +const char XWT_friendlyName[] = "XWT ActiveX Control (build " BUILDID ")"; +const char XWT_versionIndependantProgramID[] = "XWT.ActiveX"; +const char XWT_programID[] = "XWT.ActiveX (build " BUILDID ")"; +extern "C" const CLSID XWT_clsid = CLSID_STRUCT; +static HMODULE g_hModule = NULL; //DLL handle + + + + +// Superclasses //////////////////////////////////////////////////////////////////////// + +// Option bit definitions for IObjectSafety: +#define INTERFACESAFE_FOR_UNTRUSTED_CALLER 0x00000001 // Caller of interface may be untrusted +#define INTERFACESAFE_FOR_UNTRUSTED_DATA 0x00000002 // Data passed into interface may be untrusted + +// {CB5BDC81-93C1-11cf-8F20-00805F2CD064} +//DEFINE_GUID(IID_IObjectSafety, 0xcb5bdc81, 0x93c1, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64); +extern "C" const CLSID IID_IObjectSafety; + +interface IObjectSafety : public IUnknown { + public: + virtual HRESULT __stdcall GetInterfaceSafetyOptions(REFIID riid, DWORD __RPC_FAR *pdwSupportedOptions, DWORD __RPC_FAR *pdwEnabledOptions) = 0; + virtual HRESULT __stdcall SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions) = 0; +}; + +interface IShoeHorn : IPersistPropertyBag, IObjectSafety { }; + + + +// Entry Points //////////////////////////////////////////////////////////////////////// + +// to get mingw to stop nagging me +int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { } + +// determines whether or not the DLL can be unloaded; always allow this since we don't do too much +STDAPI __declspec(dllexport) DllCanUnloadNow(void) { return S_OK; } + +extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID /*lpReserved*/) { + if (dwReason == DLL_PROCESS_ATTACH) g_hModule = (HINSTANCE)hModule; + return TRUE; +} + + + +// Other /////////////////////////////////////////////////////////////////////////////////// + +// simple assert() replacement that pops open a message box if there are errors +void check(int val, char* message) { + if (!val) { + MessageBox (NULL, message, "XWT Critical Abort", MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL | MB_SETFOREGROUND); + exit(-1); + } +} + +void clsidToString(const CLSID& clsid, char* str, int length) { + check(length >= CLSID_STRING_SIZE, "clsidToString(): string too short"); + LPOLESTR wide_str = NULL; + HRESULT hr = StringFromCLSID(clsid, &wide_str); + check(SUCCEEDED(hr), "StringFromCLSID() failed in clsidToString()"); + wcstombs(str, wide_str, length); + CoTaskMemFree(wide_str); +} + +void setRegistryKey(const char* key, const char* subkey, const char* value) { + HKEY hKey; + char keyBuf[1024]; + strcpy(keyBuf, key); + if (subkey != NULL) { + strcat(keyBuf, "\\"); + strcat(keyBuf, subkey ); + } + long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, keyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); + if (value != NULL) + check(RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)value, strlen(value) + 1) == ERROR_SUCCESS, + "RegSetValueEx() failed in setRegistryKey()"); + RegCloseKey(hKey); +} + +void deleteRegistryKey(HKEY parent, const char* target) { + HKEY hKeyChild; + RegOpenKeyEx(parent, target, 0, KEY_ALL_ACCESS, &hKeyChild); + + // Iterate over children, deleting them + FILETIME time; + char szBuffer[256]; + DWORD dwSize = 256; + while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL, NULL, NULL, &time) == S_OK) { + deleteRegistryKey(hKeyChild, szBuffer); + dwSize = 256; + } + + RegCloseKey(hKeyChild); + RegDeleteKey(parent, target); +} + +STDAPI __declspec(dllexport) DllRegisterServer(void) { + char moduleName[512]; + HRESULT result = GetModuleFileName(g_hModule, moduleName, sizeof(moduleName)/sizeof(char)); + check(result, "GetModuleFileName() failed in RegisterServer()"); + + char clsidString[CLSID_STRING_SIZE]; + clsidToString(XWT_clsid, clsidString, sizeof(clsidString)); + + // build the key + char key[64]; + strcpy(key, "CLSID\\"); + strcat(key, clsidString); + + setRegistryKey(key, NULL, XWT_friendlyName); + setRegistryKey(key, "InprocServer32", moduleName); + setRegistryKey(key, "ProgID", XWT_programID); + setRegistryKey(key, "VersionIndependentProgID", XWT_versionIndependantProgramID); + setRegistryKey(XWT_versionIndependantProgramID, NULL, XWT_friendlyName); + setRegistryKey(XWT_versionIndependantProgramID, "CLSID", clsidString); + setRegistryKey(XWT_versionIndependantProgramID, "CurVer", XWT_programID); + setRegistryKey(XWT_programID, NULL, XWT_friendlyName); + setRegistryKey(XWT_programID, "CLSID", clsidString); + return S_OK; +} + +STDAPI __declspec(dllexport) DllUnregisterServer(void) { + char clsidString[CLSID_STRING_SIZE]; + clsidToString(XWT_clsid, clsidString, sizeof(clsidString)); + + // build the key + char key[64]; + strcpy(key, "CLSID\\"); + strcat(key, clsidString); + + deleteRegistryKey(HKEY_CLASSES_ROOT, key); + deleteRegistryKey(HKEY_CLASSES_ROOT, XWT_versionIndependantProgramID); + deleteRegistryKey(HKEY_CLASSES_ROOT, XWT_programID); + return S_OK; +} + + + +// ShoeHorn ////////////////////////////////////////////////////////////////////////////////// + +class ShoeHorn : public IShoeHorn { + public: + virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) { + if(iid == IID_IUnknown) *ppv = static_cast(this); + else if(iid == XWT_clsid) *ppv = static_cast(this); + else if(iid == IID_IPersistPropertyBag) *ppv = static_cast(this); + else if(iid == IID_IObjectSafety) *ppv = static_cast(this); + else { *ppv = NULL; return E_NOINTERFACE; } + reinterpret_cast(*ppv)->AddRef(); + return S_OK; + } + virtual ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); } + virtual ULONG __stdcall Release() { + if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } + return m_cRef; + } + virtual HRESULT __stdcall GetClassID(CLSID*) { return S_OK; } + virtual HRESULT __stdcall InitNew() { return S_OK; } + virtual HRESULT __stdcall Save(IPropertyBag*, int, int) { return S_OK; } + virtual HRESULT __stdcall SetInterfaceSafetyOptions(REFIID riid, DWORD pdwSupportedOptions, DWORD pdwEnabledOptions) { return S_OK; } + virtual HRESULT __stdcall GetInterfaceSafetyOptions(REFIID riid, DWORD* pdwSupportedOptions, DWORD* pdwEnabledOptions) { + if (pdwSupportedOptions != NULL) *pdwSupportedOptions |= INTERFACESAFE_FOR_UNTRUSTED_DATA; + if (pdwEnabledOptions != NULL) *pdwSupportedOptions |= INTERFACESAFE_FOR_UNTRUSTED_DATA; + return S_OK; + } + + virtual HRESULT __stdcall Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog) { + VARIANT v; + v.vt = VT_BSTR; + HRESULT hrRead; + + WCHAR wc[100]; + char url[100]; + + MultiByteToWideChar(CP_ACP, 0, "xwar", -1, wc, 100); + pPropBag->Read(wc, &v, pErrorLog); + check(WideCharToMultiByte(CP_ACP, 0, v.bstrVal, -1, url, 100, NULL, NULL), + "WideCharToMultiByte() failed in ShoeHorn::Load()"); + + HKEY hkey; + LONG result = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ActiveX Cache", &hkey); + check(result == ERROR_SUCCESS, "RegOpenKey() failed in ShoeHorn::Load()"); + + // iterate over all the activex cache locations until we find ourselves + for(int i=0; i<9; i++) { + DWORD buflen = 999; + char buf[1000]; + VALENT valents[20]; + DWORD type; + char which[2]; + + which[0] = '0' + i; + which[1] = '\0'; + result = RegQueryValueEx(hkey, which, NULL, &type, (BYTE*)buf, &buflen); + if (result != ERROR_SUCCESS) + if (i == 0) { + check(0, "RegQueryValueEx() failed in ShoeHorn::Load()"); + } else { + break; + } + buf[buflen] = '\0'; + + char cmdline[200]; + for(int i=0; i<200; i++) cmdline[i] = '\0'; + strncpy(cmdline, buf, 200); + strncpy(cmdline + strlen(cmdline), "\\xwt-" BUILDID ".exe", 200 - strlen(cmdline)); + strncpy(cmdline + strlen(cmdline), " ", 200 - strlen(cmdline)); + strncpy(cmdline + strlen(cmdline), url, 200 - strlen(cmdline)); + + PROCESS_INFORMATION pInfo; + STARTUPINFO sInfo; + sInfo.cb = sizeof(STARTUPINFO); + sInfo.lpReserved = NULL; + sInfo.lpReserved2 = NULL; + sInfo.cbReserved2 = 0; + sInfo.lpDesktop = NULL; + sInfo.lpTitle = NULL; + sInfo.dwFlags = 0; + sInfo.dwX = 0; + sInfo.dwY = 0; + sInfo.dwFillAttribute = 0; + sInfo.wShowWindow = SW_SHOW; + BOOL b = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &sInfo, &pInfo); + if (b) return S_OK; + } + + check(0, "unable to locate xwt-" BUILDID ".exe in ActiveX cache folders"); + } + + ShoeHorn() : m_cRef(1) { }; + ~ShoeHorn() { }; + private: long m_cRef; +}; + + + + +// ClassFactory ////////////////////////////////////////////////////////////////////////// + +class ClassFactory : public IClassFactory { + public: + virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) { + if(iid == IID_IUnknown) *ppv = static_cast(this); + else if(iid == IID_IClassFactory) *ppv = static_cast(this); + else { + *ppv = NULL; + return E_NOINTERFACE; + } + reinterpret_cast(*ppv)->AddRef(); + return S_OK; + } + + ClassFactory() : m_cRef(1) { } + ~ClassFactory() { } + virtual HRESULT __stdcall LockServer(BOOL bLock) { return S_OK; } + virtual ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); } + virtual ULONG __stdcall Release() { + if(InterlockedDecrement(&m_cRef) == 0) { + delete this; + return 0; + } + return m_cRef; + } + + virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv) { + if(pUnknownOuter != NULL) return CLASS_E_NOAGGREGATION; + ShoeHorn* s = new ShoeHorn; + if(s == NULL) return E_OUTOFMEMORY; + HRESULT hr = s->QueryInterface(iid, ppv); + s->Release(); + return hr; + } + + private: long m_cRef; +}; + + +extern "C" __stdcall HRESULT DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) { + if(clsid != XWT_clsid) return CLASS_E_CLASSNOTAVAILABLE; + ClassFactory* pFactory = new ClassFactory; + if(pFactory == NULL) return E_OUTOFMEMORY; + HRESULT hr = pFactory->QueryInterface(iid, ppv); + pFactory->Release(); + return hr; +} + + diff --git a/src/org/xwt/plat/Win32.def b/src/org/xwt/plat/Win32.def new file mode 100644 index 0000000..9a790cb --- /dev/null +++ b/src/org/xwt/plat/Win32.def @@ -0,0 +1,5 @@ +EXPORTS + DllGetClassObject = DllGetClassObject@12 + DllCanUnloadNow = DllCanUnloadNow@0 + DllRegisterServer = DllRegisterServer@0 + DllUnregisterServer = DllUnregisterServer@0 diff --git a/src/org/xwt/plat/Win32.inf b/src/org/xwt/plat/Win32.inf new file mode 100644 index 0000000..ca2eb91 --- /dev/null +++ b/src/org/xwt/plat/Win32.inf @@ -0,0 +1,19 @@ +;; This file will be copied to bin-Win32/cabsrc/xwt-__BUILD__.inf and then packed up +;; into the .cab file for distribution + +[version] + signature="$CHICAGO$" + AdvancedINF=2.0 +[Add.Code] + xwt-__BUILD__.dll=xwt-__BUILD__.dll + xwt-__BUILD__.exe=xwt-__BUILD__.exe +[xwt-__BUILD__.dll] + file-win32-x86=thiscab + clsid={D605__BUILD__-61B3-11d6-82FA-005056CA9250} + FileVersion=7,0,0,0 + RegisterServer=yes +[xwt-__BUILD__.exe] + file-win32-x86=thiscab + clsid={FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFE} + FileVersion=7,0,0,0 + RegisterServer=no diff --git a/src/org/xwt/shoehorn/ShoeHorn.java b/src/org/xwt/shoehorn/ShoeHorn.java new file mode 100644 index 0000000..bf34c2f --- /dev/null +++ b/src/org/xwt/shoehorn/ShoeHorn.java @@ -0,0 +1,3 @@ +// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] +package org.xwt.shoehorn; +public class ShoeHorn extends org.xwt.shoehorn3.ShoeHorn { } diff --git a/src/org/xwt/shoehorn/ShoeHorn1.java b/src/org/xwt/shoehorn/ShoeHorn1.java new file mode 100644 index 0000000..0fdac2e --- /dev/null +++ b/src/org/xwt/shoehorn/ShoeHorn1.java @@ -0,0 +1,3 @@ +// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] +package org.xwt.shoehorn; +public class ShoeHorn1 extends org.xwt.shoehorn3.ShoeHorn { } diff --git a/src/org/xwt/shoehorn2/ShoeHorn.java b/src/org/xwt/shoehorn2/ShoeHorn.java new file mode 100644 index 0000000..4e3edbf --- /dev/null +++ b/src/org/xwt/shoehorn2/ShoeHorn.java @@ -0,0 +1,3 @@ +// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] +package org.xwt.shoehorn2; +public class ShoeHorn extends org.xwt.shoehorn3.ShoeHorn { } diff --git a/src/org/xwt/shoehorn3/Crypto.java b/src/org/xwt/shoehorn3/Crypto.java new file mode 100644 index 0000000..cf90501 --- /dev/null +++ b/src/org/xwt/shoehorn3/Crypto.java @@ -0,0 +1,10138 @@ +// This is just a cut-and-paste of a bunch of files from Bouncycastle +package org.xwt.shoehorn3; + +import java.io.*; +import java.math.BigInteger; +import java.util.*; +import java.text.*; + +abstract class ASN1OctetString + extends DERObject +{ + byte[] string; + + /** + * return an Octet String from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1OctetString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * return an Octet String from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1OctetString getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1OctetString) + { + return (ASN1OctetString)obj; + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + if (obj instanceof ASN1Sequence) + { + Vector v = new Vector(); + Enumeration e = ((ASN1Sequence)obj).getObjects(); + + while (e.hasMoreElements()) + { + v.addElement(e.nextElement()); + } + + return new BERConstructedOctetString(v); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * @param string the octets making up the octet string. + */ + public ASN1OctetString( + byte[] string) + { + this.string = string; + } + + public ASN1OctetString( + DEREncodable obj) + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + + dOut.writeObject(obj); + dOut.close(); + + this.string = bOut.toByteArray(); + } + catch (IOException e) + { + throw new IllegalArgumentException("Error processing object : " + e.toString()); + } + } + + public byte[] getOctets() + { + return string; + } + + public int hashCode() + { + byte[] b = this.getOctets(); + int value = 0; + + for (int i = 0; i != b.length; i++) + { + value ^= (b[i] & 0xff) << (i % 4); + } + + return value; + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof DEROctetString)) + { + return false; + } + + DEROctetString other = (DEROctetString)o; + + byte[] b1 = other.getOctets(); + byte[] b2 = this.getOctets(); + + if (b1.length != b2.length) + { + return false; + } + + for (int i = 0; i != b1.length; i++) + { + if (b1[i] != b2[i]) + { + return false; + } + } + + return true; + } + + abstract void encode(DEROutputStream out) + throws IOException; +} + + +class ASN1OutputStream + extends DEROutputStream +{ + public ASN1OutputStream( + OutputStream os) + { + super(os); + } + + public void writeObject( + Object obj) + throws IOException + { + if (obj == null) + { + writeNull(); + } + else if (obj instanceof DERObject) + { + ((DERObject)obj).encode(this); + } + else if (obj instanceof DEREncodable) + { + ((DEREncodable)obj).getDERObject().encode(this); + } + else + { + throw new IOException("object not ASN1Encodable"); + } + } +} + + +abstract class ASN1Sequence + extends DERObject +{ + private Vector seq = new Vector(); + + /** + * return an ASN1Sequence from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1Sequence getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1Sequence) + { + return (ASN1Sequence)obj; + } + + throw new IllegalArgumentException("unknown object in getInstance"); + } + + /** + * Return an ASN1 sequence from a tagged object. There is a special + * case here, if an object appears to have been explicitly tagged on + * reading but we were expecting it to be implictly tagged in the + * normal course of events it indicates that we lost the surrounding + * sequence - so we need to add it back (this will happen if the tagged + * object is a sequence that contains other sequences). If you are + * dealing with implicitly tagged sequences you really should + * be using this method. + * + * @param obj the tagged object. + * @param explicit true if the object is meant to be explicitly tagged, + * false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1Sequence getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + if (explicit) + { + if (!obj.isExplicit()) + { + throw new IllegalArgumentException("object implicit - explicit expected."); + } + + return (ASN1Sequence)obj.getObject(); + } + else + { + // + // constructed object which appears to be explicitly tagged + // when it should be implicit means we have to add the + // surrounding sequence. + // + if (obj.isExplicit()) + { + ASN1Sequence seq; + + if (obj instanceof BERTaggedObject) + { + seq = new BERConstructedSequence(); + } + else + { + seq = new DERConstructedSequence(); + } + + seq.addObject(obj.getObject()); + + return seq; + } + else + { + ASN1Sequence seq; + + if (obj.getObject() instanceof ASN1Sequence) + { + return (ASN1Sequence)obj.getObject(); + } + } + } + + throw new IllegalArgumentException( + "unknown object in getInstanceFromTagged"); + } + + public Enumeration getObjects() + { + return seq.elements(); + } + + /** + * return the object at the sequence postion indicated by index. + * + * @param the sequence number (starting at zero) of the object + * @return the object at the sequence postion indicated by index. + */ + public DEREncodable getObjectAt( + int index) + { + return (DEREncodable)seq.elementAt(index); + } + + /** + * return the number of objects in this sequence. + * + * @return the number of objects in this sequence. + */ + public int size() + { + return seq.size(); + } + + public int hashCode() + { + Enumeration e = this.getObjects(); + int hashCode = 0; + + while (e.hasMoreElements()) + { + hashCode ^= e.nextElement().hashCode(); + } + + return hashCode; + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof ASN1Sequence)) + { + return false; + } + + ASN1Sequence other = (ASN1Sequence)o; + + if (this.size() != other.size()) + { + return false; + } + + Enumeration s1 = this.getObjects(); + Enumeration s2 = other.getObjects(); + + while (s1.hasMoreElements()) + { + if (!s1.nextElement().equals(s2.nextElement())) + { + return false; + } + } + + return true; + } + + protected void addObject( + DEREncodable obj) + { + seq.addElement(obj); + } + + abstract void encode(DEROutputStream out) + throws IOException; +} + + +abstract class ASN1Set + extends DERObject +{ + protected Vector set = new Vector(); + + /** + * return an ASN1Set from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1Set getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1Set) + { + return (ASN1Set)obj; + } + + throw new IllegalArgumentException("unknown object in getInstance"); + } + + /** + * Return an ASN1 set from a tagged object. There is a special + * case here, if an object appears to have been explicitly tagged on + * reading but we were expecting it to be implictly tagged in the + * normal course of events it indicates that we lost the surrounding + * set - so we need to add it back (this will happen if the tagged + * object is a sequence that contains other sequences). If you are + * dealing with implicitly tagged sets you really should + * be using this method. + * + * @param obj the tagged object. + * @param explicit true if the object is meant to be explicitly tagged + * false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1Set getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + if (explicit) + { + if (!obj.isExplicit()) + { + throw new IllegalArgumentException("object implicit - explicit expected."); + } + + return (ASN1Set)obj.getObject(); + } + else + { + // + // constructed object which appears to be explicitly tagged + // and it's really implicit means we have to add the + // surrounding sequence. + // + if (obj.isExplicit()) + { + ASN1Set set = new DERSet(obj.getObject()); + + return set; + } + else + { + // + // in this case the parser returns a sequence, convert it + // into a set. + // + DEREncodableVector v = new DEREncodableVector(); + + if (obj.getObject() instanceof ASN1Sequence) + { + ASN1Sequence s = (ASN1Sequence)obj.getObject(); + Enumeration e = s.getObjects(); + + while (e.hasMoreElements()) + { + v.add((DEREncodable)e.nextElement()); + } + + return new DERSet(v); + } + } + } + + throw new IllegalArgumentException( + "unknown object in getInstanceFromTagged"); + } + + public ASN1Set() + { + } + + public Enumeration getObjects() + { + return set.elements(); + } + + /** + * return the object at the set postion indicated by index. + * + * @param the set number (starting at zero) of the object + * @return the object at the set postion indicated by index. + */ + public DEREncodable getObjectAt( + int index) + { + return (DEREncodable)set.elementAt(index); + } + + /** + * return the number of objects in this set. + * + * @return the number of objects in this set. + */ + public int size() + { + return set.size(); + } + + public int hashCode() + { + Enumeration e = this.getObjects(); + int hashCode = 0; + + while (e.hasMoreElements()) + { + hashCode ^= e.nextElement().hashCode(); + } + + return hashCode; + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof ASN1Set)) + { + return false; + } + + ASN1Set other = (ASN1Set)o; + + if (this.size() != other.size()) + { + return false; + } + + Enumeration s1 = this.getObjects(); + Enumeration s2 = other.getObjects(); + + while (s1.hasMoreElements()) + { + if (!s1.nextElement().equals(s2.nextElement())) + { + return false; + } + } + + return true; + } + + protected void addObject( + DEREncodable obj) + { + set.addElement(obj); + } + + abstract void encode(DEROutputStream out) + throws IOException; +} + + +/** + * ASN.1 TaggedObject - in ASN.1 nottation this is any object proceeded by + * a [n] where n is some number - these are assume to follow the construction + * rules (as with sequences). + */ +abstract class ASN1TaggedObject + extends DERObject +{ + int tagNo; + boolean empty = false; + boolean explicit = true; + DEREncodable obj = null; + + /** + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public ASN1TaggedObject( + int tagNo, + DEREncodable obj) + { + this.explicit = true; + this.tagNo = tagNo; + this.obj = obj; + } + + /** + * @param explicit true if the object is explicitly tagged. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public ASN1TaggedObject( + boolean explicit, + int tagNo, + DEREncodable obj) + { + this.explicit = explicit; + this.tagNo = tagNo; + this.obj = obj; + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof ASN1TaggedObject)) + { + return false; + } + + ASN1TaggedObject other = (ASN1TaggedObject)o; + + if(tagNo != other.tagNo || empty != other.empty || explicit != other.explicit) + { + return false; + } + + if(obj == null) + { + if(other.obj != null) + { + return false; + } + } + else + { + if(!(obj.equals(other.obj))) + { + return false; + } + } + + return true; + } + + public int getTagNo() + { + return tagNo; + } + + /** + * return whether or not the object may be explicitly tagged. + *

+ * Note: if the object has been read from an input stream, the only + * time you can be sure if isExplicit is returning the true state of + * affairs is if it returns false. An implicitly tagged object may appear + * to be explicitly tagged, so you need to understand the context under + * which the reading was done as well, see getObject below. + */ + public boolean isExplicit() + { + return explicit; + } + + public boolean isEmpty() + { + return empty; + } + + /** + * return whatever was following the tag. + *

+ * Note: tagged objects are generally context dependent if you're + * trying to extract a tagged object you should be going via the + * appropriate getInstance method. + */ + public DERObject getObject() + { + if (obj != null) + { + return obj.getDERObject(); + } + + return null; + } + + abstract void encode(DEROutputStream out) + throws IOException; +} + + +class BERConstructedOctetString + extends DEROctetString +{ + /** + * convert a vector of octet strings into a single byte string + */ + static private byte[] toBytes( + Vector octs) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + for (int i = 0; i != octs.size(); i++) + { + DEROctetString o = (DEROctetString)octs.elementAt(i); + + try + { + bOut.write(o.getOctets()); + } + catch (IOException e) + { + throw new RuntimeException("exception converting octets " + e.toString()); + } + } + + return bOut.toByteArray(); + } + + private Vector octs; + + /** + * @param string the octets making up the octet string. + */ + public BERConstructedOctetString( + byte[] string) + { + super(string); + } + + public BERConstructedOctetString( + Vector octs) + { + super(toBytes(octs)); + + this.octs = octs; + } + + public BERConstructedOctetString( + DERObject obj) + { + super(obj); + } + + public BERConstructedOctetString( + DEREncodable obj) + { + super(obj.getDERObject()); + } + + public byte[] getOctets() + { + return string; + } + + /** + * return the DER octets that make up this string. + */ + public Enumeration getObjects() + { + if (octs == null) + { + octs = generateOcts(); + } + + return octs.elements(); + } + + private Vector generateOcts() + { + int start = 0; + int end = 0; + Vector vec = new Vector(); + + while ((end + 1) < string.length) + { + if (string[end] == 0 && string[end + 1] == 0) + { + byte[] nStr = new byte[end - start + 1]; + + for (int i = 0; i != nStr.length; i++) + { + nStr[i] = string[start + i]; + } + + vec.addElement(new DEROctetString(nStr)); + start = end + 1; + } + end++; + } + + byte[] nStr = new byte[string.length - start]; + for (int i = 0; i != nStr.length; i++) + { + nStr[i] = string[start + i]; + } + + vec.addElement(new DEROctetString(nStr)); + + return vec; + } + + public void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(CONSTRUCTED | OCTET_STRING); + + out.write(0x80); + + if (octs == null) + { + octs = generateOcts(); + } + + for (int i = 0; i != octs.size(); i++) + { + out.writeObject(octs.elementAt(i)); + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} + + +class BERConstructedSequence + extends DERConstructedSequence +{ + /* + */ + void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(SEQUENCE | CONSTRUCTED); + out.write(0x80); + + Enumeration e = getObjects(); + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} + + +class BERInputStream + extends DERInputStream +{ + private DERObject END_OF_STREAM = new DERObject() { + void encode( + DEROutputStream out) + throws IOException + { + throw new IOException("Eeek!"); + } + + }; + public BERInputStream( + InputStream is) + { + super(is); + } + + /** + * read a string of bytes representing an indefinite length object. + */ + private byte[] readIndefiniteLengthFully() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + int b, b1; + + b1 = read(); + + while ((b = read()) >= 0) + { + if (b1 == 0 && b == 0) + { + break; + } + + bOut.write(b1); + b1 = b; + } + + return bOut.toByteArray(); + } + + private BERConstructedOctetString buildConstructedOctetString() + throws IOException + { + Vector octs = new Vector(); + + for (;;) + { + DERObject o = readObject(); + + if (o == END_OF_STREAM) + { + break; + } + + octs.addElement(o); + } + + return new BERConstructedOctetString(octs); + } + + public DERObject readObject() + throws IOException + { + int tag = read(); + if (tag == -1) + { + throw new EOFException(); + } + + int length = readLength(); + + if (length < 0) // indefinite length method + { + switch (tag) + { + case NULL: + return null; + case SEQUENCE | CONSTRUCTED: + BERConstructedSequence seq = new BERConstructedSequence(); + + for (;;) + { + DERObject obj = readObject(); + + if (obj == END_OF_STREAM) + { + break; + } + + seq.addObject(obj); + } + return seq; + case OCTET_STRING | CONSTRUCTED: + return buildConstructedOctetString(); + case SET | CONSTRUCTED: + DEREncodableVector v = new DEREncodableVector(); + + for (;;) + { + DERObject obj = readObject(); + + if (obj == END_OF_STREAM) + { + break; + } + + v.add(obj); + } + return new BERSet(v); + default: + // + // with tagged object tag number is bottom 5 bits + // + if ((tag & TAGGED) != 0) + { + if ((tag & 0x1f) == 0x1f) + { + throw new IOException("unsupported high tag encountered"); + } + + // + // simple type - implicit... return an octet string + // + if ((tag & CONSTRUCTED) == 0) + { + byte[] bytes = readIndefiniteLengthFully(); + + return new BERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes)); + } + + // + // either constructed or explicitly tagged + // + DERObject dObj = readObject(); + + if (dObj == END_OF_STREAM) // empty tag! + { + return new DERTaggedObject(tag & 0x1f); + } + + DERObject next = readObject(); + + // + // explicitly tagged (probably!) - if it isn't we'd have to + // tell from the context + // + if (next == END_OF_STREAM) + { + return new BERTaggedObject(tag & 0x1f, dObj); + } + + // + // another implicit object, we'll create a sequence... + // + seq = new BERConstructedSequence(); + + seq.addObject(dObj); + + do + { + seq.addObject(next); + next = readObject(); + } + while (next != END_OF_STREAM); + + return new BERTaggedObject(false, tag & 0x1f, seq); + } + + throw new IOException("unknown BER object encountered"); + } + } + else + { + if (tag == 0 && length == 0) // end of contents marker. + { + return END_OF_STREAM; + } + + byte[] bytes = new byte[length]; + + readFully(bytes); + + return buildObject(tag, bytes); + } + } +} + + +class BEROutputStream + extends DEROutputStream +{ + public BEROutputStream( + OutputStream os) + { + super(os); + } + + public void writeObject( + Object obj) + throws IOException + { + if (obj == null) + { + writeNull(); + } + else if (obj instanceof DERObject) + { + ((DERObject)obj).encode(this); + } + else if (obj instanceof DEREncodable) + { + ((DEREncodable)obj).getDERObject().encode(this); + } + else + { + throw new IOException("object not BEREncodable"); + } + } +} + + +class BERSet + extends DERSet +{ + /** + * create an empty sequence + */ + public BERSet() + { + } + + /** + * create a set containing one object + */ + public BERSet( + DEREncodable obj) + { + super(obj); + } + + /** + * create a set containing a vector of objects. + */ + public BERSet( + DEREncodableVector v) + { + super(v); + } + + /* + */ + void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(SET | CONSTRUCTED); + out.write(0x80); + + Enumeration e = getObjects(); + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} + + +/** + * BER TaggedObject - in ASN.1 nottation this is any object proceeded by + * a [n] where n is some number - these are assume to follow the construction + * rules (as with sequences). + */ +class BERTaggedObject + extends DERTaggedObject +{ + /** + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public BERTaggedObject( + int tagNo, + DEREncodable obj) + { + super(tagNo, obj); + } + + /** + * @param explicit true if an explicitly tagged object. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public BERTaggedObject( + boolean explicit, + int tagNo, + DEREncodable obj) + { + super(explicit, tagNo, obj); + } + + /** + * create an implicitly tagged object that contains a zero + * length sequence. + */ + public BERTaggedObject( + int tagNo) + { + super(false, tagNo, new BERConstructedSequence()); + } + + void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(CONSTRUCTED | TAGGED | tagNo); + out.write(0x80); + + if (!empty) + { + if (!explicit) + { + if (obj instanceof BERConstructedOctetString) + { + Enumeration e = ((BERConstructedOctetString)obj).getObjects(); + + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + } + else if (obj instanceof ASN1Sequence) + { + Enumeration e = ((ASN1Sequence)obj).getObjects(); + + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + } + else if (obj instanceof ASN1Set) + { + Enumeration e = ((ASN1Set)obj).getObjects(); + + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + } + else + { + throw new RuntimeException("not implemented: " + obj.getClass().getName()); + } + } + else + { + out.writeObject(obj); + } + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} + + +class Attribute + implements DEREncodable +{ + private DERObjectIdentifier attrType; + private ASN1Set attrValues; + + /** + * return an Attribute object from the given object. + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static Attribute getInstance( + Object o) + { + if (o == null || o instanceof Attribute) + { + return (Attribute)o; + } + + if (o instanceof ASN1Sequence) + { + return new Attribute((ASN1Sequence)o); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public Attribute( + ASN1Sequence seq) + { + attrType = (DERObjectIdentifier)seq.getObjectAt(0); + attrValues = (ASN1Set)seq.getObjectAt(1); + } + + public Attribute( + DERObjectIdentifier attrType, + ASN1Set attrValues) + { + this.attrType = attrType; + this.attrValues = attrValues; + } + + public DERObjectIdentifier getAttrType() + { + return attrType; + } + + public ASN1Set getAttrValues() + { + return attrValues; + } + + /** + *

+     * Attribute ::= SEQUENCE {
+     * 	attrType OBJECT IDENTIFIER,
+     * 	attrValues SET OF AttributeValue
+     * }
+     * 
+ */ + public DERObject getDERObject() + { + DEREncodableVector v = new DEREncodableVector(); + + v.add(attrType); + v.add(attrValues); + + return new DERSequence(v); + } +} +// Decompiled by Jad v1.5.7f. Copyright 2000 Pavel Kouznetsov. +// Jad home page: http://www.geocities.com/SiliconValley/Bridge/8617/jad.html +// Decompiler options: packimports(3) +// Source File Name: SignedAttributes.java + + + +// Referenced classes of package org.bouncycastle.asn1.cms: +// Attribute + +class SignedAttributes + implements DEREncodable +{ + + public SignedAttributes(Vector vector) + { + setAttributes(vector); + } + + public SignedAttributes(DERConstructedSet derconstructedset) + { + attributes = derconstructedset; + } + + public SignedAttributes(SignedAttributes signedattributes) + { + attributes = signedattributes.attributes; + } + + public static SignedAttributes getInstance(Object obj) + { + if(obj == null) + return null; + if(obj instanceof SignedAttributes) + return (SignedAttributes)obj; + if(obj instanceof DERConstructedSet) + return new SignedAttributes((DERConstructedSet)obj); + if(obj instanceof DERTaggedObject) + return getInstance(((DERTaggedObject)obj).getObject()); + else + throw new IllegalArgumentException("Invalid SignedAttributes"); + } + + public static SignedAttributes newInstance(Object obj) + { + if(obj == null) + return null; + if(obj instanceof SignedAttributes) + return new SignedAttributes((SignedAttributes)obj); + if(obj instanceof DERConstructedSet) + return new SignedAttributes((DERConstructedSet)obj); + if(obj instanceof DERTaggedObject) + return getInstance(((DERTaggedObject)obj).getObject()); + else + throw new IllegalArgumentException("Invalid SignedAttributes"); + } + + public Vector getAttributes() + { + int i = attributes.getSize(); + Vector vector = new Vector(); + for(int j = 0; j < i; j++) + vector.addElement(Attribute.getInstance(attributes.getObjectAt(j))); + + return vector; + } + + private void setAttributes(Vector vector) + { + int i = vector.size(); + attributes = new DERConstructedSet(); + for(int j = 0; j < i; j++) + attributes.addObject(Attribute.getInstance(vector.elementAt(j))); + + } + + public DERObject getDERObject() + { + return attributes; + } + + private DERConstructedSet attributes; +} + + +class DERBitString + extends DERObject +{ + protected byte[] data; + protected int padBits; + + /** + * return the correct number of pad bits for a bit string defined in + * a 16 bit constant + */ + static protected int getPadBits( + int bitString) + { + int val; + + if (bitString == 0) + { + return 7; + } + + if (bitString > 255) + { + val = ((bitString >> 8) & 0xFF); + } + else + { + val = (bitString & 0xFF); + } + + int bits = 1; + + while (((val <<= 1) & 0xFF) != 0) + { + bits++; + } + + return 8 - bits; + } + + /** + * return the correct number of bytes for a bit string defined in + * a 16 bit constant + */ + static protected byte[] getBytes( + int bitString) + { + if (bitString > 255) + { + byte[] bytes = new byte[2]; + + bytes[0] = (byte)(bitString & 0xFF); + bytes[1] = (byte)((bitString >> 8) & 0xFF); + + return bytes; + } + else + { + byte[] bytes = new byte[1]; + + bytes[0] = (byte)(bitString & 0xFF); + + return bytes; + } + } + + /** + * return a Bit String from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERBitString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERBitString) + { + return (DERBitString)obj; + } + + if (obj instanceof ASN1OctetString) + { + byte[] bytes = ((ASN1OctetString)obj).getOctets(); + int padBits = bytes[0]; + byte[] data = new byte[bytes.length - 1]; + + System.arraycopy(bytes, 1, data, 0, bytes.length - 1); + + return new DERBitString(data, padBits); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Bit String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERBitString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + protected DERBitString( + byte data, + int padBits) + { + this.data = new byte[1]; + this.data[0] = data; + this.padBits = padBits; + } + + /** + * @param data the octets making up the bit string. + * @param padBits the number of extra bits at the end of the string. + */ + public DERBitString( + byte[] data, + int padBits) + { + this.data = data; + this.padBits = padBits; + } + + public DERBitString( + byte[] data) + { + this(data, 0); + } + + public DERBitString( + DEREncodable obj) + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + + dOut.writeObject(obj); + dOut.close(); + + this.data = bOut.toByteArray(); + this.padBits = 0; + } + catch (IOException e) + { + throw new IllegalArgumentException("Error processing object : " + e.toString()); + } + } + + public byte[] getBytes() + { + return data; + } + + public int getPadBits() + { + return padBits; + } + + void encode( + DEROutputStream out) + throws IOException + { + byte[] bytes = new byte[getBytes().length + 1]; + + bytes[0] = (byte)getPadBits(); + System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1); + + out.writeEncoded(BIT_STRING, bytes); + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof DERBitString)) + { + return false; + } + + DERBitString other = (DERBitString)o; + + if (data.length != other.data.length) + { + return false; + } + + for (int i = 0; i != data.length; i++) + { + if (data[i] != other.data[i]) + { + return false; + } + } + + return (padBits == other.padBits); + } +} + + +/** + * DER BMPString object. + */ +class DERBMPString + extends DERObject + implements DERString +{ + String string; + + /** + * return a BMP String from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERBMPString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERBMPString) + { + return (DERBMPString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERBMPString(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a BMP String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERBMPString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + + /** + * basic constructor - byte encoded string. + */ + public DERBMPString( + byte[] string) + { + char[] cs = new char[string.length / 2]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff)); + } + + this.string = new String(cs); + } + + /** + * basic constructor + */ + public DERBMPString( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DERBMPString)) + { + return false; + } + + DERPrintableString s = (DERPrintableString)o; + + return this.getString().equals(s.getString()); + } + + void encode( + DEROutputStream out) + throws IOException + { + char[] c = string.toCharArray(); + byte[] b = new byte[c.length * 2]; + + for (int i = 0; i != c.length; i++) + { + b[2 * i] = (byte)(c[i] >> 8); + b[2 * i + 1] = (byte)c[i]; + } + + out.writeEncoded(BMP_STRING, b); + } +} + + +class DERBoolean + extends DERObject +{ + byte value; + + public static final DERBoolean FALSE = new DERBoolean(false); + public static final DERBoolean TRUE = new DERBoolean(true); + + /** + * return a boolean from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERBoolean getInstance( + Object obj) + { + if (obj == null || obj instanceof DERBoolean) + { + return (DERBoolean)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERBoolean(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a DERBoolean from the passed in boolean. + */ + public static DERBoolean getInstance( + boolean value) + { + return (value ? TRUE : FALSE); + } + + /** + * return a Boolean from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERBoolean getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public DERBoolean( + byte[] value) + { + this.value = value[0]; + } + + public DERBoolean( + boolean value) + { + this.value = (value) ? (byte)0xff : (byte)0; + } + + public boolean isTrue() + { + return (value != 0); + } + + void encode( + DEROutputStream out) + throws IOException + { + byte[] bytes = new byte[1]; + + bytes[0] = value; + + out.writeEncoded(BOOLEAN, bytes); + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof DERBoolean)) + { + return false; + } + + return (value == ((DERBoolean)o).value); + } + +} + + +class DERConstructedSequence + extends ASN1Sequence +{ + public void addObject( + DEREncodable obj) + { + super.addObject(obj); + } + + public int getSize() + { + return size(); + } + + /* + * A note on the implementation: + *

+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputing SEQUENCE, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + DEROutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + Enumeration e = this.getObjects(); + + while (e.hasMoreElements()) + { + Object obj = e.nextElement(); + + dOut.writeObject(obj); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes); + } +} + + +class DERConstructedSet + extends ASN1Set +{ + public DERConstructedSet() + { + } + + /** + * @param obj - a single object that makes up the set. + */ + public DERConstructedSet( + DEREncodable obj) + { + this.addObject(obj); + } + + /** + * @param v - a vector of objects making up the set. + */ + public DERConstructedSet( + DEREncodableVector v) + { + for (int i = 0; i != v.size(); i++) + { + this.addObject(v.get(i)); + } + } + + public void addObject( + DEREncodable obj) + { + super.addObject(obj); + } + + public int getSize() + { + return size(); + } + + /* + * A note on the implementation: + *

+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputing SET, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + DEROutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + Enumeration e = this.getObjects(); + + while (e.hasMoreElements()) + { + Object obj = e.nextElement(); + + dOut.writeObject(obj); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(SET | CONSTRUCTED, bytes); + } +} + +interface DEREncodable +{ + public DERObject getDERObject(); +} + + +/** + * a general class for building up a vector of DER encodable objects + */ +class DEREncodableVector +{ + private Vector v = new Vector(); + + public void add( + DEREncodable obj) + { + v.addElement(obj); + } + + public DEREncodable get( + int i) + { + return (DEREncodable)v.elementAt(i); + } + + public int size() + { + return v.size(); + } +} + + +class DEREnumerated + extends DERObject +{ + byte[] bytes; + + /** + * return an integer from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DEREnumerated getInstance( + Object obj) + { + if (obj == null || obj instanceof DEREnumerated) + { + return (DEREnumerated)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DEREnumerated(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Enumerated from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DEREnumerated getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public DEREnumerated( + int value) + { + bytes = BigInteger.valueOf(value).toByteArray(); + } + + public DEREnumerated( + BigInteger value) + { + bytes = value.toByteArray(); + } + + public DEREnumerated( + byte[] bytes) + { + this.bytes = bytes; + } + + public BigInteger getValue() + { + return new BigInteger(bytes); + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(ENUMERATED, bytes); + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof DEREnumerated)) + { + return false; + } + + DEREnumerated other = (DEREnumerated)o; + + if (bytes.length != other.bytes.length) + { + return false; + } + + for (int i = 0; i != bytes.length; i++) + { + if (bytes[i] != other.bytes[i]) + { + return false; + } + } + + return true; + } +} + + +/** + * Generalized time object. + */ +class DERGeneralizedTime + extends DERObject +{ + String time; + + /** + * return a generalized time from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERGeneralizedTime getInstance( + Object obj) + { + if (obj == null || obj instanceof DERGeneralizedTime) + { + return (DERGeneralizedTime)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERGeneralizedTime(((ASN1OctetString)obj).getOctets()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Generalized Time object from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERGeneralizedTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * The correct format for this is YYYYMMDDHHMMSSZ, or without the Z + * for local time, or Z+-HHMM on the end, for difference between local + * time and UTC time. + *

+ * + * @param time the time string. + */ + public DERGeneralizedTime( + String time) + { + this.time = time; + } + + /** + * base constructer from a java.util.date object + */ + public DERGeneralizedTime( + Date time) + { + SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'"); + + dateF.setTimeZone(new SimpleTimeZone(0,"Z")); + + this.time = dateF.format(time); + } + + DERGeneralizedTime( + byte[] bytes) + { + // + // explicitly convert to characters + // + char[] dateC = new char[bytes.length]; + + for (int i = 0; i != dateC.length; i++) + { + dateC[i] = (char)(bytes[i] & 0xff); + } + + this.time = new String(dateC); + } + + /** + * return the time - always in the form of + * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm). + *

+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *

+     *     dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+     * 
+ * To read in the time and get a date which is compatible with our local + * time zone. + */ + public String getTime() + { + // + // standardise the format. + // + if (time.length() == 15) + { + return time.substring(0, 14) + "GMT+00:00"; + } + else if (time.length() == 17) + { + return time.substring(0, 14) + "GMT" + time.substring(15, 17) + ":" + time.substring(17, 19); + } + + return time; + } + + private byte[] getOctets() + { + char[] cs = time.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(GENERALIZED_TIME, this.getOctets()); + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof DERGeneralizedTime)) + { + return false; + } + + return time.equals(((DERGeneralizedTime)o).time); + } +} + + +/** + * DER IA5String object - this is an ascii string. + */ +class DERIA5String + extends DERObject + implements DERString +{ + String string; + + /** + * return a IA5 string from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERIA5String getInstance( + Object obj) + { + if (obj == null || obj instanceof DERIA5String) + { + return (DERIA5String)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERIA5String(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an IA5 String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERIA5String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - with bytes. + */ + public DERIA5String( + byte[] string) + { + char[] cs = new char[string.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + + this.string = new String(cs); + } + + /** + * basic constructor - with string. + */ + public DERIA5String( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(IA5_STRING, this.getOctets()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DERIA5String)) + { + return false; + } + + DERIA5String s = (DERIA5String)o; + + return this.getString().equals(s.getString()); + } +} + + + + +class DERInputStream + extends FilterInputStream implements DERTags +{ + public DERInputStream( + InputStream is) + { + super(is); + } + + protected int readLength() + throws IOException + { + int length = read(); + if (length < 0) + { + throw new IOException("EOF found when length expected"); + } + + if (length == 0x80) + { + return -1; // indefinite-length encoding + } + + if (length > 127) + { + int size = length & 0x7f; + + length = 0; + for (int i = 0; i < size; i++) + { + int next = read(); + + if (next < 0) + { + throw new IOException("EOF found reading length"); + } + + length = (length << 8) + next; + } + } + + return length; + } + + protected void readFully( + byte[] bytes) + throws IOException + { + int left = bytes.length; + + if (left == 0) + { + return; + } + + while ((left -= read(bytes, bytes.length - left, left)) != 0) + { + ; + } + } + + /** + * build an object given its tag and a byte stream to construct it + * from. + */ + protected DERObject buildObject( + int tag, + byte[] bytes) + throws IOException + { + switch (tag) + { + case NULL: + return null; + case SEQUENCE | CONSTRUCTED: + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + BERInputStream dIn = new BERInputStream(bIn); + DERConstructedSequence seq = new DERConstructedSequence(); + + try + { + for (;;) + { + DERObject obj = dIn.readObject(); + + seq.addObject(obj); + } + } + catch (EOFException ex) + { + return seq; + } + case SET | CONSTRUCTED: + bIn = new ByteArrayInputStream(bytes); + dIn = new BERInputStream(bIn); + + DEREncodableVector v = new DEREncodableVector(); + + try + { + for (;;) + { + DERObject obj = dIn.readObject(); + + v.add(obj); + } + } + catch (EOFException ex) + { + return new DERConstructedSet(v); + } + case BOOLEAN: + return new DERBoolean(bytes); + case INTEGER: + return new DERInteger(bytes); + case ENUMERATED: + return new DEREnumerated(bytes); + case OBJECT_IDENTIFIER: + return new DERObjectIdentifier(bytes); + case BIT_STRING: + int padBits = bytes[0]; + byte[] data = new byte[bytes.length - 1]; + + System.arraycopy(bytes, 1, data, 0, bytes.length - 1); + + return new DERBitString(data, padBits); + case UTF8_STRING: + return new DERUTF8String(bytes); + case PRINTABLE_STRING: + return new DERPrintableString(bytes); + case IA5_STRING: + return new DERIA5String(bytes); + case T61_STRING: + return new DERT61String(bytes); + case VISIBLE_STRING: + return new DERVisibleString(bytes); + case UNIVERSAL_STRING: + return new DERUniversalString(bytes); + case BMP_STRING: + return new DERBMPString(bytes); + case OCTET_STRING: + return new DEROctetString(bytes); + case UTC_TIME: + return new DERUTCTime(bytes); + case GENERALIZED_TIME: + return new DERGeneralizedTime(bytes); + default: + // + // with tagged object tag number is bottom 5 bits + // + if ((tag & TAGGED) != 0) + { + if ((tag & 0x1f) == 0x1f) + { + throw new IOException("unsupported high tag encountered"); + } + + if (bytes.length == 0) // empty tag! + { + return new DERTaggedObject(false, tag & 0x1f, new DERConstructedSequence()); + } + + // + // simple type - implicit... return an octet string + // + if ((tag & CONSTRUCTED) == 0) + { + return new DERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes)); + } + + bIn = new ByteArrayInputStream(bytes); + dIn = new BERInputStream(bIn); + + DEREncodable dObj = dIn.readObject(); + + // + // explicitly tagged (probably!) - if it isn't we'd have to + // tell from the context + // + if (dIn.available() == 0) + { + return new DERTaggedObject(tag & 0x1f, dObj); + } + + // + // another implicit object, we'll create a sequence... + // + seq = new DERConstructedSequence(); + + seq.addObject(dObj); + + try + { + for (;;) + { + dObj = dIn.readObject(); + + seq.addObject(dObj); + } + } + catch (EOFException ex) + { + // ignore -- + } + + return new DERTaggedObject(false, tag & 0x1f, seq); + } + + return new DERUnknownTag(tag, bytes); + } + } + + public DERObject readObject() + throws IOException + { + int tag = read(); + if (tag == -1) + { + throw new EOFException(); + } + + int length = readLength(); + byte[] bytes = new byte[length]; + + readFully(bytes); + + return buildObject(tag, bytes); + } +} + + +class DERInteger + extends DERObject +{ + byte[] bytes; + + /** + * return an integer from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERInteger getInstance( + Object obj) + { + if (obj == null || obj instanceof DERInteger) + { + return (DERInteger)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERInteger(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Integer from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERInteger getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public DERInteger( + int value) + { + bytes = BigInteger.valueOf(value).toByteArray(); + } + + public DERInteger( + BigInteger value) + { + bytes = value.toByteArray(); + } + + public DERInteger( + byte[] bytes) + { + this.bytes = bytes; + } + + public BigInteger getValue() + { + return new BigInteger(bytes); + } + + /** + * in some cases positive values get crammed into a space, + * that's not quite big enough... + */ + public BigInteger getPositiveValue() + { + return new BigInteger(1, bytes); + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(INTEGER, bytes); + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof DERInteger)) + { + return false; + } + + DERInteger other = (DERInteger)o; + + if (bytes.length != other.bytes.length) + { + return false; + } + + for (int i = 0; i != bytes.length; i++) + { + if (bytes[i] != other.bytes[i]) + { + return false; + } + } + + return true; + } +} + + +abstract class DERObject + implements DERTags, DEREncodable +{ + public DERObject getDERObject() + { + return this; + } + + abstract void encode(DEROutputStream out) + throws IOException; +} + + +class DERObjectIdentifier + extends DERObject +{ + String identifier; + + /** + * return an OID from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERObjectIdentifier getInstance( + Object obj) + { + if (obj == null || obj instanceof DERObjectIdentifier) + { + return (DERObjectIdentifier)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERObjectIdentifier(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Object Identifier from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERObjectIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + + DERObjectIdentifier( + byte[] bytes) + { + int head = bytes[0] & 0xff; + StringBuffer objId = new StringBuffer(); + int value = 0; + boolean first = true; + + for (int i = 0; i != bytes.length; i++) + { + int b = bytes[i] & 0xff; + + value = value * 128 + (b & 0x7f); + if ((b & 0x80) == 0) // end of number reached + { + if (first) + { + switch (value / 40) + { + case 0: + objId.append('0'); + break; + case 1: + objId.append('1'); + value -= 40; + break; + default: + objId.append('2'); + value -= 80; + } + first = false; + } + + objId.append('.'); + objId.append(Integer.toString(value)); + value = 0; + } + } + + this.identifier = objId.toString(); + } + + public DERObjectIdentifier( + String identifier) + { + this.identifier = identifier; + } + + public String getId() + { + return identifier; + } + + private void writeField( + OutputStream out, + int fieldValue) + throws IOException + { + if (fieldValue >= (1 << 7)) + { + if (fieldValue >= (1 << 14)) + { + if (fieldValue >= (1 << 21)) + { + if (fieldValue >= (1 << 28)) + { + out.write((fieldValue >> 28) | 0x80); + } + out.write((fieldValue >> 21) | 0x80); + } + out.write((fieldValue >> 14) | 0x80); + } + out.write((fieldValue >> 7) | 0x80); + } + out.write(fieldValue & 0x7f); + } + + void encode( + DEROutputStream out) + throws IOException + { + OIDTokenizer tok = new OIDTokenizer(identifier); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + + writeField(bOut, + Integer.parseInt(tok.nextToken()) * 40 + + Integer.parseInt(tok.nextToken())); + + while (tok.hasMoreTokens()) + { + writeField(bOut, Integer.parseInt(tok.nextToken())); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(OBJECT_IDENTIFIER, bytes); + } + + public int hashCode() + { + return identifier.hashCode(); + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof DERObjectIdentifier)) + { + return false; + } + + return identifier.equals(((DERObjectIdentifier)o).identifier); + } +} + + +class DEROctetString + extends ASN1OctetString +{ + /** + * @param string the octets making up the octet string. + */ + public DEROctetString( + byte[] string) + { + super(string); + } + + public DEROctetString( + DEREncodable obj) + { + super(obj); + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(OCTET_STRING, string); + } +} + + + +class DEROutputStream + extends FilterOutputStream implements DERTags +{ + public DEROutputStream( + OutputStream os) + { + super(os); + } + + private void writeLength( + int length) + throws IOException + { + if (length > 127) + { + int size = 1; + int val = length; + + while ((val >>>= 8) != 0) + { + size++; + } + + write((byte)(size | 0x80)); + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + write((byte)(length >> i)); + } + } + else + { + write((byte)length); + } + } + + void writeEncoded( + int tag, + byte[] bytes) + throws IOException + { + write(tag); + writeLength(bytes.length); + write(bytes); + } + + protected void writeNull() + throws IOException + { + write(NULL); + write(0x00); + } + + public void writeObject( + Object obj) + throws IOException + { + if (obj == null) + { + writeNull(); + } + else if (obj instanceof DERObject) + { + ((DERObject)obj).encode(this); + } + else if (obj instanceof DEREncodable) + { + ((DEREncodable)obj).getDERObject().encode(this); + } + else + { + throw new IOException("object not DEREncodable"); + } + } +} + + +/** + * DER PrintableString object. + */ +class DERPrintableString + extends DERObject + implements DERString +{ + String string; + + /** + * return a printable string from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERPrintableString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERPrintableString) + { + return (DERPrintableString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERPrintableString(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Printable String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERPrintableString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - byte encoded string. + */ + public DERPrintableString( + byte[] string) + { + char[] cs = new char[string.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + + this.string = new String(cs); + } + + /** + * basic constructor + */ + public DERPrintableString( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(PRINTABLE_STRING, this.getOctets()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DERPrintableString)) + { + return false; + } + + DERPrintableString s = (DERPrintableString)o; + + return this.getString().equals(s.getString()); + } +} + + +class DERSequence + extends ASN1Sequence +{ + /** + * create an empty sequence + */ + public DERSequence() + { + } + + /** + * create a sequence containing one object + */ + public DERSequence( + DEREncodable obj) + { + this.addObject(obj); + } + + /** + * create a sequence containing a vector of objects. + */ + public DERSequence( + DEREncodableVector v) + { + for (int i = 0; i != v.size(); i++) + { + this.addObject(v.get(i)); + } + } + + /* + * A note on the implementation: + *

+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputing SEQUENCE, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + DEROutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + Enumeration e = this.getObjects(); + + while (e.hasMoreElements()) + { + Object obj = e.nextElement(); + + dOut.writeObject(obj); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes); + } +} + + +/** + * A DER encoded set object + */ +class DERSet + extends ASN1Set +{ + /** + * create an empty set + */ + public DERSet() + { + } + + /** + * @param obj - a single object that makes up the set. + */ + public DERSet( + DEREncodable obj) + { + this.addObject(obj); + } + + /** + * @param v - a vector of objects making up the set. + */ + public DERSet( + DEREncodableVector v) + { + for (int i = 0; i != v.size(); i++) + { + this.addObject(v.get(i)); + } + } + + /* + * A note on the implementation: + *

+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputing SET, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + DEROutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + Enumeration e = this.getObjects(); + + while (e.hasMoreElements()) + { + Object obj = e.nextElement(); + + dOut.writeObject(obj); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(SET | CONSTRUCTED, bytes); + } +} + +/** + * basic interface for DER string objects. + */ +interface DERString +{ + public String getString(); +} + + +/** + * DER T61String (also the teletex string) + */ +class DERT61String + extends DERObject + implements DERString +{ + String string; + + /** + * return a T61 string from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERT61String getInstance( + Object obj) + { + if (obj == null || obj instanceof DERT61String) + { + return (DERT61String)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERT61String(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an T61 String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERT61String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - with bytes. + */ + public DERT61String( + byte[] string) + { + this.string = new String(string); + } + + /** + * basic constructor - with string. + */ + public DERT61String( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(T61_STRING, string.getBytes()); + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof DERT61String)) + { + return false; + } + + return this.getString().equals(((DERT61String)o).getString()); + } +} + + +/** + * DER TaggedObject - in ASN.1 nottation this is any object proceeded by + * a [n] where n is some number - these are assume to follow the construction + * rules (as with sequences). + */ +class DERTaggedObject + extends ASN1TaggedObject +{ + /** + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public DERTaggedObject( + int tagNo, + DEREncodable obj) + { + super(tagNo, obj); + } + + /** + * @param explicit true if an explicitly tagged object. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public DERTaggedObject( + boolean explicit, + int tagNo, + DEREncodable obj) + { + super(explicit, tagNo, obj); + } + + /** + * create an implicitly tagged object that contains a zero + * length sequence. + */ + public DERTaggedObject( + int tagNo) + { + super(false, tagNo, new DERSequence()); + } + + void encode( + DEROutputStream out) + throws IOException + { + if (!empty) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + + dOut.writeObject(obj); + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + if (explicit) + { + out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, bytes); + } + else + { + // + // need to mark constructed types... + // + if ((bytes[0] & CONSTRUCTED) != 0) + { + bytes[0] = (byte)(CONSTRUCTED | TAGGED | tagNo); + } + else + { + bytes[0] = (byte)(TAGGED | tagNo); + } + + out.write(bytes); + } + } + else + { + out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, new byte[0]); + } + } +} + +interface DERTags +{ + public static final int BOOLEAN = 0x01; + public static final int INTEGER = 0x02; + public static final int BIT_STRING = 0x03; + public static final int OCTET_STRING = 0x04; + public static final int NULL = 0x05; + public static final int OBJECT_IDENTIFIER = 0x06; + public static final int EXTERNAL = 0x08; + public static final int ENUMERATED = 0x0a; + public static final int SEQUENCE = 0x10; + public static final int SEQUENCE_OF = 0x10; // for completeness + public static final int SET = 0x11; + public static final int SET_OF = 0x11; // for completeness + public static final int CONSTRUCTED = 0x20; + public static final int TAGGED = 0x80; + + public static final int NUMERIC_STRING = 0x12; + public static final int PRINTABLE_STRING = 0x13; + public static final int T61_STRING = 0x14; + public static final int VIDEOTEX_STRING = 0x15; + public static final int IA5_STRING = 0x16; + public static final int UTC_TIME = 0x17; + public static final int GENERALIZED_TIME = 0x18; + public static final int GRAPHIC_STRING = 0x19; + public static final int VISIBLE_STRING = 0x1a; + public static final int GENERAL_STRING = 0x1b; + public static final int UNIVERSAL_STRING = 0x1c; + public static final int BMP_STRING = 0x1e; + public static final int UTF8_STRING = 0x0c; +} + + +/** + * DER UniversalString object. + */ +class DERUniversalString + extends DERObject + implements DERString +{ + byte[] string; + char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + /** + * return a Universal String from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERUniversalString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERUniversalString) + { + return (DERUniversalString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERUniversalString(((ASN1OctetString)obj).getOctets()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Universal String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERUniversalString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - byte encoded string. + */ + public DERUniversalString( + byte[] string) + { + this.string = string; + } + + /** + * UniversalStrings have characters which are 4 bytes long - for the + * moment we just return them in Hex... + */ + public String getString() + { + StringBuffer buf = new StringBuffer(); + + for (int i = 0; i != string.length; i++) + { + buf.append(table[(string[i] >>> 4) % 0xf]); + buf.append(table[string[i] & 0xf]); + } + + return buf.toString(); + } + + public byte[] getOctets() + { + return string; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(UNIVERSAL_STRING, this.getOctets()); + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof DERUniversalString)) + { + return false; + } + + return this.getString().equals(((DERUniversalString)o).getString()); + } +} + + +/** + * We insert one of these when we find a tag we don't recognise. + */ +class DERUnknownTag + extends DERObject +{ + int tag; + byte[] data; + + /** + * @param tag the tag value. + * @param data the octets making up the time. + */ + public DERUnknownTag( + int tag, + byte[] data) + { + this.tag = tag; + this.data = data; + } + + public int getTag() + { + return tag; + } + + public byte[] getData() + { + return data; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(tag, data); + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof DERUnknownTag)) + { + return false; + } + + DERUnknownTag other = (DERUnknownTag)o; + + if(tag != other.tag) + { + return false; + } + + if(data.length != other.data.length) + { + return false; + } + + for(int i = 0; i < data.length; i++) + { + if(data[i] != other.data[i]) + { + return false; + } + } + + return true; + } +} + + +/** + * UTC time object. + */ +class DERUTCTime + extends DERObject +{ + String time; + + /** + * return an UTC Time from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERUTCTime getInstance( + Object obj) + { + if (obj == null || obj instanceof DERUTCTime) + { + return (DERUTCTime)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERUTCTime(((ASN1OctetString)obj).getOctets()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an UTC Time from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERUTCTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were + * never encoded. When you're creating one of these objects from scratch, that's + * what you want to use, otherwise we'll try to deal with whatever gets read from + * the input stream... (this is why the input format is different from the getTime() + * method output). + *

+ * + * @param time the time string. + */ + public DERUTCTime( + String time) + { + this.time = time; + } + + /** + * base constructer from a java.util.date object + */ + public DERUTCTime( + Date time) + { + SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'"); + + dateF.setTimeZone(new SimpleTimeZone(0,"Z")); + + this.time = dateF.format(time); + } + + DERUTCTime( + byte[] bytes) + { + // + // explicitly convert to characters + // + char[] dateC = new char[bytes.length]; + + for (int i = 0; i != dateC.length; i++) + { + dateC[i] = (char)(bytes[i] & 0xff); + } + + this.time = new String(dateC); + } + + /** + * return the time - always in the form of + * YYMMDDhhmmssGMT(+hh:mm|-hh:mm). + *

+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *

+     *     dateF = new SimpleDateFormat("yyMMddHHmmssz");
+     * 
+ * To read in the time and get a date which is compatible with our local + * time zone. + *

+ * Note: In some cases, due to the local date processing, this + * may lead to unexpected results. If you want to stick the normal + * convention of 1950 to 2049 use the getAdjustedTime() method. + */ + public String getTime() + { + // + // standardise the format. + // + if (time.length() == 11) + { + return time.substring(0, 10) + "00GMT+00:00"; + } + else if (time.length() == 13) + { + return time.substring(0, 12) + "GMT+00:00"; + } + else if (time.length() == 17) + { + return time.substring(0, 12) + "GMT" + time.substring(12, 15) + ":" + time.substring(15, 17); + } + + return time; + } + + /** + * return the time as an adjusted date with a 4 digit year. This goes + * in the range of 1950 - 2049. + */ + public String getAdjustedTime() + { + String d = this.getTime(); + + if (d.charAt(0) < '5') + { + return "20" + d; + } + else + { + return "19" + d; + } + } + + private byte[] getOctets() + { + char[] cs = time.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(UTC_TIME, this.getOctets()); + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof DERUTCTime)) + { + return false; + } + + return time.equals(((DERUTCTime)o).time); + } +} + + +/** + * DER UTF8String object. + */ +class DERUTF8String + extends DERObject + implements DERString +{ + String string; + + /** + * return an UTF8 string from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERUTF8String getInstance( + Object obj) + { + if (obj == null || obj instanceof DERUTF8String) + { + return (DERUTF8String)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERUTF8String(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an UTF8 String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERUTF8String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - byte encoded string. + */ + DERUTF8String( + byte[] string) + { + int i = 0; + int length = 0; + + while (i < string.length) + { + length++; + if ((string[i] & 0xe0) == 0xe0) + { + i += 3; + } + else if ((string[i] & 0xc0) == 0xc0) + { + i += 2; + } + else + { + i += 1; + } + } + + char[] cs = new char[length]; + + i = 0; + length = 0; + + while (i < string.length) + { + char ch; + + if ((string[i] & 0xe0) == 0xe0) + { + ch = (char)(((string[i] & 0x1f) << 12) + | ((string[i + 1] & 0x3f) << 6) | (string[i + 2] & 0x3f)); + i += 3; + } + else if ((string[i] & 0xc0) == 0xc0) + { + ch = (char)(((string[i] & 0x3f) << 6) | (string[i + 1] & 0x3f)); + i += 2; + } + else + { + ch = (char)(string[i] & 0xff); + i += 1; + } + + cs[length++] = ch; + } + + this.string = new String(cs); + } + + /** + * basic constructor + */ + public DERUTF8String( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DERUTF8String)) + { + return false; + } + + DERUTF8String s = (DERUTF8String)o; + + return this.getString().equals(s.getString()); + } + + void encode( + DEROutputStream out) + throws IOException + { + char[] c = string.toCharArray(); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + for (int i = 0; i != c.length; i++) + { + char ch = c[i]; + + if (ch < 0x0080) + { + bOut.write(ch); + } + else if (ch < 0x0800) + { + bOut.write(0xc0 | (ch >> 6)); + bOut.write(0x80 | (ch & 0x3f)); + } + else + { + bOut.write(0xe0 | (ch >> 12)); + bOut.write(0x80 | ((ch >> 6) & 0x3F)); + bOut.write(0x80 | (ch & 0x3F)); + } + } + + out.writeEncoded(UTF8_STRING, bOut.toByteArray()); + } +} + + +/** + * DER VisibleString object. + */ +class DERVisibleString + extends DERObject + implements DERString +{ + String string; + + /** + * return a Visible String from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERVisibleString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERVisibleString) + { + return (DERVisibleString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERVisibleString(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Visible String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERVisibleString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - byte encoded string. + */ + public DERVisibleString( + byte[] string) + { + char[] cs = new char[string.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + + this.string = new String(cs); + } + + /** + * basic constructor + */ + public DERVisibleString( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(VISIBLE_STRING, this.getOctets()); + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof DERVisibleString)) + { + return false; + } + + return this.getString().equals(((DERVisibleString)o).getString()); + } +} + +/** + * class for breaking up an OID into it's component tokens, ala + * java.util.StringTokenizer. We need this class as some of the + * lightweight Java environment don't support classes like + * StringTokenizer. + */ +class OIDTokenizer +{ + private String oid; + private int index; + + public OIDTokenizer( + String oid) + { + this.oid = oid; + this.index = 0; + } + + public boolean hasMoreTokens() + { + return (index != -1); + } + + public String nextToken() + { + if (index == -1) + { + return null; + } + + String token; + int end = oid.indexOf('.', index); + + if (end == -1) + { + token = oid.substring(index); + index = -1; + return token; + } + + token = oid.substring(index, end); + + index = end + 1; + return token; + } +} + + +interface PKCSObjectIdentifiers +{ + // + // pkcs-1 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } + // + static final String pkcs_1 = "1.2.840.113549.1.1"; + static final DERObjectIdentifier rsaEncryption = new DERObjectIdentifier(pkcs_1 + ".1"); + static final DERObjectIdentifier md2WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".2"); + static final DERObjectIdentifier md4WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".3"); + static final DERObjectIdentifier md5WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".4"); + static final DERObjectIdentifier sha1WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".5"); + static final DERObjectIdentifier srsaOAEPEncryptionSET = new DERObjectIdentifier(pkcs_1 + ".6"); + static final DERObjectIdentifier sha256WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".11"); + static final DERObjectIdentifier sha384WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".12"); + static final DERObjectIdentifier sha512WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".13"); + + // + // pkcs-3 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 } + // + static final String pkcs_3 = "1.2.840.113549.1.3"; + static final DERObjectIdentifier dhKeyAgreement = new DERObjectIdentifier(pkcs_3 + ".1"); + + // + // pkcs-5 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 } + // + static final String pkcs_5 = "1.2.840.113549.1.5"; + + static final DERObjectIdentifier id_PBES2 = new DERObjectIdentifier(pkcs_5 + ".13"); + + static final DERObjectIdentifier id_PBKDF2 = new DERObjectIdentifier(pkcs_5 + ".12"); + + // + // encryptionAlgorithm OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) 3 } + // + static final String encryptionAlgorithm = "1.2.840.113549.3"; + + static final DERObjectIdentifier des_EDE3_CBC = new DERObjectIdentifier(encryptionAlgorithm + ".7"); + static final DERObjectIdentifier RC2_CBC = new DERObjectIdentifier(encryptionAlgorithm + ".2"); + + // + // object identifiers for digests + // + + // + // md2 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2} + // + static final DERObjectIdentifier md2 = new DERObjectIdentifier("1.2.840.113549.2.2"); + + // + // md5 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5} + // + static final DERObjectIdentifier md5 = new DERObjectIdentifier("1.2.840.113549.2.5"); + + // + // pkcs-7 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 } + // + static final String pkcs_7 = "1.2.840.113549.1.7"; + static final DERObjectIdentifier data = new DERObjectIdentifier(pkcs_7 + ".1"); + static final DERObjectIdentifier signedData = new DERObjectIdentifier(pkcs_7 + ".2"); + static final DERObjectIdentifier envelopedData = new DERObjectIdentifier(pkcs_7 + ".3"); + static final DERObjectIdentifier signedAndEnvelopedData = new DERObjectIdentifier(pkcs_7 + ".4"); + static final DERObjectIdentifier digestedData = new DERObjectIdentifier(pkcs_7 + ".5"); + static final DERObjectIdentifier encryptedData = new DERObjectIdentifier(pkcs_7 + ".6"); + + // + // pkcs-9 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } + // + static final String pkcs_9 = "1.2.840.113549.1.9"; + + static final DERObjectIdentifier pkcs_9_at_emailAddress = new DERObjectIdentifier(pkcs_9 + ".1"); + static final DERObjectIdentifier pkcs_9_at_unstructuredName = new DERObjectIdentifier(pkcs_9 + ".2"); + static final DERObjectIdentifier pkcs_9_at_contentType = new DERObjectIdentifier(pkcs_9 + ".3"); + static final DERObjectIdentifier pkcs_9_at_messageDigest = new DERObjectIdentifier(pkcs_9 + ".4"); + static final DERObjectIdentifier pkcs_9_at_signingTime = new DERObjectIdentifier(pkcs_9 + ".5"); + static final DERObjectIdentifier pkcs_9_at_counterSignature = new DERObjectIdentifier(pkcs_9 + ".6"); + static final DERObjectIdentifier pkcs_9_at_challengePassword = new DERObjectIdentifier(pkcs_9 + ".7"); + static final DERObjectIdentifier pkcs_9_at_unstructuredAddress = new DERObjectIdentifier(pkcs_9 + ".8"); + static final DERObjectIdentifier pkcs_9_at_extendedCertificateAttributes = new DERObjectIdentifier(pkcs_9 + ".9"); + + static final DERObjectIdentifier pkcs_9_at_signingDescription = new DERObjectIdentifier(pkcs_9 + ".13"); + static final DERObjectIdentifier pkcs_9_at_extensionRequest = new DERObjectIdentifier(pkcs_9 + ".14"); + static final DERObjectIdentifier pkcs_9_at_smimeCapabilities = new DERObjectIdentifier(pkcs_9 + ".15"); + + static final DERObjectIdentifier pkcs_9_at_friendlyName = new DERObjectIdentifier(pkcs_9 + ".20"); + static final DERObjectIdentifier pkcs_9_at_localKeyId = new DERObjectIdentifier(pkcs_9 + ".21"); + + static final DERObjectIdentifier x509certType = new DERObjectIdentifier(pkcs_9 + ".22.1"); + + // + // SMIME capability sub oids. + // + static final DERObjectIdentifier preferSignedData = new DERObjectIdentifier(pkcs_9 + ".15.1"); + static final DERObjectIdentifier canNotDecryptAny = new DERObjectIdentifier(pkcs_9 + ".15.2"); + static final DERObjectIdentifier sMIMECapabilitiesVersions = new DERObjectIdentifier(pkcs_9 + ".15.3"); + + // + // other SMIME attributes + // + + // + // id-aa OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)} + // + static String id_aa = "1.2.840.113549.1.9.16.2"; + + /* + * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11} + * + */ + static DERObjectIdentifier id_aa_encrypKeyPref = new DERObjectIdentifier(id_aa + ".11"); + + // + // pkcs-12 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 } + // + static final String pkcs_12 = "1.2.840.113549.1.12"; + static final String bagtypes = pkcs_12 + ".10.1"; + + static final DERObjectIdentifier keyBag = new DERObjectIdentifier(bagtypes + ".1"); + static final DERObjectIdentifier pkcs8ShroudedKeyBag = new DERObjectIdentifier(bagtypes + ".2"); + static final DERObjectIdentifier certBag = new DERObjectIdentifier(bagtypes + ".3"); + static final DERObjectIdentifier crlBag = new DERObjectIdentifier(bagtypes + ".4"); + static final DERObjectIdentifier secretBag = new DERObjectIdentifier(bagtypes + ".5"); + static final DERObjectIdentifier safeContentsBag = new DERObjectIdentifier(bagtypes + ".6"); +} + + + + +class AlgorithmIdentifier + implements DEREncodable +{ + private DERObjectIdentifier objectId; + private DEREncodable parameters; + private boolean parametersDefined = false; + + public static AlgorithmIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static AlgorithmIdentifier getInstance( + Object obj) + { + if (obj instanceof AlgorithmIdentifier) + { + return (AlgorithmIdentifier)obj; + } + + if (obj instanceof DERObjectIdentifier) + { + return new AlgorithmIdentifier((DERObjectIdentifier)obj); + } + + if (obj instanceof String) + { + return new AlgorithmIdentifier((String)obj); + } + + if (obj instanceof ASN1Sequence) + { + return new AlgorithmIdentifier((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public AlgorithmIdentifier( + DERObjectIdentifier objectId) + { + this.objectId = objectId; + } + + public AlgorithmIdentifier( + String objectId) + { + this.objectId = new DERObjectIdentifier(objectId); + } + + public AlgorithmIdentifier( + DERObjectIdentifier objectId, + DEREncodable parameters) + { + parametersDefined = true; + this.objectId = objectId; + this.parameters = parameters; + } + + public AlgorithmIdentifier( + ASN1Sequence seq) + { + objectId = (DERObjectIdentifier)seq.getObjectAt(0); + + if (seq.size() == 2) + { + parametersDefined = true; + parameters = seq.getObjectAt(1); + } + else + { + parameters = null; + } + } + + public DERObjectIdentifier getObjectId() + { + return objectId; + } + + public DEREncodable getParameters() + { + return parameters; + } + + /** + *

+     *      AlgorithmIdentifier ::= SEQUENCE {
+     *                            algorithm OBJECT IDENTIFIER,
+     *                            parameters ANY DEFINED BY algorithm OPTIONAL }
+     * 
+ */ + public DERObject getDERObject() + { + DERConstructedSequence seq = new DERConstructedSequence(); + + seq.addObject(objectId); + + if (parametersDefined) + { + seq.addObject(parameters); + } + + return seq; + } + + public boolean equals( + Object o) + { + if ((o == null) || !(o instanceof AlgorithmIdentifier)) + { + return false; + } + + AlgorithmIdentifier other = (AlgorithmIdentifier)o; + + if (!this.getObjectId().equals(other.getObjectId())) + { + return false; + } + + if (this.getParameters() == null && other.getParameters() == null) + { + return true; + } + + if (this.getParameters() == null || other.getParameters() == null) + { + return false; + } + + ByteArrayOutputStream b1Out = new ByteArrayOutputStream(); + ByteArrayOutputStream b2Out = new ByteArrayOutputStream(); + DEROutputStream d1Out = new DEROutputStream(b1Out); + DEROutputStream d2Out = new DEROutputStream(b2Out); + + try + { + d1Out.writeObject(this.getParameters()); + d2Out.writeObject(other.getParameters()); + + byte[] b1 = b1Out.toByteArray(); + byte[] b2 = b2Out.toByteArray(); + + if (b1.length != b2.length) + { + return false; + } + + for (int i = 0; i != b1.length; i++) + { + if (b1[i] != b2[i]) + { + return false; + } + } + } + catch (Exception e) + { + return false; + } + + return true; + } +} + + + + +/** + *
+ * id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::=  { id-ce 35 }
+ *
+ *   AuthorityKeyIdentifier ::= SEQUENCE {
+ *      keyIdentifier             [0] IMPLICIT KeyIdentifier           OPTIONAL,
+ *      authorityCertIssuer       [1] IMPLICIT GeneralNames            OPTIONAL,
+ *      authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL  }
+ *
+ *   KeyIdentifier ::= OCTET STRING
+ * 
+ * + */ +class AuthorityKeyIdentifier + implements DEREncodable, DERTags +{ + ASN1OctetString keyidentifier=null; + GeneralNames certissuer=null; + DERInteger certserno=null; + + public static AuthorityKeyIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static AuthorityKeyIdentifier getInstance( + Object obj) + { + if (obj instanceof AuthorityKeyIdentifier) + { + return (AuthorityKeyIdentifier)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new AuthorityKeyIdentifier((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public AuthorityKeyIdentifier( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + DERTaggedObject o = (DERTaggedObject)e.nextElement(); + + switch (o.getTagNo()) + { + case 0: + this.keyidentifier = ASN1OctetString.getInstance(o, false); + break; + case 1: + this.certissuer = GeneralNames.getInstance(o, false); + break; + case 2: + this.certserno = DERInteger.getInstance(o, false); + break; + default: + throw new IllegalArgumentException("illegal tag"); + } + } + } + + /** + * + * Calulates the keyidentifier using a SHA1 hash over the BIT STRING + * from SubjectPublicKeyInfo as defined in RFC2459. + * + * Example of making a AuthorityKeyIdentifier: + *
+     *   SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((DERConstructedSequence)new DERInputStream(
+     *       new ByteArrayInputStream(publicKey.getEncoded())).readObject());
+     *   AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki);
+     * 
+ * + **/ + public AuthorityKeyIdentifier( + SubjectPublicKeyInfo spki) + { + Digest digest = new SHA1Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + byte[] bytes = spki.getPublicKeyData().getBytes(); + digest.update(bytes, 0, bytes.length); + digest.doFinal(resBuf, 0); + this.keyidentifier = new DEROctetString(resBuf); + } + + /** + * create an AuthorityKeyIdentifier with the GeneralNames tag and + * the serial number provided as well. + */ + public AuthorityKeyIdentifier( + SubjectPublicKeyInfo spki, + GeneralNames name, + BigInteger serialNumber) + { + Digest digest = new SHA1Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + byte[] bytes = spki.getPublicKeyData().getBytes(); + digest.update(bytes, 0, bytes.length); + digest.doFinal(resBuf, 0); + + this.keyidentifier = new DEROctetString(resBuf); + this.certissuer = name; + this.certserno = new DERInteger(serialNumber); + } + + public byte[] getKeyIdentifier() + { + if (keyidentifier != null) + { + return keyidentifier.getOctets(); + } + + return null; + } + + /** + *
+     *   AuthorityKeyIdentifier ::= SEQUENCE {
+     *      keyIdentifier             [0] IMPLICIT KeyIdentifier           OPTIONAL,
+     *      authorityCertIssuer       [1] IMPLICIT GeneralNames            OPTIONAL,
+     *      authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL  }
+     *
+     *   KeyIdentifier ::= OCTET STRING
+     * 
+ */ + public DERObject getDERObject() + { + DERConstructedSequence seq = new DERConstructedSequence(); + + if (keyidentifier != null) + { + seq.addObject(new DERTaggedObject(false, 0, keyidentifier)); + } + + if (certissuer != null) + { + seq.addObject(new DERTaggedObject(false, 1, certissuer)); + } + + if (certserno != null) + { + seq.addObject(new DERTaggedObject(false, 2, certserno)); + } + + + return seq; + } + + public String toString() + { + return ("AuthorityKeyIdentifier: KeyID(" + this.keyidentifier.getOctets() + ")"); + } +} + + + +class BasicConstraints + implements DEREncodable +{ + DERBoolean cA = new DERBoolean(false); + DERInteger pathLenConstraint = null; + + public static BasicConstraints getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static BasicConstraints getInstance( + Object obj) + { + if (obj instanceof BasicConstraints) + { + return (BasicConstraints)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new BasicConstraints((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public BasicConstraints( + ASN1Sequence seq) + { + if (seq.size() != 0) + { + this.cA = (DERBoolean)seq.getObjectAt(0); + this.pathLenConstraint = (DERInteger)seq.getObjectAt(1); + } + } + + public BasicConstraints( + boolean cA, + int pathLenConstraint) + { + this.cA = new DERBoolean(cA); + this.pathLenConstraint = new DERInteger(pathLenConstraint); + } + + public BasicConstraints( + boolean cA) + { + this.cA = new DERBoolean(cA); + this.pathLenConstraint = null; + } + + public boolean isCA() + { + return cA.isTrue(); + } + + public BigInteger getPathLenConstraint() + { + if (pathLenConstraint != null) + { + return pathLenConstraint.getValue(); + } + + return null; + } + + /** + *
+     * BasicConstraints := SEQUENCE {
+     *    cA                  BOOLEAN DEFAULT FALSE,
+     *    pathLenConstraint   INTEGER (0..MAX) OPTIONAL
+     * }
+     * 
+ */ + public DERObject getDERObject() + { + DERConstructedSequence seq = new DERConstructedSequence(); + + seq.addObject(cA); + + if (pathLenConstraint != null) + { + seq.addObject(pathLenConstraint); + } + + return seq; + } + + public String toString() + { + return "BasicConstraints: isCa(" + this.isCA() + "), pathLenConstraint = " + pathLenConstraint.getValue(); + } +} + + + +/** + * PKIX RFC-2459 + * + * The X.509 v2 CRL syntax is as follows. For signature calculation, + * the data that is to be signed is ASN.1 DER encoded. + * + *
+ * CertificateList  ::=  SEQUENCE  {
+ *      tbsCertList          TBSCertList,
+ *      signatureAlgorithm   AlgorithmIdentifier,
+ *      signatureValue       BIT STRING  }
+ * 
+ */ +class CertificateList + implements DEREncodable +{ + TBSCertList tbsCertList; + AlgorithmIdentifier sigAlgId; + DERBitString sig; + + public static CertificateList getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static CertificateList getInstance( + Object obj) + { + if (obj instanceof CertificateList) + { + return (CertificateList)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new CertificateList((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public CertificateList( + ASN1Sequence seq) + { + tbsCertList = TBSCertList.getInstance(seq.getObjectAt(0)); + sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + sig = (DERBitString)seq.getObjectAt(2); + } + + public TBSCertList getTBSCertList() + { + return tbsCertList; + } + + public TBSCertList.CRLEntry[] getRevokedCertificates() + { + return tbsCertList.getRevokedCertificates(); + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return sigAlgId; + } + + public DERBitString getSignature() + { + return sig; + } + + public int getVersion() + { + return tbsCertList.getVersion(); + } + + public X509Name getIssuer() + { + return tbsCertList.getIssuer(); + } + + public Time getThisUpdate() + { + return tbsCertList.getThisUpdate(); + } + + public Time getNextUpdate() + { + return tbsCertList.getNextUpdate(); + } + + public DERObject getDERObject() + { + DERConstructedSequence seq = new DERConstructedSequence(); + seq.addObject(tbsCertList); + seq.addObject(sigAlgId); + seq.addObject(sig); + return seq; + } +} + + +class CRLDistPoint + implements DEREncodable +{ + ASN1Sequence seq = null; + + public static CRLDistPoint getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static CRLDistPoint getInstance( + Object obj) + { + if (obj instanceof CRLDistPoint) + { + return (CRLDistPoint)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new CRLDistPoint((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public CRLDistPoint( + ASN1Sequence seq) + { + this.seq = seq; + } + + public CRLDistPoint( + DistributionPoint[] points) + { + DEREncodableVector v = new DEREncodableVector(); + + for (int i = 0; i != points.length; i++) + { + v.add(points[i]); + } + + seq = new DERSequence(v); + } + + /** + *
+     * CRLDistPoint ::= SEQUENCE SIZE {1..MAX} OF DistributionPoint
+     * 
+ */ + public DERObject getDERObject() + { + return seq; + } +} + + + +/** + *
+ * CRLNumber::= INTEGER(0..MAX)
+ * 
+ */ +class CRLNumber + extends DERInteger +{ + + public CRLNumber( + BigInteger number) + { + super(number); + } + + public BigInteger getCRLNumber() + { + return getPositiveValue(); + } +} + + +class CRLReason + extends DEREnumerated +{ + public static final int UNSPECIFIED = 0; + public static final int KEY_COMPROMISE = 1; + public static final int CA_COMPROMISE = 2; + public static final int AFFILIATION_CHANGED = 3; + public static final int SUPERSEDED = 4; + public static final int CESSATION_OF_OPERATION = 5; + public static final int CERTIFICATE_HOLD = 6; + public static final int REMOVE_FROM_CRL = 8; + public static final int PRIVILEGE_WITHDRAWN = 9; + public static final int AA_COMPROMISE = 10; + + /** + *
+     * CRLReason ::= ENUMERATED {
+     *  unspecified             (0),
+     *  keyCompromise           (1),
+     *  cACompromise            (2),
+     *  affiliationChanged      (3),
+     *  superseded              (4),
+     *  cessationOfOperation    (5),
+     *  certificateHold         (6),
+     *  removeFromCRL           (8),
+     *  privilegeWithdrawn      (9),
+     *  aACompromise           (10)
+     * }
+     * 
+ */ + public CRLReason( + int reason) + { + super(reason); + } +} + + + +/** + *
+ * DigestInfo::=SEQUENCE{
+ *          digestAlgorithm  AlgorithmIdentifier,
+ *          digest OCTET STRING }
+ * 
+ */ +class DigestInfo + implements DEREncodable +{ + private byte[] digest; + private AlgorithmIdentifier algId; + + public static DigestInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static DigestInfo getInstance( + Object obj) + { + if (obj instanceof DigestInfo) + { + return (DigestInfo)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new DigestInfo((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public DigestInfo( + AlgorithmIdentifier algId, + byte[] digest) + { + this.digest = digest; + this.algId = algId; + } + + public DigestInfo( + ASN1Sequence obj) + { + Enumeration e = obj.getObjects(); + + algId = AlgorithmIdentifier.getInstance(e.nextElement()); + digest = ((ASN1OctetString)e.nextElement()).getOctets(); + } + + public AlgorithmIdentifier getAlgorithmId() + { + return algId; + } + + public byte[] getDigest() + { + return digest; + } + + public DERObject getDERObject() + { + DEREncodableVector v = new DEREncodableVector(); + + v.add(algId); + v.add(new DEROctetString(digest)); + + return new DERSequence(v); + } +} + + +class DistributionPoint + implements DEREncodable +{ + ASN1Sequence seq = null; + + public static DistributionPoint getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static DistributionPoint getInstance( + Object obj) + { + if(obj == null || obj instanceof DistributionPoint) + { + return (DistributionPoint)obj; + } + + if(obj instanceof ASN1Sequence) + { + return new DistributionPoint((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid DistributionPoint: " + obj.getClass().getName()); + } + + public DistributionPoint( + ASN1Sequence seq) + { + this.seq = seq; + } + + public DistributionPoint( + DistributionPointName distributionPoint, + ReasonFlags reasons, + GeneralNames cRLIssuer) + { + DEREncodableVector v = new DEREncodableVector(); + + if (distributionPoint != null) + { + v.add(new DERTaggedObject(0, distributionPoint)); + } + + if (reasons != null) + { + v.add(new DERTaggedObject(1, reasons)); + } + + if (cRLIssuer != null) + { + v.add(new DERTaggedObject(2, cRLIssuer)); + } + + seq = new DERSequence(v); + } + + /** + *
+     * DistributionPoint ::= SEQUENCE {
+     *      distributionPoint [0] DistributionPointName OPTIONAL,
+     *      reasons           [1] ReasonFlags OPTIONAL,
+     *      cRLIssuer         [2] GeneralNames OPTIONAL
+     * }
+     * 
+ */ + public DERObject getDERObject() + { + return seq; + } +} + + +class DistributionPointName + implements DEREncodable +{ + DEREncodable name; + int type; + + public static final int FULL_NAME = 0; + public static final int NAME_RELATIVE_TO_CRL_ISSUER = 1; + + public DistributionPointName( + int type, + DEREncodable name) + { + this.type = type; + this.name = name; + } + + /** + *
+     * DistributionPointName ::= CHOICE {
+     *     fullName                 [0] GeneralNames,
+     *     nameRelativeToCRLIssuer  [1] RelativeDistinguishedName
+     * }
+     * 
+ */ + public DERObject getDERObject() + { + return new DERTaggedObject(false, type, name); + } +} + + + +class DSAParameter + implements DEREncodable +{ + DERInteger p, q, g; + + public static DSAParameter getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static DSAParameter getInstance( + Object obj) + { + if(obj == null || obj instanceof DSAParameter) + { + return (DSAParameter)obj; + } + + if(obj instanceof ASN1Sequence) + { + return new DSAParameter((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid DSAParameter: " + obj.getClass().getName()); + } + + public DSAParameter( + BigInteger p, + BigInteger q, + BigInteger g) + { + this.p = new DERInteger(p); + this.q = new DERInteger(q); + this.g = new DERInteger(g); + } + + public DSAParameter( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + p = (DERInteger)e.nextElement(); + q = (DERInteger)e.nextElement(); + g = (DERInteger)e.nextElement(); + } + + public BigInteger getP() + { + return p.getPositiveValue(); + } + + public BigInteger getQ() + { + return q.getPositiveValue(); + } + + public BigInteger getG() + { + return g.getPositiveValue(); + } + + public DERObject getDERObject() + { + DEREncodableVector v = new DEREncodableVector(); + + v.add(p); + v.add(q); + v.add(g); + + return new DERSequence(v); + } +} + + +/** + *
+ * 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 }
+ * 
+ */ +class GeneralName + implements DEREncodable +{ + DEREncodable obj; + int tag; + boolean isInsideImplicit = false; // if we are in an implicitly tagged object + + public GeneralName( + X509Name directoryName) + { + this.obj = directoryName; + this.tag = 4; + } + + /** + * When the subjectAltName extension contains an Internet mail address, + * the address MUST be included as an rfc822Name. The format of an + * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822]. + * + * When the subjectAltName extension contains a domain name service + * label, the domain name MUST be stored in the dNSName (an IA5String). + * The name MUST be in the "preferred name syntax," as specified by RFC + * 1034 [RFC 1034]. + * + * When the subjectAltName extension contains a URI, the name MUST be + * stored in the uniformResourceIdentifier (an IA5String). The name MUST + * be a non-relative URL, and MUST follow the URL syntax and encoding + * rules specified in [RFC 1738]. The name must include both a scheme + * (e.g., "http" or "ftp") and a scheme-specific-part. The scheme- + * specific-part must include a fully qualified domain name or IP + * address as the host. + * + * When the subjectAltName extension contains a iPAddress, the address + * MUST be stored in the octet string in "network byte order," as + * specified in RFC 791 [RFC 791]. The least significant bit (LSB) of + * each octet is the LSB of the corresponding byte in the network + * address. For IP Version 4, as specified in RFC 791, the octet string + * MUST contain exactly four octets. For IP Version 6, as specified in + * RFC 1883, the octet string MUST contain exactly sixteen octets [RFC + * 1883]. + */ + public GeneralName( + DERObject name, int tag) + { + this.obj = name; + this.tag = tag; + } + + /** + * mark whether or not we are contained inside an implicitly tagged + * object. + * @deprecated + */ + public void markInsideImplicit( + boolean isInsideImplicit) + { + this.isInsideImplicit = isInsideImplicit; + } + + public DERObject getDERObject() + { + if (obj.getDERObject() instanceof ASN1Sequence) + { + return new DERTaggedObject(true, tag, obj); + } + else + { + return new DERTaggedObject(false, tag, obj); + } + } +} + + + +class GeneralNames + implements DEREncodable +{ + ASN1Sequence seq; + boolean isInsideImplicit = false; + + public static GeneralNames getInstance( + Object obj) + { + if (obj == null || obj instanceof GeneralNames) + { + return (GeneralNames)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new GeneralNames((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + public static GeneralNames getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public GeneralNames( + ASN1Sequence seq) + { + this.seq = seq; + } + + /* + * this is a hack! But it will have to do until the ambiguity rules + * get sorted out for implicit/explicit tagging... + * @deprecated + */ + public void markInsideImplicit( + boolean isInsideImplicit) + { + this.isInsideImplicit = isInsideImplicit; + } + + /** + *
+     * GeneralNames ::= SEQUENCE SIZE {1..MAX} OF GeneralName
+     * 
+ */ + public DERObject getDERObject() + { + return seq; + } +} + + +/** + *
+ *    id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
+ *
+ *    KeyUsage ::= BIT STRING {
+ *         digitalSignature        (0),
+ *         nonRepudiation          (1),
+ *         keyEncipherment         (2),
+ *         dataEncipherment        (3),
+ *         keyAgreement            (4),
+ *         keyCertSign             (5),
+ *         cRLSign                 (6),
+ *         encipherOnly            (7),
+ *         decipherOnly            (8) }
+ * 
+ */ +class KeyUsage + extends DERBitString +{ + public static final int digitalSignature = (1 << 7); + public static final int nonRepudiation = (1 << 6); + public static final int keyEncipherment = (1 << 5); + public static final int dataEncipherment = (1 << 4); + public static final int keyAgreement = (1 << 3); + public static final int keyCertSign = (1 << 2); + public static final int cRLSign = (1 << 1); + public static final int encipherOnly = (1 << 0); + public static final int decipherOnly = (1 << 15); + + /** + * Basic constructor. + * + * @param usage - the bitwise OR of the Key Usage flags giving the + * allowed uses for the key. + * e.g. (X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment) + */ + public KeyUsage( + int usage) + { + super(getBytes(usage), getPadBits(usage)); + } + + public KeyUsage( + DERBitString usage) + { + super(usage.getBytes(), usage.getPadBits()); + } + + public String toString() + { + return "KeyUsage: 0x" + Integer.toHexString(data[0] & 0xff); + } +} + + +class ReasonFlags + extends DERBitString +{ + public static final int UNUSED = (1 << 7); + public static final int KEY_COMPROMISE = (1 << 6); + public static final int CA_COMPROMISE = (1 << 5); + public static final int AFFILIATION_CHANGED = (1 << 4); + public static final int SUPERSEDED = (1 << 3); + public static final int CESSATION_OF_OPERATION = (1 << 2); + public static final int CERTIFICATE_HOLD = (1 << 1); + public static final int PRIVILEGE_WITHDRAWN = (1 << 0); + public static final int AA_COMPROMISE = (1 << 15); + + /** + *
+     * ReasonFlags ::= BIT STRING {
+     *    unused(0),
+     *    keyCompromise(1),
+     *    cACompromise(2),
+     *    affiliationChanged(3),
+     *    superseded(4),
+     *    cessationOfOperation(5),
+     *    certficateHold(6)
+     * }
+     * 
+ * @param reasons - the bitwise OR of the Key Reason flags giving the + * allowed uses for the key. + */ + public ReasonFlags( + int reasons) + { + super(getBytes(reasons), getPadBits(reasons)); + } + + public ReasonFlags( + DERBitString reasons) + { + super(reasons.getBytes(), reasons.getPadBits()); + } +} + + + +class RSAPublicKeyStructure + implements DEREncodable +{ + private BigInteger modulus; + private BigInteger publicExponent; + + public static RSAPublicKeyStructure getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static RSAPublicKeyStructure getInstance( + Object obj) + { + if(obj == null || obj instanceof RSAPublicKeyStructure) + { + return (RSAPublicKeyStructure)obj; + } + + if(obj instanceof ASN1Sequence) + { + return new RSAPublicKeyStructure((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("Invalid RSAPublicKeyStructure: " + obj.getClass().getName()); + } + + public RSAPublicKeyStructure( + BigInteger modulus, + BigInteger publicExponent) + { + this.modulus = modulus; + this.publicExponent = publicExponent; + } + + public RSAPublicKeyStructure( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + modulus = ((DERInteger)e.nextElement()).getValue(); + publicExponent = ((DERInteger)e.nextElement()).getValue(); + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getPublicExponent() + { + return publicExponent; + } + + /** + * This outputs the key in PKCS1v2 format. + *
+     *      RSAPublicKey ::= SEQUENCE {
+     *                          modulus INTEGER, -- n
+     *                          publicExponent INTEGER, -- e
+     *                      }
+     * 
+ *

+ */ + public DERObject getDERObject() + { + DERConstructedSequence seq = new DERConstructedSequence(); + + seq.addObject(new DERInteger(getModulus())); + seq.addObject(new DERInteger(getPublicExponent())); + + return seq; + } +} + + +/** + *

+ * SubjectKeyIdentifier::= OCTET STRING
+ * 
+ */ +class SubjectKeyIdentifier + implements DEREncodable +{ + private byte[] keyidentifier; + + public static SubjectKeyIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1OctetString.getInstance(obj, explicit)); + } + + public static SubjectKeyIdentifier getInstance( + Object obj) + { + if(obj == null || obj instanceof SubjectKeyIdentifier) + { + return (SubjectKeyIdentifier)obj; + } + + if(obj instanceof SubjectPublicKeyInfo) + { + return new SubjectKeyIdentifier((SubjectPublicKeyInfo)obj); + } + + if(obj instanceof ASN1OctetString) + { + return new SubjectKeyIdentifier((ASN1OctetString)obj); + } + + throw new IllegalArgumentException("Invalid SubjectKeyIdentifier: " + obj.getClass().getName()); + } + + public SubjectKeyIdentifier( + byte[] keyid) + { + this.keyidentifier=keyid; + } + + public SubjectKeyIdentifier( + ASN1OctetString keyid) + { + this.keyidentifier=keyid.getOctets(); + + } + + /** + * + * Calulates the keyidentifier using a SHA1 hash over the BIT STRING + * from SubjectPublicKeyInfo as defined in RFC2459. + * + **/ + public SubjectKeyIdentifier( + SubjectPublicKeyInfo spki) + { + Digest digest = new SHA1Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + byte[] bytes = spki.getPublicKeyData().getBytes(); + digest.update(bytes, 0, bytes.length); + digest.doFinal(resBuf, 0); + this.keyidentifier=resBuf; + } + + public byte[] getKeyIdentifier() + { + return keyidentifier; + } + + /** + *
+     * SubjectKeyIdentifier := OCTET STRING
+     * 
+ */ + public DERObject getDERObject() + { + return new DEROctetString(keyidentifier); + } +} + + + +/** + * The object that contains the public key stored in a certficate. + *

+ * The getEncoded() method in the public keys in the JCE produces a DER + * encoded one of these. + */ +class SubjectPublicKeyInfo + implements DEREncodable +{ + private AlgorithmIdentifier algId; + private DERBitString keyData; + + public static SubjectPublicKeyInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static SubjectPublicKeyInfo getInstance( + Object obj) + { + if (obj instanceof SubjectPublicKeyInfo) + { + return (SubjectPublicKeyInfo)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new SubjectPublicKeyInfo((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public SubjectPublicKeyInfo( + AlgorithmIdentifier algId, + DEREncodable publicKey) + { + this.keyData = new DERBitString(publicKey); + this.algId = algId; + } + + public SubjectPublicKeyInfo( + AlgorithmIdentifier algId, + byte[] publicKey) + { + this.keyData = new DERBitString(publicKey); + this.algId = algId; + } + + public SubjectPublicKeyInfo( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + this.algId = AlgorithmIdentifier.getInstance(e.nextElement()); + this.keyData = (DERBitString)e.nextElement(); + } + + public AlgorithmIdentifier getAlgorithmId() + { + return algId; + } + + /** + * for when the public key is an encoded object - if the bitstring + * can't be decoded this routine throws an IOException. + * + * @exception IOException - if the bit string doesn't represent a DER + * encoded object. + */ + public DERObject getPublicKey() + throws IOException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(keyData.getBytes()); + DERInputStream dIn = new DERInputStream(bIn); + + return dIn.readObject(); + } + + /** + * for when the public key is raw bits... + */ + public DERBitString getPublicKeyData() + { + return keyData; + } + + /** + *

+     * SubjectPublicKeyInfo ::= SEQUENCE {
+     *                          algorithm AlgorithmIdentifier,
+     *                          publicKey BIT STRING }
+     * 
+ */ + public DERObject getDERObject() + { + DERConstructedSequence seq = new DERConstructedSequence(); + + seq.addObject(algId); + seq.addObject(keyData); + + return seq; + } +} + + +/** + *
+ * TBSCertificate ::= SEQUENCE {
+ *      version          [ 0 ]  Version DEFAULT v1(0),
+ *      serialNumber            CertificateSerialNumber,
+ *      signature               AlgorithmIdentifier,
+ *      issuer                  Name,
+ *      validity                Validity,
+ *      subject                 Name,
+ *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
+ *      issuerUniqueID    [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ *      subjectUniqueID   [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ *      extensions        [ 3 ] Extensions OPTIONAL
+ *      }
+ * 
+ *

+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class + * will parse them, but you really shouldn't be creating new ones. + */ +class TBSCertificateStructure + implements DEREncodable, X509ObjectIdentifiers, PKCSObjectIdentifiers +{ + ASN1Sequence seq; + + DERInteger version; + DERInteger serialNumber; + AlgorithmIdentifier signature; + X509Name issuer; + Time startDate, endDate; + X509Name subject; + SubjectPublicKeyInfo subjectPublicKeyInfo; + DERBitString issuerUniqueId; + DERBitString subjectUniqueId; + X509Extensions extensions; + + public static TBSCertificateStructure getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static TBSCertificateStructure getInstance( + Object obj) + { + if (obj instanceof TBSCertificateStructure) + { + return (TBSCertificateStructure)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new TBSCertificateStructure((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public TBSCertificateStructure( + ASN1Sequence seq) + { + int seqStart = 0; + + this.seq = seq; + + // + // some certficates don't include a version number - we assume v1 + // + if (seq.getObjectAt(0) instanceof DERTaggedObject) + { + version = DERInteger.getInstance(seq.getObjectAt(0)); + } + else + { + seqStart = -1; // field 0 is missing! + version = new DERInteger(0); + } + + serialNumber = DERInteger.getInstance(seq.getObjectAt(seqStart + 1)); + + signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2)); + issuer = X509Name.getInstance(seq.getObjectAt(seqStart + 3)); + + // + // before and after dates + // + ASN1Sequence dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4); + + startDate = Time.getInstance(dates.getObjectAt(0)); + endDate = Time.getInstance(dates.getObjectAt(1)); + + subject = X509Name.getInstance(seq.getObjectAt(seqStart + 5)); + + // + // public key info. + // + subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6)); + + for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--) + { + DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras); + + switch (extra.getTagNo()) + { + case 1: + issuerUniqueId = DERBitString.getInstance(extra); + break; + case 2: + subjectUniqueId = DERBitString.getInstance(extra); + break; + case 3: + extensions = X509Extensions.getInstance(extra); + } + } + } + + public int getVersion() + { + return version.getValue().intValue() + 1; + } + + public DERInteger getVersionNumber() + { + return version; + } + + public DERInteger getSerialNumber() + { + return serialNumber; + } + + public AlgorithmIdentifier getSignature() + { + return signature; + } + + public X509Name getIssuer() + { + return issuer; + } + + public Time getStartDate() + { + return startDate; + } + + public Time getEndDate() + { + return endDate; + } + + public X509Name getSubject() + { + return subject; + } + + public SubjectPublicKeyInfo getSubjectPublicKeyInfo() + { + return subjectPublicKeyInfo; + } + + public DERBitString getIssuerUniqueId() + { + return issuerUniqueId; + } + + public DERBitString getSubjectUniqueId() + { + return subjectUniqueId; + } + + public X509Extensions getExtensions() + { + return extensions; + } + + public DERObject getDERObject() + { + return seq; + } +} + + + +/** + * PKIX RFC-2459 + * + *

+ * TBSCertList  ::=  SEQUENCE  {
+ *      version                 Version OPTIONAL,
+ *                                   -- if present, shall be v2
+ *      signature               AlgorithmIdentifier,
+ *      issuer                  Name,
+ *      thisUpdate              Time,
+ *      nextUpdate              Time OPTIONAL,
+ *      revokedCertificates     SEQUENCE OF SEQUENCE  {
+ *           userCertificate         CertificateSerialNumber,
+ *           revocationDate          Time,
+ *           crlEntryExtensions      Extensions OPTIONAL
+ *                                         -- if present, shall be v2
+ *                                }  OPTIONAL,
+ *      crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
+ *                                         -- if present, shall be v2
+ *                                }
+ * 
+ */ + +class TBSCertList + implements DEREncodable +{ + class CRLEntry + implements DEREncodable + { + DERConstructedSequence seq; + + DERInteger userCertificate; + Time revocationDate; + X509Extensions crlEntryExtensions; + + public CRLEntry( + DERConstructedSequence seq) + { + this.seq = seq; + + userCertificate = (DERInteger)seq.getObjectAt(0); + revocationDate = Time.getInstance(seq.getObjectAt(1)); + if (seq.getSize() == 3) + { + crlEntryExtensions = X509Extensions.getInstance(seq.getObjectAt(2)); + } + } + + public DERInteger getUserCertificate() + { + return userCertificate; + } + + public Time getRevocationDate() + { + return revocationDate; + } + + public X509Extensions getExtensions() + { + return crlEntryExtensions; + } + + public DERObject getDERObject() + { + return seq; + } + } + + ASN1Sequence seq; + + DERInteger version; + AlgorithmIdentifier signature; + X509Name issuer; + Time thisUpdate; + Time nextUpdate; + CRLEntry[] revokedCertificates; + X509Extensions crlExtensions; + + public static TBSCertList getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static TBSCertList getInstance( + Object obj) + { + if (obj instanceof TBSCertList) + { + return (TBSCertList)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new TBSCertList((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public TBSCertList( + ASN1Sequence seq) + { + int seqPos = 0; + + this.seq = seq; + + if (seq.getObjectAt(seqPos) instanceof DERInteger) + { + version = (DERInteger)seq.getObjectAt(seqPos++); + } + else + { + version = new DERInteger(0); + } + + signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqPos++)); + issuer = X509Name.getInstance(seq.getObjectAt(seqPos++)); + thisUpdate = Time.getInstance(seq.getObjectAt(seqPos++)); + + if (seqPos < seq.size() + && (seq.getObjectAt(seqPos) instanceof DERUTCTime + || seq.getObjectAt(seqPos) instanceof DERGeneralizedTime + || seq.getObjectAt(seqPos) instanceof Time)) + { + nextUpdate = Time.getInstance(seq.getObjectAt(seqPos++)); + } + + if (seqPos < seq.size() + && !(seq.getObjectAt(seqPos) instanceof DERTaggedObject)) + { + DERConstructedSequence certs = (DERConstructedSequence)seq.getObjectAt(seqPos++); + revokedCertificates = new CRLEntry[certs.getSize()]; + + for ( int i = 0; i < revokedCertificates.length; i++) + { + revokedCertificates[i] = new CRLEntry((DERConstructedSequence)certs.getObjectAt(i)); + } + } + + if (seqPos < seq.size() + && seq.getObjectAt(seqPos) instanceof DERTaggedObject) + { + crlExtensions = X509Extensions.getInstance(seq.getObjectAt(seqPos++)); + } + } + + public int getVersion() + { + return version.getValue().intValue() + 1; + } + + public DERInteger getVersionNumber() + { + return version; + } + + public AlgorithmIdentifier getSignature() + { + return signature; + } + + public X509Name getIssuer() + { + return issuer; + } + + public Time getThisUpdate() + { + return thisUpdate; + } + + public Time getNextUpdate() + { + return nextUpdate; + } + + public CRLEntry[] getRevokedCertificates() + { + return revokedCertificates; + } + + public X509Extensions getExtensions() + { + return crlExtensions; + } + + public DERObject getDERObject() + { + return seq; + } +} + + + +class Time + implements DEREncodable +{ + DERObject time; + + public static Time getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public Time( + DERObject time) + { + if (!(time instanceof DERUTCTime) + && !(time instanceof DERGeneralizedTime)) + { + throw new IllegalArgumentException("unknown object passed to Time"); + } + + this.time = time; + } + + /** + * creates a time object from a given date - if the date is between 1950 + * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime + * is used. + */ + public Time( + Date date) + { + SimpleTimeZone tz = new SimpleTimeZone(0, "Z"); + SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss"); + + dateF.setTimeZone(tz); + + String d = dateF.format(date) + "Z"; + int year = Integer.parseInt(d.substring(0, 4)); + + if (year < 1950 || year > 2049) + { + time = new DERGeneralizedTime(d); + } + else + { + time = new DERUTCTime(d.substring(2)); + } + } + + public static Time getInstance( + Object obj) + { + if (obj instanceof Time) + { + return (Time)obj; + } + else if (obj instanceof DERUTCTime) + { + return new Time((DERUTCTime)obj); + } + else if (obj instanceof DERGeneralizedTime) + { + return new Time((DERGeneralizedTime)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public String getTime() + { + if (time instanceof DERUTCTime) + { + return ((DERUTCTime)time).getAdjustedTime(); + } + else + { + return ((DERGeneralizedTime)time).getTime(); + } + } + + public Date getDate() + { + SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); + + return dateF.parse(this.getTime(), new ParsePosition(0)); + } + + /** + *
+     * Time ::= CHOICE {
+     *             utcTime        UTCTime,
+     *             generalTime    GeneralizedTime }
+     * 
+ */ + public DERObject getDERObject() + { + return time; + } +} + + +/** + * Generator for Version 1 TBSCertificateStructures. + *
+ * TBSCertificate ::= SEQUENCE {
+ *      version          [ 0 ]  Version DEFAULT v1(0),
+ *      serialNumber            CertificateSerialNumber,
+ *      signature               AlgorithmIdentifier,
+ *      issuer                  Name,
+ *      validity                Validity,
+ *      subject                 Name,
+ *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
+ *      }
+ * 
+ * + */ +class V1TBSCertificateGenerator +{ + DERTaggedObject version = new DERTaggedObject(0, new DERInteger(0)); + + DERInteger serialNumber; + AlgorithmIdentifier signature; + X509Name issuer; + Time startDate, endDate; + X509Name subject; + SubjectPublicKeyInfo subjectPublicKeyInfo; + + public V1TBSCertificateGenerator() + { + } + + public void setSerialNumber( + DERInteger serialNumber) + { + this.serialNumber = serialNumber; + } + + public void setSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + public void setIssuer( + X509Name issuer) + { + this.issuer = issuer; + } + + public void setStartDate( + Time startDate) + { + this.startDate = startDate; + } + + public void setStartDate( + DERUTCTime startDate) + { + this.startDate = new Time(startDate); + } + + public void setEndDate( + Time endDate) + { + this.endDate = endDate; + } + + public void setEndDate( + DERUTCTime endDate) + { + this.endDate = new Time(endDate); + } + + public void setSubject( + X509Name subject) + { + this.subject = subject; + } + + public void setSubjectPublicKeyInfo( + SubjectPublicKeyInfo pubKeyInfo) + { + this.subjectPublicKeyInfo = pubKeyInfo; + } + + public TBSCertificateStructure generateTBSCertificate() + { + if ((serialNumber == null) || (signature == null) + || (issuer == null) || (startDate == null) || (endDate == null) + || (subject == null) || (subjectPublicKeyInfo == null)) + { + throw new IllegalStateException("not all mandatory fields set in V1 TBScertificate generator"); + } + + DERConstructedSequence seq = new DERConstructedSequence(); + + seq.addObject(version); + seq.addObject(serialNumber); + seq.addObject(signature); + seq.addObject(issuer); + + // + // before and after dates + // + DERConstructedSequence validity = new DERConstructedSequence(); + + validity.addObject(startDate); + validity.addObject(endDate); + + seq.addObject(validity); + + seq.addObject(subject); + + seq.addObject(subjectPublicKeyInfo); + + return new TBSCertificateStructure(seq); + } +} + + + +/** + * Generator for Version 2 TBSCertList structures. + *
+ *  TBSCertList  ::=  SEQUENCE  {
+ *       version                 Version OPTIONAL,
+ *                                    -- if present, shall be v2
+ *       signature               AlgorithmIdentifier,
+ *       issuer                  Name,
+ *       thisUpdate              Time,
+ *       nextUpdate              Time OPTIONAL,
+ *       revokedCertificates     SEQUENCE OF SEQUENCE  {
+ *            userCertificate         CertificateSerialNumber,
+ *            revocationDate          Time,
+ *            crlEntryExtensions      Extensions OPTIONAL
+ *                                          -- if present, shall be v2
+ *                                 }  OPTIONAL,
+ *       crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
+ *                                          -- if present, shall be v2
+ *                                 }
+ * 
+ * + * Note: This class may be subject to change + */ +class V2TBSCertListGenerator +{ + DERInteger version = new DERInteger(1); + + AlgorithmIdentifier signature; + X509Name issuer; + Time thisUpdate, nextUpdate=null; + X509Extensions extensions=null; + private Vector crlentries=null; + + public V2TBSCertListGenerator() + { + } + + + public void setSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + public void setIssuer( + X509Name issuer) + { + this.issuer = issuer; + } + + public void setThisUpdate( + DERUTCTime thisUpdate) + { + this.thisUpdate = new Time(thisUpdate); + } + + public void setNextUpdate( + DERUTCTime nextUpdate) + { + this.nextUpdate = new Time(nextUpdate); + } + + public void setThisUpdate( + Time thisUpdate) + { + this.thisUpdate = thisUpdate; + } + + public void setNextUpdate( + Time nextUpdate) + { + this.nextUpdate = nextUpdate; + } + + public void addCRLEntry( + DERConstructedSequence crlEntry) + { + if (crlentries == null) + crlentries = new Vector(); + crlentries.addElement(crlEntry); + } + + public void addCRLEntry(DERInteger userCertificate, DERUTCTime revocationDate, int reason) + { + addCRLEntry(userCertificate, new Time(revocationDate), reason); + } + + public void addCRLEntry(DERInteger userCertificate, Time revocationDate, int reason) + { + DERConstructedSequence seq = new DERConstructedSequence(); + seq.addObject(userCertificate); + seq.addObject(revocationDate); + + if (reason != 0) + { + CRLReason rf = new CRLReason(reason); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + try + { + dOut.writeObject(rf); + } + catch (IOException e) + { + throw new IllegalArgumentException("error encoding value: " + e); + } + byte[] value = bOut.toByteArray(); + DERConstructedSequence eseq = new DERConstructedSequence(); + DERConstructedSequence eseq1 = new DERConstructedSequence(); + eseq1.addObject(X509Extensions.ReasonCode); + eseq1.addObject(new DEROctetString(value)); + eseq.addObject(eseq1); + X509Extensions ex = new X509Extensions(eseq); + seq.addObject(ex); + } + if (crlentries == null) + crlentries = new Vector(); + crlentries.addElement(seq); + } + + public void setExtensions( + X509Extensions extensions) + { + this.extensions = extensions; + } + + public TBSCertList generateTBSCertList() + { + if ((signature == null) || (issuer == null) || (thisUpdate == null)) + { + throw new IllegalStateException("Not all mandatory fields set in V2 TBSCertList generator."); + } + + DERConstructedSequence seq = new DERConstructedSequence(); + + seq.addObject(version); + seq.addObject(signature); + seq.addObject(issuer); + + seq.addObject(thisUpdate); + if (nextUpdate != null) + seq.addObject(nextUpdate); + + // Add CRLEntries if they exist + if (crlentries != null) { + DERConstructedSequence certseq = new DERConstructedSequence(); + Enumeration it = crlentries.elements(); + while( it.hasMoreElements() ) { + certseq.addObject((DERConstructedSequence)it.nextElement()); + } + seq.addObject(certseq); + } + + if (extensions != null) + { + seq.addObject(new DERTaggedObject(0, extensions)); + } + + return new TBSCertList(seq); + } +} + + +/** + * Generator for Version 3 TBSCertificateStructures. + *
+ * TBSCertificate ::= SEQUENCE {
+ *      version          [ 0 ]  Version DEFAULT v1(0),
+ *      serialNumber            CertificateSerialNumber,
+ *      signature               AlgorithmIdentifier,
+ *      issuer                  Name,
+ *      validity                Validity,
+ *      subject                 Name,
+ *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
+ *      issuerUniqueID    [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ *      subjectUniqueID   [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ *      extensions        [ 3 ] Extensions OPTIONAL
+ *      }
+ * 
+ * + */ +class V3TBSCertificateGenerator +{ + DERTaggedObject version = new DERTaggedObject(0, new DERInteger(2)); + + DERInteger serialNumber; + AlgorithmIdentifier signature; + X509Name issuer; + Time startDate, endDate; + X509Name subject; + SubjectPublicKeyInfo subjectPublicKeyInfo; + X509Extensions extensions; + + public V3TBSCertificateGenerator() + { + } + + public void setSerialNumber( + DERInteger serialNumber) + { + this.serialNumber = serialNumber; + } + + public void setSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + public void setIssuer( + X509Name issuer) + { + this.issuer = issuer; + } + + public void setStartDate( + DERUTCTime startDate) + { + this.startDate = new Time(startDate); + } + + public void setStartDate( + Time startDate) + { + this.startDate = startDate; + } + + public void setEndDate( + DERUTCTime endDate) + { + this.endDate = new Time(endDate); + } + + public void setEndDate( + Time endDate) + { + this.endDate = endDate; + } + + public void setSubject( + X509Name subject) + { + this.subject = subject; + } + + public void setSubjectPublicKeyInfo( + SubjectPublicKeyInfo pubKeyInfo) + { + this.subjectPublicKeyInfo = pubKeyInfo; + } + + public void setExtensions( + X509Extensions extensions) + { + this.extensions = extensions; + } + + public TBSCertificateStructure generateTBSCertificate() + { + if ((serialNumber == null) || (signature == null) + || (issuer == null) || (startDate == null) || (endDate == null) + || (subject == null) || (subjectPublicKeyInfo == null)) + { + throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator"); + } + + DERConstructedSequence seq = new DERConstructedSequence(); + + seq.addObject(version); + seq.addObject(serialNumber); + seq.addObject(signature); + seq.addObject(issuer); + + // + // before and after dates + // + DERConstructedSequence validity = new DERConstructedSequence(); + + validity.addObject(startDate); + validity.addObject(endDate); + + seq.addObject(validity); + + seq.addObject(subject); + + seq.addObject(subjectPublicKeyInfo); + + if (extensions != null) + { + seq.addObject(new DERTaggedObject(3, extensions)); + } + + return new TBSCertificateStructure(seq); + } +} + + +/** + * an X509Certificate structure. + *
+ *  Certificate ::= SEQUENCE {
+ *      tbsCertificate          TBSCertificate,
+ *      signatureAlgorithm      AlgorithmIdentifier,
+ *      signature               BIT STRING
+ *  }
+ * 
+ */ +class X509CertificateStructure + implements DEREncodable, X509ObjectIdentifiers, PKCSObjectIdentifiers +{ + ASN1Sequence seq; + TBSCertificateStructure tbsCert; + AlgorithmIdentifier sigAlgId; + DERBitString sig; + + public static X509CertificateStructure getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static X509CertificateStructure getInstance( + Object obj) + { + if (obj instanceof X509CertificateStructure) + { + return (X509CertificateStructure)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new X509CertificateStructure((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + public X509CertificateStructure( + ASN1Sequence seq) + { + this.seq = seq; + + // + // correct x509 certficate + // + if (seq.size() == 3) + { + tbsCert = TBSCertificateStructure.getInstance(seq.getObjectAt(0)); + sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); + + sig = (DERBitString)seq.getObjectAt(2); + } + } + + public TBSCertificateStructure getTBSCertificate() + { + return tbsCert; + } + + public int getVersion() + { + return tbsCert.getVersion(); + } + + public DERInteger getSerialNumber() + { + return tbsCert.getSerialNumber(); + } + + public X509Name getIssuer() + { + return tbsCert.getIssuer(); + } + + public Time getStartDate() + { + return tbsCert.getStartDate(); + } + + public Time getEndDate() + { + return tbsCert.getEndDate(); + } + + public X509Name getSubject() + { + return tbsCert.getSubject(); + } + + public SubjectPublicKeyInfo getSubjectPublicKeyInfo() + { + return tbsCert.getSubjectPublicKeyInfo(); + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return sigAlgId; + } + + public DERBitString getSignature() + { + return sig; + } + + public DERObject getDERObject() + { + return seq; + } +} + + +/** + * an object for the elements in the X.509 V3 extension block. + */ +class X509Extension +{ + boolean critical; + DEROctetString value; + + public X509Extension( + DERBoolean critical, + DEROctetString value) + { + this.critical = critical.isTrue(); + this.value = value; + } + + public X509Extension( + boolean critical, + DEROctetString value) + { + this.critical = critical; + this.value = value; + } + + public boolean isCritical() + { + return critical; + } + + public DEROctetString getValue() + { + return value; + } + + public int hashCode() + { + if (this.isCritical()) + { + return this.getValue().hashCode(); + } + + + return ~this.getValue().hashCode(); + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof X509Extension)) + { + return false; + } + + X509Extension other = (X509Extension)o; + + return other.getValue().equals(this.getValue()) + && (other.isCritical() == this.isCritical()); + } +} + + + +class X509Extensions + implements DEREncodable +{ + /** + * Subject Key Identifier + */ + public static final DERObjectIdentifier SubjectKeyIdentifier = new DERObjectIdentifier("2.5.29.14"); + + /** + * Key Usage + */ + public static final DERObjectIdentifier KeyUsage = new DERObjectIdentifier("2.5.29.15"); + + /** + * Private Key Usage Period + */ + public static final DERObjectIdentifier PrivateKeyUsagePeriod = new DERObjectIdentifier("2.5.29.16"); + + /** + * Subject Alternative Name + */ + public static final DERObjectIdentifier SubjectAlternativeName = new DERObjectIdentifier("2.5.29.17"); + + /** + * Issuer Alternative Name + */ + public static final DERObjectIdentifier IssuerAlternativeName = new DERObjectIdentifier("2.5.29.18"); + + /** + * Basic Constraints + */ + public static final DERObjectIdentifier BasicConstraints = new DERObjectIdentifier("2.5.29.19"); + + /** + * CRL Number + */ + public static final DERObjectIdentifier CRLNumber = new DERObjectIdentifier("2.5.29.20"); + + /** + * Reason code + */ + public static final DERObjectIdentifier ReasonCode = new DERObjectIdentifier("2.5.29.21"); + + /** + * Hold Instruction Code + */ + public static final DERObjectIdentifier InstructionCode = new DERObjectIdentifier("2.5.29.23"); + + /** + * Invalidity Date + */ + public static final DERObjectIdentifier InvalidityDate = new DERObjectIdentifier("2.5.29.24"); + + /** + * Delta CRL indicator + */ + public static final DERObjectIdentifier DeltaCRLIndicator = new DERObjectIdentifier("2.5.29.27"); + + /** + * Issuing Distribution Point + */ + public static final DERObjectIdentifier IssuingDistributionPoint = new DERObjectIdentifier("2.5.29.28"); + + /** + * Certificate Issuer + */ + public static final DERObjectIdentifier CertificateIssuer = new DERObjectIdentifier("2.5.29.29"); + + /** + * Name Constraints + */ + public static final DERObjectIdentifier NameConstraints = new DERObjectIdentifier("2.5.29.30"); + + /** + * CRL Distribution Points + */ + public static final DERObjectIdentifier CRLDistributionPoints = new DERObjectIdentifier("2.5.29.31"); + + /** + * Certificate Policies + */ + public static final DERObjectIdentifier CertificatePolicies = new DERObjectIdentifier("2.5.29.32"); + + /** + * Policy Mappings + */ + public static final DERObjectIdentifier PolicyMappings = new DERObjectIdentifier("2.5.29.33"); + + /** + * Authority Key Identifier + */ + public static final DERObjectIdentifier AuthorityKeyIdentifier = new DERObjectIdentifier("2.5.29.35"); + + /** + * Policy Constraints + */ + public static final DERObjectIdentifier PolicyConstraints = new DERObjectIdentifier("2.5.29.36"); + + /** + * Extended Key Usage + */ + public static final DERObjectIdentifier ExtendedKeyUsage = new DERObjectIdentifier("2.5.29.37"); + + private Hashtable extensions = new Hashtable(); + private Vector ordering = new Vector(); + + public static X509Extensions getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static X509Extensions getInstance( + Object obj) + { + if (obj == null || obj instanceof X509Extensions) + { + return (X509Extensions)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new X509Extensions((ASN1Sequence)obj); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * Constructor from DERConstructedSequence. + * + * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString) + */ + public X509Extensions( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1Sequence s = (ASN1Sequence)e.nextElement(); + Enumeration e1 = s.getObjects(); + + if (s.size() == 3) + { + extensions.put(s.getObjectAt(0), new X509Extension((DERBoolean)s.getObjectAt(1), (DEROctetString)s.getObjectAt(2))); + } + else + { + extensions.put(s.getObjectAt(0), new X509Extension(false, (DEROctetString)s.getObjectAt(1))); + } + + ordering.addElement(s.getObjectAt(0)); + } + } + + /** + * constructor from a table of extensions. + *

+ * it's is assumed the table contains OID/String pairs. + */ + public X509Extensions( + Hashtable extensions) + { + this(null, extensions); + } + + /** + * constructor from a table of extensions with ordering + *

+ * it's is assumed the table contains OID/String pairs. + */ + public X509Extensions( + Vector ordering, + Hashtable extensions) + { + Enumeration e; + + if (ordering == null) + { + e = extensions.keys(); + } + else + { + e = ordering.elements(); + } + + while (e.hasMoreElements()) + { + this.ordering.addElement(e.nextElement()); + } + + e = this.ordering.elements(); + + while (e.hasMoreElements()) + { + DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); + X509Extension ext = (X509Extension)extensions.get(oid); + + this.extensions.put(oid, ext); + } + } + + /** + * return an Enumeration of the extension field's object ids. + */ + public Enumeration oids() + { + return ordering.elements(); + } + + /** + * return the extension represented by the object identifier + * passed in. + * + * @return the extension if it's present, null otherwise. + */ + public X509Extension getExtension( + DERObjectIdentifier oid) + { + return (X509Extension)extensions.get(oid); + } + + public DERObject getDERObject() + { + DEREncodableVector vec = new DEREncodableVector(); + Enumeration e = ordering.elements(); + + while (e.hasMoreElements()) + { + DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); + X509Extension ext = (X509Extension)extensions.get(oid); + DEREncodableVector v = new DEREncodableVector(); + + v.add(oid); + + if (ext.isCritical()) + { + v.add(new DERBoolean(true)); + } + + v.add(ext.getValue()); + + vec.add(new DERSequence(v)); + } + + return new DERSequence(vec); + } + + public int hashCode() + { + Enumeration e = extensions.keys(); + int hashCode = 0; + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + hashCode ^= o.hashCode(); + hashCode ^= extensions.get(o).hashCode(); + } + + return hashCode; + } + + public boolean equals( + Object o) + { + if (o == null || !(o instanceof X509Extensions)) + { + return false; + } + + X509Extensions other = (X509Extensions)o; + + Enumeration e1 = extensions.keys(); + Enumeration e2 = other.extensions.keys(); + + while (e1.hasMoreElements() && e2.hasMoreElements()) + { + Object o1 = e1.nextElement(); + Object o2 = e2.nextElement(); + + if (!o1.equals(o2)) + { + return false; + } + } + + if (e1.hasMoreElements() || e2.hasMoreElements()) + { + return false; + } + + return true; + } +} + + + +class X509Name + implements DEREncodable +{ + /** + * country code - StringType(SIZE(2)) + */ + public static final DERObjectIdentifier C = new DERObjectIdentifier("2.5.4.6"); + + /** + * organization - StringType(SIZE(1..64)) + */ + public static final DERObjectIdentifier O = new DERObjectIdentifier("2.5.4.10"); + + /** + * organizational unit name - StringType(SIZE(1..64)) + */ + public static final DERObjectIdentifier OU = new DERObjectIdentifier("2.5.4.11"); + + /** + * Title + */ + public static final DERObjectIdentifier T = new DERObjectIdentifier("2.5.4.12"); + + /** + * common name - StringType(SIZE(1..64)) + */ + public static final DERObjectIdentifier CN = new DERObjectIdentifier("2.5.4.3"); + + /** + * device serial number name - StringType(SIZE(1..64)) + */ + public static final DERObjectIdentifier SN = new DERObjectIdentifier("2.5.4.5"); + + /** + * locality name - StringType(SIZE(1..64)) + */ + public static final DERObjectIdentifier L = new DERObjectIdentifier("2.5.4.7"); + + /** + * state, or province name - StringType(SIZE(1..64)) + */ + public static final DERObjectIdentifier ST = new DERObjectIdentifier("2.5.4.8"); + + + /** + * email address (RSA PKCS#9 extension) - IA5String + *

+ * note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here. + */ + public static final DERObjectIdentifier EmailAddress = new DERObjectIdentifier("1.2.840.113549.1.9.1"); + + /** + * email address in Verisign certificates + */ + public static final DERObjectIdentifier E = EmailAddress; + + /* + * others... + */ + public static final DERObjectIdentifier DC = new DERObjectIdentifier("0.9.2342.19200300.100.1.25"); + + /** + * LDAP User id. + */ + public static final DERObjectIdentifier UID = new DERObjectIdentifier("0.9.2342.19200300.100.1.1"); + + /** + * look up table translating OID values into their common symbols. + */ + public static Hashtable OIDLookUp = new Hashtable(); + + /** + * look up table translating common symbols into their OIDS. + */ + public static Hashtable SymbolLookUp = new Hashtable(); + + static + { + OIDLookUp.put(C, "C"); + OIDLookUp.put(O, "O"); + OIDLookUp.put(T, "T"); + OIDLookUp.put(OU, "OU"); + OIDLookUp.put(CN, "CN"); + OIDLookUp.put(L, "L"); + OIDLookUp.put(ST, "ST"); + OIDLookUp.put(SN, "SN"); + OIDLookUp.put(EmailAddress, "E"); + OIDLookUp.put(DC, "DC"); + OIDLookUp.put(UID, "UID"); + + SymbolLookUp.put("c", C); + SymbolLookUp.put("o", O); + SymbolLookUp.put("t", T); + SymbolLookUp.put("ou", OU); + SymbolLookUp.put("cn", CN); + SymbolLookUp.put("l", L); + SymbolLookUp.put("st", ST); + SymbolLookUp.put("sn", SN); + SymbolLookUp.put("emailaddress", E); + SymbolLookUp.put("dc", DC); + SymbolLookUp.put("e", E); + SymbolLookUp.put("uid", UID); + } + + private Vector ordering = new Vector(); + private Vector values = new Vector(); + private ASN1Sequence seq; + + public static X509Name getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static X509Name getInstance( + Object obj) + { + if (obj == null || obj instanceof X509Name) + { + return (X509Name)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new X509Name((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory"); + } + + /** + * Constructor from ASN1Sequence + * + * the principal will be a list of constructed sets, each containing an (OID, String) pair. + */ + public X509Name( + ASN1Sequence seq) + { + this.seq = seq; + + Enumeration e = seq.getObjects(); + + while (e.hasMoreElements()) + { + ASN1Set set = (ASN1Set)e.nextElement(); + ASN1Sequence s = (ASN1Sequence)set.getObjectAt(0); + + ordering.addElement(s.getObjectAt(0)); + values.addElement(((DERString)s.getObjectAt(1)).getString()); + } + } + + /** + * constructor from a table of attributes. + *

+ * it's is assumed the table contains OID/String pairs, and the contents + * of the table are copied into an internal table as part of the + * construction process. + *

+ * Note: if the name you are trying to generate should be + * following a specific ordering, you should use the constructor + * with the ordering specified below. + */ + public X509Name( + Hashtable attributes) + { + this(null, attributes); + } + + /** + * constructor from a table of attributes with ordering. + *

+ * it's is assumed the table contains OID/String pairs, and the contents + * of the table are copied into an internal table as part of the + * construction process. The ordering vector should contain the OIDs + * in the order they are meant to be encoded or printed in toString. + */ + public X509Name( + Vector ordering, + Hashtable attributes) + { + if (ordering != null) + { + for (int i = 0; i != ordering.size(); i++) + { + this.ordering.addElement(ordering.elementAt(i)); + } + } + else + { + Enumeration e = attributes.keys(); + + while (e.hasMoreElements()) + { + this.ordering.addElement(e.nextElement()); + } + } + + for (int i = 0; i != this.ordering.size(); i++) + { + DERObjectIdentifier oid = (DERObjectIdentifier)this.ordering.elementAt(i); + + if (OIDLookUp.get(oid) == null) + { + throw new IllegalArgumentException("Unknown object id - " + oid.getId() + " - passed to distinguished name"); + } + + if (attributes.get(oid) == null) + { + throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name"); + } + + this.values.addElement(attributes.get(oid)); // copy the hash table + } + } + + /** + * takes two vectors one of the oids and the other of the values. + */ + public X509Name( + Vector ordering, + Vector values) + { + if (ordering.size() != values.size()) + { + throw new IllegalArgumentException("ordering vector must be same length as values."); + } + + for (int i = 0; i < ordering.size(); i++) + { + this.ordering.addElement(ordering.elementAt(i)); + this.values.addElement(values.elementAt(i)); + } + } + + /** + * takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or + * some such, converting it into an ordered set of name attributes. + */ + public X509Name( + String dirName) + { + X509NameTokenizer nTok = new X509NameTokenizer(dirName); + + while (nTok.hasMoreTokens()) + { + String token = nTok.nextToken(); + int index = token.indexOf('='); + + if (index == -1) + { + throw new IllegalArgumentException("badly formated directory string"); + } + + String name = token.substring(0, index); + String value = token.substring(index + 1); + DERObjectIdentifier oid = null; + + if (name.toUpperCase().startsWith("OID.")) + { + oid = new DERObjectIdentifier(name.substring(4)); + } + else if (name.charAt(0) >= '0' && name.charAt(0) <= '9') + { + oid = new DERObjectIdentifier(name); + } + else + { + oid = (DERObjectIdentifier)SymbolLookUp.get(name.toLowerCase()); + if (oid == null) + { + throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name"); + } + } + + this.ordering.addElement(oid); + this.values.addElement(value); + } + } + + /** + * return false if we have characters out of the range of a printable + * string, true otherwise. + */ + private boolean canBePrintable( + String str) + { + for (int i = str.length() - 1; i >= 0; i--) + { + if (str.charAt(i) > 0x007f) + { + return false; + } + } + + return true; + } + + public DERObject getDERObject() + { + if (seq == null) + { + DEREncodableVector vec = new DEREncodableVector(); + + for (int i = 0; i != ordering.size(); i++) + { + DEREncodableVector v = new DEREncodableVector(); + DERObjectIdentifier oid = (DERObjectIdentifier)ordering.elementAt(i); + + v.add(oid); + + String str = (String)values.elementAt(i); + + if (oid.equals(EmailAddress)) + { + v.add(new DERIA5String(str)); + } + else + { + if (canBePrintable(str)) + { + v.add(new DERPrintableString(str)); + } + else + { + v.add(new DERUTF8String(str)); + } + } + + vec.add(new DERSet(new DERSequence(v))); + } + + seq = new DERSequence(vec); + } + + return seq; + } + + /** + * test for equality - note: case is ignored. + */ + public boolean equals(Object _obj) + { + if (_obj == this) + { + return true; + } + + if (_obj == null || !(_obj instanceof X509Name)) + { + return false; + } + + X509Name _oxn = (X509Name)_obj; + int _orderingSize = ordering.size(); + + if (_orderingSize != _oxn.ordering.size()) + { + return false; + } + + boolean[] _indexes = new boolean[_orderingSize]; + + for(int i = 0; i < _orderingSize; i++) + { + boolean _found = false; + String _oid = ((DERObjectIdentifier)ordering.elementAt(i)).getId(); + String _val = (String)values.elementAt(i); + + for(int j = 0; j < _orderingSize; j++) + { + if(_indexes[j] == true) + { + continue; + } + + String _oOID = ((DERObjectIdentifier)_oxn.ordering.elementAt(j)).getId(); + String _oVal = (String)_oxn.values.elementAt(j); + + // was equalsIgnoreCase but MIDP doesn't like that. + if(_oid.equals(_oOID) && _val.toLowerCase().equals(_oVal.toLowerCase())) + { + _indexes[j] = true; + _found = true; + break; + } + + } + + if(!_found) + { + return false; + } + } + + return true; + } + + public int hashCode() + { + ASN1Sequence seq = (ASN1Sequence)this.getDERObject(); + Enumeration e = seq.getObjects(); + int hashCode = 0; + + while (e.hasMoreElements()) + { + hashCode ^= e.nextElement().hashCode(); + } + + return hashCode; + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + boolean first = true; + Enumeration e1 = ordering.elements(); + Enumeration e2 = values.elements(); + + while (e1.hasMoreElements()) + { + Object oid = e1.nextElement(); + String sym = (String)OIDLookUp.get(oid); + + if (first) + { + first = false; + } + else + { + buf.append(","); + } + + if (sym != null) + { + buf.append(sym); + } + else + { + buf.append(((DERObjectIdentifier)oid).getId()); + } + + buf.append("="); + + int index = buf.length(); + + buf.append((String)e2.nextElement()); + + int end = buf.length(); + + while (index != end) + { + if ((buf.charAt(index) == ',') + || (buf.charAt(index) == '"') + || (buf.charAt(index) == '\\') + || (buf.charAt(index) == '+') + || (buf.charAt(index) == '<') + || (buf.charAt(index) == '>') + || (buf.charAt(index) == ';')) + { + buf.insert(index, "\\"); + index++; + end++; + } + + index++; + } + } + + return buf.toString(); + } +} + +/** + * class for breaking up an X500 Name into it's component tokens, ala + * java.util.StringTokenizer. We need this class as some of the + * lightweight Java environment don't support classes like + * StringTokenizer. + */ +class X509NameTokenizer +{ + private String oid; + private int index; + private StringBuffer buf = new StringBuffer(); + + public X509NameTokenizer( + String oid) + { + this.oid = oid; + this.index = -1; + } + + public boolean hasMoreTokens() + { + return (index != oid.length()); + } + + public String nextToken() + { + if (index == oid.length()) + { + return null; + } + + int end = index + 1; + boolean quoted = false; + boolean escaped = false; + + buf.setLength(0); + + while (end != oid.length()) + { + char c = oid.charAt(end); + + if (c == '"') + { + if (!escaped) + { + quoted = !quoted; + } + else + { + buf.append(c); + } + escaped = false; + } + else + { + if (escaped || quoted) + { + buf.append(c); + escaped = false; + } + else if (c == '\\') + { + escaped = true; + } + else if (c == ',') + { + break; + } + else + { + buf.append(c); + } + } + end++; + } + + index = end; + return buf.toString().trim(); + } +} + + +interface X509ObjectIdentifiers +{ + // + // base id + // + static final String id = "2.5.4"; + + static final DERObjectIdentifier commonName = new DERObjectIdentifier(id + ".3"); + static final DERObjectIdentifier countryName = new DERObjectIdentifier(id + ".6"); + static final DERObjectIdentifier localityName = new DERObjectIdentifier(id + ".7"); + static final DERObjectIdentifier stateOrProvinceName = new DERObjectIdentifier(id + ".8"); + static final DERObjectIdentifier organization = new DERObjectIdentifier(id + ".10"); + static final DERObjectIdentifier organizationalUnitName = new DERObjectIdentifier(id + ".11"); + + // id-SHA1 OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } // + static final DERObjectIdentifier id_SHA1 = new DERObjectIdentifier("1.3.14.3.2.26"); + + // + // ripemd160 OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)} + // + static final DERObjectIdentifier ripemd160 = new DERObjectIdentifier("1.3.36.3.2.1"); + + // + // ripemd160WithRSAEncryption OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) } + // + static final DERObjectIdentifier ripemd160WithRSAEncryption = new DERObjectIdentifier("1.3.36.3.3.1.2"); + + + static final DERObjectIdentifier id_ea_rsa = new DERObjectIdentifier("2.5.8.1.1"); +} + + + +/** + * base interface that a public/private key block cipher needs + * to conform to. + */ +interface AsymmetricBlockCipher +{ + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + */ + public void init(boolean forEncryption, CipherParameters param); + + /** + * returns the largest size an input block can be. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize(); + + /** + * returns the maximum size of the block produced by this cipher. + * + * @return maximum size of the output block produced by the cipher. + */ + public int getOutputBlockSize(); + + /** + * process the block of len bytes stored in in from offset inOff. + * + * @param in the input data + * @param inOff offset into the in array where the data starts + * @param len the length of the block to be processed. + * @return the resulting byte array of the encryption/decryption process. + * @exception InvalidCipherTextException data decrypts improperly. + * @exception DataLengthException the input data is too large for the cipher. + */ + public byte[] processBlock(byte[] in, int inOff, int len) + throws InvalidCipherTextException; +} + +/** + * a holding class for public/private parameter pairs. + */ +class AsymmetricCipherKeyPair +{ + private CipherParameters publicParam; + private CipherParameters privateParam; + + /** + * basic constructor. + * + * @param publicParam a public key parameters object. + * @param privateParam the corresponding private key parameters. + */ + public AsymmetricCipherKeyPair( + CipherParameters publicParam, + CipherParameters privateParam) + { + this.publicParam = publicParam; + this.privateParam = privateParam; + } + + /** + * return the public key parameters. + * + * @return the public key parameters. + */ + public CipherParameters getPublic() + { + return publicParam; + } + + /** + * return the private key parameters. + * + * @return the private key parameters. + */ + public CipherParameters getPrivate() + { + return privateParam; + } +} + +/** + * interface that a public/private key pair generator should conform to. + */ +interface AsymmetricCipherKeyPairGenerator +{ + /** + * intialise the key pair generator. + * + * @param the parameters the key pair is to be initialised with. + */ + public void init(KeyGenerationParameters param); + + /** + * return an AsymmetricCipherKeyPair containing the generated keys. + * + * @return an AsymmetricCipherKeyPair containing the generated keys. + */ + public AsymmetricCipherKeyPair generateKeyPair(); +} + + + +/** + * Block cipher engines are expected to conform to this interface. + */ +interface BlockCipher +{ + /** + * Initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public String getAlgorithmName(); + + /** + * Return the block size for this cipher (in bytes). + * + * @return the block size for this cipher in bytes. + */ + public int getBlockSize(); + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException; + + /** + * Reset the cipher. After resetting the cipher is in the same state + * as it was after the last init (if there was one). + */ + public void reset(); +} + + +/** + * The base class for symmetric, or secret, cipher key generators. + */ +class CipherKeyGenerator +{ + protected Random random; + protected int strength; + + /** + * initialise the key generator. + * + * @param param the parameters to be used for key generation + */ + public void init( + KeyGenerationParameters param) + { + this.random = param.getRandom(); + this.strength = (param.getStrength() + 7) / 8; + } + + /** + * generate a secret key. + * + * @return a byte array containing the key value. + */ + public byte[] generateKey() + { + byte[] key = new byte[strength]; + + random.nextBytes(key); + + return key; + } +} + +/** + * all parameter classes implement this. + */ +interface CipherParameters +{ +} + +/** + * the foundation class for the hard exceptions thrown by the crypto packages. + */ +class CryptoException + extends Exception +{ + /** + * base constructor. + */ + public CryptoException() + { + } + + /** + * create a CryptoException with the given message. + * + * @param message the message to be carried with the exception. + */ + public CryptoException( + String message) + { + super(message); + } +} + +/** + * this exception is thrown if a buffer that is meant to have output + * copied into it turns out to be too short, or if we've been given + * insufficient input. In general this exception will get thrown rather + * than an ArrayOutOfBounds exception. + */ +class DataLengthException + extends RuntimeCryptoException +{ + /** + * base constructor. + */ + public DataLengthException() + { + } + + /** + * create a DataLengthException with the given message. + * + * @param message the message to be carried with the exception. + */ + public DataLengthException( + String message) + { + super(message); + } +} + +/** + * interface that a message digest conforms to. + */ +interface Digest +{ + /** + * return the algorithm name + * + * @return the algorithm name + */ + public String getAlgorithmName(); + + /** + * return the size, in bytes, of the digest produced by this message digest. + * + * @return the size, in bytes, of the digest produced by this message digest. + */ + public int getDigestSize(); + + /** + * update the message digest with a single byte. + * + * @param in the input byte to be entered. + */ + public void update(byte in); + + /** + * update the message digest with a block of bytes. + * + * @param in the byte array containing the data. + * @param inOff the offset into the byte array where the data starts. + * @param len the length of the data. + */ + public void update(byte[] in, int inOff, int len); + + /** + * close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param out the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + public int doFinal(byte[] out, int outOff); + + /** + * reset the digest back to it's initial state. + */ + public void reset(); +} + + +/** + * base implementation of MD4 family style digest as outlined in + * "Handbook of Applied Cryptography", pages 344 - 347. + */ +abstract class GeneralDigest + implements Digest +{ + private byte[] xBuf; + private int xBufOff; + + private long byteCount; + + /** + * Standard constructor + */ + protected GeneralDigest() + { + xBuf = new byte[4]; + xBufOff = 0; + } + + /** + * Copy constructor. We are using copy constructors in place + * of the Object.clone() interface as this interface is not + * supported by J2ME. + */ + protected GeneralDigest(GeneralDigest t) + { + xBuf = new byte[t.xBuf.length]; + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + } + + public void update( + byte in) + { + xBuf[xBufOff++] = in; + + if (xBufOff == xBuf.length) + { + processWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + public void update( + byte[] in, + int inOff, + int len) + { + // + // fill the current word + // + while ((xBufOff != 0) && (len > 0)) + { + update(in[inOff]); + + inOff++; + len--; + } + + // + // process whole words. + // + while (len > xBuf.length) + { + processWord(in, inOff); + + inOff += xBuf.length; + len -= xBuf.length; + byteCount += xBuf.length; + } + + // + // load in the remainder. + // + while (len > 0) + { + update(in[inOff]); + + inOff++; + len--; + } + } + + public void finish() + { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + update((byte)128); + + while (xBufOff != 0) + { + update((byte)0); + } + + processLength(bitLength); + + processBlock(); + } + + public void reset() + { + byteCount = 0; + + xBufOff = 0; + for ( int i = 0; i < xBuf.length; i++ ) { + xBuf[i] = 0; + } + } + + protected abstract void processWord(byte[] in, int inOff); + + protected abstract void processLength(long bitLength); + + protected abstract void processBlock(); +} + +/** + * implementation of MD2 + * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992 + */ +class MD2Digest + implements Digest +{ + private static final int DIGEST_LENGTH = 16; + + /* X buffer */ + private byte[] X = new byte[48]; + private int xOff; + /* M buffer */ + private byte[] M = new byte[16]; + private int mOff; + /* check sum */ + private byte[] C = new byte[16]; + private int COff; + + public MD2Digest() + { + reset(); + } + public MD2Digest(MD2Digest t) + { + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + System.arraycopy(t.M, 0, M, 0, t.M.length); + mOff = t.mOff; + System.arraycopy(t.C, 0, C, 0, t.C.length); + COff = t.COff; + } + /** + * return the algorithm name + * + * @return the algorithm name + */ + public String getAlgorithmName() + { + return "MD2"; + } + /** + * return the size, in bytes, of the digest produced by this message digest. + * + * @return the size, in bytes, of the digest produced by this message digest. + */ + public int getDigestSize() + { + return DIGEST_LENGTH; + } + /** + * close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param out the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + public int doFinal(byte[] out, int outOff) + { + // add padding + byte paddingByte = (byte)(M.length-mOff); + for (int i=mOff;i 0)) + { + update(in[inOff]); + inOff++; + len--; + } + + // + // process whole words. + // + while (len > 16) + { + System.arraycopy(in,inOff,M,0,16); + processCheckSum(M); + processBlock(M); + len -= 16; + inOff += 16; + } + + // + // load in the remainder. + // + while (len > 0) + { + update(in[inOff]); + inOff++; + len--; + } + } + protected void processCheckSum(byte[] m) + { + int L = C[15]; + for (int i=0;i<16;i++) + { + C[i] ^= S[(m[i] ^ L) & 0xff]; + L = C[i]; + } + } + protected void processBlock(byte[] m) + { + for (int i=0;i<16;i++) + { + X[i+16] = m[i]; + X[i+32] = (byte)(m[i] ^ X[i]); + } + // encrypt block + int t = 0; + + for (int j=0;j<18;j++) + { + for (int k=0;k<48;k++) + { + t = X[k] ^= S[t]; + t = t & 0xff; + } + t = (t + j)%256; + } + } + // 256-byte random permutation constructed from the digits of PI + private static final byte[] S = { + (byte)41,(byte)46,(byte)67,(byte)201,(byte)162,(byte)216,(byte)124, + (byte)1,(byte)61,(byte)54,(byte)84,(byte)161,(byte)236,(byte)240, + (byte)6,(byte)19,(byte)98,(byte)167,(byte)5,(byte)243,(byte)192, + (byte)199,(byte)115,(byte)140,(byte)152,(byte)147,(byte)43,(byte)217, + (byte)188,(byte)76,(byte)130,(byte)202,(byte)30,(byte)155,(byte)87, + (byte)60,(byte)253,(byte)212,(byte)224,(byte)22,(byte)103,(byte)66, + (byte)111,(byte)24,(byte)138,(byte)23,(byte)229,(byte)18,(byte)190, + (byte)78,(byte)196,(byte)214,(byte)218,(byte)158,(byte)222,(byte)73, + (byte)160,(byte)251,(byte)245,(byte)142,(byte)187,(byte)47,(byte)238, + (byte)122,(byte)169,(byte)104,(byte)121,(byte)145,(byte)21,(byte)178, + (byte)7,(byte)63,(byte)148,(byte)194,(byte)16,(byte)137,(byte)11, + (byte)34,(byte)95,(byte)33,(byte)128,(byte)127,(byte)93,(byte)154, + (byte)90,(byte)144,(byte)50,(byte)39,(byte)53,(byte)62,(byte)204, + (byte)231,(byte)191,(byte)247,(byte)151,(byte)3,(byte)255,(byte)25, + (byte)48,(byte)179,(byte)72,(byte)165,(byte)181,(byte)209,(byte)215, + (byte)94,(byte)146,(byte)42,(byte)172,(byte)86,(byte)170,(byte)198, + (byte)79,(byte)184,(byte)56,(byte)210,(byte)150,(byte)164,(byte)125, + (byte)182,(byte)118,(byte)252,(byte)107,(byte)226,(byte)156,(byte)116, + (byte)4,(byte)241,(byte)69,(byte)157,(byte)112,(byte)89,(byte)100, + (byte)113,(byte)135,(byte)32,(byte)134,(byte)91,(byte)207,(byte)101, + (byte)230,(byte)45,(byte)168,(byte)2,(byte)27,(byte)96,(byte)37, + (byte)173,(byte)174,(byte)176,(byte)185,(byte)246,(byte)28,(byte)70, + (byte)97,(byte)105,(byte)52,(byte)64,(byte)126,(byte)15,(byte)85, + (byte)71,(byte)163,(byte)35,(byte)221,(byte)81,(byte)175,(byte)58, + (byte)195,(byte)92,(byte)249,(byte)206,(byte)186,(byte)197,(byte)234, + (byte)38,(byte)44,(byte)83,(byte)13,(byte)110,(byte)133,(byte)40, + (byte)132, 9,(byte)211,(byte)223,(byte)205,(byte)244,(byte)65, + (byte)129,(byte)77,(byte)82,(byte)106,(byte)220,(byte)55,(byte)200, + (byte)108,(byte)193,(byte)171,(byte)250,(byte)36,(byte)225,(byte)123, + (byte)8,(byte)12,(byte)189,(byte)177,(byte)74,(byte)120,(byte)136, + (byte)149,(byte)139,(byte)227,(byte)99,(byte)232,(byte)109,(byte)233, + (byte)203,(byte)213,(byte)254,(byte)59,(byte)0,(byte)29,(byte)57, + (byte)242,(byte)239,(byte)183,(byte)14,(byte)102,(byte)88,(byte)208, + (byte)228,(byte)166,(byte)119,(byte)114,(byte)248,(byte)235,(byte)117, + (byte)75,(byte)10,(byte)49,(byte)68,(byte)80,(byte)180,(byte)143, + (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51, + (byte)159,(byte)17,(byte)131,(byte)20 + }; +} + + +/** + * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. + */ +class MD5Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 16; + + private int H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public MD5Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public MD5Digest(MD5Digest t) + { + super(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "MD5"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H1, out, outOff); + unpackWord(H2, out, outOff + 4); + unpackWord(H3, out, outOff + 8); + unpackWord(H4, out, outOff + 12); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private static final int S11 = 7; + private static final int S12 = 12; + private static final int S13 = 17; + private static final int S14 = 22; + + // + // round 2 left rotates + // + private static final int S21 = 5; + private static final int S22 = 9; + private static final int S23 = 14; + private static final int S24 = 20; + + // + // round 3 left rotates + // + private static final int S31 = 4; + private static final int S32 = 11; + private static final int S33 = 16; + private static final int S34 = 23; + + // + // round 4 left rotates + // + private static final int S41 = 6; + private static final int S42 = 10; + private static final int S43 = 15; + private static final int S44 = 21; + + /* + * rotate int x left n bits. + */ + private int rotateLeft( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * F, G, H and I are the basic MD5 functions. + */ + private int F( + int u, + int v, + int w) + { + return (u & v) | (~u & w); + } + + private int G( + int u, + int v, + int w) + { + return (u & w) | (v & ~w); + } + + private int H( + int u, + int v, + int w) + { + return u ^ v ^ w; + } + + private int K( + int u, + int v, + int w) + { + return v ^ (u | ~w); + } + + protected void processBlock() + { + int a = H1; + int b = H2; + int c = H3; + int d = H4; + + // + // Round 1 - F cycle, 16 times. + // + a = rotateLeft((a + F(b, c, d) + X[ 0] + 0xd76aa478), S11) + b; + d = rotateLeft((d + F(a, b, c) + X[ 1] + 0xe8c7b756), S12) + a; + c = rotateLeft((c + F(d, a, b) + X[ 2] + 0x242070db), S13) + d; + b = rotateLeft((b + F(c, d, a) + X[ 3] + 0xc1bdceee), S14) + c; + a = rotateLeft((a + F(b, c, d) + X[ 4] + 0xf57c0faf), S11) + b; + d = rotateLeft((d + F(a, b, c) + X[ 5] + 0x4787c62a), S12) + a; + c = rotateLeft((c + F(d, a, b) + X[ 6] + 0xa8304613), S13) + d; + b = rotateLeft((b + F(c, d, a) + X[ 7] + 0xfd469501), S14) + c; + a = rotateLeft((a + F(b, c, d) + X[ 8] + 0x698098d8), S11) + b; + d = rotateLeft((d + F(a, b, c) + X[ 9] + 0x8b44f7af), S12) + a; + c = rotateLeft((c + F(d, a, b) + X[10] + 0xffff5bb1), S13) + d; + b = rotateLeft((b + F(c, d, a) + X[11] + 0x895cd7be), S14) + c; + a = rotateLeft((a + F(b, c, d) + X[12] + 0x6b901122), S11) + b; + d = rotateLeft((d + F(a, b, c) + X[13] + 0xfd987193), S12) + a; + c = rotateLeft((c + F(d, a, b) + X[14] + 0xa679438e), S13) + d; + b = rotateLeft((b + F(c, d, a) + X[15] + 0x49b40821), S14) + c; + + // + // Round 2 - G cycle, 16 times. + // + a = rotateLeft((a + G(b, c, d) + X[ 1] + 0xf61e2562), S21) + b; + d = rotateLeft((d + G(a, b, c) + X[ 6] + 0xc040b340), S22) + a; + c = rotateLeft((c + G(d, a, b) + X[11] + 0x265e5a51), S23) + d; + b = rotateLeft((b + G(c, d, a) + X[ 0] + 0xe9b6c7aa), S24) + c; + a = rotateLeft((a + G(b, c, d) + X[ 5] + 0xd62f105d), S21) + b; + d = rotateLeft((d + G(a, b, c) + X[10] + 0x02441453), S22) + a; + c = rotateLeft((c + G(d, a, b) + X[15] + 0xd8a1e681), S23) + d; + b = rotateLeft((b + G(c, d, a) + X[ 4] + 0xe7d3fbc8), S24) + c; + a = rotateLeft((a + G(b, c, d) + X[ 9] + 0x21e1cde6), S21) + b; + d = rotateLeft((d + G(a, b, c) + X[14] + 0xc33707d6), S22) + a; + c = rotateLeft((c + G(d, a, b) + X[ 3] + 0xf4d50d87), S23) + d; + b = rotateLeft((b + G(c, d, a) + X[ 8] + 0x455a14ed), S24) + c; + a = rotateLeft((a + G(b, c, d) + X[13] + 0xa9e3e905), S21) + b; + d = rotateLeft((d + G(a, b, c) + X[ 2] + 0xfcefa3f8), S22) + a; + c = rotateLeft((c + G(d, a, b) + X[ 7] + 0x676f02d9), S23) + d; + b = rotateLeft((b + G(c, d, a) + X[12] + 0x8d2a4c8a), S24) + c; + + // + // Round 3 - H cycle, 16 times. + // + a = rotateLeft((a + H(b, c, d) + X[ 5] + 0xfffa3942), S31) + b; + d = rotateLeft((d + H(a, b, c) + X[ 8] + 0x8771f681), S32) + a; + c = rotateLeft((c + H(d, a, b) + X[11] + 0x6d9d6122), S33) + d; + b = rotateLeft((b + H(c, d, a) + X[14] + 0xfde5380c), S34) + c; + a = rotateLeft((a + H(b, c, d) + X[ 1] + 0xa4beea44), S31) + b; + d = rotateLeft((d + H(a, b, c) + X[ 4] + 0x4bdecfa9), S32) + a; + c = rotateLeft((c + H(d, a, b) + X[ 7] + 0xf6bb4b60), S33) + d; + b = rotateLeft((b + H(c, d, a) + X[10] + 0xbebfbc70), S34) + c; + a = rotateLeft((a + H(b, c, d) + X[13] + 0x289b7ec6), S31) + b; + d = rotateLeft((d + H(a, b, c) + X[ 0] + 0xeaa127fa), S32) + a; + c = rotateLeft((c + H(d, a, b) + X[ 3] + 0xd4ef3085), S33) + d; + b = rotateLeft((b + H(c, d, a) + X[ 6] + 0x04881d05), S34) + c; + a = rotateLeft((a + H(b, c, d) + X[ 9] + 0xd9d4d039), S31) + b; + d = rotateLeft((d + H(a, b, c) + X[12] + 0xe6db99e5), S32) + a; + c = rotateLeft((c + H(d, a, b) + X[15] + 0x1fa27cf8), S33) + d; + b = rotateLeft((b + H(c, d, a) + X[ 2] + 0xc4ac5665), S34) + c; + + // + // Round 4 - K cycle, 16 times. + // + a = rotateLeft((a + K(b, c, d) + X[ 0] + 0xf4292244), S41) + b; + d = rotateLeft((d + K(a, b, c) + X[ 7] + 0x432aff97), S42) + a; + c = rotateLeft((c + K(d, a, b) + X[14] + 0xab9423a7), S43) + d; + b = rotateLeft((b + K(c, d, a) + X[ 5] + 0xfc93a039), S44) + c; + a = rotateLeft((a + K(b, c, d) + X[12] + 0x655b59c3), S41) + b; + d = rotateLeft((d + K(a, b, c) + X[ 3] + 0x8f0ccc92), S42) + a; + c = rotateLeft((c + K(d, a, b) + X[10] + 0xffeff47d), S43) + d; + b = rotateLeft((b + K(c, d, a) + X[ 1] + 0x85845dd1), S44) + c; + a = rotateLeft((a + K(b, c, d) + X[ 8] + 0x6fa87e4f), S41) + b; + d = rotateLeft((d + K(a, b, c) + X[15] + 0xfe2ce6e0), S42) + a; + c = rotateLeft((c + K(d, a, b) + X[ 6] + 0xa3014314), S43) + d; + b = rotateLeft((b + K(c, d, a) + X[13] + 0x4e0811a1), S44) + c; + a = rotateLeft((a + K(b, c, d) + X[ 4] + 0xf7537e82), S41) + b; + d = rotateLeft((d + K(a, b, c) + X[11] + 0xbd3af235), S42) + a; + c = rotateLeft((c + K(d, a, b) + X[ 2] + 0x2ad7d2bb), S43) + d; + b = rotateLeft((b + K(c, d, a) + X[ 9] + 0xeb86d391), S44) + c; + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } +} + + +/** + * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349. + * + * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5 + * is the "endienness" of the word processing! + */ +class SHA1Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 20; + + private int H1, H2, H3, H4, H5; + + private int[] X = new int[80]; + private int xOff; + + /** + * Standard constructor + */ + public SHA1Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA1Digest(SHA1Digest t) + { + super(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SHA-1"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16) + | ((in[inOff + 2] & 0xff) << 8) | ((in[inOff + 3] & 0xff)); + + if (xOff == 16) + { + processBlock(); + } + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)(word >>> 24); + out[outOff + 1] = (byte)(word >>> 16); + out[outOff + 2] = (byte)(word >>> 8); + out[outOff + 3] = (byte)word; + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H1, out, outOff); + unpackWord(H2, out, outOff + 4); + unpackWord(H3, out, outOff + 8); + unpackWord(H4, out, outOff + 12); + unpackWord(H5, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + H5 = 0xc3d2e1f0; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // Additive constants + // + private static final int Y1 = 0x5a827999; + private static final int Y2 = 0x6ed9eba1; + private static final int Y3 = 0x8f1bbcdc; + private static final int Y4 = 0xca62c1d6; + + private int f( + int u, + int v, + int w) + { + return ((u & v) | ((~u) & w)); + } + + private int h( + int u, + int v, + int w) + { + return (u ^ v ^ w); + } + + private int g( + int u, + int v, + int w) + { + return ((u & v) | (u & w) | (v & w)); + } + + private int rotateLeft( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + protected void processBlock() + { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i <= 79; i++) + { + X[i] = rotateLeft((X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]), 1); + } + + // + // set up working variables. + // + int A = H1; + int B = H2; + int C = H3; + int D = H4; + int E = H5; + + // + // round 1 + // + for (int j = 0; j <= 19; j++) + { + int t = rotateLeft(A, 5) + f(B, C, D) + E + X[j] + Y1; + + E = D; + D = C; + C = rotateLeft(B, 30); + B = A; + A = t; + } + + // + // round 2 + // + for (int j = 20; j <= 39; j++) + { + int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y2; + + E = D; + D = C; + C = rotateLeft(B, 30); + B = A; + A = t; + } + + // + // round 3 + // + for (int j = 40; j <= 59; j++) + { + int t = rotateLeft(A, 5) + g(B, C, D) + E + X[j] + Y3; + + E = D; + D = C; + C = rotateLeft(B, 30); + B = A; + A = t; + } + + // + // round 4 + // + for (int j = 60; j <= 79; j++) + { + int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y4; + + E = D; + D = C; + C = rotateLeft(B, 30); + B = A; + A = t; + } + + H1 += A; + H2 += B; + H3 += C; + H4 += D; + H5 += E; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } +} + + + +/** + * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this + * depends on your application - see PKCS1 Version 2 for details. + */ +class PKCS1Encoding + implements AsymmetricBlockCipher +{ + private static int HEADER_LENGTH = 10; + + private Random random; + private AsymmetricBlockCipher engine; + private boolean forEncryption; + private boolean forPrivateKey; + + public PKCS1Encoding( + AsymmetricBlockCipher cipher) + { + this.engine = cipher; + } + + public AsymmetricBlockCipher getUnderlyingCipher() + { + return engine; + } + + public void init( + boolean forEncryption, + CipherParameters param) + { + AsymmetricKeyParameter kParam; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + this.random = rParam.getRandom(); + kParam = (AsymmetricKeyParameter)rParam.getParameters(); + } + else + { + this.random = new Random(); + kParam = (AsymmetricKeyParameter)param; + } + + engine.init(forEncryption, kParam); + + this.forPrivateKey = kParam.isPrivate(); + this.forEncryption = forEncryption; + } + + public int getInputBlockSize() + { + int baseBlockSize = engine.getInputBlockSize(); + + if (forEncryption) + { + return baseBlockSize - HEADER_LENGTH; + } + else + { + return baseBlockSize; + } + } + + public int getOutputBlockSize() + { + int baseBlockSize = engine.getOutputBlockSize(); + + if (forEncryption) + { + return baseBlockSize; + } + else + { + return baseBlockSize - HEADER_LENGTH; + } + } + + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + if (forEncryption) + { + return encodeBlock(in, inOff, inLen); + } + else + { + return decodeBlock(in, inOff, inLen); + } + } + + private byte[] encodeBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + byte[] block = new byte[engine.getInputBlockSize()]; + + if (forPrivateKey) + { + block[0] = 0x01; // type code 1 + + for (int i = 1; i != block.length - inLen - 1; i++) + { + block[i] = (byte)0xFF; + } + } + else + { + random.nextBytes(block); // random fill + + block[0] = 0x02; // type code 2 + + // + // a zero byte marks the end of the padding, so all + // the pad bytes must be non-zero. + // + for (int i = 1; i != block.length - inLen - 1; i++) + { + while (block[i] == 0) + { + block[i] = (byte)random.nextInt(); + } + } + } + + block[block.length - inLen - 1] = 0x00; // mark the end of the padding + System.arraycopy(in, inOff, block, block.length - inLen, inLen); + + return engine.processBlock(block, 0, block.length); + } + + /** + * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format. + */ + private byte[] decodeBlock( + byte[] in, + int inOff, + int inLen) + throws InvalidCipherTextException + { + byte[] block = engine.processBlock(in, inOff, inLen); + + if (block.length < getOutputBlockSize()) + { + throw new InvalidCipherTextException("block truncated"); + } + + if (block[0] != 1 && block[0] != 2) + { + throw new InvalidCipherTextException("unknown block type"); + } + + // + // find and extract the message block. + // + int start; + + for (start = 1; start != block.length; start++) + { + if (block[start] == 0) + { + break; + } + } + + start++; // data should start at the next byte + + if (start >= block.length || start < HEADER_LENGTH) + { + throw new InvalidCipherTextException("no data in block"); + } + + byte[] result = new byte[block.length - start]; + + System.arraycopy(block, start, result, 0, result.length); + + return result; + } +} + + +class RC4Engine implements StreamCipher +{ + private final static int STATE_LENGTH = 256; + + /* + * variables to hold the state of the RC4 engine + * during encryption and decryption + */ + + private byte[] engineState = null; + private int x = 0; + private int y = 0; + private byte[] workingKey = null; + + /** + * initialise a RC4 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params + ) + { + if (params instanceof KeyParameter) + { + /* + * RC4 encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. + */ + workingKey = ((KeyParameter)params).getKey(); + setKey(workingKey); + + return; + } + + throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "RC4"; + } + + public byte returnByte(byte in) + { + x = (x + 1) & 0xff; + y = (engineState[x] + y) & 0xff; + + // swap + byte tmp = engineState[x]; + engineState[x] = engineState[y]; + engineState[y] = tmp; + + // xor + return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]); + } + + public void processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff + ) + { + if ((inOff + len) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + + for (int i = 0; i < len ; i++) + { + x = (x + 1) & 0xff; + y = (engineState[x] + y) & 0xff; + + // swap + byte tmp = engineState[x]; + engineState[x] = engineState[y]; + engineState[y] = tmp; + + // xor + out[i+outOff] = (byte)(in[i + inOff] + ^ engineState[(engineState[x] + engineState[y]) & 0xff]); + } + } + + public void reset() + { + setKey(workingKey); + } + + // Private implementation + + private void setKey(byte[] keyBytes) + { + workingKey = keyBytes; + + // System.out.println("the key length is ; "+ workingKey.length); + + x = 0; + y = 0; + + if (engineState == null) + { + engineState = new byte[STATE_LENGTH]; + } + + // reset the state of the engine + for (int i=0; i < STATE_LENGTH; i++) + { + engineState[i] = (byte)i; + } + + int i1 = 0; + int i2 = 0; + + for (int i=0; i < STATE_LENGTH; i++) + { + i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff; + // do the byte-swap inline + byte tmp = engineState[i]; + engineState[i] = engineState[i2]; + engineState[i2] = tmp; + i1 = (i1+1) % keyBytes.length; + } + } +} + + + +/** + * this does your basic RSA algorithm. + */ +class RSAEngine + implements AsymmetricBlockCipher +{ + private RSAKeyParameters key; + private boolean forEncryption; + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void init( + boolean forEncryption, + CipherParameters param) + { + this.key = (RSAKeyParameters)param; + this.forEncryption = forEncryption; + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize() + { + int bitSize = key.getModulus().bitLength(); + + if (forEncryption) + { + return (bitSize + 7) / 8 - 1; + } + else + { + return (bitSize + 7) / 8; + } + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int getOutputBlockSize() + { + int bitSize = key.getModulus().bitLength(); + + if (forEncryption) + { + return (bitSize + 7) / 8; + } + else + { + return (bitSize + 7) / 8 - 1; + } + } + + /** + * Process a single block using the basic RSA algorithm. + * + * @param in the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param inLen the length of the data to be processed. + * @return the result of the RSA process. + * @exception DataLengthException the input block is too large. + */ + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + { + if (inLen > (getInputBlockSize() + 1)) + { + throw new DataLengthException("input too large for RSA cipher.\n"); + } + else if (inLen == (getInputBlockSize() + 1) && (in[inOff] & 0x80) != 0) + { + throw new DataLengthException("input too large for RSA cipher.\n"); + } + + byte[] block; + + if (inOff != 0 || inLen != in.length) + { + block = new byte[inLen]; + + System.arraycopy(in, inOff, block, 0, inLen); + } + else + { + block = in; + } + + BigInteger input = new BigInteger(1, block); + byte[] output; + + if (key instanceof RSAPrivateCrtKeyParameters) + { + // + // we have the extra factors, use the Chinese Remainder Theorem - the author + // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for + // advice regarding the expression of this. + // + RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key; + + BigInteger d = crtKey.getExponent(); + BigInteger p = crtKey.getP(); + BigInteger q = crtKey.getQ(); + BigInteger dP = crtKey.getDP(); + BigInteger dQ = crtKey.getDQ(); + BigInteger qInv = crtKey.getQInv(); + + BigInteger mP, mQ, h, m; + + // mP = ((input mod p) ^ dP)) mod p + mP = (input.remainder(p)).modPow(dP, p); + + // mQ = ((input mod q) ^ dQ)) mod q + mQ = (input.remainder(q)).modPow(dQ, q); + + // h = qInv * (mP - mQ) mod p + h = mP.subtract(mQ); + h = h.multiply(qInv); + h = h.mod(p); // mod (in Java) returns the positive residual + + // m = h * q + mQ + m = h.multiply(q); + m = m.add(mQ); + + output = m.toByteArray(); + } + else + { + output = input.modPow( + key.getExponent(), key.getModulus()).toByteArray(); + } + + if (forEncryption) + { + if (output[0] == 0 && output.length > getOutputBlockSize()) // have ended up with an extra zero byte, copy down. + { + byte[] tmp = new byte[output.length - 1]; + + System.arraycopy(output, 1, tmp, 0, tmp.length); + + return tmp; + } + + if (output.length < getOutputBlockSize()) // have ended up with less bytes than normal, lengthen + { + byte[] tmp = new byte[getOutputBlockSize()]; + + System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length); + + return tmp; + } + } + else + { + if (output[0] == 0) // have ended up with an extra zero byte, copy down. + { + byte[] tmp = new byte[output.length - 1]; + + System.arraycopy(output, 1, tmp, 0, tmp.length); + + return tmp; + } + } + return output; + } +} + +/** + * this exception is thrown whenever we find something we don't expect in a + * message. + */ +class InvalidCipherTextException + extends CryptoException +{ + /** + * base constructor. + */ + public InvalidCipherTextException() + { + } + + /** + * create a InvalidCipherTextException with the given message. + * + * @param message the message to be carried with the exception. + */ + public InvalidCipherTextException( + String message) + { + super(message); + } +} + + + +class DigestInputStream + extends FilterInputStream +{ + protected Digest digest; + + public DigestInputStream( + InputStream stream, + Digest digest) + { + super(stream); + this.digest = digest; + } + + public int read() + throws IOException + { + int b = in.read(); + + if (b >= 0) + { + digest.update((byte)b); + } + return b; + } + + public int read( + byte[] b, + int off, + int len) + throws IOException + { + int n = in.read(b, off, len); + if (n > 0) + { + digest.update(b, off, n); + } + return n; + } + + public Digest getDigest() + { + return digest; + } +} + + + +class DigestOutputStream + extends FilterOutputStream +{ + protected Digest digest; + + public DigestOutputStream( + OutputStream stream, + Digest digest) + { + super(stream); + this.digest = digest; + } + + public void write(int b) + throws IOException + { + digest.update((byte)b); + out.write(b); + } + + public void write( + byte[] b, + int off, + int len) + throws IOException + { + digest.update(b, off, len); + out.write(b, off, len); + } + + public Digest getDigest() + { + return digest; + } +} + + +/** + * The base class for parameters to key generators. + */ +class KeyGenerationParameters +{ + private Random random; + private int strength; + + /** + * initialise the generator with a source of randomness + * and a strength (in bits). + * + * @param random the random byte source. + * @param strength the size, in bits, of the keys we want to produce. + */ + public KeyGenerationParameters( + Random random, + int strength) + { + this.random = random; + this.strength = strength; + } + + /** + * return the random source associated with this + * generator. + * + * @return the generators random source. + */ + public Random getRandom() + { + return random; + } + + /** + * return the bit strength for keys produced by this generator, + * + * @return the strength of the keys this generator produces (in bits). + */ + public int getStrength() + { + return strength; + } +} + + +class AsymmetricKeyParameter + implements CipherParameters +{ + boolean privateKey; + + public AsymmetricKeyParameter( + boolean privateKey) + { + this.privateKey = privateKey; + } + + public boolean isPrivate() + { + return privateKey; + } +} + + +class KeyParameter + implements CipherParameters +{ + private byte[] key; + + public KeyParameter( + byte[] key) + { + this(key, 0, key.length); + } + + public KeyParameter( + byte[] key, + int keyOff, + int keyLen) + { + this.key = new byte[keyLen]; + + System.arraycopy(key, keyOff, this.key, 0, keyLen); + } + + public byte[] getKey() + { + return key; + } +} + + + +class ParametersWithRandom + implements CipherParameters +{ + private Random random; + private CipherParameters parameters; + + public ParametersWithRandom( + CipherParameters parameters, + Random random) + { + this.random = random; + this.parameters = parameters; + } + + public ParametersWithRandom( + CipherParameters parameters) + { + this.random = null; + this.parameters = parameters; + } + + public Random getRandom() + { + if (random == null) + { + random = new Random(); + } + return random; + } + + public CipherParameters getParameters() + { + return parameters; + } +} + + + +class RSAKeyParameters + extends AsymmetricKeyParameter +{ + private BigInteger modulus; + private BigInteger exponent; + + public RSAKeyParameters( + boolean isPrivate, + BigInteger modulus, + BigInteger exponent) + { + super(isPrivate); + + this.modulus = modulus; + this.exponent = exponent; + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getExponent() + { + return exponent; + } +} + + +class RSAPrivateCrtKeyParameters + extends RSAKeyParameters +{ + private BigInteger e; + private BigInteger p; + private BigInteger q; + private BigInteger dP; + private BigInteger dQ; + private BigInteger qInv; + + /** + * + */ + public RSAPrivateCrtKeyParameters( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger p, + BigInteger q, + BigInteger dP, + BigInteger dQ, + BigInteger qInv) + { + super(true, modulus, privateExponent); + + this.e = publicExponent; + this.p = p; + this.q = q; + this.dP = dP; + this.dQ = dQ; + this.qInv = qInv; + } + + public BigInteger getPublicExponent() + { + return e; + } + + public BigInteger getP() + { + return p; + } + + public BigInteger getQ() + { + return q; + } + + public BigInteger getDP() + { + return dP; + } + + public BigInteger getDQ() + { + return dQ; + } + + public BigInteger getQInv() + { + return qInv; + } +} + +/** + * the foundation class for the exceptions thrown by the crypto packages. + */ +class RuntimeCryptoException + extends RuntimeException +{ + /** + * base constructor. + */ + public RuntimeCryptoException() + { + } + + /** + * create a RuntimeCryptoException with the given message. + * + * @param message the message to be carried with the exception. + */ + public RuntimeCryptoException( + String message) + { + super(message); + } +} + +/** + * a wrapper for block ciphers with a single byte block size, so that they + * can be treated like stream ciphers. + */ +class StreamBlockCipher + implements StreamCipher +{ + private BlockCipher cipher; + + private byte[] oneByte = new byte[1]; + + /** + * basic constructor. + * + * @param cipher the block cipher to be wrapped. + * @exception IllegalArgumentException if the cipher has a block size other than + * one. + */ + public StreamBlockCipher( + BlockCipher cipher) + { + if (cipher.getBlockSize() != 1) + { + throw new IllegalArgumentException("block cipher block size != 1."); + } + + this.cipher = cipher; + } + + /** + * initialise the underlying cipher. + * + * @param forEncryption true if we are setting up for encryption, false otherwise. + * @param param the necessary parameters for the underlying cipher to be initialised. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + cipher.init(forEncryption, params); + } + + /** + * return the name of the algorithm we are wrapping. + * + * @return the name of the algorithm we are wrapping. + */ + public String getAlgorithmName() + { + return cipher.getAlgorithmName(); + } + + /** + * encrypt/decrypt a single byte returning the result. + * + * @param in the byte to be processed. + * @return the result of processing the input byte. + */ + public byte returnByte( + byte in) + { + oneByte[0] = in; + + cipher.processBlock(oneByte, 0, oneByte, 0); + + return oneByte[0]; + } + + /** + * process a block of bytes from in putting the result into out. + * + * @param in the input byte array. + * @param inOff the offset into the in array where the data to be processed starts. + * @param len the number of bytes to be processed. + * @param out the output buffer the processed bytes go into. + * @param outOff the offset into the output byte array the processed data stars at. + * @exception DataLengthException if the output buffer is too small. + */ + public void processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + throws DataLengthException + { + if (outOff + len > out.length) + { + throw new DataLengthException("output buffer too small in processBytes()"); + } + + for (int i = 0; i != len; i++) + { + cipher.processBlock(in, inOff + i, out, outOff + i); + } + } + + /** + * reset the underlying cipher. This leaves it in the same state + * it was at after the last init (if there was one). + */ + public void reset() + { + cipher.reset(); + } +} + +/** + * the interface stream ciphers conform to. + */ +interface StreamCipher +{ + /** + * Initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public String getAlgorithmName(); + + /** + * encrypt/decrypt a single byte returning the result. + * + * @param in the byte to be processed. + * @return the result of processing the input byte. + */ + public byte returnByte(byte in); + + /** + * process a block of bytes from in putting the result into out. + * + * @param in the input byte array. + * @param inOff the offset into the in array where the data to be processed starts. + * @param len the number of bytes to be processed. + * @param out the output buffer the processed bytes go into. + * @param outOff the offset into the output byte array the processed data stars at. + * @exception DataLengthException if the output buffer is too small. + */ + public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException; + + /** + * reset the cipher. This leaves it in the same state + * it was at after the last init (if there was one). + */ + public void reset(); +} + +class Base64 +{ + private static final byte[] encodingTable = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', + (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', + (byte)'7', (byte)'8', (byte)'9', + (byte)'+', (byte)'/' + }; + + /** + * encode the input data producong a base 64 encoded byte array. + * + * @return a byte array containing the base 64 encoded data. + */ + public static byte[] encode( + byte[] data) + { + byte[] bytes; + + int modulus = data.length % 3; + if (modulus == 0) + { + bytes = new byte[4 * data.length / 3]; + } + else + { + bytes = new byte[4 * ((data.length / 3) + 1)]; + } + + int dataLength = (data.length - modulus); + int a1, a2, a3; + for (int i = 0, j = 0; i < dataLength; i += 3, j += 4) + { + a1 = data[i] & 0xff; + a2 = data[i + 1] & 0xff; + a3 = data[i + 2] & 0xff; + + bytes[j] = encodingTable[(a1 >>> 2) & 0x3f]; + bytes[j + 1] = encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]; + bytes[j + 2] = encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]; + bytes[j + 3] = encodingTable[a3 & 0x3f]; + } + + /* + * process the tail end. + */ + int b1, b2, b3; + int d1, d2; + + switch (modulus) + { + case 0: /* nothing left to do */ + break; + case 1: + d1 = data[data.length - 1] & 0xff; + b1 = (d1 >>> 2) & 0x3f; + b2 = (d1 << 4) & 0x3f; + + bytes[bytes.length - 4] = encodingTable[b1]; + bytes[bytes.length - 3] = encodingTable[b2]; + bytes[bytes.length - 2] = (byte)'='; + bytes[bytes.length - 1] = (byte)'='; + break; + case 2: + d1 = data[data.length - 2] & 0xff; + d2 = data[data.length - 1] & 0xff; + + b1 = (d1 >>> 2) & 0x3f; + b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f; + b3 = (d2 << 2) & 0x3f; + + bytes[bytes.length - 4] = encodingTable[b1]; + bytes[bytes.length - 3] = encodingTable[b2]; + bytes[bytes.length - 2] = encodingTable[b3]; + bytes[bytes.length - 1] = (byte)'='; + break; + } + + return bytes; + } + + /* + * set up the decoding table. + */ + private static final byte[] decodingTable; + + static + { + decodingTable = new byte[128]; + + for (int i = 'A'; i <= 'Z'; i++) + { + decodingTable[i] = (byte)(i - 'A'); + } + + for (int i = 'a'; i <= 'z'; i++) + { + decodingTable[i] = (byte)(i - 'a' + 26); + } + + for (int i = '0'; i <= '9'; i++) + { + decodingTable[i] = (byte)(i - '0' + 52); + } + + decodingTable['+'] = 62; + decodingTable['/'] = 63; + } + + /** + * decode the base 64 encoded input data. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode( + byte[] data) + { + byte[] bytes; + byte b1, b2, b3, b4; + + if (data[data.length - 2] == '=') + { + bytes = new byte[(((data.length / 4) - 1) * 3) + 1]; + } + else if (data[data.length - 1] == '=') + { + bytes = new byte[(((data.length / 4) - 1) * 3) + 2]; + } + else + { + bytes = new byte[((data.length / 4) * 3)]; + } + + for (int i = 0, j = 0; i < data.length - 4; i += 4, j += 3) + { + b1 = decodingTable[data[i]]; + b2 = decodingTable[data[i + 1]]; + b3 = decodingTable[data[i + 2]]; + b4 = decodingTable[data[i + 3]]; + + bytes[j] = (byte)((b1 << 2) | (b2 >> 4)); + bytes[j + 1] = (byte)((b2 << 4) | (b3 >> 2)); + bytes[j + 2] = (byte)((b3 << 6) | b4); + } + + if (data[data.length - 2] == '=') + { + b1 = decodingTable[data[data.length - 4]]; + b2 = decodingTable[data[data.length - 3]]; + + bytes[bytes.length - 1] = (byte)((b1 << 2) | (b2 >> 4)); + } + else if (data[data.length - 1] == '=') + { + b1 = decodingTable[data[data.length - 4]]; + b2 = decodingTable[data[data.length - 3]]; + b3 = decodingTable[data[data.length - 2]]; + + bytes[bytes.length - 2] = (byte)((b1 << 2) | (b2 >> 4)); + bytes[bytes.length - 1] = (byte)((b2 << 4) | (b3 >> 2)); + } + else + { + b1 = decodingTable[data[data.length - 4]]; + b2 = decodingTable[data[data.length - 3]]; + b3 = decodingTable[data[data.length - 2]]; + b4 = decodingTable[data[data.length - 1]]; + + bytes[bytes.length - 3] = (byte)((b1 << 2) | (b2 >> 4)); + bytes[bytes.length - 2] = (byte)((b2 << 4) | (b3 >> 2)); + bytes[bytes.length - 1] = (byte)((b3 << 6) | b4); + } + + return bytes; + } + + /** + * decode the base 64 encoded String data. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode( + String data) + { + byte[] bytes; + byte b1, b2, b3, b4; + + if (data.charAt(data.length() - 2) == '=') + { + bytes = new byte[(((data.length() / 4) - 1) * 3) + 1]; + } + else if (data.charAt(data.length() - 1) == '=') + { + bytes = new byte[(((data.length() / 4) - 1) * 3) + 2]; + } + else + { + bytes = new byte[((data.length() / 4) * 3)]; + } + + for (int i = 0, j = 0; i < data.length() - 4; i += 4, j += 3) + { + b1 = decodingTable[data.charAt(i)]; + b2 = decodingTable[data.charAt(i + 1)]; + b3 = decodingTable[data.charAt(i + 2)]; + b4 = decodingTable[data.charAt(i + 3)]; + + bytes[j] = (byte)((b1 << 2) | (b2 >> 4)); + bytes[j + 1] = (byte)((b2 << 4) | (b3 >> 2)); + bytes[j + 2] = (byte)((b3 << 6) | b4); + } + + if (data.charAt(data.length() - 2) == '=') + { + b1 = decodingTable[data.charAt(data.length() - 4)]; + b2 = decodingTable[data.charAt(data.length() - 3)]; + + bytes[bytes.length - 1] = (byte)((b1 << 2) | (b2 >> 4)); + } + else if (data.charAt(data.length() - 1) == '=') + { + b1 = decodingTable[data.charAt(data.length() - 4)]; + b2 = decodingTable[data.charAt(data.length() - 3)]; + b3 = decodingTable[data.charAt(data.length() - 2)]; + + bytes[bytes.length - 2] = (byte)((b1 << 2) | (b2 >> 4)); + bytes[bytes.length - 1] = (byte)((b2 << 4) | (b3 >> 2)); + } + else + { + b1 = decodingTable[data.charAt(data.length() - 4)]; + b2 = decodingTable[data.charAt(data.length() - 3)]; + b3 = decodingTable[data.charAt(data.length() - 2)]; + b4 = decodingTable[data.charAt(data.length() - 1)]; + + bytes[bytes.length - 3] = (byte)((b1 << 2) | (b2 >> 4)); + bytes[bytes.length - 2] = (byte)((b2 << 4) | (b3 >> 2)); + bytes[bytes.length - 1] = (byte)((b3 << 6) | b4); + } + + return bytes; + } +} diff --git a/src/org/xwt/shoehorn3/ShoeHorn.java b/src/org/xwt/shoehorn3/ShoeHorn.java new file mode 100644 index 0000000..f7bc319 --- /dev/null +++ b/src/org/xwt/shoehorn3/ShoeHorn.java @@ -0,0 +1,628 @@ +// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] +package org.xwt.shoehorn3; + +import java.applet.*; +import java.util.*; +import java.lang.reflect.*; +import java.io.*; +import java.net.*; +import java.util.zip.*; +import java.awt.*; +import java.math.*; + +/** This class is XWT's presence on the user's computer; it must be run as trusted code */ +public class ShoeHorn extends Applet { + + // Startup Phase //////////////////////////////////////////////////////////////////// + private String build = null; + public ShoeHorn() { log("*** constructor invoked for " + this.getClass().getName()); } + + public final String getParameter(String arg) { return super.getParameter(arg); } + public final void main(String[] s) { new ShoeHorn().start(); } + private void log(String s) { System.out.println(s); } + + /** this just ensures that we are running with full privileges */ + public final void start() { + build = getParameter("build"); + new Thread() { public void run() { + log("ShoeHorn thread spawned"); + try { + + if (System.getProperty("java.vendor", "").startsWith("Netscape")) { + log("Detected Navigator 4.x"); + Method m = Class.forName("netscape.security.PrivilegeManager").getMethod("enablePrivilege", new Class[] { String.class }); + m.invoke(null, new Object[] { "MarimbaInternalTarget" }); + m.invoke(null, new Object[] { "UniversalExecAccess" }); + m.invoke(null, new Object[] { "UniversalPropertyRead" }); + go(); + + } else if (System.getProperty("java.vendor", "").startsWith("Microsoft")) { + //com.ms.security.PolicyEngine.assertPermission(com.ms.security.PermissionID.SYSTEM); + Class permissionIdClass = Class.forName("com.ms.security.PermissionID"); + Object o = permissionIdClass.getField("SYSTEM").get(null); + Method m = Class.forName("com.ms.security.PolicyEngine").getMethod("assertPermission", new Class[] { permissionIdClass }); + m.invoke(null, new Object[] { o }); + go(); + + } else { + log("Detected non-Navigator JVM"); + Method m = Class.forName("org.xwt.shoehorn3.ShoeHorn$Java12").getMethod("run", new Class[] { Object.class }); + m.invoke(null, new Object[] { ShoeHorn.this }); + } + } catch (Throwable e) { + if (e instanceof InvocationTargetException) e = ((InvocationTargetException)e).getTargetException(); + e.printStackTrace(); + update(-1.0, "Error; please check the Java console"); + } + } }.start(); + } + + /** ask Java Plugin for privs */ + private static class Java12 { + public static void run(final Object a) { + java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { + public Object run() { + ((ShoeHorn)a).go(); + return null; + } + }); + } + } + + + // Implantation //////////////////////////////////////////////////////////////////// + + /** inserts the required entries into the user's ~/.java.policy */ + private void modifyPolicyFile() throws IOException { + log("Adjusting ~/.java.policy"); + File policy = new File(System.getProperty("user.home") + File.separatorChar + ".java.policy"); + if (policy.exists()) { + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(policy))); + String s = null; + while((s = br.readLine()) != null) + if (s.startsWith("// XWT_MARKER:")) { + log("Java policy file has already been adjusted"); + return; + } + } + FileOutputStream fos = new FileOutputStream(policy.getAbsolutePath(), true); + PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos)); + pw.println(""); + pw.println("// XWT_MARKER: this line and the following two grant blocks are required for XWT; DO NOT REMOVE THEM."); + pw.println("grant {"); + pw.println(" permission java.io.FilePermission \"${user.home}${/}.xwt${/}shoehorn.jar\", \"read\";"); + pw.println("};"); + pw.println("grant codebase \"file:${user.home}${/}.xwt${/}shoehorn.jar\" {"); + pw.println(" permission java.security.AllPermission;"); + pw.println("};"); + pw.println("// END_XWT_MARKER"); + pw.println(""); + pw.flush(); + pw.close(); + } + + /** read ourselves out of the resources and write a jarfile to some trusted place */ + private void implantSelf() throws IOException { + InputStream manifest = getClass().getClassLoader().getResourceAsStream("META-INF/manifest.mf"); + log("my classloader is " + getClass().getClassLoader().getClass().getName()); + ClassLoader loader = getClass().getClassLoader(); + if (manifest == null || loader == null || + (loader.getClass().getName().indexOf("Applet") == -1 && loader.getClass().getName().indexOf("Plugin") == -1)) + return; + BufferedReader br = new BufferedReader(new InputStreamReader(manifest)); + Vector entries = new Vector(); + String s = null; + while((s = br.readLine()) != null) + if (s.startsWith("Name: ")) + entries.addElement(s.substring(6)); + + String ext_dirs = System.getProperty("java.ext.dirs"); + log("java.ext.dirs = " + ext_dirs); + ext_dirs = ext_dirs + File.pathSeparatorChar + System.getProperty("user.home") + File.separatorChar + ".xwt"; + StringTokenizer st = new StringTokenizer(ext_dirs, File.pathSeparatorChar + ""); + while(st.hasMoreTokens()) { + String dir = st.nextToken(); + new File(dir).mkdirs(); + try { + // we have to modify the policy file BEFORE implanting in ~/.xwt to ensure that the policy mods work + if (!st.hasMoreTokens()) modifyPolicyFile(); + implantInDirectory(dir, entries); + return; + } catch (IOException e) { + log("Failed to implant in " + dir + " due to " + e); + } + } + log("Failed to implant self!"); + } + + private void implantInDirectory(String dir, Vector entries) throws IOException { + File f = new File(dir + File.separatorChar + "shoehorn.tmp"); + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f)); + for(int i=0; i -1) { + p = Runtime.getRuntime().exec("command.com /c set"); + } else if ((os.indexOf("nt") > -1) || (os.indexOf("windows 2000") > -1) ) { + p = Runtime.getRuntime().exec("cmd.exe /c set"); + } else { + p = Runtime.getRuntime().exec("env"); + } + BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); + String s; + while ((s = br.readLine()) != null) v.addElement(s); + v.addElement(newvar); + String[] ret; + v.copyInto(ret = new String[v.size()]); + return ret; + } + + + + // Applet Painting Functions //////////////////////////////////////////////////////// + + private Image backbuffer = null; + public final void paint(Graphics g) { if (backbuffer != null) g.drawImage(backbuffer, 0, 0, null); } + + public final Graphics getGraphics() { return super.getGraphics(); } + public final Dimension getSize() { return super.getSize(); } + + private void update(double loaded, String text) { + // indicates we should paint ourselves + Graphics g2 = getGraphics(); + String s = text; + if (backbuffer == null || backbuffer.getWidth(null) != getSize().width || backbuffer.getHeight(null) != getSize().height) + backbuffer = createImage(getSize().width, getSize().height); + if (backbuffer == null) return; + Graphics g = backbuffer.getGraphics(); + + g.setColor(loaded < 0 ? Color.red : Color.blue); + loaded = Math.abs(loaded); + + int w = (int)((double)getSize().width * loaded); + g.fillRect(0, 0, w, getSize().height); + g.setColor(Color.darkGray); + g.fillRect(w, 0, getSize().width - w, getSize().height); + + Font f = new Font("Sans-serif", Font.BOLD, 12); + FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(f); + g.setFont(f); + + int x = (getSize().width - fm.stringWidth(s)) / 2; + int y = ((getSize().height - fm.getMaxAscent() - fm.getMaxDescent()) / 2) + fm.getMaxAscent(); + g.setColor(Color.white); + g.drawString(s, x, y); + + if (g2 != null) { + g2.setClip(0, 0, getSize().width, getSize().height); + g2.drawImage(backbuffer, 0, 0, null); + } + } + + + // Misc Constants /////////////////////////////////////////////////////////// + + private static final String XWT_foundation_public_key = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoVweDZ+h3jcN2Qz1YwWJR8gVF0m/IE" + + "BasLIcvey9y1VkyN9jY6b9qm/Z2UoVtvAlgezd4CsJedUCIMe7uURyHGnqI4MLrozxLz3" + + "zqx5EYChsJt+Ju/f44KYnMx7upUQ4irfxOj6RpHy3E5GbW4XO96WwFlOuaR8+HRwKCXGP" + + "QvQIDAQAB"; + + private static final String[] commonJavaLocations = new String[] { + "/usr/bin/java", + "/usr/java/bin/java", + "/usr/local/bin/java", + "/usr/local/java/bin/java", + "/usr/lib/j2sdk1.4/bin/java", + "/usr/lib/j2sdk1.3/bin/java", + "/usr/lib/j2sdk1.2/bin/java" + }; + +} + + + + +/* + +/// NSJVM Notes /////////////////////////////////////////////////////////////////// + + PrivilegeManager.enablePrivilege("MarimbaInternalTarget"); + PrivilegeManager.enablePrivilege("UniversalSystemClipboardAccess"); + netscape.security.PrivilegeManager.enablePrivilege("UniversalConnect"); (sockets) + netscape.security.PrivilegeManager.enablePrivilege("UniversalSystemClipboardAccess"); + netscape.security.PrivilegeManager.enablePrivilege("UniversalTopLevelWindow"); + + - Netscape's ClassLoader.getResource() is broken, see http://developer.netscape.com/docs/technote/java/getresource/getresource.html + - this will fix it: netscape.security.PrivilegeManager.enablePrivilege("UniversalPropertyRead"); + + - If you create a classloader, include + public boolean checkMatchPrincipalAlways(int i) { + return ((AppletClassLoader)this.getClass().getClassLoader()).checkMatchPrincipalAlways(i); + } + + - protected int _modifiersToButtonNumber(int modifiers) { + if ((modifiers & (InputEvent.BUTTON1_MASK & 15)) == (InputEvent.BUTTON1_MASK & 15)) return 1; + if ((modifiers & (InputEvent.BUTTON2_MASK & 15)) == (InputEvent.BUTTON2_MASK & 15)) return 3; + if ((modifiers & (InputEvent.BUTTON3_MASK & 15)) == (InputEvent.BUTTON3_MASK & 15)) return 2; + + +/// MSJVM Notes /////////////////////////////////////////////////////////////////// + +- To sniff the JVM, check if (window.clientInformation.javaEnabled()), or if + (navigator.javaEnabled()). See also http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndetect/html/detectvm.asp + +- MSJVM note: you have to write to the getGraphics() of a Frame before you can setIconImage() it. + +- How to create trusted classes within an already-running MSJVM: + + class MicrosoftClassLoader extends SecurityClassLoader { + static PermissionSet ps; // a PermissionSet that allows everything + static { + PermissionDataSet pds = new PermissionDataSet(); + IPermission perm = new IPermission() { + public void check(Object request) { } + public IPermission combine(IPermission other) { return this; } + public IPermission copy() { return this; } + public int compareSet(Object o) { return SetComparison.DISJOINT; } + }; + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ClientStoragePermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ExecutionPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.FileIOPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.MultimediaPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.NetIOPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.PrintingPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.PropertyPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ReflectionPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.SecurityPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.SystemStreamsPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ThreadPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.UIPermission"), perm); + pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.UserFileIOPermission"), perm); + ps = new PermissionSet(pds); + } + public Class defineClass(String name, byte[] b) { + Class c = super.defineClass(name, b, 0, b.length, ps, com.ms.security.PolicyEngine.getPrincipalOfClass(Resources.class)); + if (name.startsWith("net")) System.out.println("defineClass " + name + " yielded " + c); + // for some reason, the MSJVM doesn't request resolves properly, so we have to do it manually on every class-load + resolveClass(c); + return c; + } + +- How to inhibit background-clearing on the MSJVM + + // Used to ensure that getPeer() doesn't go off in an infinite loop + boolean ok = false; + + // The MSJVM needs us to occasionally falsify getPeer() in order to thwart unneeded background-clearing + public ComponentPeer getPeer() { + if (Thread.currentThread() == Platform.fastPathThread) return super.getPeer(); + if (ok) return super.getPeer(); + + // to prevent recursive stack-dumping... =) + ok = true; + Dimension d = getSize(); + ok = false; + if (last != null && last.width == d.width && last.height == d.height) + return super.getPeer(); + + String s = com.ms.wfc.util.Debug.getStackTraceText(); + for(int i = s.indexOf('d'); i != -1; i = s.indexOf('d', i + 1)) { + if (s.regionMatches(i, "doClearAndPaint", 0, 15)) { + last = getSize(); + update(null); + return null; + } + } + return super.getPeer(); + } + +*/