mirror of
https://github.com/gregtwallace/apc-p15-tool.git
synced 2025-01-22 08:14:08 +00:00
key: finish key encoding and start cert
This commit is contained in:
parent
85462c93b1
commit
1f6dad4907
14 changed files with 592 additions and 115 deletions
1
go.mod
1
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
github.com/peterbourgon/ff/v4 v4.0.0-alpha.4
|
||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
|
||||
go.uber.org/zap v1.26.0
|
||||
golang.org/x/crypto v0.18.0
|
||||
)
|
||||
|
||||
require go.uber.org/multierr v1.11.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -16,6 +16,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
|||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
219
pkg/pkcs15/encrypted_envelope.go
Normal file
219
pkg/pkcs15/encrypted_envelope.go
Normal file
|
@ -0,0 +1,219 @@
|
|||
package pkcs15
|
||||
|
||||
import (
|
||||
"apc-p15-tool/pkg/tools"
|
||||
"apc-p15-tool/pkg/tools/asn1obj"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/asn1"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
// fixed specs for apc cert
|
||||
const (
|
||||
apcKEKPassword = "user"
|
||||
apcKEKIterations = 5000
|
||||
)
|
||||
|
||||
// encryptedKeyEnvelope encrypts p15's rsa private key using the algorithms and
|
||||
// params expected in the APC file. Salt values are always random.
|
||||
func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) {
|
||||
// calculate values for the object
|
||||
kekSalt := make([]byte, 8)
|
||||
_, err := rand.Read(kekSalt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// kek hash alg
|
||||
kekHash := sha256.New
|
||||
// size of 3DES key (k1 + k2 + k3)
|
||||
kekSize := 24
|
||||
|
||||
// kek
|
||||
kek := pbkdf2.Key([]byte(apcKEKPassword), kekSalt, apcKEKIterations, kekSize, kekHash)
|
||||
|
||||
// make DES cipher from KEK for CEK
|
||||
cekDesCipher, err := des.NewTripleDESCipher(kek)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// cek (16 bytes for authEnc128) -- see: rfc3211
|
||||
cekLen := uint8(16)
|
||||
cek := make([]byte, cekLen)
|
||||
_, err = rand.Read(cek)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// LEN + Check Val [3]
|
||||
wrappedCEK := append([]byte{cekLen}, tools.BitwiseComplimentOf(cek[:3])...)
|
||||
|
||||
// + CEK
|
||||
wrappedCEK = append(wrappedCEK, cek...)
|
||||
|
||||
// + padding (if needed)
|
||||
// pad wrapped CEK to min 2 * block len
|
||||
cekPadLen := 0
|
||||
if len(wrappedCEK) < 2*cekDesCipher.BlockSize() {
|
||||
cekPadLen = 2*cekDesCipher.BlockSize() - len(wrappedCEK)
|
||||
} else if len(wrappedCEK)%cekDesCipher.BlockSize() != 0 {
|
||||
// pad if not a multiple of block len
|
||||
cekPadLen = cekDesCipher.BlockSize() - len(wrappedCEK)%cekDesCipher.BlockSize()
|
||||
}
|
||||
cekPadding := make([]byte, cekPadLen)
|
||||
_, err = rand.Read(cekPadding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wrappedCEK = append(wrappedCEK, cekPadding...)
|
||||
|
||||
// double encrypt CEK
|
||||
cekEncryptSalt := make([]byte, 8)
|
||||
_, err = rand.Read(cekEncryptSalt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cekEncrypter := cipher.NewCBCEncrypter(cekDesCipher, cekEncryptSalt)
|
||||
|
||||
encryptedCEKOnly1Rd := make([]byte, len(wrappedCEK))
|
||||
cekEncrypter.CryptBlocks(encryptedCEKOnly1Rd, wrappedCEK)
|
||||
encryptedCEK := make([]byte, len(encryptedCEKOnly1Rd))
|
||||
cekEncrypter.CryptBlocks(encryptedCEK, encryptedCEKOnly1Rd)
|
||||
|
||||
// content encryption
|
||||
contentEncSalt := make([]byte, 8)
|
||||
_, err = rand.Read(contentEncSalt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contentEncryptKey := pbkdf2.Key(cek, []byte("encryption"), 1, 24, sha1.New)
|
||||
contentDesCipher, err := des.NewTripleDESCipher(contentEncryptKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content := p15.privateKeyObject()
|
||||
// pad content, see: https://datatracker.ietf.org/doc/html/rfc3852 6.3
|
||||
contentPadLen := uint8(contentDesCipher.BlockSize() - (len(content) % contentDesCipher.BlockSize()))
|
||||
// ALWAYS pad, if content is exact, add full block of padding
|
||||
if contentPadLen == 0 {
|
||||
contentPadLen = uint8(contentDesCipher.BlockSize())
|
||||
}
|
||||
for i := uint8(1); i <= contentPadLen; i++ {
|
||||
content = append(content, byte(contentPadLen))
|
||||
}
|
||||
|
||||
contentEncrypter := cipher.NewCBCEncrypter(contentDesCipher, contentEncSalt)
|
||||
encryptedContent := make([]byte, len(content))
|
||||
contentEncrypter.CryptBlocks(encryptedContent, content)
|
||||
|
||||
// encrypted content MAC
|
||||
macKey := pbkdf2.Key(cek, []byte("authentication"), 1, 32, sha1.New)
|
||||
|
||||
// data encryption alg block
|
||||
encAlgObj := asn1obj.Sequence([][]byte{
|
||||
// ContentEncryptionAlgorithmIdentifier
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDauthEnc128),
|
||||
// ContentEncryptionAlgorithmIdentifier details/info
|
||||
asn1obj.Sequence([][]byte{
|
||||
// encryption alg & salt
|
||||
asn1obj.Sequence([][]byte{
|
||||
// encryption alg
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDdesEDE3CBC),
|
||||
// encryption alg's salt
|
||||
asn1obj.OctetString(contentEncSalt),
|
||||
}),
|
||||
// mac alg
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDhmacWithSHA256),
|
||||
asn1.NullBytes,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
macHasher := hmac.New(sha256.New, macKey)
|
||||
// the data the MAC covers is the algId header bytes + encrypted data bytes
|
||||
hashMe := append(encAlgObj, encryptedContent...)
|
||||
|
||||
// make MAC
|
||||
_, err = macHasher.Write(hashMe)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mac := macHasher.Sum(nil)
|
||||
|
||||
// build object
|
||||
// AuthEnvelopedData Type
|
||||
envelope := [][]byte{
|
||||
// CMSVersion
|
||||
asn1obj.Integer(big.NewInt(2)),
|
||||
// RecipientInfos
|
||||
asn1obj.Set([][]byte{
|
||||
// 1st and only 'RecipientInfo' - pwri [3] PasswordRecipientinfo
|
||||
asn1obj.ExplicitCompound(3, [][]byte{
|
||||
// CMSVersion
|
||||
asn1obj.Integer(big.NewInt(0)),
|
||||
// keyDerivationAlgorithm [0]
|
||||
asn1obj.ExplicitCompound(0, [][]byte{
|
||||
// KeyDerivationAlgorithmIdentifier
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDpkcs5PBKDF2),
|
||||
// KeyDerivationAlgorithmIdentifier details/info
|
||||
asn1obj.Sequence([][]byte{
|
||||
// kek pbkdf2 Salt
|
||||
asn1obj.OctetString(kekSalt),
|
||||
// kek pbkdf2 Iterations
|
||||
asn1obj.Integer(big.NewInt(apcKEKIterations)),
|
||||
// kek pbkdf2 hash type
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDhmacWithSHA256),
|
||||
asn1.NullBytes,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
// keyEncryptionAlgorithm (for CEK)
|
||||
asn1obj.Sequence([][]byte{
|
||||
// KeyEncryptionAlgorithmIdentifier
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDpwriKEK),
|
||||
// KeyEncryptionAlgorithmIdentifier details/info
|
||||
asn1obj.Sequence([][]byte{
|
||||
// encryption alg
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDdesEDE3CBC),
|
||||
// encryption alg's salt
|
||||
asn1obj.OctetString(cekEncryptSalt),
|
||||
}),
|
||||
}),
|
||||
// EncryptedKey (the actual ciphertext for the CEK)
|
||||
asn1obj.OctetString(encryptedCEK),
|
||||
}),
|
||||
}),
|
||||
// EncryptedContentInfo (actual encrypted content)
|
||||
asn1obj.Sequence([][]byte{
|
||||
// ContentType
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDpkcs7Data),
|
||||
// encryption alg OBJ
|
||||
encAlgObj,
|
||||
// [0] IMPLICIT EncryptedContent (AKA the ciphertext)
|
||||
asn1obj.ExplicitValue(0, encryptedContent),
|
||||
}),
|
||||
// MAC
|
||||
asn1obj.OctetString(mac),
|
||||
}
|
||||
|
||||
// combine to singular byte slice
|
||||
finalEnv := []byte{}
|
||||
for i := range envelope {
|
||||
finalEnv = append(finalEnv, envelope[i]...)
|
||||
}
|
||||
|
||||
return finalEnv, nil
|
||||
}
|
|
@ -3,6 +3,7 @@ package pkcs15
|
|||
import (
|
||||
"apc-p15-tool/pkg/tools/asn1obj"
|
||||
"crypto/sha1"
|
||||
"encoding/asn1"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
|
@ -13,7 +14,7 @@ func (p15 *pkcs15KeyCert) keyId() []byte {
|
|||
asn1obj.Sequence([][]byte{
|
||||
// Key is RSA
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1),
|
||||
asn1obj.Null(),
|
||||
asn1.NullBytes,
|
||||
}),
|
||||
// BIT STRING of rsa key public key
|
||||
asn1obj.BitString(
|
||||
|
@ -33,3 +34,19 @@ func (p15 *pkcs15KeyCert) keyId() []byte {
|
|||
|
||||
return hasher.Sum(nil)
|
||||
}
|
||||
|
||||
// keyIdInt2 returns the sequence for keyId with INT val of 2
|
||||
// For APC, this appears to be the same value is the base keyId
|
||||
// but this isn't compliant with the spec which actually seems
|
||||
// to call for SKID (skid octet value copied directly out of the
|
||||
// certificate's x509 extension)
|
||||
func (p15 *pkcs15KeyCert) keyIdInt2() []byte {
|
||||
// Create Object
|
||||
obj := asn1obj.Sequence([][]byte{
|
||||
asn1obj.Integer(big.NewInt(2)),
|
||||
// Note: This is for APC, doesn't seem compliant with spec though
|
||||
asn1obj.OctetString(p15.keyId()),
|
||||
})
|
||||
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
package pkcs15
|
||||
|
||||
import (
|
||||
"apc-p15-tool/pkg/tools/asn1obj"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
const (
|
||||
apcKeyLabel = "Private key"
|
||||
)
|
||||
|
||||
// ToP15File turns the key and cert into a properly formatted and encoded
|
||||
// p15 file
|
||||
func (p15 *pkcs15KeyCert) ToP15File() ([]byte, error) {
|
||||
// private key object
|
||||
pkey, err := p15.toP15PrivateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ContentInfo
|
||||
p15File := asn1obj.Sequence([][]byte{
|
||||
|
||||
// contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type)
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content),
|
||||
|
||||
// content
|
||||
asn1obj.Explicit(0,
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.Integer(big.NewInt(0)),
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.Explicit(0,
|
||||
asn1obj.Explicit(0,
|
||||
pkey,
|
||||
),
|
||||
),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
return p15File, nil
|
||||
}
|
||||
|
||||
// toP15PrivateKey creates the encoded private key. it is broken our from the larger p15
|
||||
// function for readability
|
||||
func (p15 *pkcs15KeyCert) toP15PrivateKey() ([]byte, error) {
|
||||
// key object
|
||||
key := asn1obj.Sequence([][]byte{
|
||||
// commonObjectAttributes - Label
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.UTF8String(apcKeyLabel),
|
||||
}),
|
||||
// CommonKeyAttributes
|
||||
asn1obj.Sequence([][]byte{
|
||||
// CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE )
|
||||
asn1obj.OctetString(p15.keyId()),
|
||||
// CommonKeyAttributes - usage (trailing 0s will drop)
|
||||
asn1obj.BitString([]byte{byte(0b11100010)}),
|
||||
// CommonKeyAttributes - accessFlags (trailing 0s will drop)
|
||||
asn1obj.BitString([]byte{byte(0b10110000)}),
|
||||
}),
|
||||
})
|
||||
|
||||
return key, nil
|
||||
}
|
36
pkg/pkcs15/pem_parse.go
Normal file
36
pkg/pkcs15/pem_parse.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package pkcs15
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
// pkcs15KeyCert holds the data for a key and certificate pair; it provides
|
||||
// various methods to transform pkcs15 data
|
||||
type pkcs15KeyCert struct {
|
||||
key *rsa.PrivateKey
|
||||
cert *x509.Certificate
|
||||
}
|
||||
|
||||
// ParsePEMToPKCS15 parses the provide pem files to a pkcs15 struct; it also does some
|
||||
// basic sanity check; if any of this fails, an error is returned
|
||||
func ParsePEMToPKCS15(keyPem, certPem []byte) (*pkcs15KeyCert, error) {
|
||||
// decode / check key
|
||||
key, err := pemKeyDecode(keyPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// decode / check cert
|
||||
cert, err := pemCertDecode(certPem, keyPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p15 := &pkcs15KeyCert{
|
||||
key: key,
|
||||
cert: cert,
|
||||
}
|
||||
|
||||
return p15, nil
|
||||
}
|
|
@ -1,36 +1,127 @@
|
|||
package pkcs15
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"apc-p15-tool/pkg/tools/asn1obj"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// pkcs15KeyCert holds the data for a key and certificate pair; it provides
|
||||
// various methods to transform pkcs15 data
|
||||
type pkcs15KeyCert struct {
|
||||
key *rsa.PrivateKey
|
||||
cert *x509.Certificate
|
||||
}
|
||||
const (
|
||||
apcKeyLabel = "Private key"
|
||||
)
|
||||
|
||||
// ParsePEMToPKCS15 parses the provide pem files to a pkcs15 struct; it also does some
|
||||
// basic sanity check; if any of this fails, an error is returned
|
||||
func ParsePEMToPKCS15(keyPem, certPem []byte) (*pkcs15KeyCert, error) {
|
||||
// decode / check key
|
||||
key, err := pemKeyDecode(keyPem)
|
||||
// ToP15File turns the key and cert into a properly formatted and encoded
|
||||
// p15 file
|
||||
func (p15 *pkcs15KeyCert) ToP15File() ([]byte, error) {
|
||||
// private key object
|
||||
pkey, err := p15.toP15PrivateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// decode / check cert
|
||||
cert, err := pemCertDecode(certPem, keyPem)
|
||||
cert, err := p15.toP15Cert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p15 := &pkcs15KeyCert{
|
||||
key: key,
|
||||
cert: cert,
|
||||
// ContentInfo
|
||||
p15File := asn1obj.Sequence([][]byte{
|
||||
|
||||
// contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type)
|
||||
asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content),
|
||||
|
||||
// content
|
||||
asn1obj.ExplicitCompound(0, [][]byte{
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.Integer(big.NewInt(0)),
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.ExplicitCompound(0, [][]byte{
|
||||
asn1obj.ExplicitCompound(0, [][]byte{
|
||||
pkey,
|
||||
}),
|
||||
}),
|
||||
asn1obj.ExplicitCompound(4, [][]byte{
|
||||
asn1obj.ExplicitCompound(0, [][]byte{
|
||||
cert,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
return p15File, nil
|
||||
}
|
||||
|
||||
// toP15PrivateKey creates the encoded private key. it is broken our from the larger p15
|
||||
// function for readability
|
||||
// NOTE: Do not use this to try and turn just a private key into a p15, the format isn't
|
||||
// quite the same.
|
||||
func (p15 *pkcs15KeyCert) toP15PrivateKey() ([]byte, error) {
|
||||
// rsa encrypted key in encrypted envelope
|
||||
envelope, err := p15.encryptedKeyEnvelope()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p15, nil
|
||||
// key object
|
||||
key := asn1obj.Sequence([][]byte{
|
||||
// commonObjectAttributes - Label
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.UTF8String(apcKeyLabel),
|
||||
}),
|
||||
// CommonKeyAttributes
|
||||
asn1obj.Sequence([][]byte{
|
||||
// CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE )
|
||||
asn1obj.OctetString(p15.keyId()),
|
||||
// CommonKeyAttributes - usage (trailing 0s will drop)
|
||||
asn1obj.BitString([]byte{byte(0b11100010)}),
|
||||
// CommonKeyAttributes - accessFlags (trailing 0s will drop)
|
||||
asn1obj.BitString([]byte{byte(0b10110000)}),
|
||||
// CommonKeyAttributes - startDate
|
||||
asn1obj.GeneralizedTime(p15.cert.NotBefore),
|
||||
// CommonKeyAttributes - [0] endDate
|
||||
asn1obj.GeneralizedTimeExplicitValue(0, p15.cert.NotAfter),
|
||||
}),
|
||||
// ObjectValue - indirect-protected
|
||||
asn1obj.ExplicitCompound(1, [][]byte{
|
||||
asn1obj.Sequence([][]byte{
|
||||
// AuthEnvelopedData Type ([4])
|
||||
asn1obj.ExplicitCompound(4, [][]byte{
|
||||
envelope,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// toP15Cert creates the encoded certificate. it is broken our from the larger p15
|
||||
// function for readability
|
||||
// NOTE: Do not use this to try and turn just a cert into a p15. I don't believe,
|
||||
// such a thing is permissible under the spec.
|
||||
func (p15 *pkcs15KeyCert) toP15Cert() ([]byte, error) {
|
||||
|
||||
// cert object
|
||||
cert := asn1obj.Sequence([][]byte{
|
||||
// commonObjectAttributes - Label
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.UTF8String(apcKeyLabel),
|
||||
}),
|
||||
// keyIds of various types
|
||||
asn1obj.Sequence([][]byte{
|
||||
asn1obj.OctetString(p15.keyId()),
|
||||
// additional keyids
|
||||
asn1obj.ExplicitCompound(2, [][]byte{
|
||||
p15.keyIdInt2(),
|
||||
// p15.keyIdInt3(),
|
||||
// p15.keyIdInt6(),
|
||||
// p15.keyIdInt7(),
|
||||
// p15.keyIdInt8(),
|
||||
// p15.keyIdInt9(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
|
24
pkg/pkcs15/private_key.go
Normal file
24
pkg/pkcs15/private_key.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package pkcs15
|
||||
|
||||
import "apc-p15-tool/pkg/tools/asn1obj"
|
||||
|
||||
// privateKeyObject returns the ASN.1 representation of a private key
|
||||
func (p15 *pkcs15KeyCert) privateKeyObject() []byte {
|
||||
// ensure all expected vals are available
|
||||
p15.key.Precompute()
|
||||
|
||||
pkey := asn1obj.Sequence([][]byte{
|
||||
// P
|
||||
asn1obj.IntegerExplicitValue(3, p15.key.Primes[0]),
|
||||
// Q
|
||||
asn1obj.IntegerExplicitValue(4, p15.key.Primes[1]),
|
||||
// Dp
|
||||
asn1obj.IntegerExplicitValue(5, p15.key.Precomputed.Dp),
|
||||
// Dq
|
||||
asn1obj.IntegerExplicitValue(6, p15.key.Precomputed.Dq),
|
||||
// Qinv
|
||||
asn1obj.IntegerExplicitValue(7, p15.key.Precomputed.Qinv),
|
||||
})
|
||||
|
||||
return pkey
|
||||
}
|
46
pkg/tools/asn1obj/explicit.go
Normal file
46
pkg/tools/asn1obj/explicit.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package asn1obj
|
||||
|
||||
import "encoding/asn1"
|
||||
|
||||
// ExplicitCompound wraps another ASN.1 Object(s) with the EXPLICIT wrapper using
|
||||
// the tag number specified
|
||||
func ExplicitCompound(explicitTagNumber int, wrappedElements [][]byte) []byte {
|
||||
val := []byte{}
|
||||
for i := range wrappedElements {
|
||||
val = append(val, wrappedElements[i]...)
|
||||
}
|
||||
|
||||
raw := asn1.RawValue{
|
||||
Class: asn1.ClassContextSpecific,
|
||||
Tag: explicitTagNumber,
|
||||
IsCompound: true,
|
||||
Bytes: val,
|
||||
}
|
||||
|
||||
// should never error
|
||||
asn1result, err := asn1.Marshal(raw)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return asn1result
|
||||
}
|
||||
|
||||
// ExplicitValue creates an EXPLICIT Object with a byte data value (i.e. it
|
||||
// is NOT compound) using the tag number specified
|
||||
func ExplicitValue(explicitTagNumber int, val []byte) []byte {
|
||||
raw := asn1.RawValue{
|
||||
Class: asn1.ClassContextSpecific,
|
||||
Tag: explicitTagNumber,
|
||||
IsCompound: false,
|
||||
Bytes: val,
|
||||
}
|
||||
|
||||
// should never error
|
||||
asn1result, err := asn1.Marshal(raw)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return asn1result
|
||||
}
|
85
pkg/tools/asn1obj/generalizedtime.go
Normal file
85
pkg/tools/asn1obj/generalizedtime.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package asn1obj
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GeneralizedTime returns the specified time as a GeneralizedTime
|
||||
func GeneralizedTime(t time.Time) []byte {
|
||||
// should never error
|
||||
asn1result, err := asn1.MarshalWithParams(t, "generalized")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return asn1result
|
||||
}
|
||||
|
||||
// helper funcs from golang asn1 package
|
||||
func appendTwoDigits(dst []byte, v int) []byte {
|
||||
return append(dst, byte('0'+(v/10)%10), byte('0'+v%10))
|
||||
}
|
||||
|
||||
func appendFourDigits(dst []byte, v int) []byte {
|
||||
var bytes [4]byte
|
||||
for i := range bytes {
|
||||
bytes[3-i] = '0' + byte(v%10)
|
||||
v /= 10
|
||||
}
|
||||
return append(dst, bytes[:]...)
|
||||
}
|
||||
|
||||
// generalizedTimevalue returns the specified time encoded as a
|
||||
// GeneralizedTime but WITHOUT the ASN.1 headers (class/tag/length)
|
||||
func generalizedTimevalue(t time.Time) []byte {
|
||||
dst := []byte{}
|
||||
|
||||
year := t.Year()
|
||||
if year < 0 || year > 9999 {
|
||||
panic("cannot represent time as GeneralizedTime (invalid year)")
|
||||
}
|
||||
|
||||
dst = appendFourDigits(dst, year)
|
||||
|
||||
_, month, day := t.Date()
|
||||
|
||||
dst = appendTwoDigits(dst, int(month))
|
||||
dst = appendTwoDigits(dst, day)
|
||||
|
||||
hour, min, sec := t.Clock()
|
||||
|
||||
dst = appendTwoDigits(dst, hour)
|
||||
dst = appendTwoDigits(dst, min)
|
||||
dst = appendTwoDigits(dst, sec)
|
||||
|
||||
_, offset := t.Zone()
|
||||
|
||||
switch {
|
||||
case offset/60 == 0:
|
||||
return append(dst, 'Z')
|
||||
case offset > 0:
|
||||
dst = append(dst, '+')
|
||||
case offset < 0:
|
||||
dst = append(dst, '-')
|
||||
}
|
||||
|
||||
offsetMinutes := offset / 60
|
||||
if offsetMinutes < 0 {
|
||||
offsetMinutes = -offsetMinutes
|
||||
}
|
||||
|
||||
dst = appendTwoDigits(dst, offsetMinutes/60)
|
||||
dst = appendTwoDigits(dst, offsetMinutes%60)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// helper funcs from golang asn1 package - END
|
||||
|
||||
// GeneralizedTimeExplicitValue returns t encoded as a GeneralizedTime, however
|
||||
// instead of tagging it with GeneralizedTime it is instead tagged with an
|
||||
// explicit tag of the specified tag number
|
||||
func GeneralizedTimeExplicitValue(explicitTagNumber int, t time.Time) []byte {
|
||||
return ExplicitValue(explicitTagNumber, generalizedTimevalue(t))
|
||||
}
|
|
@ -15,3 +15,20 @@ func Integer(bigInt *big.Int) []byte {
|
|||
|
||||
return asn1result
|
||||
}
|
||||
|
||||
// IntegerExplicitValue returns bigInt encoded as an Integer, however
|
||||
// instead of tagging it with Integer it is instead tagged with an
|
||||
// explicit tag of the specified tag number
|
||||
func IntegerExplicitValue(explicitTagNumber int, bigInt *big.Int) []byte {
|
||||
intBytes := Integer(bigInt)
|
||||
|
||||
asn1Obj := asn1.RawValue{}
|
||||
rest, err := asn1.Unmarshal(intBytes, &asn1Obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else if len(rest) > 0 {
|
||||
panic("invalid extra data")
|
||||
}
|
||||
|
||||
return ExplicitValue(explicitTagNumber, asn1Obj.Bytes)
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package asn1obj
|
||||
|
||||
import "encoding/asn1"
|
||||
|
||||
// Explicit wraps another ASN.1 Object with the EXPLICIT wrapper using
|
||||
// the tag number specified
|
||||
func Explicit(explicitTagNumber int, wrappedElement []byte) []byte {
|
||||
raw := asn1.RawValue{
|
||||
Class: asn1.ClassContextSpecific,
|
||||
Tag: explicitTagNumber,
|
||||
IsCompound: true,
|
||||
Bytes: wrappedElement,
|
||||
}
|
||||
|
||||
// should never error
|
||||
asn1result, err := asn1.Marshal(raw)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return asn1result
|
||||
}
|
||||
|
||||
// Null returns the NULL value
|
||||
func Null() []byte {
|
||||
return asn1.NullBytes
|
||||
}
|
|
@ -5,6 +5,12 @@ import "encoding/asn1"
|
|||
var (
|
||||
OIDPkscs15Content = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 15, 3, 1} // pkcs15content (PKCS #15 content type)
|
||||
OIDrsaEncryptionPKCS1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} // rsaEncryption (PKCS #1)
|
||||
OIDpkcs5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12} // pkcs5PBKDF2 (PKCS #5 v2.0)
|
||||
OIDhmacWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9} // hmacWithSHA256 (RSADSI digestAlgorithm)
|
||||
OIDpwriKEK = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 3, 9} // pwriKEK (S/MIME Algorithms)
|
||||
OIDdesEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} // des-EDE3-CBC (RSADSI encryptionAlgorithm)
|
||||
OIDpkcs7Data = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} // data (PKCS #7)
|
||||
OIDauthEnc128 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 3, 15} // authEnc128 (S/MIME Algorithms)
|
||||
)
|
||||
|
||||
// ObjectIdentifier returns an ASN.1 OBJECT IDENTIFIER with the oidValue bytes
|
||||
|
|
26
pkg/tools/asn1obj/set.go
Normal file
26
pkg/tools/asn1obj/set.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package asn1obj
|
||||
|
||||
import "encoding/asn1"
|
||||
|
||||
// Set returns an ASN.1 SET with the specified content
|
||||
func Set(content [][]byte) []byte {
|
||||
val := []byte{}
|
||||
for i := range content {
|
||||
val = append(val, content[i]...)
|
||||
}
|
||||
|
||||
raw := asn1.RawValue{
|
||||
Class: asn1.ClassUniversal,
|
||||
Tag: asn1.TagSet,
|
||||
IsCompound: true,
|
||||
Bytes: val,
|
||||
}
|
||||
|
||||
// should never error
|
||||
asn1result, err := asn1.Marshal(raw)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return asn1result
|
||||
}
|
Loading…
Reference in a new issue