太阳能公司网站建设多少钱,购物网站模板带后台,ftp 网站管理,海口网站建设在线Java Cryptography API 使你能够在 Java 中加密和解密数据#xff0c;以及管理密钥、签名、验证消息、计算加密哈希等等。Cryptography 属于经常被简称为 crypto#xff0c;所以你有时候可能会看到 Java crypto 而不是 Java Cryptography#xff0c;这两个术语是同一个意思。…Java Cryptography API 使你能够在 Java 中加密和解密数据以及管理密钥、签名、验证消息、计算加密哈希等等。Cryptography 属于经常被简称为 crypto所以你有时候可能会看到 Java crypto 而不是 Java Cryptography这两个术语是同一个意思。
Java Cryptography API 由被称为 Java Cryptography Extension 提供Java Cryptography Extension 经常被引用通过它的缩写 JCE。Java Cryptography Extension 长期以来一直是 Java 平台的一部分。JCE 最初与 Java 是分开的因为美国对加密技术有一些出口限制。因此最强的加密算法并未包含在标准 Java 平台中。如果你是美国境内的公司你可以从 Java JCE 获得这些更强大的加密算法但世界其他地区不得不使用较弱的算法。到 2017 年的时候美国加密出口规则已经放宽了很多因此世界上大部分地区都可以通过 JCT 从国际加密标准中受益。
Java Cryptography ArchitectureJCA是 Java 加密 API 内部设计的名称。JCA 是围绕一些核心通用类和接口构建的。这些接口背后的真正功能是由 Providers 提供的。因此你可以使用一个 Cipher 类来加密和解密某些数据但具体的密码实现取决于所使用的具体 Provider。
Provider
java.security.Provider 类是 Java cryptography API 中的核心类。为了使用 Java crypto API你需要一组 Provider。Java SDK 带有自己的 Provider。如果你没有显示设置 Provider则会使用 Java SDK 默认的 Provider。但是此 Provider 可能不支持你要使用的加密算法。因此你可能不得不设置自己的 Provider。
在 Java cryptography API 中最流行的 Provider 之一是 Bouncy Castle如下是一个设置 BouncyCastleProvider 的示例
import org.bouncycastle.jce.provider.BouncyCastleProvider;import java.security.Security;public class ProviderExample {public static void main(String[] args) {Security.addProvider(new BouncyCastleProvider());}
}Cipher
javax.crypto.Cipher 类表示一种加密算法Cipher 是在密码学领域中一个加密算法的标准术语这就是为什么 Java 类被叫做 Cipher 而不是 Encrypter/Decrypter 或者其他的原因。
你可以使用一个 Cipher 实例在 Java 中加密和解密数据。
创建 Cipher
在你使用 Cipher 之前需要先创建一个 Cipher 类的实例。你可以通过调用 Cipher 的 getInstance() 方法来创建一个 Cipher 实例该方法需要传入一个用来表明你想要使用的加密算法的参数实例如下
Cipher cipher Cipher.getInstance(AES);此示例创建了一个使用 AES 加密算法的 Cipher 实例。
Cipher 模式
一些加密算法可以在不同的模式下工作加密模式指定有关算法应如何加密数据的详细信息因此加密模式影响了部分加密算法。
加密模式有时可以与多种不同的加密算法一起使用 ---- 就像附加到核心加密算法的一门技术。这就是为什么这些模式被认为与加密算法本身是分开的而不是具体加密算法的 “附加组件”。以下是一些著名的 cipher 模式
ECB - Electronic CodebookCBC - Cipher Block ChainingCFB - Cipher FeedbackOFB - Output FeedbackCTR - Counter
初始化 Cipher 时你可以将其模式附加到加密算法的名称后面。例如要使用 Cipher Block ChainingCBC创建 AES Cipher 实例你可以使用以下代码
Cipher cipher Cipher.getInstance(AES/CBC/PKCS5Padding);由于密码块链接也需要 “填充方案”因此 “填充方案” 附加在加密算法名称字符串的末尾。
值得注意的是默认的 Java SDK Provider 并不包含所有的加密算法和模式你可能需要一个外部的 Provider 比如 Bouncy Castle 来使用你想要的模式和填充方案来实例化 Cipher。
初始化 Cipher
在使用 Cipher 实例之前你必须对其进行初始化。初始化一个 Cipher 是指调用它的 init() 方法init() 方法需要两个参数
加密/解密密码操作模式加密/解密密钥
下面是一个以加密方式初始化 Cipher 实例的例子
Key key ... // get / create symmetric encryption key
cipher.init(Cipher.ENCRYPT_MODE, key);下面是一个以解密方式初始化 Cipher 实例的例子
Key key ... // get / create symmetric encryption key
cipher.init(Cipher.DECRYPT_MODE, key);加密和解密数据
为了使用 Cipher 实例加密或解密数据你可以调用以下两个方法中的一个
update()doFinal()
这两个方法都有多个接受不同参数的重载版本我将在这里介绍最常用的版本。
如果你想要加密/解密单个数据块只需调用 doFinal() 并传入需要加密/解密的数据。如下是一个加密示例
byte[] plainText abcdefghijklmnopqrstuvwxyz.getBytes(UTF-8);
byte[] cipherText cipher.doFinal(plainText);只需要在初始化 Cipher 的时候指定是加密模式还是解密模式就可以使用同一个方法 doFinal 来加密/解密数据。
如果你想要加密/解密多个数据块例如来自一个大文件的多个块你可以为每个数据块调用一次 update()并在最后一个数据块调用 doFinal()下面是一个加密多个数据块的例子
byte[] data1 abcdefghijklmnopqrstuvwxyz.getBytes(UTF-8);
byte[] data2 zyxwvutsrqponmlkjihgfedcba.getBytes(UTF-8);
byte[] data3 01234567890123456789012345.getBytes(UTF-8); byte[] cipherText1 cipher.update(data1);
byte[] cipherText2 cipher.update(data2);
byte[] cipherText3 cipher.doFinal(data3);最后一个数据块需要调用 doFinal() 的原因是一些加密算法需要填充数据以适应确定的密码块大小比如 8 byte 边界。但是我们不想填充中间的数据块因此中间数据块调用 update()最后一个数据块调用 doFinal()。
当解密多个数据块时也是一样的以下是使用 Cipher 实例解密多个数据块的示例
byte[] plainText1 cipher.update(cipherText1);
byte[] plainText2 cipher.update(cipherText2);
byte[] plainText3 cipher.doFinal(cipherText3);在这之前记得将 Cipher 实例初始化为解密模式。
加密/解密部分字节数组
Cipher 类的加解密方法能够加密/解密一个字节数组中的部分数据你只需要简单地将 offset 和 length 传递给 update() 或者 doFinal() 方法即可如下所示
int offset 10;
int length 24;
byte[] cipherText cipher.doFinal(data, offset, length);此示例加密从索引 8 开始的 24 个字节。
加密/解密到现有字节数组中
到目前为止本教程中展示的所有加密和解密示例都是通过返回一个新的字节数组然而你也可以将加密/解密后的数据填充到现有的字节数组中这对于减少创建的字节数组的数量很有用。
通过将目标字节数组作为参数传递给 update() 或 doFinal() 方法来可以实现这一点示例如下
int offset 10;
int length 24;
byte[] dest new byte[1024];
cipher.doFinal(data, offset, length, dest);此示例加密源字节数组中从索引 10 开始的 24 个字节并将加密后的数据从索引 0 开始填充到 dest 字节数组中如果你想要为 dest 字节数组设置不同的起始索引Cipher 类中有额外接收目标字节数组偏移量的版本的 update() 和 doFinal() 方法下面示例中给 doFinal() 方法传递了目标字节数组的偏移量
int offset 10;
int length 24;
byte[] dest new byte[1024];
int destOffset 12
cipher.doFinal(data, offset, length, dest, destOffset);重用 Cipher 实例
初始化 Cipher 实例是一项开销很大的操作。因此重用 Cipher 实例是个好主意。幸运的是Cipher 类在设计时考虑到了重用。
当你在 Cipher 实例上调用 doFinal() 方法时Cipher 实例将返回到初始化后的状态。Cipher 实例能够用来加密解密数据多次。
下面是一个重用 Cipher 实例的例子
Cipher cipher Cipher.getInstance(AES);Key key ... // get / create symmetric encryption key
cipher.init(Cipher.ENCRYPT_MODE, key);byte[] data1 abcdefghijklmnopqrstuvwxyz.getBytes(UTF-8);
byte[] data2 zyxwvutsrqponmlkjihgfedcba.getBytes(UTF-8);byte[] cipherText1 cipher.update(data1);
byte[] cipherText2 cipher.doFinal(data2);byte[] data3 01234567890123456789012345.getBytes(UTF-8);
byte[] cipherText3 cipher.doFinal(data3);MessageDigest
Java MessageDigest 类表示一个加密散列算法它被用来从二进制数据中计算消息摘要。当你收到一些加密数据时你无法从数据本身看出它是否在传输过程中被修改消息摘要可以帮助解决该问题。
为了能够检测加密数据是否在传输过程中被修改发送方可以根据数据计算消息摘要并将其与数据一起发送。当你收到加密后的数据和消息摘要时你可以根据数据重新计算消息摘要并检查计算出的消息摘要是否与随数据接收的消息摘要相匹配。如果两个消息摘要匹配则有可能加密数据在传输过程中未被修改。
创建 MessageDigest 实例
要创建 MessageDigest 实例你可以调用 MessageDigest 类的静态方法 getInstance()。以下是创建 MessageDigest 实例的示例
MessageDigest messageDigest MessageDigest.getInstance(SHA-256);传递给 getInstance() 方法的文本参数是要使用的具体消息摘要算法的名称。
消息摘要算法
Java Cryptography API 支持以下消息摘要算法
MD2MD5SHA-1SHA-256SHA-384SHA-512
并非所有的这些消息摘要算法都同样安全建议使用 SHA-256 或更高版本以获得尽可能高的安全性。
计算 MessageDigest
一旦你创建了 MessageDigest 实例之后你就可以使用它来计算数据的消息摘要了如果你有单个数据块来计算消息摘要请使用 digest() 方法以下是从单个数据块计算消息摘要的方式
byte[] data1 0123456789.getBytes(UTF-8); MessageDigest messageDigest MessageDigest.getInstance(SHA-256);
byte[] digest messageDigest.digest(data1);如果你有多个数据块要包含在同一个消息摘要中请先调用 update() 方法并在最后调用 digest() 方法以下是从多个数据块计算消息摘要的方式
byte[] data1 0123456789.getBytes(UTF-8);
byte[] data2 abcdefghijklmnopqrstuvxyz.getBytes(UTF-8);MessageDigest messageDigest MessageDigest.getInstance(SHA-256);
messageDigest.update(data1);
messageDigest.update(data2);byte[] digest messageDigest.digest();Mac
javax.crypto.Mac 类可以从二进制数据中创建 Message Authentication Code一个 Mac 是一个使用安全密钥加密之后的消息摘要只有当你有用密钥时才能验证 MAC。
创建 Mac 实例
在你使用 Mac 类之前你必须先创建 Mac 实例创建 Mac 实例可以使用 getInstance() 静态方法示例如下
Mac mac Mac.getInstance(HmacSHA256);传递给 getInstance() 方法的字符串参数包含了要使用的 MAC 算法的名字在这个例子中是 HmacSHA256。
初始化 Mac
在创建 Mac 实例之后你需要初始化初始化 Mac 实例是通过调用 init() 方法并传入安全密钥作为参数如下所示
byte[] keyBytes new byte[]{0,1,2,3,4,5,6,7,8 ,9,10,11,12,13,14,15};
String algorithm RawBytes;
SecretKeySpec key new SecretKeySpec(keyBytes, algorithm);mac.init(key);init() 方法接受一个 Key 实例在这个例子中是 SecreKeySpec它是 Key 接口的实现类。
计算 Mac
初始化之后你就可以计算 Mac 的值了计算 Mac 的值你需要调用 update() 或者 doFinal() 方法如果你只有一个数据块需要计算你可以直接使用 doFinal() 方法如下所示
byte[] data abcdefghijklmnopqrstuvxyz.getBytes(UTF-8);
byte[] macBytes mac.doFinal(data);如果你有多个数据块要计算那么你必须给每个数据块调用 update() 方法并在最后调用 doFinal() 方法示例如下
byte[] data abcdefghijklmnopqrstuvxyz.getBytes(UTF-8);
byte[] data2 0123456789.getBytes(UTF-8);mac.update(data);
mac.update(data2);byte[] macBytes mac.doFinal();Signature
java.security.Signature 类可以为二进制数据创建数字签名数字签名是使用私钥/公钥对中的私钥加密后的消息摘要。任何拥有公钥的人都可以验证该数字签名。
创建 Signature 实例
在使用 Signature 类之前你必须创建一个 Signature 实例你可以通过调用静态方法 getInstance() 方法创建一个 Signature 实例下面是一个创建 Signature 实例的示例
Signature signature Signature.getInstance(SHA256WithDSA);作为参数传递给 getInstance() 方法的字符串是要使用的数字签名算法的名称。
初始化 Signature 实例
创建 Signature 实例后你需要先对其进行初始化然后才能使用它。你通过调用 Signature 实例的 init() 方法来初始化实例这是一个 Signature 初始化示例
SecureRandom secureRandom new SecureRandom();
KeyPairGenerator keyPairGenerator KeyPairGenerator.getInstance(DSA);
KeyPair keyPair keyPairGenerator.generateKeyPair();signature.initSign(keyPair.getPrivate(), secureRandom);如你所见Signature 实例是使用私钥/公钥对的私钥和一个 SecureRandom 实例初始化的。
创建数字签名
Signature 实例被初始化后你就可以使用它来创建数字签名。你可以通过调用一次或多次 update() 方法来创建数字签名并在最后调用 sign() 方法以下是为二进制数据块创建数字签名的示例
byte[] data abcdefghijklmnopqrstuvxyz.getBytes(UTF-8);
signature.update(data);byte[] digitalSignature signature.sign();验证数字签名
如果要验证其他人创建的数字签名则必须将 Signature 实例初始化为验证模式而不是签名模式。以下是将 Signature 实例初始化为验证模式的方式
Signature signature Signature.getInstance(SHA256WithDSA);signature.initVerify(keyPair.getPublic());在上面的示例中Signature 实例被初始化为验证模式并将公钥/私钥对的公钥作为参数传递进去了。
一旦初始化为验证模式你就可以使用 Signature 实例来验证一个数字签名了如下示例展示了如何验证一个数字签名
byte[] data2 abcdefghijklmnopqrstuvxyz.getBytes(UTF-8);
signature2.update(data2);boolean verified signature2.verify(digitalSignature);完整的签名和验证示例如下
SecureRandom secureRandom new SecureRandom();
KeyPairGenerator keyPairGenerator KeyPairGenerator.getInstance(DSA);
KeyPair keyPair keyPairGenerator.generateKeyPair();Signature signature Signature.getInstance(SHA256WithDSA);signature.initSign(keyPair.getPrivate(), secureRandom);byte[] data abcdefghijklmnopqrstuvxyz.getBytes(UTF-8);
signature.update(data);byte[] digitalSignature signature.sign();Signature signature2 Signature.getInstance(SHA256WithDSA);
signature2.initVerify(keyPair.getPublic());signature2.update(data);boolean verified signature2.verify(digitalSignature);System.out.println(verified verified);KeyGenerator
javax.crypto.KeyGenerator 被用于生成对称加密密钥对称加密密钥是一种密钥对称加密算法可以使用它来加密和解密数据。
创建 KeyGenerator 实例
在使用 KeyGenerator 类之前你必须先创建一个 KeyGenerator 实例你可以通过调用静态方法 getInstance() 并传递创建密钥的加密算法的名称来做到这一点以下是创建 KeyGenerator 实例的示例
KeyGenerator keyGenerator KeyGenerator.getInstance(AES);此示例创建一个 KeyGenerator 实例该实例可以通过 AES 加密算法生成密钥。
初始化 KeyGenerator
创建 KeyGenerator 实例后你必须对其进行初始化初始化一个 KeyGenerator 实例是通过调用它的 init() 方法来完成的这是初始化 KeyGenerator 实例的示例
SecureRandom secureRandom new SecureRandom();
int keyBitSize 256;keyGenerator.init(keyBitSize, secureRandom);KeyGenerator 的 init() 方法接收两个参数要生成的密钥的 bit 位是多少以及一个 SecureRandom 实例。
生成密钥
初始化 KeyGenerator 实例后你可以使用它来生成密钥。生成密钥是通过调用 KeyGenerator.generateKey() 方法完成的。以下是生成对称密钥的示例
SecretKey secretKey keyGenerator.generateKey();KeyPair
java.security.KeyPair 代表一个非对称密钥对换句话说就是一个公钥私钥对KeyPair 实例通常在执行非对称加密时使用例如对数据进行加密或者签名的时候。
获取 KeyPair 实例
你通常会从 Java KeyStore 或 Java KeyPairGenerator 获取一个 KeyPair 实例。
访问 KeyPair 中的 Public Key
你可以通过调用 KeyPair.getPublic() 方法来访问一个 PulicKey实例如下
PublicKey publicKey keyPair.getPublic();访问 KeyPair 中的 Private Key
你可以通过调用 KeyPair 的 getPrivate() 方法来访问 PrivateKey示例如下
PrivateKey privateKey keyPair.getPrivate();KeyPairGenerator
java.security.KeyPairGenerator 类用于生成非对称加密/解密密钥对。非对称密钥对由两个密钥组成。第一个密钥通常用于加密数据第二个密钥用于解密第一个密钥加密的数据。
最广为人知的非对称密钥对类型是公钥、私钥类型的密钥对。私钥用于加密数据公钥用于解密数据。实际上你也可以使用公钥加密数据并使用私钥解密。
私钥通常是保密的公钥通常是公开的。
创建 KeyPairGenerator 实例
要使用 KeyPairGenerator你必须首先创建一个 KeyPairGenerator 实例创建 KeyPairGenerator 实例是通过调用 getInstance() 方法完成的以下是创建 KeyPairGenerator 实例的示例
KeyPairGenerator keyPairGenerator KeyPairGenerator.getInstance(RSA);getInstance() 方法通过接收加密算法的名称作为参数来生成 KeyPairGenerator在此例中我们使用 RSA 加密算法。
初始化 KeyPairGenerator
根据生成密钥对的算法你可能必须初始化 KeyPairGenerator 实例初始化 KeyPairGenerator 是通过调用它的 initialize() 方法来完成的下面是初始化一个 KeyPairGenerator 实例的例子
keyPairGenerator.initialize(2048);此示例初始化 KeyPairGenerator 以生成大小为 2048 位的密钥。
生成 KeyPair
要使用一个 KeyPairGenerator 生成一个 KeyPair你可以调用 generatorKeyPair() 方法下面是一个生成 KeyPair 的示例
KeyPair keyPair keyPairGenerator.generateKeyPair();KeyStore
Java KeyStore 是一个可以存储密钥的数据库一个 Java KeyStore 在 Java 中的抽象是 java.security.KeyStore 类。一个 KeyStore 可以被写入磁盘并再次读取KeyStore 作为一个整体可以用密钥保护并且 KeyStore 中的每个 key entry 可以有自己的密码来保护自己。这使得 KeyStore 成为安全处理加密密钥的有用机制。
一个 KeyStore 可以持有以下类型的密钥
Private keysPublic keys certificatesSecret keys
私钥和公钥用于非对称加密公钥可以有关联的证书证书是一个用于验证持有公钥的个人、组织和设备身份的文档。证书通常由验证方进行数字签名作为证明。
安全密钥用于非对称加密在大部分场景中在一个安全连接被建立的时候会商定非对称密钥因此你可能使用 KeyStore 存储公钥和私钥的时间存储安全密钥的时间多。
创建 KeyStore
你可以通过调用 KeyStore.getInstance() 方法来创建 KeyStore以下是创建 KeyStore 的示例
KeyStore keyStore KeyStore.getInstance(KeyStore.getDefaultType());此示例创建默认类型的 KeyStore 实例也可以通过将不同的参数传递给 getInstance() 方法来创建其他类型的实例例如这是一个创建 PKCS12 类型的示例
KeyStore keyStore KeyStore.getInstance(PKCS12);加载 KeyStore
在 KeyStore 实例可以使用之前必须先加载它。KeyStore 实例通常被写入磁盘或其他类型的存储以备后用。这就是为什么 KeyStore 假定你必须先读入它的数据才能使用它的原因。但是可以初始化一个没有数据的空 KeyStore 实例。
从文件或其他存储中加载 KeyStore 数据是通过调用 KeyStore.load() 方法来完成的该方法有两个参数
加载数据的 InputStream包含 KeyStore 密码的 char[]
这是加载 KeyStore 的示例
char[] keyStorePassword 123abc.toCharArray();
try(InputStream keyStoreData new FileInputStream(keystore.ks)){ keyStore.load(keyStoreData, keyStorePassword);
}此示例加载位于 keystore.ks 文件中的 KeyStore。
如果你不想将任何数据加载到 KeyStore 中只需给 InputStream 参数传递 null 即可下例示例加载一个空的 KeyStore
keyStore3.load(null, keyStorePassword);你必须始终加载 KeyStore 实例无论是有数据的 KeyStore 还是空的 KeyStore否则 KeyStore 是未初始化的所有对其方法的调用都将抛出异常。
获取 Keys
你可以通过调用 getEntry() 方法来获取 KeyStore 中的密钥一个 KeyStore Entry 映射一个密钥的别名每个密钥被自己的密钥保护。因此要访问密钥你必须将密钥的别名和密钥的密码传递给 getEntry() 方法下面是访问 KeyStore 实例中的 key entry 的示例
char[] keyPassword 789xyz.toCharArray();
KeyStore.ProtectionParameter entryPassword new KeyStore.PasswordProtection(keyPassword); KeyStore.Entry keyEntry keyStore3.getEntry(keyAlias, entryPassword);如果你知道要访问的密钥是一个私钥则可以将 KeyStore.Entry 实例转换为 KeyStore.PrivateKeyEntry示例如下
KeyStore.PrivateKeyEntry privateKeyEntry (KeyStore.PrivateKeyEntry) keyStore3.getEntry(keyAlias, entryPassword);转换成 KeyStore.PrivateKeyEntry 之后通过以下方法你能够访问私钥证书和证书链。
getPrivateKey()getCertificate()getCertificateChain()
设置 Keys
你也可以给 KeyStore 实例设置密钥示例如下
SecretKey secretKey getSecretKey();
KeyStore.SecretKeyEntry secretKeyEntry new KeyStore.SecretKeyEntry(secretKey);keyStore3.setEntry(keyAlias2, secretKeyEntry, entryPassword);存储 KeyStore
有时你可能想要存储一个 KeyStore 到存储器中磁盘、数据库等用于在某个时间再次加载它。你可以通过 store() 方法存储一个 KeyStore示例如下
char[] keyStorePassword 123abc.toCharArray();
try (FileOutputStream keyStoreOutputStream new FileOutputStream(data/keystore.ks)) {keyStore3.store(keyStoreOutputStream, keyStorePassword);
}参考 https://jenkov.com/tutorials/java-cryptography/index.html