目录
一 、什么是RSA
二、der编码与pem编码
三、openssl命令生成rsa密钥
四、RSA加密标准PKCS
五、密钥与证书
六、jks证书
RSA是一种公钥密码算法,它的名字由三位开发者,即Ron Rivest、Adi Shamir和Leonard Adleman的姓氏的首字母组成的。RSA被用于公钥密码和数字签名。
我们使用程序代码或者openssl命令生成秘钥对时,会先在内存中生成数据,然后序列化到文件中。而数据序列化需要一种特定的编码格式——DER。DER 是distinguished encode rule 的简写,它是ASN.1编码标准的一种编码方式,该编码方式的输出是二进制而不是普通的文本。DER 编码二进制输出的文件将会是一个二进制文件。PEM是一个用来存储和发送密码学key、证书和其他数据的文件格式的事实标准。许多使用ASN.1的密码学标准(比如X.509和PKCS)都使用DER编码,而DER编码的内容是二进制的,不适合与邮件传输(早期Email不能发送附件),因此使用PEM把二进制内容转换成ASCII码。文件内容的格式像下面这样:
-----BEGIN label-----
BASE64Encoded
-----END label-----
label用来区分内容到底是什么类型,例如一个RSA Private Key使用pem格式编码的文件内容如下:
-----BEGIN RSA PRIVATE KEY-----
BASE64Encoded
-----END RSA PRIVATE KEY-----
PEM实际上就是把DER编码的文件的二进制内容用base64编码一下,然后加上-----BEGIN label-----
这样的头和-----END label-----
这样的尾,中间则是DER文件的Base64编码。
1. 使用openssl生成rsa私钥
openssl genrsa -out private.pem 2028
openssl生成的私钥文件默认时pem编码格式的,可以使用下面的命令转换为der编码格式:
openssl rsa -in private.pem -outform der -out private.der
2. 由rsa私钥得到rsa公钥
openssl rsa -in private.pem -pubout -out public.pem
1. PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公开密钥密码的发展而制订的一系列标准。PKCS 目前共发布过 15 个标准,下面介绍3个我们经常会遇到的标准格式:
1) PKCS#1:RSA加密标准。PKCS#1定义了RSA公钥函数的基本格式标准,特别是数字签名。它定义了数字签名如何计算,包括待签名数据和签名本身的格式;它也定义了PSA公/私钥的语法。上面使用openssl命令生成的密钥便是pkcs#1格式的。
2) PKCS#8:私钥信息语法标准。PKCS#8定义了私钥信息语法和加密私钥语法,其中私钥加密使用了PKCS#5标准。java中便是使用的该标准。
3) PKCS#12:个人信息交换语法标准。PKCS#12定义了个人身份信息(包括私钥、证书、各种秘密和扩展字段)的格式。PKCS#12有助于传输证书及对应的私钥,于是用户可以在不同设备间移动他们的个人身份信息。浏览器中便是使用的该标准。
2. java代码读取PKCS#8格式的rsa密钥文件
public static PrivateKey getPemPrivateKey(String filename) throws Exception {
byte[] keyBytes = readFile(filename);
String temp = new String(keyBytes);
// 去掉lable标签
String privKeyPEM = temp.replace("-----BEGIN PRIVATE KEY-----", "");
privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", "");
// 转换为DER格式的二进制编码
BASE64Decoder b64 = new BASE64Decoder();
byte[] decoded = b64.decodeBuffer(privKeyPEM);
// 指定key格式为pcks#8
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
上面的代码指定了加载密钥key的格式为pkcs#8,那java是否可以其他pkcs格式的密钥呢?答案是不可以,至少在不使用第三方扩展插件的情况下是不可以。我们在读取rsa密钥文件时,经常遇到的一个错误是 java.security.InvalidKeyException: IOException : Short read of DER length 。其问题原因便是key的pkcs格式不对,需要对key进行格式转换操作。
3. openssl转换密钥pkcs格式
openssl pkcs8 -topk8 -inform PEM -in private.pem -outform pem -nocrypt -out pkcs8.pem
openssl rsa -in pkcs8.pem -pubout -out public_pkcs8.pem
1. 什么是证书
公开密钥证书(public key certificate)是一种用于验证发送者的姓名和授权的数字签名文件。这种签名文件由一种特殊格式的数据段组成,其中包含有证书持有者的姓名(可能是使用者或者是系统的名字),持有者的公开密钥,以及认证机构用于鉴定的数字签名等信息。在基于RSA算法加密通信的AB双方,A需要先把自己的公钥发给B,B给A发消息时,先用A的公钥将消息加密,再传给A。但这其中存在一个问题,就是B无法验证A的身份,因为任何第三方C都可以冒充A将自己的公钥发送给B。而证书便是对公钥key的身份做说明。证书中除了包含公钥内容,还包含了对自己身份申明的信息。
2. SHA
如果证书中只是包含公钥以及公钥的申明信息,那还是存在安全问题,因为任何第三方攻击者都可以拦截该证书信息并随意篡改文件内容。所以需要一种手段来保证文件内容没有被破坏。首先,不能再对证书内容加密,因为公钥内容是包含在证书里面的。我们在网站下载大文件时,一些网站会给出对该文件进行md5运算后得到的一串hash值,我们在下载文件完成后,同样使用md5算法对文件生成一个hash值,通过比较hash值是否相等来确定文件完整性。而证书校验也采用类似的手段,使用hash函数生成证书的信息摘要,该摘要是一串hash字符值。然后使用rsa私钥对摘要加密,将加密后的值再存入证书文件中,作为证书的一部分一起发送给接收方,接收方取出证书中的公钥,然后使用公钥解密信息摘要值,并对证书内容使用相同的hash函数得到新的信息摘要,然后比较两个值是否相等。这个过程便是证书的数字签名。安全Hash函数(SHA)是使用最广泛的Hash函数。由于其他曾被广泛使用的Hash函数都被发现存在安全隐患,从2005年至今,SHA或许是仅存的Hash算法标准。
SHA由美国标准与技术研究所(NIST)设计并于1993年发表,该版本称为SHA-0,由于很快被发现存在安全隐患,1995年发布了SHA-1。2002年,NIST分别发布了SHA-256、SHA-384、SHA-512,这些算法统称SHA-2。2008年又新增了SHA-224。由于SHA-1已经不太安全,目前SHA-2各版本已成为主流。
我们经常见到的名词RSA256,其实是RSA-SHA256的简称,指的是采用RSA非对称加密,采用SHA256生成证书的数字签名。而RSA1024,RSA2048是指生成密钥key的长度。
3. openssl生成自签名证书
使用 已有RSA 私钥生成自签名证书:
openssl req -new -x509 -days 365 -key private.pem -out mycert.crt
4. CA证书
上面我们提到了使用证书来申明key的来源,使用数字签名来保证证书内容没有被篡改。但是没有办法证明证书的合法性,作为第三方攻击者可以使用同样的手段生成自己的证书冒充发送方并发送给接收方。所以需要一个认证中心来确保证书的来源,便是CA。CA是证书的签发机构,它是公钥基础设施(Public Key Infrastructure,PKI)的核心。CA是负责签发证书、认证证书、管理已颁发证书的机关。
CA证书与自签名证书不同之处在于,CA机构使用自己的私钥为我们提交的证书生成数字签名,在用户端(如浏览器端)会获取到这些CA机构的公钥,用这个公钥来验证其证书的合法性以及获取到证书里面的公钥。
1. jks与keytool
Java密钥库(JKS)是安全证书(授权证书或公钥证书)以及相应的私钥(例如用于SSL加密)的存储库。简单来说它也是一种证书,是java研发的专利格式。Keytool 是一个Java数据证书的管理工具 ,Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中在keystore里。
重点来了:开发java web程序,需要安全认证时,使用java keytool工具生成jks文件以及导出公钥证书,不需要使用openssl工具生成公私钥这些步骤,因为jks文件里面已经包含了这些数据。
2. 生成jks文件以及导出公钥证书
keytool -genkey -alias "myjks" -keyalg "RSA" -keystore "myjks.keystore" -keypass "123456" -storepass "123456" -validity 180
导出公钥证书:
keytool -keystore myjks.keystore -export -alias myjks -file myjks.crt -rfc
3. java程序读取jks文件:
public static RSAPrivateKey getPrivateKeyFromFile(String keystorePath, String alias) throws Exception {
FileInputStream fileInputStream = new FileInputStream(keystorePath);
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(fileInputStream, KEYSTORE_PWD.toCharArray());
RSAPrivateKey privateKey = (RSAPrivateKey) ks.getKey(alias, KEY_PWD.toCharArray());
return privateKey;
}
4. java程序读取jks公钥证书
public static RSAPublicKey getPublicKeyFromFile(String filePath) throws IOException, CertificateException {
String pemString = FileUtils.readFileToString(new File(filePath));
CertificateFactory fact = CertificateFactory.getInstance("X.509");
ByteArrayInputStream is = new ByteArrayInputStream(
pemString.getBytes("UTF8"));
X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
return (RSAPublicKey) cer.getPublicKey();
}