转自:http://joshuasabrina.iteye.com/blog/1798245
导读:
- 这是一篇介绍Java Security能做什么的文章。很遗憾,它不会告诉你怎么去做。
- 本文相关的JDK版本是JDK7,当然,仍适用于JDK6。
Java平台(Java运行时环境,即JVM + Java API) 在多个层面上提供了security机制。
Java Language Security and Bytecode Verification
Java语言层面和字节码验证层面的安全机制
从语言层面来说,Java是类型安全的。它提供了自动的内存管理和GC机制,这些语言特性增强了代码的鲁棒性,减少了安全隐患。
类加载和校验机制确保了只有合法的Java代码才会被执行。
此外,Java语言提供了4种不同的访问权限:private, public, protected, package。这些访问权限可以指派给类,方法和属性,使得程序员可以限制对这些类型的访问。
众所周知,Java编译器会把Java代码编译成字节码。字节码校验器会在字节码执行之前检查它是否符合Java的规则:语言规则、命名空间 、内存管理、栈溢出、类型转换等...在代码执行之前屏蔽一些隐患。
以上罗列的是Java core 和JVM提供的一些security相关机制。不得不提的是,除此之外,Java还提供了一整套功能强大的Security API。这套API覆盖了密码相关的功能、PKI (public key infrastructure)、认证(authentication)、安全通信(secure conmmunication)、访问控制(access control)等方方面面。接下来我们将重点介绍这部分的内容。
Basic Security Architecture
Security 基础架构
Java Security API提供了可互操作的算法和安全服务的实现。服务以provider的形式实现,可以以插件的形式植入应用程序中。程序员可以透明地使用这些服务,如此使得程序员可以集中精力在如何把安全组件集成到自己的应用程序中,而不是去实现这些安全功能。此外,除了Java提供的安全服务外,用户可以编写自定义的security provider,按需扩展Java的security平台。
Security Provider
java.security.Provider抽象了Java security provider。它指定了provider的名字,罗列了它实现的安全服务。多个provider可能同时被配置,他们会以优先级排列。当一个安全服务收到请求,这个服务的最高优先级的provider提供服务。
应用通过相关的getInstance方法来获得安全服务,比如:调用java.security.MessageDigest的getInstance方法来获得一个message digest 算法(MD5)。[例子源于Java官网]
- MessageDigest md = MessageDigest.getInstance("MD5");
如下图:
程序可能通过provider的名字随意地请求一个指定provider的实现:
- MessageDigest md =
- MessageDigest.getInstance("MD5", "ProviderC");
如下图:
提供一个Oracle Provider的清单(JDK7)。
一些相关的文件:
某些provider的配置可能需要用户自定义安全属性。你可以在~jre/lib/security目录下找到这些配置文件,当然,有些配置也可以通过API设置。关于这块的说明会在其他文章里提及,本文只是个overview。
Cryptography Arthitecture (JCA)
密码架构
Java的Cryptography架构是一个提供访问和开发密码功能的框架。它提供了许多cryptographic服务:
- Message digest algorithms 【信息摘要算法, 如:MD5】
- Digital signature algorithms 【数字签名算法,DSA】
- Symmetric bulk encryption 【对称块加密, 如:DES】
- Symmetric stream encryption 【对称流加密, 如:RC4】
- Asymmetric encryption 【非对称加密, 如:RSA】
- Password-based encryption (PBE) 【密码加密】
- Elliptic Curve Cryptography (ECC) 【椭圆曲线加密】
- Key agreement algorithms 【key协议算法】
- Key generators 【key生成器】
- Message Authentication Codes (MACs) 【消息认证码】
- (Pseudo-)random number generators 【伪随机数生成器】
因为历史原因,Cryptography API位于两个独立的包内: java.security(Signature, MessageDigest)和javax.crypto(Cipher, KeyAgreement)。
Java内置的Provider提供了许多通用的密码算法,比如:RSA, DSA, ECDSA等签名算法、DES, AES, ARCFOUR等加密算法、MD5, SHA-1, SHA-256等信息摘要算法、还有Diffie-Hellman和ECDH这样的密钥协商算法。
此外,还有一些特殊的provider。
比如SunPKCS11, 它允许Java代码无缝地使用PKCS#11的compliant tokens。
在Windows平台上,Java还提供了一些native的Provider去连接本地的 Microsoft CryptoAPI。这个provider的名字叫MSCAPI。它允许Java应用无缝地使用Windows平台的cryptographic service。
Public Key Infrastructure
公钥基础构件
PKI是一个术语,主要用于描述一类基于公钥的安全信息交互框架。它允许identity,比如人或者组织,绑定在数字证书上,并且提供一种验证证书的方法。PKI包括了密钥、证书、公钥加密和可信的证书颁发机构(CAs,用于生成和签署证书)。
Java平台提供API和provider支持X.509、CRLs、PKIX证书路径的构建和验证。PKI相关的类可以在java.security和java.security.cert包下获取。
密钥和证书的仓库
Java提供了长期持久化密钥和证书的功能。它通过key store和certificate store来实现。java.security.KeyStore这个类代表了一个key store,一个安全的,用于存储密码和可信证书的仓库。java.security.cert.CertStore类代表了一个certificate store,用于存储不相关的,不受信的证书。同样它可以存储CRLs。
Java平台包含标准的PKCS#11和PKCS#12 key store类型,此外还有一个基于文件的key store类型,叫做JKS(Java Key Store)。Java平台内置了一个名叫cacerts的特殊JKS,它为CA证书提供一个默认的密钥库。
SunPKCS11(之前在密码架构小节有提过哦)包含了一个PKCS#11的key store实现。这意味着安全硬件(比如:smartcard)的密钥和证书可以被Java应用所使用。
此外,Java平台提供了LDAP的certificate store类型用于访问存放在LDAP中的证书。还有一个certificate type用于访问存放在java.util.Collection中的证书。
PKI Tools
Java提供了两个内置的工具帮助用户使用密钥,证书和Key store:keytool和jarsigner。
keytool用于创建和管理key store。它可以
- 创建公钥私钥对
- 显示,导入、导出X.509 v1, v2, v3的证书
- 创建自签名证书
- 基于证书请求创建证书
- 导入证书回复(certificate reply)【在外部CA颁发证书之后,会受到一个回复,该回复通常是一个PKCS7编码的证书】
- 指定公钥证书为可信
jarsigner用于给JAR文件签名或者验证已经签名的JAR包。
Authentication
认证
Authentication是确认用户身份的过程。在Java运行时环境的上下文中,指认证执行Java程序用户的过程。在这种情况下,这个过程依赖于在Cryptography小节描述到的一些服务。
Java平台提供API,用户可以通过插件式的登录模块为应用提供用户认证功能。应用调用LoginContext类(在javax.security.auth.login包内),它依赖于配置。该配置指定了哪个登录模块(所有登录模块必须实现javax.security.auth.spi.LoginModule接口)为应用提供实际的认证功能。
应用只使用标准的LoginContext API, 所以能保持对于底层插件模块的独立性。新的模块可以被无缝地添加到应用中,不需要修改任何的应用代码。如下图:
必须提醒的是,虽说登录模块是插件式的组件,它能够以配置的形式增加到Java平台中,但是他们并没有注册为security provider,所以,他们也不会被Provider的查询模块查到(Security Provider小节的图很能说明问题)。此外,请注意,各登录模块是由各自独立的配置所管理。
Java平台提供了一些内置的登录模块,所有模块都可以再com.sun.security.auth.module包下找到:
- Krb5LoginModule 【Kerberos认证】
- JndiLoginModule 【使用LDAP/NIS的用户名/密码认证】
- KeyStoreLoginModule 【基于Key store的认证】
当然,认证也可以在建立双方之间的安全通讯通道上的过程中完成。Java平台提供了许多标准通信协议的实现(下文将会介绍)。
Secure Communication
安全通信
数据走网络,总会通过一些不需要接收该信息的用户。当数据包含一些私人信息时(比如密码,信用卡号等),我们必须采取措施使这些信息不落入未经授权者之手。我们也需要确保数据在传输过程中不被他人篡改。
Cryptography(请见Cryptography小节)是安全通讯的基础。Java平台提供了很多API来支持和实现许多标准的安全通讯协议。
SSL/TLS
对于这部分协议的实现包含了数据加密、数据完整性、服务器认证和客户端认证(可选)功能。用户的应用可以使用SSL/TLS来为两端提供安全的数据通路。它支持任何应用协议,比如:HTTP。
javax.net.ssl.SSLSocket,这个socket类基于普通的stream socket(java.net.Socket),并封装了SSL/TLS协议功能。此外,你的应用可能需要像New-I/O这样的交替式数据传输功能。javax.net.ssl.SSLEngine类可以帮你生成和解读SSL/TLS数据包。
Java平台还提供API支持插件式(基于Provider)的key manager和trust manager。Key Manager封装在javax.net.ssl.KeyManager内,它管理用于认证的密钥。trust manager封装在javax.net.ssl.TrustManager类中,由它来决断哪个用户是受信的,这基于它自身管理的Key store中的证书。
Java平台包含了一个内置的实现了SSL/TLS协议的Provider,它支持如下协议:
- SSLv3
- TLSv1
- TLSv1.1
- TLSv1.2
SASL
Simple Authentication and Security Layer, SASL定义了认证数据如何被交换,但是它本身并没有指定认证数据的内容。SASL框架可以适用于任何SASL支持的认证机制。目前有很多 标准的SASL机制用于不同的安全级别和部署场景。
用户的应用使用Java的SASL API并不需要强制绑定任何特定的SASL机制,应用可以选择使用自己需要的机制。API支持客户端应用和服务端应用。用户可以使用javax.security.sasl.Sasl类创建SaslClient和SaslServer对象。
SASL机制的实现可以从provider包下找到。每个provider可能提供一个或多个SASL机制。这些provider已经注册到标准的provider架构中,用户可以很方便地使用它们。下面罗列Java平台提供的SASL provider:
- DRAM-MD5,DIGEST-MD5,EXTERNAL,GSSAPI,NTLM,PLAIN client machanisms 【协议相关的说明】
- DRAM-MD5,DIGEST-MD5,GSSAPI,NTLM server machanisms 【协议相关的说明】
GSS-API and Kerberos
GSS-API, Generic Security Service Application Programming Interface。GSS-API提供应用开发者基于大量底层安全机制的统一安全服务入口。目前,Java的GSS-API需要使用Kerberos v5,而Java已经包含了内置的Kerberos实现。注意,之前介绍的Krb5LoginModule可以结合GSS Kerberos使用。此外,Java平台还有内置的SPNEGO(Simple and Protected GSS API Negotiation Mechanism)的实现。
在两个应用使用Java GSS-API安全通信之前,它们必须建立一个共同的安全上下文。上下文封装了共享的状态信息, 比如:密钥。这两个应用都需要创建并使用org.ietf.jgss.GSSContext对象去建立和维护这些共享信息。
Java GSS API放在org.ietf.jgss包下。Java平台也定义了基础的Kerberos类,比如KerberosPrinciple,KerberosTicket,KerberosKey和KeyTab。这些类都在javax.security.auth.kerberos包下。
Access Control
访问控制
Java平台的访问控制架构保护了对敏感资源(比如本地文件)或者应用代码(类的方法)的访问。所有访问控制的决策由security manager(java.lang.SecurityManger类)仲裁。
Java Applets和Java Web应用会自动使用SecurityManager。但是,本地的应用如果通过java命令来执行,默认是不会使用SecurityManager的。如果你想在上述情况下使用,那你的应用必须显示地设置它。在Java程序中,你可以调用java.lang.System#setSecurityManager方法来使用SecurityManager,你也可以通过设置参数的方式达成同样的效果:-Djava.security.manager。
权限
当Java代码被类加载器加载时,类加载器会为代码自动关联下列信息:
- 代码从哪里加载的
- 谁签名了该代码(如果有的话)
- 授权给该代码的默认权限
无论代码是从一个不受信的网络下载(比如: applet)还是从本地文件系统加载,以上信息都会关联到你的代码。代码的加载路径由一个URL表示,code signer是signer的证书链。默认的权限为java.security.Permission对象。
对于网络下载的代码,默认权限会自动授权代码可以访问下载源的主机。对于本地代码,代码默认有对于代码目录和子目录的访问权限。
值得注意的是,在类加载时,系统并不会去验证执行代码的用户身份。如果有必要,应该由应用代码来完成认证的工作(见上文认证小节)。 一旦用户通过认证,应用可以动态地关联运行代码的用户和被执行的代码。这个过程由javax.security.auth.Subject类的doAs方法完成。
策略 (Policy)
如上文所述,一个默认权限的有限集在类加载时由类加载器授权给了代码,Admin可以通过安全策略灵活地管理代码权限。
Java平台把策略封装在java.security.Policy类中。在任何时间,有且只有一个Policy对象被加载到了Java运行时。这个Policy负责仲裁代码是否可以访问受保护的资源。Policy如何仲裁则依赖于实现。比如,可以使用数据库保存授权信息或者使用一个外部的服务。
Java平台提供了默认的策略实现,它从一个或多个ASCII(UTF-8)文件中读取security properties。这些策略文件包含了授权给代码的权限集。这些策略必须符合特定的语法。策略文件可以由简单的文本编辑器或者图形化工具policytool编写。
Policy详解,policytool使用方式和例子: 面向Applet, 面向Application
执行访问控制
当应用请求访问受限资源时,默认来说,Java会评判整个调用栈来决定程序是否可以访问该资源。
如上文所述,资源受SecurityManager保护。Java平台和应用中的安全敏感代码保护着资源访问:
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(perm);
- }
代码中的perm是Permission对象,比如,要读取/tmp/abc文件,那么Permission对象可以构建如下:
- Permission perm =
- new java.io.FilePermission("/tmp/abc", "read");
SecurityManager的默认实现把决策委派给java.security.AccessController的实现。AccessController会遍历调用栈中的每个代码元素的安全策略信息。这些策略基于Admin配置的permission决定请求是否被授权。如果未被授权则跑出java.lang.SecurityException异常。
下图描绘了访问控制的执行过程。在这个特定的例子中,调用栈中有两个元素:ClassA和ClassB。ClassA调用了ClassB的一个方法。这个方法会通过FileInputStream去访问/tmp/abc这个文件。FileInputStream的构造器创建了FilePermission:perm。接着,perm被传递给SecurityManager的checkPermission方法。在本例中,只有ClassA和ClassB的权限需要验证,这是因为,所有的系统代码(FileInputStream,SecurityManager,AccessController)自动获取了所有权限。
从图片中可以看到,ClassA和ClassB加载自不同的路径,并且签名者也不同。它们也可能被授予不同的权限集合。只有这两个类都被授予了访问abc的FilePermission,AccessController才会通过访问abc的请求。
XML Signature
XML签名
Java XML数字签名API用于生成和校验XML的数字签名。XML签名可以应用于任何数据类,XML或者二进制。签名用XML表示。XML签名可以用来保护你的数据并提供数据完整性,消息认证,签名者认证功能。
API支持所有的W3C推荐的XML签名语法。API支持扩展和插件形式。它基于Java Cryptography服务provider架构。该API由6个包组成:
- javax.xml.crypto
- javax.xml.crypto.dsig
- javax.xml.crypto.dsig.keyinfo
- javax.xml.crypto.dsig.spec
- javax.xml.crypto.dom
- javax.xml.crypto.dsig.dom
Security类清单
Package Class/Interface Name Usagecom.sun.security.auth.module | JndiLoginModule | Performs username/password authentication using LDAP or NIS |
com.sun.security.auth.module | KeyStoreLoginModule | Performs authentication based on key store login |
com.sun.security.auth.module | Krb5LoginModule | Performs authentication using Kerberos protocols |
java.lang | SecurityException | Indicates a security violation |
java.lang | SecurityManager | Mediates all access control decisions |
java.lang | System | Installs the SecurityManager |
java.security | AccessController | Called by default implementation of SecurityManager to make access control decisions |
java.security | Key | Represents a cryptographic key |
java.security | KeyStore | Represents a repository of keys and trusted certificates |
java.security | MessageDigest | Represents a message digest |
java.security | Permission | Represents access to a particular resource |
java.security | Policy | Encapsulates the security policy |
java.security | Provider | Encapsulates security service implementations |
java.security | Security | Manages security providers and security properties |
java.security | Signature | Creates and verifies digital signatures |
java.security.cert | Certificate | Represents a public key certificate |
java.security.cert | CertStore | Represents a repository of unrelated and typically untrusted certificates |
java.security.cert | CRL | Represents a CRL |
javax.crypto | Cipher | Performs encryption and decryption |
javax.crypto | KeyAgreement | Performs a key exchange |
javax.net.ssl | KeyManager | Manages keys used to perform SSL/TLS authentication |
javax.net.ssl | SSLEngine | Produces/consumes SSL/TLS packets, allowing the application freedom to choose a transport mechanism |
javax.net.ssl | SSLSocket | Represents a network socket that encapsulates SSL/TLS support on top of a normal stream socket |
javax.net.ssl | TrustManager | Makes decisions about who to trust in SSL/TLS interactions (for example, based on trusted certificates in key stores) |
javax.security.auth | Subject | Represents a user |
javax.security.auth.kerberos | KerberosPrincipal | Represents a Kerberos principal |
javax.security.auth.kerberos | KerberosTicket | Represents a Kerberos ticket |
javax.security.auth.kerberos | KerberosKey | Represents a Kerberos key |
javax.security.auth.kerberos | KerberosTab | Represents a Kerberos keytab file |
javax.security.auth.login | LoginContext | Supports pluggable authentication |
javax.security.auth.spi | LoginModule | Implements a specific authentication mechanism |
javax.security.sasl | Sasl | Creates SaslClient and SaslServer objects |
javax.security.sasl | SaslClient | Performs SASL authentication as a client |
javax.security.sasl | SaslServer | Performs SASL authentication as a server |
org.ietf.jgss | GSSContext | Encapsulates a GSS-API security context and provides the security services available via the context |
Security工具清单
Java Security工具
Tool Usagejar | Creates Java Archive (JAR) files |
jarsigner | Signs and verifies signatures on JAR files |
keytool | Creates and manages key stores |
policytool | Creates and edits policy files for use with default Policy implementation |
Kerberos相关工具
Tool Usagekinit | Obtains and caches Kerberos ticket-granting tickets |
klist | Lists entries in the local Kerberos credentials cache and key table |
ktab | Manages the names and service keys stored in the local Kerberos key table |
参考文档:
1. http://docs.oracle.com/javase/7/docs/technotes/guides/security/overview
2. http://docs.oracle.com/javase/7/docs/api/index.html
3. 《Inside the Java Virtual Machine》by Bill Venners