随笔-17  评论-6  文章-1  trackbacks-0
 
问:我源文件为main.c, x.c, y.c, z.c,头文件为x.h,y.h,z.h
如何编译成.so动态库?
编译器用gcc
最好能给出详细参数解释,谢谢

答:
# 声称动代连接库,假设名称为libtest.so
gcc x.c y.c z.c -fPIC -shared -o libtest.so

# 将main.c和动态连接库进行连接生成可执行文件
gcc main.c -L. -ltest -o main

# 输出LD_LIBRARY_PATH环境变量,一边动态库装载器能够找到需要的动态库
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

# 测试是否动态连接,如果列出libtest.so,那么应该是连接正常了
ldd main

# 执行就不用说了吧

-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

-L.:表示要连接的库在当前目录中

-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称

LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用
/sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。
posted @ 2006-03-03 16:20 小铁匠 阅读(5116) | 评论 (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 正则表达式中的特殊字符,否则程序里要转义麻烦。

keytool -export -alias demo -keystore demoKeystore -rfc -file demo.cer //从密钥仓库中导出保存公钥的证书
输入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字节数据,分开加密
   byte[] code = new byte[(((plain.length-1)/117+1))*128];  
            int ixplain = 0;
            int ixcode = 0;
            while((plain.length - ixplain) > 117) {//每117字节做一次加密
                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字节做一次解密
                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 小铁匠 阅读(3428) | 评论 (0)编辑 收藏
在c++中new的对象,如果不返回java,必须用release掉,否则内存泄露。包括NewStringUTF,NewObject
。如果返回java不必release,java会自己回收。

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

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

内存泄露可以先从windows资源管理器中,看到随程序运行,内存不断增长的趋势,具体可以用hp jmeter检测在运行程序时,加jvm参数 -Xrunhprof:heap=all,cutoff=0 ,生成java.hprof.txt,用jmeter打开,Metric -> Residual Objects (Count),可以看到未回收的对象,选中要查看的对象,点Mark记录下要查看的对象,Window -> New Window 打开新窗口,用Metric -> Reference Graph Tree,然后点Find Immediately可以看到对象被哪里引用。


找出内存泄漏另一方法

程序有内存泄漏的第一个迹象通常是它抛出一个 OutOfMemoryError,或者因为频繁的垃圾收集而表现出糟糕的性能。幸运的是,垃圾收集可以提供能够用来诊断内存泄漏的大量信息。如果以 -verbose:gc 或者 -Xloggc 选项调用 JVM,那么每次 GC 运行时在控制台上或者日志文件中会打印出一个诊断信息,包括它所花费的时间、当前堆使用情况以及恢复了多少内存。记录 GC 使用情况并不具有干扰性,因此如果需要分析内存问题或者调优垃圾收集器,在生产环境中默认启用 GC 日志是值得的。

有工具可以利用 GC 日志输出并以图形方式将它显示出来,JTune 就是这样的一种工具(请参阅 参考资料)。观察 GC 之后堆大小的图,可以看到程序内存使用的趋势。对于大多数程序来说,可以将内存使用分为两部分:baseline 使用和 current load 使用。对于服务器应用程序,baseline 使用就是应用程序在没有任何负荷、但是已经准备好接受请求时的内存使用,current load 使用是在处理请求过程中使用的、但是在请求处理完成后会释放的内存。只要负荷大体上是恒定的,应用程序通常会很快达到一个稳定的内存使用水平。如果在应用程序已经完成了其初始化并且负荷没有增加的情况下,内存使用持续增加,那么程序就可能在处理前面的请求时保留了生成的对象。


图 1 显示  GC 之后应用程序堆大小随着时间的变化图。上升趋势是存在内存泄漏的警示信号。(在真实的应用程序中,坡度不会这么大,但是在收集了足够长时间的 GC 数据后,上升趋势通常会表现得很明显。)


图 1. 持续上升的内存使用趋势

确信有了内存泄漏后,下一步就是找出哪种对象造成了这个问题。所有内存分析器都可以生成按照对象类进行分解的堆快照。有一些很好的商业堆分析工具,但是找出内存泄漏不一定要花钱买这些工具 —— 内置的 hprof 工具也可完成这项工作。要使用 hprof 并让它跟踪内存使用,需要以 -Xrunhprof:heap=sites 选项调用 JVM。

清单 3 显示分解了应用程序内存使用的 hprof 输出的相关部分。(hprof 工具在应用程序退出时,或者用 kill -3 或在 Windows 中按 Ctrl+Break 时生成使用分解。)注意两次快照相比,Map.EntryTaskint[] 对象有了显著增加。

请参阅 清单 3

清单 4 展示了 hprof 输出的另一部分,给出了 Map.Entry 对象的分配点的调用堆栈信息。这个输出告诉我们哪些调用链生成了 Map.Entry 对象,并带有一些程序分析,找出内存泄漏来源一般来说是相当容易的。


清单 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);
当jstr是null时,env->GetStringLength(jstr)会出错,导致jvm崩溃
posted @ 2006-03-02 11:23 小铁匠 阅读(4229) | 评论 (0)编辑 收藏
HP-UX下使用JNI访问标准C++程序

问题的关键在于用aCC编译时的参数
根据HP网站上的两篇文章可以很容易的使用JNI访问传统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 
但是,如果代码中使用到了标准C++,也就是用到了STL,就会出现莫名其妙的JVM crash. 而且一般的现象是使用string的时候出错

最后发现是JVM的多线程机制和aCC编译的缺省的多线程机制不一样.所以编译时需要加参数指定
总的说来,编译参数为
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 是指定多线程机制;同时必须添加-D_HPUX_SOURCE 参数,否则,编译时会出现奇怪的错误
连接参数为
-AA -b -lCsup_v2 -lstd_v2 
值得注意的是根据上面所说的第二篇文章可知使用-AA编译连接时,要连的库是libCsup_v2.sllibstd_v2.sl(这两个库是支持标准C++的库),而不是第一篇文章中提到的libCsup.sllibstd.sl(这两个库是支持传统C++的库). 

另外,有几个碰到的问题
1. 
如果编译参数没有指定多线程机制,禁用JIT(启动JVM加参数:-Djava.compiler=none -Xint )可以使简单的例子通过,但是有些情况下还是会出错

2. 
null作为String传入JNI native接口代码中是,使用env->GetStringUTFChars(jstring)会出现如下错误导致虚拟机崩溃
Function=verify_instance_jfieldID__18jfieldIDWorkaroundSFP12klassOopDescP9_jfieldID 

3. 
在使用String作为JNI的传入传出参数,使用GetStringUTFChars解决不了中文问题,还是会有乱码正确的解决方法是使用以下两个函数
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 小铁匠 阅读(973) | 评论 (0)编辑 收藏

使用Axis传送附件有两种方式:

1. 将你要传送的文件封装在DataHandler中,然后将DataHandler对象或DataHandler数组(多个文件传送的时候)作为客户端调用函数的参数(从客户端上传文件到服务器)Axis服务的返回类型(从服务器端下载文件到客户端)进行传输。

2. 还有一种方式是直接修改soap信封的内容,将附件信息加到soap信封中发送。

这里我们只讨论第一种方法,因为它实现起来非常简单。关于第二种方法在Axis包的webapps/attachments/TestRf.java中有详细的原码可以参考。

下面的例子是把文件从服务器端下载到客户端:

1.服务端程序:

假设传输多个文件:在服务器端将文件取出来,并将文件封装在DataHandler数组中。
代码如下:

 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数组中,并返回。

2. 客户端的访问:

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

 URL myURL = new URL("http://192.168.0.26:8080/axis/servlet/AxisServlet");
 call.setTargetEndpointAddress(myURL); //设定服务的主机和位置
 call.setOperationName(new QName("urn:MyAttachServer","echoDir")); //设置要调用的服务的方法
 QName qnameAttachment = new QName("urn:MyAttachServer","DataHandler");

 call.registerTypeMapping(DataHandler.class, qnameAttachment, JAFDataHandlerSerializerFactory.class,JAFDataHandlerDeserializerFactory.class); //为附件(即DataHandler类)创建序列化生成器

 call.addParameter("source", XMLType.XSD_STRING ,ParameterMode.IN); //设置服务调用方法的传入参数类型
 call.setReturnType(XMLType.SOAP_ARRAY); //设置调用服务方法的返回类型,由于返回的是DataHandler数组,所以设置为SOAP_ARRAY类型
 javax.activation.DataHandler[] ret = (javax.activation.DataHandler[])call.invoke(new Object[]{null}); //调用方法

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

3. 服务的部署:

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

  编写deploy.wsdd文件:

 <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java" xmlns:ns1="urn:att_STC_Server" >
  <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,部署服务。

posted @ 2006-02-20 16:28 小铁匠 阅读(1007) | 评论 (1)编辑 收藏

2004-05-29 17:39:53

主题: 使用Java实现CA(四)

    前面几篇文章已经把如何用Java实现一个CA基本上讲完了.但是他们都有一个特点,就是用户的信息都是在现场获取的,不能做申请和签发相分离.今天我们要讲述的是PKCS#10证书请求文件.它的作用就是可以使申请和签发相分离.

    PKCS#10证书请求结构中的主要信息包含了被签发者(证书申请者)的主体名称(DN)和他的公钥.因此一个CA在获取到一个PKCS#10证书请求后,就可以从中获取到任何和签发证书有关的信息,然后用它自己的私钥签发证书.

    使用BC Provider在Java中构造一个证书请求格式的对象调用其构造函数即可,这个函数如下:

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

    它的参数是自签名算法,证书申请者的DN,证书申请者的公钥,额外的属性集(就是要申请的证书的扩展信息),申请证书者的私钥.申请证书者的私钥仅仅是用来进行一下自签名,并不出现在证书请求中,需要自签名的目的是保证该公钥确实为申请者所有.

    调用该对象的getEncoded()方法可以将其进行DER编码,然后储存起来,该对象还有另一个构造函数:
    PKCS10CertificationRequest(byte[] bytes)
    这个构造函数的作用就是直接从储存的DER编码中把这个对象还原出来.

    利用证书请求结构进行证书签发的代码如下,这里假设CSR是一个已经获取出来的PKCS10CertificationRequest结构:

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

    这样,申请者的主体DN,申请者的公钥,申请者希望在证书扩展信息中填写的属性都得到了,剩下的事情就和用户在现场输入时一样了,其它的信息一般是申请者不能决定的.另外证书请求格式中有一样信息没有明确给出来,那就是证书的有效期,这个应该单独询问用户,或者用其它的方法保存起来.



[返回顶部]


2004-05-28 16:46:12

主题: 使用Java实现CA(三)

    前几次我已经基本上把如何做CA所需要的基础知识讲得差不多了,今天直接讲如何用Java程序来实现一个CA应该就不是什么太困难的事情了.

    要做CA,第一步要准备好自己的证书和私钥.私钥如何从文件里面读取出来前面已经讲过了.从文件系统中读出证书的代码如下:

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

    这里cerBIS是一个InputStream类型的对象.例如一个标准的X509v3格式的证书文件所形成的输入流.

    第二步就是从用户那里获取输入,然后构造主体名称结构DN,如何构造DN上次已经说过了,如何从用户那里获取输入,这个不在本文讨论范围之内.

    下一步就是获取用户的公钥,好和他所需要的证书对应起来.也有不少CA的做法就是在这里替用户现场生成一对密钥对,然后把公钥放到证书中签发给用户.这个应该看实际需要选择合适的方式.

    现在一切信息都已经准备好了,可以签发证书了,下面的代码说明了这个过程:

    //构造一个证书生成器对象

    X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();

    // 从CA的证书中获取签发者的主体名称(DN)
    // 这里有一点小技巧,我们要把JCE中定义的
    // 用来表示DN的对象X500Principal转化成在
    // BC Provider中的相应的对象X509Name
    // 先从CA的证书中读取出CA的DN进行DER编码
    DERInputStream dnStream =
                 new DERInputStream(
      new ByteArrayInputStream(
       caCert.getSubjectX500Principal().
        getEncoded()));
    // 马上又从编码后的字节流中读取DER编码对象
    DERConstructedSequence  dnSequence =
     (DERConstructedSequence)dnStream.readObject();
    // 利用读取出来的DER编码对象创建X509Name
    // 对象,并设置为证书生成器中的"签发者DN"
    certGen.setIssuerDN(new X509Name(dnSequence));
    // 设置好证书生成器中的"接收方DN"
    certGen.setSubjectDN(subjectDN);
    // 设置好一些扩展字段,包括签发者和
    // 接收者的公钥标识
    certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
    createSubjectKeyId(keyToCertify));
    certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
    createAuthorityKeyId(caCert.getPublicKey()));
    // 设置证书的有效期和序列号
    certGen.setNotBefore(startDate);
    certGen.setNotAfter(endDate);
    certGen.setSerialNumber(serialNumber);
    // 设置签名算法,本例中使用MD5hash后RSA
    // 签名,并且设置好主体的公钥
    certGen.setSignatureAlgorithm("MD5withRSA");
    certGen.setPublicKey(keyToCertify);

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

    这里是上面用到的生成签发者公钥标识的函数: 

    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;
    }

    生成主体公钥标识的函数和上面的类似,把AuthorityKeyIdentifier替换成SubjectKeyIdentifier就可以了.

    这里要注意的是,CA的公钥也是在一份证书里,这种证书的特点是签发者DN和接收者DN一样,也就是说,这种证书是CA自己给自己颁发的证书,也就是"自签名证书",它上面的公钥是CA自身的公钥,用来签名的私钥就是该公钥对应的私钥.一般每个CA都要有这么一份证书,除非该CA不是根CA,即它的权威性不是由它自己证明,而是由它的上级CA证明.但是,最后总归要有一个根CA,它为各个安全应用程序的用户所信赖.

    到这里我们已经把CA最基本的功能如何用Java实现讲完了,下一次讲如何从PKCS#10格式证书请求文件中读取出用户信息,然后直接签发公钥.



[返回顶部]


2004-05-27 15:34:59

主题: 使用Java实现CA(二)

    昨天本来快写完了,结果不小心按了"tab"键,然后向按退格键,结果退到前一个页面了,然后全部都白写了,不爽.只好今天重新写了.

    上次我们讲到如何生成密钥对,以及如何将诸如公钥,私钥,证书等这一类安全对象在文件系统和内存之间来回转换.这些是准备开CA的基本功,今天我们讲一下CA的基本原理以及如何使用主体名称结构DN(Distinguish Name)来表示每一个证书中的主体.

    一份证书就是一个权威机构对一个主体的身份的确认的证明.即一份证书表示一个权威机构确认了一个主体就是它自己,而不是其它的冒名顶替者.主体可以是一个个人,也可以不是,例如,在需要安全服务的时候,需要为一台网站的服务器颁发证书,这种证书的主体就是一台服务器.签署证书的权威机构就叫做CA,该权威机构本身也是一个主体.权威机构通过对包含有待认证的主体的一系列信息的待签名证书"(TBS,to be signed)进行数字签名来表示该机构对它的认可.一份包含了待认证的主体的相关信息的TBS再加上CA使用自己的私钥进行签名产生的字节流放在一起,就构成了一份标准的X509证书.

    一个TBS中包含了以下这些主要信息:

    证书的版本,通常是3(X509v3)

    证书的序列号,RFC3280中规定,每个CA必须确保它颁发的每一份证书的序列号都是唯一的,并且序列号只能使用非负整数.

    签发者(CA)的主体名称,一个DN对象.

    证书的有效期,表示方法是两个时间值,表示了该证书从何时开始生效,何时开始作废.

    待认证的主体的主体名称,也是一个DN对象.

    待认证的主体的公钥,任何安全应用在确认完证书的有效性后,就可以开始使用该主体的公钥与之进行安全通信.

    如果是X509v3证书,即版本号是3的话,后面还有一个证书扩展信息字段,可以在证书里面添加一些其它的信息.

    下面我们来看一下表示主体的主体名称结构:DN.这个结就构是一个属性的集合.每个属性有属性名称和属性值.它的作用就是用来表示"我是谁",也就是说,这个证书到底是谁颁发给谁的,这个证书对应的公钥是谁拥有的.

    通常使用一个字符串来表示DN结构,这种字符串说明了这种结构中的各个属性的类型和值:

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

    这里C是国家和地区代码,S和L都是地区代码,S相当于省或者州这样的级别,L相当于城市级别,O是组织机构名称,OU是次级组织机构名称,CN是主体的通用名(common name).在这里,C,S,L等等属性的类型都是相对固定的,例如C一般就是用来表示国家和地区代码,在DN结构中还可以添加一些其它类型的信息,一般也都是以"xxx=xxx"这样来表示的.

    下面我们来说明如何在Java语言中构造出一个主体名称对象.

    BC Provider中使用X509Name对象来表示DN,构造一个X509Name的步骤大体如下:

    先构造两个vector对象,用来表示属性名称和属性值:

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

    然后在oids这个用来表示属性名称的vector对象中将属性名称一个一个添加进去:

    oids.addElement(X509Name.C);

    ......

    oids.addElement(X509Name.CN);

    X509Name对象里面有若干常量,例如上面的X509Name.C.还有X509Name.ST等等,都可以和上面举的例子对应起来.

    然后将属性值添加到attributes对象中:

    attributes.addElement("CN");

    ......

    attributes.addElement("Wolfenstein");

    最后就可以构造出一个X509Name对象:

    X509Name SubjectDN = new X509Name(oids, attributes);

    这样,我们的主体名称结构就确立起来了.

    下次我们就可以讲关键的部分了,那就是如何用Java程序完成CA最重要的功能,签署证书.



[返回顶部]


2004-05-25 21:23:52

主题: 使用Java实现CA(一)

    通过我昨天的文章大家应该已经清楚了,用Java写信息安全方面的程序需要做的准备工作.好了,现在假设你已经是一个对Java语言本身比较熟悉,能够用Java写一些程序的人,并且这些该下载的jar包已经下载好了,可以正式开工了.

    在所有的此类程序的开头(无论是在类的构造函数中也好,在初始化函数中也好),都要先来上这么一句:Security.addProvider(new BouncyCastleProvider());将BouncyCaslte的Provider添加到系统中,这样以后系统在运行相关程序的时候调用的就是这个Provider中的加密算法.

    然后我们就可以开始开CA了.首先,作为一个CA要有自己的一对公钥和私钥,我们先要生成这么一对.使用KeyPairGenerator对象就可以了,调用KeyPairGenerator.getInstance方法可以根据要生成的密钥类型来产生一个合适的实例,例如常用的RSA,DSA等.然后调用该对象的initialize方法和generateKeyPair方法就可以产生一个KeyPair对象了.然后调用KeyPair对象中的相应方法就可以获取生成的密钥对中的公钥和私钥了.

    有了公钥和私钥对以后,下面的一个很现实问题就是如何把它们储存起来.通常我们要对这些安全对象,如公钥,私钥,证书等先进行编码.编码的目的是为了把结构复杂的安全对象变成字节流以便存储和读取,如DER编码.另外,通常还把DER编码后的字节流再次进行base64编码,以便使字节流中所有的字节都变成可打印的字节.

    在Java语言中,这些安全对象基本上都有getEncoded()方法.例如:

    byte[] keyBytes = privateKey.getEncoded();

    这样就把一个私钥进行了DER编码后的结果保存到一个byte数组中了.然后就可以把这个byte数组保存到任何介质中.如果有必要的话,可以使用BC Provider中的Base64编码解码器类进行编码,就像这样:

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

    要从文件中读取私钥则应该这样:

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

    这里说明一下,对RSA私钥进行编码就会自动地按照PKCS8进行.因此读取的时候将包含编码的字节数组作为PKCS8EncodedKeySpec对象的构造函数的参数就可以生成一个该类型的对象.然后创建一个密钥工厂对象就可以按照要求生成一个RSA私钥了.很显然这里的keyData应该是和上面的keyBytes内容相同.

    为了提高系统的安全性,通常私钥在经过DER编码后,还会使用一个口令进行加密,然后再储存在文件系统中.在使用私钥的时候,如果没有正确的口令,是无法把私钥还原出来的.

    保存证书和保存私钥类似.Certificate对象中也有一个getEncoded的方法.

    这次就讲这些.大家应该可以把这些安全对象很熟练地从文件系统和内存之间来回地折腾了吧.这对以后实现CA是很重要的.下次我会讲一下证书中表示主体的方法:DN.



[返回顶部]


2004-05-24 17:23:06

主题: 使用Java开发和信息安全相关的程序

    这里说的信息安全是相对于系统安全而言的,它更侧重于加密,解密,数字签名,验证,证书等等.而系统安全主要侧重于系统本身是否有安全漏洞,如常见的由于软件设计的不完善而导致的满天飞的缓冲区溢出等等.

    Java语言中负责加密功能的部件是JCE(Java Crypto Extenstion),它使用开放式的思想,可以允许用户自己编写加密算法的具体实现的模块等.这些东西被称为JCE Provider,JCE的提供者.SUN公司本身提供了一些Provider.不过我推荐使用Bouncy Castle的Provider.原因是它实现的加密算法多,连比较新的椭圆曲线(ECC)算法都有了.去http://www.bouncycastle.org/可以找到你所希望的.Bouncy Castle除了提供Provider本身以外,还包括了一个S/MIME和一个open pgp 的jar包只有Provider本身是必要的,后两个包是方便你编程而提供的.例如有了S/MIME包后,你就不再需要为诸如"加密一个字符串或者一片文章然后签名"之类的很现实的应用程序写上一大堆代码了,只要引用S/MIME包中的某几个类,很快就可以搞定.而open pgp的存在,使你在用Java编写和PGP/GPG交互的程序时方便多了.

    这次先写这么多了,下次开始具体讲把这些东西搞下来后怎么开始干活吧.我以我现在手头上正在做的事情告诉大家,如何做一个CA.

    有了BC Provider,开CA,真的就是这么简单!

posted @ 2006-02-20 11:07 小铁匠 阅读(6098) | 评论 (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 小铁匠 阅读(498) | 评论 (0)编辑 收藏
仅列出标题
共2页: 上一页 1 2