- 浏览: 50707 次
- 性别:
- 来自: 武汉
文章分类
- 全部博客 (49)
- java基础 (9)
- mysql (1)
- oracle (1)
- linux (6)
- struts (0)
- spring (0)
- hibernate (0)
- struts2 (1)
- javascript (1)
- jquery (1)
- extjs (0)
- css (0)
- 架构 (6)
- 版本控制(svn、git) (1)
- resin (0)
- jboss (0)
- nginx (0)
- 模板技术(freemarker、velocity ) (0)
- android (0)
- 搜索(lucene、solr) (0)
- 工作流jbpm (0)
- webservice (4)
- ejb3 (6)
- java图表技术 (3)
- 构建与部署(ant 、maven) (0)
- 生活感悟 (1)
- 开发资源 (1)
- java分布式 (7)
最新评论
-
daunty:
其实不一定的,闲的时候可以关掉多余的服务器就好了哦,水平架构不 ...
由12306.cn谈谈网站性能技术 -
iamicebergs:
写得很好,有见解~
由12306.cn谈谈网站性能技术
安全性是Java应用程序的非功能性需求的重要组成部分,如同其它的非功能性需求一样,安全性很容易被开发人员所忽略。当然,对于Java EE的开发人员来说,安全性的话题可能没那么陌生,用户认证和授权可能是绝大部分Web应用都有的功能。类似Spring Security这样的框架,也使得开发变得更加简单。本文并不会讨论Web应用的安全性,而是介绍Java安全一些底层和基本的内容。
认证
用户认证是应用安全性的重要组成部分,其目的是确保应用的使用者具有合法的身份。 Java安全中使用术语主体(Subject)来表示访问请求的来源。一个主体可以是任何的实体。一个主体可以有多个不同的身份标识(Principal)。比如一个应用的用户这类主体,就可以有用户名、身份证号码和手机号码等多种身份标识。除了身份标识之外,一个主体还可以有公开或是私有的安全相关的凭证(Credential),包括密码和密钥等。
典型的用户认证过程是通过登录操作来完成的。在登录成功之后,一个主体中就具备了相应的身份标识。Java提供了一个可扩展的登录框架,使得应用开发人员可以很容易的定制和扩展与登录相关的逻辑。登录的过程由LoginContext启动。在创建LoginContext的时候需要指定一个登录配置(Configuration)的名称。该登录配置中包含了登录所需的多个LoginModule的信息。每个LoginModule实现了一种登录方式。当调用LoginContext的login方法的时候,所配置的每个LoginModule会被调用来执行登录操作。如果整个登录过程成功,则通过getSubject方法就可以获取到包含了身份标识信息的主体。开发人员可以实现自己的LoginModule来定制不同的登录逻辑。
每个LoginModule的登录方式由两个阶段组成。第一个阶段是在login方法的实现中。这个阶段用来进行必要的身份认证,可能需要获取用户的输入,以及通过数据库、网络操作或其它方式来完成认证。当认证成功之后,把必要的信息保存起来。如果认证失败,则抛出相关的异常。第二阶段是在commit或abort方法中。由于一个登录过程可能涉及到多个LoginModule。LoginContext会根据每个LoginModule的认证结果以及相关的配置信息来确定本次登录是否成功。LoginContext用来判断的依据是每个LoginModule对整个登录过程的必要性,分成必需、必要、充分和可选这四种情况。如果登录成功,则每个LoginModule的commit方法会被调用,用来把身份标识关联到主体上。如果登录失败,则LoginModule 的abort方法会被调用,用来清除之前保存的认证相关信息。
在LoginModule进行认证的过程中,如果需要获取用户的输入,可以通过CallbackHandler和对应的Callback来完成。每个Callback可以用来进行必要的数据传递。典型的启动登录的过程如下:
public Subject login() throws LoginException {
TextInputCallbackHandler callbackHandler = new TextInputCallbackHandler();
LoginContext lc = new LoginContext("SmsApp", callbackHandler);
lc.login();
return lc.getSubject();
} 这里的SmsApp是登录配置的名称,可以在配置文件中找到。该配置文件的内容也很简单。
SmsApp {
security.login.SmsLoginModule required;
}; 这里声明了使用security.login.SmsLoginModule这个登录模块,而且该模块是必需的。配置文件可以通过启动程序时的参数java.security.auth.login.config来指定,或修改JVM的默认设置。下面看看SmsLoginModule的核心方法login和commit。
public boolean login() throws LoginException {
TextInputCallback phoneInputCallback = new TextInputCallback("Phone number: ");
TextInputCallback smsInputCallback = new TextInputCallback("Code: ");
try {
handler.handle(new Callback[] {phoneInputCallback, smsInputCallback});
} catch (Exception e) {
throw new LoginException(e.getMessage());
}
String code = smsInputCallback.getText();
boolean isValid = code.length() > 3; //此处只是简单的进行验证。
if (isValid) {
phoneNumber = phoneInputCallback.getText();
}
return isValid;
}
public boolean commit() throws LoginException {
if (phoneNumber != null) {
subject.getPrincipals().add(new PhonePrincipal(phoneNumber));
return true;
}
return false;
} 这里使用了两个TextInputCallback来获取用户的输入。当用户输入的编码有效的时候,就把相关的信息记录下来,此处是用户的手机号码。在commit方法中,就把该手机号码作为用户的身份标识与主体关联起来。
权限控制
在验证了访问请求来源的合法身份之后,另一项工作是验证其是否具有相应的权限。权限由Permission及其子类来表示。每个权限都有一个名称,该名称的含义与权限类型相关。某些权限有与之对应的动作列表。比较典型的是文件操作权限FilePermission,它的名称是文件的路径,而它的动作列表则包括读取、写入和执行等。Permission类中最重要的是implies方法,它定义了权限之间的包含关系,是进行验证的基础。
权限控制包括管理和验证两个部分。管理指的是定义应用中的权限控制策略,而验证指的则是在运行时刻根据策略来判断某次请求是否合法。策略可以与主体关联,也可以没有关联。策略由Policy来表示,JDK提供了基于文件存储的基本实现。开发人员也可以提供自己的实现。在应用运行过程中,只可能有一个Policy处于生效的状态。验证部分的具体执行者是AccessController,其中的checkPermission方法用来验证给定的权限是否被允许。在应用中执行相关的访问请求之前,都需要调用checkPermission方法来进行验证。如果验证失败的话,该方法会抛出AccessControlException异常。 JVM中内置提供了一些对访问关键部分内容的访问控制检查,不过只有在启动应用的时通过参数-Djava.security.manager启用了安全管理器之后才能生效,并与策略相配合。
与访问控制相关的另外一个概念是特权动作。特权动作只关心动作本身所要求的权限是否具备,而并不关心调用者是谁。比如一个写入文件的特权动作,它只要求对该文件有写入权限即可,并不关心是谁要求它执行这样的动作。特权动作根据是否抛出受检异常,分为PrivilegedAction和PrivilegedExceptionAction。这两个接口都只有一个run方法用来执行相关的动作,也可以向调用者返回结果。通过AccessController的doPrivileged方法就可以执行特权动作。
Java安全使用了保护域的概念。每个保护域都包含一组类、身份标识和权限,其意义是在当访问请求的来源是这些身份标识的时候,这些类的实例就自动具有给定的这些权限。保护域的权限既可以是固定,也可以根据策略来动态变化。ProtectionDomain类用来表示保护域,它的两个构造方法分别用来支持静态和动态的权限。一般来说,应用程序通常会涉及到系统保护域和应用保护域。不少的方法调用可能会跨越多个保护域的边界。因此,在AccessController进行访问控制验证的时候,需要考虑当前操作的调用上下文,主要指的是方法调用栈上不同方法所属于的不同保护域。这个调用上下文一般是与当前线程绑定在一起的。通过AccessController的getContext方法可以获取到表示调用上下文的AccessControlContext对象,相当于访问控制验证所需的调用栈的一个快照。在有些情况下,会需要传递此对象以方便在其它线程中进行访问控制验证。
考虑下面的权限验证代码:
Subject subject = new Subject();
ViewerPrincipal principal = new ViewerPrincipal("Alex");
subject.getPrincipals().add(principal);
Subject.doAsPrivileged(subject, new PrivilegedAction<Object>() {
public Object run() {
new Viewer().view();
return null;
}
}, null); 这里创建了一个新的Subject对象并关联上身份标识。通常来说,这个过程是由登录操作来完成的。通过Subject的doAsPrivileged方法就可以执行一个特权动作。Viewer对象的view方法会使用AccessController来检查是否具有相应的权限。策略配置文件的内容也比较简单,在启动程序的时候通过参数java.security.auth.policy指定文件路径即可。
grant Principal security.access.ViewerPrincipal "Alex" {
permission security.access.ViewPermission "CONFIDENTIAL";
}; //这里把名称为CONFIDENTIAL的ViewPermission授权给了身份标识为Alex的主体。 加密、解密与签名
构建安全的Java应用离不开加密和解密。Java的密码框架采用了常见的服务提供者架构,以提供所需的可扩展性和互操作性。该密码框架提供了一系列常用的服务,包括加密、数字签名和报文摘要等。这些服务都有服务提供者接口(SPI),服务的实现者只需要实现这些接口,并注册到密码框架中即可。比如加密服务Cipher的SPI接口就是CipherSpi。每个服务都可以有不同的算法来实现。密码框架也提供了相应的工厂方法用来获取到服务的实例。比如想使用采用MD5算法的报文摘要服务,只需要调用MessageDigest.getInstance("MD5")即可。
加密和解密过程中并不可少的就是密钥(Key)。加密算法一般分成对称和非对称两种。对称加密算法使用同一个密钥进行加密和解密;而非对称加密算法使用一对公钥和私钥,一个加密的时候,另外一个就用来解密。不同的加密算法,有不同的密钥。对称加密算法使用的是SecretKey,而非对称加密算法则使用PublicKey和PrivateKey。与密钥Key对应的另一个接口是KeySpec,用来描述不同算法的密钥的具体内容。比如一个典型的使用对称加密的方式如下:
KeyGenerator generator = KeyGenerator.getInstance("DES");
SecretKey key = generator.generateKey();
saveFile("key.data", key.getEncoded());
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
String text = "Hello World";
byte[] encrypted = cipher.doFinal(text.getBytes());
saveFile("encrypted.bin", encrypted);加密的时候首先要生成一个密钥,再由Cipher服务来完成。可以把密钥的内容保存起来,方便传递给需要解密的程序。
byte[] keyData = getData("key.data");
SecretKeySpec keySpec = new SecretKeySpec(keyData, "DES");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] data = getData("encrypted.bin");
byte[] result = cipher.doFinal(data);解密的时候先从保存的文件中得到密钥编码之后的内容,再通过SecretKeySpec获取到密钥本身的内容,再进行解密。
报文摘要的目的在于防止信息被有意或无意的修改。通过对原始数据应用某些算法,可以得到一个校验码。当收到数据之后,只需要应用同样的算法,再比较校验码是否一致,就可以判断数据是否被修改过。相对原始数据来说,校验码长度更小,更容易进行比较。消息认证码(Message Authentication Code)与报文摘要类似,不同的是计算的过程中加入了密钥,只有掌握了密钥的接收者才能验证数据的完整性。
使用公钥和私钥就可以实现数字签名的功能。某个发送者使用私钥对消息进行加密,接收者使用公钥进行解密。由于私钥只有发送者知道,当接收者使用公钥解密成功之后,就可以判定消息的来源肯定是特定的发送者。这就相当于发送者对消息进行了签名。数字签名由Signature服务提供,签名和验证的过程都比较直接。
Signature signature = Signature.getInstance("SHA1withDSA");
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("DSA");
KeyPair keyPair = keyGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
signature.initSign(privateKey);
byte[] data = "Hello World".getBytes();
signature.update(data);
byte[] signatureData = signature.sign(); //得到签名
PublicKey publicKey = keyPair.getPublic();
signature.initVerify(publicKey);
signature.update(data);
boolean result = signature.verify(signatureData); //进行验证验证数字签名使用的公钥可以通过文件或证书的方式来进行发布。
安全套接字连接
在各种数据传输方式中,网络传输目前使用较广,但是安全隐患也更多。安全套接字连接指的是对套接字连接进行加密。加密的时候可以选择对称加密算法。但是如何在发送者和接收者之间安全的共享密钥,是个很麻烦的问题。如果再用加密算法来加密密钥,则成为了一个循环问题。非对称加密算法则适合于这种情况。私钥自己保管,公钥则公开出去。发送数据的时候,用私钥加密,接收者用公开的公钥解密;接收数据的时候,则正好相反。这种做法解决了共享密钥的问题,但是另外的一个问题是如何确保接收者所得到的公钥确实来自所声明的发送者,而不是伪造的。为此,又引入了证书的概念。证书中包含了身份标识和对应的公钥。证书由用户所信任的机构签发,并用该机构的私钥来加密。在有些情况下,某个证书签发机构的真实性会需要由另外一个机构的证书来证明。通过这种证明关系,会形成一个证书的链条。而链条的根则是公认的值得信任的机构。只有当证书链条上的所有证书都被信任的时候,才能信任证书中所给出的公钥。
日常开发中比较常接触的就是HTTPS,即安全的HTTP连接。大部分用Java程序访问采用HTTPS网站时出现的错误都与证书链条相关。有些网站采用的不是由正规安全机构签发的证书,或是证书已经过期。如果必须访问这样的HTTPS网站的话,可以提供自己的套接字工厂和主机名验证类来绕过去。另外一种做法是通过keytool工具把证书导入到系统的信任证书库之中。
URL url = new URL("https://localhost:8443");
SSLContext context = SSLContext.getInstance("TLS");
context.init(new KeyManager[] {}, new TrustManager[] {new MyTrustManager()}, new SecureRandom());HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(context.getSocketFactory());
connection.setHostnameVerifier(new MyHostnameVerifier());这里的MyTrustManager实现了X509TrustManager接口,但是所有方法都是默认实现。而MyHostnameVerifier实现了HostnameVerifier接口,其中的verify方法总是返回true。
认证
用户认证是应用安全性的重要组成部分,其目的是确保应用的使用者具有合法的身份。 Java安全中使用术语主体(Subject)来表示访问请求的来源。一个主体可以是任何的实体。一个主体可以有多个不同的身份标识(Principal)。比如一个应用的用户这类主体,就可以有用户名、身份证号码和手机号码等多种身份标识。除了身份标识之外,一个主体还可以有公开或是私有的安全相关的凭证(Credential),包括密码和密钥等。
典型的用户认证过程是通过登录操作来完成的。在登录成功之后,一个主体中就具备了相应的身份标识。Java提供了一个可扩展的登录框架,使得应用开发人员可以很容易的定制和扩展与登录相关的逻辑。登录的过程由LoginContext启动。在创建LoginContext的时候需要指定一个登录配置(Configuration)的名称。该登录配置中包含了登录所需的多个LoginModule的信息。每个LoginModule实现了一种登录方式。当调用LoginContext的login方法的时候,所配置的每个LoginModule会被调用来执行登录操作。如果整个登录过程成功,则通过getSubject方法就可以获取到包含了身份标识信息的主体。开发人员可以实现自己的LoginModule来定制不同的登录逻辑。
每个LoginModule的登录方式由两个阶段组成。第一个阶段是在login方法的实现中。这个阶段用来进行必要的身份认证,可能需要获取用户的输入,以及通过数据库、网络操作或其它方式来完成认证。当认证成功之后,把必要的信息保存起来。如果认证失败,则抛出相关的异常。第二阶段是在commit或abort方法中。由于一个登录过程可能涉及到多个LoginModule。LoginContext会根据每个LoginModule的认证结果以及相关的配置信息来确定本次登录是否成功。LoginContext用来判断的依据是每个LoginModule对整个登录过程的必要性,分成必需、必要、充分和可选这四种情况。如果登录成功,则每个LoginModule的commit方法会被调用,用来把身份标识关联到主体上。如果登录失败,则LoginModule 的abort方法会被调用,用来清除之前保存的认证相关信息。
在LoginModule进行认证的过程中,如果需要获取用户的输入,可以通过CallbackHandler和对应的Callback来完成。每个Callback可以用来进行必要的数据传递。典型的启动登录的过程如下:
public Subject login() throws LoginException {
TextInputCallbackHandler callbackHandler = new TextInputCallbackHandler();
LoginContext lc = new LoginContext("SmsApp", callbackHandler);
lc.login();
return lc.getSubject();
} 这里的SmsApp是登录配置的名称,可以在配置文件中找到。该配置文件的内容也很简单。
SmsApp {
security.login.SmsLoginModule required;
}; 这里声明了使用security.login.SmsLoginModule这个登录模块,而且该模块是必需的。配置文件可以通过启动程序时的参数java.security.auth.login.config来指定,或修改JVM的默认设置。下面看看SmsLoginModule的核心方法login和commit。
public boolean login() throws LoginException {
TextInputCallback phoneInputCallback = new TextInputCallback("Phone number: ");
TextInputCallback smsInputCallback = new TextInputCallback("Code: ");
try {
handler.handle(new Callback[] {phoneInputCallback, smsInputCallback});
} catch (Exception e) {
throw new LoginException(e.getMessage());
}
String code = smsInputCallback.getText();
boolean isValid = code.length() > 3; //此处只是简单的进行验证。
if (isValid) {
phoneNumber = phoneInputCallback.getText();
}
return isValid;
}
public boolean commit() throws LoginException {
if (phoneNumber != null) {
subject.getPrincipals().add(new PhonePrincipal(phoneNumber));
return true;
}
return false;
} 这里使用了两个TextInputCallback来获取用户的输入。当用户输入的编码有效的时候,就把相关的信息记录下来,此处是用户的手机号码。在commit方法中,就把该手机号码作为用户的身份标识与主体关联起来。
权限控制
在验证了访问请求来源的合法身份之后,另一项工作是验证其是否具有相应的权限。权限由Permission及其子类来表示。每个权限都有一个名称,该名称的含义与权限类型相关。某些权限有与之对应的动作列表。比较典型的是文件操作权限FilePermission,它的名称是文件的路径,而它的动作列表则包括读取、写入和执行等。Permission类中最重要的是implies方法,它定义了权限之间的包含关系,是进行验证的基础。
权限控制包括管理和验证两个部分。管理指的是定义应用中的权限控制策略,而验证指的则是在运行时刻根据策略来判断某次请求是否合法。策略可以与主体关联,也可以没有关联。策略由Policy来表示,JDK提供了基于文件存储的基本实现。开发人员也可以提供自己的实现。在应用运行过程中,只可能有一个Policy处于生效的状态。验证部分的具体执行者是AccessController,其中的checkPermission方法用来验证给定的权限是否被允许。在应用中执行相关的访问请求之前,都需要调用checkPermission方法来进行验证。如果验证失败的话,该方法会抛出AccessControlException异常。 JVM中内置提供了一些对访问关键部分内容的访问控制检查,不过只有在启动应用的时通过参数-Djava.security.manager启用了安全管理器之后才能生效,并与策略相配合。
与访问控制相关的另外一个概念是特权动作。特权动作只关心动作本身所要求的权限是否具备,而并不关心调用者是谁。比如一个写入文件的特权动作,它只要求对该文件有写入权限即可,并不关心是谁要求它执行这样的动作。特权动作根据是否抛出受检异常,分为PrivilegedAction和PrivilegedExceptionAction。这两个接口都只有一个run方法用来执行相关的动作,也可以向调用者返回结果。通过AccessController的doPrivileged方法就可以执行特权动作。
Java安全使用了保护域的概念。每个保护域都包含一组类、身份标识和权限,其意义是在当访问请求的来源是这些身份标识的时候,这些类的实例就自动具有给定的这些权限。保护域的权限既可以是固定,也可以根据策略来动态变化。ProtectionDomain类用来表示保护域,它的两个构造方法分别用来支持静态和动态的权限。一般来说,应用程序通常会涉及到系统保护域和应用保护域。不少的方法调用可能会跨越多个保护域的边界。因此,在AccessController进行访问控制验证的时候,需要考虑当前操作的调用上下文,主要指的是方法调用栈上不同方法所属于的不同保护域。这个调用上下文一般是与当前线程绑定在一起的。通过AccessController的getContext方法可以获取到表示调用上下文的AccessControlContext对象,相当于访问控制验证所需的调用栈的一个快照。在有些情况下,会需要传递此对象以方便在其它线程中进行访问控制验证。
考虑下面的权限验证代码:
Subject subject = new Subject();
ViewerPrincipal principal = new ViewerPrincipal("Alex");
subject.getPrincipals().add(principal);
Subject.doAsPrivileged(subject, new PrivilegedAction<Object>() {
public Object run() {
new Viewer().view();
return null;
}
}, null); 这里创建了一个新的Subject对象并关联上身份标识。通常来说,这个过程是由登录操作来完成的。通过Subject的doAsPrivileged方法就可以执行一个特权动作。Viewer对象的view方法会使用AccessController来检查是否具有相应的权限。策略配置文件的内容也比较简单,在启动程序的时候通过参数java.security.auth.policy指定文件路径即可。
grant Principal security.access.ViewerPrincipal "Alex" {
permission security.access.ViewPermission "CONFIDENTIAL";
}; //这里把名称为CONFIDENTIAL的ViewPermission授权给了身份标识为Alex的主体。 加密、解密与签名
构建安全的Java应用离不开加密和解密。Java的密码框架采用了常见的服务提供者架构,以提供所需的可扩展性和互操作性。该密码框架提供了一系列常用的服务,包括加密、数字签名和报文摘要等。这些服务都有服务提供者接口(SPI),服务的实现者只需要实现这些接口,并注册到密码框架中即可。比如加密服务Cipher的SPI接口就是CipherSpi。每个服务都可以有不同的算法来实现。密码框架也提供了相应的工厂方法用来获取到服务的实例。比如想使用采用MD5算法的报文摘要服务,只需要调用MessageDigest.getInstance("MD5")即可。
加密和解密过程中并不可少的就是密钥(Key)。加密算法一般分成对称和非对称两种。对称加密算法使用同一个密钥进行加密和解密;而非对称加密算法使用一对公钥和私钥,一个加密的时候,另外一个就用来解密。不同的加密算法,有不同的密钥。对称加密算法使用的是SecretKey,而非对称加密算法则使用PublicKey和PrivateKey。与密钥Key对应的另一个接口是KeySpec,用来描述不同算法的密钥的具体内容。比如一个典型的使用对称加密的方式如下:
KeyGenerator generator = KeyGenerator.getInstance("DES");
SecretKey key = generator.generateKey();
saveFile("key.data", key.getEncoded());
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
String text = "Hello World";
byte[] encrypted = cipher.doFinal(text.getBytes());
saveFile("encrypted.bin", encrypted);加密的时候首先要生成一个密钥,再由Cipher服务来完成。可以把密钥的内容保存起来,方便传递给需要解密的程序。
byte[] keyData = getData("key.data");
SecretKeySpec keySpec = new SecretKeySpec(keyData, "DES");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] data = getData("encrypted.bin");
byte[] result = cipher.doFinal(data);解密的时候先从保存的文件中得到密钥编码之后的内容,再通过SecretKeySpec获取到密钥本身的内容,再进行解密。
报文摘要的目的在于防止信息被有意或无意的修改。通过对原始数据应用某些算法,可以得到一个校验码。当收到数据之后,只需要应用同样的算法,再比较校验码是否一致,就可以判断数据是否被修改过。相对原始数据来说,校验码长度更小,更容易进行比较。消息认证码(Message Authentication Code)与报文摘要类似,不同的是计算的过程中加入了密钥,只有掌握了密钥的接收者才能验证数据的完整性。
使用公钥和私钥就可以实现数字签名的功能。某个发送者使用私钥对消息进行加密,接收者使用公钥进行解密。由于私钥只有发送者知道,当接收者使用公钥解密成功之后,就可以判定消息的来源肯定是特定的发送者。这就相当于发送者对消息进行了签名。数字签名由Signature服务提供,签名和验证的过程都比较直接。
Signature signature = Signature.getInstance("SHA1withDSA");
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("DSA");
KeyPair keyPair = keyGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
signature.initSign(privateKey);
byte[] data = "Hello World".getBytes();
signature.update(data);
byte[] signatureData = signature.sign(); //得到签名
PublicKey publicKey = keyPair.getPublic();
signature.initVerify(publicKey);
signature.update(data);
boolean result = signature.verify(signatureData); //进行验证验证数字签名使用的公钥可以通过文件或证书的方式来进行发布。
安全套接字连接
在各种数据传输方式中,网络传输目前使用较广,但是安全隐患也更多。安全套接字连接指的是对套接字连接进行加密。加密的时候可以选择对称加密算法。但是如何在发送者和接收者之间安全的共享密钥,是个很麻烦的问题。如果再用加密算法来加密密钥,则成为了一个循环问题。非对称加密算法则适合于这种情况。私钥自己保管,公钥则公开出去。发送数据的时候,用私钥加密,接收者用公开的公钥解密;接收数据的时候,则正好相反。这种做法解决了共享密钥的问题,但是另外的一个问题是如何确保接收者所得到的公钥确实来自所声明的发送者,而不是伪造的。为此,又引入了证书的概念。证书中包含了身份标识和对应的公钥。证书由用户所信任的机构签发,并用该机构的私钥来加密。在有些情况下,某个证书签发机构的真实性会需要由另外一个机构的证书来证明。通过这种证明关系,会形成一个证书的链条。而链条的根则是公认的值得信任的机构。只有当证书链条上的所有证书都被信任的时候,才能信任证书中所给出的公钥。
日常开发中比较常接触的就是HTTPS,即安全的HTTP连接。大部分用Java程序访问采用HTTPS网站时出现的错误都与证书链条相关。有些网站采用的不是由正规安全机构签发的证书,或是证书已经过期。如果必须访问这样的HTTPS网站的话,可以提供自己的套接字工厂和主机名验证类来绕过去。另外一种做法是通过keytool工具把证书导入到系统的信任证书库之中。
URL url = new URL("https://localhost:8443");
SSLContext context = SSLContext.getInstance("TLS");
context.init(new KeyManager[] {}, new TrustManager[] {new MyTrustManager()}, new SecureRandom());HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(context.getSocketFactory());
connection.setHostnameVerifier(new MyHostnameVerifier());这里的MyTrustManager实现了X509TrustManager接口,但是所有方法都是默认实现。而MyHostnameVerifier实现了HostnameVerifier接口,其中的verify方法总是返回true。
发表评论
-
学习正则表达式
2011-12-26 15:04 791入门 学习正则表达式的最好方法是从例子开始,理解例子之后 ... -
51CTO JAVA NIO技术专题
2011-12-22 21:08 981看到51CTO对为JAVA 的NIO做了一个技术专题,做个标记 ... -
json for java
2011-12-15 13:58 687一、JSON介绍 JSON(JavaScript O ... -
java.util.HashMap源码要点浅析
2011-07-14 14:43 722java.util.HashMap源码要点浅析 1、散列表要 ... -
Java堆和栈的区别
2011-07-12 17:50 714Java堆和栈的区别 堆 ... -
Java深度历险(八)——Java I/O(转自infoq)
2011-07-04 14:52 736在应用程序中,通常会 ... -
Java深度历险(十)——Java对象序列化与RMI(转载自infoq)
2011-06-30 10:11 648对于一个存在于Java虚拟机中的对象来说,其内部的状态只保持在 ... -
java中计算随机数
2010-12-20 11:22 1038java中一般有两种随机数 ...
相关推荐
java深度历险 InfoQ中文站 1、java字节码操作 2、java类的加载、链接和初始化 3、java线程 4、java垃圾回收机制与引用类型 5、java泛型 6、java注解 7、java反射与动态代理 8、javaI/O 9、java安全 10、java对象序列...
本迷你书源自成富先生为InfoQ中文站撰写的《Java深度历险》专栏。本专栏的十篇文章,是关于Java不同方面但深入浅出的主题内容。在几乎每篇专栏的结尾,都有多于平均数量的积极的评论,在InfoQ内部月度内容排行上尤为...
《Java 深度历险》专栏的作者成富,是IBM 中国软件开发中心的高级工程师,也是 我的前同事。他曾经是CTO 毛新生的得意门生,承担过Lotus Mashups 产品的重要 研发职责,现在负责领导Project Vulcan 项目的重要组件在...
理解JMM对于编写高效、正确且线程安全的Java代码至关重要。 Java内存模型规定了程序中的变量是如何在处理器之间以及内存系统中交互的。它包括了主内存(Main Memory)和每个线程的本地内存(Thread Local Memory)...
Jigsaw项目的核心是Java平台模块化系统(JPMS),这有助于更好地适应不同大小的计算设备,并改进了安全性、可维护性和性能。在文档中也提到了JEP(JDK Enhancement Proposals),这是为了改进模块化JDK、源码、运行...
Python和R是常见的机器学习编程语言,但Java也有如Weka和 Deeplearning4j这样的库,可以进行深度学习和神经网络开发。 架构师的角色需要理解和整合这些技术,设计出高效、可扩展和可靠的系统。他们需要熟悉软件设计...
《InfoQ架构师2016合集》是面向IT专业人士,尤其是架构师群体的一份珍贵资源,集合了2016年InfoQ平台上的众多精彩文章和讨论,旨在分享和探讨当时的最新技术和最佳实践。InfoQ作为一个全球知名的IT资讯网站,其内容...
InfoQ云生态期刊是一系列深度探讨云计算领域动态、技术趋势和实践案例的专业出版物。这套期刊涵盖了从第一期到第八期的完整内容,为读者提供了丰富的云计算知识库。InfoQ作为一个知名的IT信息与社区平台,其发布的云...
- **JavaWorld** 和 **InfoQ**:这两个网站提供了最新的Java技术和趋势资讯,是跟踪行业动态的好地方。 - **Javadoc**:Java的自动文档生成工具,帮助你理解类库的使用方法。 - **Java EE官方教程**:如果你对企业级...
本书的部分内容在出版早期发表在Java并发编程网和InfoQ等技术社区,得到了非常高的评价。它选取了Java并发编程中最核心的技术进行讲解,从JDK源码、JVM、CPU等多角度全面剖析和讲解了Java并发编程的框架、工具、原理...
除此之外,InfoQ是一个提供最新技术资讯、深度文章和业界新闻的网站,尤其对于跟踪Java发展动态很有帮助。另外,SitePoint和TutorialsPoint也是获取编程教程和指南的好地方。 除了书籍和网站,还有一些开源项目值得...
Java 模块化开发 Java 模块化开发是大型 Java 系统的一个重要特征。模块化是指将项目划分为多个模块,以便改进构建过程,但是在运行时却很少考虑划分模块的问题。在 Java 开发中,模块化可以分为静态模块化(static...
Java开发者在提升技能和了解最新技术动态时,访问一些高质量的英文网站是非常必要的。以下是对这些网站的详细解析,它们涵盖了编程、框架、工具、社区和技术趋势等方面: 1. Oracle Java官网 ...
InfoQ是一个知名的在线平台,提供最新的软件开发资讯、深度文章、会议报道和技术访谈等内容。由于描述是“NULL”,我们无法直接获取到具体的信息,但可以依据标签“源码”和“工具”来推测讨论的内容。 标签“源码...
InfoQ是一个全球知名的软件开发资讯平台,以其深度、专业和前沿的内容深受业界好评。以下是对这些内容的详细解读和相关知识点的梳理: 1. **架构设计原则**:文中可能涵盖了一系列架构设计的基本原则,如单一职责...
这个网站是IBM提供的一个全面的技术学习平台,涵盖了Java、Linux、XML、安全和Web服务等多个技术领域。在这里,你可以找到技术专区,了解最新的技术动态,同时也有各种产品的详细信息,如Websphere、DB2、Lotus、...
《infoQ架构师月刊下部》集合了2013年至2017年8月期间在infoQ平台上发布的关于架构领域的深度文章和专题,是广大架构师和IT从业者学习、研究架构技术的重要资源。infoQ作为一个全球知名的IT技术交流平台,其内容覆盖...
infoq 架构师8月刊 infoq 架构师8月刊 infoq 架构师8月刊