隨筆-17  評論-6  文章-1  trackbacks-0
           
          問:我源文件為main.c, x.c, y.c, z.c,頭文件為x.h,y.h,z.h
          如何編譯成.so動態(tài)庫?
          編譯器用gcc
          最好能給出詳細(xì)參數(shù)解釋,謝謝

          答:
          # 聲稱動代連接庫,假設(shè)名稱為libtest.so
          gcc x.c y.c z.c -fPIC -shared -o libtest.so

          # 將main.c和動態(tài)連接庫進(jìn)行連接生成可執(zhí)行文件
          gcc main.c -L. -ltest -o main

          # 輸出LD_LIBRARY_PATH環(huán)境變量,一邊動態(tài)庫裝載器能夠找到需要的動態(tài)庫
          export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

          # 測試是否動態(tài)連接,如果列出libtest.so,那么應(yīng)該是連接正常了
          ldd main

          # 執(zhí)行就不用說了吧

          -fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關(guān)的所以動態(tài)載入時是通過代碼拷貝的方式來滿足不同進(jìn)程的需要,而不能達(dá)到真正代碼段共享的目的。

          -L.:表示要連接的庫在當(dāng)前目錄中

          -ltest:編譯器查找動態(tài)連接庫時有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱

          LD_LIBRARY_PATH:這個環(huán)境變量指示動態(tài)連接器可以裝載動態(tài)庫的路徑。
          當(dāng)然如果有root權(quán)限的話,可以修改/etc/ld.so.conf文件,然后調(diào)用
          /sbin/ldconfig來達(dá)到同樣的目的,不過如果沒有root權(quán)限,那么只能采用輸出LD_LIBRARY_PATH的方法了。
          posted @ 2006-03-03 16:20 小鐵匠 閱讀(5132) | 評論 (0)編輯 收藏

          keytool -genkey -dname "CN=demo, OU=softDept, O=company, L=puddong,S=shanghai, C=cn" -alias demo -keyalg RSA -keysize 1024 -keystore demoKeystore -validity 3650 -storepass storePwd -keypass demoPwd
          生成保存公鑰和私鑰的密鑰倉庫,保存在demoKeystore文件中。這里storepass 和 keypass 不要有java 正則表達(dá)式中的特殊字符,否則程序里要轉(zhuǎn)義麻煩。

          keytool -export -alias demo -keystore demoKeystore -rfc -file demo.cer //從密鑰倉庫中導(dǎo)出保存公鑰的證書
          輸入keypass 即demoPwd 


            try{     
             //密鑰倉庫
             KeyStore ks = KeyStore.getInstance("JKS");
          //讀取密鑰倉庫
             FileInputStream ksfis = new FileInputStream("demoKeystore");
             BufferedInputStream ksbufin = new BufferedInputStream(ksfis);
             char[] storePwd = "storePwd".toCharArray();
             ks.load(ksbufin, storePwd);
             ksbufin.close();
             char[] keyPwd = "demoPwd".toCharArray();
          //從密鑰倉庫得到私鑰
             PrivateKey priK = (PrivateKey) ks.getKey("demo", keyPwd);  
          //生成cipher
             Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding",new org.bouncycastle.jce.provider.BouncyCastleProvider());
          //用私鑰初始化cipher
             cipher.init(Cipher.ENCRYPT_MODE, priK);
             byte[] plain = "This is plain text".getBytes("UTF-8");
             
             //因為用的1024位rsa算法,一次只能加密1024/8-11字節(jié)數(shù)據(jù),分開加密
             byte[] code = new byte[(((plain.length-1)/117+1))*128];  
                      int ixplain = 0;
                      int ixcode = 0;
                      while((plain.length - ixplain) > 117) {//每117字節(jié)做一次加密
                          ixcode += cipher.doFinal(plain, ixplain, 117, code, ixcode);
                          ixplain += 117;
                      }
                      cipher.doFinal(plain, ixplain, plain.length - ixplain, code, ixcode);
                      //加密后的code
                      System.out.println(Arrays.toString(code));
                      //通常會用base64編碼
                     String base64 = encoder.encode(code);

             CertificateFactory certificatefactory = CertificateFactory
               .getInstance("X.509");
             //讀取證書
             FileInputStream fin = new FileInputStream("demo.cer");
             X509Certificate certificate = (X509Certificate) certificatefactory
               .generateCertificate(fin);
             fin.close();
             //得到公鑰
             PublicKey pubK = certificate.getPublicKey();
                   //初始化cipher
                      cipher.init(Cipher.DECRYPT_MODE, pubK);
                //base64解碼
                      code = decoder.decodeBuffer(base64);
                      System.out.println(Arrays.toString(code));
                      byte[] plain2 = new byte[code.length];
                      int ixplain2 = 0;
                      int ixcode2 = 0;
                      while((code.length - ixcode2) > 128) {//每128字節(jié)做一次解密
                          ixplain2 += cipher.doFinal(code, ixcode2, 128, plain2, ixplain2);
                          ixcode2 += 128;
                      }
                      ixplain2 += cipher.doFinal(code, ixcode2, code.length - ixcode2, plain2, ixplain2);
                      String s2 = new String(plain2, 0, ixplain2, "UTF-8");
                      System.out.println(s2);
             
            }catch(Exception ex){
             ex.printStackTrace();
            }

          keytool使用方法可以參考jdk文檔
          Java keytool工具的作用及使用方法

          posted @ 2006-03-02 14:32 小鐵匠 閱讀(3432) | 評論 (0)編輯 收藏
          在c++中new的對象,如果不返回java,必須用release掉,否則內(nèi)存泄露。包括NewStringUTF,NewObject
          。如果返回java不必release,java會自己回收。

           jstring jstr = env->NewStringUTF((*p).sess_id);
             ...
           env->DeleteLocalRef( jstr);

          jobject jobj = env->NewObject(clazz,midInit);
          return jobj;

          內(nèi)存泄露可以先從windows資源管理器中,看到隨程序運行,內(nèi)存不斷增長的趨勢,具體可以用hp jmeter檢測在運行程序時,加jvm參數(shù) -Xrunhprof:heap=all,cutoff=0 ,生成java.hprof.txt,用jmeter打開,Metric -> Residual Objects (Count),可以看到未回收的對象,選中要查看的對象,點Mark記錄下要查看的對象,Window -> New Window 打開新窗口,用Metric -> Reference Graph Tree,然后點Find Immediately可以看到對象被哪里引用。


          找出內(nèi)存泄漏另一方法

          程序有內(nèi)存泄漏的第一個跡象通常是它拋出一個 OutOfMemoryError,或者因為頻繁的垃圾收集而表現(xiàn)出糟糕的性能。幸運的是,垃圾收集可以提供能夠用來診斷內(nèi)存泄漏的大量信息。如果以 -verbose:gc 或者 -Xloggc 選項調(diào)用 JVM,那么每次 GC 運行時在控制臺上或者日志文件中會打印出一個診斷信息,包括它所花費的時間、當(dāng)前堆使用情況以及恢復(fù)了多少內(nèi)存。記錄 GC 使用情況并不具有干擾性,因此如果需要分析內(nèi)存問題或者調(diào)優(yōu)垃圾收集器,在生產(chǎn)環(huán)境中默認(rèn)啟用 GC 日志是值得的。

          有工具可以利用 GC 日志輸出并以圖形方式將它顯示出來,JTune 就是這樣的一種工具(請參閱 參考資料)。觀察 GC 之后堆大小的圖,可以看到程序內(nèi)存使用的趨勢。對于大多數(shù)程序來說,可以將內(nèi)存使用分為兩部分:baseline 使用和 current load 使用。對于服務(wù)器應(yīng)用程序,baseline 使用就是應(yīng)用程序在沒有任何負(fù)荷、但是已經(jīng)準(zhǔn)備好接受請求時的內(nèi)存使用,current load 使用是在處理請求過程中使用的、但是在請求處理完成后會釋放的內(nèi)存。只要負(fù)荷大體上是恒定的,應(yīng)用程序通常會很快達(dá)到一個穩(wěn)定的內(nèi)存使用水平。如果在應(yīng)用程序已經(jīng)完成了其初始化并且負(fù)荷沒有增加的情況下,內(nèi)存使用持續(xù)增加,那么程序就可能在處理前面的請求時保留了生成的對象。


          圖 1 顯示  GC 之后應(yīng)用程序堆大小隨著時間的變化圖。上升趨勢是存在內(nèi)存泄漏的警示信號。(在真實的應(yīng)用程序中,坡度不會這么大,但是在收集了足夠長時間的 GC 數(shù)據(jù)后,上升趨勢通常會表現(xiàn)得很明顯。)


          圖 1. 持續(xù)上升的內(nèi)存使用趨勢

          確信有了內(nèi)存泄漏后,下一步就是找出哪種對象造成了這個問題。所有內(nèi)存分析器都可以生成按照對象類進(jìn)行分解的堆快照。有一些很好的商業(yè)堆分析工具,但是找出內(nèi)存泄漏不一定要花錢買這些工具 —— 內(nèi)置的 hprof 工具也可完成這項工作。要使用 hprof 并讓它跟蹤內(nèi)存使用,需要以 -Xrunhprof:heap=sites 選項調(diào)用 JVM。

          清單 3 顯示分解了應(yīng)用程序內(nèi)存使用的 hprof 輸出的相關(guān)部分。(hprof 工具在應(yīng)用程序退出時,或者用 kill -3 或在 Windows 中按 Ctrl+Break 時生成使用分解。)注意兩次快照相比,Map.EntryTaskint[] 對象有了顯著增加。

          請參閱 清單 3

          清單 4 展示了 hprof 輸出的另一部分,給出了 Map.Entry 對象的分配點的調(diào)用堆棧信息。這個輸出告訴我們哪些調(diào)用鏈生成了 Map.Entry 對象,并帶有一些程序分析,找出內(nèi)存泄漏來源一般來說是相當(dāng)容易的。


          清單 4. HPROF 輸出,顯示 Map.Entry 對象的分配點
          
          
          TRACE 300446:
          	java.util.HashMap$Entry.<init>(<Unknown Source>:Unknown line)
          	java.util.HashMap.addEntry(<Unknown Source>:Unknown line)
          	java.util.HashMap.put(<Unknown Source>:Unknown line)
          	java.util.Collections$SynchronizedMap.put(<Unknown Source>:Unknown line)
          	com.quiotix.dummy.MapLeaker.newTask(MapLeaker.java:48)
          	com.quiotix.dummy.MapLeaker.main(MapLeaker.java:64)
          




          另外
          jstring jstr = (jstring)env->CallObjectMethod(authenRequest, mid_authenReq_getSdId_S);
           env->GetStringUTFRegion(jstr,0,env->GetStringLength(jstr),authenReq.sd_id);
          當(dāng)jstr是null時,env->GetStringLength(jstr)會出錯,導(dǎo)致jvm崩潰
          posted @ 2006-03-02 11:23 小鐵匠 閱讀(4246) | 評論 (0)編輯 收藏
          HP-UX下使用JNI訪問標(biāo)準(zhǔn)C++程序

          問題的關(guān)鍵在于用aCC編譯時的參數(shù)
          根據(jù)HP網(wǎng)站上的兩篇文章可以很容易的使用JNI訪問傳統(tǒng)C++(Classical C++)程序
          http://www.hp.com/products1/unix/java/infolibrary/prog_guide/JNI_java2.html 
          http://forums1.itrc.hp.com/service/forums/questionanswer.do?admit=716493758+1092296929165+28353475&threadId=245738 
          但是,如果代碼中使用到了標(biāo)準(zhǔn)C++,也就是用到了STL,就會出現(xiàn)莫名其妙的JVM crash. 而且一般的現(xiàn)象是使用string的時候出錯

          最后發(fā)現(xiàn)是JVM的多線程機(jī)制和aCC編譯的缺省的多線程機(jī)制不一樣.所以編譯時需要加參數(shù)指定
          總的說來,編譯參數(shù)為
          OPTS=-AA +z +u4 -D_RWSTD_MULTI_THREAD -D_REENTRANT -D_HPUX -D_HPUX_SOURCE -D_POSIX_C_SOURCE=199506L -D_XOPEN_SOURCE_EXTENDED 

          其中,-D_RWSTD_MULTI_THREAD -D_REENTRANT 是指定多線程機(jī)制;同時必須添加-D_HPUX_SOURCE 參數(shù),否則,編譯時會出現(xiàn)奇怪的錯誤
          連接參數(shù)為
          -AA -b -lCsup_v2 -lstd_v2 
          值得注意的是根據(jù)上面所說的第二篇文章可知使用-AA編譯連接時,要連的庫是libCsup_v2.sllibstd_v2.sl(這兩個庫是支持標(biāo)準(zhǔn)C++的庫),而不是第一篇文章中提到的libCsup.sllibstd.sl(這兩個庫是支持傳統(tǒng)C++的庫). 

          另外,有幾個碰到的問題
          1. 
          如果編譯參數(shù)沒有指定多線程機(jī)制,禁用JIT(啟動JVM加參數(shù):-Djava.compiler=none -Xint )可以使簡單的例子通過,但是有些情況下還是會出錯

          2. 
          當(dāng)null作為String傳入JNI native接口代碼中是,使用env->GetStringUTFChars(jstring)會出現(xiàn)如下錯誤導(dǎo)致虛擬機(jī)崩潰
          Function=verify_instance_jfieldID__18jfieldIDWorkaroundSFP12klassOopDescP9_jfieldID 

          3. 
          在使用String作為JNI的傳入傳出參數(shù),使用GetStringUTFChars解決不了中文問題,還是會有亂碼正確的解決方法是使用以下兩個函數(shù)
          void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) 

              jclass cls = env->FindClass(name); 
              /* if cls is NULL, an exception has already been thrown */ 
              if (cls != NULL) { 
                  env->ThrowNew(cls, msg); 
              } 
              /* free the local ref */ 
              env->DeleteLocalRef(cls); 


          jstring JNU_NewStringNative(JNIEnv *env, const char *str) 

            if (str==NULL) 
            { 
             return NULL; 
            } 
            jclass jcls_str = env->FindClass("java/lang/String"); 
            jmethodID jmethod_str = env->GetMethodID(jcls_str, "", "([B)V"); 

            jstring result; 
            jbyteArray bytes = 0; 
            int len; 

            if (env->EnsureLocalCapacity(2) < 0) { 
              return NULL; /* out of memory error */ 
            } 
            len = strlen(str); 
            bytes = env->NewByteArray(len); 
            if (bytes != NULL) { 
              env->SetByteArrayRegion(bytes, 0, len,(jbyte *)str); 
              result = (jstring)env->NewObject(jcls_str, jmethod_str, bytes); 
              env->DeleteLocalRef(bytes); 
              return result; 
            } /* else fall through */ 
            return NULL; 



          char *JNU_GetStringNativeChars(JNIEnv *env, jstring jstr) 

              jbyteArray bytes = 0; 
              jthrowable exc; 
              char *result = 0; 
              if (env->EnsureLocalCapacity(2) < 0) { 
                  return 0; /* out of memory error */ 
              } 
          jclass jcls_str = env->FindClass("java/lang/String"); 
          jmethodID MID_String_getBytes = env->GetMethodID(jcls_str, "getBytes", "()[B"]; 

              bytes = (jbyteArray)env->CallObjectMethod(jstr, MID_String_getBytes); 
              exc = env->ExceptionOccurred(); 
              if (!exc) { 
                  jint len = env->GetArrayLength( bytes); 
                  result = (char *)malloc(len + 1); 
                  if (result == 0) { 
                      JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 
                                      0); 
                      env->DeleteLocalRef(bytes); 
                      return 0; 
                  } 
                  env->GetByteArrayRegion(bytes, 0, len, (jbyte *)result); 
                  result[len] = 0; /* NULL-terminate */ 
              } else { 
                  env->DeleteLocalRef(exc); 
              } 
              env->DeleteLocalRef(bytes); 
              return (char*)result; 


          ★注意:使用char *JNU_GetStringNativeChars()獲得的指針用完后要顯式的free().

          posted @ 2006-02-27 15:54 小鐵匠 閱讀(982) | 評論 (0)編輯 收藏

          使用Axis傳送附件有兩種方式:

          1. 將你要傳送的文件封裝在DataHandler中,然后將DataHandler對象或DataHandler數(shù)組(多個文件傳送的時候)作為客戶端調(diào)用函數(shù)的參數(shù)(從客戶端上傳文件到服務(wù)器)Axis服務(wù)的返回類型(從服務(wù)器端下載文件到客戶端)進(jìn)行傳輸。

          2. 還有一種方式是直接修改soap信封的內(nèi)容,將附件信息加到soap信封中發(fā)送。

          這里我們只討論第一種方法,因為它實現(xiàn)起來非常簡單。關(guān)于第二種方法在Axis包的webapps/attachments/TestRf.java中有詳細(xì)的原碼可以參考。

          下面的例子是把文件從服務(wù)器端下載到客戶端:

          1.服務(wù)端程序:

          假設(shè)傳輸多個文件:在服務(wù)器端將文件取出來,并將文件封裝在DataHandler數(shù)組中。
          代碼如下:

           DataHandler[] ret = new DataHandler[totalFileNum];
           ... ...
           java.io.File myFile = new java.io.File(filePath);
           if(myFile.isFile() && myFile.canRead())
           {
            String fname = myFile.getAbsoluteFile().getCanonicalPath();
            DataHandler[0] = new DataHandler(new FileDataSource(fname));
           }
           ... ...

           return ret;

          上面的代碼將所有的文件封裝在了DataHandler數(shù)組中,并返回。

          2. 客戶端的訪問:

          代碼如下:
           Service service = new Service();
           Call call = (Call) service.createCall();

           URL myURL = new URL(" call.setTargetEndpointAddress(myURL); //設(shè)定服務(wù)的主機(jī)和位置
           call.setOperationName(new QName("urn:MyAttachServer","echoDir")); //設(shè)置要調(diào)用的服務(wù)的方法
           QName qnameAttachment = new QName("urn:MyAttachServer","DataHandler");

           call.registerTypeMapping(DataHandler.class, qnameAttachment, JAFDataHandlerSerializerFactory.class,JAFDataHandlerDeserializerFactory.class); //為附件(即DataHandler類)創(chuàng)建序列化生成器

           call.addParameter("source", XMLType.XSD_STRING ,ParameterMode.IN); //設(shè)置服務(wù)調(diào)用方法的傳入?yún)?shù)類型
           call.setReturnType(XMLType.SOAP_ARRAY); //設(shè)置調(diào)用服務(wù)方法的返回類型,由于返回的是DataHandler數(shù)組,所以設(shè)置為SOAP_ARRAY類型
           javax.activation.DataHandler[] ret = (javax.activation.DataHandler[])call.invoke(new Object[]{null}); //調(diào)用方法

           for (i = 0; i < ret.length; ++i)
                  {
                      DataHandler recDH = ret[i];
                      java.io.File receivedFile = new java.io.File(recDH.getName()); //文件生成
                  }

          3. 服務(wù)的部署:

          注意:你要在部署的時候,定義DataHandler的序列化生成器。

            編寫deploy.wsdd文件:

           <deployment xmlns="  <service name="urn:att_STC_Server" provider="java:RPC" >
              <parameter name="className" value="samples.att_STC.att_STC_Server"/>
              <parameter name="allowedMethods" value="echoDir"/>

           <typeMapping deserializer="org.apache.axis.encoding.ser.JAFDataHandlerDeserializerFactory"
             languageSpecificType="java:javax.activation.DataHandler" qname="ns1:DataHandler"
              serializer="org.apache.axis.encoding.ser.JAFDataHandlerSerializerFactory"
              encodingStyle="
          http://schemas.xmlsoap.org/soap/encoding/"/>
            </service>

          </deployment>

          運行java org.apache.axis.client.AdminClient %* deploy.wsdd,部署服務(wù)。

          posted @ 2006-02-20 16:28 小鐵匠 閱讀(1041) | 評論 (1)編輯 收藏

          2004-05-29 17:39:53

          主題: 使用Java實現(xiàn)CA(四)

              前面幾篇文章已經(jīng)把如何用Java實現(xiàn)一個CA基本上講完了.但是他們都有一個特點,就是用戶的信息都是在現(xiàn)場獲取的,不能做申請和簽發(fā)相分離.今天我們要講述的是PKCS#10證書請求文件.它的作用就是可以使申請和簽發(fā)相分離.

              PKCS#10證書請求結(jié)構(gòu)中的主要信息包含了被簽發(fā)者(證書申請者)的主體名稱(DN)和他的公鑰.因此一個CA在獲取到一個PKCS#10證書請求后,就可以從中獲取到任何和簽發(fā)證書有關(guān)的信息,然后用它自己的私鑰簽發(fā)證書.

              使用BC Provider在Java中構(gòu)造一個證書請求格式的對象調(diào)用其構(gòu)造函數(shù)即可,這個函數(shù)如下:

              PKCS10CertificationRequest(java.lang.String signatureAlgorithm, X509Name subject, java.security.PublicKey key, ASN1Set attributes, java.security.PrivateKey signingKey)

              它的參數(shù)是自簽名算法,證書申請者的DN,證書申請者的公鑰,額外的屬性集(就是要申請的證書的擴(kuò)展信息),申請證書者的私鑰.申請證書者的私鑰僅僅是用來進(jìn)行一下自簽名,并不出現(xiàn)在證書請求中,需要自簽名的目的是保證該公鑰確實為申請者所有.

              調(diào)用該對象的getEncoded()方法可以將其進(jìn)行DER編碼,然后儲存起來,該對象還有另一個構(gòu)造函數(shù):
              PKCS10CertificationRequest(byte[] bytes)
              這個構(gòu)造函數(shù)的作用就是直接從儲存的DER編碼中把這個對象還原出來.

              利用證書請求結(jié)構(gòu)進(jìn)行證書簽發(fā)的代碼如下,這里假設(shè)CSR是一個已經(jīng)獲取出來的PKCS10CertificationRequest結(jié)構(gòu):

              PublicKey SubjectPublicKey = CSR.getPublicKey();
              CertificationRequestInfo CSRInfo = CSR.getCertificationRequestInfo();
              X509Name SubjectDN = CSRInfo.getSubject();
              ASN1Set Attributes = CSRInfo.getAttributes();

              這樣,申請者的主體DN,申請者的公鑰,申請者希望在證書擴(kuò)展信息中填寫的屬性都得到了,剩下的事情就和用戶在現(xiàn)場輸入時一樣了,其它的信息一般是申請者不能決定的.另外證書請求格式中有一樣信息沒有明確給出來,那就是證書的有效期,這個應(yīng)該單獨詢問用戶,或者用其它的方法保存起來.



          [返回頂部]


          2004-05-28 16:46:12

          主題: 使用Java實現(xiàn)CA(三)

              前幾次我已經(jīng)基本上把如何做CA所需要的基礎(chǔ)知識講得差不多了,今天直接講如何用Java程序來實現(xiàn)一個CA應(yīng)該就不是什么太困難的事情了.

              要做CA,第一步要準(zhǔn)備好自己的證書和私鑰.私鑰如何從文件里面讀取出來前面已經(jīng)講過了.從文件系統(tǒng)中讀出證書的代碼如下:

              CertificateFactory certCF = CertificateFactory.getInstance("X.509");
              X509Certificate caCert = certCF.generateCertificate(certBIS);

              這里cerBIS是一個InputStream類型的對象.例如一個標(biāo)準(zhǔn)的X509v3格式的證書文件所形成的輸入流.

              第二步就是從用戶那里獲取輸入,然后構(gòu)造主體名稱結(jié)構(gòu)DN,如何構(gòu)造DN上次已經(jīng)說過了,如何從用戶那里獲取輸入,這個不在本文討論范圍之內(nèi).

              下一步就是獲取用戶的公鑰,好和他所需要的證書對應(yīng)起來.也有不少CA的做法就是在這里替用戶現(xiàn)場生成一對密鑰對,然后把公鑰放到證書中簽發(fā)給用戶.這個應(yīng)該看實際需要選擇合適的方式.

              現(xiàn)在一切信息都已經(jīng)準(zhǔn)備好了,可以簽發(fā)證書了,下面的代碼說明了這個過程:

              //構(gòu)造一個證書生成器對象

              X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();

              // 從CA的證書中獲取簽發(fā)者的主體名稱(DN)
              // 這里有一點小技巧,我們要把JCE中定義的
              // 用來表示DN的對象X500Principal轉(zhuǎn)化成在
              // BC Provider中的相應(yīng)的對象X509Name
              // 先從CA的證書中讀取出CA的DN進(jìn)行DER編碼
              DERInputStream dnStream =
                           new DERInputStream(
                new ByteArrayInputStream(
                 caCert.getSubjectX500Principal().
                  getEncoded()));
              // 馬上又從編碼后的字節(jié)流中讀取DER編碼對象
              DERConstructedSequence  dnSequence =
               (DERConstructedSequence)dnStream.readObject();
              // 利用讀取出來的DER編碼對象創(chuàng)建X509Name
              // 對象,并設(shè)置為證書生成器中的"簽發(fā)者DN"
              certGen.setIssuerDN(new X509Name(dnSequence));
              // 設(shè)置好證書生成器中的"接收方DN"
              certGen.setSubjectDN(subjectDN);
              // 設(shè)置好一些擴(kuò)展字段,包括簽發(fā)者和
              // 接收者的公鑰標(biāo)識
              certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
              createSubjectKeyId(keyToCertify));
              certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
              createAuthorityKeyId(caCert.getPublicKey()));
              // 設(shè)置證書的有效期和序列號
              certGen.setNotBefore(startDate);
              certGen.setNotAfter(endDate);
              certGen.setSerialNumber(serialNumber);
              // 設(shè)置簽名算法,本例中使用MD5hash后RSA
              // 簽名,并且設(shè)置好主體的公鑰
              certGen.setSignatureAlgorithm("MD5withRSA");
              certGen.setPublicKey(keyToCertify);

              // 如果以上一切都正常地話,就可以生成證書了
              X509Certificate cert = null;
              cert = certGen.generateX509Certificate(caPrivateKey);

              這里是上面用到的生成簽發(fā)者公鑰標(biāo)識的函數(shù): 

              protected AuthorityKeyIdentifier createAuthorityKeyId(PublicKey pubKey)
              {
              AuthorityKeyIdentifier authKeyId = null;

              try
              {
              ByteArrayInputStream bIn = new ByteArrayInputStream(pubKey.getEncoded());
              SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
                  (DERConstructedSequence)new DERInputStream(bIn).readObject());
              authKeyId = new AuthorityKeyIdentifier(info);
              }
              catch (IOException e)
              {
              System.err.println("Error generating SubjectKeyIdentifier:  " +
                  e.toString());
              System.exit(1);
              }

              return authKeyId;
              }

              生成主體公鑰標(biāo)識的函數(shù)和上面的類似,把AuthorityKeyIdentifier替換成SubjectKeyIdentifier就可以了.

              這里要注意的是,CA的公鑰也是在一份證書里,這種證書的特點是簽發(fā)者DN和接收者DN一樣,也就是說,這種證書是CA自己給自己頒發(fā)的證書,也就是"自簽名證書",它上面的公鑰是CA自身的公鑰,用來簽名的私鑰就是該公鑰對應(yīng)的私鑰.一般每個CA都要有這么一份證書,除非該CA不是根CA,即它的權(quán)威性不是由它自己證明,而是由它的上級CA證明.但是,最后總歸要有一個根CA,它為各個安全應(yīng)用程序的用戶所信賴.

              到這里我們已經(jīng)把CA最基本的功能如何用Java實現(xiàn)講完了,下一次講如何從PKCS#10格式證書請求文件中讀取出用戶信息,然后直接簽發(fā)公鑰.



          [返回頂部]


          2004-05-27 15:34:59

          主題: 使用Java實現(xiàn)CA(二)

              昨天本來快寫完了,結(jié)果不小心按了"tab"鍵,然后向按退格鍵,結(jié)果退到前一個頁面了,然后全部都白寫了,不爽.只好今天重新寫了.

              上次我們講到如何生成密鑰對,以及如何將諸如公鑰,私鑰,證書等這一類安全對象在文件系統(tǒng)和內(nèi)存之間來回轉(zhuǎn)換.這些是準(zhǔn)備開CA的基本功,今天我們講一下CA的基本原理以及如何使用主體名稱結(jié)構(gòu)DN(Distinguish Name)來表示每一個證書中的主體.

              一份證書就是一個權(quán)威機(jī)構(gòu)對一個主體的身份的確認(rèn)的證明.即一份證書表示一個權(quán)威機(jī)構(gòu)確認(rèn)了一個主體就是它自己,而不是其它的冒名頂替者.主體可以是一個個人,也可以不是,例如,在需要安全服務(wù)的時候,需要為一臺網(wǎng)站的服務(wù)器頒發(fā)證書,這種證書的主體就是一臺服務(wù)器.簽署證書的權(quán)威機(jī)構(gòu)就叫做CA,該權(quán)威機(jī)構(gòu)本身也是一個主體.權(quán)威機(jī)構(gòu)通過對包含有待認(rèn)證的主體的一系列信息的待簽名證書"(TBS,to be signed)進(jìn)行數(shù)字簽名來表示該機(jī)構(gòu)對它的認(rèn)可.一份包含了待認(rèn)證的主體的相關(guān)信息的TBS再加上CA使用自己的私鑰進(jìn)行簽名產(chǎn)生的字節(jié)流放在一起,就構(gòu)成了一份標(biāo)準(zhǔn)的X509證書.

              一個TBS中包含了以下這些主要信息:

              證書的版本,通常是3(X509v3)

              證書的序列號,RFC3280中規(guī)定,每個CA必須確保它頒發(fā)的每一份證書的序列號都是唯一的,并且序列號只能使用非負(fù)整數(shù).

              簽發(fā)者(CA)的主體名稱,一個DN對象.

              證書的有效期,表示方法是兩個時間值,表示了該證書從何時開始生效,何時開始作廢.

              待認(rèn)證的主體的主體名稱,也是一個DN對象.

              待認(rèn)證的主體的公鑰,任何安全應(yīng)用在確認(rèn)完證書的有效性后,就可以開始使用該主體的公鑰與之進(jìn)行安全通信.

              如果是X509v3證書,即版本號是3的話,后面還有一個證書擴(kuò)展信息字段,可以在證書里面添加一些其它的信息.

              下面我們來看一下表示主體的主體名稱結(jié)構(gòu):DN.這個結(jié)就構(gòu)是一個屬性的集合.每個屬性有屬性名稱和屬性值.它的作用就是用來表示"我是誰",也就是說,這個證書到底是誰頒發(fā)給誰的,這個證書對應(yīng)的公鑰是誰擁有的.

              通常使用一個字符串來表示DN結(jié)構(gòu),這種字符串說明了這種結(jié)構(gòu)中的各個屬性的類型和值:

              C=CN;S=BeiJing;L=BeiJing;O=PKU;OU=ICST;CN=wolfenstein

              這里C是國家和地區(qū)代碼,S和L都是地區(qū)代碼,S相當(dāng)于省或者州這樣的級別,L相當(dāng)于城市級別,O是組織機(jī)構(gòu)名稱,OU是次級組織機(jī)構(gòu)名稱,CN是主體的通用名(common name).在這里,C,S,L等等屬性的類型都是相對固定的,例如C一般就是用來表示國家和地區(qū)代碼,在DN結(jié)構(gòu)中還可以添加一些其它類型的信息,一般也都是以"xxx=xxx"這樣來表示的.

              下面我們來說明如何在Java語言中構(gòu)造出一個主體名稱對象.

              BC Provider中使用X509Name對象來表示DN,構(gòu)造一個X509Name的步驟大體如下:

              先構(gòu)造兩個vector對象,用來表示屬性名稱和屬性值:

              Vector oids = new Vector();
              Vector attributes = new Vector();

              然后在oids這個用來表示屬性名稱的vector對象中將屬性名稱一個一個添加進(jìn)去:

              oids.addElement(X509Name.C);

              ......

              oids.addElement(X509Name.CN);

              X509Name對象里面有若干常量,例如上面的X509Name.C.還有X509Name.ST等等,都可以和上面舉的例子對應(yīng)起來.

              然后將屬性值添加到attributes對象中:

              attributes.addElement("CN");

              ......

              attributes.addElement("Wolfenstein");

              最后就可以構(gòu)造出一個X509Name對象:

              X509Name SubjectDN = new X509Name(oids, attributes);

              這樣,我們的主體名稱結(jié)構(gòu)就確立起來了.

              下次我們就可以講關(guān)鍵的部分了,那就是如何用Java程序完成CA最重要的功能,簽署證書.



          [返回頂部]


          2004-05-25 21:23:52

          主題: 使用Java實現(xiàn)CA(一)

              通過我昨天的文章大家應(yīng)該已經(jīng)清楚了,用Java寫信息安全方面的程序需要做的準(zhǔn)備工作.好了,現(xiàn)在假設(shè)你已經(jīng)是一個對Java語言本身比較熟悉,能夠用Java寫一些程序的人,并且這些該下載的jar包已經(jīng)下載好了,可以正式開工了.

              在所有的此類程序的開頭(無論是在類的構(gòu)造函數(shù)中也好,在初始化函數(shù)中也好),都要先來上這么一句:Security.addProvider(new BouncyCastleProvider());將BouncyCaslte的Provider添加到系統(tǒng)中,這樣以后系統(tǒng)在運行相關(guān)程序的時候調(diào)用的就是這個Provider中的加密算法.

              然后我們就可以開始開CA了.首先,作為一個CA要有自己的一對公鑰和私鑰,我們先要生成這么一對.使用KeyPairGenerator對象就可以了,調(diào)用KeyPairGenerator.getInstance方法可以根據(jù)要生成的密鑰類型來產(chǎn)生一個合適的實例,例如常用的RSA,DSA等.然后調(diào)用該對象的initialize方法和generateKeyPair方法就可以產(chǎn)生一個KeyPair對象了.然后調(diào)用KeyPair對象中的相應(yīng)方法就可以獲取生成的密鑰對中的公鑰和私鑰了.

              有了公鑰和私鑰對以后,下面的一個很現(xiàn)實問題就是如何把它們儲存起來.通常我們要對這些安全對象,如公鑰,私鑰,證書等先進(jìn)行編碼.編碼的目的是為了把結(jié)構(gòu)復(fù)雜的安全對象變成字節(jié)流以便存儲和讀取,如DER編碼.另外,通常還把DER編碼后的字節(jié)流再次進(jìn)行base64編碼,以便使字節(jié)流中所有的字節(jié)都變成可打印的字節(jié).

              在Java語言中,這些安全對象基本上都有g(shù)etEncoded()方法.例如:

              byte[] keyBytes = privateKey.getEncoded();

              這樣就把一個私鑰進(jìn)行了DER編碼后的結(jié)果保存到一個byte數(shù)組中了.然后就可以把這個byte數(shù)組保存到任何介質(zhì)中.如果有必要的話,可以使用BC Provider中的Base64編碼解碼器類進(jìn)行編碼,就像這樣:

              byte data[] = Base64.encode(keyBytes);

              要從文件中讀取私鑰則應(yīng)該這樣:

              PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyData);
              KeyFactory kfac = KeyFactory.getInstance("RSA");
              privateKey = kfac.generatePrivate(spec);

              這里說明一下,對RSA私鑰進(jìn)行編碼就會自動地按照PKCS8進(jìn)行.因此讀取的時候?qū)幋a的字節(jié)數(shù)組作為PKCS8EncodedKeySpec對象的構(gòu)造函數(shù)的參數(shù)就可以生成一個該類型的對象.然后創(chuàng)建一個密鑰工廠對象就可以按照要求生成一個RSA私鑰了.很顯然這里的keyData應(yīng)該是和上面的keyBytes內(nèi)容相同.

              為了提高系統(tǒng)的安全性,通常私鑰在經(jīng)過DER編碼后,還會使用一個口令進(jìn)行加密,然后再儲存在文件系統(tǒng)中.在使用私鑰的時候,如果沒有正確的口令,是無法把私鑰還原出來的.

              保存證書和保存私鑰類似.Certificate對象中也有一個getEncoded的方法.

              這次就講這些.大家應(yīng)該可以把這些安全對象很熟練地從文件系統(tǒng)和內(nèi)存之間來回地折騰了吧.這對以后實現(xiàn)CA是很重要的.下次我會講一下證書中表示主體的方法:DN.



          [返回頂部]


          2004-05-24 17:23:06

          主題: 使用Java開發(fā)和信息安全相關(guān)的程序

              這里說的信息安全是相對于系統(tǒng)安全而言的,它更側(cè)重于加密,解密,數(shù)字簽名,驗證,證書等等.而系統(tǒng)安全主要側(cè)重于系統(tǒng)本身是否有安全漏洞,如常見的由于軟件設(shè)計的不完善而導(dǎo)致的滿天飛的緩沖區(qū)溢出等等.

              Java語言中負(fù)責(zé)加密功能的部件是JCE(Java Crypto Extenstion),它使用開放式的思想,可以允許用戶自己編寫加密算法的具體實現(xiàn)的模塊等.這些東西被稱為JCE Provider,JCE的提供者.SUN公司本身提供了一些Provider.不過我推薦使用Bouncy Castle的Provider.原因是它實現(xiàn)的加密算法多,連比較新的橢圓曲線(ECC)算法都有了.去http://www.bouncycastle.org/可以找到你所希望的.Bouncy Castle除了提供Provider本身以外,還包括了一個S/MIME和一個open pgp 的jar包只有Provider本身是必要的,后兩個包是方便你編程而提供的.例如有了S/MIME包后,你就不再需要為諸如"加密一個字符串或者一片文章然后簽名"之類的很現(xiàn)實的應(yīng)用程序?qū)懮弦淮蠖汛a了,只要引用S/MIME包中的某幾個類,很快就可以搞定.而open pgp的存在,使你在用Java編寫和PGP/GPG交互的程序時方便多了.

              這次先寫這么多了,下次開始具體講把這些東西搞下來后怎么開始干活吧.我以我現(xiàn)在手頭上正在做的事情告訴大家,如何做一個CA.

              有了BC Provider,開CA,真的就是這么簡單!

          posted @ 2006-02-20 11:07 小鐵匠 閱讀(6116) | 評論 (0)編輯 收藏
          jdbc url:jdbc:oracle:thin:@(DESCRIPTION=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.0.1)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.0.2)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=sa)))
          其他照常
          posted @ 2005-12-14 10:12 小鐵匠 閱讀(508) | 評論 (0)編輯 收藏
          僅列出標(biāo)題
          共2頁: 上一頁 1 2 


          My blog is worth $10,000,000.00 ^_^.
          How much is your blog worth?

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          相冊

          搜索

          •  

          積分與排名

          • 積分 - 58783
          • 排名 - 887

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 合山市| 遂宁市| 金阳县| 马鞍山市| 明光市| 波密县| 罗山县| 禄丰县| 东辽县| 隆回县| 韩城市| 司法| 平顶山市| 彭水| 松原市| 永和县| 诸城市| 云霄县| 峡江县| 桑植县| 南靖县| 乐安县| 冀州市| 思茅市| 凤翔县| 淮北市| 策勒县| 河北省| 渝北区| 林西县| 油尖旺区| 渝中区| 诏安县| 灵台县| 肇东市| 隆安县| 商南县| 青龙| 海林市| 池州市| 隆回县|