Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add decryption of AES-encrypted private keys #26

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pom-android-with-async-io.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.madgag.spongycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.58.0.0</version>
</dependency>
</dependencies>
<distributionManagement>
<snapshotRepository>
Expand Down
5 changes: 5 additions & 0 deletions pom-android.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.madgag.spongycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.58.0.0</version>
</dependency>
</dependencies>
<distributionManagement>
<snapshotRepository>
Expand Down
5 changes: 5 additions & 0 deletions pom-without-protobuf.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.madgag.spongycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.58.0.0</version>
</dependency>
</dependencies>
<distributionManagement>
<snapshotRepository>
Expand Down
13 changes: 13 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.madgag.spongycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.58.0.0</version>
</dependency>
</dependencies>
<distributionManagement>
<snapshotRepository>
Expand All @@ -66,6 +71,14 @@
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
Expand Down
4 changes: 2 additions & 2 deletions src/net/named_data/jndn/security/tpm/TpmBackEndFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ public TpmBackEndFile(String locationPath)
byte[] pkcs = Common.base64Decode(base64.toString());

try {
key.loadPkcs1(ByteBuffer.wrap(pkcs), null);
key.loadPkcs8(ByteBuffer.wrap(pkcs));
} catch (TpmPrivateKey.Error ex) {
throw new TpmBackEnd.Error("Error decoding private key file: " + ex);
}
Expand All @@ -298,7 +298,7 @@ public TpmBackEndFile(String locationPath)
String filePath = toFilePath(keyName).getAbsolutePath();
String base64;
try {
base64 = Common.base64Encode(key.toPkcs1().getImmutableArray(), true);
base64 = Common.base64Encode(key.toPkcs8().getImmutableArray(), true);
} catch (TpmPrivateKey.Error ex) {
throw new TpmBackEnd.Error("Error encoding private key file: " + ex);
}
Expand Down
210 changes: 62 additions & 148 deletions src/net/named_data/jndn/security/tpm/TpmPrivateKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@

package net.named_data.jndn.security.tpm;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.security.Security;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
Expand All @@ -48,12 +52,24 @@
import net.named_data.jndn.security.RsaKeyParams;
import net.named_data.jndn.util.Blob;
import net.named_data.jndn.util.Common;
import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
import org.spongycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.spongycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.spongycastle.operator.InputDecryptorProvider;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.spongycastle.pkcs.PKCSException;


/**
* A TpmPrivateKey holds an in-memory private key and provides cryptographic
* operations such as for signing by the in-memory TPM.
*/
public class TpmPrivateKey {
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a newline after this


/**
* A TpmPrivateKey.Error extends Exception and represents an error in private
* key processing.
Expand Down Expand Up @@ -127,6 +143,48 @@ else if (keyType == KeyType.RSA)
loadPkcs8(pkcs8.buf(), keyType);
}

/**
* Load the encrypted private key from a buffer with the PKCS #8 encoding of
* the EncryptedPrivateKeyInfo.
* This replaces any existing private key in this object.
* @param encoding The byte buffer with the private key encoding.
* @param password The password for decrypting the private key, which should
* have characters in the range of 1 to 127.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does this limitation come from?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's leftover from the code I replaced. I'll remove it.

* @throws TpmPrivateKey.Error for errors decoding or decrypting the key.
*/
public final void
loadEncryptedPkcs8
(ByteBuffer encoding, ByteBuffer password) throws TpmPrivateKey.Error {
//BouncyCastle classes expect a byte array and char array
byte[] encodingBytes = new byte[10];
encodingBytes = new byte[encoding.capacity()];
encoding.get(encodingBytes, 0, encodingBytes.length);
encoding.clear();

CharBuffer charBuffer = Charset.forName("ISO-8859-1").decode(password);
char[] passwordBytes = charBuffer.array();

try {
PKCS8EncryptedPrivateKeyInfo privateKeyInfo = new PKCS8EncryptedPrivateKeyInfo(encodingBytes);
JceOpenSSLPKCS8DecryptorProviderBuilder jce = new JceOpenSSLPKCS8DecryptorProviderBuilder();
jce.setProvider("SC");
InputDecryptorProvider decProv = jce.build(passwordBytes);
PrivateKeyInfo info = privateKeyInfo.decryptPrivateKeyInfo(decProv);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
privateKey_ = converter.getPrivateKey(info);
String keyTypeString = privateKey_.getAlgorithm();
if (keyTypeString.equals("RSA")) keyType_ = KeyType.RSA;
else if (keyTypeString.equals("ECDSA")) keyType_= KeyType.EC;
else throw new TpmPrivateKey.Error
("loadEncryptedPkcs8: Key type " + keyTypeString + " not supported");

} catch (IOException | OperatorCreationException | PKCSException ex) {
throw new TpmPrivateKey.Error
("loadEncryptedPkcs8: Error parsing PrivateKey info: " + ex);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong indentation


}

/**
* Load the unencrypted private key from a buffer with the PKCS #1 encoding.
* This replaces any existing private key in this object. This partially
Expand Down Expand Up @@ -228,149 +286,6 @@ else if (keyType == KeyType.RSA) {
loadPkcs8(encoding, null);
}

/**
* Load the encrypted private key from a buffer with the PKCS #8 encoding of
* the EncryptedPrivateKeyInfo.
* This replaces any existing private key in this object. This partially
* decodes the private key to determine the key type.
* @param encoding The byte buffer with the private key encoding.
* @param password The password for decrypting the private key, which should
* have characters in the range of 1 to 127.
* @throws TpmPrivateKey.Error for errors decoding or decrypting the key.
*/
public final void
loadEncryptedPkcs8(ByteBuffer encoding, ByteBuffer password)
throws TpmPrivateKey.Error
{
// Decode the PKCS #8 EncryptedPrivateKeyInfo.
// See https://tools.ietf.org/html/rfc5208.
String oidString;
Object parameters;
Blob encryptedKey;
try {
DerNode parsedNode = DerNode.parse(encoding, 0);
List encryptedPkcs8Children = parsedNode.getChildren();
List algorithmIdChildren = DerNode.getSequence
(encryptedPkcs8Children, 0).getChildren();
oidString = "" + ((DerNode.DerOid)algorithmIdChildren.get(0)).toVal();
parameters = algorithmIdChildren.get(1);

encryptedKey =
(Blob)((DerNode.DerOctetString)encryptedPkcs8Children.get(1)).toVal();
}
catch (Throwable ex) {
throw new TpmPrivateKey.Error
("Cannot decode the PKCS #8 EncryptedPrivateKeyInfo: " + ex);
}

// Use the password to get the unencrypted pkcs8Encoding.
byte[] pkcs8Encoding;
if (oidString.equals(PBES2_OID)) {
// Decode the PBES2 parameters. See https://www.ietf.org/rfc/rfc2898.txt .
String keyDerivationOidString;
Object keyDerivationParameters;
String encryptionSchemeOidString;
Object encryptionSchemeParameters;
try {
List parametersChildren = ((DerNode.DerSequence)parameters).getChildren();

List keyDerivationAlgorithmIdChildren = DerNode.getSequence
(parametersChildren, 0).getChildren();
keyDerivationOidString = "" +
((DerNode.DerOid)keyDerivationAlgorithmIdChildren.get(0)).toVal();
keyDerivationParameters = keyDerivationAlgorithmIdChildren.get(1);

List encryptionSchemeAlgorithmIdChildren = DerNode.getSequence
(parametersChildren, 1).getChildren();
encryptionSchemeOidString = "" +
((DerNode.DerOid)encryptionSchemeAlgorithmIdChildren.get(0)).toVal();
encryptionSchemeParameters = encryptionSchemeAlgorithmIdChildren.get(1);
}
catch (Throwable ex) {
throw new TpmPrivateKey.Error
("Cannot decode the PBES2 parameters: " + ex);
}

// Get the derived key from the password.
byte[] derivedKey = null;
if (keyDerivationOidString.equals(PBKDF2_OID)) {
// Decode the PBKDF2 parameters.
Blob salt;
int nIterations;
try {
List pbkdf2ParametersChildren =
((DerNode.DerSequence)keyDerivationParameters).getChildren();
salt = (Blob)
((DerNode.DerOctetString)pbkdf2ParametersChildren.get(0)).toVal();
nIterations = (int)
((DerNode.DerInteger)pbkdf2ParametersChildren.get(1)).toVal();
}
catch (Throwable ex) {
throw new TpmPrivateKey.Error
("Cannot decode the PBES2 parameters: " + ex);
}

// Check the encryption scheme here to get the needed result length.
int resultLength;
if (encryptionSchemeOidString.equals(DES_EDE3_CBC_OID))
resultLength = DES_EDE3_KEY_LENGTH;
else
throw new TpmPrivateKey.Error
("Unrecognized PBES2 encryption scheme OID: " +
encryptionSchemeOidString);

try {
derivedKey = Common.computePbkdf2WithHmacSha1
(new Blob(password, false).getImmutableArray(),
salt.getImmutableArray(), nIterations, resultLength);
}
catch (Throwable ex) {
throw new TpmPrivateKey.Error
("Error computing the derived key using PBKDF2 with HMAC SHA1: " + ex);
}
}
else
throw new TpmPrivateKey.Error
("Unrecognized PBES2 key derivation OID: " + keyDerivationOidString);

// Use the derived key to get the unencrypted pkcs8Encoding.
if (encryptionSchemeOidString.equals(DES_EDE3_CBC_OID)) {
// Decode the DES-EDE3-CBC parameters.
Blob initialVector;
try {
initialVector = (Blob)
((DerNode.DerOctetString)encryptionSchemeParameters).toVal();
}
catch (Throwable ex) {
throw new TpmPrivateKey.Error
("Cannot decode the DES-EDE3-CBC parameters: " + ex);
}

try {
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init
(Cipher.DECRYPT_MODE,
new SecretKeySpec(derivedKey, "DESede"),
new IvParameterSpec(initialVector.getImmutableArray()));
pkcs8Encoding = cipher.doFinal(encryptedKey.getImmutableArray());
}
catch (Throwable ex) {
throw new TpmPrivateKey.Error
("Error decrypting PKCS #8 key with DES-EDE3-CBC: " + ex);
}
}
else
throw new TpmPrivateKey.Error
("Unrecognized PBES2 encryption scheme OID: " +
encryptionSchemeOidString);
}
else
throw new TpmPrivateKey.Error
("Unrecognized PKCS #8 EncryptedPrivateKeyInfo OID: " + oidString);

loadPkcs8(ByteBuffer.wrap(pkcs8Encoding));
}

/**
* Get the encoded public key for this private key.
* @return The public key encoding Blob.
Expand Down Expand Up @@ -554,8 +469,7 @@ else if (keyType_ == KeyType.RSA) {

/**
* Get the encoded encrypted private key in PKCS #8.
* @param password The password for encrypting the private key, which should
* have characters in the range of 1 to 127.
* @param password The password for encrypting the private key.
* @return The encoding Blob of the EncryptedPrivateKeyInfo.
* @throws TpmPrivateKey.Error if no private key is loaded, or error encoding.
*/
Expand Down Expand Up @@ -692,7 +606,7 @@ else if (keyParams.getKeyType() == KeyType.EC) {
*/
private static Blob
encodePkcs8PrivateKey(ByteBuffer privateKeyDer, OID oid, DerNode parameters)
throws TpmPrivateKey.Error
throws TpmPrivateKey.Error
{
try {
DerSequence algorithmIdentifier = new DerSequence();
Expand Down
Loading