Java programming language, with its core features and security extensions, enables us in writing powerful secure applications. Security includes language safety, cryptography, Public Key Infrastructure (PKI), authentication, secure communication, and access control. In this chapter, we shall discuss the basics of security and its implementation in Java, with numerous examples to illustrate the concepts.
This section gives an overview of Java security, from secure language features to the security APIs, tools, and built-in provider services, highlighting key packages and classes where applicable.
1. Language Security
The Java language itself provides secure class loading and verification mechanism to ensure that only legitimate Java code is executed. Java programs are compiled into machine-independent representation called bytecode. When these bytecodes are executed, they are verified by a special component of Java Runtime Environment, called bytecode verifier. The verifier thoroughly checks if the bytecodes strictly conform to the language specification and violate language rules or namespace regulations. The verifier also checks for illegal typecasts, stack overflows or underflows and memory management violations. This ensures that only legitimate bytecodes are executed in the Java runtime.
In addition, Java itself is type-safe and provides automatic memory management, garbage collection, and range-checking on arrays etc. enhancing the robustness of application code.
The Java language also enables developers to restrict access to their classes, methods, and fields using different access modifiers such as private, protected, public and package. The unrestricted access specifier is public. The most restrictive specifier is private. The protected specifier allows access to any subclass, or to other classes within the same package. Package-level access only allows access to classes within the same package.
2. Basic Security
Java security technology spans a wide range of areas, including cryptography, public key infrastructure, secure communication, authentication, access control and language safety. It contains a rich set of APIs, tools, and implementations of commonly used security algorithms, mechanisms, and protocols. These together provide the programmers a comprehensive security framework for developing and managing applications.
Three principles were adopted while designing security APIs: Implementation independence, Implementation interoperability and Algorithm extensibility.
The first principle says that the applications themselves need not implement security; instead they can make use of the security services from Java. These services are implemented as providers which are represented by the class java.security.Provider. Oracle’s implementation of Java includes a number of built-in pre-configured providers that implement a basic set of security services. Applications may rely on these providers, which are plugged into the Java platform via a standard interface.
The second principle says that providers are functionally similar and interoperable. This means applications using different providers may seamlessly interoperate.
The last principle says that athough applications usually rely on built-in providers, they are free to use proprietary services or emerging standards that have not yet been implemented. The Java platform is helpful enough to install such custom providers implementing those services.
The following sections give an overview of various security areas such as cryptography, authentication, etc.
3. Java Cryptography Architecture (JCA)
The Java Cryptography Architecture (JCA) is a framework for accessing and developing cryptographic functionality. The JCA is a major part of Java Security Architecture and contains services such as
- Symmetric stream encryption
- Symmetric bulk encryption
- Asymmetric encryption
- Password-based encryption (PBE)
- Elliptic Curve Cryptography (ECC)
- Message digest algorithms
- Digital signature algorithms
- Key agreement algorithms
- Key generators
- Message Authentication Codes (MACs)
- (Pseudo-)random number generators
The cryptography APIs are organized into two distinct packages. The java.security package and javax.crypto package.
The former package contains the classes and interfaces for the security framework. This includes access control related classes and supports generation and storage of cryptographic public key pairs, a number of cryptographic operations such as message digest and signature generation. This package also includes classes that support signed/guarded objects and secure random number generation.
The javax.crypto packages the classes and interfaces for cryptographic operations (such as encryption, key generation and key agreement, and Message Authentication Code generation) and supports secure streams and sealed objects. The encryption includes symmetric, asymmetric, block, and stream ciphers.
The Java platform includes built-in providers for many of the most commonly used cryptographic algorithms, including the RSA, DSA, and ECDSA signature algorithms, the DES, AES, and ARCFOUR encryption algorithms, the MD5, SHA-1, and SHA-256 message digest algorithms, and the Diffie-Hellman and ECDH key agreement algorithms. These default providers implement cryptographic algorithms in Java code.
The Java comes with security providers that implement commonly used cryptographic algorithms, including the RSA, DSA, and ECDSA signature algorithms, the DES, AES, and ARCFOUR encryption algorithms, the MD5, SHA-1, and SHA-256 message digest algorithms, and the Diffie-Hellman and ECDH key agreement algorithms. Java also includes a built-in provider, named SunPKCS11, that acts as a bridge to a native PKCS#11 (v2.x) token.
The cryptography is often divided as two categories: secret key cryptography and public key cryptography. Let us have a brief overview of these two:
3.1. Secret-key Cryptography
In this category of cryptography, both the sender and receiver share the key only known to the communicating parties. Since, often the same key is used for encryption and dycryption, the key is also referred to as symmetric key. One of its important advantages is that it can encrypt data with arbitrary size. Though there are many powerful key generation algorithms, the main challenge is exchanging the secret key in a safe way. Moreover if the secret key is somehow disclosed, the entire purpose is lost.
3.2. Public-key Cryptography
In this both communicating peers maintain a pair of two keys. One of the two keys is only known to that party and is known as private key. The other one may be known to everybody and is known as public key and hence its name. These two keys are related in such a way that one can be used to decrypt data entrypted by the other. The advantage of this method lies in the use of public key. Intruders can even know this key but cannot proceed too much further.
However, public key encryption algorithms do not support encryption of arbitrary size. For example, RSA can encrypt maximum data as follows:
Maximum data size (in bytes) = RSA key size (in bytes) – header
For RSA PKCS#1 v1.5 padding, the header size is 11 bytes. So, for a key size of 256 bytes (i.e. 2048 bits), the maximum size of data that RSA can encrypt is 245 (=256 – 11) bytes. Although, a larger key may be chosen, it takes too much time for encryption/decryption unrealistic for real-time applications. That is why, public key encryption algorithms are not meant for general data encryption. Instead, they are usually used to encrypt secret keys, authentication codes etc. For encryption of arbitrary data, the following strategy may be employed (and is also recommended):
- Generate a symmetric (secret) key with preferred size.
- Get the public key certificate from the intended receiver.
- Encrypt the symmetric key with RSA using receiver’s public key obtained from the certificate and send the encrypted key.
- Decrypt the symmetric key (secret) with the RSA using receiver’s private key.
- Encrypt the data with the symmetric key and send it to the receiver.
- Decrypt the data with the symmetric key.
Steps 1-4 are done only once, whereas steps 5-6 are repeated until the data exchange is over.
4. Public Key Infrastructure (PKI)
This is a framework that deals with the exchange of information based on public key cryptography. The fundamental idea of public key cryptography is digitally signed public key certificates. Java provides APIs to work with these certificates such as creating, building, and validating certificates. The API supports X.509/PKIX compliant digital certificates. The API consists of two packages java.security and java.security.cert.
4.1. Public Key Certificates
In public key cryptography, a certificate (also known as a digital certificate) is an electronic document that contains a digital signature and a public key. This signature and key together identify entities such as a person or an organization etc. The certificate is typically used to verify that a public key belongs to an individual.
4.2. Certificate Format
The format of pubic key certificate was specified as a part of the well-known standard X.509. The structure of an X.509 digital certificate is summarized as follows:
- Serial Number
- Algorithm ID
- Not Before
- Not After
- Subject Public Key Info
- Public Key Algorithm
- Subject Public Key
- Issuer Unique Identifier (optional)
- Subject Unique Identifier (optional)
- Extensions (optional)
- Certificate Signature Algorithm
- Certificate Signature
Java security API provides classes and interfaces to represent such certificates and working with them. We shall write several programs in Java to do various certificate-related operations.
4.3. Digital Signature
A digital signature is a scheme for ensuring the authenticity of a message or document. The signatory of a digital signature has a pair of keys one of which is called public key (assumed to be known to anybody), and the other is called private key (assumed to be known only to the signatory). Given a piece of target data (usually another key), its summary, called a digest is created using a hash function. The digest data summary is used in conjunction with the DSA algorithm to create the digital signature that is sent with the message. Signature verification involves the use of the same hash function.
The digest is then encrypted using DSA algorithm in conjunction with the signatory’s private key. This encrypted version of the data’s digest is known as signature of the data. The target data and its signature are then sent (as a single entity or separately) to another application, which in turn, verifies the signature of the data using DSA and the signatory’s public key. If the signature is correct, the data is said to reach as an integral part (i.e. without any modification).
4.4. Key and Certificate Store
Java platform allows us to store keys and certificates in persistent store via java.security. KeyStore and java.security.cert.Certstore respectively. In addition to standard PKCS11 and PKCS12 key store types, Java also provides a built-in store in a format known as JKS (Java Key Store) format. This JKS key store can be manipulated using a Java tool called keytooi. A detailed usage of this tool is demonstrated later in this chapter.
5. PKI Tools
Java comes with tools to work with keys and certificates and keystores. The tool keytooi can be used to do the following:
- Create private/public key pairs using various key generation allgorithms
- Create self-signed certificates
- Display, import, and export X.509 certificates stored in files
- Issue certificate (PKCS#10) requests to be sent to CAs
- Create certificates based on certificate requests
- Import certificate replies (obtained from the CAs sent certificate requests)
- Designate public key certificates as trusted
The jarsigner tool is used to sign JAR (Java ARchive) files and/or to verify signatures on signed JAR files. A detailed discussion about these tools can be found later in this chapter.
Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.