Add jceks sub-package

This commit is contained in:
Cedric Staub
2016-06-02 15:41:55 -07:00
parent 85bea13299
commit 8b92817d07
14 changed files with 949 additions and 1 deletions
+3
View File
@@ -14,3 +14,6 @@ install:
before_script:
- make check
script:
- make test
+4 -1
View File
@@ -7,6 +7,9 @@ depends:
build:
go build .
test:
go test -v `glide novendor`
check:
go vet -v .
go vet -v `glide novendor`
!(gofmt -d $(SOURCE_FILES) | grep .)
+7
View File
@@ -0,0 +1,7 @@
# JCEKS
Package jceks parses JCEKS (Java Cryptogaphy Extension Key Store)
files and extracts keys and certificates. This module only implements
a fraction of the JCEKS cryptographic protocols. In particular, it
implements the SHA1 signature verification of the key store and the
PBEWithMD5AndDES3CBC cipher for encrypting private keys.
+426
View File
@@ -0,0 +1,426 @@
/*-
* Copyright 2016 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package jceks parses JCEKS (Java Cryptogaphy Extension Key Store)
// files and extracts keys and certificates. This module only implements
// a fraction of the JCEKS cryptographic protocols. In particular, it
// implements the SHA1 signature verification of the key store and the
// PBEWithMD5AndDES3CBC cipher for encrypting private keys.
package jceks
import (
"bytes"
"crypto/rsa"
"crypto/sha1"
"crypto/subtle"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"fmt"
"hash"
"io"
"os"
"time"
)
const (
jceksMagic = 0xcececece
jceksVersion = 0x02
jksMagic = 0xfeedfeed
)
var (
oidKeyProtector = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 42, 2, 17, 1, 1}
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
)
type encryptedPrivateKeyInfo struct {
Algo pkix.AlgorithmIdentifier
EncryptedKey []byte
}
type privateKeyInfo struct {
Version int
Algo pkix.AlgorithmIdentifier
PrivateKey []byte
}
type privateKeyEntry struct {
date time.Time
encodedKey []byte
certs []*x509.Certificate
}
func (e *privateKeyEntry) String() string {
return fmt.Sprintf("private-key: %s", e.date)
}
func (e *privateKeyEntry) Recover(password []byte) (*rsa.PrivateKey, error) {
var eKey encryptedPrivateKeyInfo
_, err := asn1.Unmarshal(e.encodedKey, &eKey)
if err != nil {
return nil, err
}
var decryptedKey []byte
switch {
case eKey.Algo.Algorithm.Equal(oidPBEWithMD5AndDES3CBC):
decryptedKey, err = recoverPBEWithMD5AndDES3CBC(eKey.Algo, eKey.EncryptedKey, password)
case eKey.Algo.Algorithm.Equal(oidKeyProtector):
// JavaSoft proprietary key-protection algorithm (used to protect
// private keys in the keystore implementation that comes with JDK
// 1.2). We shouldn't need this.
fallthrough
default:
return nil, fmt.Errorf("unsupported encrypted-private-key algorithm: %v", eKey.Algo.Algorithm)
}
if err != nil {
return nil, err
}
var pKey privateKeyInfo
if _, err := asn1.Unmarshal(decryptedKey, &pKey); err != nil {
return nil, err
}
if !pKey.Algo.Algorithm.Equal(oidPublicKeyRSA) {
return nil, fmt.Errorf("unsupported private-key algorithm: %v", pKey.Algo.Algorithm)
}
return x509.ParsePKCS1PrivateKey(pKey.PrivateKey)
}
type trustedCertEntry struct {
date time.Time
cert *x509.Certificate
}
func (e *trustedCertEntry) String() string {
return fmt.Sprintf("trusted-cert: %s", e.date)
}
// KeyStore maintains a map from alias name to the entry for that
// alias. Entries are currently either privateKeyEntry or
// trustedCertEntry.
type KeyStore struct {
entries map[string]interface{}
}
// readUTF reads a java encoded UTF-8 string. The encoding provides a
// 2-byte prefix indicating the length of the string.
func readUTF(r io.Reader) (string, error) {
var length uint16
err := binary.Read(r, binary.BigEndian, &length)
if err != nil {
return "", err
}
buf := make([]byte, length)
_, err = io.ReadFull(r, buf)
if err != nil {
return "", err
}
return string(buf), nil
}
// readBytes reads a byte array from the reader. The encoding provides
// a 4-byte prefix indicating the number of bytes which follow.
func readBytes(r io.Reader) ([]byte, error) {
length, err := readInt32(r)
if err != nil {
return nil, err
}
buf := make([]byte, length)
_, err = io.ReadFull(r, buf)
if err != nil {
return nil, err
}
return buf, nil
}
func readInt32(r io.Reader) (int32, error) {
var v int32
err := binary.Read(r, binary.BigEndian, &v)
return v, err
}
func readUint32(r io.Reader) (uint32, error) {
var v uint32
err := binary.Read(r, binary.BigEndian, &v)
return v, err
}
func readDate(r io.Reader) (time.Time, error) {
var v int64
err := binary.Read(r, binary.BigEndian, &v)
if err != nil {
return time.Time{}, err
}
sec := v / 1000
nsec := (v - sec*1000) * 1000 * 1000
return time.Unix(sec, nsec), nil
}
// Returns a SHA1 hash which has been pre-keyed with the specified
// password according to the JCEKS algorithm.
func getPreKeyedHash(password []byte) hash.Hash {
md := sha1.New()
buf := make([]byte, len(password)*2)
for i := 0; i < len(password); i++ {
buf[i*2+1] = password[i]
}
md.Write(buf)
// Yes, "Mighty Aprhodite" is a constant used by this method.
md.Write([]byte("Mighty Aphrodite"))
return md
}
func parseHeader(r io.Reader) (uint32, error) {
magic, err := readUint32(r)
if err != nil {
return 0, err
}
if magic != jceksMagic && magic != jksMagic {
return 0, fmt.Errorf("unexpected magic: %08x != (%08x || %08x)", magic, uint32(jceksMagic), uint32(jksMagic))
}
version, err := readUint32(r)
if err != nil {
return 0, err
}
return version, nil
}
func (ks *KeyStore) parsePrivateKey(r io.Reader) error {
alias, err := readUTF(r)
if err != nil {
return err
}
entry := &privateKeyEntry{
certs: []*x509.Certificate{},
}
entry.date, err = readDate(r)
if err != nil {
return err
}
entry.encodedKey, err = readBytes(r)
if err != nil {
return err
}
nCerts, err := readInt32(r)
if err != nil {
return err
}
for j := 0; j < int(nCerts); j++ {
certType, err := readUTF(r)
if err != nil {
return err
}
if certType != "X.509" {
return fmt.Errorf("unable to handle certificate type: %s", certType)
}
certBytes, err := readBytes(r)
if err != nil {
return err
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil
}
entry.certs = append(entry.certs, cert)
}
ks.entries[alias] = entry
return nil
}
func (ks *KeyStore) parseTrustedCert(r io.Reader) error {
alias, err := readUTF(r)
if err != nil {
return err
}
entry := &trustedCertEntry{}
entry.date, err = readDate(r)
if err != nil {
return err
}
certType, err := readUTF(r)
if err != nil {
return err
}
if certType != "X.509" {
return fmt.Errorf("unable to handle certificate type: %s", certType)
}
certBytes, err := readBytes(r)
if err != nil {
return err
}
entry.cert, err = x509.ParseCertificate(certBytes)
if err != nil {
return nil
}
ks.entries[alias] = entry
return nil
}
// Parse parses the key store from the specified reader.
func (ks *KeyStore) Parse(r io.Reader, password []byte) error {
var md hash.Hash
if password != nil {
md = getPreKeyedHash(password)
r = io.TeeReader(r, md)
}
version, err := parseHeader(r)
if err != nil {
return err
}
if version != jceksVersion {
return fmt.Errorf("unexpected version: %d != %d", version, jceksVersion)
}
count, err := readInt32(r)
if err != nil {
return err
}
for i := 0; i < int(count); i++ {
tag, err := readInt32(r)
if err != nil {
return err
}
switch tag {
case 1:
// Private-key entry
err := ks.parsePrivateKey(r)
if err != nil {
return err
}
case 2:
// Trusted-cert entry
err := ks.parseTrustedCert(r)
if err != nil {
return err
}
case 3:
// Secret-key entry
fallthrough
default:
panic(fmt.Errorf("unimplemented tag: %d", tag))
}
}
if md != nil {
computed := md.Sum([]byte{})
actual := make([]byte, len(computed))
_, err := io.ReadFull(r, actual)
if err != nil {
return err
}
if subtle.ConstantTimeCompare(computed, actual) != 1 {
return fmt.Errorf("keystore was tampered with or password was incorrect")
}
}
return nil
}
// GetPrivateKeyAndCerts retrieves the specified private key. Returns
// nil if the private key does not exist or alias points to a non
// private key entry.
func (ks *KeyStore) GetPrivateKeyAndCerts(alias string, password []byte) (
key *rsa.PrivateKey, certs []*x509.Certificate, err error) {
entry := ks.entries[alias]
if entry == nil {
return
}
switch t := entry.(type) {
case *privateKeyEntry:
if len(t.certs) < 1 {
return nil, nil, fmt.Errorf("key has no certificates")
}
if (t.certs[0].KeyUsage & x509.KeyUsageDigitalSignature) == 0 {
return nil, nil, fmt.Errorf("key cannot be used for digital signatures: %x", t.certs[0].KeyUsage)
}
key, err = t.Recover(password)
if err == nil {
certs = t.certs
return
}
}
return
}
// GetCert retrieves the specified certificate. Returns nil if the
// certificate does not exist or alias points to a non certificate
// entry.
func (ks *KeyStore) GetCert(alias string) (*x509.Certificate, error) {
entry := ks.entries[alias]
if entry == nil {
return nil, nil
}
switch t := entry.(type) {
case *trustedCertEntry:
return t.cert, nil
}
return nil, nil
}
// ListPrivateKeys lists the names of the private keys stored in the key store.
func (ks *KeyStore) ListPrivateKeys() []string {
var r []string
for k, v := range ks.entries {
if _, ok := v.(*privateKeyEntry); ok {
r = append(r, k)
}
}
return r
}
// ListCerts lists the names of the certs stored in the key store.
func (ks *KeyStore) ListCerts() []string {
var r []string
for k, v := range ks.entries {
if _, ok := v.(*trustedCertEntry); ok {
r = append(r, k)
}
}
return r
}
func (ks *KeyStore) String() string {
var buf bytes.Buffer
for k, v := range ks.entries {
fmt.Fprintf(&buf, "%s\n", k)
fmt.Fprintf(&buf, " %s\n", v)
}
return buf.String()
}
// Load loads the key store from the specified file.
func Load(filename string, password []byte) (*KeyStore, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
ks := &KeyStore{
entries: make(map[string]interface{}),
}
err = ks.Parse(file, password)
if err != nil {
return nil, err
}
return ks, err
}
+234
View File
@@ -0,0 +1,234 @@
/*-
* Copyright 2016 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jceks
import (
"bytes"
"crypto/rsa"
"errors"
"flag"
"fmt"
"os"
"os/exec"
"reflect"
"testing"
)
var (
generateTestData = flag.Bool("generate", false, "generate test data")
)
type testData struct {
certFilename string
keyFilename string
p12Filename string
jceksFilename string
storePassword string
keyPassword string
alias string
}
func newTestData(prefix string) *testData {
return &testData{
certFilename: "testdata/" + prefix + ".crt",
keyFilename: "testdata/" + prefix + ".key",
p12Filename: "testdata/" + prefix + ".p12",
jceksFilename: "testdata/" + prefix + ".jceks",
storePassword: prefix + "-store-password",
keyPassword: prefix + "-key-password",
alias: prefix + "-some-alias",
}
}
func (d *testData) cleanup() {
os.Remove(d.certFilename)
os.Remove(d.keyFilename)
os.Remove(d.p12Filename)
os.Remove(d.jceksFilename)
}
func runCommand(name string, args ...string) (string, error) {
cmd := exec.Command(name, args...)
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
err := cmd.Run()
out := buf.Bytes()
if err != nil {
return "", errors.New(string(out))
}
return string(out), nil
}
func (d *testData) generatePrivateKeyAndCert(t *testing.T) {
_, err := runCommand("openssl", "req", "-x509",
"-nodes", "-days", "365", "-newkey", "rsa:2048",
"-subj", "/CN=Test User/O=Test Organization/C=US",
"-extensions", "v3_req",
"-keyout", d.keyFilename,
"-out", d.certFilename)
if err != nil {
t.Fatal(err)
}
}
func (d *testData) convertPrivateKeyAndCertToPkcs12(t *testing.T) {
_, err := runCommand("openssl", "pkcs12", "-export",
"-in", d.certFilename,
"-inkey", d.keyFilename,
"-name", d.alias,
"-out", d.p12Filename,
"-passout", fmt.Sprintf("pass:%s", d.storePassword))
if err != nil {
t.Fatal(err)
}
}
func (d *testData) convertPkcs12ToJceks(t *testing.T) {
_, err := runCommand("keytool", "-importkeystore",
"-alias", d.alias,
"-destkeypass", d.keyPassword,
"-destkeystore", d.jceksFilename,
"-deststorepass", d.storePassword,
"-srckeystore", d.p12Filename,
"-srcstoretype", "PKCS12",
"-srcstorepass", d.storePassword,
"-storetype", "JCEKS")
if err != nil {
t.Fatal(err)
}
}
func (d *testData) importCertToJceks(t *testing.T) {
_, err := runCommand("keytool", "-importcert", "-noprompt",
"-alias", d.alias,
"-file", d.certFilename,
"-keystore", d.jceksFilename,
"-storepass", d.storePassword,
"-storetype", "JCEKS")
if err != nil {
t.Fatal(err)
}
}
func equalRSAPublicKey(a, b *rsa.PublicKey) bool {
if a.E != b.E {
return false
}
return a.N.Cmp(b.N) == 0
}
func equalRSAPrivateKey(a, b *rsa.PrivateKey) bool {
if !equalRSAPublicKey(&a.PublicKey, &b.PublicKey) {
return false
}
if a.D.Cmp(b.D) != 0 {
return false
}
if len(a.Primes) != len(b.Primes) {
return false
}
for i := 0; i < len(a.Primes); i++ {
if a.Primes[i].Cmp(b.Primes[i]) != 0 {
return false
}
}
return true
}
func TestPrivateKey(t *testing.T) {
d := newTestData("private-key")
if *generateTestData {
d.cleanup()
d.generatePrivateKeyAndCert(t)
d.convertPrivateKeyAndCertToPkcs12(t)
d.convertPkcs12ToJceks(t)
}
ks, err := Load(d.jceksFilename, []byte(d.storePassword))
if err != nil {
t.Fatal(err)
}
key, certs, err := ks.GetPrivateKeyAndCerts(d.alias, []byte(d.keyPassword))
if err != nil {
t.Fatal(err)
}
if key == nil {
t.Fatal("unable to load key")
}
expected, err := LoadPEMKey(d.keyFilename)
if err != nil {
t.Fatal(err)
}
if !equalRSAPrivateKey(key, expected) {
t.Fatalf("keys are not equal")
}
if len(certs) != 1 {
t.Fatalf("unexpected number of certs: %d != 1", len(certs))
}
expectedCert, err := LoadPEMCert(d.certFilename)
if err != nil {
t.Fatal(err)
}
if !certs[0].Equal(expectedCert) {
t.Fatalf("certs are not equal")
}
keyAliases := ks.ListPrivateKeys()
if !reflect.DeepEqual(keyAliases, []string{d.alias}) {
t.Fatalf("unexpected private key aliases: %s", keyAliases)
}
}
func TestTrustedCert(t *testing.T) {
d := newTestData("trusted-cert")
if *generateTestData {
d.cleanup()
d.generatePrivateKeyAndCert(t)
d.importCertToJceks(t)
}
ks, err := Load(d.jceksFilename, []byte(d.storePassword))
if err != nil {
t.Fatal(err)
}
cert, err := ks.GetCert(d.alias)
if err != nil {
t.Fatal(err)
}
if cert == nil {
t.Fatal("unable to load cert")
}
expectedCert, err := LoadPEMCert(d.certFilename)
if err != nil {
t.Fatal(err)
}
if !cert.Equal(expectedCert) {
t.Fatalf("certs are not equal")
}
certAliases := ks.ListCerts()
if !reflect.DeepEqual(certAliases, []string{d.alias}) {
t.Fatalf("unexpected cert aliases: %s", certAliases)
}
}
+114
View File
@@ -0,0 +1,114 @@
/*-
* Copyright 2016 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jceks
import (
"bytes"
"crypto/cipher"
"crypto/des"
"crypto/md5"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
)
var (
oidPBEWithMD5AndDES3CBC = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 42, 2, 19, 1}
)
type pbeParameters struct {
Salt []byte
Iterations int
}
// Here's how this algorithm works:
//
// 1. Split salt in two halves. If the two halves are identical,
// invert one of them.
// 2. Concatenate password with each of the halves.
// 3. Digest each concatenation with c iterations, where c is the
// iterationCount. Concatenate the output from each digest round with the
// password, and use the result as the input to the next digest operation.
// The digest algorithm is MD5.
// 4. After c iterations, use the 2 resulting digests as follows:
// The 16 bytes of the first digest and the 1st 8 bytes of the 2nd digest
// form the triple DES key, and the last 8 bytes of the 2nd digest form the
// IV.
func recoverPBEWithMD5AndDES3CBC(
algo pkix.AlgorithmIdentifier, encryptedKey, password []byte) ([]byte, error) {
var params pbeParameters
if _, err := asn1.Unmarshal(algo.Parameters.FullBytes, &params); err != nil {
return nil, err
}
// Convert password to byte array, so that it can be digested.
passwdBytes := make([]byte, len(password))
for i := 0; i < len(password); i++ {
passwdBytes[i] = password[i] & 0x7f
}
salt := params.Salt
if len(salt) != 8 {
return nil, fmt.Errorf("unexpected salt length: %d", len(salt))
}
if bytes.Compare(salt[0:4], salt[4:]) == 0 {
// First and second half of salt are equal, invert first half.
for i := 0; i < 2; i++ {
salt[i], salt[3-i] = salt[3-i], salt[i]
}
}
const keyLen = 24
const blockSize = des.BlockSize
derivedKey := make([]byte, keyLen+blockSize)
// Now digest each half (concatenated with password). For each
// half, go through the loop as many times as specified by the
// iteration count parameter (inner for loop). Concatenate the
// output from each digest round with the password, and use the
// result as the input to the next digest operation.
md := md5.New()
for i := 0; i < 2; i++ {
n := len(salt) / 2
toBeHashed := salt[i*n : (i+1)*n]
for j := 0; j < params.Iterations; j++ {
md.Write(toBeHashed)
md.Write(passwdBytes)
toBeHashed = md.Sum([]byte{})
md.Reset()
}
copy(derivedKey[i*len(toBeHashed):], toBeHashed)
}
cipherKey := derivedKey[0:keyLen]
iv := derivedKey[keyLen:]
des3, err := des.NewTripleDESCipher(cipherKey)
if err != nil {
return nil, err
}
decrypter := cipher.NewCBCDecrypter(des3, iv)
if (len(encryptedKey) % decrypter.BlockSize()) != 0 {
return nil, fmt.Errorf("encrypted data must be a multiple of block length: %d %d",
len(encryptedKey), decrypter.BlockSize())
}
decryptedKey := make([]byte, len(encryptedKey))
decrypter.CryptBlocks(decryptedKey, encryptedKey)
return decryptedKey, nil
}
+19
View File
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIJALRGD3GBYHSZMA0GCSqGSIb3DQEBBQUAMD0xEjAQBgNV
BAMTCVRlc3QgVXNlcjEaMBgGA1UEChMRVGVzdCBPcmdhbml6YXRpb24xCzAJBgNV
BAYTAlVTMB4XDTE0MDMxNDE0MTA0NFoXDTE1MDMxNDE0MTA0NFowPTESMBAGA1UE
AxMJVGVzdCBVc2VyMRowGAYDVQQKExFUZXN0IE9yZ2FuaXphdGlvbjELMAkGA1UE
BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjX4e65+mxX5Ha
SbSHwluBy9v7+pJrOuEco+w1dFjw7g5ijjB/5HW72N9J8LfQrEAMX9FuMue7lE1y
GcgL9Kepuz71jPjyjrHpFNa90dKzrhwkQ6f9aLkKd5D+84ua7/O7/l5yiuD+slvm
dppj+WxfWfQ58bgFjoxwkt2voYa/+pYK52CkykaQ0jtfEIdbftRAT2rLVSD6UljT
9t54xUL80CHgog8nlySTFdpPnTQ2jpYBDziRpO6Agjz6zjOo2tiY+yo/2QL3rhzQ
e8zYVjH+jZ3hKAeQp5kOTdfjkxJzQe3LLm0pQCkaWhglcnZhSqdErVScNmv87nR8
CEXXK9O/AgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMA0GCSqGSIb3
DQEBBQUAA4IBAQAzbdBJFsYq2Rrh0xwHuYTcA/NesrV1wC36kCcVbFI3JVqZ2lZz
MlA89mJ8oOfH2Cg0hB9XlQoysdYiKXwl9tJP6lOimSp2nSn0NOKBaSvvpUyMBocn
29L1IPm6BrauGGDhKxByA3sPc/FSZnCx2p1OO9HG3vAprYJppiiOSTwuRHO4+PBj
gowPrvXGucKHfNlekxN1D6dYi+Zg+61pA/6qDJ+ZKXVECE3NN3H/o6WuOIB5tFVE
tCJNSxo/8KnrU1TMG1e9EvcL3kq3rmXEID6pwmxqg3piKg3Dar/GkpxGmdB306UF
HnRjsjYEj+HWOpfOy1vPbbKMXtC3TayPyGZ6
-----END CERTIFICATE-----
Binary file not shown.
+27
View File
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA41+HuufpsV+R2km0h8Jbgcvb+/qSazrhHKPsNXRY8O4OYo4w
f+R1u9jfSfC30KxADF/RbjLnu5RNchnIC/Snqbs+9Yz48o6x6RTWvdHSs64cJEOn
/Wi5CneQ/vOLmu/zu/5ecorg/rJb5naaY/lsX1n0OfG4BY6McJLdr6GGv/qWCudg
pMpGkNI7XxCHW37UQE9qy1Ug+lJY0/beeMVC/NAh4KIPJ5ckkxXaT500No6WAQ84
kaTugII8+s4zqNrYmPsqP9kC964c0HvM2FYx/o2d4SgHkKeZDk3X45MSc0Htyy5t
KUApGloYJXJ2YUqnRK1UnDZr/O50fAhF1yvTvwIDAQABAoIBAGnboCW0s2iRRiaL
CjHqmw/jCZhXILQrxYLADskUUhRZwPjBmnLwup+qaMrT98B/cZJRSgA0Uw9SDHyC
5FAsp6KuOrG34G+NX+dUfGYDukVNWmzH0v54My5cXHVWjjikXqW31+EcJ4RtJbw2
m8rP5VS/XKVdlH+BzoDa37tVSJc4AraA3XHaU6ebuWojSiHJ3T1L1gTZKmOIL2dw
ETIgxKfQTVwceWBmQv61pG5bxqIsmDpeCWjeULchSvuiHG0xRfzJYewhv8LKVpEJ
/bSb5kciAm+nnigme2kjsp05AGmrncv7XYRrn1caJc30kwGsbv5Y7UJ9ZG5amlL6
ltBxSQECgYEA+iqKyEdf/3qORker/WvHEL54mrbfSYoB3B6mr7uKb/JuIOk1yYX5
Fb0xDavxypBvwAUTNpRjjMRr1HpKggfv4ARudgjBxjD5Sy1TaBypYlB2gVuPRHZv
TLbM7erVQVsg2jC36nQboO8ZiKiSpFlZiAn0ieCxmE9ixATzOJp9lwcCgYEA6Kzq
uX/8BMFLmcHodpAakvxTfv3qGgz0GpSQAmu6bo4qyWuYH4hiBe8PDzCfSfwkhWFr
Q06s9rKseSyAzqF5YBjq8+8bKjZyBaC8m4QEpgocZpwQqKicrnngh7LWiYVNLfw/
qPfrLc0RmgZistWbES1/6Pm3x+Z49PLyVcUAt4kCgYB4RYO7jjUlCrLkLwkNKYfn
EOvC1jC7llIWldXlnvCLqa4wvG5TmMmMHg07WXNBw/c2BjqafvTtdHGzEahIo7A7
r2W78bHXqyvvbLcw0rbMwYp33qEedSJFa41SxRgJ99nvjISff3rZAJryDLmTsjFN
KhwbPZ+kbmY5f3e/uuaueQKBgDkfyvkD/QHF2yPCwanqMzwHCxDQkhsXNw8Xjkup
2zmtWb/d1JlZSIega5gVHeZyKx08D7OUq05eC44saOtSJZR8SaLd/1Nbzp6nGecs
gF+rd9GRW12tF9qWPZPTSmy093/kwFRhmbHC+SFRlAXH/6w1+YNfW8mOQgARbYG1
PjnRAoGBAIbnGfWSLl6jlo1W9CVk8AylY3TFmVygbijjrK3UYL/3KLJn6nb2BeW0
IsigU5qe+za2zW2KDkw//9vqm0omda66ubwU24uXjKwhZbrAAhmy8kFZNRzpMZIF
Q3RqsGxPxWt7Rk79OBxtnPLWc8nxpGTliOUzgeZqnLy1aSAZnngn
-----END RSA PRIVATE KEY-----
BIN
View File
Binary file not shown.
+19
View File
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIJANpdtwWQVU6qMA0GCSqGSIb3DQEBBQUAMD0xEjAQBgNV
BAMTCVRlc3QgVXNlcjEaMBgGA1UEChMRVGVzdCBPcmdhbml6YXRpb24xCzAJBgNV
BAYTAlVTMB4XDTE0MDMxNDE0MTA0NVoXDTE1MDMxNDE0MTA0NVowPTESMBAGA1UE
AxMJVGVzdCBVc2VyMRowGAYDVQQKExFUZXN0IE9yZ2FuaXphdGlvbjELMAkGA1UE
BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTX69McLtRMUhP
ZSKzyTee1kY6XUyiHqEapxhKxTkT5HyQaKspIeP60keOiCn6y/stwXK/4fjm4XMx
jDIw2HE6/gwLBXPr3IH3x44XXjK9U3SGSCrFhP0aI4dNxLq7oKhzfR15Wz3pw4zY
q3mtT4muefqjkcVUNTwJyzxA5D5km1Coq7xKZ9Demt71NUcAYOfJswFh97bI8QpK
MfaYbUSWw0um+Mz2vlL20Yq2xWjX+CgclnVWjsf/zrbXkuGyWPE1VbTCzwvdbUHW
8cokI+UBQC/FGIxVq11hGn+Tk8cMubnDLEqRf3oiCGx0wvKixP55YlthRElSaNKt
DmduKH2hAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMA0GCSqGSIb3
DQEBBQUAA4IBAQAY6BFSl2zjhsD2udgb7mgOFgCwzWzCis5+Oy+2H/uNjyeBuSgM
qrh779y+BRapxrqBSYo8QWltapAlRJ8MwIYJpHz2FqN4bY38r3WMZ380wVT/wDYh
6WFB3REVKEkhsW2HWX4cOF8m2iD/WOR2x2T0F5EC4W1nofl/oIhgujDNjmerZEMF
JPnXHGRNOpYtyZhWuo+CFh500vV9Azomcw3z3NJ4teW5Z2T0mmOQWLB1cc1rAVYB
W9uqE5GyHcQ6VHdQHjq3luxOvzH88slQ2S/SC9a41l1Z6EAe8wlVEa17wTQ3PR2g
UHFTtH5OUR9neGEx+G+GqnR7jfRAPZNv0DsH
-----END CERTIFICATE-----
Binary file not shown.
+27
View File
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA01+vTHC7UTFIT2Uis8k3ntZGOl1Moh6hGqcYSsU5E+R8kGir
KSHj+tJHjogp+sv7LcFyv+H45uFzMYwyMNhxOv4MCwVz69yB98eOF14yvVN0hkgq
xYT9GiOHTcS6u6Coc30deVs96cOM2Kt5rU+Jrnn6o5HFVDU8Ccs8QOQ+ZJtQqKu8
SmfQ3pre9TVHAGDnybMBYfe2yPEKSjH2mG1ElsNLpvjM9r5S9tGKtsVo1/goHJZ1
Vo7H/86215LhsljxNVW0ws8L3W1B1vHKJCPlAUAvxRiMVatdYRp/k5PHDLm5wyxK
kX96IghsdMLyosT+eWJbYURJUmjSrQ5nbih9oQIDAQABAoIBAQCV28Ds8P/dEJOz
toBj9sT4V/JybrNmPVD7FHykhi9xawzlVVAEWYLI0UzqQJ+CsBvk1MIGSK+vASgq
eLsc5ldg+7yOE8+b6To78b9L0f0nPYPfsEqivyay4X2MJW4+mCjVuF6tK4M5uOqi
svARb9KtYM3SKgc9LIDkcLLHTwrtR+ZRZNQ0rSXuE2dSJMETCXCdzuSaoQfoOThM
Gmv6Sm04PVws5x2BQso9oThQHZnwCquSaveLMPguxnwanRKvCQkNvtnGzAeFQ7o3
Lx5Zag4/4L5XLh0QT9+mWH8oRSFysPIk2pMhqzAAUxVjpo0RKmNYw7wEgYdwsXJl
pC2X+iABAoGBAPGroa7gPgDYCzcf7tNYOTz/nDbwUXsv4B0bCsUId+71ExnVwqxI
ubqYB0v23qjX+679CC1qsMb38//CBNlwruyLjxaDct3UFo3IfiXY1hAeN+Anvzeb
gfoD3s2XGHPIG5niViHIEwZKubpzsQ6UXl8HSFBCPFD1piXJ7qs/zeHJAoGBAN/o
LGbWww3vrYKZ+BCd3wIFrxurco94RDq7CfhTAinkc26WTmagyw4kXmZS0mtL8ycn
5PhIvCJaugnEUG1kEWDsYH57GMBG/BVoJC8to0yLrVh5fVzYl343i+KkqA3Y99bE
NFoshK+L1JH3klPhqmN7wnra/VNp2LJm4Nz+4mkZAoGAHjc5VeYPmodoj5HciGwl
a+0BmRTe+yn3OWxiIlR2ulfF9Zr2ZhgJsLzFXMgW+sFWZICafyMxyw7BYR7fAFjI
Zibk0wnIWNflogCJVS4RRZ6hmdMeY1N8IshNGSNlGUTRvqG/5yVey5CYPCmu34XJ
btQ4RGCjrfOovFzNDHhDw5ECgYAE5jWigmx+L5JiWzAcXPf2OV2dg2DcVstXZaRQ
NLDFbeRAtTU99aK7ynvuTT2hb2YAo1TVQfIr5kRP1mXUHu5qaoGqAtOF0YfOiBrS
lXMPR7chSnc9wtd9wYVkDipHM0oo/t4OYw78MFkUYJBpGXT6/EhDG+uTGavOK4Yc
D8+wcQKBgQDU6tlGCHK1kfc1xO6uoxHB7yLVM7lh+irLHJb0gt/zGdJUOHom+4r3
zBW22vWWrYv2kck0ez6hvwgw1eIys6yY3Cz+8GpR0wVPxj4CiVDWfhQZeuu4rP1d
BFGjC0y1npoI4bJE8G3QiNJYRKaHCaf2hdvNr3AhwDT6QCDo+kQCtQ==
-----END RSA PRIVATE KEY-----
+69
View File
@@ -0,0 +1,69 @@
/*-
* Copyright 2016 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jceks
import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"strings"
)
// LoadPEMKey extracts a private key from a PEM file.
func LoadPEMKey(filename string) (*rsa.PrivateKey, error) {
keyPEMBlock, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var keyDERBlock *pem.Block
for {
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
if keyDERBlock == nil {
return nil, fmt.Errorf("failed to parse key PEM data")
}
if keyDERBlock.Type == "PRIVATE KEY" ||
strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
break
}
}
return x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
}
// LoadPEMCert extracts a certificate from a PEM file.
func LoadPEMCert(filename string) (*x509.Certificate, error) {
certPEMBlock, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var certDERBlock *pem.Block
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
return nil, fmt.Errorf("failed to parse certificate PEM data")
}
if certDERBlock.Type == "CERTIFICATE" {
break
}
}
return x509.ParseCertificate(certDERBlock.Bytes)
}